5 #import <OCMock/OCMock.h>
6 #import <XCTest/XCTest.h>
8 #include "flutter/fml/platform/darwin/message_loop_darwin.h"
9 #import "flutter/lib/ui/window/platform_configuration.h"
10 #include "flutter/lib/ui/window/pointer_data.h"
11 #import "flutter/lib/ui/window/viewport_metrics.h"
22 #import "flutter/shell/platform/embedder/embedder.h"
23 #import "flutter/third_party/spring_animation/spring_animation.h"
31 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
32 callback:(nullable FlutterKeyEventCallback)callback
33 userData:(nullable
void*)userData;
34 - (fml::RefPtr<fml::TaskRunner>)uiTaskRunner;
44 @property(nonatomic, assign) BOOL didCallNotifyLowMemory;
46 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
47 callback:(nullable FlutterKeyEventCallback)callback
48 userData:(nullable
void*)userData;
57 - (void)notifyLowMemory {
58 _didCallNotifyLowMemory = YES;
61 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
62 callback:(FlutterKeyEventCallback)callback
63 userData:(
void*)userData API_AVAILABLE(ios(9.0)) {
64 if (callback == nil) {
70 CFRunLoopPerformBlock(CFRunLoopGetCurrent(), fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode,
72 callback(
true, userData);
78 - (BOOL)createShell:(NSString*)entrypoint
79 libraryURI:(NSString*)libraryURI
80 initialRoute:(NSString*)initialRoute;
81 - (void)dispatchPointerDataPacket:(std::unique_ptr<
flutter::PointerDataPacket>)packet;
82 - (void)updateViewportMetrics:(
flutter::ViewportMetrics)viewportMetrics;
114 @property(nonatomic, retain, readonly)
131 - (void)surfaceUpdated:(BOOL)appeared;
132 - (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences;
134 nextAction:(
void (^)())next API_AVAILABLE(ios(13.4));
135 - (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer;
137 - (void)onUserSettingsChanged:(NSNotification*)notification;
138 - (void)applicationWillTerminate:(NSNotification*)notification;
139 - (void)goToApplicationLifecycle:(nonnull NSString*)state;
140 - (void)handleKeyboardNotification:(NSNotification*)notification;
141 - (CGFloat)calculateKeyboardInset:(CGRect)keyboardFrame keyboardMode:(
int)keyboardMode;
142 - (BOOL)shouldIgnoreKeyboardNotification:(NSNotification*)notification;
143 - (FlutterKeyboardMode)calculateKeyboardAttachMode:(NSNotification*)notification;
144 - (CGFloat)calculateMultitaskingAdjustment:(CGRect)screenRect keyboardFrame:(CGRect)keyboardFrame;
145 - (void)startKeyBoardAnimation:(NSTimeInterval)duration;
148 - (void)setUpKeyboardSpringAnimationIfNeeded:(CAAnimation*)keyboardAnimation;
149 - (void)setUpKeyboardAnimationVsyncClient:
154 - (
flutter::PointerData)generatePointerDataForFake;
156 initialRoute:(nullable NSString*)initialRoute;
157 - (void)applicationBecameActive:(NSNotification*)notification;
158 - (void)applicationWillResignActive:(NSNotification*)notification;
159 - (void)applicationWillTerminate:(NSNotification*)notification;
160 - (void)applicationDidEnterBackground:(NSNotification*)notification;
161 - (void)applicationWillEnterForeground:(NSNotification*)notification;
162 - (void)sceneBecameActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
163 - (void)sceneWillResignActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
164 - (void)sceneWillDisconnect:(NSNotification*)notification API_AVAILABLE(ios(13.0));
165 - (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
166 - (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
167 - (void)triggerTouchRateCorrectionIfNeeded:(NSSet*)touches;
171 @property(nonatomic, strong)
id mockEngine;
172 @property(nonatomic, strong)
id mockTextInputPlugin;
173 @property(nonatomic, strong)
id messageSent;
174 - (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback;
177 @interface UITouch ()
179 @property(nonatomic, readwrite) UITouchPhase phase;
195 self.messageSent = nil;
201 [
self.mockEngine stopMocking];
202 self.mockEngine = nil;
203 self.mockTextInputPlugin = nil;
204 self.messageSent = nil;
207 - (id)setUpMockScreen {
208 UIScreen* mockScreen = OCMClassMock([UIScreen
class]);
210 CGRect screenBounds = CGRectMake(0, 0, 1170, 2532);
211 OCMStub([mockScreen bounds]).andReturn(screenBounds);
212 CGFloat screenScale = 1;
213 OCMStub([mockScreen scale]).andReturn(screenScale);
219 screen:(UIScreen*)screen
220 viewFrame:(CGRect)viewFrame
221 convertedFrame:(CGRect)convertedFrame {
222 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
223 id mockView = OCMClassMock([UIView
class]);
224 OCMStub([mockView frame]).andReturn(viewFrame);
225 OCMStub([mockView convertRect:viewFrame toCoordinateSpace:[OCMArg any]])
226 .andReturn(convertedFrame);
227 OCMStub([viewControllerMock viewIfLoaded]).andReturn(mockView);
232 - (void)testViewDidLoadWillInvokeCreateTouchRateCorrectionVSyncClient {
239 [viewControllerMock loadView];
240 [viewControllerMock viewDidLoad];
241 OCMVerify([viewControllerMock createTouchRateCorrectionVSyncClientIfNeeded]);
244 - (void)testStartKeyboardAnimationWillInvokeSetupKeyboardSpringAnimationIfNeeded {
251 viewControllerMock.targetViewInsetBottom = 100;
252 [viewControllerMock startKeyBoardAnimation:0.25];
254 CAAnimation* keyboardAnimation =
255 [[viewControllerMock keyboardAnimationView].layer animationForKey:@"position"];
257 OCMVerify([viewControllerMock setUpKeyboardSpringAnimationIfNeeded:keyboardAnimation]);
260 - (void)testSetupKeyboardSpringAnimationIfNeeded {
267 UIScreen* screen = [
self setUpMockScreen];
268 CGRect viewFrame = screen.bounds;
269 [
self setUpMockView:viewControllerMock
272 convertedFrame:viewFrame];
275 [viewControllerMock setUpKeyboardSpringAnimationIfNeeded:nil];
276 SpringAnimation* keyboardSpringAnimation = [viewControllerMock keyboardSpringAnimation];
277 XCTAssertTrue(keyboardSpringAnimation == nil);
280 CABasicAnimation* nonSpringAnimation = [CABasicAnimation animation];
281 nonSpringAnimation.duration = 1.0;
282 nonSpringAnimation.fromValue = [NSNumber numberWithFloat:0.0];
283 nonSpringAnimation.toValue = [NSNumber numberWithFloat:1.0];
284 nonSpringAnimation.keyPath =
@"position";
285 [viewControllerMock setUpKeyboardSpringAnimationIfNeeded:nonSpringAnimation];
286 keyboardSpringAnimation = [viewControllerMock keyboardSpringAnimation];
288 XCTAssertTrue(keyboardSpringAnimation == nil);
291 CASpringAnimation* springAnimation = [CASpringAnimation animation];
292 springAnimation.mass = 1.0;
293 springAnimation.stiffness = 100.0;
294 springAnimation.damping = 10.0;
295 springAnimation.keyPath =
@"position";
296 springAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
297 springAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
298 [viewControllerMock setUpKeyboardSpringAnimationIfNeeded:springAnimation];
299 keyboardSpringAnimation = [viewControllerMock keyboardSpringAnimation];
300 XCTAssertTrue(keyboardSpringAnimation != nil);
303 - (void)testKeyboardAnimationIsShowingAndCompounding {
310 UIScreen* screen = [
self setUpMockScreen];
311 CGRect viewFrame = screen.bounds;
312 [
self setUpMockView:viewControllerMock
315 convertedFrame:viewFrame];
318 CGFloat screenHeight = screen.bounds.size.height;
319 CGFloat screenWidth = screen.bounds.size.height;
322 CGRect initialShowKeyboardBeginFrame = CGRectMake(0, screenHeight, screenWidth, 250);
323 CGRect initialShowKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
324 NSNotification* fakeNotification = [NSNotification
325 notificationWithName:UIKeyboardWillChangeFrameNotification
328 @"UIKeyboardFrameBeginUserInfoKey" : @(initialShowKeyboardBeginFrame),
329 @"UIKeyboardFrameEndUserInfoKey" : @(initialShowKeyboardEndFrame),
330 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
331 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
333 viewControllerMock.targetViewInsetBottom = 0;
334 [viewControllerMock handleKeyboardNotification:fakeNotification];
335 BOOL isShowingAnimation1 = viewControllerMock.keyboardAnimationIsShowing;
336 XCTAssertTrue(isShowingAnimation1);
339 CGRect compoundingShowKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
340 CGRect compoundingShowKeyboardEndFrame = CGRectMake(0, screenHeight - 500, screenWidth, 500);
341 fakeNotification = [NSNotification
342 notificationWithName:UIKeyboardWillChangeFrameNotification
345 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingShowKeyboardBeginFrame),
346 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingShowKeyboardEndFrame),
347 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
348 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
351 [viewControllerMock handleKeyboardNotification:fakeNotification];
352 BOOL isShowingAnimation2 = viewControllerMock.keyboardAnimationIsShowing;
353 XCTAssertTrue(isShowingAnimation2);
354 XCTAssertTrue(isShowingAnimation1 == isShowingAnimation2);
357 CGRect initialHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 500, screenWidth, 250);
358 CGRect initialHideKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
359 fakeNotification = [NSNotification
360 notificationWithName:UIKeyboardWillChangeFrameNotification
363 @"UIKeyboardFrameBeginUserInfoKey" : @(initialHideKeyboardBeginFrame),
364 @"UIKeyboardFrameEndUserInfoKey" : @(initialHideKeyboardEndFrame),
365 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
366 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
369 [viewControllerMock handleKeyboardNotification:fakeNotification];
370 BOOL isShowingAnimation3 = viewControllerMock.keyboardAnimationIsShowing;
371 XCTAssertFalse(isShowingAnimation3);
372 XCTAssertTrue(isShowingAnimation2 != isShowingAnimation3);
375 CGRect compoundingHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
376 CGRect compoundingHideKeyboardEndFrame = CGRectMake(0, screenHeight, screenWidth, 500);
377 fakeNotification = [NSNotification
378 notificationWithName:UIKeyboardWillChangeFrameNotification
381 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingHideKeyboardBeginFrame),
382 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingHideKeyboardEndFrame),
383 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
384 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
387 [viewControllerMock handleKeyboardNotification:fakeNotification];
388 BOOL isShowingAnimation4 = viewControllerMock.keyboardAnimationIsShowing;
389 XCTAssertFalse(isShowingAnimation4);
390 XCTAssertTrue(isShowingAnimation3 == isShowingAnimation4);
393 - (void)testShouldIgnoreKeyboardNotification {
395 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
400 UIScreen* screen = [
self setUpMockScreen];
401 CGRect viewFrame = screen.bounds;
402 [
self setUpMockView:viewControllerMock
405 convertedFrame:viewFrame];
407 CGFloat screenWidth = screen.bounds.size.width;
408 CGFloat screenHeight = screen.bounds.size.height;
409 CGRect emptyKeyboard = CGRectZero;
410 CGRect zeroHeightKeyboard = CGRectMake(0, 0, screenWidth, 0);
411 CGRect validKeyboardEndFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
415 NSNotification* notification =
416 [NSNotification notificationWithName:UIKeyboardWillHideNotification
419 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
420 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
421 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
424 BOOL shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
425 XCTAssertTrue(shouldIgnore == NO);
429 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
432 @"UIKeyboardFrameEndUserInfoKey" : @(emptyKeyboard),
433 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
434 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
436 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
437 XCTAssertTrue(shouldIgnore == YES);
442 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
445 @"UIKeyboardFrameEndUserInfoKey" : @(zeroHeightKeyboard),
446 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
447 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
449 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
450 XCTAssertTrue(shouldIgnore == NO);
455 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
458 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
459 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
460 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
462 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
463 XCTAssertTrue(shouldIgnore == YES);
468 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
471 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
472 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
473 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
475 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
476 XCTAssertTrue(shouldIgnore == NO);
478 if (@available(iOS 13.0, *)) {
482 OCMStub([viewControllerMock isKeyboardInOrTransitioningFromBackground]).andReturn(YES);
486 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
489 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
490 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
491 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
493 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
494 XCTAssertTrue(shouldIgnore == YES);
497 - (void)testKeyboardAnimationWillNotCrashWhenEngineDestroyed {
503 [viewController setUpKeyboardAnimationVsyncClient:^(fml::TimePoint){
508 - (void)testKeyboardAnimationWillWaitUIThreadVsync {
521 const int delayTime = 1;
522 [engine uiTaskRunner]->PostTask([] { sleep(delayTime); });
523 XCTestExpectation* expectation = [
self expectationWithDescription:@"keyboard animation callback"];
525 __block CFTimeInterval fulfillTime;
527 fulfillTime = CACurrentMediaTime();
528 [expectation fulfill];
530 CFTimeInterval startTime = CACurrentMediaTime();
531 [viewController setUpKeyboardAnimationVsyncClient:callback];
532 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
533 XCTAssertTrue(fulfillTime - startTime > delayTime);
536 - (void)testCalculateKeyboardAttachMode {
538 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
544 UIScreen* screen = [
self setUpMockScreen];
545 CGRect viewFrame = screen.bounds;
546 [
self setUpMockView:viewControllerMock
549 convertedFrame:viewFrame];
551 CGFloat screenWidth = screen.bounds.size.width;
552 CGFloat screenHeight = screen.bounds.size.height;
555 CGRect keyboardFrame = CGRectZero;
556 NSNotification* notification =
557 [NSNotification notificationWithName:UIKeyboardWillHideNotification
560 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
561 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
562 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
564 FlutterKeyboardMode keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
565 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
568 keyboardFrame = CGRectZero;
569 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
572 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
573 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
574 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
576 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
577 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
580 keyboardFrame = CGRectMake(0, 0, screenWidth, 0);
581 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
584 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
585 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
586 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
588 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
589 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
592 keyboardFrame = CGRectMake(0, 0, 320, 320);
593 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
596 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
597 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
598 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
600 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
601 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
604 keyboardFrame = CGRectMake(0, 0, screenWidth, 320);
605 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
608 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
609 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
610 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
612 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
613 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
616 keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
617 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
620 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
621 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
622 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
624 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
625 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
628 CGFloat longDecimalHeight = 320.666666666666666;
629 keyboardFrame = CGRectMake(0, screenHeight - longDecimalHeight, screenWidth, longDecimalHeight);
630 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
633 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
634 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
635 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
637 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
638 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
641 keyboardFrame = CGRectMake(0, screenHeight - .0000001, screenWidth, longDecimalHeight);
642 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
645 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
646 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
647 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
649 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
650 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
653 keyboardFrame = CGRectMake(0, screenHeight, screenWidth, 320);
654 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
657 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
658 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
659 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
661 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
662 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
665 - (void)testCalculateMultitaskingAdjustment {
667 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
673 UIScreen* screen = [
self setUpMockScreen];
674 CGFloat screenWidth = screen.bounds.size.width;
675 CGFloat screenHeight = screen.bounds.size.height;
676 CGRect screenRect = screen.bounds;
677 CGRect viewOrigFrame = CGRectMake(0, 0, 320, screenHeight - 40);
678 CGRect convertedViewFrame = CGRectMake(20, 20, 320, screenHeight - 40);
679 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
680 id mockView = [
self setUpMockView:viewControllerMock
682 viewFrame:viewOrigFrame
683 convertedFrame:convertedViewFrame];
684 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
685 OCMStub([mockTraitCollection userInterfaceIdiom]).andReturn(UIUserInterfaceIdiomPad);
686 OCMStub([mockTraitCollection horizontalSizeClass]).andReturn(UIUserInterfaceSizeClassCompact);
687 OCMStub([mockTraitCollection verticalSizeClass]).andReturn(UIUserInterfaceSizeClassRegular);
688 OCMStub([mockView traitCollection]).andReturn(mockTraitCollection);
690 CGFloat adjustment = [viewControllerMock calculateMultitaskingAdjustment:screenRect
691 keyboardFrame:keyboardFrame];
692 XCTAssertTrue(adjustment == 20);
695 - (void)testCalculateKeyboardInset {
697 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
702 UIScreen* screen = [
self setUpMockScreen];
703 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
705 CGFloat screenWidth = screen.bounds.size.width;
706 CGFloat screenHeight = screen.bounds.size.height;
707 CGRect viewOrigFrame = CGRectMake(0, 0, 320, screenHeight - 40);
708 CGRect convertedViewFrame = CGRectMake(20, 20, 320, screenHeight - 40);
709 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
711 [
self setUpMockView:viewControllerMock
713 viewFrame:viewOrigFrame
714 convertedFrame:convertedViewFrame];
716 CGFloat inset = [viewControllerMock calculateKeyboardInset:keyboardFrame
717 keyboardMode:FlutterKeyboardModeDocked];
718 XCTAssertTrue(inset == 300 * screen.scale);
721 - (void)testHandleKeyboardNotification {
728 UIScreen* screen = [
self setUpMockScreen];
729 CGFloat screenWidth = screen.bounds.size.width;
730 CGFloat screenHeight = screen.bounds.size.height;
731 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
732 CGRect viewFrame = screen.bounds;
734 NSNotification* notification =
735 [NSNotification notificationWithName:UIKeyboardWillShowNotification
738 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
739 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
740 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
743 [
self setUpMockView:viewControllerMock
746 convertedFrame:viewFrame];
747 viewControllerMock.targetViewInsetBottom = 0;
748 XCTestExpectation* expectation = [
self expectationWithDescription:@"update viewport"];
749 OCMStub([viewControllerMock updateViewportMetricsIfNeeded]).andDo(^(NSInvocation* invocation) {
750 [expectation fulfill];
753 [viewControllerMock handleKeyboardNotification:notification];
754 XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 320 * screen.scale);
755 OCMVerify([viewControllerMock startKeyBoardAnimation:0.25]);
756 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
759 - (void)testEnsureBottomInsetIsZeroWhenKeyboardDismissed {
761 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
767 CGRect keyboardFrame = CGRectZero;
769 NSNotification* fakeNotification =
770 [NSNotification notificationWithName:UIKeyboardWillHideNotification
773 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
774 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
775 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
778 viewControllerMock.targetViewInsetBottom = 10;
779 [viewControllerMock handleKeyboardNotification:fakeNotification];
780 XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 0);
783 - (void)testEnsureViewportMetricsWillInvokeAndDisplayLinkWillInvalidateInViewDidDisappear {
785 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
790 [viewControllerMock viewDidDisappear:YES];
791 OCMVerify([viewControllerMock ensureViewportMetricsIsCorrect]);
792 OCMVerify([viewControllerMock invalidateKeyboardAnimationVSyncClient]);
795 - (void)testViewDidDisappearDoesntPauseEngineWhenNotTheViewController {
798 mockEngine.lifecycleChannel = lifecycleChannel;
803 id viewControllerMock = OCMPartialMock(viewControllerA);
804 OCMStub([viewControllerMock surfaceUpdated:NO]);
806 [viewControllerA viewDidDisappear:NO];
807 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
808 OCMReject([viewControllerMock surfaceUpdated:[OCMArg any]]);
811 - (void)testAppWillTerminateViewDidDestroyTheEngine {
813 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
818 OCMStub([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
820 [viewController applicationWillTerminate:nil];
821 OCMVerify([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
825 - (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController {
828 mockEngine.lifecycleChannel = lifecycleChannel;
836 OCMStub([viewControllerMock surfaceUpdated:NO]);
837 [viewController viewDidDisappear:NO];
838 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
839 OCMVerify([viewControllerMock surfaceUpdated:NO]);
841 XCTAssertNil(weakViewController);
845 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear {
847 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
851 [viewController viewWillAppear:YES];
856 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear {
858 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
868 [viewControllerA viewWillAppear:YES];
869 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
873 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear {
875 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
879 [viewController viewDidAppear:YES];
884 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear {
886 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
896 [viewControllerA viewDidAppear:YES];
897 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
901 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear {
904 mockEngine.lifecycleChannel = lifecycleChannel;
909 [viewController viewWillDisappear:NO];
910 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
914 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear {
917 mockEngine.lifecycleChannel = lifecycleChannel;
925 [viewControllerA viewDidDisappear:NO];
926 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
929 - (void)testUpdateViewportMetricsIfNeeded_DoesntInvokeEngineWhenNotTheViewController {
931 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
940 [viewControllerA updateViewportMetricsIfNeeded];
941 flutter::ViewportMetrics viewportMetrics;
942 OCMVerify(never(), [
mockEngine updateViewportMetrics:viewportMetrics]);
945 - (void)testUpdateViewportMetricsIfNeeded_DoesInvokeEngineWhenIsTheViewController {
947 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
952 flutter::ViewportMetrics viewportMetrics;
953 OCMExpect([
mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
954 [viewController updateViewportMetricsIfNeeded];
958 - (void)testUpdateViewportMetricsIfNeeded_DoesNotInvokeEngineWhenShouldBeIgnoredDuringRotation {
960 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
965 UIScreen* screen = [
self setUpMockScreen];
966 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
969 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
970 OCMStub([mockCoordinator transitionDuration]).andReturn(0.5);
973 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
975 [viewController updateViewportMetricsIfNeeded];
977 OCMVerify(never(), [
mockEngine updateViewportMetrics:flutter::ViewportMetrics()]);
980 - (void)testViewWillTransitionToSize_DoesDelayEngineCallIfNonZeroDuration {
982 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
987 UIScreen* screen = [
self setUpMockScreen];
988 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
992 NSTimeInterval transitionDuration = 0.5;
993 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
994 OCMStub([mockCoordinator transitionDuration]).andReturn(transitionDuration);
996 flutter::ViewportMetrics viewportMetrics;
997 OCMExpect([
mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
999 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1001 [viewController updateViewportMetricsIfNeeded];
1002 OCMVerify(never(), [
mockEngine updateViewportMetrics:flutter::ViewportMetrics()]);
1006 XCTWaiterResult result = [XCTWaiter
1007 waitForExpectations:@[ [
self expectationWithDescription:@"Waiting for rotation duration"] ]
1008 timeout:transitionDuration];
1009 XCTAssertEqual(result, XCTWaiterResultTimedOut);
1014 - (void)testViewWillTransitionToSize_DoesNotDelayEngineCallIfZeroDuration {
1016 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1021 UIScreen* screen = [
self setUpMockScreen];
1022 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1026 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1027 OCMStub([mockCoordinator transitionDuration]).andReturn(0);
1029 flutter::ViewportMetrics viewportMetrics;
1030 OCMExpect([
mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1033 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1034 [viewController updateViewportMetricsIfNeeded];
1039 - (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController {
1041 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1050 UIView* view = viewControllerA.view;
1051 XCTAssertNotNil(view);
1055 - (void)testViewDidLoadDoesInvokeEngineWhenIsTheViewController {
1057 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1064 XCTAssertNotNil(view);
1065 OCMVerify(times(1), [
mockEngine attachView]);
1068 - (void)testViewDidLoadDoesntInvokeEngineAttachViewWhenEngineNeedsLaunch {
1070 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1076 [viewController sharedSetupWithProject:nil initialRoute:nil];
1079 XCTAssertNotNil(view);
1083 - (void)testSplashScreenViewRemoveNotCrash {
1088 [flutterViewController setSplashScreenView:[[UIView alloc] init]];
1089 [flutterViewController setSplashScreenView:nil];
1092 - (void)testInternalPluginsWeakPtrNotCrash {
1098 [vc addInternalPlugins];
1101 [(NSArray<id<FlutterKeyPrimaryResponder>>*)keyboardManager.primaryResponders firstObject];
1102 sendEvent = [keyPrimaryResponder sendEvent];
1106 sendEvent({}, nil, nil);
1111 - (void)testInternalPluginsInvokeInViewDidLoad {
1113 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1121 XCTAssertNotNil(view);
1122 [viewController viewDidLoad];
1126 - (void)testBinaryMessenger {
1130 XCTAssertNotNil(vc);
1132 OCMStub([
self.
mockEngine binaryMessenger]).andReturn(messenger);
1134 OCMVerify([
self.
mockEngine binaryMessenger]);
1137 - (void)testViewControllerIsReleased {
1139 __weak UIView* weakView;
1148 [viewController viewDidLoad];
1152 XCTAssertNil(weakViewController);
1153 XCTAssertNil(weakView);
1156 #pragma mark - Platform Brightness
1158 - (void)testItReportsLightPlatformBrightnessByDefault {
1161 OCMStub([
self.
mockEngine settingsChannel]).andReturn(settingsChannel);
1168 [vc traitCollectionDidChange:nil];
1171 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1172 return [message[@"platformBrightness"] isEqualToString:@"light"];
1176 [settingsChannel stopMocking];
1179 - (void)testItReportsPlatformBrightnessWhenViewWillAppear {
1183 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1184 OCMStub([
mockEngine settingsChannel]).andReturn(settingsChannel);
1190 [vc viewWillAppear:false];
1193 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1194 return [message[@"platformBrightness"] isEqualToString:@"light"];
1198 [settingsChannel stopMocking];
1201 - (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt {
1202 if (@available(iOS 13, *)) {
1210 OCMStub([
self.
mockEngine settingsChannel]).andReturn(settingsChannel);
1211 id mockTraitCollection =
1212 [
self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
1221 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1224 [partialMockVC traitCollectionDidChange:nil];
1227 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1228 return [message[@"platformBrightness"] isEqualToString:@"dark"];
1232 [partialMockVC stopMocking];
1233 [settingsChannel stopMocking];
1234 [mockTraitCollection stopMocking];
1239 - (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style {
1240 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1241 OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style);
1242 return mockTraitCollection;
1245 #pragma mark - Platform Contrast
1247 - (void)testItReportsNormalPlatformContrastByDefault {
1248 if (@available(iOS 13, *)) {
1256 OCMStub([
self.
mockEngine settingsChannel]).andReturn(settingsChannel);
1263 [vc traitCollectionDidChange:nil];
1266 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1267 return [message[@"platformContrast"] isEqualToString:@"normal"];
1271 [settingsChannel stopMocking];
1274 - (void)testItReportsPlatformContrastWhenViewWillAppear {
1275 if (@available(iOS 13, *)) {
1281 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1285 OCMStub([
mockEngine settingsChannel]).andReturn(settingsChannel);
1291 [vc viewWillAppear:false];
1294 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1295 return [message[@"platformContrast"] isEqualToString:@"normal"];
1299 [settingsChannel stopMocking];
1302 - (void)testItReportsHighContrastWhenTraitCollectionRequestsIt {
1303 if (@available(iOS 13, *)) {
1311 OCMStub([
self.
mockEngine settingsChannel]).andReturn(settingsChannel);
1313 id mockTraitCollection = [
self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh];
1322 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1325 [partialMockVC traitCollectionDidChange:mockTraitCollection];
1328 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1329 return [message[@"platformContrast"] isEqualToString:@"high"];
1333 [partialMockVC stopMocking];
1334 [settingsChannel stopMocking];
1335 [mockTraitCollection stopMocking];
1338 - (void)testItReportsAccessibilityOnOffSwitchLabelsFlagNotSet {
1339 if (@available(iOS 13, *)) {
1349 OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(NO);
1352 int32_t flags = [partialMockViewController accessibilityFlags];
1355 XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) == 0);
1358 - (void)testItReportsAccessibilityOnOffSwitchLabelsFlagSet {
1359 if (@available(iOS 13, *)) {
1369 OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(YES);
1372 int32_t flags = [partialMockViewController accessibilityFlags];
1375 XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) != 0);
1378 - (void)testAccessibilityPerformEscapePopsRoute {
1380 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1382 OCMStub([
mockEngine navigationChannel]).andReturn(mockNavigationChannel);
1389 OCMVerify([mockNavigationChannel invokeMethod:
@"popRoute" arguments:nil]);
1391 [mockNavigationChannel stopMocking];
1394 - (void)testPerformOrientationUpdateForcesOrientationChange {
1395 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1396 currentOrientation:UIInterfaceOrientationLandscapeLeft
1397 didChangeOrientation:YES
1398 resultingOrientation:UIInterfaceOrientationPortrait];
1400 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1401 currentOrientation:UIInterfaceOrientationLandscapeRight
1402 didChangeOrientation:YES
1403 resultingOrientation:UIInterfaceOrientationPortrait];
1405 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1406 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1407 didChangeOrientation:YES
1408 resultingOrientation:UIInterfaceOrientationPortrait];
1410 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1411 currentOrientation:UIInterfaceOrientationLandscapeLeft
1412 didChangeOrientation:YES
1413 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1415 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1416 currentOrientation:UIInterfaceOrientationLandscapeRight
1417 didChangeOrientation:YES
1418 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1420 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1421 currentOrientation:UIInterfaceOrientationPortrait
1422 didChangeOrientation:YES
1423 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1425 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1426 currentOrientation:UIInterfaceOrientationPortrait
1427 didChangeOrientation:YES
1428 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1430 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1431 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1432 didChangeOrientation:YES
1433 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1435 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1436 currentOrientation:UIInterfaceOrientationPortrait
1437 didChangeOrientation:YES
1438 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1440 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1441 currentOrientation:UIInterfaceOrientationLandscapeRight
1442 didChangeOrientation:YES
1443 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1445 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1446 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1447 didChangeOrientation:YES
1448 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1450 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1451 currentOrientation:UIInterfaceOrientationPortrait
1452 didChangeOrientation:YES
1453 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1455 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1456 currentOrientation:UIInterfaceOrientationLandscapeLeft
1457 didChangeOrientation:YES
1458 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1460 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1461 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1462 didChangeOrientation:YES
1463 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1465 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1466 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1467 didChangeOrientation:YES
1468 resultingOrientation:UIInterfaceOrientationPortrait];
1471 - (void)testPerformOrientationUpdateDoesNotForceOrientationChange {
1472 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1473 currentOrientation:UIInterfaceOrientationPortrait
1474 didChangeOrientation:NO
1475 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1477 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1478 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1479 didChangeOrientation:NO
1480 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1482 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1483 currentOrientation:UIInterfaceOrientationLandscapeLeft
1484 didChangeOrientation:NO
1485 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1487 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1488 currentOrientation:UIInterfaceOrientationLandscapeRight
1489 didChangeOrientation:NO
1490 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1492 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1493 currentOrientation:UIInterfaceOrientationPortrait
1494 didChangeOrientation:NO
1495 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1497 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1498 currentOrientation:UIInterfaceOrientationLandscapeLeft
1499 didChangeOrientation:NO
1500 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1502 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1503 currentOrientation:UIInterfaceOrientationLandscapeRight
1504 didChangeOrientation:NO
1505 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1507 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1508 currentOrientation:UIInterfaceOrientationPortrait
1509 didChangeOrientation:NO
1510 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1512 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1513 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1514 didChangeOrientation:NO
1515 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1517 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1518 currentOrientation:UIInterfaceOrientationLandscapeLeft
1519 didChangeOrientation:NO
1520 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1522 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1523 currentOrientation:UIInterfaceOrientationLandscapeRight
1524 didChangeOrientation:NO
1525 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1527 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1528 currentOrientation:UIInterfaceOrientationLandscapeLeft
1529 didChangeOrientation:NO
1530 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1532 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1533 currentOrientation:UIInterfaceOrientationLandscapeRight
1534 didChangeOrientation:NO
1535 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1540 - (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask
1541 currentOrientation:(UIInterfaceOrientation)currentOrientation
1542 didChangeOrientation:(BOOL)didChange
1543 resultingOrientation:(UIInterfaceOrientation)resultingOrientation {
1544 id mockApplication = OCMClassMock([UIApplication
class]);
1548 __block __weak
id weakPreferences;
1554 if (@available(iOS 16.0, *)) {
1555 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1556 mockVC = OCMPartialMock(realVC);
1557 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1558 if (realVC.supportedInterfaceOrientations == mask) {
1559 OCMReject([mockWindowScene requestGeometryUpdateWithPreferences:[OCMArg any]
1560 errorHandler:[OCMArg any]]);
1564 OCMExpect([mockWindowScene
1565 requestGeometryUpdateWithPreferences:[OCMArg checkWithBlock:^BOOL(
1566 UIWindowSceneGeometryPreferencesIOS*
1568 weakPreferences = preferences;
1569 return preferences.interfaceOrientations == mask;
1571 errorHandler:[OCMArg any]]);
1573 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
1574 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockWindowScene]);
1576 deviceMock = OCMPartialMock([UIDevice currentDevice]);
1578 OCMReject([deviceMock setValue:[OCMArg any] forKey:
@"orientation"]);
1580 OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:
@"orientation"]);
1582 if (@available(iOS 13.0, *)) {
1583 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1584 mockVC = OCMPartialMock(realVC);
1585 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1586 OCMStub(((UIWindowScene*)mockWindowScene).interfaceOrientation)
1587 .andReturn(currentOrientation);
1589 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
1590 OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation);
1594 [realVC performOrientationUpdate:mask];
1595 if (@available(iOS 16.0, *)) {
1596 OCMVerifyAll(mockWindowScene);
1598 OCMVerifyAll(deviceMock);
1601 [mockWindowScene stopMocking];
1602 [deviceMock stopMocking];
1603 [mockApplication stopMocking];
1604 XCTAssertNil(weakPreferences);
1609 - (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast {
1610 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1611 OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast);
1612 return mockTraitCollection;
1615 - (void)testWillDeallocNotification {
1616 XCTestExpectation* expectation =
1617 [[XCTestExpectation alloc] initWithDescription:@"notification called"];
1624 [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
1626 queue:[NSOperationQueue mainQueue]
1627 usingBlock:^(NSNotification* _Nonnull note) {
1628 [expectation fulfill];
1630 XCTAssertNotNil(realVC);
1633 [
self waitForExpectations:@[ expectation ] timeout:1.0];
1636 - (void)testReleasesKeyboardManagerOnDealloc {
1641 [viewController addInternalPlugins];
1643 XCTAssertNotNil(weakKeyboardManager);
1644 [viewController deregisterNotifications];
1648 XCTAssertNil(weakKeyboardManager);
1651 - (void)testDoesntLoadViewInInit {
1654 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
1658 XCTAssertFalse([realVC isViewLoaded],
@"shouldn't have loaded since it hasn't been shown");
1659 engine.viewController = nil;
1662 - (void)testHideOverlay {
1665 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
1669 XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden,
@"");
1670 [[NSNotificationCenter defaultCenter] postNotificationName:FlutterViewControllerHideHomeIndicator
1672 XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden,
@"");
1673 engine.viewController = nil;
1676 - (void)testNotifyLowMemory {
1682 OCMStub([viewControllerMock surfaceUpdated:NO]);
1683 [viewController beginAppearanceTransition:NO animated:NO];
1684 [viewController endAppearanceTransition];
1685 XCTAssertTrue(
mockEngine.didCallNotifyLowMemory);
1688 - (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback {
1689 NSMutableDictionary* replyMessage = [@{
1694 self.messageSent = message;
1695 CFRunLoopPerformBlock(CFRunLoopGetCurrent(), fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode,
1697 callback(replyMessage);
1702 if (@available(iOS 13.4, *)) {
1709 OCMStub([
mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
1710 .andCall(
self,
@selector(sendMessage:reply:));
1712 mockEngine.textInputPlugin =
self.mockTextInputPlugin;
1720 [vc addInternalPlugins];
1722 [vc handlePressEvent:keyUpEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0)
1727 XCTAssert([
self.
messageSent[
@"keymap"] isEqualToString:
@"ios"]);
1728 XCTAssert([
self.
messageSent[
@"type"] isEqualToString:
@"keyup"]);
1729 XCTAssert([
self.
messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
1730 XCTAssert([
self.
messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
1731 XCTAssert([
self.
messageSent[
@"characters"] isEqualToString:
@""]);
1732 XCTAssert([
self.
messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@""]);
1733 [vc deregisterNotifications];
1737 if (@available(iOS 13.4, *)) {
1745 OCMStub([
mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
1746 .andCall(
self,
@selector(sendMessage:reply:));
1748 mockEngine.textInputPlugin =
self.mockTextInputPlugin;
1755 [vc addInternalPlugins];
1757 [vc handlePressEvent:keyDownEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0f, "A",
1763 XCTAssert([
self.
messageSent[
@"keymap"] isEqualToString:
@"ios"]);
1764 XCTAssert([
self.
messageSent[
@"type"] isEqualToString:
@"keydown"]);
1765 XCTAssert([
self.
messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
1766 XCTAssert([
self.
messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
1767 XCTAssert([
self.
messageSent[
@"characters"] isEqualToString:
@"A"]);
1768 XCTAssert([
self.
messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@"a"]);
1769 [vc deregisterNotifications];
1774 if (@available(iOS 13.4, *)) {
1780 OCMStub([keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
1781 .andCall(
self,
@selector(sendMessage:reply:));
1783 OCMStub([
self.
mockEngine keyEventChannel]).andReturn(keyEventChannel);
1791 [vc addInternalPlugins];
1793 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseStationary, UIKeyboardHIDUsageKeyboardA,
1794 UIKeyModifierShift, 123.0)
1797 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseCancelled, UIKeyboardHIDUsageKeyboardA,
1798 UIKeyModifierShift, 123.0)
1801 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseChanged, UIKeyboardHIDUsageKeyboardA,
1802 UIKeyModifierShift, 123.0)
1807 OCMVerify(never(), [keyEventChannel sendMessage:[OCMArg any]]);
1808 [vc deregisterNotifications];
1812 if (@available(iOS 13.4, *)) {
1821 XCTAssertNotNil(vc);
1822 UIView* view = vc.view;
1823 XCTAssertNotNil(view);
1824 NSArray* gestureRecognizers = view.gestureRecognizers;
1825 XCTAssertNotNil(gestureRecognizers);
1828 for (
id gesture in gestureRecognizers) {
1829 if ([gesture isKindOfClass:[UIPanGestureRecognizer
class]]) {
1834 XCTAssertTrue(found);
1838 if (@available(iOS 13.4, *)) {
1847 XCTAssertNotNil(vc);
1849 id mockPanGestureRecognizer = OCMClassMock([UIPanGestureRecognizer
class]);
1850 XCTAssertNotNil(mockPanGestureRecognizer);
1852 [vc discreteScrollEvent:mockPanGestureRecognizer];
1854 [[[
self.mockEngine verify] ignoringNonObjectArgs]
1855 dispatchPointerDataPacket:std::make_unique<flutter::PointerDataPacket>(0)];
1858 - (void)testFakeEventTimeStamp {
1862 XCTAssertNotNil(vc);
1864 flutter::PointerData pointer_data = [vc generatePointerDataForFake];
1865 int64_t current_micros = [[NSProcessInfo processInfo] systemUptime] * 1000 * 1000;
1866 int64_t interval_micros = current_micros - pointer_data.time_stamp;
1867 const int64_t tolerance_millis = 2;
1868 XCTAssertTrue(interval_micros / 1000 < tolerance_millis,
1869 @"PointerData.time_stamp should be equal to NSProcessInfo.systemUptime");
1872 - (void)testSplashScreenViewCanSetNil {
1875 [flutterViewController setSplashScreenView:nil];
1878 - (void)testLifeCycleNotificationBecameActive {
1883 UIWindow* window = [[UIWindow alloc] init];
1884 [window addSubview:flutterViewController.view];
1885 flutterViewController.view.bounds = CGRectMake(0, 0, 100, 100);
1886 [flutterViewController viewDidLayoutSubviews];
1887 NSNotification* sceneNotification =
1888 [NSNotification notificationWithName:UISceneDidActivateNotification object:nil userInfo:nil];
1889 NSNotification* applicationNotification =
1890 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
1893 id mockVC = OCMPartialMock(flutterViewController);
1894 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
1895 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
1896 #if APPLICATION_EXTENSION_API_ONLY
1897 OCMVerify([mockVC sceneBecameActive:[OCMArg any]]);
1898 OCMReject([mockVC applicationBecameActive:[OCMArg any]]);
1900 OCMReject([mockVC sceneBecameActive:[OCMArg any]]);
1901 OCMVerify([mockVC applicationBecameActive:[OCMArg any]]);
1903 XCTAssertFalse(flutterViewController.isKeyboardInOrTransitioningFromBackground);
1904 OCMVerify([mockVC surfaceUpdated:YES]);
1905 XCTestExpectation* timeoutApplicationLifeCycle =
1906 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
1907 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)),
1908 dispatch_get_main_queue(), ^{
1909 [timeoutApplicationLifeCycle fulfill];
1910 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
1911 [flutterViewController deregisterNotifications];
1913 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
1916 - (void)testLifeCycleNotificationWillResignActive {
1921 NSNotification* sceneNotification =
1922 [NSNotification notificationWithName:UISceneWillDeactivateNotification
1925 NSNotification* applicationNotification =
1926 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
1929 id mockVC = OCMPartialMock(flutterViewController);
1930 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
1931 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
1932 #if APPLICATION_EXTENSION_API_ONLY
1933 OCMVerify([mockVC sceneWillResignActive:[OCMArg any]]);
1934 OCMReject([mockVC applicationWillResignActive:[OCMArg any]]);
1936 OCMReject([mockVC sceneWillResignActive:[OCMArg any]]);
1937 OCMVerify([mockVC applicationWillResignActive:[OCMArg any]]);
1939 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
1940 [flutterViewController deregisterNotifications];
1943 - (void)testLifeCycleNotificationWillTerminate {
1948 NSNotification* sceneNotification =
1949 [NSNotification notificationWithName:UISceneDidDisconnectNotification
1952 NSNotification* applicationNotification =
1953 [NSNotification notificationWithName:UIApplicationWillTerminateNotification
1956 id mockVC = OCMPartialMock(flutterViewController);
1959 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
1960 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
1961 #if APPLICATION_EXTENSION_API_ONLY
1962 OCMVerify([mockVC sceneWillDisconnect:[OCMArg any]]);
1963 OCMReject([mockVC applicationWillTerminate:[OCMArg any]]);
1965 OCMReject([mockVC sceneWillDisconnect:[OCMArg any]]);
1966 OCMVerify([mockVC applicationWillTerminate:[OCMArg any]]);
1968 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
1970 [flutterViewController deregisterNotifications];
1973 - (void)testLifeCycleNotificationDidEnterBackground {
1978 NSNotification* sceneNotification =
1979 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
1982 NSNotification* applicationNotification =
1983 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
1986 id mockVC = OCMPartialMock(flutterViewController);
1987 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
1988 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
1989 #if APPLICATION_EXTENSION_API_ONLY
1990 OCMVerify([mockVC sceneDidEnterBackground:[OCMArg any]]);
1991 OCMReject([mockVC applicationDidEnterBackground:[OCMArg any]]);
1993 OCMReject([mockVC sceneDidEnterBackground:[OCMArg any]]);
1994 OCMVerify([mockVC applicationDidEnterBackground:[OCMArg any]]);
1996 XCTAssertTrue(flutterViewController.isKeyboardInOrTransitioningFromBackground);
1997 OCMVerify([mockVC surfaceUpdated:NO]);
1998 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
1999 [flutterViewController deregisterNotifications];
2002 - (void)testLifeCycleNotificationWillEnterForeground {
2007 NSNotification* sceneNotification =
2008 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2011 NSNotification* applicationNotification =
2012 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2015 id mockVC = OCMPartialMock(flutterViewController);
2016 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
2017 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
2018 #if APPLICATION_EXTENSION_API_ONLY
2019 OCMVerify([mockVC sceneWillEnterForeground:[OCMArg any]]);
2020 OCMReject([mockVC applicationWillEnterForeground:[OCMArg any]]);
2022 OCMReject([mockVC sceneWillEnterForeground:[OCMArg any]]);
2023 OCMVerify([mockVC applicationWillEnterForeground:[OCMArg any]]);
2025 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2026 [flutterViewController deregisterNotifications];
2029 - (void)testLifeCycleNotificationCancelledInvalidResumed {
2034 NSNotification* applicationDidBecomeActiveNotification =
2035 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2038 NSNotification* applicationWillResignActiveNotification =
2039 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2042 id mockVC = OCMPartialMock(flutterViewController);
2043 [[NSNotificationCenter defaultCenter] postNotification:applicationDidBecomeActiveNotification];
2044 [[NSNotificationCenter defaultCenter] postNotification:applicationWillResignActiveNotification];
2045 #if APPLICATION_EXTENSION_API_ONLY
2047 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2050 XCTestExpectation* timeoutApplicationLifeCycle =
2051 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2052 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)),
2053 dispatch_get_main_queue(), ^{
2054 OCMReject([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2055 [timeoutApplicationLifeCycle fulfill];
2056 [flutterViewController deregisterNotifications];
2058 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2061 - (void)testSetupKeyboardAnimationVsyncClientWillCreateNewVsyncClientForFlutterViewController {
2062 id bundleMock = OCMPartialMock([NSBundle mainBundle]);
2063 OCMStub([bundleMock objectForInfoDictionaryKey:
@"CADisableMinimumFrameDurationOnPhone"])
2066 double maxFrameRate = 120;
2067 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2075 [viewController setUpKeyboardAnimationVsyncClient:callback];
2077 CADisplayLink* link = [viewController.keyboardAnimationVSyncClient getDisplayLink];
2078 XCTAssertNotNil(link);
2079 if (@available(iOS 15.0, *)) {
2080 XCTAssertEqual(link.preferredFrameRateRange.maximum, maxFrameRate);
2081 XCTAssertEqual(link.preferredFrameRateRange.preferred, maxFrameRate);
2082 XCTAssertEqual(link.preferredFrameRateRange.minimum, maxFrameRate / 2);
2084 XCTAssertEqual(link.preferredFramesPerSecond, maxFrameRate);
2089 testCreateTouchRateCorrectionVSyncClientWillCreateVsyncClientWhenRefreshRateIsLargerThan60HZ {
2091 double maxFrameRate = 120;
2092 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2098 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2102 - (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateNewVSyncClientWhenClientAlreadyExists {
2104 double maxFrameRate = 120;
2105 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2112 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2114 XCTAssertNotNil(clientBefore);
2116 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2118 XCTAssertNotNil(clientAfter);
2120 XCTAssertTrue(clientBefore == clientAfter);
2123 - (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateVsyncClientWhenRefreshRateIs60HZ {
2125 double maxFrameRate = 60;
2126 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2132 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2136 - (void)testTriggerTouchRateCorrectionVSyncClientCorrectly {
2138 double maxFrameRate = 120;
2139 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2145 [viewController loadView];
2146 [viewController viewDidLoad];
2149 CADisplayLink* link = [client getDisplayLink];
2151 UITouch* fakeTouchBegan = [[UITouch alloc] init];
2152 fakeTouchBegan.phase = UITouchPhaseBegan;
2154 UITouch* fakeTouchMove = [[UITouch alloc] init];
2155 fakeTouchMove.phase = UITouchPhaseMoved;
2157 UITouch* fakeTouchEnd = [[UITouch alloc] init];
2158 fakeTouchEnd.phase = UITouchPhaseEnded;
2160 UITouch* fakeTouchCancelled = [[UITouch alloc] init];
2161 fakeTouchCancelled.phase = UITouchPhaseCancelled;
2164 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchBegan, nil]];
2165 XCTAssertFalse(link.isPaused);
2168 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd, nil]];
2169 XCTAssertTrue(link.isPaused);
2172 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchMove, nil]];
2173 XCTAssertFalse(link.isPaused);
2176 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchCancelled, nil]];
2177 XCTAssertTrue(link.isPaused);
2180 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2181 initWithObjects:fakeTouchBegan, fakeTouchEnd, nil]];
2182 XCTAssertFalse(link.isPaused);
2185 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd,
2186 fakeTouchCancelled, nil]];
2187 XCTAssertTrue(link.isPaused);
2190 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2191 initWithObjects:fakeTouchMove, fakeTouchEnd, nil]];
2192 XCTAssertFalse(link.isPaused);
2195 - (void)testFlutterViewControllerStartKeyboardAnimationWillCreateVsyncClientCorrectly {
2202 [viewController startKeyBoardAnimation:0.25];
2207 testSetupKeyboardAnimationVsyncClientWillNotCreateNewVsyncClientWhenKeyboardAnimationCallbackIsNil {
2213 [viewController setUpKeyboardAnimationVsyncClient:nil];
2217 - (void)testSupportsShowingSystemContextMenuForIOS16AndAbove {
2223 BOOL supportsShowingSystemContextMenu = [viewController supportsShowingSystemContextMenu];
2224 if (@available(iOS 16.0, *)) {
2225 XCTAssertTrue(supportsShowingSystemContextMenu);
2227 XCTAssertFalse(supportsShowingSystemContextMenu);