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)updateAutoResizeConstraints;
146- (void)checkAndUpdateAutoResizeConstraints;
147- (void)onUserSettingsChanged:(NSNotification*)notification;
148- (void)applicationWillTerminate:(NSNotification*)notification;
149- (void)goToApplicationLifecycle:(nonnull NSString*)state;
150- (void)handleKeyboardNotification:(NSNotification*)notification;
151- (CGFloat)calculateKeyboardInset:(CGRect)keyboardFrame keyboardMode:(
int)keyboardMode;
152- (
BOOL)shouldIgnoreKeyboardNotification:(NSNotification*)notification;
153- (FlutterKeyboardMode)calculateKeyboardAttachMode:(NSNotification*)notification;
154- (CGFloat)calculateMultitaskingAdjustment:(CGRect)screenRect keyboardFrame:(CGRect)keyboardFrame;
155- (void)startKeyBoardAnimation:(NSTimeInterval)duration;
156- (void)hideKeyboardImmediately;
157- (UIView*)keyboardAnimationView;
159- (void)setUpKeyboardSpringAnimationIfNeeded:(CAAnimation*)keyboardAnimation;
160- (void)setUpKeyboardAnimationVsyncClient:
162- (void)ensureViewportMetricsIsCorrect;
163- (void)invalidateKeyboardAnimationVSyncClient;
164- (void)addInternalPlugins;
165- (
flutter::PointerData)generatePointerDataForFake;
167 initialRoute:(nullable NSString*)initialRoute;
168- (void)applicationBecameActive:(NSNotification*)notification;
169- (void)applicationWillResignActive:(NSNotification*)notification;
170- (void)applicationWillTerminate:(NSNotification*)notification;
171- (void)applicationDidEnterBackground:(NSNotification*)notification;
172- (void)applicationWillEnterForeground:(NSNotification*)notification;
173- (void)sceneBecameActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
174- (void)sceneWillResignActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
175- (void)sceneWillDisconnect:(NSNotification*)notification API_AVAILABLE(ios(13.0));
176- (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
177- (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
178- (void)triggerTouchRateCorrectionIfNeeded:(NSSet*)touches;
182@property(nonatomic, strong)
id mockEngine;
183@property(nonatomic, strong)
id mockTextInputPlugin;
184@property(nonatomic, strong)
id messageSent;
185- (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback;
190@property(nonatomic, readwrite) UITouchPhase phase;
194@interface VSyncClient (Testing)
196- (CADisplayLink*)getDisplayLink;
206 self.messageSent = nil;
212 [
self.mockEngine stopMocking];
213 self.mockEngine = nil;
214 self.mockTextInputPlugin = nil;
215 self.messageSent = nil;
218- (
id)setUpMockScreen {
219 UIScreen* mockScreen = OCMClassMock([UIScreen
class]);
221 CGRect screenBounds = CGRectMake(0, 0, 1170, 2532);
222 OCMStub([mockScreen bounds]).andReturn(screenBounds);
223 CGFloat screenScale = 1;
224 OCMStub([mockScreen scale]).andReturn(screenScale);
230 screen:(UIScreen*)screen
231 viewFrame:(CGRect)viewFrame
232 convertedFrame:(CGRect)convertedFrame {
233 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
234 id mockView = OCMClassMock([UIView
class]);
235 OCMStub([mockView frame]).andReturn(viewFrame);
236 OCMStub([mockView convertRect:viewFrame toCoordinateSpace:[OCMArg any]])
237 .andReturn(convertedFrame);
238 OCMStub([viewControllerMock viewIfLoaded]).andReturn(mockView);
243- (void)testViewDidLoadWillInvokeCreateTouchRateCorrectionVSyncClient {
250 [viewControllerMock loadView];
251 [viewControllerMock viewDidLoad];
252 OCMVerify([viewControllerMock createTouchRateCorrectionVSyncClientIfNeeded]);
255- (void)testStartKeyboardAnimationWillInvokeSetupKeyboardSpringAnimationIfNeeded {
262 viewControllerMock.targetViewInsetBottom = 100;
263 [viewControllerMock startKeyBoardAnimation:0.25];
265 CAAnimation* keyboardAnimation =
266 [[viewControllerMock keyboardAnimationView].layer animationForKey:@"position"];
268 OCMVerify([viewControllerMock setUpKeyboardSpringAnimationIfNeeded:keyboardAnimation]);
271- (void)testSetupKeyboardSpringAnimationIfNeeded {
278 UIScreen* screen = [
self setUpMockScreen];
279 CGRect viewFrame = screen.bounds;
280 [
self setUpMockView:viewControllerMock
283 convertedFrame:viewFrame];
286 [viewControllerMock setUpKeyboardSpringAnimationIfNeeded:nil];
287 SpringAnimation* keyboardSpringAnimation = [viewControllerMock keyboardSpringAnimation];
288 XCTAssertTrue(keyboardSpringAnimation == nil);
291 CABasicAnimation* nonSpringAnimation = [CABasicAnimation animation];
292 nonSpringAnimation.duration = 1.0;
293 nonSpringAnimation.fromValue = [NSNumber numberWithFloat:0.0];
294 nonSpringAnimation.toValue = [NSNumber numberWithFloat:1.0];
295 nonSpringAnimation.keyPath =
@"position";
296 [viewControllerMock setUpKeyboardSpringAnimationIfNeeded:nonSpringAnimation];
297 keyboardSpringAnimation = [viewControllerMock keyboardSpringAnimation];
299 XCTAssertTrue(keyboardSpringAnimation == nil);
302 CASpringAnimation* springAnimation = [CASpringAnimation animation];
303 springAnimation.mass = 1.0;
304 springAnimation.stiffness = 100.0;
305 springAnimation.damping = 10.0;
306 springAnimation.keyPath =
@"position";
307 springAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
308 springAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
309 [viewControllerMock setUpKeyboardSpringAnimationIfNeeded:springAnimation];
310 keyboardSpringAnimation = [viewControllerMock keyboardSpringAnimation];
311 XCTAssertTrue(keyboardSpringAnimation != nil);
314- (void)testKeyboardAnimationIsShowingAndCompounding {
321 UIScreen* screen = [
self setUpMockScreen];
322 CGRect viewFrame = screen.bounds;
323 [
self setUpMockView:viewControllerMock
326 convertedFrame:viewFrame];
329 CGFloat screenHeight = screen.bounds.size.height;
330 CGFloat screenWidth = screen.bounds.size.height;
333 CGRect initialShowKeyboardBeginFrame = CGRectMake(0, screenHeight, screenWidth, 250);
334 CGRect initialShowKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
335 NSNotification* fakeNotification = [NSNotification
336 notificationWithName:UIKeyboardWillChangeFrameNotification
339 @"UIKeyboardFrameBeginUserInfoKey" : @(initialShowKeyboardBeginFrame),
340 @"UIKeyboardFrameEndUserInfoKey" : @(initialShowKeyboardEndFrame),
341 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
342 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
344 viewControllerMock.targetViewInsetBottom = 0;
345 [viewControllerMock handleKeyboardNotification:fakeNotification];
346 BOOL isShowingAnimation1 = viewControllerMock.keyboardAnimationIsShowing;
347 XCTAssertTrue(isShowingAnimation1);
350 CGRect compoundingShowKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
351 CGRect compoundingShowKeyboardEndFrame = CGRectMake(0, screenHeight - 500, screenWidth, 500);
352 fakeNotification = [NSNotification
353 notificationWithName:UIKeyboardWillChangeFrameNotification
356 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingShowKeyboardBeginFrame),
357 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingShowKeyboardEndFrame),
358 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
359 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
362 [viewControllerMock handleKeyboardNotification:fakeNotification];
363 BOOL isShowingAnimation2 = viewControllerMock.keyboardAnimationIsShowing;
364 XCTAssertTrue(isShowingAnimation2);
365 XCTAssertTrue(isShowingAnimation1 == isShowingAnimation2);
368 CGRect initialHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 500, screenWidth, 250);
369 CGRect initialHideKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
370 fakeNotification = [NSNotification
371 notificationWithName:UIKeyboardWillChangeFrameNotification
374 @"UIKeyboardFrameBeginUserInfoKey" : @(initialHideKeyboardBeginFrame),
375 @"UIKeyboardFrameEndUserInfoKey" : @(initialHideKeyboardEndFrame),
376 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
377 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
380 [viewControllerMock handleKeyboardNotification:fakeNotification];
381 BOOL isShowingAnimation3 = viewControllerMock.keyboardAnimationIsShowing;
382 XCTAssertFalse(isShowingAnimation3);
383 XCTAssertTrue(isShowingAnimation2 != isShowingAnimation3);
386 CGRect compoundingHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
387 CGRect compoundingHideKeyboardEndFrame = CGRectMake(0, screenHeight, screenWidth, 500);
388 fakeNotification = [NSNotification
389 notificationWithName:UIKeyboardWillChangeFrameNotification
392 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingHideKeyboardBeginFrame),
393 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingHideKeyboardEndFrame),
394 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
395 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
398 [viewControllerMock handleKeyboardNotification:fakeNotification];
399 BOOL isShowingAnimation4 = viewControllerMock.keyboardAnimationIsShowing;
400 XCTAssertFalse(isShowingAnimation4);
401 XCTAssertTrue(isShowingAnimation3 == isShowingAnimation4);
404- (void)testShouldIgnoreKeyboardNotification {
406 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
411 UIScreen* screen = [
self setUpMockScreen];
412 CGRect viewFrame = screen.bounds;
413 [
self setUpMockView:viewControllerMock
416 convertedFrame:viewFrame];
418 CGFloat screenWidth = screen.bounds.size.width;
419 CGFloat screenHeight = screen.bounds.size.height;
420 CGRect emptyKeyboard = CGRectZero;
421 CGRect zeroHeightKeyboard = CGRectMake(0, 0, screenWidth, 0);
422 CGRect validKeyboardEndFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
426 NSNotification* notification =
427 [NSNotification notificationWithName:UIKeyboardWillHideNotification
430 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
431 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
432 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
435 BOOL shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
436 XCTAssertTrue(shouldIgnore == NO);
440 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
443 @"UIKeyboardFrameEndUserInfoKey" : @(emptyKeyboard),
444 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
445 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
447 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
448 XCTAssertTrue(shouldIgnore == YES);
453 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
456 @"UIKeyboardFrameEndUserInfoKey" : @(zeroHeightKeyboard),
457 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
458 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
460 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
461 XCTAssertTrue(shouldIgnore == NO);
466 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
469 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
470 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
471 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
473 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
474 XCTAssertTrue(shouldIgnore == YES);
479 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
482 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
483 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
484 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
486 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
487 XCTAssertTrue(shouldIgnore == NO);
490- (void)testKeyboardAnimationWillNotCrashWhenEngineDestroyed {
496 [viewController setUpKeyboardAnimationVsyncClient:^(fml::TimePoint){
501- (void)testKeyboardAnimationWillWaitUIThreadVsync {
514 const int delayTime = 1;
515 [engine uiTaskRunner]->PostTask([] { sleep(delayTime); });
516 XCTestExpectation* expectation = [
self expectationWithDescription:@"keyboard animation callback"];
518 __block CFTimeInterval fulfillTime;
520 fulfillTime = CACurrentMediaTime();
521 [expectation fulfill];
523 CFTimeInterval startTime = CACurrentMediaTime();
524 [viewController setUpKeyboardAnimationVsyncClient:callback];
525 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
526 XCTAssertTrue(fulfillTime - startTime > delayTime);
529- (void)testCalculateKeyboardAttachMode {
531 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
537 UIScreen* screen = [
self setUpMockScreen];
538 CGRect viewFrame = screen.bounds;
539 [
self setUpMockView:viewControllerMock
542 convertedFrame:viewFrame];
544 CGFloat screenWidth = screen.bounds.size.width;
545 CGFloat screenHeight = screen.bounds.size.height;
548 CGRect keyboardFrame = CGRectZero;
549 NSNotification* notification =
550 [NSNotification notificationWithName:UIKeyboardWillHideNotification
553 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
554 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
555 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
557 FlutterKeyboardMode keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
558 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
561 keyboardFrame = CGRectZero;
562 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
565 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
566 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
567 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
569 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
570 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
573 keyboardFrame = CGRectMake(0, 0, screenWidth, 0);
574 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
577 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
578 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
579 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
581 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
582 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
585 keyboardFrame = CGRectMake(0, 0, 320, 320);
586 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
589 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
590 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
591 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
593 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
594 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
597 keyboardFrame = CGRectMake(0, 0, screenWidth, 320);
598 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
601 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
602 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
603 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
605 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
606 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
609 keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
610 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
613 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
614 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
615 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
617 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
618 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
621 CGFloat longDecimalHeight = 320.666666666666666;
622 keyboardFrame = CGRectMake(0, screenHeight - longDecimalHeight, screenWidth, longDecimalHeight);
623 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
626 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
627 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
628 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
630 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
631 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
634 keyboardFrame = CGRectMake(0, screenHeight - .0000001, screenWidth, longDecimalHeight);
635 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
638 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
639 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
640 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
642 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
643 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
646 keyboardFrame = CGRectMake(0, screenHeight, screenWidth, 320);
647 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
650 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
651 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
652 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
654 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
655 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
658- (void)testCalculateMultitaskingAdjustment {
660 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
666 UIScreen* screen = [
self setUpMockScreen];
667 CGFloat screenWidth = screen.bounds.size.width;
668 CGFloat screenHeight = screen.bounds.size.height;
669 CGRect screenRect = screen.bounds;
670 CGRect viewOrigFrame = CGRectMake(0, 0, 320, screenHeight - 40);
671 CGRect convertedViewFrame = CGRectMake(20, 20, 320, screenHeight - 40);
672 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
673 id mockView = [
self setUpMockView:viewControllerMock
675 viewFrame:viewOrigFrame
676 convertedFrame:convertedViewFrame];
677 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
678 OCMStub([mockTraitCollection userInterfaceIdiom]).andReturn(UIUserInterfaceIdiomPad);
679 OCMStub([mockTraitCollection horizontalSizeClass]).andReturn(UIUserInterfaceSizeClassCompact);
680 OCMStub([mockTraitCollection verticalSizeClass]).andReturn(UIUserInterfaceSizeClassRegular);
681 OCMStub([mockView traitCollection]).andReturn(mockTraitCollection);
683 CGFloat adjustment = [viewControllerMock calculateMultitaskingAdjustment:screenRect
684 keyboardFrame:keyboardFrame];
685 XCTAssertTrue(adjustment == 20);
688- (void)testCalculateKeyboardInset {
690 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
695 UIScreen* screen = [
self setUpMockScreen];
696 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
698 CGFloat screenWidth = screen.bounds.size.width;
699 CGFloat screenHeight = screen.bounds.size.height;
700 CGRect viewOrigFrame = CGRectMake(0, 0, 320, screenHeight - 40);
701 CGRect convertedViewFrame = CGRectMake(20, 20, 320, screenHeight - 40);
702 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
704 [
self setUpMockView:viewControllerMock
706 viewFrame:viewOrigFrame
707 convertedFrame:convertedViewFrame];
709 CGFloat inset = [viewControllerMock calculateKeyboardInset:keyboardFrame
710 keyboardMode:FlutterKeyboardModeDocked];
711 XCTAssertTrue(inset == 300 * screen.scale);
714- (void)testHandleKeyboardNotification {
721 UIScreen* screen = [
self setUpMockScreen];
722 CGFloat screenWidth = screen.bounds.size.width;
723 CGFloat screenHeight = screen.bounds.size.height;
724 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
725 CGRect viewFrame = screen.bounds;
727 NSNotification* notification =
728 [NSNotification notificationWithName:UIKeyboardWillShowNotification
731 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
732 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
733 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
736 [
self setUpMockView:viewControllerMock
739 convertedFrame:viewFrame];
740 viewControllerMock.targetViewInsetBottom = 0;
741 XCTestExpectation* expectation = [
self expectationWithDescription:@"update viewport"];
742 OCMStub([viewControllerMock updateViewportMetricsIfNeeded]).andDo(^(NSInvocation* invocation) {
743 [expectation fulfill];
746 [viewControllerMock handleKeyboardNotification:notification];
747 XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 320 * screen.scale);
748 OCMVerify([viewControllerMock startKeyBoardAnimation:0.25]);
749 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
752- (void)testEnsureBottomInsetIsZeroWhenKeyboardDismissed {
754 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
760 CGRect keyboardFrame = CGRectZero;
762 NSNotification* fakeNotification =
763 [NSNotification notificationWithName:UIKeyboardWillHideNotification
766 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
767 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
768 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
771 viewControllerMock.targetViewInsetBottom = 10;
772 [viewControllerMock handleKeyboardNotification:fakeNotification];
773 XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 0);
776- (void)testStopKeyBoardAnimationWhenReceivedWillHideNotificationAfterWillShowNotification {
785 UIScreen* screen = [
self setUpMockScreen];
786 CGRect viewFrame = screen.bounds;
787 [
self setUpMockView:viewControllerMock
790 convertedFrame:viewFrame];
791 viewControllerMock.targetViewInsetBottom = 0;
793 CGFloat screenHeight = screen.bounds.size.height;
794 CGFloat screenWidth = screen.bounds.size.height;
795 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
799 NSNotification* fakeShowNotification =
800 [NSNotification notificationWithName:UIKeyboardWillShowNotification
803 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
804 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
805 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
807 [viewControllerMock handleKeyboardNotification:fakeShowNotification];
808 XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 320 * screen.scale);
811 NSNotification* fakeHideNotification =
812 [NSNotification notificationWithName:UIKeyboardWillHideNotification
815 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
816 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.0),
817 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
819 [viewControllerMock handleKeyboardNotification:fakeHideNotification];
820 XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 0);
823 XCTAssertNil(viewControllerMock.keyboardAnimationView);
824 XCTAssertNil(viewControllerMock.keyboardSpringAnimation);
827- (void)testEnsureViewportMetricsWillInvokeAndDisplayLinkWillInvalidateInViewDidDisappear {
829 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
834 [viewControllerMock viewDidDisappear:YES];
835 OCMVerify([viewControllerMock ensureViewportMetricsIsCorrect]);
836 OCMVerify([viewControllerMock invalidateKeyboardAnimationVSyncClient]);
839- (void)testViewDidDisappearDoesntPauseEngineWhenNotTheViewController {
847 id viewControllerMock = OCMPartialMock(viewControllerA);
848 OCMStub([viewControllerMock surfaceUpdated:NO]);
850 [viewControllerA viewDidDisappear:NO];
851 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
852 OCMReject([viewControllerMock surfaceUpdated:[OCMArg any]]);
855- (void)testAppWillTerminateViewDidDestroyTheEngine {
857 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
862 OCMStub([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
863 OCMStub([mockEngine destroyContext]);
864 [viewController applicationWillTerminate:nil];
865 OCMVerify([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
866 OCMVerify([mockEngine destroyContext]);
869- (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController {
880 OCMStub([viewControllerMock surfaceUpdated:NO]);
881 [viewController viewDidDisappear:NO];
882 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
883 OCMVerify([viewControllerMock surfaceUpdated:NO]);
885 XCTAssertNil(weakViewController);
889 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear {
891 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
895 [viewController viewWillAppear:YES];
900 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear {
902 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
912 [viewControllerA viewWillAppear:YES];
913 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
917 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear {
919 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
923 [viewController viewDidAppear:YES];
928 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear {
930 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
940 [viewControllerA viewDidAppear:YES];
941 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
945 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear {
953 [viewController viewWillDisappear:NO];
954 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
958 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear {
969 [viewControllerA viewDidDisappear:NO];
970 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
973- (void)testUpdateViewportMetricsIfNeeded_DoesntInvokeEngineWhenNotTheViewController {
975 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
984 [viewControllerA updateViewportMetricsIfNeeded];
986 OCMVerify(never(), [mockEngine updateViewportMetrics:viewportMetrics]);
989- (void)testUpdateViewportMetricsIfNeeded_DoesInvokeEngineWhenIsTheViewController {
991 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
997 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
998 [viewController updateViewportMetricsIfNeeded];
999 OCMVerifyAll(mockEngine);
1002- (void)testUpdatedViewportMetricsDoesResizeFlutterViewWhenAutoResizable {
1004 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1009 id mockVC = OCMPartialMock(realVC);
1012 OCMExpect([mockVC updateAutoResizeConstraints]);
1014 [mockVC setAutoResizable:YES];
1016 [mockVC viewDidLayoutSubviews];
1018 OCMVerifyAll(mockVC);
1021- (void)testUpdatedViewportMetricsDoesNotResizeFlutterViewWhenNotAutoResizable {
1023 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1028 id mockVC = OCMPartialMock(realVC);
1031 OCMReject([mockVC updateAutoResizeConstraints]);
1033 [mockVC setAutoResizable:NO];
1035 [mockVC viewDidLayoutSubviews];
1037 OCMVerifyAll(mockVC);
1040- (void)testUpdateViewportMetricsIfNeeded_DoesNotInvokeEngineWhenShouldBeIgnoredDuringRotation {
1042 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1047 UIScreen* screen = [
self setUpMockScreen];
1048 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1051 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1052 OCMStub([mockCoordinator transitionDuration]).andReturn(0.5);
1055 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1057 [viewController updateViewportMetricsIfNeeded];
1062- (void)testViewWillTransitionToSize_DoesDelayEngineCallIfNonZeroDuration {
1064 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1069 UIScreen* screen = [
self setUpMockScreen];
1070 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1074 NSTimeInterval transitionDuration = 0.5;
1075 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1076 OCMStub([mockCoordinator transitionDuration]).andReturn(transitionDuration);
1079 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1081 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1083 [viewController updateViewportMetricsIfNeeded];
1088 XCTWaiterResult result = [XCTWaiter
1089 waitForExpectations:@[ [
self expectationWithDescription:@"Waiting for rotation duration"] ]
1090 timeout:transitionDuration];
1091 XCTAssertEqual(result, XCTWaiterResultTimedOut);
1093 OCMVerifyAll(mockEngine);
1096- (void)testViewWillTransitionToSize_DoesNotDelayEngineCallIfZeroDuration {
1098 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1103 UIScreen* screen = [
self setUpMockScreen];
1104 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1108 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1109 OCMStub([mockCoordinator transitionDuration]).andReturn(0);
1112 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1115 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1116 [viewController updateViewportMetricsIfNeeded];
1118 OCMVerifyAll(mockEngine);
1121- (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController {
1123 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1132 UIView*
view = viewControllerA.view;
1133 XCTAssertNotNil(
view);
1134 OCMVerify(never(), [mockEngine attachView]);
1137- (void)testViewDidLoadDoesInvokeEngineWhenIsTheViewController {
1139 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1146 XCTAssertNotNil(
view);
1147 OCMVerify(times(1), [mockEngine attachView]);
1150- (void)testViewDidLoadDoesntInvokeEngineAttachViewWhenEngineNeedsLaunch {
1152 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1158 [viewController sharedSetupWithProject:nil initialRoute:nil];
1161 XCTAssertNotNil(
view);
1162 OCMVerify(never(), [mockEngine attachView]);
1165- (void)testSplashScreenViewRemoveNotCrash {
1170 [flutterViewController setSplashScreenView:[[UIView alloc] init]];
1171 [flutterViewController setSplashScreenView:nil];
1174- (void)testInternalPluginsWeakPtrNotCrash {
1180 [vc addInternalPlugins];
1183 [(NSArray<id<FlutterKeyPrimaryResponder>>*)keyboardManager.primaryResponders firstObject];
1184 sendEvent = [keyPrimaryResponder sendEvent];
1188 sendEvent({}, nil, nil);
1193- (void)testInternalPluginsInvokeInViewDidLoad {
1195 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1203 XCTAssertNotNil(
view);
1204 [viewController viewDidLoad];
1208- (void)testBinaryMessenger {
1212 XCTAssertNotNil(vc);
1213 id messenger = OCMProtocolMock(
@protocol(FlutterBinaryMessenger));
1214 OCMStub([
self.mockEngine binaryMessenger]).andReturn(messenger);
1216 OCMVerify([
self.mockEngine binaryMessenger]);
1219- (void)testViewControllerIsReleased {
1221 __weak UIView* weakView;
1230 [viewController loadView];
1231 [viewController viewDidLoad];
1235 XCTAssertNil(weakViewController);
1236 XCTAssertNil(weakView);
1239#pragma mark - Platform Brightness
1241- (void)testItReportsLightPlatformBrightnessByDefault {
1244 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1251 [vc traitCollectionDidChange:nil];
1254 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1255 return [message[@"platformBrightness"] isEqualToString:@"light"];
1259 [settingsChannel stopMocking];
1262- (void)testItReportsPlatformBrightnessWhenViewWillAppear {
1266 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1267 OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel);
1273 [vc viewWillAppear:false];
1276 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1277 return [message[@"platformBrightness"] isEqualToString:@"light"];
1281 [settingsChannel stopMocking];
1284- (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt {
1287 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1288 id mockTraitCollection =
1289 [
self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
1298 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1301 [partialMockVC traitCollectionDidChange:nil];
1304 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1305 return [message[@"platformBrightness"] isEqualToString:@"dark"];
1309 [partialMockVC stopMocking];
1310 [settingsChannel stopMocking];
1311 [mockTraitCollection stopMocking];
1316- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style {
1317 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1318 OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style);
1319 return mockTraitCollection;
1322- (void)testTraitCollectionDidChangeCallsResetIntrinsicContentSizeWhenAutoResizable {
1324 id mockEngine = OCMPartialMock([[
FlutterEngine alloc] init]);
1325 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1330 id partialMockVC = OCMPartialMock(realVC);
1332 id mockFlutterView = OCMClassMock([
FlutterView class]);
1333 OCMStub([partialMockVC flutterView]).andReturn(mockFlutterView);
1336 OCMStub([partialMockVC isAutoResizable]).andReturn(YES);
1339 OCMExpect([mockFlutterView resetIntrinsicContentSize]);
1342 [partialMockVC traitCollectionDidChange:nil];
1345 OCMVerifyAll(mockFlutterView);
1348 [partialMockVC stopMocking];
1349 [mockFlutterView stopMocking];
1352#pragma mark - Platform Contrast
1354- (void)testItReportsNormalPlatformContrastByDefault {
1357 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1364 [vc traitCollectionDidChange:nil];
1367 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1368 return [message[@"platformContrast"] isEqualToString:@"normal"];
1372 [settingsChannel stopMocking];
1375- (void)testItReportsPlatformContrastWhenViewWillAppear {
1377 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1381 OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel);
1387 [vc viewWillAppear:false];
1390 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1391 return [message[@"platformContrast"] isEqualToString:@"normal"];
1395 [settingsChannel stopMocking];
1398- (void)testItReportsHighContrastWhenTraitCollectionRequestsIt {
1401 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1403 id mockTraitCollection = [
self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh];
1412 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1415 [partialMockVC traitCollectionDidChange:mockTraitCollection];
1418 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1419 return [message[@"platformContrast"] isEqualToString:@"high"];
1423 [partialMockVC stopMocking];
1424 [settingsChannel stopMocking];
1425 [mockTraitCollection stopMocking];
1428- (void)testItReportsAlwaysUsed24HourFormat {
1431 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1437 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(YES);
1438 OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1439 return [message[@"alwaysUse24HourFormat"] isEqual:@(YES)];
1441 [vc onUserSettingsChanged:nil];
1442 [mockHourFormat stopMocking];
1446 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(NO);
1447 OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1448 return [message[@"alwaysUse24HourFormat"] isEqual:@(NO)];
1450 [vc onUserSettingsChanged:nil];
1451 [mockHourFormat stopMocking];
1454 [settingsChannel stopMocking];
1457- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagNotSet {
1462 OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(NO);
1465 int32_t flags = [partialMockViewController accessibilityFlags];
1471- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagSet {
1476 OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(YES);
1479 int32_t flags = [partialMockViewController accessibilityFlags];
1485- (void)testAccessibilityPerformEscapePopsRoute {
1487 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1489 OCMStub([mockEngine navigationChannel]).andReturn(mockNavigationChannel);
1496 OCMVerify([mockNavigationChannel invokeMethod:
@"popRoute" arguments:nil]);
1498 [mockNavigationChannel stopMocking];
1501- (void)testPerformOrientationUpdateForcesOrientationChange {
1502 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1503 currentOrientation:UIInterfaceOrientationLandscapeLeft
1504 didChangeOrientation:YES
1505 resultingOrientation:UIInterfaceOrientationPortrait];
1507 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1508 currentOrientation:UIInterfaceOrientationLandscapeRight
1509 didChangeOrientation:YES
1510 resultingOrientation:UIInterfaceOrientationPortrait];
1512 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1513 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1514 didChangeOrientation:YES
1515 resultingOrientation:UIInterfaceOrientationPortrait];
1517 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1518 currentOrientation:UIInterfaceOrientationLandscapeLeft
1519 didChangeOrientation:YES
1520 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1522 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1523 currentOrientation:UIInterfaceOrientationLandscapeRight
1524 didChangeOrientation:YES
1525 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1527 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1528 currentOrientation:UIInterfaceOrientationPortrait
1529 didChangeOrientation:YES
1530 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1532 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1533 currentOrientation:UIInterfaceOrientationPortrait
1534 didChangeOrientation:YES
1535 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1537 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1538 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1539 didChangeOrientation:YES
1540 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1542 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1543 currentOrientation:UIInterfaceOrientationPortrait
1544 didChangeOrientation:YES
1545 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1547 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1548 currentOrientation:UIInterfaceOrientationLandscapeRight
1549 didChangeOrientation:YES
1550 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1552 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1553 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1554 didChangeOrientation:YES
1555 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1557 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1558 currentOrientation:UIInterfaceOrientationPortrait
1559 didChangeOrientation:YES
1560 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1562 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1563 currentOrientation:UIInterfaceOrientationLandscapeLeft
1564 didChangeOrientation:YES
1565 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1567 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1568 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1569 didChangeOrientation:YES
1570 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1572 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1573 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1574 didChangeOrientation:YES
1575 resultingOrientation:UIInterfaceOrientationPortrait];
1578- (void)testPerformOrientationUpdateDoesNotForceOrientationChange {
1579 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1580 currentOrientation:UIInterfaceOrientationPortrait
1581 didChangeOrientation:NO
1582 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1584 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1585 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1586 didChangeOrientation:NO
1587 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1589 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1590 currentOrientation:UIInterfaceOrientationLandscapeLeft
1591 didChangeOrientation:NO
1592 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1594 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1595 currentOrientation:UIInterfaceOrientationLandscapeRight
1596 didChangeOrientation:NO
1597 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1599 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1600 currentOrientation:UIInterfaceOrientationPortrait
1601 didChangeOrientation:NO
1602 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1604 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1605 currentOrientation:UIInterfaceOrientationLandscapeLeft
1606 didChangeOrientation:NO
1607 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1609 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1610 currentOrientation:UIInterfaceOrientationLandscapeRight
1611 didChangeOrientation:NO
1612 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1614 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1615 currentOrientation:UIInterfaceOrientationPortrait
1616 didChangeOrientation:NO
1617 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1619 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1620 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1621 didChangeOrientation:NO
1622 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1624 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1625 currentOrientation:UIInterfaceOrientationLandscapeLeft
1626 didChangeOrientation:NO
1627 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1629 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1630 currentOrientation:UIInterfaceOrientationLandscapeRight
1631 didChangeOrientation:NO
1632 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1634 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1635 currentOrientation:UIInterfaceOrientationLandscapeLeft
1636 didChangeOrientation:NO
1637 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1639 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1640 currentOrientation:UIInterfaceOrientationLandscapeRight
1641 didChangeOrientation:NO
1642 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1647- (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask
1648 currentOrientation:(UIInterfaceOrientation)currentOrientation
1649 didChangeOrientation:(
BOOL)didChange
1650 resultingOrientation:(UIInterfaceOrientation)resultingOrientation {
1651 id mockApplication = OCMClassMock([UIApplication
class]);
1655 __block __weak
id weakPreferences;
1661 if (@available(iOS 16.0, *)) {
1662 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1663 mockVC = OCMPartialMock(realVC);
1664 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1665 if (realVC.supportedInterfaceOrientations == mask) {
1666 OCMReject([mockWindowScene requestGeometryUpdateWithPreferences:[OCMArg any]
1667 errorHandler:[OCMArg any]]);
1671 OCMExpect([mockWindowScene
1672 requestGeometryUpdateWithPreferences:[OCMArg checkWithBlock:^
BOOL(
1673 UIWindowSceneGeometryPreferencesIOS*
1675 weakPreferences = preferences;
1676 return preferences.interfaceOrientations == mask;
1678 errorHandler:[OCMArg any]]);
1680 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
1681 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockWindowScene]);
1683 deviceMock = OCMPartialMock([UIDevice currentDevice]);
1685 OCMReject([deviceMock setValue:[OCMArg any] forKey:
@"orientation"]);
1687 OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:
@"orientation"]);
1689 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1690 mockVC = OCMPartialMock(realVC);
1691 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1692 OCMStub(((UIWindowScene*)mockWindowScene).interfaceOrientation).andReturn(currentOrientation);
1695 [realVC performOrientationUpdate:mask];
1696 if (@available(iOS 16.0, *)) {
1697 OCMVerifyAll(mockWindowScene);
1699 OCMVerifyAll(deviceMock);
1702 [mockWindowScene stopMocking];
1703 [deviceMock stopMocking];
1704 [mockApplication stopMocking];
1705 XCTAssertNil(weakPreferences);
1710- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast {
1711 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1712 OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast);
1713 return mockTraitCollection;
1716- (void)testWillDeallocNotification {
1717 XCTestExpectation* expectation =
1718 [[XCTestExpectation alloc] initWithDescription:@"notification called"];
1725 [NSNotificationCenter.defaultCenter addObserverForName:FlutterViewControllerWillDealloc
1727 queue:[NSOperationQueue mainQueue]
1728 usingBlock:^(NSNotification* _Nonnull note) {
1729 [expectation fulfill];
1731 XCTAssertNotNil(realVC);
1734 [
self waitForExpectations:@[ expectation ] timeout:1.0];
1737- (void)testReleasesKeyboardManagerOnDealloc {
1742 [viewController addInternalPlugins];
1744 XCTAssertNotNil(weakKeyboardManager);
1745 [viewController deregisterNotifications];
1749 XCTAssertNil(weakKeyboardManager);
1752- (void)testDoesntLoadViewInInit {
1755 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
1759 XCTAssertFalse([realVC isViewLoaded],
@"shouldn't have loaded since it hasn't been shown");
1763- (void)testHideOverlay {
1766 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
1770 XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden,
@"");
1771 [NSNotificationCenter.defaultCenter postNotificationName:FlutterViewControllerHideHomeIndicator
1773 XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden,
@"");
1777- (void)testNotifyLowMemory {
1783 OCMStub([viewControllerMock surfaceUpdated:NO]);
1784 [viewController beginAppearanceTransition:NO animated:NO];
1785 [viewController endAppearanceTransition];
1789- (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback {
1790 NSMutableDictionary* replyMessage = [@{
1803 if (@available(iOS 13.4, *)) {
1810 OCMStub([mockEngine.
keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
1811 .andCall(
self, @selector(sendMessage:reply:));
1812 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
1821 [vc addInternalPlugins];
1823 [vc handlePressEvent:keyUpEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0)
1827 XCTAssert(
self.messageSent != nil);
1828 XCTAssert([
self.messageSent[
@"keymap"] isEqualToString:
@"ios"]);
1829 XCTAssert([
self.messageSent[
@"type"] isEqualToString:
@"keyup"]);
1830 XCTAssert([
self.messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
1831 XCTAssert([
self.messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
1832 XCTAssert([
self.messageSent[
@"characters"] isEqualToString:
@""]);
1833 XCTAssert([
self.messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@""]);
1834 [vc deregisterNotifications];
1838 if (@available(iOS 13.4, *)) {
1846 OCMStub([mockEngine.
keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
1847 .andCall(
self, @selector(sendMessage:reply:));
1848 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
1856 [vc addInternalPlugins];
1858 [vc handlePressEvent:keyDownEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0f, "A",
1863 XCTAssert(
self.messageSent != nil);
1864 XCTAssert([
self.messageSent[
@"keymap"] isEqualToString:
@"ios"]);
1865 XCTAssert([
self.messageSent[
@"type"] isEqualToString:
@"keydown"]);
1866 XCTAssert([
self.messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
1867 XCTAssert([
self.messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
1868 XCTAssert([
self.messageSent[
@"characters"] isEqualToString:
@"A"]);
1869 XCTAssert([
self.messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@"a"]);
1870 [vc deregisterNotifications];
1875 if (@available(iOS 13.4, *)) {
1881 OCMStub([keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
1882 .andCall(
self, @selector(sendMessage:reply:));
1883 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
1884 OCMStub([
self.mockEngine keyEventChannel]).andReturn(keyEventChannel);
1892 [vc addInternalPlugins];
1894 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseStationary, UIKeyboardHIDUsageKeyboardA,
1895 UIKeyModifierShift, 123.0)
1898 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseCancelled, UIKeyboardHIDUsageKeyboardA,
1899 UIKeyModifierShift, 123.0)
1902 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseChanged, UIKeyboardHIDUsageKeyboardA,
1903 UIKeyModifierShift, 123.0)
1907 XCTAssert(
self.messageSent == nil);
1908 OCMVerify(never(), [keyEventChannel sendMessage:[OCMArg any]]);
1909 [vc deregisterNotifications];
1913 if (@available(iOS 13.4, *)) {
1922 XCTAssertNotNil(vc);
1923 UIView*
view = vc.view;
1924 XCTAssertNotNil(
view);
1925 NSArray* gestureRecognizers =
view.gestureRecognizers;
1926 XCTAssertNotNil(gestureRecognizers);
1929 for (
id gesture in gestureRecognizers) {
1930 if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
1935 XCTAssertTrue(found);
1939 if (@available(iOS 13.4, *)) {
1948 XCTAssertNotNil(vc);
1950 id mockPanGestureRecognizer = OCMClassMock([UIPanGestureRecognizer
class]);
1951 XCTAssertNotNil(mockPanGestureRecognizer);
1953 [vc discreteScrollEvent:mockPanGestureRecognizer];
1956 [[mockPanGestureRecognizer verify] locationInView:[OCMArg any]];
1957 [[[
self.mockEngine verify] ignoringNonObjectArgs]
1958 dispatchPointerDataPacket:std::make_unique<flutter::PointerDataPacket>(0)];
1961- (void)testFakeEventTimeStamp {
1965 XCTAssertNotNil(vc);
1968 int64_t current_micros = [[NSProcessInfo processInfo] systemUptime] * 1000 * 1000;
1969 int64_t interval_micros = current_micros - pointer_data.time_stamp;
1970 const int64_t tolerance_millis = 2;
1971 XCTAssertTrue(interval_micros / 1000 < tolerance_millis,
1972 @"PointerData.time_stamp should be equal to NSProcessInfo.systemUptime");
1975- (void)testSplashScreenViewCanSetNil {
1978 [flutterViewController setSplashScreenView:nil];
1981- (void)testLifeCycleNotificationApplicationBecameActive {
1986 UIWindow*
window = [[UIWindow alloc] init];
1987 [window addSubview:flutterViewController.view];
1988 flutterViewController.view.bounds = CGRectMake(0, 0, 100, 100);
1989 [flutterViewController viewDidLayoutSubviews];
1990 NSNotification* sceneNotification =
1991 [NSNotification notificationWithName:UISceneDidActivateNotification object:nil userInfo:nil];
1992 NSNotification* applicationNotification =
1993 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
1996 id mockVC = OCMPartialMock(flutterViewController);
1997 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
1998 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
1999 OCMReject([mockVC sceneBecameActive:[OCMArg any]]);
2000 OCMVerify([mockVC applicationBecameActive:[OCMArg any]]);
2001 XCTAssertFalse(flutterViewController.isKeyboardInOrTransitioningFromBackground);
2002 OCMVerify([mockVC surfaceUpdated:YES]);
2003 XCTestExpectation* timeoutApplicationLifeCycle =
2004 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2005 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2006 dispatch_get_main_queue(), ^{
2007 [timeoutApplicationLifeCycle fulfill];
2008 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2009 [flutterViewController deregisterNotifications];
2011 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2014- (void)testLifeCycleNotificationSceneBecameActive {
2015 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2016 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2017 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2020 [engine runWithEntrypoint:nil];
2023 UIWindow*
window = [[UIWindow alloc] init];
2024 [window addSubview:flutterViewController.view];
2025 flutterViewController.view.bounds = CGRectMake(0, 0, 100, 100);
2026 [flutterViewController viewDidLayoutSubviews];
2027 NSNotification* sceneNotification =
2028 [NSNotification notificationWithName:UISceneDidActivateNotification object:nil userInfo:nil];
2029 NSNotification* applicationNotification =
2030 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2033 id mockVC = OCMPartialMock(flutterViewController);
2034 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2035 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2036 OCMVerify([mockVC sceneBecameActive:[OCMArg any]]);
2037 OCMReject([mockVC applicationBecameActive:[OCMArg any]]);
2038 XCTAssertFalse(flutterViewController.isKeyboardInOrTransitioningFromBackground);
2039 OCMVerify([mockVC surfaceUpdated:YES]);
2040 XCTestExpectation* timeoutApplicationLifeCycle =
2041 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2042 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2043 dispatch_get_main_queue(), ^{
2044 [timeoutApplicationLifeCycle fulfill];
2045 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2046 [flutterViewController deregisterNotifications];
2048 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2049 [mockBundle stopMocking];
2052- (void)testLifeCycleNotificationApplicationWillResignActive {
2057 NSNotification* sceneNotification =
2058 [NSNotification notificationWithName:UISceneWillDeactivateNotification
2061 NSNotification* applicationNotification =
2062 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2065 id mockVC = OCMPartialMock(flutterViewController);
2066 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2067 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2068 OCMReject([mockVC sceneWillResignActive:[OCMArg any]]);
2069 OCMVerify([mockVC applicationWillResignActive:[OCMArg any]]);
2070 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2071 [flutterViewController deregisterNotifications];
2074- (void)testLifeCycleNotificationSceneWillResignActive {
2075 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2076 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2077 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2080 [engine runWithEntrypoint:nil];
2083 NSNotification* sceneNotification =
2084 [NSNotification notificationWithName:UISceneWillDeactivateNotification
2087 NSNotification* applicationNotification =
2088 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2091 id mockVC = OCMPartialMock(flutterViewController);
2092 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2093 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2094 OCMVerify([mockVC sceneWillResignActive:[OCMArg any]]);
2095 OCMReject([mockVC applicationWillResignActive:[OCMArg any]]);
2096 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2097 [flutterViewController deregisterNotifications];
2098 [mockBundle stopMocking];
2101- (void)testLifeCycleNotificationApplicationWillTerminate {
2106 NSNotification* sceneNotification =
2107 [NSNotification notificationWithName:UISceneDidDisconnectNotification
2110 NSNotification* applicationNotification =
2111 [NSNotification notificationWithName:UIApplicationWillTerminateNotification
2114 id mockVC = OCMPartialMock(flutterViewController);
2115 id mockEngine = OCMPartialMock(
engine);
2116 OCMStub([mockVC
engine]).andReturn(mockEngine);
2117 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2118 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2119 OCMReject([mockVC sceneWillDisconnect:[OCMArg any]]);
2120 OCMVerify([mockVC applicationWillTerminate:[OCMArg any]]);
2121 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
2122 OCMVerify([mockEngine destroyContext]);
2123 [flutterViewController deregisterNotifications];
2126- (void)testLifeCycleNotificationSceneWillTerminate {
2127 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2128 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2129 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2132 [engine runWithEntrypoint:nil];
2135 NSNotification* sceneNotification =
2136 [NSNotification notificationWithName:UISceneDidDisconnectNotification
2139 NSNotification* applicationNotification =
2140 [NSNotification notificationWithName:UIApplicationWillTerminateNotification
2143 id mockVC = OCMPartialMock(flutterViewController);
2144 id mockEngine = OCMPartialMock(
engine);
2145 OCMStub([mockVC
engine]).andReturn(mockEngine);
2146 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2147 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2148 OCMVerify([mockVC sceneWillDisconnect:[OCMArg any]]);
2149 OCMReject([mockVC applicationWillTerminate:[OCMArg any]]);
2150 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
2151 OCMVerify([mockEngine destroyContext]);
2152 [flutterViewController deregisterNotifications];
2153 [mockBundle stopMocking];
2156- (void)testLifeCycleNotificationApplicationDidEnterBackground {
2161 NSNotification* sceneNotification =
2162 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2165 NSNotification* applicationNotification =
2166 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
2169 id mockVC = OCMPartialMock(flutterViewController);
2170 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2171 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2172 OCMReject([mockVC sceneDidEnterBackground:[OCMArg any]]);
2173 OCMVerify([mockVC applicationDidEnterBackground:[OCMArg any]]);
2174 XCTAssertTrue(flutterViewController.isKeyboardInOrTransitioningFromBackground);
2175 OCMVerify([mockVC surfaceUpdated:NO]);
2176 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2177 [flutterViewController deregisterNotifications];
2180- (void)testLifeCycleNotificationSceneDidEnterBackground {
2181 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2182 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2183 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2186 [engine runWithEntrypoint:nil];
2189 NSNotification* sceneNotification =
2190 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2193 NSNotification* applicationNotification =
2194 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
2197 id mockVC = OCMPartialMock(flutterViewController);
2198 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2199 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2200 OCMVerify([mockVC sceneDidEnterBackground:[OCMArg any]]);
2201 OCMReject([mockVC applicationDidEnterBackground:[OCMArg any]]);
2202 XCTAssertTrue(flutterViewController.isKeyboardInOrTransitioningFromBackground);
2203 OCMVerify([mockVC surfaceUpdated:NO]);
2204 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2205 [flutterViewController deregisterNotifications];
2206 [mockBundle stopMocking];
2209- (void)testLifeCycleNotificationApplicationWillEnterForeground {
2214 NSNotification* sceneNotification =
2215 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2218 NSNotification* applicationNotification =
2219 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2222 id mockVC = OCMPartialMock(flutterViewController);
2223 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2224 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2225 OCMReject([mockVC sceneWillEnterForeground:[OCMArg any]]);
2226 OCMVerify([mockVC applicationWillEnterForeground:[OCMArg any]]);
2227 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2228 [flutterViewController deregisterNotifications];
2231- (void)testLifeCycleNotificationSceneWillEnterForeground {
2232 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2233 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2234 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2237 [engine runWithEntrypoint:nil];
2240 NSNotification* sceneNotification =
2241 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2244 NSNotification* applicationNotification =
2245 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2248 id mockVC = OCMPartialMock(flutterViewController);
2249 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2250 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2251 OCMVerify([mockVC sceneWillEnterForeground:[OCMArg any]]);
2252 OCMReject([mockVC applicationWillEnterForeground:[OCMArg any]]);
2253 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2254 [flutterViewController deregisterNotifications];
2255 [mockBundle stopMocking];
2258- (void)testLifeCycleNotificationCancelledInvalidResumed {
2263 NSNotification* applicationDidBecomeActiveNotification =
2264 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2267 NSNotification* applicationWillResignActiveNotification =
2268 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2271 id mockVC = OCMPartialMock(flutterViewController);
2272 [NSNotificationCenter.defaultCenter postNotification:applicationDidBecomeActiveNotification];
2273 [NSNotificationCenter.defaultCenter postNotification:applicationWillResignActiveNotification];
2274 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2276 XCTestExpectation* timeoutApplicationLifeCycle =
2277 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2278 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2279 dispatch_get_main_queue(), ^{
2280 OCMReject([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2281 [timeoutApplicationLifeCycle fulfill];
2282 [flutterViewController deregisterNotifications];
2284 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2287- (void)testSetupKeyboardAnimationVsyncClientWillCreateNewVsyncClientForFlutterViewController {
2288 id bundleMock = OCMPartialMock([NSBundle mainBundle]);
2292 double maxFrameRate = 120;
2293 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2301 [viewController setUpKeyboardAnimationVsyncClient:callback];
2303 CADisplayLink* link = [viewController.keyboardAnimationVSyncClient getDisplayLink];
2304 XCTAssertNotNil(link);
2305 if (@available(iOS 15.0, *)) {
2306 XCTAssertEqual(link.preferredFrameRateRange.maximum, maxFrameRate);
2307 XCTAssertEqual(link.preferredFrameRateRange.preferred, maxFrameRate);
2308 XCTAssertEqual(link.preferredFrameRateRange.minimum, maxFrameRate / 2);
2310 XCTAssertEqual(link.preferredFramesPerSecond, maxFrameRate);
2315 testCreateTouchRateCorrectionVSyncClientWillCreateVsyncClientWhenRefreshRateIsLargerThan60HZ {
2317 double maxFrameRate = 120;
2318 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2324 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2328- (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateNewVSyncClientWhenClientAlreadyExists {
2330 double maxFrameRate = 120;
2331 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2338 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2340 XCTAssertNotNil(clientBefore);
2342 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2344 XCTAssertNotNil(clientAfter);
2346 XCTAssertTrue(clientBefore == clientAfter);
2349- (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateVsyncClientWhenRefreshRateIs60HZ {
2351 double maxFrameRate = 60;
2352 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2358 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2362- (void)testTriggerTouchRateCorrectionVSyncClientCorrectly {
2364 double maxFrameRate = 120;
2365 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2371 [viewController loadView];
2372 [viewController viewDidLoad];
2375 CADisplayLink* link = [client getDisplayLink];
2377 UITouch* fakeTouchBegan = [[UITouch alloc] init];
2378 fakeTouchBegan.phase = UITouchPhaseBegan;
2380 UITouch* fakeTouchMove = [[UITouch alloc] init];
2381 fakeTouchMove.phase = UITouchPhaseMoved;
2383 UITouch* fakeTouchEnd = [[UITouch alloc] init];
2384 fakeTouchEnd.phase = UITouchPhaseEnded;
2386 UITouch* fakeTouchCancelled = [[UITouch alloc] init];
2387 fakeTouchCancelled.phase = UITouchPhaseCancelled;
2390 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchBegan, nil]];
2391 XCTAssertFalse(link.isPaused);
2394 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd, nil]];
2395 XCTAssertTrue(link.isPaused);
2398 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchMove, nil]];
2399 XCTAssertFalse(link.isPaused);
2402 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchCancelled, nil]];
2403 XCTAssertTrue(link.isPaused);
2406 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2407 initWithObjects:fakeTouchBegan, fakeTouchEnd, nil]];
2408 XCTAssertFalse(link.isPaused);
2411 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd,
2412 fakeTouchCancelled, nil]];
2413 XCTAssertTrue(link.isPaused);
2416 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2417 initWithObjects:fakeTouchMove, fakeTouchEnd, nil]];
2418 XCTAssertFalse(link.isPaused);
2421- (void)testFlutterViewControllerStartKeyboardAnimationWillCreateVsyncClientCorrectly {
2428 [viewController startKeyBoardAnimation:0.25];
2433 testSetupKeyboardAnimationVsyncClientWillNotCreateNewVsyncClientWhenKeyboardAnimationCallbackIsNil {
2439 [viewController setUpKeyboardAnimationVsyncClient:nil];
2443- (void)testSupportsShowingSystemContextMenuForIOS16AndAbove {
2449 BOOL supportsShowingSystemContextMenu = [viewController supportsShowingSystemContextMenu];
2450 if (@available(iOS 16.0, *)) {
2451 XCTAssertTrue(supportsShowingSystemContextMenu);
2453 XCTAssertFalse(supportsShowingSystemContextMenu);
2457- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsActive {
2463 id mockApplication = OCMClassMock([UIApplication
class]);
2464 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateActive);
2465 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2470- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsBackground {
2476 id mockApplication = OCMClassMock([UIApplication
class]);
2477 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateBackground);
2478 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2483- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsInactive {
2489 id mockApplication = OCMClassMock([UIApplication
class]);
2490 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateInactive);
2491 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2496- (void)testStateIsActiveAndBackgroundWhenSceneStateIsActive {
2497 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2498 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2499 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2502 [engine runWithEntrypoint:nil];
2507 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateForegroundActive);
2511 [mockBundle stopMocking];
2512 [mockVC stopMocking];
2515- (void)testStateIsActiveAndBackgroundWhenSceneStateIsBackground {
2516 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2517 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2518 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2521 [engine runWithEntrypoint:nil];
2526 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateBackground);
2530 [mockBundle stopMocking];
2531 [mockVC stopMocking];
2534- (void)testStateIsActiveAndBackgroundWhenSceneStateIsInactive {
2535 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2536 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2537 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2540 [engine runWithEntrypoint:nil];
2545 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateForegroundInactive);
2549 [mockBundle stopMocking];
2550 [mockVC stopMocking];
2553- (void)testPerformImplicitEngineCallbacks {
2554 id mockRegistrant = OCMProtocolMock(
@protocol(FlutterPluginRegistrant));
2555 id appDelegate = [[UIApplication sharedApplication] delegate];
2556 [appDelegate setMockLaunchEngine:self.mockEngine];
2557 UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Flutter" bundle:nil];
2558 XCTAssertTrue([appDelegate respondsToSelector:@selector(setPluginRegistrant:)]);
2559 [appDelegate setPluginRegistrant:mockRegistrant];
2562 [appDelegate setPluginRegistrant:nil];
2564 OCMVerify([
self.mockEngine performImplicitEngineCallback]);
2565 [appDelegate setMockLaunchEngine:nil];
2568- (void)testPerformImplicitEngineCallbacksUsesAppLaunchEventFallbacks {
2574 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2575 OCMStub([viewControllerMock awokenFromNib]).andReturn(YES);
2577 id mockApplication = OCMClassMock([UIApplication
class]);
2578 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2580 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2581 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2583 id mockScene = OCMClassMock([UIScene
class]);
2584 id mockSceneDelegate = OCMProtocolMock(
@protocol(UISceneDelegate));
2585 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
2586 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockScene]);
2590 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2592 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2593 OCMVerify([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2594 OCMVerify([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2597- (void)testPerformImplicitEngineCallbacksNoAppLaunchEventFallbacksWhenNoStoryboard {
2603 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2604 OCMStub([viewControllerMock awokenFromNib]).andReturn(NO);
2606 id mockApplication = OCMClassMock([UIApplication
class]);
2607 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2609 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2610 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2612 id mockScene = OCMClassMock([UIScene
class]);
2613 id mockSceneDelegate = OCMProtocolMock(
@protocol(UISceneDelegate));
2614 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
2615 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockScene]);
2619 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2621 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2622 OCMReject([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2623 OCMReject([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2626- (void)testPerformImplicitEngineCallbacksNoAppLaunchEventFallbacksWhenNoScenes {
2632 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2633 OCMStub([viewControllerMock awokenFromNib]).andReturn(YES);
2635 id mockApplication = OCMClassMock([UIApplication
class]);
2636 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2638 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2639 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2643 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2645 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2646 OCMReject([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2647 OCMReject([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2650- (void)testGrabLaunchEngine {
2651 id appDelegate = [[UIApplication sharedApplication] delegate];
2652 XCTAssertTrue([appDelegate respondsToSelector:@selector(setMockLaunchEngine:)]);
2653 [appDelegate setMockLaunchEngine:self.mockEngine];
2654 UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Flutter" bundle:nil];
2655 XCTAssertTrue(storyboard);
2661 [appDelegate setMockLaunchEngine:nil];
2664- (void)testDoesntGrabLaunchEngine {
2665 id appDelegate = [[UIApplication sharedApplication] delegate];
2666 XCTAssertTrue([appDelegate respondsToSelector:@selector(setMockLaunchEngine:)]);
2667 [appDelegate setMockLaunchEngine:self.mockEngine];
2669 XCTAssertNotNil(flutterViewController.
engine);
2670 XCTAssertNotEqual(flutterViewController.
engine,
self.mockEngine);
2671 [appDelegate setMockLaunchEngine:nil];
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterReply)(id _Nullable reply)
static CFStringRef kMessageLoopCFRunLoopMode
void(* FlutterKeyEventCallback)(bool, void *)
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...