5#import <OCMock/OCMock.h>
6#import <XCTest/XCTest.h>
35@interface FlutterKeyboardInsetManager (Test)
36- (void)setUpKeyboardAnimationVsyncClient:
40@property(nonatomic, weak) id<FlutterKeyboardInsetManagerDelegate>
delegate;
43- (void)handleKeyboardNotification:(NSNotification*)notification;
44- (
BOOL)isKeyboardNotificationForDifferentView:(NSNotification*)notification;
45- (CGFloat)calculateKeyboardInset:(CGRect)keyboardFrame keyboardMode:(
int)keyboardMode;
46- (
BOOL)shouldIgnoreKeyboardNotification:(NSNotification*)notification;
47- (FlutterKeyboardMode)calculateKeyboardAttachMode:(NSNotification*)notification;
48- (CGFloat)calculateMultitaskingAdjustment:(CGRect)screenRect keyboardFrame:(CGRect)keyboardFrame;
49- (void)startKeyBoardAnimation:(NSTimeInterval)duration;
53- (void)setUpKeyboardSpringAnimationIfNeeded:(CAAnimation*)keyboardAnimation;
59@property(nonatomic, assign)
BOOL didCallStartKeyboardAnimation;
63- (void)startKeyBoardAnimation:(NSTimeInterval)duration {
64 [
super startKeyBoardAnimation:duration];
65 self.didCallStartKeyboardAnimation = YES;
70@property(nonatomic, strong) UIScreen* mockScreen;
71@property(nonatomic, strong) UIView* mockView;
73@property(nonatomic, assign) CGFloat currentInset;
74@property(nonatomic, copy)
void (^updateViewportMetricsBlock)(CGFloat inset);
75@property(nonatomic, assign)
BOOL isViewLoaded;
76@property(nonatomic, assign)
BOOL mockIsPadInSlideOverOrStageManagerMode;
77@property(nonatomic, assign) CGRect mockConvertedViewRect;
81- (void)updateViewportMetricsWithInset:(CGFloat)inset {
83 if (
self.updateViewportMetricsBlock) {
84 self.updateViewportMetricsBlock(inset);
87- (CGFloat)physicalViewInsetBottom {
88 return self.currentInset;
94 return self.mockEngine;
96- (UIScreen*)flutterScreenIfViewLoaded {
97 return self.mockScreen;
99- (
BOOL)isPadInSlideOverOrStageManagerMode {
100 return self.mockIsPadInSlideOverOrStageManagerMode;
102- (CGRect)convertViewRectToScreen:(CGRect)rect {
103 return self.mockConvertedViewRect;
115@property(nonatomic, assign)
BOOL didCallNotifyLowMemory;
122 userData:(nullable
void*)userData;
125- (
BOOL)runWithEntrypoint:(nullable NSString*)entrypoint;
132@synthesize lifecycleChannel;
133@synthesize keyEventChannel;
137- (void)notifyLowMemory {
138 _didCallNotifyLowMemory = YES;
141- (instancetype)init {
142 if (
self = [super init]) {
145 initWithTaskRunner:fml::MessageLoop::GetCurrent().GetTaskRunner()];
151 return _uiTaskRunner;
154- (
BOOL)runWithEntrypoint:(nullable NSString*)entrypoint {
164 userData:(
void*)userData API_AVAILABLE(ios(9.0)) {
179- (
BOOL)createShell:(NSString*)entrypoint
180 libraryURI:(NSString*)libraryURI
181 initialRoute:(NSString*)initialRoute;
187@interface FlutterEngine (TestLowMemory)
188- (void)notifyLowMemory;
214@interface FlutterKeyboardManager (Tests)
215@property(nonatomic, retain, readonly)
216 NSMutableArray<id<FlutterKeyPrimaryResponder>>* primaryResponders;
219@interface FlutterEmbedderKeyResponder (Tests)
223@interface NSObject (Tests)
227@interface FlutterViewController (Tests) <FlutterKeyboardInsetManagerDelegate>
229@property(nonatomic, assign)
double targetViewInsetBottom;
230@property(nonatomic, assign)
BOOL keyboardAnimationIsShowing;
231@property(nonatomic, strong) FlutterVSyncClient* keyboardAnimationVSyncClient;
232@property(nonatomic, strong) FlutterVSyncClient* touchRateCorrectionVSyncClient;
233@property(nonatomic, assign)
BOOL awokenFromNib;
235- (void)createTouchRateCorrectionVSyncClientIfNeeded;
236- (void)surfaceUpdated:(
BOOL)appeared;
237- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences;
238- (void)handlePressEvent:(FlutterUIPressProxy*)press
239 nextAction:(
void (^)())next API_AVAILABLE(ios(13.4));
240- (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer;
241- (void)updateViewportMetricsIfNeeded;
242- (void)updateAutoResizeConstraints;
243- (void)checkAndUpdateAutoResizeConstraints;
244- (void)onUserSettingsChanged:(NSNotification*)notification;
245- (void)applicationWillTerminate:(NSNotification*)notification;
246- (void)goToApplicationLifecycle:(nonnull NSString*)state;
248- (void)addInternalPlugins;
249- (
flutter::PointerData)generatePointerDataForFake;
251 initialRoute:(nullable NSString*)initialRoute;
252- (void)applicationBecameActive:(NSNotification*)notification;
253- (void)applicationWillResignActive:(NSNotification*)notification;
254- (void)applicationWillTerminate:(NSNotification*)notification;
255- (void)applicationDidEnterBackground:(NSNotification*)notification;
256- (void)applicationWillEnterForeground:(NSNotification*)notification;
257- (void)sceneBecameActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
258- (void)sceneWillResignActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
259- (void)sceneWillDisconnect:(NSNotification*)notification API_AVAILABLE(ios(13.0));
260- (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
261- (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
262- (void)triggerTouchRateCorrectionIfNeeded:(NSSet*)touches;
263- (void)onAccessibilityStatusChanged:(NSNotification*)notification;
267@property(nonatomic, strong)
id mockEngine;
268@property(nonatomic, strong)
id mockTextInputPlugin;
269@property(nonatomic, strong)
id messageSent;
270- (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback;
275@property(nonatomic, readwrite) UITouchPhase phase;
285 self.messageSent = nil;
291 [
self.mockEngine stopMocking];
292 self.mockEngine = nil;
293 self.mockTextInputPlugin = nil;
294 self.messageSent = nil;
297- (
id)setUpMockScreen {
298 UIScreen* mockScreen = OCMClassMock([UIScreen
class]);
300 CGRect screenBounds = CGRectMake(0, 0, 1170, 2532);
301 OCMStub([mockScreen bounds]).andReturn(screenBounds);
302 CGFloat screenScale = 1;
303 OCMStub([mockScreen scale]).andReturn(screenScale);
309 screen:(UIScreen*)screen
310 viewFrame:(CGRect)viewFrame
311 convertedFrame:(CGRect)convertedFrame {
312 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
313 UIView*
view = [[UIView alloc] initWithFrame:viewFrame];
314 UIWindow*
window = [[UIWindow alloc] initWithFrame:viewFrame];
315 [window addSubview:view];
317 OCMStub([viewControllerMock viewIfLoaded]).andReturn(view);
318 OCMStub([viewControllerMock view]).andReturn(view);
323- (void)testViewDidLoadWillInvokeCreateTouchRateCorrectionVSyncClient {
330 [viewControllerMock loadView];
331 [viewControllerMock viewDidLoad];
332 OCMVerify([viewControllerMock createTouchRateCorrectionVSyncClientIfNeeded]);
335- (void)testStartKeyboardAnimationWillInvokeSetupKeyboardSpringAnimationIfNeeded {
342 OCMStub([viewControllerMock isViewLoaded]).andReturn(YES);
343 [viewControllerMock view];
345 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
349 id managerMock = OCMPartialMock(
viewController.keyboardInsetManager);
352 id viewClassMock = OCMClassMock([UIView
class]);
353 OCMStub([viewClassMock animateWithDuration:0.25 animations:[OCMArg any] completion:[OCMArg any]])
354 .andDo(^(NSInvocation* invocation) {
355 void (^animations)(void);
356 [invocation getArgument:&animations atIndex:3];
360 void (^completion)(
BOOL finished);
361 [invocation getArgument:&completion atIndex:4];
368 [managerMock startKeyBoardAnimation:0.25];
370 OCMVerify([managerMock setUpKeyboardSpringAnimationIfNeeded:[OCMArg any]]);
373- (void)testSetupKeyboardSpringAnimationIfNeeded {
378 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
379 UIScreen* screen = [
self setUpMockScreen];
380 CGRect viewFrame = screen.bounds;
381 [
self setUpMockView:viewControllerMock
384 convertedFrame:viewFrame];
387 [viewController.keyboardInsetManager setUpKeyboardSpringAnimationIfNeeded:nil];
389 [viewController.keyboardInsetManager keyboardSpringAnimation];
390 XCTAssertTrue(keyboardSpringAnimation == nil);
393 CABasicAnimation* nonSpringAnimation = [CABasicAnimation animation];
394 nonSpringAnimation.duration = 1.0;
395 nonSpringAnimation.fromValue = [NSNumber numberWithFloat:0.0];
396 nonSpringAnimation.toValue = [NSNumber numberWithFloat:1.0];
397 nonSpringAnimation.keyPath =
@"position";
398 [viewController.keyboardInsetManager setUpKeyboardSpringAnimationIfNeeded:nonSpringAnimation];
399 keyboardSpringAnimation = [viewController.keyboardInsetManager keyboardSpringAnimation];
401 XCTAssertTrue(keyboardSpringAnimation == nil);
404 CASpringAnimation* springAnimation = [CASpringAnimation animation];
405 springAnimation.mass = 1.0;
406 springAnimation.stiffness = 100.0;
407 springAnimation.damping = 10.0;
408 springAnimation.keyPath =
@"position";
409 springAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
410 springAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
411 [viewController.keyboardInsetManager setUpKeyboardSpringAnimationIfNeeded:springAnimation];
412 keyboardSpringAnimation = [viewController.keyboardInsetManager keyboardSpringAnimation];
413 XCTAssertTrue(keyboardSpringAnimation != nil);
423- (void)testKeyboardAnimationIsShowingAndCompounding {
424 UIScreen* screen = [
self setUpMockScreen];
425 CGFloat screenWidth = screen.bounds.size.width;
426 CGFloat screenHeight = screen.bounds.size.height;
427 CGRect viewFrame = screen.bounds;
441 id managerMock = OCMPartialMock(manager);
442 OCMStub([managerMock shouldIgnoreKeyboardNotification:[OCMArg any]]).andReturn(NO);
446 CGRect initialShowKeyboardBeginFrame = CGRectMake(0, screenHeight, screenWidth, 250);
447 CGRect initialShowKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
448 NSNotification* fakeNotification = [NSNotification
449 notificationWithName:UIKeyboardWillChangeFrameNotification
452 @"UIKeyboardFrameBeginUserInfoKey" : @(initialShowKeyboardBeginFrame),
453 @"UIKeyboardFrameEndUserInfoKey" : @(initialShowKeyboardEndFrame),
454 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
455 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
457 manager.targetViewInsetBottom = 0;
458 [managerMock handleKeyboardNotification:fakeNotification];
459 BOOL isShowingAnimation1 =
manager.keyboardAnimationIsShowing;
460 XCTAssertTrue(isShowingAnimation1);
463 CGRect compoundingShowKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
464 CGRect compoundingShowKeyboardEndFrame = CGRectMake(0, screenHeight - 500, screenWidth, 500);
465 fakeNotification = [NSNotification
466 notificationWithName:UIKeyboardWillChangeFrameNotification
469 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingShowKeyboardBeginFrame),
470 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingShowKeyboardEndFrame),
471 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
472 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
475 [managerMock handleKeyboardNotification:fakeNotification];
476 BOOL isShowingAnimation2 =
manager.keyboardAnimationIsShowing;
477 XCTAssertTrue(isShowingAnimation2);
478 XCTAssertTrue(isShowingAnimation1 == isShowingAnimation2);
481 CGRect initialHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 500, screenWidth, 250);
482 CGRect initialHideKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
483 fakeNotification = [NSNotification
484 notificationWithName:UIKeyboardWillChangeFrameNotification
487 @"UIKeyboardFrameBeginUserInfoKey" : @(initialHideKeyboardBeginFrame),
488 @"UIKeyboardFrameEndUserInfoKey" : @(initialHideKeyboardEndFrame),
489 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
490 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
493 [managerMock handleKeyboardNotification:fakeNotification];
494 BOOL isShowingAnimation3 =
manager.keyboardAnimationIsShowing;
495 XCTAssertFalse(isShowingAnimation3);
496 XCTAssertTrue(isShowingAnimation2 != isShowingAnimation3);
499 CGRect compoundingHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
500 CGRect compoundingHideKeyboardEndFrame = CGRectMake(0, screenHeight, screenWidth, 500);
501 fakeNotification = [NSNotification
502 notificationWithName:UIKeyboardWillChangeFrameNotification
505 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingHideKeyboardBeginFrame),
506 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingHideKeyboardEndFrame),
507 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
508 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
511 [managerMock handleKeyboardNotification:fakeNotification];
512 BOOL isShowingAnimation4 =
manager.keyboardAnimationIsShowing;
513 XCTAssertFalse(isShowingAnimation4);
514 XCTAssertTrue(isShowingAnimation3 == isShowingAnimation4);
517- (void)testShouldIgnoreKeyboardNotification {
527 initWithDelegate:(id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock];
530 UIScreen* screen = [
self setUpMockScreen];
531 CGRect viewFrame = screen.bounds;
532 [
self setUpMockView:viewControllerMock
535 convertedFrame:viewFrame];
537 CGFloat screenWidth = screen.bounds.size.width;
538 CGFloat screenHeight = screen.bounds.size.height;
539 CGRect emptyKeyboard = CGRectZero;
540 CGRect zeroHeightKeyboard = CGRectMake(0, 0, screenWidth, 0);
541 CGRect validKeyboardEndFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
545 NSNotification* notification =
546 [NSNotification notificationWithName:UIKeyboardWillHideNotification
549 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
550 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
551 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
554 BOOL shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
555 XCTAssertTrue(shouldIgnore == NO);
559 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
562 @"UIKeyboardFrameEndUserInfoKey" : @(emptyKeyboard),
563 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
564 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
566 shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
567 XCTAssertTrue(shouldIgnore == YES);
572 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
575 @"UIKeyboardFrameEndUserInfoKey" : @(zeroHeightKeyboard),
576 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
577 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
579 shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
580 XCTAssertTrue(shouldIgnore == NO);
585 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
588 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
589 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
590 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
592 shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
593 XCTAssertTrue(shouldIgnore == YES);
598 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
601 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
602 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
603 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
605 shouldIgnore = [managerMock shouldIgnoreKeyboardNotification:notification];
606 XCTAssertTrue(shouldIgnore == NO);
608 [(id)viewControllerMock stopMocking];
611- (void)testKeyboardAnimationWillNotCrashWhenEngineDestroyed {
617 [viewController.keyboardInsetManager
618 setUpKeyboardAnimationVsyncClient:^(NSTimeInterval targetTime){
623- (void)testKeyboardAnimationFirstVsyncCallbackCalculatesSafeInset {
630 OCMStub([viewControllerMock isViewLoaded]).andReturn(YES);
631 [viewControllerMock view];
633 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
637 XCTestExpectation* expectation = [
self expectationWithDescription:@"metrics updated"];
639 __block CGFloat capturedInset = -1.0;
640 __block
BOOL fulfilled = NO;
641 OCMStub([viewControllerMock updateViewportMetricsWithInset:0])
642 .ignoringNonObjectArgs()
643 .andDo(^(NSInvocation* invocation) {
644 [invocation getArgument:&capturedInset atIndex:2];
649 [viewController.keyboardInsetManager invalidateKeyboardAnimationVSyncClient];
650 [expectation fulfill];
655 CASpringAnimation* springAnimation = [CASpringAnimation animation];
656 springAnimation.mass = 1.0;
657 springAnimation.stiffness = 100.0;
658 springAnimation.damping = 10.0;
659 springAnimation.keyPath =
@"position";
661 viewController.keyboardInsetManager.targetViewInsetBottom = 300.0;
664 [viewController.keyboardInsetManager startKeyBoardAnimation:0.25];
665 [viewController.keyboardInsetManager setUpKeyboardSpringAnimationIfNeeded:springAnimation];
668 FlutterVSyncClient* client =
viewController.keyboardInsetManager.keyboardAnimationVSyncClient;
669 [client onDisplayLink:client.displayLink];
672 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
675 XCTAssertFalse(isnan(capturedInset));
676 XCTAssertFalse(isinf(capturedInset));
677 XCTAssertGreaterThanOrEqual(capturedInset, 0.0);
678 XCTAssertLessThan(capturedInset, 300.0);
681- (void)testKeyboardAnimationWillWaitUIThreadVsync {
694 const int delayTime = 1;
699 id mockCADisplayLink = OCMClassMock([CADisplayLink
class]);
701 ClassMethod([mockCADisplayLink displayLinkWithTarget:[OCMArg any]
702 selector:sel_registerName(
"onDisplayLink:")]));
704 XCTestExpectation* expectation = [
self expectationWithDescription:@"keyboard animation callback"];
705 __block CFTimeInterval fulfillTime = 0;
706 CFTimeInterval startTime = CACurrentMediaTime();
707 [viewController.keyboardInsetManager
708 setUpKeyboardAnimationVsyncClient:^(NSTimeInterval targetTime) {
709 fulfillTime = CACurrentMediaTime();
710 [expectation fulfill];
713 FlutterVSyncClient* client =
viewController.keyboardInsetManager.keyboardAnimationVSyncClient;
714 [client onDisplayLink:client.displayLink];
716 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
717 NSTimeInterval epsilon = 0.005;
718 XCTAssertGreaterThanOrEqual(fulfillTime - startTime, delayTime - epsilon);
720 [mockCADisplayLink stopMocking];
723- (void)testCalculateKeyboardAttachMode {
729 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
730 UIScreen* screen = [
self setUpMockScreen];
731 CGRect viewFrame = screen.bounds;
732 [
self setUpMockView:viewControllerMock
735 convertedFrame:viewFrame];
737 CGFloat screenWidth = screen.bounds.size.width;
738 CGFloat screenHeight = screen.bounds.size.height;
741 CGRect keyboardFrame = CGRectZero;
742 NSNotification* notification =
743 [NSNotification notificationWithName:UIKeyboardWillHideNotification
746 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
747 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
748 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
750 FlutterKeyboardMode keyboardMode =
751 [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
752 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
755 keyboardFrame = CGRectZero;
756 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
759 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
760 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
761 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
763 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
764 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
767 keyboardFrame = CGRectMake(0, 0, screenWidth, 0);
768 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
771 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
772 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
773 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
775 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
776 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
779 keyboardFrame = CGRectMake(0, 0, 320, 320);
780 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
783 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
784 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
785 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
787 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
788 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
791 keyboardFrame = CGRectMake(0, 0, screenWidth, 320);
792 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
795 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
796 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
797 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
799 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
800 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
803 keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
804 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
807 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
808 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
809 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
811 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
812 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
815 CGFloat longDecimalHeight = 320.666666666666666;
816 keyboardFrame = CGRectMake(0, screenHeight - longDecimalHeight, screenWidth, longDecimalHeight);
817 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
820 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
821 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
822 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
824 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
825 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
828 keyboardFrame = CGRectMake(0, screenHeight - .0000001, screenWidth, longDecimalHeight);
829 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
832 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
833 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
834 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
836 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
837 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
840 keyboardFrame = CGRectMake(0, screenHeight, screenWidth, 320);
841 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
844 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
845 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
846 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
848 keyboardMode = [viewController.keyboardInsetManager calculateKeyboardAttachMode:notification];
849 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
852- (void)testCalculateMultitaskingAdjustment {
853 UIScreen* screen = [UIScreen mainScreen];
854 CGFloat screenWidth = screen.bounds.size.width;
855 CGFloat screenHeight = screen.bounds.size.height;
856 CGRect screenRect = screen.bounds;
857 CGRect convertedViewFrame = CGRectMake(0, 0, 320, screenHeight - 20);
858 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
870 CGFloat adjustment = [manager calculateMultitaskingAdjustment:screenRect
871 keyboardFrame:keyboardFrame];
872 XCTAssertTrue(adjustment == 20);
875- (void)testCalculateKeyboardInset {
876 UIScreen* screen = [UIScreen mainScreen];
877 CGFloat screenWidth = screen.bounds.size.width;
878 CGFloat screenHeight = screen.bounds.size.height;
879 CGRect convertedViewFrame = CGRectMake(0, 0, 320, screenHeight - 20);
880 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
891 CGFloat inset = [manager calculateKeyboardInset:keyboardFrame
892 keyboardMode:FlutterKeyboardModeDocked];
893 XCTAssertTrue(inset == 300 * screen.scale);
896- (void)testHandleKeyboardNotification {
897 UIScreen* screen = [
self setUpMockScreen];
898 CGFloat screenWidth = screen.bounds.size.width;
899 CGFloat screenHeight = screen.bounds.size.height;
900 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
901 CGRect viewFrame = screen.bounds;
903 NSNotification* notification = [NSNotification
904 notificationWithName:UIKeyboardWillShowNotification
907 @"UIKeyboardFrameEndUserInfoKey" : [NSValue valueWithCGRect:keyboardFrame],
908 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
909 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
931- (void)testEnsureBottomInsetIsZeroWhenKeyboardDismissed {
940 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
944 CGRect keyboardFrame = CGRectZero;
946 NSNotification* fakeNotification =
947 [NSNotification notificationWithName:UIKeyboardWillHideNotification
950 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
951 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
952 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
956 [viewController.keyboardInsetManager handleKeyboardNotification:fakeNotification];
957 XCTAssertTrue(
viewController.keyboardInsetManager.targetViewInsetBottom == 0);
960- (void)testStopKeyBoardAnimationWhenReceivedWillHideNotificationAfterWillShowNotification {
970 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
973 OCMStub([viewControllerMock isViewLoaded]).andReturn(YES);
974 [viewControllerMock view];
976 UIScreen* screen = [
self setUpMockScreen];
977 OCMStub([screen scale]).andReturn(1.0);
978 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
979 CGRect viewFrame = screen.bounds;
980 [
self setUpMockView:viewControllerMock
983 convertedFrame:viewFrame];
986 CGFloat screenWidth = screen.bounds.size.width;
987 CGFloat screenHeight = screen.bounds.size.height;
988 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
992 NSNotification* fakeShowNotification =
993 [NSNotification notificationWithName:UIKeyboardWillShowNotification
996 UIKeyboardFrameEndUserInfoKey : @(keyboardFrame),
997 UIKeyboardAnimationDurationUserInfoKey : @0.25,
998 UIKeyboardIsLocalUserInfoKey : @(isLocal)
1000 [viewController.keyboardInsetManager handleKeyboardNotification:fakeShowNotification];
1001 XCTAssertEqual(
viewController.keyboardInsetManager.targetViewInsetBottom, 320 * screen.scale);
1004 NSNotification* fakeHideNotification =
1005 [NSNotification notificationWithName:UIKeyboardWillHideNotification
1008 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
1009 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.0),
1010 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
1012 [viewController.keyboardInsetManager handleKeyboardNotification:fakeHideNotification];
1013 XCTAssertEqual(
viewController.keyboardInsetManager.targetViewInsetBottom, 0);
1016 XCTAssertNil([
viewController.keyboardInsetManager keyboardAnimationView]);
1017 XCTAssertNil([
viewController.keyboardInsetManager keyboardSpringAnimation]);
1020- (void)testEnsureViewportMetricsWillInvokeAndDisplayLinkWillInvalidateInViewDidDisappear {
1028 (id<FlutterKeyboardInsetManagerDelegate>)viewControllerMock;
1030 id managerMock = OCMPartialMock(
viewController.keyboardInsetManager);
1033 [viewControllerMock viewDidDisappear:YES];
1035 OCMVerify([managerMock ensureViewportMetricsIsCorrect]);
1036 OCMVerify([managerMock invalidateKeyboardAnimationVSyncClient]);
1039- (void)testViewDidDisappearDoesntPauseEngineWhenNotTheViewController {
1047 id viewControllerMock = OCMPartialMock(viewControllerA);
1048 OCMStub([viewControllerMock surfaceUpdated:NO]);
1050 [viewControllerA viewDidDisappear:NO];
1051 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
1052 OCMReject([viewControllerMock surfaceUpdated:[OCMArg any]]);
1055- (void)testAppWillTerminateViewDidDestroyTheEngine {
1057 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1062 OCMStub([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
1063 OCMStub([mockEngine destroyContext]);
1064 [viewController applicationWillTerminate:nil];
1065 OCMVerify([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
1066 OCMVerify([mockEngine destroyContext]);
1069- (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController {
1080 OCMStub([viewControllerMock surfaceUpdated:NO]);
1081 [viewController viewDidDisappear:NO];
1082 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
1083 OCMVerify([viewControllerMock surfaceUpdated:NO]);
1085 XCTAssertNil(weakViewController);
1089 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear {
1091 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1095 [viewController viewWillAppear:YES];
1100 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear {
1102 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1112 [viewControllerA viewWillAppear:YES];
1113 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
1117 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear {
1119 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1123 [viewController viewDidAppear:YES];
1128 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear {
1130 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1140 [viewControllerA viewDidAppear:YES];
1141 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
1145 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear {
1153 [viewController viewWillDisappear:NO];
1154 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
1158 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear {
1169 [viewControllerA viewDidDisappear:NO];
1170 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
1173- (void)testUpdateViewportMetricsIfNeeded_DoesntInvokeEngineWhenNotTheViewController {
1175 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1184 [viewControllerA updateViewportMetricsIfNeeded];
1186 OCMVerify(never(), [mockEngine updateViewportMetrics:viewportMetrics]);
1189- (void)testUpdateViewportMetricsIfNeeded_DoesInvokeEngineWhenIsTheViewController {
1191 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1197 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1198 [viewController updateViewportMetricsIfNeeded];
1199 OCMVerifyAll(mockEngine);
1202- (void)testUpdatedViewportMetricsDoesResizeFlutterViewWhenAutoResizable {
1204 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1209 id mockVC = OCMPartialMock(realVC);
1212 OCMExpect([mockVC updateAutoResizeConstraints]);
1214 [mockVC setAutoResizable:YES];
1216 [mockVC viewDidLayoutSubviews];
1218 OCMVerifyAll(mockVC);
1221- (void)testUpdatedViewportMetricsDoesNotResizeFlutterViewWhenNotAutoResizable {
1223 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1228 id mockVC = OCMPartialMock(realVC);
1231 OCMReject([mockVC updateAutoResizeConstraints]);
1233 [mockVC setAutoResizable:NO];
1235 [mockVC viewDidLayoutSubviews];
1237 OCMVerifyAll(mockVC);
1240- (void)testUpdateViewportMetricsIfNeeded_DoesNotInvokeEngineWhenShouldBeIgnoredDuringRotation {
1242 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1247 UIScreen* screen = [
self setUpMockScreen];
1248 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1251 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1252 OCMStub([mockCoordinator transitionDuration]).andReturn(0.5);
1255 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1257 [viewController updateViewportMetricsIfNeeded];
1262- (void)testViewWillTransitionToSize_DoesDelayEngineCallIfNonZeroDuration {
1264 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1269 UIScreen* screen = [
self setUpMockScreen];
1270 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1274 NSTimeInterval transitionDuration = 0.5;
1275 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1276 OCMStub([mockCoordinator transitionDuration]).andReturn(transitionDuration);
1279 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1281 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1283 [viewController updateViewportMetricsIfNeeded];
1288 XCTWaiterResult result = [XCTWaiter
1289 waitForExpectations:@[ [
self expectationWithDescription:@"Waiting for rotation duration"] ]
1290 timeout:transitionDuration];
1291 XCTAssertEqual(result, XCTWaiterResultTimedOut);
1293 OCMVerifyAll(mockEngine);
1296- (void)testViewWillTransitionToSize_DoesNotDelayEngineCallIfZeroDuration {
1298 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1303 UIScreen* screen = [
self setUpMockScreen];
1304 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1308 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1309 OCMStub([mockCoordinator transitionDuration]).andReturn(0);
1312 OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1315 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1316 [viewController updateViewportMetricsIfNeeded];
1318 OCMVerifyAll(mockEngine);
1321- (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController {
1323 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1332 UIView*
view = viewControllerA.view;
1333 XCTAssertNotNil(view);
1334 OCMVerify(never(), [mockEngine attachView]);
1337- (void)testViewDidLoadDoesInvokeEngineWhenIsTheViewController {
1339 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1346 XCTAssertNotNil(view);
1347 OCMVerify(times(1), [mockEngine attachView]);
1350- (void)testViewDidLoadDoesntInvokeEngineAttachViewWhenEngineNeedsLaunch {
1352 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1358 [viewController sharedSetupWithProject:nil initialRoute:nil];
1361 XCTAssertNotNil(view);
1362 OCMVerify(never(), [mockEngine attachView]);
1365- (void)testSplashScreenViewRemoveNotCrash {
1370 [flutterViewController setSplashScreenView:[[UIView alloc] init]];
1371 [flutterViewController setSplashScreenView:nil];
1374- (void)testInternalPluginsWeakPtrNotCrash {
1380 [vc addInternalPlugins];
1383 [(NSArray<id<FlutterKeyPrimaryResponder>>*)keyboardManager.primaryResponders firstObject];
1384 sendEvent = [keyPrimaryResponder sendEvent];
1388 sendEvent({}, nil, nil);
1393- (void)testInternalPluginsInvokeInViewDidLoad {
1395 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1403 XCTAssertNotNil(view);
1404 [viewController viewDidLoad];
1408- (void)testBinaryMessenger {
1412 XCTAssertNotNil(vc);
1413 id messenger = OCMProtocolMock(
@protocol(FlutterBinaryMessenger));
1414 OCMStub([
self.mockEngine binaryMessenger]).andReturn(messenger);
1416 OCMVerify([
self.mockEngine binaryMessenger]);
1419- (void)testViewControllerIsReleased {
1421 __weak UIView* weakView;
1430 [viewController loadView];
1431 [viewController viewDidLoad];
1435 XCTAssertNil(weakViewController);
1436 XCTAssertNil(weakView);
1439#pragma mark - Platform Brightness
1441- (void)testItReportsLightPlatformBrightnessByDefault {
1444 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1451 [vc traitCollectionDidChange:nil];
1454 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1455 return [message[@"platformBrightness"] isEqualToString:@"light"];
1459 [settingsChannel stopMocking];
1462- (void)testItReportsPlatformBrightnessWhenViewWillAppear {
1466 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1467 OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel);
1473 [vc viewWillAppear:false];
1476 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1477 return [message[@"platformBrightness"] isEqualToString:@"light"];
1481 [settingsChannel stopMocking];
1484- (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt {
1487 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1488 id mockTraitCollection =
1489 [
self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
1498 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1501 [partialMockVC traitCollectionDidChange:nil];
1504 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1505 return [message[@"platformBrightness"] isEqualToString:@"dark"];
1509 [partialMockVC stopMocking];
1510 [settingsChannel stopMocking];
1511 [mockTraitCollection stopMocking];
1516- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style {
1517 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1518 OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style);
1519 return mockTraitCollection;
1522- (void)testTraitCollectionDidChangeCallsResetIntrinsicContentSizeWhenAutoResizable {
1524 id mockEngine = OCMPartialMock([[
FlutterEngine alloc] init]);
1525 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1530 id partialMockVC = OCMPartialMock(realVC);
1532 id mockFlutterView = OCMClassMock([
FlutterView class]);
1533 OCMStub([partialMockVC flutterView]).andReturn(mockFlutterView);
1536 OCMStub([partialMockVC isAutoResizable]).andReturn(YES);
1539 OCMExpect([mockFlutterView resetIntrinsicContentSize]);
1542 [partialMockVC traitCollectionDidChange:nil];
1545 OCMVerifyAll(mockFlutterView);
1548 [partialMockVC stopMocking];
1549 [mockFlutterView stopMocking];
1552#pragma mark - Platform Contrast
1554- (void)testItReportsNormalPlatformContrastByDefault {
1557 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1564 [vc traitCollectionDidChange:nil];
1567 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1568 return [message[@"platformContrast"] isEqualToString:@"normal"];
1572 [settingsChannel stopMocking];
1575- (void)testItReportsPlatformContrastWhenViewWillAppear {
1577 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1581 OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel);
1587 [vc viewWillAppear:false];
1590 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1591 return [message[@"platformContrast"] isEqualToString:@"normal"];
1595 [settingsChannel stopMocking];
1598- (void)testItReportsHighContrastWhenTraitCollectionRequestsIt {
1601 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1603 id mockTraitCollection = [
self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh];
1612 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1615 [partialMockVC traitCollectionDidChange:mockTraitCollection];
1618 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1619 return [message[@"platformContrast"] isEqualToString:@"high"];
1623 [partialMockVC stopMocking];
1624 [settingsChannel stopMocking];
1625 [mockTraitCollection stopMocking];
1628- (void)testItReportsAlwaysUsed24HourFormat {
1631 OCMStub([
self.mockEngine settingsChannel]).andReturn(settingsChannel);
1637 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(YES);
1638 OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1639 return [message[@"alwaysUse24HourFormat"] isEqual:@(YES)];
1641 [vc onUserSettingsChanged:nil];
1642 [mockHourFormat stopMocking];
1646 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(NO);
1647 OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^
BOOL(
id message) {
1648 return [message[@"alwaysUse24HourFormat"] isEqual:@(NO)];
1650 [vc onUserSettingsChanged:nil];
1651 [mockHourFormat stopMocking];
1654 [settingsChannel stopMocking];
1657- (void)testOnAccessibilityStatusChangedCallsEnableSemanticsWithFlags {
1660 id mockAccessibilityFeatures = OCMClassMock([FlutterAccessibilityFeatures
class]);
1661 OCMStub([mockAccessibilityFeatures flags]).andReturn(333);
1663 OCMStub([mockViewController accessibilityFeatures]).andReturn(mockAccessibilityFeatures);
1665 [mockViewController onAccessibilityStatusChanged:nil];
1666 OCMVerify([
self.mockEngine enableSemantics:[OCMArg any] withFlags:333]);
1669- (void)testHandleAccessibilityNotifications {
1673 __block NSUInteger callsCount = 0;
1674 OCMStub([mockViewController onAccessibilityStatusChanged:[OCMArg isNotNil]])
1675 .andDo(^(NSInvocation* invocation) {
1679 FlutterAccessibilityFeatures* accessibilityFeatures = [[FlutterAccessibilityFeatures alloc] init];
1680 NSArray<NSString*>* accessibilityNotification = [accessibilityFeatures observedNotificationNames];
1682 for (NSUInteger
i = 0;
i < [accessibilityNotification count];
i++) {
1683 NSString* notificationName = [accessibilityNotification objectAtIndex:i];
1684 [[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:nil];
1685 XCTAssertEqual(callsCount,
i + 1);
1689- (void)testAccessibilityPerformEscapePopsRoute {
1691 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1693 OCMStub([mockEngine navigationChannel]).andReturn(mockNavigationChannel);
1700 OCMVerify([mockNavigationChannel invokeMethod:
@"popRoute" arguments:nil]);
1702 [mockNavigationChannel stopMocking];
1705- (void)testPerformOrientationUpdateForcesOrientationChange {
1706 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1707 currentOrientation:UIInterfaceOrientationLandscapeLeft
1708 didChangeOrientation:YES
1709 resultingOrientation:UIInterfaceOrientationPortrait];
1711 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1712 currentOrientation:UIInterfaceOrientationLandscapeRight
1713 didChangeOrientation:YES
1714 resultingOrientation:UIInterfaceOrientationPortrait];
1716 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1717 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1718 didChangeOrientation:YES
1719 resultingOrientation:UIInterfaceOrientationPortrait];
1721 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1722 currentOrientation:UIInterfaceOrientationLandscapeLeft
1723 didChangeOrientation:YES
1724 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1726 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1727 currentOrientation:UIInterfaceOrientationLandscapeRight
1728 didChangeOrientation:YES
1729 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1731 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1732 currentOrientation:UIInterfaceOrientationPortrait
1733 didChangeOrientation:YES
1734 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1736 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1737 currentOrientation:UIInterfaceOrientationPortrait
1738 didChangeOrientation:YES
1739 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1741 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1742 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1743 didChangeOrientation:YES
1744 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1746 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1747 currentOrientation:UIInterfaceOrientationPortrait
1748 didChangeOrientation:YES
1749 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1751 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1752 currentOrientation:UIInterfaceOrientationLandscapeRight
1753 didChangeOrientation:YES
1754 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1756 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1757 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1758 didChangeOrientation:YES
1759 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1761 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1762 currentOrientation:UIInterfaceOrientationPortrait
1763 didChangeOrientation:YES
1764 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1766 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1767 currentOrientation:UIInterfaceOrientationLandscapeLeft
1768 didChangeOrientation:YES
1769 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1771 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1772 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1773 didChangeOrientation:YES
1774 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1776 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1777 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1778 didChangeOrientation:YES
1779 resultingOrientation:UIInterfaceOrientationPortrait];
1782- (void)testPerformOrientationUpdateDoesNotForceOrientationChange {
1783 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1784 currentOrientation:UIInterfaceOrientationPortrait
1785 didChangeOrientation:NO
1786 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1788 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1789 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1790 didChangeOrientation:NO
1791 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1793 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1794 currentOrientation:UIInterfaceOrientationLandscapeLeft
1795 didChangeOrientation:NO
1796 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1798 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1799 currentOrientation:UIInterfaceOrientationLandscapeRight
1800 didChangeOrientation:NO
1801 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1803 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1804 currentOrientation:UIInterfaceOrientationPortrait
1805 didChangeOrientation:NO
1806 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1808 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1809 currentOrientation:UIInterfaceOrientationLandscapeLeft
1810 didChangeOrientation:NO
1811 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1813 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1814 currentOrientation:UIInterfaceOrientationLandscapeRight
1815 didChangeOrientation:NO
1816 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1818 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1819 currentOrientation:UIInterfaceOrientationPortrait
1820 didChangeOrientation:NO
1821 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1823 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1824 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1825 didChangeOrientation:NO
1826 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1828 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1829 currentOrientation:UIInterfaceOrientationLandscapeLeft
1830 didChangeOrientation:NO
1831 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1833 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1834 currentOrientation:UIInterfaceOrientationLandscapeRight
1835 didChangeOrientation:NO
1836 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1838 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1839 currentOrientation:UIInterfaceOrientationLandscapeLeft
1840 didChangeOrientation:NO
1841 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1843 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1844 currentOrientation:UIInterfaceOrientationLandscapeRight
1845 didChangeOrientation:NO
1846 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1851- (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask
1852 currentOrientation:(UIInterfaceOrientation)currentOrientation
1853 didChangeOrientation:(
BOOL)didChange
1854 resultingOrientation:(UIInterfaceOrientation)resultingOrientation {
1855 id mockApplication = OCMClassMock([UIApplication
class]);
1859 __block __weak
id weakPreferences;
1865 if (@available(iOS 16.0, *)) {
1866 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1867 mockVC = OCMPartialMock(realVC);
1868 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1869 if (realVC.supportedInterfaceOrientations == mask) {
1870 OCMReject([mockWindowScene requestGeometryUpdateWithPreferences:[OCMArg any]
1871 errorHandler:[OCMArg any]]);
1875 OCMExpect([mockWindowScene
1876 requestGeometryUpdateWithPreferences:[OCMArg checkWithBlock:^
BOOL(
1877 UIWindowSceneGeometryPreferencesIOS*
1879 weakPreferences = preferences;
1880 return preferences.interfaceOrientations == mask;
1882 errorHandler:[OCMArg any]]);
1884 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
1885 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockWindowScene]);
1887 deviceMock = OCMPartialMock([UIDevice currentDevice]);
1889 OCMReject([deviceMock setValue:[OCMArg any] forKey:
@"orientation"]);
1891 OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:
@"orientation"]);
1893 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1894 mockVC = OCMPartialMock(realVC);
1895 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1896 OCMStub(((UIWindowScene*)mockWindowScene).interfaceOrientation).andReturn(currentOrientation);
1899 [realVC performOrientationUpdate:mask];
1900 if (@available(iOS 16.0, *)) {
1901 OCMVerifyAll(mockWindowScene);
1903 OCMVerifyAll(deviceMock);
1906 [mockWindowScene stopMocking];
1907 [deviceMock stopMocking];
1908 [mockApplication stopMocking];
1909 XCTAssertNil(weakPreferences);
1914- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast {
1915 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1916 OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast);
1917 return mockTraitCollection;
1920- (void)testWillDeallocNotification {
1921 XCTestExpectation* expectation =
1922 [[XCTestExpectation alloc] initWithDescription:@"notification called"];
1929 [NSNotificationCenter.defaultCenter addObserverForName:FlutterViewControllerWillDealloc
1931 queue:[NSOperationQueue mainQueue]
1932 usingBlock:^(NSNotification* _Nonnull note) {
1933 [expectation fulfill];
1935 XCTAssertNotNil(realVC);
1938 [
self waitForExpectations:@[ expectation ] timeout:1.0];
1941- (void)testReleasesKeyboardManagerOnDealloc {
1946 [viewController addInternalPlugins];
1948 XCTAssertNotNil(weakKeyboardManager);
1949 [viewController deregisterNotifications];
1953 XCTAssertNil(weakKeyboardManager);
1956- (void)testDoesntLoadViewInInit {
1959 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
1963 XCTAssertFalse([realVC isViewLoaded],
@"shouldn't have loaded since it hasn't been shown");
1967- (void)testHideOverlay {
1970 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
1974 XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden,
@"");
1975 [NSNotificationCenter.defaultCenter postNotificationName:FlutterViewControllerHideHomeIndicator
1977 XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden,
@"");
1981- (void)testNotifyLowMemory {
1987 OCMStub([viewControllerMock surfaceUpdated:NO]);
1988 [viewController beginAppearanceTransition:NO animated:NO];
1989 [viewController endAppearanceTransition];
1993- (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback {
1994 NSMutableDictionary* replyMessage = [@{
2007 if (@available(iOS 13.4, *)) {
2014 OCMStub([mockEngine.
keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
2015 .andCall(
self, @selector(sendMessage:reply:));
2016 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
2025 [vc addInternalPlugins];
2027 [vc handlePressEvent:keyUpEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0)
2031 XCTAssert(
self.messageSent != nil);
2032 XCTAssert([
self.messageSent[
@"keymap"] isEqualToString:
@"ios"]);
2033 XCTAssert([
self.messageSent[
@"type"] isEqualToString:
@"keyup"]);
2034 XCTAssert([
self.messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
2035 XCTAssert([
self.messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
2036 XCTAssert([
self.messageSent[
@"characters"] isEqualToString:
@""]);
2037 XCTAssert([
self.messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@""]);
2038 [vc deregisterNotifications];
2042 if (@available(iOS 13.4, *)) {
2050 OCMStub([mockEngine.
keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
2051 .andCall(
self, @selector(sendMessage:reply:));
2052 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
2060 [vc addInternalPlugins];
2062 [vc handlePressEvent:keyDownEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0f, "A",
2067 XCTAssert(
self.messageSent != nil);
2068 XCTAssert([
self.messageSent[
@"keymap"] isEqualToString:
@"ios"]);
2069 XCTAssert([
self.messageSent[
@"type"] isEqualToString:
@"keydown"]);
2070 XCTAssert([
self.messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
2071 XCTAssert([
self.messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
2072 XCTAssert([
self.messageSent[
@"characters"] isEqualToString:
@"A"]);
2073 XCTAssert([
self.messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@"a"]);
2074 [vc deregisterNotifications];
2079 if (@available(iOS 13.4, *)) {
2085 OCMStub([keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
2086 .andCall(
self, @selector(sendMessage:reply:));
2087 OCMStub([
self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES);
2088 OCMStub([
self.mockEngine keyEventChannel]).andReturn(keyEventChannel);
2096 [vc addInternalPlugins];
2098 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseStationary, UIKeyboardHIDUsageKeyboardA,
2099 UIKeyModifierShift, 123.0)
2102 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseCancelled, UIKeyboardHIDUsageKeyboardA,
2103 UIKeyModifierShift, 123.0)
2106 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseChanged, UIKeyboardHIDUsageKeyboardA,
2107 UIKeyModifierShift, 123.0)
2111 XCTAssert(
self.messageSent == nil);
2112 OCMVerify(never(), [keyEventChannel sendMessage:[OCMArg any]]);
2113 [vc deregisterNotifications];
2117 if (@available(iOS 13.4, *)) {
2126 XCTAssertNotNil(vc);
2127 UIView*
view = vc.view;
2128 XCTAssertNotNil(view);
2129 NSArray* gestureRecognizers =
view.gestureRecognizers;
2130 XCTAssertNotNil(gestureRecognizers);
2133 for (
id gesture in gestureRecognizers) {
2134 if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
2139 XCTAssertTrue(found);
2143 if (@available(iOS 13.4, *)) {
2152 XCTAssertNotNil(vc);
2154 id mockPanGestureRecognizer = OCMClassMock([UIPanGestureRecognizer
class]);
2155 XCTAssertNotNil(mockPanGestureRecognizer);
2157 [vc discreteScrollEvent:mockPanGestureRecognizer];
2160 [[mockPanGestureRecognizer verify] locationInView:[OCMArg any]];
2161 [[[
self.mockEngine verify] ignoringNonObjectArgs]
2162 dispatchPointerDataPacket:std::make_unique<flutter::PointerDataPacket>(0)];
2165- (void)testFakeEventTimeStamp {
2169 XCTAssertNotNil(vc);
2172 int64_t current_micros = [[NSProcessInfo processInfo] systemUptime] * 1000 * 1000;
2173 int64_t interval_micros = current_micros - pointer_data.time_stamp;
2174 const int64_t tolerance_millis = 2;
2175 XCTAssertTrue(interval_micros / 1000 < tolerance_millis,
2176 @"PointerData.time_stamp should be equal to NSProcessInfo.systemUptime");
2179- (void)testSplashScreenViewCanSetNil {
2182 [flutterViewController setSplashScreenView:nil];
2185- (void)testLifeCycleNotificationApplicationBecameActive {
2190 UIWindow*
window = [[UIWindow alloc] init];
2191 [window addSubview:flutterViewController.view];
2192 flutterViewController.view.bounds = CGRectMake(0, 0, 100, 100);
2193 [flutterViewController viewDidLayoutSubviews];
2194 NSNotification* sceneNotification =
2195 [NSNotification notificationWithName:UISceneDidActivateNotification object:nil userInfo:nil];
2196 NSNotification* applicationNotification =
2197 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2200 id mockVC = OCMPartialMock(flutterViewController);
2201 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2202 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2203 OCMReject([mockVC sceneBecameActive:[OCMArg any]]);
2204 OCMVerify([mockVC applicationBecameActive:[OCMArg any]]);
2206 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2207 OCMVerify([mockVC surfaceUpdated:YES]);
2208 XCTestExpectation* timeoutApplicationLifeCycle =
2209 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2210 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2211 dispatch_get_main_queue(), ^{
2212 [timeoutApplicationLifeCycle fulfill];
2213 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2214 [flutterViewController deregisterNotifications];
2216 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2219- (void)testLifeCycleNotificationSceneBecameActive {
2220 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2221 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2222 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2225 [engine runWithEntrypoint:nil];
2228 UIWindow*
window = [[UIWindow alloc] init];
2229 [window addSubview:flutterViewController.view];
2230 flutterViewController.view.bounds = CGRectMake(0, 0, 100, 100);
2231 [flutterViewController viewDidLayoutSubviews];
2232 NSNotification* sceneNotification =
2233 [NSNotification notificationWithName:UISceneDidActivateNotification object:nil userInfo:nil];
2234 NSNotification* applicationNotification =
2235 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2238 id mockVC = OCMPartialMock(flutterViewController);
2239 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2240 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2241 OCMVerify([mockVC sceneBecameActive:[OCMArg any]]);
2242 OCMReject([mockVC applicationBecameActive:[OCMArg any]]);
2244 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2245 OCMVerify([mockVC surfaceUpdated:YES]);
2246 XCTestExpectation* timeoutApplicationLifeCycle =
2247 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2248 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2249 dispatch_get_main_queue(), ^{
2250 [timeoutApplicationLifeCycle fulfill];
2251 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2252 [flutterViewController deregisterNotifications];
2254 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2255 [mockBundle stopMocking];
2258- (void)testLifeCycleNotificationApplicationWillResignActive {
2263 NSNotification* sceneNotification =
2264 [NSNotification notificationWithName:UISceneWillDeactivateNotification
2267 NSNotification* applicationNotification =
2268 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2271 id mockVC = OCMPartialMock(flutterViewController);
2272 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2273 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2274 OCMReject([mockVC sceneWillResignActive:[OCMArg any]]);
2275 OCMVerify([mockVC applicationWillResignActive:[OCMArg any]]);
2276 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2277 [flutterViewController deregisterNotifications];
2280- (void)testLifeCycleNotificationSceneWillResignActive {
2281 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2282 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2283 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2286 [engine runWithEntrypoint:nil];
2289 NSNotification* sceneNotification =
2290 [NSNotification notificationWithName:UISceneWillDeactivateNotification
2293 NSNotification* applicationNotification =
2294 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2297 id mockVC = OCMPartialMock(flutterViewController);
2298 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2299 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2300 OCMVerify([mockVC sceneWillResignActive:[OCMArg any]]);
2301 OCMReject([mockVC applicationWillResignActive:[OCMArg any]]);
2302 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2303 [flutterViewController deregisterNotifications];
2304 [mockBundle stopMocking];
2307- (void)testLifeCycleNotificationApplicationWillTerminate {
2312 NSNotification* sceneNotification =
2313 [NSNotification notificationWithName:UISceneDidDisconnectNotification
2316 NSNotification* applicationNotification =
2317 [NSNotification notificationWithName:UIApplicationWillTerminateNotification
2320 id mockVC = OCMPartialMock(flutterViewController);
2321 id mockEngine = OCMPartialMock(
engine);
2322 OCMStub([mockVC
engine]).andReturn(mockEngine);
2323 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2324 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2325 OCMReject([mockVC sceneWillDisconnect:[OCMArg any]]);
2326 OCMVerify([mockVC applicationWillTerminate:[OCMArg any]]);
2327 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
2328 OCMVerify([mockEngine destroyContext]);
2329 [flutterViewController deregisterNotifications];
2332- (void)testLifeCycleNotificationSceneWillTerminate {
2333 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2334 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2335 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2338 [engine runWithEntrypoint:nil];
2341 NSNotification* sceneNotification =
2342 [NSNotification notificationWithName:UISceneDidDisconnectNotification
2345 NSNotification* applicationNotification =
2346 [NSNotification notificationWithName:UIApplicationWillTerminateNotification
2349 id mockVC = OCMPartialMock(flutterViewController);
2350 id mockEngine = OCMPartialMock(
engine);
2351 OCMStub([mockVC
engine]).andReturn(mockEngine);
2352 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2353 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2354 OCMVerify([mockVC sceneWillDisconnect:[OCMArg any]]);
2355 OCMReject([mockVC applicationWillTerminate:[OCMArg any]]);
2356 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
2357 OCMVerify([mockEngine destroyContext]);
2358 [flutterViewController deregisterNotifications];
2359 [mockBundle stopMocking];
2362- (void)testLifeCycleNotificationApplicationDidEnterBackground {
2367 NSNotification* sceneNotification =
2368 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2371 NSNotification* applicationNotification =
2372 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
2375 id mockVC = OCMPartialMock(flutterViewController);
2376 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2377 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2378 OCMReject([mockVC sceneDidEnterBackground:[OCMArg any]]);
2379 OCMVerify([mockVC applicationDidEnterBackground:[OCMArg any]]);
2381 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2382 OCMVerify([mockVC surfaceUpdated:NO]);
2383 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2384 [flutterViewController deregisterNotifications];
2387- (void)testLifeCycleNotificationSceneDidEnterBackground {
2388 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2389 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2390 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2393 [engine runWithEntrypoint:nil];
2396 NSNotification* sceneNotification =
2397 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2400 NSNotification* applicationNotification =
2401 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
2404 id mockVC = OCMPartialMock(flutterViewController);
2405 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2406 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2407 OCMVerify([mockVC sceneDidEnterBackground:[OCMArg any]]);
2408 OCMReject([mockVC applicationDidEnterBackground:[OCMArg any]]);
2410 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2411 OCMVerify([mockVC surfaceUpdated:NO]);
2412 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2413 [flutterViewController deregisterNotifications];
2414 [mockBundle stopMocking];
2427 testLifeCycleNotificationSceneDidEnterBackgroundIgnoresOtherScenes
API_AVAILABLE(ios(13.0)) {
2428 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2429 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2430 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2433 [engine runWithEntrypoint:nil];
2436 id mockVC = OCMPartialMock(flutterViewController);
2439 id flutterWindowScene = OCMClassMock([UIWindowScene
class]);
2440 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(flutterWindowScene);
2443 id auxiliaryScene = OCMClassMock([UIWindowScene
class]);
2447 NSNotification* auxiliarySceneNotification =
2448 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2449 object:auxiliaryScene
2451 [NSNotificationCenter.defaultCenter postNotification:auxiliarySceneNotification];
2452 OCMVerify(never(), [mockVC surfaceUpdated:[OCMArg any]]);
2453 OCMVerify(never(), [mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2455 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2459 NSNotification* flutterSceneNotification =
2460 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2461 object:flutterWindowScene
2463 [NSNotificationCenter.defaultCenter postNotification:flutterSceneNotification];
2464 OCMVerify([mockVC surfaceUpdated:NO]);
2465 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2467 flutterViewController.keyboardInsetManager.isKeyboardInOrTransitioningFromBackground);
2469 [flutterViewController deregisterNotifications];
2470 [mockBundle stopMocking];
2473- (void)testLifeCycleNotificationApplicationWillEnterForeground {
2478 NSNotification* sceneNotification =
2479 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2482 NSNotification* applicationNotification =
2483 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2486 id mockVC = OCMPartialMock(flutterViewController);
2487 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2488 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2489 OCMReject([mockVC sceneWillEnterForeground:[OCMArg any]]);
2490 OCMVerify([mockVC applicationWillEnterForeground:[OCMArg any]]);
2491 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2492 [flutterViewController deregisterNotifications];
2495- (void)testLifeCycleNotificationSceneWillEnterForeground {
2496 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2497 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2498 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2501 [engine runWithEntrypoint:nil];
2504 NSNotification* sceneNotification =
2505 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2508 NSNotification* applicationNotification =
2509 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2512 id mockVC = OCMPartialMock(flutterViewController);
2513 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2514 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2515 OCMVerify([mockVC sceneWillEnterForeground:[OCMArg any]]);
2516 OCMReject([mockVC applicationWillEnterForeground:[OCMArg any]]);
2517 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2518 [flutterViewController deregisterNotifications];
2519 [mockBundle stopMocking];
2522- (void)testLifeCycleNotificationCancelledInvalidResumed {
2527 NSNotification* applicationDidBecomeActiveNotification =
2528 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2531 NSNotification* applicationWillResignActiveNotification =
2532 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2535 id mockVC = OCMPartialMock(flutterViewController);
2536 [NSNotificationCenter.defaultCenter postNotification:applicationDidBecomeActiveNotification];
2537 [NSNotificationCenter.defaultCenter postNotification:applicationWillResignActiveNotification];
2538 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2540 XCTestExpectation* timeoutApplicationLifeCycle =
2541 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2542 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2543 dispatch_get_main_queue(), ^{
2544 OCMReject([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2545 [timeoutApplicationLifeCycle fulfill];
2546 [flutterViewController deregisterNotifications];
2548 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2551- (void)testSetupKeyboardAnimationVsyncClientWillCreateNewVsyncClientForFlutterViewController {
2552 id bundleMock = OCMPartialMock([NSBundle mainBundle]);
2553 OCMStub([bundleMock objectForInfoDictionaryKey:
@"CADisableMinimumFrameDurationOnPhone"])
2555 id mockDisplayLinkManager = [OCMockObject mockForClass:[FlutterDisplayLinkManager class]];
2556 double maxFrameRate = 120;
2557 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2565 [viewController.keyboardInsetManager setUpKeyboardAnimationVsyncClient:callback];
2566 XCTAssertNotNil(
viewController.keyboardInsetManager.keyboardAnimationVSyncClient);
2567 CADisplayLink* link =
2568 viewController.keyboardInsetManager.keyboardAnimationVSyncClient.displayLink;
2569 XCTAssertNotNil(link);
2570 CADisplayLink* linkMock = OCMPartialMock(link);
2571 if (@available(iOS 15.0, *)) {
2572 CAFrameRateRange range = CAFrameRateRangeMake(maxFrameRate / 2, maxFrameRate, maxFrameRate);
2573 NSValue* rangeValue = [NSValue valueWithBytes:&range objCType:@encode(CAFrameRateRange)];
2574 [[[(id)linkMock stub] andReturnValue:rangeValue] preferredFrameRateRange];
2576 XCTAssertEqual(linkMock.preferredFrameRateRange.maximum, maxFrameRate);
2577 XCTAssertEqual(linkMock.preferredFrameRateRange.preferred, maxFrameRate);
2578 XCTAssertEqual(linkMock.preferredFrameRateRange.minimum, maxFrameRate / 2);
2580 [[[(id)linkMock stub] andReturnValue:@(maxFrameRate)] preferredFramesPerSecond];
2581 XCTAssertEqual(linkMock.preferredFramesPerSecond, maxFrameRate);
2586 testCreateTouchRateCorrectionVSyncClientWillCreateVsyncClientWhenRefreshRateIsLargerThan60HZ {
2587 id mockDisplayLinkManager = [OCMockObject mockForClass:[FlutterDisplayLinkManager class]];
2588 double maxFrameRate = 120;
2589 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2595 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2599- (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateNewVSyncClientWhenClientAlreadyExists {
2600 id mockDisplayLinkManager = [OCMockObject mockForClass:[FlutterDisplayLinkManager class]];
2601 double maxFrameRate = 120;
2602 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2609 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2610 FlutterVSyncClient* clientBefore =
viewController.touchRateCorrectionVSyncClient;
2611 XCTAssertNotNil(clientBefore);
2613 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2614 FlutterVSyncClient* clientAfter =
viewController.touchRateCorrectionVSyncClient;
2615 XCTAssertNotNil(clientAfter);
2617 XCTAssertTrue(clientBefore == clientAfter);
2620- (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateVsyncClientWhenRefreshRateIs60HZ {
2621 id mockDisplayLinkManager = [OCMockObject mockForClass:[FlutterDisplayLinkManager class]];
2622 double maxFrameRate = 60;
2623 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2629 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2633- (void)testTriggerTouchRateCorrectionVSyncClientCorrectly {
2634 id mockDisplayLinkManager = [OCMockObject mockForClass:[FlutterDisplayLinkManager class]];
2635 double maxFrameRate = 120;
2636 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2642 [viewController loadView];
2643 [viewController viewDidLoad];
2645 FlutterVSyncClient* client =
viewController.touchRateCorrectionVSyncClient;
2646 CADisplayLink* link = client.displayLink;
2648 UITouch* fakeTouchBegan = [[UITouch alloc] init];
2649 fakeTouchBegan.phase = UITouchPhaseBegan;
2651 UITouch* fakeTouchMove = [[UITouch alloc] init];
2652 fakeTouchMove.phase = UITouchPhaseMoved;
2654 UITouch* fakeTouchEnd = [[UITouch alloc] init];
2655 fakeTouchEnd.phase = UITouchPhaseEnded;
2657 UITouch* fakeTouchCancelled = [[UITouch alloc] init];
2658 fakeTouchCancelled.phase = UITouchPhaseCancelled;
2661 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchBegan, nil]];
2662 XCTAssertFalse(link.isPaused);
2665 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd, nil]];
2666 XCTAssertTrue(link.isPaused);
2669 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchMove, nil]];
2670 XCTAssertFalse(link.isPaused);
2673 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchCancelled, nil]];
2674 XCTAssertTrue(link.isPaused);
2677 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2678 initWithObjects:fakeTouchBegan, fakeTouchEnd, nil]];
2679 XCTAssertFalse(link.isPaused);
2682 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd,
2683 fakeTouchCancelled, nil]];
2684 XCTAssertTrue(link.isPaused);
2687 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2688 initWithObjects:fakeTouchMove, fakeTouchEnd, nil]];
2689 XCTAssertFalse(link.isPaused);
2692- (void)testFlutterViewControllerStartKeyboardAnimationWillCreateVsyncClientCorrectly {
2693 id mockDisplayLink = OCMClassMock([CADisplayLink
class]);
2694 OCMStub(ClassMethod([mockDisplayLink displayLinkWithTarget:[OCMArg any]
2695 selector:sel_registerName(
"onDisplayLink:")]))
2696 .andReturn(mockDisplayLink);
2705 manager.targetViewInsetBottom = 100;
2706 [manager startKeyBoardAnimation:0.25];
2708 XCTAssertNotNil(
manager.keyboardAnimationVSyncClient);
2711 [mockDisplayLink stopMocking];
2715 testSetupKeyboardAnimationVsyncClientWillNotCreateNewVsyncClientWhenKeyboardAnimationCallbackIsNil {
2721 [viewController.keyboardInsetManager setUpKeyboardAnimationVsyncClient:nil];
2722 XCTAssertNil(
viewController.keyboardInsetManager.keyboardAnimationVSyncClient);
2725- (void)testSupportsShowingSystemContextMenuForIOS16AndAbove {
2731 BOOL supportsShowingSystemContextMenu = [viewController supportsShowingSystemContextMenu];
2732 if (@available(iOS 16.0, *)) {
2733 XCTAssertTrue(supportsShowingSystemContextMenu);
2735 XCTAssertFalse(supportsShowingSystemContextMenu);
2739- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsActive {
2745 id mockApplication = OCMClassMock([UIApplication
class]);
2746 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateActive);
2747 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2752- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsBackground {
2758 id mockApplication = OCMClassMock([UIApplication
class]);
2759 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateBackground);
2760 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2765- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsInactive {
2771 id mockApplication = OCMClassMock([UIApplication
class]);
2772 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateInactive);
2773 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2778- (void)testStateIsActiveAndBackgroundWhenSceneStateIsActive {
2779 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2780 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2781 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2784 [engine runWithEntrypoint:nil];
2789 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateForegroundActive);
2793 [mockBundle stopMocking];
2794 [mockVC stopMocking];
2797- (void)testStateIsActiveAndBackgroundWhenSceneStateIsBackground {
2798 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2799 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2800 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2803 [engine runWithEntrypoint:nil];
2808 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateBackground);
2812 [mockBundle stopMocking];
2813 [mockVC stopMocking];
2816- (void)testStateIsActiveAndBackgroundWhenSceneStateIsInactive {
2817 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2818 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2819 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2822 [engine runWithEntrypoint:nil];
2827 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateForegroundInactive);
2831 [mockBundle stopMocking];
2832 [mockVC stopMocking];
2835- (void)testPerformImplicitEngineCallbacks {
2836 id mockRegistrant = OCMProtocolMock(
@protocol(FlutterPluginRegistrant));
2837 id appDelegate = [[UIApplication sharedApplication] delegate];
2838 [appDelegate setMockLaunchEngine:self.mockEngine];
2839 UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Flutter" bundle:nil];
2840 XCTAssertTrue([appDelegate respondsToSelector:@selector(setPluginRegistrant:)]);
2841 [appDelegate setPluginRegistrant:mockRegistrant];
2844 [appDelegate setPluginRegistrant:nil];
2846 OCMVerify([
self.mockEngine performImplicitEngineCallback]);
2847 [appDelegate setMockLaunchEngine:nil];
2850- (void)testPerformImplicitEngineCallbacksUsesAppLaunchEventFallbacks {
2856 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2857 OCMStub([viewControllerMock awokenFromNib]).andReturn(YES);
2859 id mockApplication = OCMClassMock([UIApplication
class]);
2860 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2862 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2863 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2865 id mockScene = OCMClassMock([UIScene
class]);
2866 id mockSceneDelegate = OCMProtocolMock(
@protocol(UISceneDelegate));
2867 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
2868 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockScene]);
2872 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2874 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2875 OCMVerify([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2876 OCMVerify([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2879- (void)testPerformImplicitEngineCallbacksNoAppLaunchEventFallbacksWhenNoStoryboard {
2885 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2886 OCMStub([viewControllerMock awokenFromNib]).andReturn(NO);
2888 id mockApplication = OCMClassMock([UIApplication
class]);
2889 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2891 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2892 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2894 id mockScene = OCMClassMock([UIScene
class]);
2895 id mockSceneDelegate = OCMProtocolMock(
@protocol(UISceneDelegate));
2896 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
2897 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockScene]);
2901 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2903 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2904 OCMReject([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2905 OCMReject([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2908- (void)testPerformImplicitEngineCallbacksNoAppLaunchEventFallbacksWhenNoScenes {
2914 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2915 OCMStub([viewControllerMock awokenFromNib]).andReturn(YES);
2917 id mockApplication = OCMClassMock([UIApplication
class]);
2918 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2920 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2921 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2925 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2927 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2928 OCMReject([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2929 OCMReject([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2932- (void)testGrabLaunchEngine {
2933 id appDelegate = [[UIApplication sharedApplication] delegate];
2934 XCTAssertTrue([appDelegate respondsToSelector:@selector(setMockLaunchEngine:)]);
2935 [appDelegate setMockLaunchEngine:self.mockEngine];
2936 UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Flutter" bundle:nil];
2937 XCTAssertTrue(storyboard);
2943 [appDelegate setMockLaunchEngine:nil];
2946- (void)testDoesntGrabLaunchEngine {
2947 id appDelegate = [[UIApplication sharedApplication] delegate];
2948 XCTAssertTrue([appDelegate respondsToSelector:@selector(setMockLaunchEngine:)]);
2949 [appDelegate setMockLaunchEngine:self.mockEngine];
2951 XCTAssertNotNil(flutterViewController.
engine);
2952 XCTAssertNotEqual(flutterViewController.
engine,
self.mockEngine);
2953 [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
BOOL runWithEntrypoint:(nullable NSString *entrypoint)
nullable FlutterFMLTaskRunner * uiTaskRunner()
FlutterViewController * viewController
BOOL runWithEntrypoint:(nullable NSString *entrypoint)
FlutterTextInputPlugin * textInputPlugin
FlutterBasicMessageChannel * lifecycleChannel
BOOL didCallNotifyLowMemory
FlutterViewController * viewController
FlutterBasicMessageChannel * keyEventChannel
void postTask:(void(^ task)(void))
Coordinates the animation of the bottom viewport inset in response to system keyboard visibility chan...
void invalidate()
Terminates any active animations and releases internal resources.
void handleKeyboardNotification:(NSNotification *notification)
Processes a system keyboard notification to update the target inset and begin any necessary animation...
CGFloat targetViewInsetBottom
The physical pixel value of the bottom inset once the current animation reaches its final state.
BOOL didCallStartKeyboardAnimation
NSObject< FlutterBinaryMessenger > * binaryMessenger
BOOL mockIsPadInSlideOverOrStageManagerMode
FlutterEngine * mockEngine
CGRect mockConvertedViewRect
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)
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
Manages the calculations and animations for software keyboard insets.