Skip to content

Commit 11ce987

Browse files
authored
Merge branch 'main' into ci/thread-sanitizer-ios
2 parents f9630c8 + 90229f2 commit 11ce987

1 file changed

Lines changed: 74 additions & 194 deletions

File tree

‎GoogleSignIn/Tests/Unit/GIDEMMErrorHandlerTest.m‎

Lines changed: 74 additions & 194 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,6 @@ @interface GIDEMMErrorHandlerTest : XCTestCase
4040
@end
4141

4242
@implementation GIDEMMErrorHandlerTest {
43-
// Whether or not the current device runs on iOS 10.
44-
BOOL _isIOS10;
45-
4643
// Whether key window has been set.
4744
BOOL _keyWindowSet;
4845

@@ -52,7 +49,6 @@ @implementation GIDEMMErrorHandlerTest {
5249

5350
- (void)setUp {
5451
[super setUp];
55-
_isIOS10 = [UIDevice currentDevice].systemVersion.integerValue == 10;
5652
_keyWindowSet = NO;
5753
_presentedViewController = nil;
5854
UIWindow *fakeKeyWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
@@ -91,6 +87,44 @@ - (void)tearDown {
9187
[super tearDown];
9288
}
9389

90+
// Waits until work already dispatched to the main queue (such as the handler presenting
91+
// its dialog) has run.
92+
- (void)waitForMainQueue {
93+
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main queue"];
94+
dispatch_async(dispatch_get_main_queue(), ^() {
95+
[expectation fulfill];
96+
});
97+
[self waitForExpectationsWithTimeout:1 handler:nil];
98+
}
99+
100+
// Returns the presented UIAlertController, or records a test failure and returns nil.
101+
- (nullable UIAlertController *)presentedAlert {
102+
if (![_presentedViewController isKindOfClass:[UIAlertController class]]) {
103+
XCTFail(@"Expected a presented UIAlertController, got %@.", _presentedViewController);
104+
return nil;
105+
}
106+
return (UIAlertController *)_presentedViewController;
107+
}
108+
109+
// Asserts that the alert has a title and message and that its actions have the expected titles.
110+
- (void)assertAlert:(UIAlertController *)alert hasActionTitles:(NSArray<NSString *> *)titles {
111+
XCTAssertNotNil(alert.title);
112+
XCTAssertNotNil(alert.message);
113+
XCTAssertEqualObjects([alert.actions valueForKey:@"title"], titles);
114+
}
115+
116+
// Invokes the handler of the alert action with the given title, or records a test failure.
117+
- (void)tapActionTitled:(NSString *)title inAlert:(UIAlertController *)alert {
118+
for (UIAlertAction *action in alert.actions) {
119+
if ([action.title isEqualToString:title]) {
120+
action.actionHandler(action);
121+
return;
122+
}
123+
}
124+
XCTFail(@"No action titled '%@' in alert (found: %@).",
125+
title, [alert.actions valueForKey:@"title"]);
126+
}
127+
94128
// Expects opening a particular URL string in performing an action.
95129
- (void)expectOpenURLString:(NSString *)urlString inAction:(void (^)(void))action {
96130
// Swizzle and mock [UIApplication sharedApplication] since it is unavailable in unit tests.
@@ -160,13 +194,6 @@ - (void)testGeneralEMMErrorOK {
160194
completion:^() {
161195
completionCalled = YES;
162196
}];
163-
if (![UIAlertController class]) {
164-
XCTAssertFalse(result);
165-
XCTAssertTrue(completionCalled);
166-
XCTAssertFalse(_keyWindowSet);
167-
XCTAssertNil(_presentedViewController);
168-
return;
169-
}
170197
XCTAssertTrue(result);
171198
XCTAssertFalse(completionCalled);
172199
XCTAssertFalse(_keyWindowSet);
@@ -183,163 +210,67 @@ - (void)testGeneralEMMErrorOK {
183210
XCTAssertFalse(_keyWindowSet);
184211
XCTAssertNil(_presentedViewController);
185212

186-
// Wait for the code under test to be executed on the main thread.
187-
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
188-
dispatch_async(dispatch_get_main_queue(), ^() {
189-
[expectation fulfill];
190-
});
191-
[self waitForExpectationsWithTimeout:1 handler:nil];
192-
XCTAssertFalse(completionCalled);
213+
[self waitForMainQueue];
193214
XCTAssertTrue(_keyWindowSet);
194-
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
195-
UIAlertController *alert = (UIAlertController *)_presentedViewController;
196-
XCTAssertNotNil(alert.title);
197-
XCTAssertNotNil(alert.message);
198-
XCTAssertEqual(alert.actions.count, 1);
215+
UIAlertController *alert = [self presentedAlert];
216+
if (!alert) return;
217+
[self assertAlert:alert hasActionTitles:@[ @"OK" ]];
218+
XCTAssertFalse(completionCalled);
199219

200-
// Pretend to touch the "OK" button.
201-
UIAlertAction *action = alert.actions[0];
202-
XCTAssertEqualObjects(action.title, @"OK");
203-
action.actionHandler(action);
220+
[self tapActionTitled:@"OK" inAlert:alert];
204221
XCTAssertTrue(completionCalled);
205222
}
206223

207224
// Verifies that the handler handles EMM screenlock required error with user tapping 'Cancel'.
208225
- (void)testScreenlockRequiredCancel {
209-
if (_isIOS10) {
210-
// The dialog is different on iOS 10.
211-
return;
212-
}
213226
__block BOOL completionCalled = NO;
214227
NSDictionary<NSString *, NSString *> *response = @{ @"error" : @"emm_passcode_required" };
215228
BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
216229
completion:^() {
217230
completionCalled = YES;
218231
}];
219-
if (![UIAlertController class]) {
220-
XCTAssertFalse(result);
221-
XCTAssertTrue(completionCalled);
222-
XCTAssertFalse(_keyWindowSet);
223-
XCTAssertNil(_presentedViewController);
224-
return;
225-
}
226232
XCTAssertTrue(result);
227233
XCTAssertFalse(completionCalled);
228234
XCTAssertFalse(_keyWindowSet);
229235
XCTAssertNil(_presentedViewController);
230236

231-
// Wait for the code under test to be executed on the main thread.
232-
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
233-
dispatch_async(dispatch_get_main_queue(), ^() {
234-
[expectation fulfill];
235-
});
236-
[self waitForExpectationsWithTimeout:1 handler:nil];
237-
XCTAssertFalse(completionCalled);
237+
[self waitForMainQueue];
238238
XCTAssertTrue(_keyWindowSet);
239-
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
240-
UIAlertController *alert = (UIAlertController *)_presentedViewController;
241-
XCTAssertNotNil(alert.title);
242-
XCTAssertNotNil(alert.message);
243-
XCTAssertEqual(alert.actions.count, 2);
239+
UIAlertController *alert = [self presentedAlert];
240+
if (!alert) return;
241+
[self assertAlert:alert hasActionTitles:@[ @"Cancel", @"Settings" ]];
242+
XCTAssertFalse(completionCalled);
244243

245-
// Pretend to touch the "Cancel" button.
246-
UIAlertAction *action = alert.actions[0];
247-
XCTAssertEqualObjects(action.title, @"Cancel");
248-
action.actionHandler(action);
244+
[self tapActionTitled:@"Cancel" inAlert:alert];
249245
XCTAssertTrue(completionCalled);
250246
}
251247

252248
// Verifies that the handler handles EMM screenlock required error with user tapping 'Settings'.
253249
- (void)testScreenlockRequiredSettings {
254-
if (_isIOS10) {
255-
// The dialog is different on iOS 10.
256-
return;
257-
}
258250
__block BOOL completionCalled = NO;
259251
NSDictionary<NSString *, NSString *> *response = @{ @"error" : @"emm_passcode_required" };
260252
BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
261253
completion:^() {
262254
completionCalled = YES;
263255
}];
264-
if (![UIAlertController class]) {
265-
XCTAssertFalse(result);
266-
XCTAssertTrue(completionCalled);
267-
XCTAssertFalse(_keyWindowSet);
268-
XCTAssertNil(_presentedViewController);
269-
return;
270-
}
271256
XCTAssertTrue(result);
272257
XCTAssertFalse(completionCalled);
273258
XCTAssertFalse(_keyWindowSet);
274259
XCTAssertNil(_presentedViewController);
275260

276-
// Wait for the code under test to be executed on the main thread.
277-
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
278-
dispatch_async(dispatch_get_main_queue(), ^() {
279-
[expectation fulfill];
280-
});
281-
[self waitForExpectationsWithTimeout:1 handler:nil];
282-
XCTAssertFalse(completionCalled);
261+
[self waitForMainQueue];
283262
XCTAssertTrue(_keyWindowSet);
284-
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
285-
UIAlertController *alert = (UIAlertController *)_presentedViewController;
286-
XCTAssertNotNil(alert.title);
287-
XCTAssertNotNil(alert.message);
288-
XCTAssertEqual(alert.actions.count, 2);
263+
UIAlertController *alert = [self presentedAlert];
264+
if (!alert) return;
265+
[self assertAlert:alert hasActionTitles:@[ @"Cancel", @"Settings" ]];
266+
XCTAssertFalse(completionCalled);
289267

290-
// Pretend to touch the "Settings" button.
291-
UIAlertAction *action = alert.actions[1];
292-
XCTAssertEqualObjects(action.title, @"Settings");
293268
[self expectOpenURLString:UIApplicationOpenSettingsURLString inAction:^() {
294-
action.actionHandler(action);
269+
[self tapActionTitled:@"Settings" inAlert:alert];
295270
}];
296271
XCTAssertTrue(completionCalled);
297272
}
298273

299-
- (void)testScreenlockRequiredOkOnIOS10 {
300-
if (!_isIOS10) {
301-
// A more useful dialog is used for other iOS versions.
302-
return;
303-
}
304-
__block BOOL completionCalled = NO;
305-
NSDictionary<NSString *, NSString *> *response = @{ @"error" : @"emm_passcode_required" };
306-
BOOL result = [[GIDEMMErrorHandler sharedInstance] handleErrorFromResponse:response
307-
completion:^() {
308-
completionCalled = YES;
309-
}];
310-
if (![UIAlertController class]) {
311-
XCTAssertFalse(result);
312-
XCTAssertTrue(completionCalled);
313-
XCTAssertFalse(_keyWindowSet);
314-
XCTAssertNil(_presentedViewController);
315-
return;
316-
}
317-
XCTAssertTrue(result);
318-
XCTAssertFalse(completionCalled);
319-
XCTAssertFalse(_keyWindowSet);
320-
XCTAssertNil(_presentedViewController);
321-
322-
// Wait for the code under test to be executed on the main thread.
323-
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
324-
dispatch_async(dispatch_get_main_queue(), ^() {
325-
[expectation fulfill];
326-
});
327-
[self waitForExpectationsWithTimeout:1 handler:nil];
328-
XCTAssertFalse(completionCalled);
329-
XCTAssertTrue(_keyWindowSet);
330-
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
331-
UIAlertController *alert = (UIAlertController *)_presentedViewController;
332-
XCTAssertNotNil(alert.title);
333-
XCTAssertNotNil(alert.message);
334-
XCTAssertEqual(alert.actions.count, 1);
335-
336-
// Pretend to touch the "OK" button.
337-
UIAlertAction *action = alert.actions[0];
338-
XCTAssertEqualObjects(action.title, @"OK");
339-
action.actionHandler(action);
340-
XCTAssertTrue(completionCalled);
341-
}
342-
343274
// Verifies that the handler handles EMM app verification required error without a URL.
344275
- (void)testAppVerificationNoURL {
345276
__block BOOL completionCalled = NO;
@@ -348,36 +279,19 @@ - (void)testAppVerificationNoURL {
348279
completion:^() {
349280
completionCalled = YES;
350281
}];
351-
if (![UIAlertController class]) {
352-
XCTAssertFalse(result);
353-
XCTAssertTrue(completionCalled);
354-
XCTAssertFalse(_keyWindowSet);
355-
XCTAssertNil(_presentedViewController);
356-
return;
357-
}
358282
XCTAssertTrue(result);
359283
XCTAssertFalse(completionCalled);
360284
XCTAssertFalse(_keyWindowSet);
361285
XCTAssertNil(_presentedViewController);
362286

363-
// Wait for the code under test to be executed on the main thread.
364-
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
365-
dispatch_async(dispatch_get_main_queue(), ^() {
366-
[expectation fulfill];
367-
});
368-
[self waitForExpectationsWithTimeout:1 handler:nil];
369-
XCTAssertFalse(completionCalled);
287+
[self waitForMainQueue];
370288
XCTAssertTrue(_keyWindowSet);
371-
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
372-
UIAlertController *alert = (UIAlertController *)_presentedViewController;
373-
XCTAssertNotNil(alert.title);
374-
XCTAssertNotNil(alert.message);
375-
XCTAssertEqual(alert.actions.count, 1);
289+
UIAlertController *alert = [self presentedAlert];
290+
if (!alert) return;
291+
[self assertAlert:alert hasActionTitles:@[ @"OK" ]];
292+
XCTAssertFalse(completionCalled);
376293

377-
// Pretend to touch the "OK" button.
378-
UIAlertAction *action = alert.actions[0];
379-
XCTAssertEqualObjects(action.title, @"OK");
380-
action.actionHandler(action);
294+
[self tapActionTitled:@"OK" inAlert:alert];
381295
XCTAssertTrue(completionCalled);
382296
}
383297

@@ -391,36 +305,19 @@ - (void)testAppVerificationCancel {
391305
completion:^() {
392306
completionCalled = YES;
393307
}];
394-
if (![UIAlertController class]) {
395-
XCTAssertFalse(result);
396-
XCTAssertTrue(completionCalled);
397-
XCTAssertFalse(_keyWindowSet);
398-
XCTAssertNil(_presentedViewController);
399-
return;
400-
}
401308
XCTAssertTrue(result);
402309
XCTAssertFalse(completionCalled);
403310
XCTAssertFalse(_keyWindowSet);
404311
XCTAssertNil(_presentedViewController);
405312

406-
// Wait for the code under test to be executed on the main thread.
407-
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
408-
dispatch_async(dispatch_get_main_queue(), ^() {
409-
[expectation fulfill];
410-
});
411-
[self waitForExpectationsWithTimeout:1 handler:nil];
412-
XCTAssertFalse(completionCalled);
313+
[self waitForMainQueue];
413314
XCTAssertTrue(_keyWindowSet);
414-
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
415-
UIAlertController *alert = (UIAlertController *)_presentedViewController;
416-
XCTAssertNotNil(alert.title);
417-
XCTAssertNotNil(alert.message);
418-
XCTAssertEqual(alert.actions.count, 2);
315+
UIAlertController *alert = [self presentedAlert];
316+
if (!alert) return;
317+
[self assertAlert:alert hasActionTitles:@[ @"Cancel", @"Connect" ]];
318+
XCTAssertFalse(completionCalled);
419319

420-
// Pretend to touch the "Cancel" button.
421-
UIAlertAction *action = alert.actions[0];
422-
XCTAssertEqualObjects(action.title, @"Cancel");
423-
action.actionHandler(action);
320+
[self tapActionTitled:@"Cancel" inAlert:alert];
424321
XCTAssertTrue(completionCalled);
425322
}
426323

@@ -433,37 +330,20 @@ - (void)testAppVerificationConnect {
433330
completion:^() {
434331
completionCalled = YES;
435332
}];
436-
if (![UIAlertController class]) {
437-
XCTAssertFalse(result);
438-
XCTAssertTrue(completionCalled);
439-
XCTAssertFalse(_keyWindowSet);
440-
XCTAssertNil(_presentedViewController);
441-
return;
442-
}
443333
XCTAssertTrue(result);
444334
XCTAssertFalse(completionCalled);
445335
XCTAssertFalse(_keyWindowSet);
446336
XCTAssertNil(_presentedViewController);
447337

448-
// Wait for the code under test to be executed on the main thread.
449-
XCTestExpectation *expectation = [self expectationWithDescription:@"wait for main thread"];
450-
dispatch_async(dispatch_get_main_queue(), ^() {
451-
[expectation fulfill];
452-
});
453-
[self waitForExpectationsWithTimeout:1 handler:nil];
454-
XCTAssertFalse(completionCalled);
338+
[self waitForMainQueue];
455339
XCTAssertTrue(_keyWindowSet);
456-
XCTAssertTrue([_presentedViewController isKindOfClass:[UIAlertController class]]);
457-
UIAlertController *alert = (UIAlertController *)_presentedViewController;
458-
XCTAssertNotNil(alert.title);
459-
XCTAssertNotNil(alert.message);
460-
XCTAssertEqual(alert.actions.count, 2);
340+
UIAlertController *alert = [self presentedAlert];
341+
if (!alert) return;
342+
[self assertAlert:alert hasActionTitles:@[ @"Cancel", @"Connect" ]];
343+
XCTAssertFalse(completionCalled);
461344

462-
// Pretend to touch the "Connect" button.
463-
UIAlertAction *action = alert.actions[1];
464-
XCTAssertEqualObjects(action.title, @"Connect");
465345
[self expectOpenURLString:@"https://host.domain/path" inAction:^() {
466-
action.actionHandler(action);
346+
[self tapActionTitled:@"Connect" inAlert:alert];
467347
}];
468348
XCTAssertTrue(completionCalled);
469349
}

0 commit comments

Comments
 (0)