Skip to content

Commit 9ac5478

Browse files
committed
Observe KVO in GIDGoogleUserTest
Uses bit mask to verify KVO notifications are sent.
1 parent 30699df commit 9ac5478

File tree

1 file changed

+98
-7
lines changed

1 file changed

+98
-7
lines changed

GoogleSignIn/Tests/Unit/GIDGoogleUserTest.m

Lines changed: 98 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,45 @@
4141
// It should be larger than kTimeAccuracy which is used in the method `XCTAssertEqualWithAccuracy`.
4242
static NSTimeInterval const kTimeIncrement = 100;
4343

44+
// List of observed properties of the class being tested.
45+
static NSString *const kObservedProperties[] = {
46+
@"accessToken",
47+
@"refreshToken",
48+
@"idToken",
49+
};
50+
static const NSUInteger kNumberOfObservedProperties =
51+
sizeof(kObservedProperties) / sizeof(*kObservedProperties);
52+
53+
// Bit position for notification change type bitmask flags.
54+
// Must match the list of observed properties above.
55+
typedef NS_ENUM(NSUInteger, ChangeType) {
56+
kChangeTypeAccessTokenPrior,
57+
kChangeTypeAccessToken,
58+
kChangeTypeRefreshTokenPrior,
59+
kChangeTypeRefreshToken,
60+
kChangeTypeIDTokenPrior,
61+
kChangeTypeIDToken,
62+
kChangeTypeEnd // not a real change type but an end mark for calculating |kChangeAll|
63+
};
64+
65+
static const NSUInteger kChangeAll = (1u << kChangeTypeEnd) - 1u;
66+
67+
#if __has_feature(c_static_assert) || __has_extension(c_static_assert)
68+
_Static_assert(kChangeTypeEnd == (sizeof(kObservedProperties) / sizeof(*kObservedProperties)) * 2,
69+
"List of observed properties must match list of change notification enums");
70+
#endif
71+
4472
@interface GIDGoogleUserTest : XCTestCase
4573
@end
4674

47-
@implementation GIDGoogleUserTest
75+
@implementation GIDGoogleUserTest {
76+
// Bitmask flags for observed changes, as specified in |ChangeType|.
77+
NSUInteger _changesObserved;
78+
}
79+
80+
- (void)setUp {
81+
_changesObserved = 0;
82+
}
4883

4984
#pragma mark - Tests
5085

@@ -102,12 +137,8 @@ - (void)testUpdateAuthState {
102137
NSTimeInterval accessTokenExpireTime = [NSDate timeIntervalSinceReferenceDate];
103138
NSTimeInterval idTokenExpireTime = accessTokenExpireTime + kTimeIncrement;
104139

105-
NSString *idToken = [self idTokenWithExpireTime:idTokenExpireTime];
106-
OIDAuthState *authState = [OIDAuthState testInstanceWithIDToken:idToken
107-
accessToken:kAccessToken
108-
accessTokenExpireTime:accessTokenExpireTime];
109-
110-
GIDGoogleUser *user = [[GIDGoogleUser alloc] initWithAuthState:authState profileData:nil];
140+
GIDGoogleUser *user = [self observedGoogleUserWithAccessTokenExpireTime:accessTokenExpireTime
141+
idTokenExpireTime:idTokenExpireTime];
111142

112143
NSTimeInterval updatedAccessTokenExpireTime = idTokenExpireTime + kTimeIncrement;
113144
NSTimeInterval updatedIDTokenExpireTime = updatedAccessTokenExpireTime + kTimeIncrement;
@@ -126,12 +157,72 @@ - (void)testUpdateAuthState {
126157
XCTAssertEqualWithAccuracy([user.idToken.expirationDate timeIntervalSinceReferenceDate],
127158
updatedIDTokenExpireTime, kTimeAccuracy);
128159
XCTAssertEqual(user.profile, updatedProfileData);
160+
XCTAssertEqual(_changesObserved, kChangeAll);
129161
}
130162

131163
#pragma mark - Helpers
132164

165+
- (GIDGoogleUser *)observedGoogleUserWithAccessTokenExpireTime:(NSTimeInterval)accessTokenExpireTime
166+
idTokenExpireTime:(NSTimeInterval)idTokenExpireTime {
167+
GIDGoogleUser *user = [self googleUserWithAccessTokenExpireTime:accessTokenExpireTime
168+
idTokenExpireTime:idTokenExpireTime];
169+
for (unsigned int i = 0; i < kNumberOfObservedProperties; ++i) {
170+
[user addObserver:self
171+
forKeyPath:kObservedProperties[i]
172+
options:NSKeyValueObservingOptionPrior
173+
context:NULL];
174+
}
175+
return user;
176+
}
177+
178+
- (GIDGoogleUser *)googleUserWithAccessTokenExpireTime:(NSTimeInterval)accessTokenExpireTime
179+
idTokenExpireTime:(NSTimeInterval)idTokenExpireTime {
180+
181+
NSString *idToken = [self idTokenWithExpireTime:idTokenExpireTime];
182+
OIDAuthState *authState = [OIDAuthState testInstanceWithIDToken:idToken
183+
accessToken:kAccessToken
184+
accessTokenExpireTime:accessTokenExpireTime];
185+
186+
return [[GIDGoogleUser alloc] initWithAuthState:authState profileData:nil];
187+
}
188+
133189
- (NSString *)idTokenWithExpireTime:(NSTimeInterval)expireTime {
134190
return [OIDTokenResponse idTokenWithSub:kUserID exp:@(expireTime + NSTimeIntervalSince1970)];
135191
}
136192

193+
#pragma mark - NSKeyValueObserving
194+
195+
- (void)observeValueForKeyPath:(NSString *)keyPath
196+
ofObject:(id)object
197+
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
198+
context:(void *)context {
199+
ChangeType changeType;
200+
if ([keyPath isEqualToString:@"accessToken"]) {
201+
if (change[NSKeyValueChangeNotificationIsPriorKey]) {
202+
changeType = kChangeTypeAccessTokenPrior;
203+
} else {
204+
changeType = kChangeTypeAccessToken;
205+
}
206+
} else if ([keyPath isEqualToString:@"refreshToken"]) {
207+
if (change[NSKeyValueChangeNotificationIsPriorKey]) {
208+
changeType = kChangeTypeRefreshTokenPrior;
209+
} else {
210+
changeType = kChangeTypeRefreshToken;
211+
}
212+
} else if ([keyPath isEqualToString:@"idToken"]) {
213+
if (change[NSKeyValueChangeNotificationIsPriorKey]) {
214+
changeType = kChangeTypeIDTokenPrior;
215+
} else {
216+
changeType = kChangeTypeIDToken;
217+
}
218+
} else {
219+
XCTFail(@"unexpected keyPath");
220+
return; // so compiler knows |changeType| is always assigned
221+
}
222+
223+
NSInteger changeMask = 1 << changeType;
224+
XCTAssertFalse(_changesObserved & changeMask); // each change type should only fire once
225+
_changesObserved |= changeMask;
226+
}
227+
137228
@end

0 commit comments

Comments
 (0)