178@property(nonatomic, readwrite) UITouchPhase phase;
182@interface VSyncClient (Testing)
184- (CADisplayLink*)getDisplayLink;
194 self.messageSent = nil;
200 [
self.mockEngine stopMocking];
201 self.mockEngine = nil;
202 self.mockTextInputPlugin = nil;
203 self.messageSent = nil;
206- (
id)setUpMockScreen {
207 UIScreen* mockScreen = OCMClassMock([UIScreen
class]);
209 CGRect screenBounds = CGRectMake(0, 0, 1170, 2532);
210 OCMStub([mockScreen bounds]).andReturn(screenBounds);
211 CGFloat screenScale = 1;
212 OCMStub([mockScreen
scale]).andReturn(screenScale);
218 screen:(UIScreen*)screen
219 viewFrame:(CGRect)viewFrame
220 convertedFrame:(CGRect)convertedFrame {
221 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
222 id mockView = OCMClassMock([UIView
class]);
223 OCMStub([mockView
frame]).andReturn(viewFrame);
224 OCMStub([mockView convertRect:viewFrame toCoordinateSpace:[OCMArg
any]])
225 .andReturn(convertedFrame);
226 OCMStub([viewControllerMock viewIfLoaded]).andReturn(mockView);
231- (void)testViewDidLoadWillInvokeCreateTouchRateCorrectionVSyncClient {
240 OCMVerify([viewControllerMock createTouchRateCorrectionVSyncClientIfNeeded]);
243- (void)testStartKeyboardAnimationWillInvokeSetupKeyboardSpringAnimationIfNeeded {
253 CAAnimation* keyboardAnimation =
256 OCMVerify([viewControllerMock setUpKeyboardSpringAnimationIfNeeded:keyboardAnimation]);
259- (void)testSetupKeyboardSpringAnimationIfNeeded {
266 UIScreen* screen = [
self setUpMockScreen];
267 CGRect viewFrame = screen.bounds;
268 [
self setUpMockView:viewControllerMock
271 convertedFrame:viewFrame];
276 XCTAssertTrue(keyboardSpringAnimation == nil);
279 CABasicAnimation* nonSpringAnimation = [CABasicAnimation animation];
280 nonSpringAnimation.duration = 1.0;
281 nonSpringAnimation.fromValue = [NSNumber numberWithFloat:0.0];
282 nonSpringAnimation.toValue = [NSNumber numberWithFloat:1.0];
283 nonSpringAnimation.keyPath =
@"position";
287 XCTAssertTrue(keyboardSpringAnimation == nil);
290 CASpringAnimation* springAnimation = [CASpringAnimation animation];
291 springAnimation.mass = 1.0;
292 springAnimation.stiffness = 100.0;
293 springAnimation.damping = 10.0;
294 springAnimation.keyPath =
@"position";
295 springAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
296 springAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
299 XCTAssertTrue(keyboardSpringAnimation != nil);
302- (void)testKeyboardAnimationIsShowingAndCompounding {
309 UIScreen* screen = [
self setUpMockScreen];
310 CGRect viewFrame = screen.bounds;
311 [
self setUpMockView:viewControllerMock
314 convertedFrame:viewFrame];
317 CGFloat screenHeight = screen.bounds.size.height;
318 CGFloat screenWidth = screen.bounds.size.height;
321 CGRect initialShowKeyboardBeginFrame = CGRectMake(0, screenHeight, screenWidth, 250);
322 CGRect initialShowKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
323 NSNotification* fakeNotification = [NSNotification
324 notificationWithName:UIKeyboardWillChangeFrameNotification
327 @"UIKeyboardFrameBeginUserInfoKey" : @(initialShowKeyboardBeginFrame),
328 @"UIKeyboardFrameEndUserInfoKey" : @(initialShowKeyboardEndFrame),
329 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
330 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
335 XCTAssertTrue(isShowingAnimation1);
338 CGRect compoundingShowKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
339 CGRect compoundingShowKeyboardEndFrame = CGRectMake(0, screenHeight - 500, screenWidth, 500);
340 fakeNotification = [NSNotification
341 notificationWithName:UIKeyboardWillChangeFrameNotification
344 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingShowKeyboardBeginFrame),
345 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingShowKeyboardEndFrame),
346 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
347 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
352 XCTAssertTrue(isShowingAnimation2);
353 XCTAssertTrue(isShowingAnimation1 == isShowingAnimation2);
356 CGRect initialHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 500, screenWidth, 250);
357 CGRect initialHideKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
358 fakeNotification = [NSNotification
359 notificationWithName:UIKeyboardWillChangeFrameNotification
362 @"UIKeyboardFrameBeginUserInfoKey" : @(initialHideKeyboardBeginFrame),
363 @"UIKeyboardFrameEndUserInfoKey" : @(initialHideKeyboardEndFrame),
364 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
365 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
370 XCTAssertFalse(isShowingAnimation3);
371 XCTAssertTrue(isShowingAnimation2 != isShowingAnimation3);
374 CGRect compoundingHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
375 CGRect compoundingHideKeyboardEndFrame = CGRectMake(0, screenHeight, screenWidth, 500);
376 fakeNotification = [NSNotification
377 notificationWithName:UIKeyboardWillChangeFrameNotification
380 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingHideKeyboardBeginFrame),
381 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingHideKeyboardEndFrame),
382 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
383 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
388 XCTAssertFalse(isShowingAnimation4);
389 XCTAssertTrue(isShowingAnimation3 == isShowingAnimation4);
392- (void)testShouldIgnoreKeyboardNotification {
399 UIScreen* screen = [
self setUpMockScreen];
400 CGRect viewFrame = screen.bounds;
401 [
self setUpMockView:viewControllerMock
404 convertedFrame:viewFrame];
406 CGFloat screenWidth = screen.bounds.size.width;
407 CGFloat screenHeight = screen.bounds.size.height;
408 CGRect emptyKeyboard = CGRectZero;
409 CGRect zeroHeightKeyboard = CGRectMake(0, 0, screenWidth, 0);
410 CGRect validKeyboardEndFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
414 NSNotification* notification =
415 [NSNotification notificationWithName:UIKeyboardWillHideNotification
418 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
419 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
420 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
424 XCTAssertTrue(shouldIgnore == NO);
428 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
431 @"UIKeyboardFrameEndUserInfoKey" : @(emptyKeyboard),
432 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
433 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
436 XCTAssertTrue(shouldIgnore == YES);
441 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
444 @"UIKeyboardFrameEndUserInfoKey" : @(zeroHeightKeyboard),
445 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
446 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
449 XCTAssertTrue(shouldIgnore == NO);
454 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
457 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
458 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
459 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
462 XCTAssertTrue(shouldIgnore == YES);
467 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
470 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
471 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
472 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
475 XCTAssertTrue(shouldIgnore == NO);
477 if (@available(iOS 13.0, *)) {
481 OCMStub([viewControllerMock isKeyboardInOrTransitioningFromBackground]).andReturn(YES);
485 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
488 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
489 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
490 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
493 XCTAssertTrue(shouldIgnore == YES);
496- (void)testKeyboardAnimationWillNotCrashWhenEngineDestroyed {
507- (void)testKeyboardAnimationWillWaitUIThreadVsync {
520 const int delayTime = 1;
521 [engine
uiTaskRunner]->PostTask([] { sleep(delayTime); });
522 XCTestExpectation*
expectation = [
self expectationWithDescription:@"keyboard animation callback"];
524 __block CFTimeInterval fulfillTime;
526 fulfillTime = CACurrentMediaTime();
527 [expectation fulfill];
529 CFTimeInterval startTime = CACurrentMediaTime();
531 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
532 XCTAssertTrue(fulfillTime - startTime > delayTime);
535- (void)testCalculateKeyboardAttachMode {
543 UIScreen* screen = [
self setUpMockScreen];
544 CGRect viewFrame = screen.bounds;
545 [
self setUpMockView:viewControllerMock
548 convertedFrame:viewFrame];
550 CGFloat screenWidth = screen.bounds.size.width;
551 CGFloat screenHeight = screen.bounds.size.height;
554 CGRect keyboardFrame = CGRectZero;
555 NSNotification* notification =
556 [NSNotification notificationWithName:UIKeyboardWillHideNotification
559 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
560 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
561 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
564 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
567 keyboardFrame = CGRectZero;
568 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
571 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
572 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
573 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
576 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
579 keyboardFrame = CGRectMake(0, 0, screenWidth, 0);
580 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
583 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
584 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
585 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
588 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
591 keyboardFrame = CGRectMake(0, 0, 320, 320);
592 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
595 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
596 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
597 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
600 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
603 keyboardFrame = CGRectMake(0, 0, screenWidth, 320);
604 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
607 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
608 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
609 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
612 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
615 keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
616 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
619 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
620 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
621 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
624 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
627 CGFloat longDecimalHeight = 320.666666666666666;
628 keyboardFrame = CGRectMake(0, screenHeight - longDecimalHeight, screenWidth, longDecimalHeight);
629 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
632 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
633 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
634 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
637 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
640 keyboardFrame = CGRectMake(0, screenHeight - .0000001, screenWidth, longDecimalHeight);
641 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
644 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
645 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
646 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
649 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
652 keyboardFrame = CGRectMake(0, screenHeight, screenWidth, 320);
653 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
656 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
657 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
658 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
661 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
664- (void)testCalculateMultitaskingAdjustment {
672 UIScreen* screen = [
self setUpMockScreen];
673 CGFloat screenWidth = screen.bounds.size.width;
674 CGFloat screenHeight = screen.bounds.size.height;
675 CGRect screenRect = screen.bounds;
676 CGRect viewOrigFrame = CGRectMake(0, 0, 320, screenHeight - 40);
677 CGRect convertedViewFrame = CGRectMake(20, 20, 320, screenHeight - 40);
678 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
679 id mockView = [
self setUpMockView:viewControllerMock
681 viewFrame:viewOrigFrame
682 convertedFrame:convertedViewFrame];
683 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
684 OCMStub([mockTraitCollection userInterfaceIdiom]).andReturn(UIUserInterfaceIdiomPad);
685 OCMStub([mockTraitCollection horizontalSizeClass]).andReturn(UIUserInterfaceSizeClassCompact);
686 OCMStub([mockTraitCollection verticalSizeClass]).andReturn(UIUserInterfaceSizeClassRegular);
687 OCMStub([mockView traitCollection]).andReturn(mockTraitCollection);
691 XCTAssertTrue(adjustment == 20);
694- (void)testCalculateKeyboardInset {
701 UIScreen* screen = [
self setUpMockScreen];
702 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
704 CGFloat screenWidth = screen.bounds.size.width;
705 CGFloat screenHeight = screen.bounds.size.height;
706 CGRect viewOrigFrame = CGRectMake(0, 0, 320, screenHeight - 40);
707 CGRect convertedViewFrame = CGRectMake(20, 20, 320, screenHeight - 40);
708 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
710 [
self setUpMockView:viewControllerMock
712 viewFrame:viewOrigFrame
713 convertedFrame:convertedViewFrame];
717 XCTAssertTrue(
inset == 300 * screen.scale);
720- (void)testHandleKeyboardNotification {
727 UIScreen* screen = [
self setUpMockScreen];
728 CGFloat screenWidth = screen.bounds.size.width;
729 CGFloat screenHeight = screen.bounds.size.height;
730 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
731 CGRect viewFrame = screen.bounds;
733 NSNotification* notification =
734 [NSNotification notificationWithName:UIKeyboardWillShowNotification
737 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
738 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
739 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
742 [
self setUpMockView:viewControllerMock
745 convertedFrame:viewFrame];
747 XCTestExpectation*
expectation = [
self expectationWithDescription:@"update viewport"];
748 OCMStub([viewControllerMock updateViewportMetricsIfNeeded]).andDo(^(NSInvocation* invocation) {
749 [expectation fulfill];
754 OCMVerify([viewControllerMock startKeyBoardAnimation:0.25]);
755 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
758- (void)testEnsureBottomInsetIsZeroWhenKeyboardDismissed {
766 CGRect keyboardFrame = CGRectZero;
768 NSNotification* fakeNotification =
769 [NSNotification notificationWithName:UIKeyboardWillHideNotification
772 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
773 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
774 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
782- (void)testEnsureViewportMetricsWillInvokeAndDisplayLinkWillInvalidateInViewDidDisappear {
789 [viewControllerMock viewDidDisappear:YES];
790 OCMVerify([viewControllerMock ensureViewportMetricsIsCorrect]);
791 OCMVerify([viewControllerMock invalidateKeyboardAnimationVSyncClient]);
794- (void)testViewDidDisappearDoesntPauseEngineWhenNotTheViewController {
802 id viewControllerMock = OCMPartialMock(viewControllerA);
803 OCMStub([viewControllerMock surfaceUpdated:NO]);
806 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
807 OCMReject([viewControllerMock surfaceUpdated:[OCMArg
any]]);
810- (void)testAppWillTerminateViewDidDestroyTheEngine {
817 OCMStub([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
818 OCMStub([mockEngine destroyContext]);
820 OCMVerify([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
821 OCMVerify([mockEngine destroyContext]);
824- (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController {
835 OCMStub([viewControllerMock surfaceUpdated:NO]);
837 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
838 OCMVerify([viewControllerMock surfaceUpdated:NO]);
840 XCTAssertNil(weakViewController);
844 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear {
855 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear {
868 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
872 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear {
883 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear {
896 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
900 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear {
909 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
913 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear {
925 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
928- (void)testUpdateViewportMetricsIfNeeded_DoesntInvokeEngineWhenNotTheViewController {
941 OCMVerify(never(), [mockEngine updateViewportMetrics:viewportMetrics]);
944- (void)testUpdateViewportMetricsIfNeeded_DoesInvokeEngineWhenIsTheViewController {
952 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
954 OCMVerifyAll(mockEngine);
957- (void)testUpdateViewportMetricsIfNeeded_DoesNotInvokeEngineWhenShouldBeIgnoredDuringRotation {
964 UIScreen* screen = [
self setUpMockScreen];
965 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
968 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
969 OCMStub([mockCoordinator transitionDuration]).andReturn(0.5);
979- (void)testViewWillTransitionToSize_DoesDelayEngineCallIfNonZeroDuration {
986 UIScreen* screen = [
self setUpMockScreen];
987 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
991 NSTimeInterval transitionDuration = 0.5;
992 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
993 OCMStub([mockCoordinator transitionDuration]).andReturn(transitionDuration);
996 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1005 XCTWaiterResult
result = [XCTWaiter
1006 waitForExpectations:@[ [
self expectationWithDescription:@"Waiting for rotation duration"] ]
1007 timeout:transitionDuration];
1008 XCTAssertEqual(
result, XCTWaiterResultTimedOut);
1010 OCMVerifyAll(mockEngine);
1013- (void)testViewWillTransitionToSize_DoesNotDelayEngineCallIfZeroDuration {
1020 UIScreen* screen = [
self setUpMockScreen];
1021 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1025 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1026 OCMStub([mockCoordinator transitionDuration]).andReturn(0);
1029 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1035 OCMVerifyAll(mockEngine);
1038- (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController {
1049 UIView* view = viewControllerA.
view;
1050 XCTAssertNotNil(view);
1051 OCMVerify(never(), [mockEngine attachView]);
1054- (void)testViewDidLoadDoesInvokeEngineWhenIsTheViewController {
1063 XCTAssertNotNil(view);
1064 OCMVerify(
times(1), [mockEngine attachView]);
1067- (void)testViewDidLoadDoesntInvokeEngineAttachViewWhenEngineNeedsLaunch {
1078 XCTAssertNotNil(view);
1079 OCMVerify(never(), [mockEngine attachView]);
1082- (void)testSplashScreenViewRemoveNotCrash {
1091- (void)testInternalPluginsWeakPtrNotCrash {
1100 [(NSArray<id<FlutterKeyPrimaryResponder>>*)keyboardManager.
primaryResponders firstObject];
1101 sendEvent = [keyPrimaryResponder
sendEvent];
1105 sendEvent({}, nil, nil);
1110- (void)testInternalPluginsInvokeInViewDidLoad {
1120 XCTAssertNotNil(view);
1125- (void)testBinaryMessenger {
1129 XCTAssertNotNil(vc);
1130 id messenger = OCMProtocolMock(
@protocol(FlutterBinaryMessenger));
1131 OCMStub([
self.mockEngine binaryMessenger]).andReturn(messenger);
1133 OCMVerify([
self.mockEngine binaryMessenger]);
1136- (void)testViewControllerIsReleased {
1143 XCTAssertNil(weakViewController);
1146#pragma mark - Platform Brightness
1148- (void)testItReportsLightPlatformBrightnessByDefault {
1151 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1161 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1162 return [message[@"platformBrightness"] isEqualToString:@"light"];
1166 [settingsChannel stopMocking];
1169- (void)testItReportsPlatformBrightnessWhenViewWillAppear {
1174 OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel);
1183 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1184 return [message[@"platformBrightness"] isEqualToString:@"light"];
1188 [settingsChannel stopMocking];
1191- (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt {
1192 if (@available(iOS 13, *)) {
1200 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1201 id mockTraitCollection =
1202 [
self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
1211 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1214 [partialMockVC traitCollectionDidChange:nil];
1217 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1218 return [message[@"platformBrightness"] isEqualToString:@"dark"];
1222 [partialMockVC stopMocking];
1223 [settingsChannel stopMocking];
1224 [mockTraitCollection stopMocking];
1229- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style {
1230 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1231 OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style);
1232 return mockTraitCollection;
1235#pragma mark - Platform Contrast
1237- (void)testItReportsNormalPlatformContrastByDefault {
1238 if (@available(iOS 13, *)) {
1246 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1256 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1257 return [message[@"platformContrast"] isEqualToString:@"normal"];
1261 [settingsChannel stopMocking];
1264- (void)testItReportsPlatformContrastWhenViewWillAppear {
1265 if (@available(iOS 13, *)) {
1275 OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel);
1284 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1285 return [message[@"platformContrast"] isEqualToString:@"normal"];
1289 [settingsChannel stopMocking];
1292- (void)testItReportsHighContrastWhenTraitCollectionRequestsIt {
1293 if (@available(iOS 13, *)) {
1301 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1303 id mockTraitCollection = [
self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh];
1312 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1315 [partialMockVC traitCollectionDidChange:mockTraitCollection];
1318 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1319 return [message[@"platformContrast"] isEqualToString:@"high"];
1323 [partialMockVC stopMocking];
1324 [settingsChannel stopMocking];
1325 [mockTraitCollection stopMocking];
1328- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagNotSet {
1329 if (@available(iOS 13, *)) {
1339 OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(NO);
1342 int32_t
flags = [partialMockViewController accessibilityFlags];
1348- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagSet {
1349 if (@available(iOS 13, *)) {
1359 OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(YES);
1362 int32_t
flags = [partialMockViewController accessibilityFlags];
1368- (void)testAccessibilityPerformEscapePopsRoute {
1372 OCMStub([mockEngine navigationChannel]).andReturn(mockNavigationChannel);
1379 OCMVerify([mockNavigationChannel invokeMethod:
@"popRoute" arguments:nil]);
1381 [mockNavigationChannel stopMocking];
1384- (void)testPerformOrientationUpdateForcesOrientationChange {
1385 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1386 currentOrientation:UIInterfaceOrientationLandscapeLeft
1387 didChangeOrientation:YES
1388 resultingOrientation:UIInterfaceOrientationPortrait];
1390 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1391 currentOrientation:UIInterfaceOrientationLandscapeRight
1392 didChangeOrientation:YES
1393 resultingOrientation:UIInterfaceOrientationPortrait];
1395 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1396 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1397 didChangeOrientation:YES
1398 resultingOrientation:UIInterfaceOrientationPortrait];
1400 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1401 currentOrientation:UIInterfaceOrientationLandscapeLeft
1402 didChangeOrientation:YES
1403 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1405 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1406 currentOrientation:UIInterfaceOrientationLandscapeRight
1407 didChangeOrientation:YES
1408 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1410 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1411 currentOrientation:UIInterfaceOrientationPortrait
1412 didChangeOrientation:YES
1413 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1415 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1416 currentOrientation:UIInterfaceOrientationPortrait
1417 didChangeOrientation:YES
1418 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1420 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1421 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1422 didChangeOrientation:YES
1423 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1425 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1426 currentOrientation:UIInterfaceOrientationPortrait
1427 didChangeOrientation:YES
1428 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1430 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1431 currentOrientation:UIInterfaceOrientationLandscapeRight
1432 didChangeOrientation:YES
1433 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1435 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1436 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1437 didChangeOrientation:YES
1438 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1440 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1441 currentOrientation:UIInterfaceOrientationPortrait
1442 didChangeOrientation:YES
1443 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1445 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1446 currentOrientation:UIInterfaceOrientationLandscapeLeft
1447 didChangeOrientation:YES
1448 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1450 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1451 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1452 didChangeOrientation:YES
1453 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1455 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1456 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1457 didChangeOrientation:YES
1458 resultingOrientation:UIInterfaceOrientationPortrait];
1461- (void)testPerformOrientationUpdateDoesNotForceOrientationChange {
1462 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1463 currentOrientation:UIInterfaceOrientationPortrait
1464 didChangeOrientation:NO
1465 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1467 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1468 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1469 didChangeOrientation:NO
1470 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1472 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1473 currentOrientation:UIInterfaceOrientationLandscapeLeft
1474 didChangeOrientation:NO
1475 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1477 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1478 currentOrientation:UIInterfaceOrientationLandscapeRight
1479 didChangeOrientation:NO
1480 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1482 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1483 currentOrientation:UIInterfaceOrientationPortrait
1484 didChangeOrientation:NO
1485 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1487 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1488 currentOrientation:UIInterfaceOrientationLandscapeLeft
1489 didChangeOrientation:NO
1490 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1492 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1493 currentOrientation:UIInterfaceOrientationLandscapeRight
1494 didChangeOrientation:NO
1495 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1497 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1498 currentOrientation:UIInterfaceOrientationPortrait
1499 didChangeOrientation:NO
1500 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1502 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1503 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1504 didChangeOrientation:NO
1505 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1507 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1508 currentOrientation:UIInterfaceOrientationLandscapeLeft
1509 didChangeOrientation:NO
1510 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1512 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1513 currentOrientation:UIInterfaceOrientationLandscapeRight
1514 didChangeOrientation:NO
1515 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1517 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1518 currentOrientation:UIInterfaceOrientationLandscapeLeft
1519 didChangeOrientation:NO
1520 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1522 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1523 currentOrientation:UIInterfaceOrientationLandscapeRight
1524 didChangeOrientation:NO
1525 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1530- (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask
1531 currentOrientation:(UIInterfaceOrientation)currentOrientation
1532 didChangeOrientation:(
BOOL)didChange
1533 resultingOrientation:(UIInterfaceOrientation)resultingOrientation {
1534 id mockApplication = OCMClassMock([UIApplication
class]);
1538 __block __weak
id weakPreferences;
1544 if (@available(iOS 16.0, *)) {
1545 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1546 mockVC = OCMPartialMock(realVC);
1547 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1549 OCMReject([mockWindowScene requestGeometryUpdateWithPreferences:[OCMArg
any]
1550 errorHandler:[OCMArg
any]]);
1554 OCMExpect([mockWindowScene
1555 requestGeometryUpdateWithPreferences:[OCMArg checkWithBlock:^
BOOL(
1556 UIWindowSceneGeometryPreferencesIOS*
1558 weakPreferences = preferences;
1559 return preferences.interfaceOrientations == mask;
1561 errorHandler:[OCMArg any]]);
1563 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
1564 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockWindowScene]);
1566 deviceMock = OCMPartialMock([UIDevice currentDevice]);
1568 OCMReject([deviceMock setValue:[OCMArg
any] forKey:
@"orientation"]);
1570 OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:
@"orientation"]);
1572 if (@available(iOS 13.0, *)) {
1573 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1574 mockVC = OCMPartialMock(realVC);
1575 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1576 OCMStub(((UIWindowScene*)mockWindowScene).interfaceOrientation)
1577 .andReturn(currentOrientation);
1579 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
1580 OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation);
1585 if (@available(iOS 16.0, *)) {
1586 OCMVerifyAll(mockWindowScene);
1588 OCMVerifyAll(deviceMock);
1591 [mockWindowScene stopMocking];
1592 [deviceMock stopMocking];
1593 [mockApplication stopMocking];
1594 XCTAssertNil(weakPreferences);
1599- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast {
1600 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1601 OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast);
1602 return mockTraitCollection;
1605- (void)testWillDeallocNotification {
1607 [[XCTestExpectation alloc] initWithDescription:@"notification called"];
1614 [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
1616 queue:[NSOperationQueue mainQueue]
1617 usingBlock:^(NSNotification* _Nonnull note) {
1618 [expectation fulfill];
1620 XCTAssertNotNil(realVC);
1623 [
self waitForExpectations:@[ expectation ] timeout:1.0];
1626- (void)testReleasesKeyboardManagerOnDealloc {
1633 XCTAssertNotNil(weakKeyboardManager);
1638 XCTAssertNil(weakKeyboardManager);
1641- (void)testDoesntLoadViewInInit {
1648 XCTAssertFalse([realVC isViewLoaded],
@"shouldn't have loaded since it hasn't been shown");
1652- (void)testHideOverlay {
1660 [[NSNotificationCenter defaultCenter] postNotificationName:FlutterViewControllerHideHomeIndicator
1666- (void)testNotifyLowMemory {
1672 OCMStub([viewControllerMock surfaceUpdated:NO]);
1673 [viewController beginAppearanceTransition:NO animated:NO];
1674 [viewController endAppearanceTransition];
1678- (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback {
1679 NSMutableDictionary* replyMessage = [@{
1692 if (@available(iOS 13.4, *)) {
1700 .andCall(
self, @selector(sendMessage:reply:));
1701 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg
any]]).andReturn(YES);
1712 [vc
handlePressEvent:keyUpEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0)
1716 XCTAssert(
self.messageSent != nil);
1717 XCTAssert([
self.messageSent[
@"keymap"] isEqualToString:
@"ios"]);
1718 XCTAssert([
self.messageSent[
@"type"] isEqualToString:
@"keyup"]);
1719 XCTAssert([
self.messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
1720 XCTAssert([
self.messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
1721 XCTAssert([
self.messageSent[
@"characters"] isEqualToString:
@""]);
1722 XCTAssert([
self.messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@""]);