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];
2417- (void)testLifeCycleNotificationApplicationWillEnterForeground {
2422 NSNotification* sceneNotification =
2423 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2426 NSNotification* applicationNotification =
2427 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2430 id mockVC = OCMPartialMock(flutterViewController);
2431 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2432 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2433 OCMReject([mockVC sceneWillEnterForeground:[OCMArg any]]);
2434 OCMVerify([mockVC applicationWillEnterForeground:[OCMArg any]]);
2435 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2436 [flutterViewController deregisterNotifications];
2439- (void)testLifeCycleNotificationSceneWillEnterForeground {
2440 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2441 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2442 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2445 [engine runWithEntrypoint:nil];
2448 NSNotification* sceneNotification =
2449 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2452 NSNotification* applicationNotification =
2453 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2456 id mockVC = OCMPartialMock(flutterViewController);
2457 [NSNotificationCenter.defaultCenter postNotification:sceneNotification];
2458 [NSNotificationCenter.defaultCenter postNotification:applicationNotification];
2459 OCMVerify([mockVC sceneWillEnterForeground:[OCMArg any]]);
2460 OCMReject([mockVC applicationWillEnterForeground:[OCMArg any]]);
2461 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2462 [flutterViewController deregisterNotifications];
2463 [mockBundle stopMocking];
2466- (void)testLifeCycleNotificationCancelledInvalidResumed {
2471 NSNotification* applicationDidBecomeActiveNotification =
2472 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2475 NSNotification* applicationWillResignActiveNotification =
2476 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2479 id mockVC = OCMPartialMock(flutterViewController);
2480 [NSNotificationCenter.defaultCenter postNotification:applicationDidBecomeActiveNotification];
2481 [NSNotificationCenter.defaultCenter postNotification:applicationWillResignActiveNotification];
2482 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2484 XCTestExpectation* timeoutApplicationLifeCycle =
2485 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2486 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 *
NSEC_PER_SEC)),
2487 dispatch_get_main_queue(), ^{
2488 OCMReject([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2489 [timeoutApplicationLifeCycle fulfill];
2490 [flutterViewController deregisterNotifications];
2492 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2495- (void)testSetupKeyboardAnimationVsyncClientWillCreateNewVsyncClientForFlutterViewController {
2496 id bundleMock = OCMPartialMock([NSBundle mainBundle]);
2497 OCMStub([bundleMock objectForInfoDictionaryKey:
@"CADisableMinimumFrameDurationOnPhone"])
2499 id mockDisplayLinkManager = [OCMockObject mockForClass:[FlutterDisplayLinkManager class]];
2500 double maxFrameRate = 120;
2501 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2509 [viewController.keyboardInsetManager setUpKeyboardAnimationVsyncClient:callback];
2510 XCTAssertNotNil(
viewController.keyboardInsetManager.keyboardAnimationVSyncClient);
2511 CADisplayLink* link =
2512 viewController.keyboardInsetManager.keyboardAnimationVSyncClient.displayLink;
2513 XCTAssertNotNil(link);
2514 CADisplayLink* linkMock = OCMPartialMock(link);
2515 if (@available(iOS 15.0, *)) {
2516 CAFrameRateRange range = CAFrameRateRangeMake(maxFrameRate / 2, maxFrameRate, maxFrameRate);
2517 NSValue* rangeValue = [NSValue valueWithBytes:&range objCType:@encode(CAFrameRateRange)];
2518 [[[(id)linkMock stub] andReturnValue:rangeValue] preferredFrameRateRange];
2520 XCTAssertEqual(linkMock.preferredFrameRateRange.maximum, maxFrameRate);
2521 XCTAssertEqual(linkMock.preferredFrameRateRange.preferred, maxFrameRate);
2522 XCTAssertEqual(linkMock.preferredFrameRateRange.minimum, maxFrameRate / 2);
2524 [[[(id)linkMock stub] andReturnValue:@(maxFrameRate)] preferredFramesPerSecond];
2525 XCTAssertEqual(linkMock.preferredFramesPerSecond, maxFrameRate);
2530 testCreateTouchRateCorrectionVSyncClientWillCreateVsyncClientWhenRefreshRateIsLargerThan60HZ {
2531 id mockDisplayLinkManager = [OCMockObject mockForClass:[FlutterDisplayLinkManager class]];
2532 double maxFrameRate = 120;
2533 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2539 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2543- (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateNewVSyncClientWhenClientAlreadyExists {
2544 id mockDisplayLinkManager = [OCMockObject mockForClass:[FlutterDisplayLinkManager class]];
2545 double maxFrameRate = 120;
2546 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2553 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2554 FlutterVSyncClient* clientBefore =
viewController.touchRateCorrectionVSyncClient;
2555 XCTAssertNotNil(clientBefore);
2557 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2558 FlutterVSyncClient* clientAfter =
viewController.touchRateCorrectionVSyncClient;
2559 XCTAssertNotNil(clientAfter);
2561 XCTAssertTrue(clientBefore == clientAfter);
2564- (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateVsyncClientWhenRefreshRateIs60HZ {
2565 id mockDisplayLinkManager = [OCMockObject mockForClass:[FlutterDisplayLinkManager class]];
2566 double maxFrameRate = 60;
2567 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2573 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2577- (void)testTriggerTouchRateCorrectionVSyncClientCorrectly {
2578 id mockDisplayLinkManager = [OCMockObject mockForClass:[FlutterDisplayLinkManager class]];
2579 double maxFrameRate = 120;
2580 (void)[[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2586 [viewController loadView];
2587 [viewController viewDidLoad];
2589 FlutterVSyncClient* client =
viewController.touchRateCorrectionVSyncClient;
2590 CADisplayLink* link = client.displayLink;
2592 UITouch* fakeTouchBegan = [[UITouch alloc] init];
2593 fakeTouchBegan.phase = UITouchPhaseBegan;
2595 UITouch* fakeTouchMove = [[UITouch alloc] init];
2596 fakeTouchMove.phase = UITouchPhaseMoved;
2598 UITouch* fakeTouchEnd = [[UITouch alloc] init];
2599 fakeTouchEnd.phase = UITouchPhaseEnded;
2601 UITouch* fakeTouchCancelled = [[UITouch alloc] init];
2602 fakeTouchCancelled.phase = UITouchPhaseCancelled;
2605 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchBegan, nil]];
2606 XCTAssertFalse(link.isPaused);
2609 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd, nil]];
2610 XCTAssertTrue(link.isPaused);
2613 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchMove, nil]];
2614 XCTAssertFalse(link.isPaused);
2617 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchCancelled, nil]];
2618 XCTAssertTrue(link.isPaused);
2621 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2622 initWithObjects:fakeTouchBegan, fakeTouchEnd, nil]];
2623 XCTAssertFalse(link.isPaused);
2626 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd,
2627 fakeTouchCancelled, nil]];
2628 XCTAssertTrue(link.isPaused);
2631 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2632 initWithObjects:fakeTouchMove, fakeTouchEnd, nil]];
2633 XCTAssertFalse(link.isPaused);
2636- (void)testFlutterViewControllerStartKeyboardAnimationWillCreateVsyncClientCorrectly {
2637 id mockDisplayLink = OCMClassMock([CADisplayLink
class]);
2638 OCMStub(ClassMethod([mockDisplayLink displayLinkWithTarget:[OCMArg any]
2639 selector:sel_registerName(
"onDisplayLink:")]))
2640 .andReturn(mockDisplayLink);
2649 manager.targetViewInsetBottom = 100;
2650 [manager startKeyBoardAnimation:0.25];
2652 XCTAssertNotNil(
manager.keyboardAnimationVSyncClient);
2655 [mockDisplayLink stopMocking];
2659 testSetupKeyboardAnimationVsyncClientWillNotCreateNewVsyncClientWhenKeyboardAnimationCallbackIsNil {
2665 [viewController.keyboardInsetManager setUpKeyboardAnimationVsyncClient:nil];
2666 XCTAssertNil(
viewController.keyboardInsetManager.keyboardAnimationVSyncClient);
2669- (void)testSupportsShowingSystemContextMenuForIOS16AndAbove {
2675 BOOL supportsShowingSystemContextMenu = [viewController supportsShowingSystemContextMenu];
2676 if (@available(iOS 16.0, *)) {
2677 XCTAssertTrue(supportsShowingSystemContextMenu);
2679 XCTAssertFalse(supportsShowingSystemContextMenu);
2683- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsActive {
2689 id mockApplication = OCMClassMock([UIApplication
class]);
2690 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateActive);
2691 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2696- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsBackground {
2702 id mockApplication = OCMClassMock([UIApplication
class]);
2703 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateBackground);
2704 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2709- (void)testStateIsActiveAndBackgroundWhenApplicationStateIsInactive {
2715 id mockApplication = OCMClassMock([UIApplication
class]);
2716 OCMStub([mockApplication applicationState]).andReturn(UIApplicationStateInactive);
2717 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2722- (void)testStateIsActiveAndBackgroundWhenSceneStateIsActive {
2723 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2724 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2725 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2728 [engine runWithEntrypoint:nil];
2733 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateForegroundActive);
2737 [mockBundle stopMocking];
2738 [mockVC stopMocking];
2741- (void)testStateIsActiveAndBackgroundWhenSceneStateIsBackground {
2742 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2743 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2744 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2747 [engine runWithEntrypoint:nil];
2752 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateBackground);
2756 [mockBundle stopMocking];
2757 [mockVC stopMocking];
2760- (void)testStateIsActiveAndBackgroundWhenSceneStateIsInactive {
2761 id mockBundle = OCMPartialMock([NSBundle mainBundle]);
2762 OCMStub([mockBundle objectForInfoDictionaryKey:
@"NSExtension"]).andReturn(@{
2763 @"NSExtensionPointIdentifier" :
@"com.apple.share-services"
2766 [engine runWithEntrypoint:nil];
2771 OCMStub([mockVC activationState]).andReturn(UISceneActivationStateForegroundInactive);
2775 [mockBundle stopMocking];
2776 [mockVC stopMocking];
2779- (void)testPerformImplicitEngineCallbacks {
2780 id mockRegistrant = OCMProtocolMock(
@protocol(FlutterPluginRegistrant));
2781 id appDelegate = [[UIApplication sharedApplication] delegate];
2782 [appDelegate setMockLaunchEngine:self.mockEngine];
2783 UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Flutter" bundle:nil];
2784 XCTAssertTrue([appDelegate respondsToSelector:@selector(setPluginRegistrant:)]);
2785 [appDelegate setPluginRegistrant:mockRegistrant];
2788 [appDelegate setPluginRegistrant:nil];
2790 OCMVerify([
self.mockEngine performImplicitEngineCallback]);
2791 [appDelegate setMockLaunchEngine:nil];
2794- (void)testPerformImplicitEngineCallbacksUsesAppLaunchEventFallbacks {
2800 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2801 OCMStub([viewControllerMock awokenFromNib]).andReturn(YES);
2803 id mockApplication = OCMClassMock([UIApplication
class]);
2804 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2806 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2807 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2809 id mockScene = OCMClassMock([UIScene
class]);
2810 id mockSceneDelegate = OCMProtocolMock(
@protocol(UISceneDelegate));
2811 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
2812 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockScene]);
2816 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2818 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2819 OCMVerify([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2820 OCMVerify([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2823- (void)testPerformImplicitEngineCallbacksNoAppLaunchEventFallbacksWhenNoStoryboard {
2829 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2830 OCMStub([viewControllerMock awokenFromNib]).andReturn(NO);
2832 id mockApplication = OCMClassMock([UIApplication
class]);
2833 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2835 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2836 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2838 id mockScene = OCMClassMock([UIScene
class]);
2839 id mockSceneDelegate = OCMProtocolMock(
@protocol(UISceneDelegate));
2840 OCMStub([mockScene delegate]).andReturn(mockSceneDelegate);
2841 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockScene]);
2845 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2847 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2848 OCMReject([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2849 OCMReject([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2852- (void)testPerformImplicitEngineCallbacksNoAppLaunchEventFallbacksWhenNoScenes {
2858 OCMStub([mockEngine performImplicitEngineCallback]).andReturn(YES);
2859 OCMStub([viewControllerMock awokenFromNib]).andReturn(YES);
2861 id mockApplication = OCMClassMock([UIApplication
class]);
2862 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
2864 OCMStub([mockApplication delegate]).andReturn(mockApplicationDelegate);
2865 OCMStub([mockApplicationDelegate takeLaunchEngine]).andReturn(mockEngine);
2869 OCMStub([mockApplicationDelegate lifeCycleDelegate]).andReturn(mockLifecycleDelegate);
2871 [viewControllerMock sharedSetupWithProject:nil initialRoute:nil];
2872 OCMReject([mockLifecycleDelegate sceneFallbackWillFinishLaunchingApplication:mockApplication]);
2873 OCMReject([mockLifecycleDelegate sceneFallbackDidFinishLaunchingApplication:mockApplication]);
2876- (void)testGrabLaunchEngine {
2877 id appDelegate = [[UIApplication sharedApplication] delegate];
2878 XCTAssertTrue([appDelegate respondsToSelector:@selector(setMockLaunchEngine:)]);
2879 [appDelegate setMockLaunchEngine:self.mockEngine];
2880 UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"Flutter" bundle:nil];
2881 XCTAssertTrue(storyboard);
2887 [appDelegate setMockLaunchEngine:nil];
2890- (void)testDoesntGrabLaunchEngine {
2891 id appDelegate = [[UIApplication sharedApplication] delegate];
2892 XCTAssertTrue([appDelegate respondsToSelector:@selector(setMockLaunchEngine:)]);
2893 [appDelegate setMockLaunchEngine:self.mockEngine];
2895 XCTAssertNotNil(flutterViewController.
engine);
2896 XCTAssertNotEqual(flutterViewController.
engine,
self.mockEngine);
2897 [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.