5#import <OCMock/OCMock.h>
6#import <XCTest/XCTest.h>
38@interface FlutterKeyboardInsetManager (Test)
39- (void)setUpKeyboardAnimationVsyncClient:
43@property(nonatomic, weak) id<FlutterKeyboardInsetManagerDelegate>
delegate;
46- (void)handleKeyboardNotification:(NSNotification*)notification;
47- (
BOOL)isKeyboardNotificationForDifferentView:(NSNotification*)notification;
48- (CGFloat)calculateKeyboardInset:(CGRect)keyboardFrame
49 keyboardMode:(FlutterKeyboardMode)keyboardMode;
50- (
BOOL)shouldIgnoreKeyboardNotification:(NSNotification*)notification;
51- (FlutterKeyboardMode)calculateKeyboardAttachMode:(NSNotification*)notification;
52- (CGFloat)calculateMultitaskingAdjustment:(CGRect)screenRect keyboardFrame:(CGRect)keyboardFrame;
53- (void)startKeyBoardAnimation:(NSTimeInterval)duration;
57- (void)setUpKeyboardSpringAnimationIfNeeded:(CAAnimation*)keyboardAnimation;
68@property(nonatomic, assign) CGFloat targetViewInsetBottom;
69@property(nonatomic, assign)
BOOL isKeyboardInOrTransitioningFromBackground;
70@property(nonatomic, strong) FlutterVSyncClient* keyboardAnimationVSyncClient;
71@property(nonatomic, assign)
BOOL didCallEnsureViewportMetricsIsCorrect;
72@property(nonatomic, assign)
BOOL didCallInvalidateKeyboardAnimationVSyncClient;
74- (instancetype)initWithDelegate:(
id<FlutterKeyboardInsetManagerDelegate>)delegate;
78 __weak id<FlutterKeyboardInsetManagerDelegate> _delegate;
81- (instancetype)initWithDelegate:(
id<FlutterKeyboardInsetManagerDelegate>)delegate {
89- (void)setDelegate:(
id<FlutterKeyboardInsetManagerDelegate>)delegate {
93- (
id<FlutterKeyboardInsetManagerDelegate>)delegate {
97- (void)startKeyBoardAnimation:(NSTimeInterval)duration {
100- (void)handleKeyboardNotification:(NSNotification*)notification {
110- (void)setUpKeyboardSpringAnimationIfNeeded:(CAAnimation*)keyboardAnimation {
113- (
BOOL)shouldIgnoreKeyboardNotification:(NSNotification*)notification {
117- (FlutterKeyboardMode)calculateKeyboardAttachMode:(NSNotification*)notification {
118 return FlutterKeyboardModeHidden;
121- (void)ensureViewportMetricsIsCorrect {
122 self.didCallEnsureViewportMetricsIsCorrect = YES;
125- (void)invalidateKeyboardAnimationVSyncClient {
126 self.didCallInvalidateKeyboardAnimationVSyncClient = YES;
129- (void)hideKeyboardImmediately {
130 [
self ensureViewportMetricsIsCorrect];
131 [
self invalidateKeyboardAnimationVSyncClient];
138- (UIView*)keyboardAnimationView {
158- (instancetype)init {
159 if (
self = [super init]) {
162 initWithTaskRunner:fml::MessageLoop::GetCurrent().GetTaskRunner()];
168 return self.mockTaskRunner;
171- (void)updateViewportMetricsWithInset:(CGFloat)inset {
172 self.currentInset = inset;
173 if (
self.updateViewportMetricsBlock) {
174 self.updateViewportMetricsBlock(inset);
178- (CGFloat)physicalViewInsetBottom {
179 return self.currentInset;
183 return self.mockView;
187 return self.mockEngine;
190- (UIScreen*)flutterScreenIfViewLoaded {
191 return self.mockScreen;
194- (
BOOL)isPadInSlideOverOrStageManagerMode {
195 return self.mockIsPadInSlideOverOrStageManagerMode;
198- (CGRect)convertViewRectToScreen:(CGRect)rect {
199 return self.mockConvertedViewRect;
212@property(nonatomic, assign)
BOOL didCallNotifyLowMemory;
219 userData:(nullable
void*)userData;
222- (
BOOL)runWithEntrypoint:(nullable NSString*)entrypoint;
229@synthesize lifecycleChannel;
230@synthesize keyEventChannel;
234- (void)notifyLowMemory {
235 _didCallNotifyLowMemory = YES;
238- (instancetype)init {
239 if (
self = [super init]) {
242 initWithTaskRunner:fml::MessageLoop::GetCurrent().GetTaskRunner()];
248 return _uiTaskRunner;
251- (
BOOL)runWithEntrypoint:(nullable NSString*)entrypoint {
261 userData:(
void*)userData API_AVAILABLE(ios(9.0)) {
276- (
BOOL)createShell:(NSString*)entrypoint
277 libraryURI:(NSString*)libraryURI
278 initialRoute:(NSString*)initialRoute;
284@interface FlutterEngine (TestLowMemory)
285- (void)notifyLowMemory;
311@interface FlutterKeyboardManager (Tests)
312@property(nonatomic, retain, readonly)
313 NSMutableArray<id<FlutterKeyPrimaryResponder>>* primaryResponders;
316@interface FlutterEmbedderKeyResponder (Tests)
320@interface NSObject (Tests)
324@interface FlutterViewController (Tests) <FlutterKeyboardInsetManagerDelegate>
326@property(nonatomic, assign)
double targetViewInsetBottom;
327@property(nonatomic, assign)
BOOL keyboardAnimationIsShowing;
328@property(nonatomic, strong) FlutterVSyncClient* keyboardAnimationVSyncClient;
329@property(nonatomic, strong) FlutterVSyncClient* touchRateCorrectionVSyncClient;
330@property(nonatomic, assign)
BOOL awokenFromNib;
332- (void)createTouchRateCorrectionVSyncClientIfNeeded;
333- (void)surfaceUpdated:(
BOOL)appeared;
334- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences;
335- (void)handlePressEvent:(FlutterUIPressProxy*)press
336 nextAction:(
void (^)())next API_AVAILABLE(ios(13.4));
337- (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer;
338- (void)updateViewportMetricsIfNeeded;
339- (void)updateAutoResizeConstraints;
340- (void)checkAndUpdateAutoResizeConstraints;
341- (void)onUserSettingsChanged:(NSNotification*)notification;
342- (void)applicationWillTerminate:(NSNotification*)notification;
343- (void)goToApplicationLifecycle:(nonnull NSString*)state;
345- (void)addInternalPlugins;
346- (
flutter::PointerData)generatePointerDataForFake;
348 initialRoute:(nullable NSString*)initialRoute;
349- (void)applicationBecameActive:(NSNotification*)notification;
350- (void)applicationWillResignActive:(NSNotification*)notification;
351- (void)applicationWillTerminate:(NSNotification*)notification;
352- (void)applicationDidEnterBackground:(NSNotification*)notification;
353- (void)applicationWillEnterForeground:(NSNotification*)notification;
354- (void)sceneBecameActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
355- (void)sceneWillResignActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
356- (void)sceneWillDisconnect:(NSNotification*)notification API_AVAILABLE(ios(13.0));
357- (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
358- (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
359- (void)triggerTouchRateCorrectionIfNeeded:(NSSet*)touches;
360- (void)onAccessibilityStatusChanged:(NSNotification*)notification;
364@property(nonatomic, strong)
id mockEngine;
365@property(nonatomic, strong)
id mockTextInputPlugin;
366@property(nonatomic, strong)
id messageSent;
367- (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback;
372@property(nonatomic, readwrite) UITouchPhase phase;
382 self.messageSent = nil;
388 [
self.mockEngine stopMocking];
389 self.mockEngine = nil;
390 self.mockTextInputPlugin = nil;
391 self.messageSent = nil;
394- (
id)setUpMockScreen {
395 UIScreen* mockScreen = OCMClassMock([UIScreen
class]);
397 CGRect screenBounds = CGRectMake(0, 0, 1170, 2532);
398 OCMStub([mockScreen bounds]).andReturn(screenBounds);
399 CGFloat screenScale = 1;
400 OCMStub([mockScreen scale]).andReturn(screenScale);
406 screen:(UIScreen*)screen
407 viewFrame:(CGRect)viewFrame
408 convertedFrame:(CGRect)convertedFrame {
409 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
410 UIView*
view = [[UIView alloc] initWithFrame:viewFrame];
411 UIWindow*
window = [[UIWindow alloc] initWithFrame:viewFrame];
412 [window addSubview:view];
414 OCMStub([viewControllerMock viewIfLoaded]).andReturn(view);
415 OCMStub([viewControllerMock view]).andReturn(view);
420- (void)testViewDidLoadWillInvokeCreateTouchRateCorrectionVSyncClient {
427 [viewControllerMock loadView];
428 [viewControllerMock viewDidLoad];
429 OCMVerify([viewControllerMock createTouchRateCorrectionVSyncClientIfNeeded]);
432- (void)testStartKeyboardAnimationWillInvokeSetupKeyboardSpringAnimationIfNeeded {
439 OCMStub([viewControllerMock isViewLoaded]).andReturn(YES);
440 __unused UIView* dummyView = [viewControllerMock view];
442 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
446 FlutterKeyboardInsetManager*
manager =
447 (FlutterKeyboardInsetManager*)
viewController.keyboardInsetManager;
449 id viewClassMock = OCMClassMock([UIView
class]);
450 OCMStub([viewClassMock animateWithDuration:0.25 animations:[OCMArg any] completion:[OCMArg any]])
451 .andDo(^(NSInvocation* invocation) {
454 __unsafe_unretained void (^animations)(void);
455 [invocation getArgument:&animations atIndex:3];
459 __unsafe_unretained void (^completion)(
BOOL finished);
460 [invocation getArgument:&completion atIndex:4];
469 manager.targetViewInsetBottom = 320;
476 XCTAssertNotNil(
manager.keyboardSpringAnimation);
478 [manager startKeyBoardAnimation:0.25];
480 XCTAssertNil(
manager.keyboardSpringAnimation);
481 [viewClassMock stopMocking];
484- (void)testSetupKeyboardSpringAnimationIfNeeded {
489 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
490 UIScreen* screen = [
self setUpMockScreen];
491 CGRect viewFrame = screen.bounds;
492 [
self setUpMockView:viewControllerMock
495 convertedFrame:viewFrame];
498 [viewController.keyboardInsetManager setUpKeyboardSpringAnimationIfNeeded:nil];
501 XCTAssertTrue(keyboardSpringAnimation == nil);
504 CABasicAnimation* nonSpringAnimation = [CABasicAnimation animation];
505 nonSpringAnimation.duration = 1.0;
506 nonSpringAnimation.fromValue = [NSNumber numberWithFloat:0.0];
507 nonSpringAnimation.toValue = [NSNumber numberWithFloat:1.0];
508 nonSpringAnimation.keyPath =
@"position";
509 [viewController.keyboardInsetManager setUpKeyboardSpringAnimationIfNeeded:nonSpringAnimation];
510 keyboardSpringAnimation =
viewController.keyboardInsetManager.keyboardSpringAnimation;
512 XCTAssertTrue(keyboardSpringAnimation == nil);
515 CASpringAnimation* springAnimation = [CASpringAnimation animation];
516 springAnimation.mass = 1.0;
517 springAnimation.stiffness = 100.0;
518 springAnimation.damping = 10.0;
519 springAnimation.keyPath =
@"position";
520 springAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
521 springAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
522 [viewController.keyboardInsetManager setUpKeyboardSpringAnimationIfNeeded:springAnimation];
523 keyboardSpringAnimation =
viewController.keyboardInsetManager.keyboardSpringAnimation;
524 XCTAssertTrue(keyboardSpringAnimation != nil);
534- (void)testKeyboardAnimationIsShowingAndCompounding {
535 UIScreen* screen = [
self setUpMockScreen];
536 CGFloat screenWidth = screen.bounds.size.width;
537 CGFloat screenHeight = screen.bounds.size.height;
538 CGRect viewFrame = screen.bounds;
543 delegate.
mockView = [[UIView alloc] init];
553 FlutterKeyboardInsetManager*
manager =
554 [[FlutterKeyboardInsetManager alloc] initWithDelegate:delegate
555 displayLinkManager:FlutterDisplayLinkManager.shared];
560 CGRect initialShowKeyboardBeginFrame = CGRectMake(0, screenHeight, screenWidth, 250);
561 CGRect initialShowKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
562 NSNotification* fakeNotification = [NSNotification
563 notificationWithName:UIKeyboardWillChangeFrameNotification
566 @"UIKeyboardFrameBeginUserInfoKey" : @(initialShowKeyboardBeginFrame),
567 @"UIKeyboardFrameEndUserInfoKey" : @(initialShowKeyboardEndFrame),
568 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
569 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
571 manager.targetViewInsetBottom = 0;
572 [manager handleKeyboardNotification:fakeNotification];
573 BOOL isShowingAnimation1 =
manager.keyboardAnimationIsShowing;
574 XCTAssertTrue(isShowingAnimation1);
577 CGRect compoundingShowKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
578 CGRect compoundingShowKeyboardEndFrame = CGRectMake(0, screenHeight - 500, screenWidth, 500);
579 fakeNotification = [NSNotification
580 notificationWithName:UIKeyboardWillChangeFrameNotification
583 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingShowKeyboardBeginFrame),
584 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingShowKeyboardEndFrame),
585 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
586 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
589 [manager handleKeyboardNotification:fakeNotification];
590 BOOL isShowingAnimation2 =
manager.keyboardAnimationIsShowing;
591 XCTAssertTrue(isShowingAnimation2);
592 XCTAssertTrue(isShowingAnimation1 == isShowingAnimation2);
595 CGRect initialHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 500, screenWidth, 250);
596 CGRect initialHideKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
597 fakeNotification = [NSNotification
598 notificationWithName:UIKeyboardWillChangeFrameNotification
601 @"UIKeyboardFrameBeginUserInfoKey" : @(initialHideKeyboardBeginFrame),
602 @"UIKeyboardFrameEndUserInfoKey" : @(initialHideKeyboardEndFrame),
603 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
604 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
607 [manager handleKeyboardNotification:fakeNotification];
608 BOOL isShowingAnimation3 =
manager.keyboardAnimationIsShowing;
609 XCTAssertFalse(isShowingAnimation3);
610 XCTAssertTrue(isShowingAnimation2 != isShowingAnimation3);
613 CGRect compoundingHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
614 CGRect compoundingHideKeyboardEndFrame = CGRectMake(0, screenHeight, screenWidth, 500);
615 fakeNotification = [NSNotification
616 notificationWithName:UIKeyboardWillChangeFrameNotification
619 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingHideKeyboardBeginFrame),
620 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingHideKeyboardEndFrame),
621 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
622 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
625 [manager handleKeyboardNotification:fakeNotification];
626 BOOL isShowingAnimation4 =
manager.keyboardAnimationIsShowing;
627 XCTAssertFalse(isShowingAnimation4);
628 XCTAssertTrue(isShowingAnimation3 == isShowingAnimation4);
630 [manager invalidate];
633- (void)testShouldIgnoreKeyboardNotification {
642 FlutterKeyboardInsetManager* managerMock = [[FlutterKeyboardInsetManager alloc]
643 initWithDelegate:(id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock
644 displayLinkManager:FlutterDisplayLinkManager.shared];
647 UIScreen* screen = [
self setUpMockScreen];
648 CGRect viewFrame = screen.bounds;
649 [
self setUpMockView:viewControllerMock
652 convertedFrame:viewFrame];
654 CGFloat screenWidth = screen.bounds.size.width;
655 CGFloat screenHeight = screen.bounds.size.height;
656 CGRect emptyKeyboard = CGRectZero;
657 CGRect zeroHeightKeyboard = CGRectMake(0, 0, screenWidth, 0);
658 CGRect validKeyboardEndFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
662 NSNotification* notification =
663 [NSNotification notificationWithName:UIKeyboardWillHideNotification
666 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
667 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
668 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
671 BOOL shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
672 XCTAssertTrue(shouldIgnore == NO);
676 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
679 @"UIKeyboardFrameEndUserInfoKey" : @(emptyKeyboard),
680 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
681 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
683 shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
684 XCTAssertTrue(shouldIgnore == YES);
689 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
692 @"UIKeyboardFrameEndUserInfoKey" : @(zeroHeightKeyboard),
693 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
694 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
696 shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
697 XCTAssertTrue(shouldIgnore == NO);
702 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
705 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
706 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
707 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
709 shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
710 XCTAssertTrue(shouldIgnore == YES);
715 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
718 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
719 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
720 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
722 shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
723 XCTAssertTrue(shouldIgnore == NO);
725 [(id)viewControllerMock stopMocking];
728- (void)testKeyboardAnimationWillNotCrashWhenEngineDestroyed {
734 [viewController.keyboardInsetManager
735 setUpKeyboardAnimationVsyncClient:^(NSTimeInterval targetTime){
740- (void)testKeyboardAnimationFirstVsyncCallbackCalculatesSafeInset {
747 OCMStub([viewControllerMock isViewLoaded]).andReturn(YES);
748 __unused UIView* dummyView = [viewControllerMock view];
750 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
754 XCTestExpectation* expectation = [
self expectationWithDescription:@"metrics updated"];
756 __block CGFloat capturedInset = -1.0;
757 __block
BOOL fulfilled = NO;
758 OCMStub([viewControllerMock updateViewportMetricsWithInset:0])
759 .ignoringNonObjectArgs()
760 .andDo(^(NSInvocation* invocation) {
761 [invocation getArgument:&capturedInset atIndex:2];
766 [viewController.keyboardInsetManager invalidateKeyboardAnimationVSyncClient];
767 [expectation fulfill];
772 CASpringAnimation* springAnimation = [CASpringAnimation animation];
773 springAnimation.mass = 1.0;
774 springAnimation.stiffness = 100.0;
775 springAnimation.damping = 10.0;
776 springAnimation.keyPath =
@"position";
778 viewController.keyboardInsetManager.targetViewInsetBottom = 300.0;
781 [viewController.keyboardInsetManager startKeyBoardAnimation:0.25];
782 [viewController.keyboardInsetManager setUpKeyboardSpringAnimationIfNeeded:springAnimation];
785 FlutterVSyncClient* client =
viewController.keyboardInsetManager.keyboardAnimationVSyncClient;
786 [client onDisplayLink:client.displayLink];
789 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
792 XCTAssertFalse(isnan(capturedInset));
793 XCTAssertFalse(isinf(capturedInset));
794 XCTAssertGreaterThanOrEqual(capturedInset, 0.0);
795 XCTAssertLessThan(capturedInset, 300.0);
798- (void)testKeyboardAnimationCallbackIsDeliveredAsynchronously {
807 id mockCADisplayLink = OCMClassMock([CADisplayLink
class]);
809 ClassMethod([mockCADisplayLink displayLinkWithTarget:[OCMArg any]
810 selector:sel_registerName(
"onDisplayLink:")]));
812 XCTestExpectation* expectation = [
self expectationWithDescription:@"keyboard animation callback"];
813 __block
BOOL callbackExecuted = NO;
814 [viewController.keyboardInsetManager
815 setUpKeyboardAnimationVsyncClient:^(NSTimeInterval targetTime) {
816 callbackExecuted = YES;
817 [expectation fulfill];
820 FlutterVSyncClient* client =
viewController.keyboardInsetManager.keyboardAnimationVSyncClient;
821 [client onDisplayLink:client.displayLink];
825 XCTAssertFalse(callbackExecuted);
828 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
829 XCTAssertTrue(callbackExecuted);
830 [mockCADisplayLink stopMocking];
833- (void)testCalculateKeyboardAttachMode {
839 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
840 UIScreen* screen = [
self setUpMockScreen];
841 CGRect viewFrame = screen.bounds;
842 [
self setUpMockView:viewControllerMock
845 convertedFrame:viewFrame];
847 CGFloat screenWidth = screen.bounds.size.width;
848 CGFloat screenHeight = screen.bounds.size.height;
851 CGRect keyboardFrame = CGRectZero;
852 NSNotification* notification =
853 [NSNotification notificationWithName:UIKeyboardWillHideNotification
856 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
857 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
858 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
860 FlutterKeyboardMode keyboardMode =
861 [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
862 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
865 keyboardFrame = CGRectZero;
866 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
869 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
870 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
871 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
873 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
874 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
877 keyboardFrame = CGRectMake(0, 0, screenWidth, 0);
878 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
881 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
882 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
883 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
885 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
886 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
889 keyboardFrame = CGRectMake(0, 0, 320, 320);
890 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
893 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
894 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
895 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
897 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
898 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
901 keyboardFrame = CGRectMake(0, 0, screenWidth, 320);
902 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
905 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
906 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
907 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
909 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
910 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
913 keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
914 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
917 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
918 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
919 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
921 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
922 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
925 CGFloat longDecimalHeight = 320.666666666666666;
926 keyboardFrame = CGRectMake(0, screenHeight - longDecimalHeight, screenWidth, longDecimalHeight);
927 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
930 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
931 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
932 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
934 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
935 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
938 keyboardFrame = CGRectMake(0, screenHeight - .0000001, screenWidth, longDecimalHeight);
939 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
942 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
943 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
944 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
946 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
947 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
950 keyboardFrame = CGRectMake(0, screenHeight, screenWidth, 320);
951 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
954 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
955 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
956 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
958 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
959 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
962- (void)testCalculateMultitaskingAdjustment {
963 UIScreen* screen = [UIScreen mainScreen];
964 CGFloat screenWidth = screen.bounds.size.width;
965 CGFloat screenHeight = screen.bounds.size.height;
966 CGRect screenRect = screen.bounds;
967 CGRect convertedViewFrame = CGRectMake(0, 0, 320, screenHeight - 20);
968 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
977 FlutterKeyboardInsetManager*
manager =
978 [[FlutterKeyboardInsetManager alloc] initWithDelegate:delegate
979 displayLinkManager:FlutterDisplayLinkManager.shared];
981 CGFloat adjustment = [manager calculateMultitaskingAdjustment:screenRect
982 keyboardFrame:keyboardFrame];
983 XCTAssertTrue(adjustment == 20);
986- (void)testCalculateKeyboardInset {
987 UIScreen* screen = [UIScreen mainScreen];
988 CGFloat screenWidth = screen.bounds.size.width;
989 CGFloat screenHeight = screen.bounds.size.height;
990 CGRect convertedViewFrame = CGRectMake(0, 0, 320, screenHeight - 20);
991 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
999 FlutterKeyboardInsetManager*
manager =
1000 [[FlutterKeyboardInsetManager alloc] initWithDelegate:delegate
1001 displayLinkManager:FlutterDisplayLinkManager.shared];
1003 CGFloat inset = [manager calculateKeyboardInset:keyboardFrame
1004 keyboardMode:FlutterKeyboardModeDocked];
1005 XCTAssertTrue(inset == 300 * screen.scale);
1008- (void)testHandleKeyboardNotification {
1009 UIScreen* screen = [
self setUpMockScreen];
1010 CGFloat screenWidth = screen.bounds.size.width;
1011 CGFloat screenHeight = screen.bounds.size.height;
1012 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
1013 CGRect viewFrame = screen.bounds;
1015 NSNotification* notification = [NSNotification
1016 notificationWithName:UIKeyboardWillShowNotification
1019 @"UIKeyboardFrameEndUserInfoKey" : [NSValue valueWithCGRect:keyboardFrame],
1020 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
1021 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
1030 delegate.
mockView = [[UIView alloc] init];
1037 FlutterKeyboardInsetManager*
manager =
1038 [[FlutterKeyboardInsetManager alloc] initWithDelegate:delegate
1039 displayLinkManager:FlutterDisplayLinkManager.shared];
1040 manager.targetViewInsetBottom = 0;
1042 [manager handleKeyboardNotification:notification];
1045 XCTAssertTrue(
manager.targetViewInsetBottom == 320 * screen.scale);
1048 XCTAssertNotNil(
manager.keyboardAnimationVSyncClient);
1050 [manager invalidate];
1053- (void)testEnsureBottomInsetIsZeroWhenKeyboardDismissed {
1062 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
1066 CGRect keyboardFrame = CGRectZero;
1068 NSNotification* fakeNotification =
1069 [NSNotification notificationWithName:UIKeyboardWillHideNotification
1072 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
1073 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
1074 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
1078 [viewController.keyboardInsetManager handleKeyboardNotification:fakeNotification];
1079 XCTAssertTrue(
viewController.keyboardInsetManager.targetViewInsetBottom == 0);
1082- (void)testStopKeyBoardAnimationWhenReceivedWillHideNotificationAfterWillShowNotification {
1092 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
1095 OCMStub([viewControllerMock isViewLoaded]).andReturn(YES);
1096 __unused UIView* dummyView2 = [viewControllerMock view];
1098 UIScreen* screen = [
self setUpMockScreen];
1099 OCMStub([screen scale]).andReturn(1.0);
1100 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1101 CGRect viewFrame = screen.bounds;
1102 [
self setUpMockView:viewControllerMock
1105 convertedFrame:viewFrame];
1108 CGFloat screenWidth = screen.bounds.size.width;
1109 CGFloat screenHeight = screen.bounds.size.height;
1110 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
1114 NSNotification* fakeShowNotification =
1115 [NSNotification notificationWithName:UIKeyboardWillShowNotification
1118 UIKeyboardFrameEndUserInfoKey : @(keyboardFrame),
1119 UIKeyboardAnimationDurationUserInfoKey : @0.25,
1120 UIKeyboardIsLocalUserInfoKey : @(isLocal)
1122 [viewController.keyboardInsetManager handleKeyboardNotification:fakeShowNotification];
1123 XCTAssertEqual(
viewController.keyboardInsetManager.targetViewInsetBottom, 320 * screen.scale);
1126 NSNotification* fakeHideNotification =
1127 [NSNotification notificationWithName:UIKeyboardWillHideNotification
1130 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
1131 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.0),
1132 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
1134 [viewController.keyboardInsetManager handleKeyboardNotification:fakeHideNotification];
1135 XCTAssertEqual(
viewController.keyboardInsetManager.targetViewInsetBottom, 0);
1138 XCTAssertNil([
viewController.keyboardInsetManager keyboardAnimationView]);
1139 XCTAssertNil([
viewController.keyboardInsetManager keyboardSpringAnimation]);
1142- (void)testEnsureViewportMetricsWillInvokeAndDisplayLinkWillInvalidateInViewDidDisappear {
1150 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
1153 initWithDelegate:(id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock];
1156 [viewControllerMock viewDidDisappear:YES];
1162- (void)testViewDidDisappearDoesntPauseEngineWhenNotTheViewController {
1170 id viewControllerMock = OCMPartialMock(viewControllerA);
1171 OCMStub([viewControllerMock surfaceUpdated:NO]);
1173 [viewControllerA viewDidDisappear:NO];
1174 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
1175 OCMReject([viewControllerMock surfaceUpdated:[OCMArg any]]);
1178- (void)testAppWillTerminateViewDidDestroyTheEngine {
1180 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1185 OCMStub([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
1186 OCMStub([mockEngine destroyContext]);
1187 [viewController applicationWillTerminate:nil];
1188 OCMVerify([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
1189 OCMVerify([mockEngine destroyContext]);
1192- (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController {
1203 OCMStub([viewControllerMock surfaceUpdated:NO]);
1204 [viewController viewDidDisappear:NO];
1205 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
1206 OCMVerify([viewControllerMock surfaceUpdated:NO]);
1208 XCTAssertNil(weakViewController);
1212 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear {
1214 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1218 [viewController viewWillAppear:YES];
1223 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear {
1225 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1235 [viewControllerA viewWillAppear:YES];
1236 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
1240 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear {
1242 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1246 [viewController viewDidAppear:YES];
1251 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear {
1253 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1263 [viewControllerA viewDidAppear:YES];
1264 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
1268 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear {
1276 [viewController viewWillDisappear:NO];
1277 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
1281 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear {
1292 [viewControllerA viewDidDisappear:NO];
1293 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
1296- (void)testUpdateViewportMetricsIfNeeded_DoesntInvokeEngineWhenNotTheViewController {
1298 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1307 [viewControllerA updateViewportMetricsIfNeeded];
1309 OCMVerify(never(), [mockEngine updateViewportMetrics:viewportMetrics]);
1312- (void)testUpdateViewportMetricsIfNeeded_DoesInvokeEngineWhenIsTheViewController {
1314 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1320 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1321 [viewController updateViewportMetricsIfNeeded];
1322 OCMVerifyAll(mockEngine);
1325- (void)testUpdatedViewportMetricsDoesResizeFlutterViewWhenAutoResizable {
1327 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1332 id mockVC = OCMPartialMock(realVC);
1335 OCMExpect([mockVC updateAutoResizeConstraints]);
1337 [mockVC setAutoResizable:YES];
1339 [mockVC viewDidLayoutSubviews];
1341 OCMVerifyAll(mockVC);
1344- (void)testUpdatedViewportMetricsDoesNotResizeFlutterViewWhenNotAutoResizable {
1346 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1351 id mockVC = OCMPartialMock(realVC);
1354 OCMReject([mockVC updateAutoResizeConstraints]);
1356 [mockVC setAutoResizable:NO];
1358 [mockVC viewDidLayoutSubviews];
1360 OCMVerifyAll(mockVC);
1363- (void)testUpdateViewportMetricsIfNeeded_DoesNotInvokeEngineWhenShouldBeIgnoredDuringRotation {
1365 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1370 UIScreen* screen = [
self setUpMockScreen];
1371 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1374 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1375 OCMStub([mockCoordinator transitionDuration]).andReturn(0.5);
1378 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1380 [viewController updateViewportMetricsIfNeeded];
1385- (void)testViewWillTransitionToSize_DoesDelayEngineCallIfNonZeroDuration {
1387 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1392 UIScreen* screen = [
self setUpMockScreen];
1393 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1397 NSTimeInterval transitionDuration = 0.5;
1398 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1399 OCMStub([mockCoordinator transitionDuration]).andReturn(transitionDuration);
1402 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1404 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1406 [viewController updateViewportMetricsIfNeeded];
1411 XCTWaiterResult result = [XCTWaiter
1412 waitForExpectations:@[ [
self expectationWithDescription:@"Waiting for rotation duration"] ]
1413 timeout:transitionDuration];
1414 XCTAssertEqual(result, XCTWaiterResultTimedOut);
1416 OCMVerifyAll(mockEngine);
1419- (void)testViewWillTransitionToSize_DoesNotDelayEngineCallIfZeroDuration {
1421 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1426 UIScreen* screen = [
self setUpMockScreen];
1427 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1431 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1432 OCMStub([mockCoordinator transitionDuration]).andReturn(0);
1435 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1438 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1439 [viewController updateViewportMetricsIfNeeded];
1441 OCMVerifyAll(mockEngine);
1444- (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController {
1446 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1455 UIView*
view = viewControllerA.view;
1456 XCTAssertNotNil(view);
1457 OCMVerify(never(), [mockEngine attachView]);
1460- (void)testViewDidLoadDoesInvokeEngineWhenIsTheViewController {
1462 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1469 XCTAssertNotNil(view);
1470 OCMVerify(times(1), [mockEngine attachView]);
1473- (void)testViewDidLoadDoesntInvokeEngineAttachViewWhenEngineNeedsLaunch {
1475 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1481 [viewController sharedSetupWithProject:nil initialRoute:nil];
1484 XCTAssertNotNil(view);
1485 OCMVerify(never(), [mockEngine attachView]);
1488- (void)testSplashScreenViewRemoveNotCrash {
1493 [flutterViewController setSplashScreenView:[[UIView alloc] init]];
1494 [flutterViewController setSplashScreenView:nil];
1497- (void)testInternalPluginsWeakPtrNotCrash {
1503 [vc addInternalPlugins];
1506 [(NSArray<id<FlutterKeyPrimaryResponder>>*)keyboardManager.primaryResponders firstObject];
1507 sendEvent = [keyPrimaryResponder sendEvent];
1511 sendEvent({}, nil, nil);
1516- (void)testInternalPluginsInvokeInViewDidLoad {
1518 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1526 XCTAssertNotNil(view);
1527 [viewController viewDidLoad];
1531- (void)testBinaryMessenger {
1535 XCTAssertNotNil(vc);
1536 id messenger = OCMProtocolMock(
@protocol(FlutterBinaryMessenger));
1537 OCMStub([
self.mockEngine binaryMessenger]).andReturn(messenger);
1539 OCMVerify([
self.mockEngine binaryMessenger]);
1542- (void)testViewControllerIsReleased {
1544 __weak UIView* weakView;
1553 [viewController loadView];
1554 [viewController viewDidLoad];
1558 XCTAssertNil(weakViewController);
1559 XCTAssertNil(weakView);
1562#pragma mark - Platform Brightness
1564- (void)testItReportsLightPlatformBrightnessByDefault {
1567 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1574 [vc traitCollectionDidChange:nil];
1577 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1578 return [message[@"platformBrightness"] isEqualToString:@"light"];
1582 [settingsChannel stopMocking];
1585- (void)testItReportsPlatformBrightnessWhenViewWillAppear {
1589 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1590 OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel);
1596 [vc viewWillAppear:false];
1599 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1600 return [message[@"platformBrightness"] isEqualToString:@"light"];
1604 [settingsChannel stopMocking];
1607- (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt {
1610 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1611 id mockTraitCollection =
1612 [
self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
1621 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1624 [partialMockVC traitCollectionDidChange:nil];
1627 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1628 return [message[@"platformBrightness"] isEqualToString:@"dark"];
1632 [partialMockVC stopMocking];
1633 [settingsChannel stopMocking];
1634 [mockTraitCollection stopMocking];
1639- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style {
1640 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1641 OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style);
1642 return mockTraitCollection;
1645- (void)testTraitCollectionDidChangeCallsResetIntrinsicContentSizeWhenAutoResizable {
1647 id mockEngine = OCMPartialMock([[
FlutterEngine alloc] init]);
1648 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1653 id partialMockVC = OCMPartialMock(realVC);
1655 id mockFlutterView = OCMClassMock([
FlutterView class]);
1656 OCMStub([partialMockVC flutterView]).andReturn(mockFlutterView);
1659 OCMStub([partialMockVC isAutoResizable]).andReturn(YES);
1662 OCMExpect([mockFlutterView resetIntrinsicContentSize]);
1665 [partialMockVC traitCollectionDidChange:nil];
1668 OCMVerifyAll(mockFlutterView);
1671 [partialMockVC stopMocking];
1672 [mockFlutterView stopMocking];
1675#pragma mark - Platform Contrast
1677- (void)testItReportsNormalPlatformContrastByDefault {
1680 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1687 [vc traitCollectionDidChange:nil];
1690 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1691 return [message[@"platformContrast"] isEqualToString:@"normal"];
1695 [settingsChannel stopMocking];
1698- (void)testItReportsPlatformContrastWhenViewWillAppear {
1700 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1704 OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel);
1710 [vc viewWillAppear:false];
1713 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1714 return [message[@"platformContrast"] isEqualToString:@"normal"];
1718 [settingsChannel stopMocking];
1721- (void)testItReportsHighContrastWhenTraitCollectionRequestsIt {
1724 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1726 id mockTraitCollection = [
self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh];
1735 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1738 [partialMockVC traitCollectionDidChange:mockTraitCollection];
1741 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1742 return [message[@"platformContrast"] isEqualToString:@"high"];
1746 [partialMockVC stopMocking];
1747 [settingsChannel stopMocking];
1748 [mockTraitCollection stopMocking];
1751- (void)testItReportsAlwaysUsed24HourFormat {
1754 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1760 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(YES);
1761 OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1762 return [message[@"alwaysUse24HourFormat"] isEqual:@(YES)];
1764 [vc onUserSettingsChanged:nil];
1765 [mockHourFormat stopMocking];
1769 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(NO);
1770 OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1771 return [message[@"alwaysUse24HourFormat"] isEqual:@(NO)];
1773 [vc onUserSettingsChanged:nil];
1774 [mockHourFormat stopMocking];
1777 [settingsChannel stopMocking];
1780- (void)testOnAccessibilityStatusChangedCallsEnableSemanticsWithFlags {
1783 id mockAccessibilityFeatures = OCMClassMock([FlutterAccessibilityFeatures
class]);
1784 OCMStub([mockAccessibilityFeatures flags]).andReturn(333);
1786 OCMStub([mockViewController accessibilityFeatures]).andReturn(mockAccessibilityFeatures);
1788 [mockViewController onAccessibilityStatusChanged:nil];
1789 OCMVerify([
self.mockEngine enableSemantics:[OCMArg any] withFlags:333]);
1792- (void)testHandleAccessibilityNotifications {
1796 __block NSUInteger callsCount = 0;
1797 OCMStub([mockViewController onAccessibilityStatusChanged:[OCMArg isNotNil]])
1798 .andDo(^(NSInvocation* invocation) {
1802 FlutterAccessibilityFeatures* accessibilityFeatures = [[FlutterAccessibilityFeatures alloc] init];
1803 NSArray<NSString*>* accessibilityNotification = [accessibilityFeatures observedNotificationNames];
1805 for (NSUInteger
i = 0;
i < [accessibilityNotification count];
i++) {
1806 NSString* notificationName = [accessibilityNotification objectAtIndex:i];
1807 [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:nil];
1808 XCTAssertEqual(callsCount,
i + 1);
1812- (void)testAccessibilityPerformEscapePopsRoute {
1814 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1816 OCMStub([mockEngine navigationChannel]).andReturn(mockNavigationChannel);
1823 OCMVerify([mockNavigationChannel invokeMethod:
@"popRoute" arguments:nil]);
1825 [mockNavigationChannel stopMocking];
1828- (void)testPerformOrientationUpdateForcesOrientationChange {
1829 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1830 currentOrientation:UIInterfaceOrientationLandscapeLeft
1831 didChangeOrientation:YES
1832 resultingOrientation:UIInterfaceOrientationPortrait];
1834 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1835 currentOrientation:UIInterfaceOrientationLandscapeRight
1836 didChangeOrientation:YES
1837 resultingOrientation:UIInterfaceOrientationPortrait];
1839 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1840 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1841 didChangeOrientation:YES
1842 resultingOrientation:UIInterfaceOrientationPortrait];
1844 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1845 currentOrientation:UIInterfaceOrientationLandscapeLeft
1846 didChangeOrientation:YES
1847 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1849 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1850 currentOrientation:UIInterfaceOrientationLandscapeRight
1851 didChangeOrientation:YES
1852 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1854 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1855 currentOrientation:UIInterfaceOrientationPortrait
1856 didChangeOrientation:YES
1857 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1859 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1860 currentOrientation:UIInterfaceOrientationPortrait
1861 didChangeOrientation:YES
1862 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1864 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1865 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1866 didChangeOrientation:YES
1867 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1869 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1870 currentOrientation:UIInterfaceOrientationPortrait
1871 didChangeOrientation:YES
1872 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1874 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1875 currentOrientation:UIInterfaceOrientationLandscapeRight
1876 didChangeOrientation:YES
1877 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1879 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1880 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1881 didChangeOrientation:YES
1882 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1884 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1885 currentOrientation:UIInterfaceOrientationPortrait
1886 didChangeOrientation:YES
1887 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1889 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1890 currentOrientation:UIInterfaceOrientationLandscapeLeft
1891 didChangeOrientation:YES
1892 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1894 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1895 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1896 didChangeOrientation:YES
1897 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1899 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1900 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1901 didChangeOrientation:YES
1902 resultingOrientation:UIInterfaceOrientationPortrait];
1905- (void)testPerformOrientationUpdateDoesNotForceOrientationChange {
1906 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1907 currentOrientation:UIInterfaceOrientationPortrait
1908 didChangeOrientation:NO
1909 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1911 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1912 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1913 didChangeOrientation:NO
1914 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1916 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1917 currentOrientation:UIInterfaceOrientationLandscapeLeft
1918 didChangeOrientation:NO
1919 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1921 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1922 currentOrientation:UIInterfaceOrientationLandscapeRight
1923 didChangeOrientation:NO
1924 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1926 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1927 currentOrientation:UIInterfaceOrientationPortrait
1928 didChangeOrientation:NO
1929 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1931 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1932 currentOrientation:UIInterfaceOrientationLandscapeLeft
1933 didChangeOrientation:NO
1934 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1936 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1937 currentOrientation:UIInterfaceOrientationLandscapeRight
1938 didChangeOrientation:NO
1939 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1941 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1942 currentOrientation:UIInterfaceOrientationPortrait
1943 didChangeOrientation:NO
1944 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1946 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1947 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1948 didChangeOrientation:NO
1949 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1951 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1952 currentOrientation:UIInterfaceOrientationLandscapeLeft
1953 didChangeOrientation:NO
1954 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1956 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1957 currentOrientation:UIInterfaceOrientationLandscapeRight
1958 didChangeOrientation:NO
1959 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1961 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1962 currentOrientation:UIInterfaceOrientationLandscapeLeft
1963 didChangeOrientation:NO
1964 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1966 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1967 currentOrientation:UIInterfaceOrientationLandscapeRight
1968 didChangeOrientation:NO
1969 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1974- (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask
1975 currentOrientation:(UIInterfaceOrientation)currentOrientation
1976 didChangeOrientation:(
BOOL)didChange
1977 resultingOrientation:(UIInterfaceOrientation)resultingOrientation {
1978 id mockApplication = OCMClassMock([UIApplication
class]);
1982 __block __weak
id weakPreferences;
1988 if (@available(iOS 16.0, *)) {
1989 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1990 mockVC = OCMPartialMock(realVC);
1991 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1992 if (realVC.supportedInterfaceOrientations == mask) {
1993 OCMReject([mockWindowScene requestGeometryUpdateWithPreferences:[OCMArg any]
1994 errorHandler:[OCMArg any]]);
1998 OCMExpect([mockWindowScene
1999 requestGeometryUpdateWithPreferences:[OCMArg checkWithBlock:^
BOOL(
2000 UIWindowSceneGeometryPreferencesIOS*
2002 weakPreferences = preferences;
2003 return preferences.interfaceOrientations == mask;
2005 errorHandler:[OCMArg any]]);
2007 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2008 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockWindowScene]);
2010 deviceMock = OCMPartialMock([UIDevice currentDevice]);
2012 OCMReject([deviceMock setValue:[OCMArg any] forKey:
@"orientation"]);
2014 OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:
@"orientation"]);
2016 mockWindowScene = OCMClassMock([UIWindowScene
class]);
2017 mockVC = OCMPartialMock(realVC);
2018 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
2019 OCMStub(((UIWindowScene*)mockWindowScene).interfaceOrientation).andReturn(currentOrientation);
2022 [realVC performOrientationUpdate:mask];
2023 if (@available(iOS 16.0, *)) {
2024 OCMVerifyAll(mockWindowScene);
2026 OCMVerifyAll(deviceMock);
2029 [mockWindowScene stopMocking];
2030 [deviceMock stopMocking];
2031 [mockApplication stopMocking];
2032 XCTAssertNil(weakPreferences);
2037- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast {
2038 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
2039 OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast);
2040 return mockTraitCollection;
2043- (void)testWillDeallocNotification {
2044 XCTestExpectation* expectation =
2045 [[XCTestExpectation alloc] initWithDescription:@"notification called"];
2052 [NSNotificationCenter.defaultCenter addObserverForName:FlutterViewControllerWillDealloc
2054 queue:[NSOperationQueue mainQueue]
2055 usingBlock:^(NSNotification* _Nonnull note) {
2056 [expectation fulfill];
2058 XCTAssertNotNil(realVC);
2061 [
self waitForExpectations:@[ expectation ] timeout:1.0];
2064- (void)testReleasesKeyboardManagerOnDealloc {
2069 [viewController addInternalPlugins];
2071 XCTAssertNotNil(weakKeyboardManager);
2072 [viewController deregisterNotifications];
2076 XCTAssertNil(weakKeyboardManager);
2079- (void)testDoesntLoadViewInInit {
2082 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
2086 XCTAssertFalse([realVC isViewLoaded],
@"shouldn't have loaded since it hasn't been shown");
2090- (void)testHideOverlay {
2093 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
2097 XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden,
@"");
2098 [NSNotificationCenter.defaultCenter postNotificationName:FlutterViewControllerHideHomeIndicator
2100 XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden,
@"");
2104- (void)testNotifyLowMemory {
2110 OCMStub([viewControllerMock surfaceUpdated:NO]);
2111 [viewController beginAppearanceTransition:NO animated:NO];
2112 [viewController endAppearanceTransition];
2116- (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback {
2117 NSMutableDictionary* replyMessage = [@{
2130 if (@available(iOS 13.4, *)) {
2137 OCMStub([mockEngine.
keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
2138 .andCall(
self, @selector(sendMessage:reply:));
2139 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
2148 [vc addInternalPlugins];
2150 [vc handlePressEvent:keyUpEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0)
2154 XCTAssert(
self.messageSent != nil);
2155 XCTAssert([
self.messageSent[
@"keymap"] isEqualToString:
@"ios"]);
2156 XCTAssert([
self.messageSent[
@"type"] isEqualToString:
@"keyup"]);
2157 XCTAssert([
self.messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
2158 XCTAssert([
self.messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
2159 XCTAssert([
self.messageSent[
@"characters"] isEqualToString:
@""]);
2160 XCTAssert([
self.messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@""]);
2161 [vc deregisterNotifications];
2165 if (@available(iOS 13.4, *)) {
2173 OCMStub([mockEngine.
keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
2174 .andCall(
self, @selector(sendMessage:reply:));
2175 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
2183 [vc addInternalPlugins];
2185 [vc handlePressEvent:keyDownEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0f, "A",
2190 XCTAssert(
self.messageSent != nil);
2191 XCTAssert([
self.messageSent[
@"keymap"] isEqualToString:
@"ios"]);
2192 XCTAssert([
self.messageSent[
@"type"] isEqualToString:
@"keydown"]);
2193 XCTAssert([
self.messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
2194 XCTAssert([
self.messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
2195 XCTAssert([
self.messageSent[
@"characters"] isEqualToString:
@"A"]);
2196 XCTAssert([
self.messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@"a"]);
2197 [vc deregisterNotifications];
2202 if (@available(iOS 13.4, *)) {
2208 OCMStub([keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
2209 .andCall(
self, @selector(sendMessage:reply:));
2210 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
2211 OCMStub([
self.mockEngine keyEventChannel]).andReturn(keyEventChannel);
2219 [vc addInternalPlugins];
2221 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseStationary, UIKeyboardHIDUsageKeyboardA,
2222 UIKeyModifierShift, 123.0)
2225 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseCancelled, UIKeyboardHIDUsageKeyboardA,
2226 UIKeyModifierShift, 123.0)
2229 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseChanged, UIKeyboardHIDUsageKeyboardA,
2230 UIKeyModifierShift, 123.0)
2234 XCTAssert(
self.messageSent == nil);
2235 OCMVerify(never(), [keyEventChannel sendMessage:[OCMArg any]]);
2236 [vc deregisterNotifications];
2240 if (@available(iOS 13.4, *)) {
2249 XCTAssertNotNil(vc);
2250 UIView*
view = vc.view;
2251 XCTAssertNotNil(view);
2252 NSArray* gestureRecognizers =
view.gestureRecognizers;
2253 XCTAssertNotNil(gestureRecognizers);
2256 for (
id gesture in gestureRecognizers) {
2257 if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
2262 XCTAssertTrue(found);
2266 if (@available(iOS 13.4, *)) {
2275 XCTAssertNotNil(vc);
2277 id mockPanGestureRecognizer = OCMClassMock([UIPanGestureRecognizer
class]);
2278 XCTAssertNotNil(mockPanGestureRecognizer);
2280 [vc discreteScrollEvent:mockPanGestureRecognizer];
2283 [[mockPanGestureRecognizer verify] locationInView:[OCMArg any]];
2284 [[[
self.mockEngine verify] ignoringNonObjectArgs]
2285 dispatchPointerDataPacket:std::make_unique<flutter::PointerDataPacket>(0)];
2288- (void)testFakeEventTimeStamp {
2292 XCTAssertNotNil(vc);
2295 int64_t current_micros = [[NSProcessInfo processInfo] systemUptime] * 1000 * 1000;
2296 int64_t interval_micros = current_micros - pointer_data.time_stamp;
2297 const int64_t tolerance_millis = 2;
2298 XCTAssertTrue(interval_micros / 1000 < tolerance_millis,
2299 @"PointerData.time_stamp should be equal to NSProcessInfo.systemUptime");
2302- (void)testSplashScreenViewCanSetNil {
2305 [flutterViewController setSplashScreenView:nil];
2308- (void)testLifeCycleNotificationApplicationBecameActive {
2313 UIWindow*
window = [[UIWindow alloc] init];
2314 [window addSubview:flutterViewController.view];
2315 flutterViewController.view.bounds = CGRectMake(0, 0, 100, 100);
2316 [flutterViewController viewDidLayoutSubviews];
2317 NSNotification* sceneNotification =
2318 [NSNotification notificationWithName:UISceneDidActivateNotification object:nil userInfo:nil];
2319 NSNotification* applicationNotification =
2320 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2323 id mockVC = OCMPartialMock(flutterViewController);
2324 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2325 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2326 OCMReject([mockVC sceneBecameActive:[OCMArg any]]);
2327 OCMVerify([mockVC applicationBecameActive:[OCMArg any]]);
2329 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2330 OCMVerify([mockVC surfaceUpdated:YES]);
2331 XCTestExpectation* timeoutApplicationLifeCycle =
2332 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2333 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2334 dispatch_get_main_queue(), ^{
2335 [timeoutApplicationLifeCycle fulfill];
2336 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2337 [flutterViewController deregisterNotifications];
2339 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2342- (void)testLifeCycleNotificationSceneBecameActive {
2343 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2344 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2345 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2348 [engine runWithEntrypoint:nil];
2351 UIWindow*
window = [[UIWindow alloc] init];
2352 [window addSubview:flutterViewController.view];
2353 flutterViewController.view.bounds = CGRectMake(0, 0, 100, 100);
2354 [flutterViewController viewDidLayoutSubviews];
2355 NSNotification* sceneNotification =
2356 [NSNotification notificationWithName:UISceneDidActivateNotification object:nil userInfo:nil];
2357 NSNotification* applicationNotification =
2358 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2361 id mockVC = OCMPartialMock(flutterViewController);
2362 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2363 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2364 OCMVerify([mockVC sceneBecameActive:[OCMArg any]]);
2365 OCMReject([mockVC applicationBecameActive:[OCMArg any]]);
2367 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2368 OCMVerify([mockVC surfaceUpdated:YES]);
2369 XCTestExpectation* timeoutApplicationLifeCycle =
2370 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2371 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2372 dispatch_get_main_queue(), ^{
2373 [timeoutApplicationLifeCycle fulfill];
2374 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2375 [flutterViewController deregisterNotifications];
2377 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2378 [mockBundle stopMocking];
2381- (void)testLifeCycleNotificationApplicationWillResignActive {
2386 NSNotification* sceneNotification =
2387 [NSNotification notificationWithName:UISceneWillDeactivateNotification
2390 NSNotification* applicationNotification =
2391 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2394 id mockVC = OCMPartialMock(flutterViewController);
2395 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2396 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2397 OCMReject([mockVC sceneWillResignActive:[OCMArg any]]);
2398 OCMVerify([mockVC applicationWillResignActive:[OCMArg any]]);
2399 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2400 [flutterViewController deregisterNotifications];
2403- (void)testLifeCycleNotificationSceneWillResignActive {
2404 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2405 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2406 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2409 [engine runWithEntrypoint:nil];
2412 NSNotification* sceneNotification =
2413 [NSNotification notificationWithName:UISceneWillDeactivateNotification
2416 NSNotification* applicationNotification =
2417 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2420 id mockVC = OCMPartialMock(flutterViewController);
2421 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2422 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2423 OCMVerify([mockVC sceneWillResignActive:[OCMArg any]]);
2424 OCMReject([mockVC applicationWillResignActive:[OCMArg any]]);
2425 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2426 [flutterViewController deregisterNotifications];
2427 [mockBundle stopMocking];
2430- (void)testLifeCycleNotificationApplicationWillTerminate {
2435 NSNotification* sceneNotification =
2436 [NSNotification notificationWithName:UISceneDidDisconnectNotification
2439 NSNotification* applicationNotification =
2440 [NSNotification notificationWithName:UIApplicationWillTerminateNotification
2443 id mockVC = OCMPartialMock(flutterViewController);
2444 id mockEngine = OCMPartialMock(
engine);
2445 OCMStub([mockVC
engine]).andReturn(mockEngine);
2446 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2447 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2448 OCMReject([mockVC sceneWillDisconnect:[OCMArg any]]);
2449 OCMVerify([mockVC applicationWillTerminate:[OCMArg any]]);
2450 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
2451 OCMVerify([mockEngine destroyContext]);
2452 [flutterViewController deregisterNotifications];
2455- (void)testLifeCycleNotificationSceneWillTerminate {
2456 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2457 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2458 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2461 [engine runWithEntrypoint:nil];
2464 NSNotification* sceneNotification =
2465 [NSNotification notificationWithName:UISceneDidDisconnectNotification
2468 NSNotification* applicationNotification =
2469 [NSNotification notificationWithName:UIApplicationWillTerminateNotification
2472 id mockVC = OCMPartialMock(flutterViewController);
2473 id mockEngine = OCMPartialMock(
engine);
2474 OCMStub([mockVC
engine]).andReturn(mockEngine);
2475 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2476 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2477 OCMVerify([mockVC sceneWillDisconnect:[OCMArg any]]);
2478 OCMReject([mockVC applicationWillTerminate:[OCMArg any]]);
2479 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
2480 OCMVerify([mockEngine destroyContext]);
2481 [flutterViewController deregisterNotifications];
2482 [mockBundle stopMocking];
2485- (void)testLifeCycleNotificationApplicationDidEnterBackground {
2490 NSNotification* sceneNotification =
2491 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2494 NSNotification* applicationNotification =
2495 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
2498 id mockVC = OCMPartialMock(flutterViewController);
2499 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2500 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2501 OCMReject([mockVC sceneDidEnterBackground:[OCMArg any]]);
2502 OCMVerify([mockVC applicationDidEnterBackground:[OCMArg any]]);
2504 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2505 OCMVerify([mockVC surfaceUpdated:NO]);
2506 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2507 [flutterViewController deregisterNotifications];
2510- (void)testLifeCycleNotificationSceneDidEnterBackground {
2511 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2512 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2513 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2516 [engine runWithEntrypoint:nil];
2519 NSNotification* sceneNotification =
2520 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2523 NSNotification* applicationNotification =
2524 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
2527 id mockVC = OCMPartialMock(flutterViewController);
2528 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2529 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2530 OCMVerify([mockVC sceneDidEnterBackground:[OCMArg any]]);
2531 OCMReject([mockVC applicationDidEnterBackground:[OCMArg any]]);
2533 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2534 OCMVerify([mockVC surfaceUpdated:NO]);
2535 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2536 [flutterViewController deregisterNotifications];
2537 [mockBundle stopMocking];
2550 testLifeCycleNotificationSceneDidEnterBackgroundIgnoresOtherScenes
API_AVAILABLE(ios(13.0)) {
2551 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2552 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2553 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2556 [engine runWithEntrypoint:nil];
2559 id mockVC = OCMPartialMock(flutterViewController);
2562 id flutterWindowScene = OCMClassMock([UIWindowScene
class]);
2563 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(flutterWindowScene);
2566 id auxiliaryScene = OCMClassMock([UIWindowScene
class]);
2570 NSNotification* auxiliarySceneNotification =
2571 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2572 object:auxiliaryScene
2574 [NSNotificationCenter.defaultCenter postNotification:auxiliarySceneNotification];
2575 OCMVerify(never(), [mockVC surfaceUpdated:[OCMArg any]]);
2576 OCMVerify(never(), [mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2578 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2582 NSNotification* flutterSceneNotification =
2583 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2584 object:flutterWindowScene
2586 [NSNotificationCenter.defaultCenter postNotification:flutterSceneNotification];
2587 OCMVerify([mockVC surfaceUpdated:NO]);
2588 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2590 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2592 [flutterViewController deregisterNotifications];
2593 [mockBundle stopMocking];
2596- (void)testLifeCycleNotificationApplicationWillEnterForeground {
2601 NSNotification* sceneNotification =
2602 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2605 NSNotification* applicationNotification =
2606 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2609 id mockVC = OCMPartialMock(flutterViewController);
2610 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2611 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2612 OCMReject([mockVC sceneWillEnterForeground:[OCMArg any]]);
2613 OCMVerify([mockVC applicationWillEnterForeground:[OCMArg any]]);
2614 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2615 [flutterViewController deregisterNotifications];
2618- (void)testLifeCycleNotificationSceneWillEnterForeground {
2619 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2620 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2621 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2624 [engine runWithEntrypoint:nil];
2627 NSNotification* sceneNotification =
2628 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2631 NSNotification* applicationNotification =
2632 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2635 id mockVC = OCMPartialMock(flutterViewController);
2636 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2637 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2638 OCMVerify([mockVC sceneWillEnterForeground:[OCMArg any]]);
2639 OCMReject([mockVC applicationWillEnterForeground:[OCMArg any]]);
2640 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2641 [flutterViewController deregisterNotifications];
2642 [mockBundle stopMocking];
2645- (void)testLifeCycleNotificationCancelledInvalidResumed {
2650 NSNotification* applicationDidBecomeActiveNotification =
2651 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2654 NSNotification* applicationWillResignActiveNotification =
2655 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2658 id mockVC = OCMPartialMock(flutterViewController);
2659 [NSNotificationCenter.defaultCenter postNotification:applicationDidBecomeActiveNotification];
2660 [NSNotificationCenter.defaultCenter postNotification:applicationWillResignActiveNotification];
2661 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2663 XCTestExpectation* timeoutApplicationLifeCycle =
2664 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2665 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2666 dispatch_get_main_queue(), ^{
2667 OCMReject([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2668 [timeoutApplicationLifeCycle fulfill];
2669 [flutterViewController deregisterNotifications];
2671 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2674- (void)testSetupKeyboardAnimationVsyncClientWillCreateNewVsyncClientForFlutterViewController {
2675 id bundleMock = OCMPartialMock([NSBundle mainBundle]);
2676 OCMStub([bundleMock objectForInfoDictionaryKey:
@"CADisableMinimumFrameDurationOnPhone"])
2678 id mockDisplayLinkManager = OCMPartialMock([FlutterDisplayLinkManager shared]);
2679 [
self addTeardownBlock:^{
2680 [mockDisplayLinkManager stopMocking];
2682 double maxFrameRate = 120;
2683 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2691 [viewController.keyboardInsetManager setUpKeyboardAnimationVsyncClient:callback];
2692 XCTAssertNotNil(
viewController.keyboardInsetManager.keyboardAnimationVSyncClient);
2693 CADisplayLink* link =
2694 viewController.keyboardInsetManager.keyboardAnimationVSyncClient.displayLink;
2695 XCTAssertNotNil(link);
2696 CADisplayLink* linkMock = OCMPartialMock(link);
2697 if (@available(iOS 15.0, *)) {
2698 CAFrameRateRange range = CAFrameRateRangeMake(maxFrameRate / 2, maxFrameRate, maxFrameRate);
2699 NSValue* rangeValue = [NSValue valueWithBytes:&range objCType:@encode(CAFrameRateRange)];
2700 [[[(id)linkMock stub] andReturnValue:rangeValue] preferredFrameRateRange];
2702 XCTAssertEqual(linkMock.preferredFrameRateRange.maximum, maxFrameRate);
2703 XCTAssertEqual(linkMock.preferredFrameRateRange.preferred, maxFrameRate);
2704 XCTAssertEqual(linkMock.preferredFrameRateRange.minimum, maxFrameRate / 2);
2706 [[[(id)linkMock stub] andReturnValue:@(maxFrameRate)] preferredFramesPerSecond];
2707 XCTAssertEqual(linkMock.preferredFramesPerSecond, maxFrameRate);
2712 testCreateTouchRateCorrectionVSyncClientWillCreateVsyncClientWhenRefreshRateIsLargerThan60HZ {
2713 id mockDisplayLinkManager = OCMPartialMock([FlutterDisplayLinkManager shared]);
2714 [
self addTeardownBlock:^{
2715 [mockDisplayLinkManager stopMocking];
2717 double maxFrameRate = 120;
2718 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2724 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2728- (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateNewVSyncClientWhenClientAlreadyExists {
2729 id mockDisplayLinkManager = OCMPartialMock([FlutterDisplayLinkManager shared]);
2730 [
self addTeardownBlock:^{
2731 [mockDisplayLinkManager stopMocking];
2733 double maxFrameRate = 120;
2734 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2741 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2742 FlutterVSyncClient* clientBefore =
viewController.touchRateCorrectionVSyncClient;
2743 XCTAssertNotNil(clientBefore);
2745 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2746 FlutterVSyncClient* clientAfter =
viewController.touchRateCorrectionVSyncClient;
2747 XCTAssertNotNil(clientAfter);
2749 XCTAssertTrue(clientBefore == clientAfter);
2752- (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateVsyncClientWhenRefreshRateIs60HZ {
2753 id mockDisplayLinkManager = OCMPartialMock([FlutterDisplayLinkManager shared]);
2754 [
self addTeardownBlock:^{
2755 [mockDisplayLinkManager stopMocking];
2757 double maxFrameRate = 60;
2758 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2764 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2768- (void)testTriggerTouchRateCorrectionVSyncClientCorrectly {
2769 id mockDisplayLinkManager = OCMPartialMock([FlutterDisplayLinkManager shared]);
2770 [
self addTeardownBlock:^{
2771 [mockDisplayLinkManager stopMocking];
2773 double maxFrameRate = 120;
2774 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2780 [viewController loadView];
2781 [viewController viewDidLoad];
2783 FlutterVSyncClient* client =
viewController.touchRateCorrectionVSyncClient;
2784 CADisplayLink* link = client.displayLink;
2786 UITouch* fakeTouchBegan = [[UITouch alloc] init];
2787 fakeTouchBegan.phase = UITouchPhaseBegan;
2789 UITouch* fakeTouchMove = [[UITouch alloc] init];
2790 fakeTouchMove.phase = UITouchPhaseMoved;
2792 UITouch* fakeTouchEnd = [[UITouch alloc] init];
2793 fakeTouchEnd.phase = UITouchPhaseEnded;
2795 UITouch* fakeTouchCancelled = [[UITouch alloc] init];
2796 fakeTouchCancelled.phase = UITouchPhaseCancelled;
2799 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchBegan, nil]];
2800 XCTAssertFalse(link.isPaused);
2803 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd, nil]];
2804 XCTAssertTrue(link.isPaused);
2807 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchMove, nil]];
2808 XCTAssertFalse(link.isPaused);
2811 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchCancelled, nil]];
2812 XCTAssertTrue(link.isPaused);
2815 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2816 initWithObjects:fakeTouchBegan, fakeTouchEnd, nil]];
2817 XCTAssertFalse(link.isPaused);
2820 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd,
2821 fakeTouchCancelled, nil]];
2822 XCTAssertTrue(link.isPaused);
2825 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2826 initWithObjects:fakeTouchMove, fakeTouchEnd, nil]];
2827 XCTAssertFalse(link.isPaused);
2830- (void)testFlutterViewControllerStartKeyboardAnimationWillCreateVsyncClientCorrectly {
2831 id mockDisplayLink = OCMClassMock([CADisplayLink
class]);
2832 OCMStub(ClassMethod([mockDisplayLink displayLinkWithTarget:[OCMArg any]
2833 selector:sel_registerName(
"onDisplayLink:")]))
2834 .andReturn(mockDisplayLink);
2841 FlutterKeyboardInsetManager*
manager =
2842 [[FlutterKeyboardInsetManager alloc] initWithDelegate:delegate
2843 displayLinkManager:FlutterDisplayLinkManager.shared];
2844 manager.targetViewInsetBottom = 100;
2845 [manager startKeyBoardAnimation:0.25];
2847 XCTAssertNotNil(
manager.keyboardAnimationVSyncClient);
2849 [manager invalidate];
2850 [mockDisplayLink stopMocking];
2854 testSetupKeyboardAnimationVsyncClientWillNotCreateNewVsyncClientWhenKeyboardAnimationCallbackIsNil {
2860 [viewController.keyboardInsetManager setUpKeyboardAnimationVsyncClient:nil];
2861 XCTAssertNil(
viewController.keyboardInsetManager.keyboardAnimationVSyncClient);
2864- (void)testSupportsShowingSystemContextMenuForIOS16AndAbove {
2870 BOOL supportsShowingSystemContextMenu = [viewController supportsShowingSystemContextMenu];
2871 if (@available(iOS 16.0, *)) {
2872 XCTAssertTrue(supportsShowingSystemContextMenu);
2874 XCTAssertFalse(supportsShowingSystemContextMenu);
2878- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsActive {
2884 id mockApplication = OCMClassMock([UIApplication
class]);
2885 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateActive);
2886 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2891- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsBackground {
2897 id mockApplication = OCMClassMock([UIApplication
class]);
2898 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateBackground);
2899 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2904- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsInactive {
2910 id mockApplication = OCMClassMock([UIApplication
class]);
2911 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateInactive);
2912 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2917- (void)testStateIsActiveAndBackgroundWhenSceneStateIsActive {
2918 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2919 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2920 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2923 [engine runWithEntrypoint:nil];
2928 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateForegroundActive);
2932 [mockBundle stopMocking];
2933 [mockVC stopMocking];
2936- (void)testStateIsActiveAndBackgroundWhenSceneStateIsBackground {
2937 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2938 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2939 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2942 [engine runWithEntrypoint:nil];
2947 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateBackground);
2951 [mockBundle stopMocking];
2952 [mockVC stopMocking];
2955- (void)testStateIsActiveAndBackgroundWhenSceneStateIsInactive {
2956 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2957 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2958 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2961 [engine runWithEntrypoint:nil];
2966 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateForegroundInactive);
2970 [mockBundle stopMocking];
2971 [mockVC stopMocking];
2974- (void)testPerformImplicitEngineCallbacks {
2975 id mockRegistrant = OCMProtocolMock(
@protocol(FlutterPluginRegistrant));
2976 id appDelegate = [[UIApplication sharedApplication] delegate];
2977 [appDelegate setMockLaunchEngine:self.mockEngine];
2978 UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Flutter" bundle:nil];
2979 XCTAssertTrue([appDelegate respondsToSelector:@selector(setPluginRegistrant:)]);
2980 [appDelegate setPluginRegistrant:mockRegistrant];
2983 [appDelegate setPluginRegistrant:nil];
2985 OCMVerify([
self.mockEngine performImplicitEngineCallback]);
2986 [appDelegate setMockLaunchEngine:nil];
2989- (void)testPerformImplicitEngineCallbacksUsesAppLaunchEventFallbacks {
2995 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2996 OCMStub([viewControllerMock awokenFromNib]).andReturn(YES);
2998 id mockApplication = OCMClassMock([UIApplication
class]);
2999 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
3001 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
3002 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
3004 id mockScene = OCMClassMock([UIScene
class]);
3005 id mockSceneDelegate = OCMProtocolMock(
@protocol(UISceneDelegate));
3006 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
3007 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockScene]);
3011 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
3013 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
3014 OCMVerify([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
3015 OCMVerify([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
3018- (void)testPerformImplicitEngineCallbacksNoAppLaunchEventFallbacksWhenNoStoryboard {
3024 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
3025 OCMStub([viewControllerMock awokenFromNib]).andReturn(NO);
3027 id mockApplication = OCMClassMock([UIApplication
class]);
3028 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
3030 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
3031 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
3033 id mockScene = OCMClassMock([UIScene
class]);
3034 id mockSceneDelegate = OCMProtocolMock(
@protocol(UISceneDelegate));
3035 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
3036 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockScene]);
3040 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
3042 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
3043 OCMReject([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
3044 OCMReject([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
3047- (void)testPerformImplicitEngineCallbacksNoAppLaunchEventFallbacksWhenNoScenes {
3053 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
3054 OCMStub([viewControllerMock awokenFromNib]).andReturn(YES);
3056 id mockApplication = OCMClassMock([UIApplication
class]);
3057 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
3059 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
3060 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
3064 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
3066 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
3067 OCMReject([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
3068 OCMReject([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
3071- (void)testGrabLaunchEngine {
3072 id appDelegate = [[UIApplication sharedApplication] delegate];
3073 XCTAssertTrue([appDelegate respondsToSelector:@selector(setMockLaunchEngine:)]);
3074 [appDelegate setMockLaunchEngine:self.mockEngine];
3075 UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Flutter" bundle:nil];
3076 XCTAssertTrue(storyboard);
3082 [appDelegate setMockLaunchEngine:nil];
3085- (void)testDoesntGrabLaunchEngine {
3086 id appDelegate = [[UIApplication sharedApplication] delegate];
3087 XCTAssertTrue([appDelegate respondsToSelector:@selector(setMockLaunchEngine:)]);
3088 [appDelegate setMockLaunchEngine:self.mockEngine];
3090 XCTAssertNotNil(flutterViewController.
engine);
3091 XCTAssertNotEqual(flutterViewController.
engine,
self.mockEngine);
3092 [appDelegate setMockLaunchEngine:nil];
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterReply)(id _Nullable reply)
void hideKeyboardImmediately()
BOOL keyboardAnimationIsShowing
BOOL isKeyboardInOrTransitioningFromBackground
void ensureViewportMetricsIsCorrect()
UIView * keyboardAnimationView()
SpringAnimation * keyboardSpringAnimation()
CGFloat targetViewInsetBottom
void invalidateKeyboardAnimationVSyncClient()
id< FlutterKeyboardInsetManagerDelegate > delegate
FlutterVSyncClient * keyboardAnimationVSyncClient
static CFStringRef kMessageLoopCFRunLoopMode
static void EnsureInitializedForCurrentThread()
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
void RunExpiredTasksNow()
void(* FlutterKeyEventCallback)(bool, void *)
FlutterDesktopBinaryReply callback
FlutterVSyncClient * keyboardAnimationVSyncClient
BOOL didCallInvalidateKeyboardAnimationVSyncClient
BOOL didCallEnsureViewportMetricsIsCorrect
BOOL runWithEntrypoint:(nullable NSString *entrypoint)
FlutterViewController * viewController
BOOL runWithEntrypoint:(nullable NSString *entrypoint)
FlutterTextInputPlugin * textInputPlugin
FlutterBasicMessageChannel * lifecycleChannel
BOOL didCallNotifyLowMemory
FlutterViewController * viewController
FlutterBasicMessageChannel * keyEventChannel
NSObject< FlutterBinaryMessenger > * binaryMessenger
BOOL mockIsPadInSlideOverOrStageManagerMode
FlutterFMLTaskRunner * mockTaskRunner
FlutterEngine * mockEngine
CGRect mockConvertedViewRect
void(^ updateViewportMetricsBlock)(CGFloat inset)
void(^ FlutterSendKeyEvent)(const FlutterKeyEvent &, _Nullable FlutterKeyEventCallback, void *_Nullable)
UITextSmartQuotesType smartQuotesType API_AVAILABLE(ios(11.0))
FlutterViewController * viewController
FlutterTextInputPlugin * textInputPlugin
NSNotificationName const FlutterViewControllerWillDealloc
void(^ FlutterKeyboardAnimationCallback)(NSTimeInterval targetTime)
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font manager