5#import <OCMock/OCMock.h>
6#import <XCTest/XCTest.h>
49 userData:(nullable
void*)userData;
60- (void)notifyLowMemory {
61 _didCallNotifyLowMemory = YES;
66 userData:(
void*)userData API_AVAILABLE(ios(9.0)) {
81- (
BOOL)createShell:(NSString*)entrypoint
82 libraryURI:(NSString*)libraryURI
83 initialRoute:(NSString*)initialRoute;
89@interface FlutterEngine (TestLowMemory)
90- (void)notifyLowMemory;
116@interface FlutterKeyboardManager (Tests)
117@property(nonatomic, retain, readonly)
118 NSMutableArray<id<FlutterKeyPrimaryResponder>>* primaryResponders;
121@interface FlutterEmbedderKeyResponder (Tests)
125@interface NSObject (Tests)
129@interface FlutterViewController (Tests)
131@property(nonatomic, assign)
double targetViewInsetBottom;
132@property(nonatomic, assign)
BOOL isKeyboardInOrTransitioningFromBackground;
133@property(nonatomic, assign)
BOOL keyboardAnimationIsShowing;
134@property(nonatomic, strong)
VSyncClient* keyboardAnimationVSyncClient;
135@property(nonatomic, strong)
VSyncClient* touchRateCorrectionVSyncClient;
136@property(nonatomic, assign)
BOOL awokenFromNib;
138- (void)createTouchRateCorrectionVSyncClientIfNeeded;
139- (void)surfaceUpdated:(
BOOL)appeared;
140- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences;
141- (void)handlePressEvent:(FlutterUIPressProxy*)press
142 nextAction:(
void (^)())next API_AVAILABLE(ios(13.4));
143- (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer;
144- (void)updateViewportMetricsIfNeeded;
145- (void)onUserSettingsChanged:(NSNotification*)notification;
146- (void)applicationWillTerminate:(NSNotification*)notification;
147- (void)goToApplicationLifecycle:(nonnull NSString*)state;
148- (void)handleKeyboardNotification:(NSNotification*)notification;
149- (CGFloat)calculateKeyboardInset:(CGRect)keyboardFrame keyboardMode:(
int)keyboardMode;
150- (
BOOL)shouldIgnoreKeyboardNotification:(NSNotification*)notification;
151- (FlutterKeyboardMode)calculateKeyboardAttachMode:(NSNotification*)notification;
152- (CGFloat)calculateMultitaskingAdjustment:(CGRect)screenRect keyboardFrame:(CGRect)keyboardFrame;
153- (void)startKeyBoardAnimation:(NSTimeInterval)duration;
154- (void)hideKeyboardImmediately;
155- (UIView*)keyboardAnimationView;
157- (void)setUpKeyboardSpringAnimationIfNeeded:(CAAnimation*)keyboardAnimation;
158- (void)setUpKeyboardAnimationVsyncClient:
160- (void)ensureViewportMetricsIsCorrect;
161- (void)invalidateKeyboardAnimationVSyncClient;
162- (void)addInternalPlugins;
163- (
flutter::PointerData)generatePointerDataForFake;
165 initialRoute:(nullable NSString*)initialRoute;
166- (void)applicationBecameActive:(NSNotification*)notification;
167- (void)applicationWillResignActive:(NSNotification*)notification;
168- (void)applicationWillTerminate:(NSNotification*)notification;
169- (void)applicationDidEnterBackground:(NSNotification*)notification;
170- (void)applicationWillEnterForeground:(NSNotification*)notification;
171- (void)sceneBecameActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
172- (void)sceneWillResignActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
173- (void)sceneWillDisconnect:(NSNotification*)notification API_AVAILABLE(ios(13.0));
174- (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
175- (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
176- (void)triggerTouchRateCorrectionIfNeeded:(NSSet*)touches;
180@property(nonatomic, strong)
id mockEngine;
181@property(nonatomic, strong)
id mockTextInputPlugin;
182@property(nonatomic, strong)
id messageSent;
183- (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback;
188@property(nonatomic, readwrite) UITouchPhase phase;
192@interface VSyncClient (Testing)
194- (CADisplayLink*)getDisplayLink;
204 self.messageSent = nil;
210 [
self.mockEngine stopMocking];
211 self.mockEngine = nil;
212 self.mockTextInputPlugin = nil;
213 self.messageSent = nil;
216- (
id)setUpMockScreen {
217 UIScreen* mockScreen = OCMClassMock([UIScreen
class]);
219 CGRect screenBounds = CGRectMake(0, 0, 1170, 2532);
220 OCMStub([mockScreen bounds]).andReturn(screenBounds);
221 CGFloat screenScale = 1;
222 OCMStub([mockScreen scale]).andReturn(screenScale);
228 screen:(UIScreen*)screen
229 viewFrame:(CGRect)viewFrame
230 convertedFrame:(CGRect)convertedFrame {
231 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
232 id mockView = OCMClassMock([UIView
class]);
233 OCMStub([mockView frame]).andReturn(viewFrame);
234 OCMStub([mockView convertRect:viewFrame toCoordinateSpace:[OCMArg any]])
235 .andReturn(convertedFrame);
236 OCMStub([viewControllerMock viewIfLoaded]).andReturn(mockView);
241- (void)testViewDidLoadWillInvokeCreateTouchRateCorrectionVSyncClient {
248 [viewControllerMock loadView];
249 [viewControllerMock viewDidLoad];
250 OCMVerify([viewControllerMock createTouchRateCorrectionVSyncClientIfNeeded]);
253- (void)testStartKeyboardAnimationWillInvokeSetupKeyboardSpringAnimationIfNeeded {
260 viewControllerMock.targetViewInsetBottom = 100;
261 [viewControllerMock startKeyBoardAnimation:0.25];
263 CAAnimation* keyboardAnimation =
264 [[viewControllerMock keyboardAnimationView].layer animationForKey:@"position"];
266 OCMVerify([viewControllerMock setUpKeyboardSpringAnimationIfNeeded:keyboardAnimation]);
269- (void)testSetupKeyboardSpringAnimationIfNeeded {
276 UIScreen* screen = [
self setUpMockScreen];
277 CGRect viewFrame = screen.bounds;
278 [
self setUpMockView:viewControllerMock
281 convertedFrame:viewFrame];
284 [viewControllerMock setUpKeyboardSpringAnimationIfNeeded:nil];
285 SpringAnimation* keyboardSpringAnimation = [viewControllerMock keyboardSpringAnimation];
286 XCTAssertTrue(keyboardSpringAnimation == nil);
289 CABasicAnimation* nonSpringAnimation = [CABasicAnimation animation];
290 nonSpringAnimation.duration = 1.0;
291 nonSpringAnimation.fromValue = [NSNumber numberWithFloat:0.0];
292 nonSpringAnimation.toValue = [NSNumber numberWithFloat:1.0];
293 nonSpringAnimation.keyPath =
@"position";
294 [viewControllerMock setUpKeyboardSpringAnimationIfNeeded:nonSpringAnimation];
295 keyboardSpringAnimation = [viewControllerMock keyboardSpringAnimation];
297 XCTAssertTrue(keyboardSpringAnimation == nil);
300 CASpringAnimation* springAnimation = [CASpringAnimation animation];
301 springAnimation.mass = 1.0;
302 springAnimation.stiffness = 100.0;
303 springAnimation.damping = 10.0;
304 springAnimation.keyPath =
@"position";
305 springAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
306 springAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
307 [viewControllerMock setUpKeyboardSpringAnimationIfNeeded:springAnimation];
308 keyboardSpringAnimation = [viewControllerMock keyboardSpringAnimation];
309 XCTAssertTrue(keyboardSpringAnimation != nil);
312- (void)testKeyboardAnimationIsShowingAndCompounding {
319 UIScreen* screen = [
self setUpMockScreen];
320 CGRect viewFrame = screen.bounds;
321 [
self setUpMockView:viewControllerMock
324 convertedFrame:viewFrame];
327 CGFloat screenHeight = screen.bounds.size.height;
328 CGFloat screenWidth = screen.bounds.size.height;
331 CGRect initialShowKeyboardBeginFrame = CGRectMake(0, screenHeight, screenWidth, 250);
332 CGRect initialShowKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
333 NSNotification* fakeNotification = [NSNotification
334 notificationWithName:UIKeyboardWillChangeFrameNotification
337 @"UIKeyboardFrameBeginUserInfoKey" : @(initialShowKeyboardBeginFrame),
338 @"UIKeyboardFrameEndUserInfoKey" : @(initialShowKeyboardEndFrame),
339 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
340 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
342 viewControllerMock.targetViewInsetBottom = 0;
343 [viewControllerMock handleKeyboardNotification:fakeNotification];
344 BOOL isShowingAnimation1 = viewControllerMock.keyboardAnimationIsShowing;
345 XCTAssertTrue(isShowingAnimation1);
348 CGRect compoundingShowKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
349 CGRect compoundingShowKeyboardEndFrame = CGRectMake(0, screenHeight - 500, screenWidth, 500);
350 fakeNotification = [NSNotification
351 notificationWithName:UIKeyboardWillChangeFrameNotification
354 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingShowKeyboardBeginFrame),
355 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingShowKeyboardEndFrame),
356 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
357 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
360 [viewControllerMock handleKeyboardNotification:fakeNotification];
361 BOOL isShowingAnimation2 = viewControllerMock.keyboardAnimationIsShowing;
362 XCTAssertTrue(isShowingAnimation2);
363 XCTAssertTrue(isShowingAnimation1 == isShowingAnimation2);
366 CGRect initialHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 500, screenWidth, 250);
367 CGRect initialHideKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
368 fakeNotification = [NSNotification
369 notificationWithName:UIKeyboardWillChangeFrameNotification
372 @"UIKeyboardFrameBeginUserInfoKey" : @(initialHideKeyboardBeginFrame),
373 @"UIKeyboardFrameEndUserInfoKey" : @(initialHideKeyboardEndFrame),
374 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
375 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
378 [viewControllerMock handleKeyboardNotification:fakeNotification];
379 BOOL isShowingAnimation3 = viewControllerMock.keyboardAnimationIsShowing;
380 XCTAssertFalse(isShowingAnimation3);
381 XCTAssertTrue(isShowingAnimation2 != isShowingAnimation3);
384 CGRect compoundingHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
385 CGRect compoundingHideKeyboardEndFrame = CGRectMake(0, screenHeight, screenWidth, 500);
386 fakeNotification = [NSNotification
387 notificationWithName:UIKeyboardWillChangeFrameNotification
390 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingHideKeyboardBeginFrame),
391 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingHideKeyboardEndFrame),
392 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
393 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
396 [viewControllerMock handleKeyboardNotification:fakeNotification];
397 BOOL isShowingAnimation4 = viewControllerMock.keyboardAnimationIsShowing;
398 XCTAssertFalse(isShowingAnimation4);
399 XCTAssertTrue(isShowingAnimation3 == isShowingAnimation4);
402- (void)testShouldIgnoreKeyboardNotification {
404 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
409 UIScreen* screen = [
self setUpMockScreen];
410 CGRect viewFrame = screen.bounds;
411 [
self setUpMockView:viewControllerMock
414 convertedFrame:viewFrame];
416 CGFloat screenWidth = screen.bounds.size.width;
417 CGFloat screenHeight = screen.bounds.size.height;
418 CGRect emptyKeyboard = CGRectZero;
419 CGRect zeroHeightKeyboard = CGRectMake(0, 0, screenWidth, 0);
420 CGRect validKeyboardEndFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
424 NSNotification* notification =
425 [NSNotification notificationWithName:UIKeyboardWillHideNotification
428 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
429 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
430 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
433 BOOL shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
434 XCTAssertTrue(shouldIgnore == NO);
438 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
441 @"UIKeyboardFrameEndUserInfoKey" : @(emptyKeyboard),
442 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
443 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
445 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
446 XCTAssertTrue(shouldIgnore == YES);
451 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
454 @"UIKeyboardFrameEndUserInfoKey" : @(zeroHeightKeyboard),
455 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
456 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
458 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
459 XCTAssertTrue(shouldIgnore == NO);
464 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
467 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
468 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
469 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
471 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
472 XCTAssertTrue(shouldIgnore == YES);
477 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
480 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
481 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
482 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
484 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
485 XCTAssertTrue(shouldIgnore == NO);
488- (void)testKeyboardAnimationWillNotCrashWhenEngineDestroyed {
494 [viewController setUpKeyboardAnimationVsyncClient:^(fml::TimePoint){
499- (void)testKeyboardAnimationWillWaitUIThreadVsync {
512 const int delayTime = 1;
513 [engine uiTaskRunner]->PostTask([] { sleep(delayTime); });
514 XCTestExpectation* expectation = [
self expectationWithDescription:@"keyboard animation callback"];
516 __block CFTimeInterval fulfillTime;
518 fulfillTime = CACurrentMediaTime();
519 [expectation fulfill];
521 CFTimeInterval startTime = CACurrentMediaTime();
522 [viewController setUpKeyboardAnimationVsyncClient:callback];
523 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
524 XCTAssertTrue(fulfillTime - startTime > delayTime);
527- (void)testCalculateKeyboardAttachMode {
529 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
535 UIScreen* screen = [
self setUpMockScreen];
536 CGRect viewFrame = screen.bounds;
537 [
self setUpMockView:viewControllerMock
540 convertedFrame:viewFrame];
542 CGFloat screenWidth = screen.bounds.size.width;
543 CGFloat screenHeight = screen.bounds.size.height;
546 CGRect keyboardFrame = CGRectZero;
547 NSNotification* notification =
548 [NSNotification notificationWithName:UIKeyboardWillHideNotification
551 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
552 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
553 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
555 FlutterKeyboardMode keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
556 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
559 keyboardFrame = CGRectZero;
560 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
563 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
564 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
565 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
567 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
568 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
571 keyboardFrame = CGRectMake(0, 0, screenWidth, 0);
572 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
575 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
576 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
577 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
579 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
580 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
583 keyboardFrame = CGRectMake(0, 0, 320, 320);
584 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
587 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
588 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
589 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
591 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
592 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
595 keyboardFrame = CGRectMake(0, 0, screenWidth, 320);
596 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
599 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
600 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
601 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
603 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
604 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
607 keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
608 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
611 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
612 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
613 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
615 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
616 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
619 CGFloat longDecimalHeight = 320.666666666666666;
620 keyboardFrame = CGRectMake(0, screenHeight - longDecimalHeight, screenWidth, longDecimalHeight);
621 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
624 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
625 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
626 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
628 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
629 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
632 keyboardFrame = CGRectMake(0, screenHeight - .0000001, screenWidth, longDecimalHeight);
633 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
636 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
637 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
638 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
640 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
641 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
644 keyboardFrame = CGRectMake(0, screenHeight, screenWidth, 320);
645 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
648 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
649 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
650 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
652 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
653 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
656- (void)testCalculateMultitaskingAdjustment {
658 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
664 UIScreen* screen = [
self setUpMockScreen];
665 CGFloat screenWidth = screen.bounds.size.width;
666 CGFloat screenHeight = screen.bounds.size.height;
667 CGRect screenRect = screen.bounds;
668 CGRect viewOrigFrame = CGRectMake(0, 0, 320, screenHeight - 40);
669 CGRect convertedViewFrame = CGRectMake(20, 20, 320, screenHeight - 40);
670 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
671 id mockView = [
self setUpMockView:viewControllerMock
673 viewFrame:viewOrigFrame
674 convertedFrame:convertedViewFrame];
675 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
676 OCMStub([mockTraitCollection userInterfaceIdiom]).andReturn(UIUserInterfaceIdiomPad);
677 OCMStub([mockTraitCollection horizontalSizeClass]).andReturn(UIUserInterfaceSizeClassCompact);
678 OCMStub([mockTraitCollection verticalSizeClass]).andReturn(UIUserInterfaceSizeClassRegular);
679 OCMStub([mockView traitCollection]).andReturn(mockTraitCollection);
681 CGFloat adjustment = [viewControllerMock calculateMultitaskingAdjustment:screenRect
682 keyboardFrame:keyboardFrame];
683 XCTAssertTrue(adjustment == 20);
686- (void)testCalculateKeyboardInset {
688 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
693 UIScreen* screen = [
self setUpMockScreen];
694 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
696 CGFloat screenWidth = screen.bounds.size.width;
697 CGFloat screenHeight = screen.bounds.size.height;
698 CGRect viewOrigFrame = CGRectMake(0, 0, 320, screenHeight - 40);
699 CGRect convertedViewFrame = CGRectMake(20, 20, 320, screenHeight - 40);
700 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
702 [
self setUpMockView:viewControllerMock
704 viewFrame:viewOrigFrame
705 convertedFrame:convertedViewFrame];
707 CGFloat inset = [viewControllerMock calculateKeyboardInset:keyboardFrame
708 keyboardMode:FlutterKeyboardModeDocked];
709 XCTAssertTrue(inset == 300 * screen.scale);
712- (void)testHandleKeyboardNotification {
719 UIScreen* screen = [
self setUpMockScreen];
720 CGFloat screenWidth = screen.bounds.size.width;
721 CGFloat screenHeight = screen.bounds.size.height;
722 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
723 CGRect viewFrame = screen.bounds;
725 NSNotification* notification =
726 [NSNotification notificationWithName:UIKeyboardWillShowNotification
729 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
730 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
731 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
734 [
self setUpMockView:viewControllerMock
737 convertedFrame:viewFrame];
738 viewControllerMock.targetViewInsetBottom = 0;
739 XCTestExpectation* expectation = [
self expectationWithDescription:@"update viewport"];
740 OCMStub([viewControllerMock updateViewportMetricsIfNeeded]).andDo(^(NSInvocation* invocation) {
741 [expectation fulfill];
744 [viewControllerMock handleKeyboardNotification:notification];
745 XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 320 * screen.scale);
746 OCMVerify([viewControllerMock startKeyBoardAnimation:0.25]);
747 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
750- (void)testEnsureBottomInsetIsZeroWhenKeyboardDismissed {
752 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
758 CGRect keyboardFrame = CGRectZero;
760 NSNotification* fakeNotification =
761 [NSNotification notificationWithName:UIKeyboardWillHideNotification
764 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
765 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
766 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
769 viewControllerMock.targetViewInsetBottom = 10;
770 [viewControllerMock handleKeyboardNotification:fakeNotification];
771 XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 0);
774- (void)testStopKeyBoardAnimationWhenReceivedWillHideNotificationAfterWillShowNotification {
783 UIScreen* screen = [
self setUpMockScreen];
784 CGRect viewFrame = screen.bounds;
785 [
self setUpMockView:viewControllerMock
788 convertedFrame:viewFrame];
789 viewControllerMock.targetViewInsetBottom = 0;
791 CGFloat screenHeight = screen.bounds.size.height;
792 CGFloat screenWidth = screen.bounds.size.height;
793 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
797 NSNotification* fakeShowNotification =
798 [NSNotification notificationWithName:UIKeyboardWillShowNotification
801 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
802 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
803 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
805 [viewControllerMock handleKeyboardNotification:fakeShowNotification];
806 XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 320 * screen.scale);
809 NSNotification* fakeHideNotification =
810 [NSNotification notificationWithName:UIKeyboardWillHideNotification
813 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
814 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.0),
815 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
817 [viewControllerMock handleKeyboardNotification:fakeHideNotification];
818 XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 0);
821 XCTAssertNil(viewControllerMock.keyboardAnimationView);
822 XCTAssertNil(viewControllerMock.keyboardSpringAnimation);
825- (void)testEnsureViewportMetricsWillInvokeAndDisplayLinkWillInvalidateInViewDidDisappear {
827 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
832 [viewControllerMock viewDidDisappear:YES];
833 OCMVerify([viewControllerMock ensureViewportMetricsIsCorrect]);
834 OCMVerify([viewControllerMock invalidateKeyboardAnimationVSyncClient]);
837- (void)testViewDidDisappearDoesntPauseEngineWhenNotTheViewController {
845 id viewControllerMock = OCMPartialMock(viewControllerA);
846 OCMStub([viewControllerMock surfaceUpdated:NO]);
848 [viewControllerA viewDidDisappear:NO];
849 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
850 OCMReject([viewControllerMock surfaceUpdated:[OCMArg any]]);
853- (void)testAppWillTerminateViewDidDestroyTheEngine {
855 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
860 OCMStub([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
861 OCMStub([mockEngine destroyContext]);
862 [viewController applicationWillTerminate:nil];
863 OCMVerify([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
864 OCMVerify([mockEngine destroyContext]);
867- (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController {
878 OCMStub([viewControllerMock surfaceUpdated:NO]);
879 [viewController viewDidDisappear:NO];
880 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
881 OCMVerify([viewControllerMock surfaceUpdated:NO]);
883 XCTAssertNil(weakViewController);
887 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear {
889 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
893 [viewController viewWillAppear:YES];
898 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear {
900 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
910 [viewControllerA viewWillAppear:YES];
911 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
915 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear {
917 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
921 [viewController viewDidAppear:YES];
926 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear {
928 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
938 [viewControllerA viewDidAppear:YES];
939 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
943 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear {
951 [viewController viewWillDisappear:NO];
952 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
956 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear {
967 [viewControllerA viewDidDisappear:NO];
968 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
971- (void)testUpdateViewportMetricsIfNeeded_DoesntInvokeEngineWhenNotTheViewController {
973 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
982 [viewControllerA updateViewportMetricsIfNeeded];
984 OCMVerify(never(), [mockEngine updateViewportMetrics:viewportMetrics]);
987- (void)testUpdateViewportMetricsIfNeeded_DoesInvokeEngineWhenIsTheViewController {
989 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
995 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
996 [viewController updateViewportMetricsIfNeeded];
997 OCMVerifyAll(mockEngine);
1000- (void)testUpdateViewportMetricsIfNeeded_DoesNotInvokeEngineWhenShouldBeIgnoredDuringRotation {
1002 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1007 UIScreen* screen = [
self setUpMockScreen];
1008 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1011 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1012 OCMStub([mockCoordinator transitionDuration]).andReturn(0.5);
1015 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1017 [viewController updateViewportMetricsIfNeeded];
1022- (void)testViewWillTransitionToSize_DoesDelayEngineCallIfNonZeroDuration {
1024 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1029 UIScreen* screen = [
self setUpMockScreen];
1030 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1034 NSTimeInterval transitionDuration = 0.5;
1035 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1036 OCMStub([mockCoordinator transitionDuration]).andReturn(transitionDuration);
1039 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1041 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1043 [viewController updateViewportMetricsIfNeeded];
1048 XCTWaiterResult result = [XCTWaiter
1049 waitForExpectations:@[ [
self expectationWithDescription:@"Waiting for rotation duration"] ]
1050 timeout:transitionDuration];
1051 XCTAssertEqual(result, XCTWaiterResultTimedOut);
1053 OCMVerifyAll(mockEngine);
1056- (void)testViewWillTransitionToSize_DoesNotDelayEngineCallIfZeroDuration {
1058 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1063 UIScreen* screen = [
self setUpMockScreen];
1064 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1068 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1069 OCMStub([mockCoordinator transitionDuration]).andReturn(0);
1072 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1075 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1076 [viewController updateViewportMetricsIfNeeded];
1078 OCMVerifyAll(mockEngine);
1081- (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController {
1083 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1092 UIView*
view = viewControllerA.view;
1093 XCTAssertNotNil(
view);
1094 OCMVerify(never(), [mockEngine attachView]);
1097- (void)testViewDidLoadDoesInvokeEngineWhenIsTheViewController {
1099 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1106 XCTAssertNotNil(
view);
1107 OCMVerify(times(1), [mockEngine attachView]);
1110- (void)testViewDidLoadDoesntInvokeEngineAttachViewWhenEngineNeedsLaunch {
1112 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1118 [viewController sharedSetupWithProject:nil initialRoute:nil];
1121 XCTAssertNotNil(
view);
1122 OCMVerify(never(), [mockEngine attachView]);
1125- (void)testSplashScreenViewRemoveNotCrash {
1130 [flutterViewController setSplashScreenView:[[UIView alloc] init]];
1131 [flutterViewController setSplashScreenView:nil];
1134- (void)testInternalPluginsWeakPtrNotCrash {
1140 [vc addInternalPlugins];
1143 [(NSArray<id<FlutterKeyPrimaryResponder>>*)keyboardManager.primaryResponders firstObject];
1144 sendEvent = [keyPrimaryResponder sendEvent];
1148 sendEvent({}, nil, nil);
1153- (void)testInternalPluginsInvokeInViewDidLoad {
1155 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1163 XCTAssertNotNil(
view);
1164 [viewController viewDidLoad];
1168- (void)testBinaryMessenger {
1172 XCTAssertNotNil(vc);
1173 id messenger = OCMProtocolMock(
@protocol(FlutterBinaryMessenger));
1174 OCMStub([
self.mockEngine binaryMessenger]).andReturn(messenger);
1176 OCMVerify([
self.mockEngine binaryMessenger]);
1179- (void)testViewControllerIsReleased {
1181 __weak UIView* weakView;
1190 [viewController loadView];
1191 [viewController viewDidLoad];
1195 XCTAssertNil(weakViewController);
1196 XCTAssertNil(weakView);
1199#pragma mark - Platform Brightness
1201- (void)testItReportsLightPlatformBrightnessByDefault {
1204 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1211 [vc traitCollectionDidChange:nil];
1214 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1215 return [message[@"platformBrightness"] isEqualToString:@"light"];
1219 [settingsChannel stopMocking];
1222- (void)testItReportsPlatformBrightnessWhenViewWillAppear {
1226 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1227 OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel);
1233 [vc viewWillAppear:false];
1236 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1237 return [message[@"platformBrightness"] isEqualToString:@"light"];
1241 [settingsChannel stopMocking];
1244- (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt {
1247 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1248 id mockTraitCollection =
1249 [
self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
1258 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1261 [partialMockVC traitCollectionDidChange:nil];
1264 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1265 return [message[@"platformBrightness"] isEqualToString:@"dark"];
1269 [partialMockVC stopMocking];
1270 [settingsChannel stopMocking];
1271 [mockTraitCollection stopMocking];
1276- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style {
1277 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1278 OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style);
1279 return mockTraitCollection;
1282#pragma mark - Platform Contrast
1284- (void)testItReportsNormalPlatformContrastByDefault {
1287 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1294 [vc traitCollectionDidChange:nil];
1297 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1298 return [message[@"platformContrast"] isEqualToString:@"normal"];
1302 [settingsChannel stopMocking];
1305- (void)testItReportsPlatformContrastWhenViewWillAppear {
1307 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1311 OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel);
1317 [vc viewWillAppear:false];
1320 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1321 return [message[@"platformContrast"] isEqualToString:@"normal"];
1325 [settingsChannel stopMocking];
1328- (void)testItReportsHighContrastWhenTraitCollectionRequestsIt {
1331 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1333 id mockTraitCollection = [
self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh];
1342 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1345 [partialMockVC traitCollectionDidChange:mockTraitCollection];
1348 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1349 return [message[@"platformContrast"] isEqualToString:@"high"];
1353 [partialMockVC stopMocking];
1354 [settingsChannel stopMocking];
1355 [mockTraitCollection stopMocking];
1358- (void)testItReportsAlwaysUsed24HourFormat {
1361 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1367 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(YES);
1368 OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1369 return [message[@"alwaysUse24HourFormat"] isEqual:@(YES)];
1371 [vc onUserSettingsChanged:nil];
1372 [mockHourFormat stopMocking];
1376 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(NO);
1377 OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1378 return [message[@"alwaysUse24HourFormat"] isEqual:@(NO)];
1380 [vc onUserSettingsChanged:nil];
1381 [mockHourFormat stopMocking];
1384 [settingsChannel stopMocking];
1387- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagNotSet {
1392 OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(NO);
1395 int32_t flags = [partialMockViewController accessibilityFlags];
1401- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagSet {
1406 OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(YES);
1409 int32_t flags = [partialMockViewController accessibilityFlags];
1415- (void)testAccessibilityPerformEscapePopsRoute {
1417 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1419 OCMStub([mockEngine navigationChannel]).andReturn(mockNavigationChannel);
1426 OCMVerify([mockNavigationChannel invokeMethod:
@"popRoute" arguments:nil]);
1428 [mockNavigationChannel stopMocking];
1431- (void)testPerformOrientationUpdateForcesOrientationChange {
1432 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1433 currentOrientation:UIInterfaceOrientationLandscapeLeft
1434 didChangeOrientation:YES
1435 resultingOrientation:UIInterfaceOrientationPortrait];
1437 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1438 currentOrientation:UIInterfaceOrientationLandscapeRight
1439 didChangeOrientation:YES
1440 resultingOrientation:UIInterfaceOrientationPortrait];
1442 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1443 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1444 didChangeOrientation:YES
1445 resultingOrientation:UIInterfaceOrientationPortrait];
1447 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1448 currentOrientation:UIInterfaceOrientationLandscapeLeft
1449 didChangeOrientation:YES
1450 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1452 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1453 currentOrientation:UIInterfaceOrientationLandscapeRight
1454 didChangeOrientation:YES
1455 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1457 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1458 currentOrientation:UIInterfaceOrientationPortrait
1459 didChangeOrientation:YES
1460 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1462 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1463 currentOrientation:UIInterfaceOrientationPortrait
1464 didChangeOrientation:YES
1465 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1467 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1468 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1469 didChangeOrientation:YES
1470 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1472 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1473 currentOrientation:UIInterfaceOrientationPortrait
1474 didChangeOrientation:YES
1475 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1477 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1478 currentOrientation:UIInterfaceOrientationLandscapeRight
1479 didChangeOrientation:YES
1480 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1482 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1483 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1484 didChangeOrientation:YES
1485 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1487 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1488 currentOrientation:UIInterfaceOrientationPortrait
1489 didChangeOrientation:YES
1490 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1492 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1493 currentOrientation:UIInterfaceOrientationLandscapeLeft
1494 didChangeOrientation:YES
1495 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1497 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1498 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1499 didChangeOrientation:YES
1500 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1502 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1503 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1504 didChangeOrientation:YES
1505 resultingOrientation:UIInterfaceOrientationPortrait];
1508- (void)testPerformOrientationUpdateDoesNotForceOrientationChange {
1509 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1510 currentOrientation:UIInterfaceOrientationPortrait
1511 didChangeOrientation:NO
1512 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1514 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1515 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1516 didChangeOrientation:NO
1517 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1519 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1520 currentOrientation:UIInterfaceOrientationLandscapeLeft
1521 didChangeOrientation:NO
1522 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1524 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1525 currentOrientation:UIInterfaceOrientationLandscapeRight
1526 didChangeOrientation:NO
1527 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1529 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1530 currentOrientation:UIInterfaceOrientationPortrait
1531 didChangeOrientation:NO
1532 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1534 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1535 currentOrientation:UIInterfaceOrientationLandscapeLeft
1536 didChangeOrientation:NO
1537 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1539 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1540 currentOrientation:UIInterfaceOrientationLandscapeRight
1541 didChangeOrientation:NO
1542 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1544 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1545 currentOrientation:UIInterfaceOrientationPortrait
1546 didChangeOrientation:NO
1547 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1549 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1550 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1551 didChangeOrientation:NO
1552 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1554 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1555 currentOrientation:UIInterfaceOrientationLandscapeLeft
1556 didChangeOrientation:NO
1557 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1559 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1560 currentOrientation:UIInterfaceOrientationLandscapeRight
1561 didChangeOrientation:NO
1562 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1564 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1565 currentOrientation:UIInterfaceOrientationLandscapeLeft
1566 didChangeOrientation:NO
1567 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1569 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1570 currentOrientation:UIInterfaceOrientationLandscapeRight
1571 didChangeOrientation:NO
1572 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1577- (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask
1578 currentOrientation:(UIInterfaceOrientation)currentOrientation
1579 didChangeOrientation:(
BOOL)didChange
1580 resultingOrientation:(UIInterfaceOrientation)resultingOrientation {
1581 id mockApplication = OCMClassMock([UIApplication
class]);
1585 __block __weak
id weakPreferences;
1591 if (@available(iOS 16.0, *)) {
1592 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1593 mockVC = OCMPartialMock(realVC);
1594 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1595 if (realVC.supportedInterfaceOrientations == mask) {
1596 OCMReject([mockWindowScene requestGeometryUpdateWithPreferences:[OCMArg any]
1597 errorHandler:[OCMArg any]]);
1601 OCMExpect([mockWindowScene
1602 requestGeometryUpdateWithPreferences:[OCMArg checkWithBlock:^
BOOL(
1603 UIWindowSceneGeometryPreferencesIOS*
1605 weakPreferences = preferences;
1606 return preferences.interfaceOrientations == mask;
1608 errorHandler:[OCMArg any]]);
1610 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
1611 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockWindowScene]);
1613 deviceMock = OCMPartialMock([UIDevice currentDevice]);
1615 OCMReject([deviceMock setValue:[OCMArg any] forKey:
@"orientation"]);
1617 OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:
@"orientation"]);
1619 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1620 mockVC = OCMPartialMock(realVC);
1621 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1622 OCMStub(((UIWindowScene*)mockWindowScene).interfaceOrientation).andReturn(currentOrientation);
1625 [realVC performOrientationUpdate:mask];
1626 if (@available(iOS 16.0, *)) {
1627 OCMVerifyAll(mockWindowScene);
1629 OCMVerifyAll(deviceMock);
1632 [mockWindowScene stopMocking];
1633 [deviceMock stopMocking];
1634 [mockApplication stopMocking];
1635 XCTAssertNil(weakPreferences);
1640- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast {
1641 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1642 OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast);
1643 return mockTraitCollection;
1646- (void)testWillDeallocNotification {
1647 XCTestExpectation* expectation =
1648 [[XCTestExpectation alloc] initWithDescription:@"notification called"];
1655 [NSNotificationCenter.defaultCenter addObserverForName:FlutterViewControllerWillDealloc
1657 queue:[NSOperationQueue mainQueue]
1658 usingBlock:^(NSNotification* _Nonnull note) {
1659 [expectation fulfill];
1661 XCTAssertNotNil(realVC);
1664 [
self waitForExpectations:@[ expectation ] timeout:1.0];
1667- (void)testReleasesKeyboardManagerOnDealloc {
1672 [viewController addInternalPlugins];
1674 XCTAssertNotNil(weakKeyboardManager);
1675 [viewController deregisterNotifications];
1679 XCTAssertNil(weakKeyboardManager);
1682- (void)testDoesntLoadViewInInit {
1685 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
1689 XCTAssertFalse([realVC isViewLoaded],
@"shouldn't have loaded since it hasn't been shown");
1693- (void)testHideOverlay {
1696 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
1700 XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden,
@"");
1701 [NSNotificationCenter.defaultCenter postNotificationName:FlutterViewControllerHideHomeIndicator
1703 XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden,
@"");
1707- (void)testNotifyLowMemory {
1713 OCMStub([viewControllerMock surfaceUpdated:NO]);
1714 [viewController beginAppearanceTransition:NO animated:NO];
1715 [viewController endAppearanceTransition];
1719- (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback {
1720 NSMutableDictionary* replyMessage = [@{
1733 if (@available(iOS 13.4, *)) {
1740 OCMStub([mockEngine.
keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
1741 .andCall(
self, @selector(sendMessage:reply:));
1742 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
1751 [vc addInternalPlugins];
1753 [vc handlePressEvent:keyUpEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0)
1757 XCTAssert(
self.messageSent != nil);
1758 XCTAssert([
self.messageSent[
@"keymap"] isEqualToString:
@"ios"]);
1759 XCTAssert([
self.messageSent[
@"type"] isEqualToString:
@"keyup"]);
1760 XCTAssert([
self.messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
1761 XCTAssert([
self.messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
1762 XCTAssert([
self.messageSent[
@"characters"] isEqualToString:
@""]);
1763 XCTAssert([
self.messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@""]);
1764 [vc deregisterNotifications];
1768 if (@available(iOS 13.4, *)) {
1776 OCMStub([mockEngine.
keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
1777 .andCall(
self, @selector(sendMessage:reply:));
1778 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
1786 [vc addInternalPlugins];
1788 [vc handlePressEvent:keyDownEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0f, "A",
1793 XCTAssert(
self.messageSent != nil);
1794 XCTAssert([
self.messageSent[
@"keymap"] isEqualToString:
@"ios"]);
1795 XCTAssert([
self.messageSent[
@"type"] isEqualToString:
@"keydown"]);
1796 XCTAssert([
self.messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
1797 XCTAssert([
self.messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
1798 XCTAssert([
self.messageSent[
@"characters"] isEqualToString:
@"A"]);
1799 XCTAssert([
self.messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@"a"]);
1800 [vc deregisterNotifications];
1805 if (@available(iOS 13.4, *)) {
1811 OCMStub([keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
1812 .andCall(
self, @selector(sendMessage:reply:));
1813 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
1814 OCMStub([
self.mockEngine keyEventChannel]).andReturn(keyEventChannel);
1822 [vc addInternalPlugins];
1824 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseStationary, UIKeyboardHIDUsageKeyboardA,
1825 UIKeyModifierShift, 123.0)
1828 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseCancelled, UIKeyboardHIDUsageKeyboardA,
1829 UIKeyModifierShift, 123.0)
1832 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseChanged, UIKeyboardHIDUsageKeyboardA,
1833 UIKeyModifierShift, 123.0)
1837 XCTAssert(
self.messageSent == nil);
1838 OCMVerify(never(), [keyEventChannel sendMessage:[OCMArg any]]);
1839 [vc deregisterNotifications];
1843 if (@available(iOS 13.4, *)) {
1852 XCTAssertNotNil(vc);
1853 UIView*
view = vc.view;
1854 XCTAssertNotNil(
view);
1855 NSArray* gestureRecognizers =
view.gestureRecognizers;
1856 XCTAssertNotNil(gestureRecognizers);
1859 for (
id gesture in gestureRecognizers) {
1860 if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
1865 XCTAssertTrue(found);
1869 if (@available(iOS 13.4, *)) {
1878 XCTAssertNotNil(vc);
1880 id mockPanGestureRecognizer = OCMClassMock([UIPanGestureRecognizer
class]);
1881 XCTAssertNotNil(mockPanGestureRecognizer);
1883 [vc discreteScrollEvent:mockPanGestureRecognizer];
1886 [[mockPanGestureRecognizer verify] locationInView:[OCMArg any]];
1887 [[[
self.mockEngine verify] ignoringNonObjectArgs]
1888 dispatchPointerDataPacket:std::make_unique<flutter::PointerDataPacket>(0)];
1891- (void)testFakeEventTimeStamp {
1895 XCTAssertNotNil(vc);
1898 int64_t current_micros = [[NSProcessInfo processInfo] systemUptime] * 1000 * 1000;
1899 int64_t interval_micros = current_micros - pointer_data.time_stamp;
1900 const int64_t tolerance_millis = 2;
1901 XCTAssertTrue(interval_micros / 1000 < tolerance_millis,
1902 @"PointerData.time_stamp should be equal to NSProcessInfo.systemUptime");
1905- (void)testSplashScreenViewCanSetNil {
1908 [flutterViewController setSplashScreenView:nil];
1911- (void)testLifeCycleNotificationApplicationBecameActive {
1916 UIWindow*
window = [[UIWindow alloc] init];
1917 [window addSubview:flutterViewController.view];
1918 flutterViewController.view.bounds = CGRectMake(0, 0, 100, 100);
1919 [flutterViewController viewDidLayoutSubviews];
1920 NSNotification* sceneNotification =
1921 [NSNotification notificationWithName:UISceneDidActivateNotification object:nil userInfo:nil];
1922 NSNotification* applicationNotification =
1923 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
1926 id mockVC = OCMPartialMock(flutterViewController);
1927 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
1928 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
1929 OCMReject([mockVC sceneBecameActive:[OCMArg any]]);
1930 OCMVerify([mockVC applicationBecameActive:[OCMArg any]]);
1931 XCTAssertFalse(flutterViewController.isKeyboardInOrTransitioningFromBackground);
1932 OCMVerify([mockVC surfaceUpdated:YES]);
1933 XCTestExpectation* timeoutApplicationLifeCycle =
1934 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
1935 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
1936 dispatch_get_main_queue(), ^{
1937 [timeoutApplicationLifeCycle fulfill];
1938 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
1939 [flutterViewController deregisterNotifications];
1941 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
1944- (void)testLifeCycleNotificationSceneBecameActive {
1945 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
1946 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
1947 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
1950 [engine runWithEntrypoint:nil];
1953 UIWindow*
window = [[UIWindow alloc] init];
1954 [window addSubview:flutterViewController.view];
1955 flutterViewController.view.bounds = CGRectMake(0, 0, 100, 100);
1956 [flutterViewController viewDidLayoutSubviews];
1957 NSNotification* sceneNotification =
1958 [NSNotification notificationWithName:UISceneDidActivateNotification object:nil userInfo:nil];
1959 NSNotification* applicationNotification =
1960 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
1963 id mockVC = OCMPartialMock(flutterViewController);
1964 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
1965 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
1966 OCMVerify([mockVC sceneBecameActive:[OCMArg any]]);
1967 OCMReject([mockVC applicationBecameActive:[OCMArg any]]);
1968 XCTAssertFalse(flutterViewController.isKeyboardInOrTransitioningFromBackground);
1969 OCMVerify([mockVC surfaceUpdated:YES]);
1970 XCTestExpectation* timeoutApplicationLifeCycle =
1971 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
1972 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
1973 dispatch_get_main_queue(), ^{
1974 [timeoutApplicationLifeCycle fulfill];
1975 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
1976 [flutterViewController deregisterNotifications];
1978 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
1979 [mockBundle stopMocking];
1982- (void)testLifeCycleNotificationApplicationWillResignActive {
1987 NSNotification* sceneNotification =
1988 [NSNotification notificationWithName:UISceneWillDeactivateNotification
1991 NSNotification* applicationNotification =
1992 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
1995 id mockVC = OCMPartialMock(flutterViewController);
1996 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
1997 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
1998 OCMReject([mockVC sceneWillResignActive:[OCMArg any]]);
1999 OCMVerify([mockVC applicationWillResignActive:[OCMArg any]]);
2000 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2001 [flutterViewController deregisterNotifications];
2004- (void)testLifeCycleNotificationSceneWillResignActive {
2005 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2006 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2007 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2010 [engine runWithEntrypoint:nil];
2013 NSNotification* sceneNotification =
2014 [NSNotification notificationWithName:UISceneWillDeactivateNotification
2017 NSNotification* applicationNotification =
2018 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2021 id mockVC = OCMPartialMock(flutterViewController);
2022 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2023 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2024 OCMVerify([mockVC sceneWillResignActive:[OCMArg any]]);
2025 OCMReject([mockVC applicationWillResignActive:[OCMArg any]]);
2026 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2027 [flutterViewController deregisterNotifications];
2028 [mockBundle stopMocking];
2031- (void)testLifeCycleNotificationApplicationWillTerminate {
2036 NSNotification* sceneNotification =
2037 [NSNotification notificationWithName:UISceneDidDisconnectNotification
2040 NSNotification* applicationNotification =
2041 [NSNotification notificationWithName:UIApplicationWillTerminateNotification
2044 id mockVC = OCMPartialMock(flutterViewController);
2045 id mockEngine = OCMPartialMock(
engine);
2046 OCMStub([mockVC
engine]).andReturn(mockEngine);
2047 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2048 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2049 OCMReject([mockVC sceneWillDisconnect:[OCMArg any]]);
2050 OCMVerify([mockVC applicationWillTerminate:[OCMArg any]]);
2051 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
2052 OCMVerify([mockEngine destroyContext]);
2053 [flutterViewController deregisterNotifications];
2056- (void)testLifeCycleNotificationSceneWillTerminate {
2057 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2058 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2059 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2062 [engine runWithEntrypoint:nil];
2065 NSNotification* sceneNotification =
2066 [NSNotification notificationWithName:UISceneDidDisconnectNotification
2069 NSNotification* applicationNotification =
2070 [NSNotification notificationWithName:UIApplicationWillTerminateNotification
2073 id mockVC = OCMPartialMock(flutterViewController);
2074 id mockEngine = OCMPartialMock(
engine);
2075 OCMStub([mockVC
engine]).andReturn(mockEngine);
2076 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2077 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2078 OCMVerify([mockVC sceneWillDisconnect:[OCMArg any]]);
2079 OCMReject([mockVC applicationWillTerminate:[OCMArg any]]);
2080 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
2081 OCMVerify([mockEngine destroyContext]);
2082 [flutterViewController deregisterNotifications];
2083 [mockBundle stopMocking];
2086- (void)testLifeCycleNotificationApplicationDidEnterBackground {
2091 NSNotification* sceneNotification =
2092 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2095 NSNotification* applicationNotification =
2096 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
2099 id mockVC = OCMPartialMock(flutterViewController);
2100 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2101 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2102 OCMReject([mockVC sceneDidEnterBackground:[OCMArg any]]);
2103 OCMVerify([mockVC applicationDidEnterBackground:[OCMArg any]]);
2104 XCTAssertTrue(flutterViewController.isKeyboardInOrTransitioningFromBackground);
2105 OCMVerify([mockVC surfaceUpdated:NO]);
2106 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2107 [flutterViewController deregisterNotifications];
2110- (void)testLifeCycleNotificationSceneDidEnterBackground {
2111 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2112 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2113 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2116 [engine runWithEntrypoint:nil];
2119 NSNotification* sceneNotification =
2120 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2123 NSNotification* applicationNotification =
2124 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
2127 id mockVC = OCMPartialMock(flutterViewController);
2128 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2129 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2130 OCMVerify([mockVC sceneDidEnterBackground:[OCMArg any]]);
2131 OCMReject([mockVC applicationDidEnterBackground:[OCMArg any]]);
2132 XCTAssertTrue(flutterViewController.isKeyboardInOrTransitioningFromBackground);
2133 OCMVerify([mockVC surfaceUpdated:NO]);
2134 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2135 [flutterViewController deregisterNotifications];
2136 [mockBundle stopMocking];
2139- (void)testLifeCycleNotificationApplicationWillEnterForeground {
2144 NSNotification* sceneNotification =
2145 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2148 NSNotification* applicationNotification =
2149 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2152 id mockVC = OCMPartialMock(flutterViewController);
2153 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2154 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2155 OCMReject([mockVC sceneWillEnterForeground:[OCMArg any]]);
2156 OCMVerify([mockVC applicationWillEnterForeground:[OCMArg any]]);
2157 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2158 [flutterViewController deregisterNotifications];
2161- (void)testLifeCycleNotificationSceneWillEnterForeground {
2162 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2163 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2164 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2167 [engine runWithEntrypoint:nil];
2170 NSNotification* sceneNotification =
2171 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2174 NSNotification* applicationNotification =
2175 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2178 id mockVC = OCMPartialMock(flutterViewController);
2179 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2180 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2181 OCMVerify([mockVC sceneWillEnterForeground:[OCMArg any]]);
2182 OCMReject([mockVC applicationWillEnterForeground:[OCMArg any]]);
2183 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2184 [flutterViewController deregisterNotifications];
2185 [mockBundle stopMocking];
2188- (void)testLifeCycleNotificationCancelledInvalidResumed {
2193 NSNotification* applicationDidBecomeActiveNotification =
2194 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2197 NSNotification* applicationWillResignActiveNotification =
2198 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2201 id mockVC = OCMPartialMock(flutterViewController);
2202 [NSNotificationCenter.defaultCenter postNotification:applicationDidBecomeActiveNotification];
2203 [NSNotificationCenter.defaultCenter postNotification:applicationWillResignActiveNotification];
2204 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2206 XCTestExpectation* timeoutApplicationLifeCycle =
2207 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2208 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2209 dispatch_get_main_queue(), ^{
2210 OCMReject([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2211 [timeoutApplicationLifeCycle fulfill];
2212 [flutterViewController deregisterNotifications];
2214 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2217- (void)testSetupKeyboardAnimationVsyncClientWillCreateNewVsyncClientForFlutterViewController {
2218 id bundleMock = OCMPartialMock([NSBundle mainBundle]);
2222 double maxFrameRate = 120;
2223 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2231 [viewController setUpKeyboardAnimationVsyncClient:callback];
2233 CADisplayLink* link = [viewController.keyboardAnimationVSyncClient getDisplayLink];
2234 XCTAssertNotNil(link);
2235 if (@available(iOS 15.0, *)) {
2236 XCTAssertEqual(link.preferredFrameRateRange.maximum, maxFrameRate);
2237 XCTAssertEqual(link.preferredFrameRateRange.preferred, maxFrameRate);
2238 XCTAssertEqual(link.preferredFrameRateRange.minimum, maxFrameRate / 2);
2240 XCTAssertEqual(link.preferredFramesPerSecond, maxFrameRate);
2245 testCreateTouchRateCorrectionVSyncClientWillCreateVsyncClientWhenRefreshRateIsLargerThan60HZ {
2247 double maxFrameRate = 120;
2248 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2254 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2258- (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateNewVSyncClientWhenClientAlreadyExists {
2260 double maxFrameRate = 120;
2261 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2268 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2270 XCTAssertNotNil(clientBefore);
2272 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2274 XCTAssertNotNil(clientAfter);
2276 XCTAssertTrue(clientBefore == clientAfter);
2279- (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateVsyncClientWhenRefreshRateIs60HZ {
2281 double maxFrameRate = 60;
2282 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2288 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2292- (void)testTriggerTouchRateCorrectionVSyncClientCorrectly {
2294 double maxFrameRate = 120;
2295 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2301 [viewController loadView];
2302 [viewController viewDidLoad];
2305 CADisplayLink* link = [client getDisplayLink];
2307 UITouch* fakeTouchBegan = [[UITouch alloc] init];
2308 fakeTouchBegan.phase = UITouchPhaseBegan;
2310 UITouch* fakeTouchMove = [[UITouch alloc] init];
2311 fakeTouchMove.phase = UITouchPhaseMoved;
2313 UITouch* fakeTouchEnd = [[UITouch alloc] init];
2314 fakeTouchEnd.phase = UITouchPhaseEnded;
2316 UITouch* fakeTouchCancelled = [[UITouch alloc] init];
2317 fakeTouchCancelled.phase = UITouchPhaseCancelled;
2320 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchBegan, nil]];
2321 XCTAssertFalse(link.isPaused);
2324 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd, nil]];
2325 XCTAssertTrue(link.isPaused);
2328 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchMove, nil]];
2329 XCTAssertFalse(link.isPaused);
2332 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchCancelled, nil]];
2333 XCTAssertTrue(link.isPaused);
2336 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2337 initWithObjects:fakeTouchBegan, fakeTouchEnd, nil]];
2338 XCTAssertFalse(link.isPaused);
2341 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd,
2342 fakeTouchCancelled, nil]];
2343 XCTAssertTrue(link.isPaused);
2346 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2347 initWithObjects:fakeTouchMove, fakeTouchEnd, nil]];
2348 XCTAssertFalse(link.isPaused);
2351- (void)testFlutterViewControllerStartKeyboardAnimationWillCreateVsyncClientCorrectly {
2358 [viewController startKeyBoardAnimation:0.25];
2363 testSetupKeyboardAnimationVsyncClientWillNotCreateNewVsyncClientWhenKeyboardAnimationCallbackIsNil {
2369 [viewController setUpKeyboardAnimationVsyncClient:nil];
2373- (void)testSupportsShowingSystemContextMenuForIOS16AndAbove {
2379 BOOL supportsShowingSystemContextMenu = [viewController supportsShowingSystemContextMenu];
2380 if (@available(iOS 16.0, *)) {
2381 XCTAssertTrue(supportsShowingSystemContextMenu);
2383 XCTAssertFalse(supportsShowingSystemContextMenu);
2387- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsActive {
2393 id mockApplication = OCMClassMock([UIApplication
class]);
2394 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateActive);
2395 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2400- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsBackground {
2406 id mockApplication = OCMClassMock([UIApplication
class]);
2407 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateBackground);
2408 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2413- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsInactive {
2419 id mockApplication = OCMClassMock([UIApplication
class]);
2420 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateInactive);
2421 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2426- (void)testStateIsActiveAndBackgroundWhenSceneStateIsActive {
2427 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2428 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2429 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2432 [engine runWithEntrypoint:nil];
2437 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateForegroundActive);
2441 [mockBundle stopMocking];
2442 [mockVC stopMocking];
2445- (void)testStateIsActiveAndBackgroundWhenSceneStateIsBackground {
2446 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2447 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2448 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2451 [engine runWithEntrypoint:nil];
2456 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateBackground);
2460 [mockBundle stopMocking];
2461 [mockVC stopMocking];
2464- (void)testStateIsActiveAndBackgroundWhenSceneStateIsInactive {
2465 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2466 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2467 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2470 [engine runWithEntrypoint:nil];
2475 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateForegroundInactive);
2479 [mockBundle stopMocking];
2480 [mockVC stopMocking];
2483- (void)testPerformImplicitEngineCallbacks {
2484 id mockRegistrant = OCMProtocolMock(
@protocol(FlutterPluginRegistrant));
2485 id appDelegate = [[UIApplication sharedApplication] delegate];
2486 [appDelegate setMockLaunchEngine:self.mockEngine];
2487 UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Flutter" bundle:nil];
2488 XCTAssertTrue([appDelegate respondsToSelector:@selector(setPluginRegistrant:)]);
2489 [appDelegate setPluginRegistrant:mockRegistrant];
2492 [appDelegate setPluginRegistrant:nil];
2494 OCMVerify([
self.mockEngine performImplicitEngineCallback]);
2495 [appDelegate setMockLaunchEngine:nil];
2498- (void)testPerformImplicitEngineCallbacksUsesAppLaunchEventFallbacks {
2504 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2505 OCMStub([viewControllerMock awokenFromNib]).andReturn(YES);
2507 id mockApplication = OCMClassMock([UIApplication
class]);
2508 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2510 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2511 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2513 id mockScene = OCMClassMock([UIScene
class]);
2514 id mockSceneDelegate = OCMProtocolMock(
@protocol(UISceneDelegate));
2515 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
2516 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockScene]);
2520 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2522 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2523 OCMVerify([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2524 OCMVerify([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2527- (void)testPerformImplicitEngineCallbacksNoAppLaunchEventFallbacksWhenNoStoryboard {
2533 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2534 OCMStub([viewControllerMock awokenFromNib]).andReturn(NO);
2536 id mockApplication = OCMClassMock([UIApplication
class]);
2537 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2539 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2540 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2542 id mockScene = OCMClassMock([UIScene
class]);
2543 id mockSceneDelegate = OCMProtocolMock(
@protocol(UISceneDelegate));
2544 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
2545 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockScene]);
2549 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2551 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2552 OCMReject([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2553 OCMReject([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2556- (void)testPerformImplicitEngineCallbacksNoAppLaunchEventFallbacksWhenNoScenes {
2562 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2563 OCMStub([viewControllerMock awokenFromNib]).andReturn(YES);
2565 id mockApplication = OCMClassMock([UIApplication
class]);
2566 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2568 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2569 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2573 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2575 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2576 OCMReject([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2577 OCMReject([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2580- (void)testGrabLaunchEngine {
2581 id appDelegate = [[UIApplication sharedApplication] delegate];
2582 XCTAssertTrue([appDelegate respondsToSelector:@selector(setMockLaunchEngine:)]);
2583 [appDelegate setMockLaunchEngine:self.mockEngine];
2584 UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Flutter" bundle:nil];
2585 XCTAssertTrue(storyboard);
2591 [appDelegate setMockLaunchEngine:nil];
2594- (void)testDoesntGrabLaunchEngine {
2595 id appDelegate = [[UIApplication sharedApplication] delegate];
2596 XCTAssertTrue([appDelegate respondsToSelector:@selector(setMockLaunchEngine:)]);
2597 [appDelegate setMockLaunchEngine:self.mockEngine];
2599 XCTAssertNotNil(flutterViewController.
engine);
2600 XCTAssertNotEqual(flutterViewController.
engine,
self.mockEngine);
2601 [appDelegate setMockLaunchEngine:nil];
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterReply)(id _Nullable reply)
static CFStringRef kMessageLoopCFRunLoopMode
void(* FlutterKeyEventCallback)(bool, void *)
G_BEGIN_DECLS GBytes * message
FlutterDesktopBinaryReply callback
BOOL runWithEntrypoint:(nullable NSString *entrypoint)
FlutterViewController * viewController
FlutterTextInputPlugin * textInputPlugin
FlutterBasicMessageChannel * lifecycleChannel
BOOL didCallNotifyLowMemory
FlutterViewController * viewController
FlutterBasicMessageChannel * keyEventChannel
NSObject< FlutterBinaryMessenger > * binaryMessenger
void(^ FlutterSendKeyEvent)(const FlutterKeyEvent &, _Nullable FlutterKeyEventCallback, void *_Nullable)
UITextSmartQuotesType smartQuotesType API_AVAILABLE(ios(11.0))
FlutterViewController * viewController
FlutterTextInputPlugin * textInputPlugin
NSNotificationName const FlutterViewControllerWillDealloc
void(^ FlutterKeyboardAnimationCallback)(fml::TimePoint)
NSString *const kCADisableMinimumFrameDurationOnPhoneKey
Info.plist key enabling the full range of ProMotion refresh rates for CADisplayLink callbacks and CAA...