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"
23 #import "flutter/shell/platform/embedder/embedder.h"
24 #import "flutter/third_party/spring_animation/spring_animation.h"
32 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
33 callback:(nullable FlutterKeyEventCallback)callback
34 userData:(nullable
void*)userData;
35 - (fml::RefPtr<fml::TaskRunner>)uiTaskRunner;
45 @property(nonatomic, assign) BOOL didCallNotifyLowMemory;
47 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
48 callback:(nullable FlutterKeyEventCallback)callback
49 userData:(nullable
void*)userData;
58 - (void)notifyLowMemory {
59 _didCallNotifyLowMemory = YES;
62 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
63 callback:(FlutterKeyEventCallback)callback
64 userData:(
void*)userData API_AVAILABLE(ios(9.0)) {
65 if (callback == nil) {
71 CFRunLoopPerformBlock(CFRunLoopGetCurrent(), fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode,
73 callback(
true, userData);
79 - (BOOL)createShell:(NSString*)entrypoint
80 libraryURI:(NSString*)libraryURI
81 initialRoute:(NSString*)initialRoute;
82 - (void)dispatchPointerDataPacket:(std::unique_ptr<
flutter::PointerDataPacket>)packet;
83 - (void)updateViewportMetrics:(
flutter::ViewportMetrics)viewportMetrics;
115 @property(nonatomic, retain, readonly)
132 - (void)surfaceUpdated:(BOOL)appeared;
133 - (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences;
135 nextAction:(
void (^)())next API_AVAILABLE(ios(13.4));
136 - (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer;
138 - (void)onUserSettingsChanged:(NSNotification*)notification;
139 - (void)applicationWillTerminate:(NSNotification*)notification;
140 - (void)goToApplicationLifecycle:(nonnull NSString*)state;
141 - (void)handleKeyboardNotification:(NSNotification*)notification;
142 - (CGFloat)calculateKeyboardInset:(CGRect)keyboardFrame keyboardMode:(
int)keyboardMode;
143 - (BOOL)shouldIgnoreKeyboardNotification:(NSNotification*)notification;
144 - (FlutterKeyboardMode)calculateKeyboardAttachMode:(NSNotification*)notification;
145 - (CGFloat)calculateMultitaskingAdjustment:(CGRect)screenRect keyboardFrame:(CGRect)keyboardFrame;
146 - (void)startKeyBoardAnimation:(NSTimeInterval)duration;
149 - (void)setUpKeyboardSpringAnimationIfNeeded:(CAAnimation*)keyboardAnimation;
150 - (void)setUpKeyboardAnimationVsyncClient:
155 - (
flutter::PointerData)generatePointerDataForFake;
157 initialRoute:(nullable NSString*)initialRoute;
158 - (void)applicationBecameActive:(NSNotification*)notification;
159 - (void)applicationWillResignActive:(NSNotification*)notification;
160 - (void)applicationWillTerminate:(NSNotification*)notification;
161 - (void)applicationDidEnterBackground:(NSNotification*)notification;
162 - (void)applicationWillEnterForeground:(NSNotification*)notification;
163 - (void)sceneBecameActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
164 - (void)sceneWillResignActive:(NSNotification*)notification API_AVAILABLE(ios(13.0));
165 - (void)sceneWillDisconnect:(NSNotification*)notification API_AVAILABLE(ios(13.0));
166 - (void)sceneDidEnterBackground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
167 - (void)sceneWillEnterForeground:(NSNotification*)notification API_AVAILABLE(ios(13.0));
168 - (void)triggerTouchRateCorrectionIfNeeded:(NSSet*)touches;
172 @property(nonatomic, strong)
id mockEngine;
173 @property(nonatomic, strong)
id mockTextInputPlugin;
174 @property(nonatomic, strong)
id messageSent;
175 - (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback;
178 @interface UITouch ()
180 @property(nonatomic, readwrite) UITouchPhase phase;
196 self.messageSent = nil;
202 [
self.mockEngine stopMocking];
203 self.mockEngine = nil;
204 self.mockTextInputPlugin = nil;
205 self.messageSent = nil;
208 - (id)setUpMockScreen {
209 UIScreen* mockScreen = OCMClassMock([UIScreen
class]);
211 CGRect screenBounds = CGRectMake(0, 0, 1170, 2532);
212 OCMStub([mockScreen bounds]).andReturn(screenBounds);
213 CGFloat screenScale = 1;
214 OCMStub([mockScreen scale]).andReturn(screenScale);
220 screen:(UIScreen*)screen
221 viewFrame:(CGRect)viewFrame
222 convertedFrame:(CGRect)convertedFrame {
223 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
224 id mockView = OCMClassMock([UIView
class]);
225 OCMStub([mockView frame]).andReturn(viewFrame);
226 OCMStub([mockView convertRect:viewFrame toCoordinateSpace:[OCMArg any]])
227 .andReturn(convertedFrame);
228 OCMStub([viewControllerMock viewIfLoaded]).andReturn(mockView);
233 - (void)testViewDidLoadWillInvokeCreateTouchRateCorrectionVSyncClient {
240 [viewControllerMock loadView];
241 [viewControllerMock viewDidLoad];
242 OCMVerify([viewControllerMock createTouchRateCorrectionVSyncClientIfNeeded]);
245 - (void)testStartKeyboardAnimationWillInvokeSetupKeyboardSpringAnimationIfNeeded {
252 viewControllerMock.targetViewInsetBottom = 100;
253 [viewControllerMock startKeyBoardAnimation:0.25];
255 CAAnimation* keyboardAnimation =
256 [[viewControllerMock keyboardAnimationView].layer animationForKey:@"position"];
258 OCMVerify([viewControllerMock setUpKeyboardSpringAnimationIfNeeded:keyboardAnimation]);
261 - (void)testSetupKeyboardSpringAnimationIfNeeded {
268 UIScreen* screen = [
self setUpMockScreen];
269 CGRect viewFrame = screen.bounds;
270 [
self setUpMockView:viewControllerMock
273 convertedFrame:viewFrame];
276 [viewControllerMock setUpKeyboardSpringAnimationIfNeeded:nil];
277 SpringAnimation* keyboardSpringAnimation = [viewControllerMock keyboardSpringAnimation];
278 XCTAssertTrue(keyboardSpringAnimation == nil);
281 CABasicAnimation* nonSpringAnimation = [CABasicAnimation animation];
282 nonSpringAnimation.duration = 1.0;
283 nonSpringAnimation.fromValue = [NSNumber numberWithFloat:0.0];
284 nonSpringAnimation.toValue = [NSNumber numberWithFloat:1.0];
285 nonSpringAnimation.keyPath =
@"position";
286 [viewControllerMock setUpKeyboardSpringAnimationIfNeeded:nonSpringAnimation];
287 keyboardSpringAnimation = [viewControllerMock keyboardSpringAnimation];
289 XCTAssertTrue(keyboardSpringAnimation == nil);
292 CASpringAnimation* springAnimation = [CASpringAnimation animation];
293 springAnimation.mass = 1.0;
294 springAnimation.stiffness = 100.0;
295 springAnimation.damping = 10.0;
296 springAnimation.keyPath =
@"position";
297 springAnimation.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, 0)];
298 springAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
299 [viewControllerMock setUpKeyboardSpringAnimationIfNeeded:springAnimation];
300 keyboardSpringAnimation = [viewControllerMock keyboardSpringAnimation];
301 XCTAssertTrue(keyboardSpringAnimation != nil);
304 - (void)testKeyboardAnimationIsShowingAndCompounding {
311 UIScreen* screen = [
self setUpMockScreen];
312 CGRect viewFrame = screen.bounds;
313 [
self setUpMockView:viewControllerMock
316 convertedFrame:viewFrame];
319 CGFloat screenHeight = screen.bounds.size.height;
320 CGFloat screenWidth = screen.bounds.size.height;
323 CGRect initialShowKeyboardBeginFrame = CGRectMake(0, screenHeight, screenWidth, 250);
324 CGRect initialShowKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
325 NSNotification* fakeNotification = [NSNotification
326 notificationWithName:UIKeyboardWillChangeFrameNotification
329 @"UIKeyboardFrameBeginUserInfoKey" : @(initialShowKeyboardBeginFrame),
330 @"UIKeyboardFrameEndUserInfoKey" : @(initialShowKeyboardEndFrame),
331 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
332 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
334 viewControllerMock.targetViewInsetBottom = 0;
335 [viewControllerMock handleKeyboardNotification:fakeNotification];
336 BOOL isShowingAnimation1 = viewControllerMock.keyboardAnimationIsShowing;
337 XCTAssertTrue(isShowingAnimation1);
340 CGRect compoundingShowKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
341 CGRect compoundingShowKeyboardEndFrame = CGRectMake(0, screenHeight - 500, screenWidth, 500);
342 fakeNotification = [NSNotification
343 notificationWithName:UIKeyboardWillChangeFrameNotification
346 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingShowKeyboardBeginFrame),
347 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingShowKeyboardEndFrame),
348 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
349 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
352 [viewControllerMock handleKeyboardNotification:fakeNotification];
353 BOOL isShowingAnimation2 = viewControllerMock.keyboardAnimationIsShowing;
354 XCTAssertTrue(isShowingAnimation2);
355 XCTAssertTrue(isShowingAnimation1 == isShowingAnimation2);
358 CGRect initialHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 500, screenWidth, 250);
359 CGRect initialHideKeyboardEndFrame = CGRectMake(0, screenHeight - 250, screenWidth, 500);
360 fakeNotification = [NSNotification
361 notificationWithName:UIKeyboardWillChangeFrameNotification
364 @"UIKeyboardFrameBeginUserInfoKey" : @(initialHideKeyboardBeginFrame),
365 @"UIKeyboardFrameEndUserInfoKey" : @(initialHideKeyboardEndFrame),
366 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
367 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
370 [viewControllerMock handleKeyboardNotification:fakeNotification];
371 BOOL isShowingAnimation3 = viewControllerMock.keyboardAnimationIsShowing;
372 XCTAssertFalse(isShowingAnimation3);
373 XCTAssertTrue(isShowingAnimation2 != isShowingAnimation3);
376 CGRect compoundingHideKeyboardBeginFrame = CGRectMake(0, screenHeight - 250, screenWidth, 250);
377 CGRect compoundingHideKeyboardEndFrame = CGRectMake(0, screenHeight, screenWidth, 500);
378 fakeNotification = [NSNotification
379 notificationWithName:UIKeyboardWillChangeFrameNotification
382 @"UIKeyboardFrameBeginUserInfoKey" : @(compoundingHideKeyboardBeginFrame),
383 @"UIKeyboardFrameEndUserInfoKey" : @(compoundingHideKeyboardEndFrame),
384 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
385 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
388 [viewControllerMock handleKeyboardNotification:fakeNotification];
389 BOOL isShowingAnimation4 = viewControllerMock.keyboardAnimationIsShowing;
390 XCTAssertFalse(isShowingAnimation4);
391 XCTAssertTrue(isShowingAnimation3 == isShowingAnimation4);
394 - (void)testShouldIgnoreKeyboardNotification {
396 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
401 UIScreen* screen = [
self setUpMockScreen];
402 CGRect viewFrame = screen.bounds;
403 [
self setUpMockView:viewControllerMock
406 convertedFrame:viewFrame];
408 CGFloat screenWidth = screen.bounds.size.width;
409 CGFloat screenHeight = screen.bounds.size.height;
410 CGRect emptyKeyboard = CGRectZero;
411 CGRect zeroHeightKeyboard = CGRectMake(0, 0, screenWidth, 0);
412 CGRect validKeyboardEndFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
416 NSNotification* notification =
417 [NSNotification notificationWithName:UIKeyboardWillHideNotification
420 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
421 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
422 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
425 BOOL shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
426 XCTAssertTrue(shouldIgnore == NO);
430 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
433 @"UIKeyboardFrameEndUserInfoKey" : @(emptyKeyboard),
434 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
435 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
437 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
438 XCTAssertTrue(shouldIgnore == YES);
443 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
446 @"UIKeyboardFrameEndUserInfoKey" : @(zeroHeightKeyboard),
447 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
448 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
450 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
451 XCTAssertTrue(shouldIgnore == NO);
456 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
459 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
460 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
461 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
463 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
464 XCTAssertTrue(shouldIgnore == YES);
469 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
472 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
473 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
474 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
476 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
477 XCTAssertTrue(shouldIgnore == NO);
479 if (@available(iOS 13.0, *)) {
483 OCMStub([viewControllerMock isKeyboardInOrTransitioningFromBackground]).andReturn(YES);
487 [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
490 @"UIKeyboardFrameEndUserInfoKey" : @(validKeyboardEndFrame),
491 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
492 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
494 shouldIgnore = [viewControllerMock shouldIgnoreKeyboardNotification:notification];
495 XCTAssertTrue(shouldIgnore == YES);
498 - (void)testKeyboardAnimationWillNotCrashWhenEngineDestroyed {
504 [viewController setUpKeyboardAnimationVsyncClient:^(fml::TimePoint){
509 - (void)testKeyboardAnimationWillWaitUIThreadVsync {
522 const int delayTime = 1;
523 [engine uiTaskRunner]->PostTask([] { sleep(delayTime); });
524 XCTestExpectation* expectation = [
self expectationWithDescription:@"keyboard animation callback"];
526 __block CFTimeInterval fulfillTime;
528 fulfillTime = CACurrentMediaTime();
529 [expectation fulfill];
531 CFTimeInterval startTime = CACurrentMediaTime();
532 [viewController setUpKeyboardAnimationVsyncClient:callback];
533 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
534 XCTAssertTrue(fulfillTime - startTime > delayTime);
537 - (void)testCalculateKeyboardAttachMode {
539 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
545 UIScreen* screen = [
self setUpMockScreen];
546 CGRect viewFrame = screen.bounds;
547 [
self setUpMockView:viewControllerMock
550 convertedFrame:viewFrame];
552 CGFloat screenWidth = screen.bounds.size.width;
553 CGFloat screenHeight = screen.bounds.size.height;
556 CGRect keyboardFrame = CGRectZero;
557 NSNotification* notification =
558 [NSNotification notificationWithName:UIKeyboardWillHideNotification
561 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
562 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
563 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
565 FlutterKeyboardMode keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
566 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
569 keyboardFrame = CGRectZero;
570 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
573 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
574 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
575 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
577 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
578 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
581 keyboardFrame = CGRectMake(0, 0, screenWidth, 0);
582 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
585 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
586 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
587 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
589 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
590 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
593 keyboardFrame = CGRectMake(0, 0, 320, 320);
594 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
597 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
598 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
599 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
601 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
602 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
605 keyboardFrame = CGRectMake(0, 0, screenWidth, 320);
606 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
609 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
610 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
611 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
613 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
614 XCTAssertTrue(keyboardMode == FlutterKeyboardModeFloating);
617 keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
618 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
621 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
622 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
623 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
625 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
626 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
629 CGFloat longDecimalHeight = 320.666666666666666;
630 keyboardFrame = CGRectMake(0, screenHeight - longDecimalHeight, screenWidth, longDecimalHeight);
631 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
634 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
635 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
636 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
638 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
639 XCTAssertTrue(keyboardMode == FlutterKeyboardModeDocked);
642 keyboardFrame = CGRectMake(0, screenHeight - .0000001, screenWidth, longDecimalHeight);
643 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
646 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
647 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
648 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
650 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
651 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
654 keyboardFrame = CGRectMake(0, screenHeight, screenWidth, 320);
655 notification = [NSNotification notificationWithName:UIKeyboardWillChangeFrameNotification
658 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
659 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
660 @"UIKeyboardIsLocalUserInfoKey" : @(YES)
662 keyboardMode = [viewControllerMock calculateKeyboardAttachMode:notification];
663 XCTAssertTrue(keyboardMode == FlutterKeyboardModeHidden);
666 - (void)testCalculateMultitaskingAdjustment {
668 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
674 UIScreen* screen = [
self setUpMockScreen];
675 CGFloat screenWidth = screen.bounds.size.width;
676 CGFloat screenHeight = screen.bounds.size.height;
677 CGRect screenRect = screen.bounds;
678 CGRect viewOrigFrame = CGRectMake(0, 0, 320, screenHeight - 40);
679 CGRect convertedViewFrame = CGRectMake(20, 20, 320, screenHeight - 40);
680 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
681 id mockView = [
self setUpMockView:viewControllerMock
683 viewFrame:viewOrigFrame
684 convertedFrame:convertedViewFrame];
685 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
686 OCMStub([mockTraitCollection userInterfaceIdiom]).andReturn(UIUserInterfaceIdiomPad);
687 OCMStub([mockTraitCollection horizontalSizeClass]).andReturn(UIUserInterfaceSizeClassCompact);
688 OCMStub([mockTraitCollection verticalSizeClass]).andReturn(UIUserInterfaceSizeClassRegular);
689 OCMStub([mockView traitCollection]).andReturn(mockTraitCollection);
691 CGFloat adjustment = [viewControllerMock calculateMultitaskingAdjustment:screenRect
692 keyboardFrame:keyboardFrame];
693 XCTAssertTrue(adjustment == 20);
696 - (void)testCalculateKeyboardInset {
698 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
703 UIScreen* screen = [
self setUpMockScreen];
704 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
706 CGFloat screenWidth = screen.bounds.size.width;
707 CGFloat screenHeight = screen.bounds.size.height;
708 CGRect viewOrigFrame = CGRectMake(0, 0, 320, screenHeight - 40);
709 CGRect convertedViewFrame = CGRectMake(20, 20, 320, screenHeight - 40);
710 CGRect keyboardFrame = CGRectMake(20, screenHeight - 320, screenWidth, 300);
712 [
self setUpMockView:viewControllerMock
714 viewFrame:viewOrigFrame
715 convertedFrame:convertedViewFrame];
717 CGFloat inset = [viewControllerMock calculateKeyboardInset:keyboardFrame
718 keyboardMode:FlutterKeyboardModeDocked];
719 XCTAssertTrue(inset == 300 * screen.scale);
722 - (void)testHandleKeyboardNotification {
729 UIScreen* screen = [
self setUpMockScreen];
730 CGFloat screenWidth = screen.bounds.size.width;
731 CGFloat screenHeight = screen.bounds.size.height;
732 CGRect keyboardFrame = CGRectMake(0, screenHeight - 320, screenWidth, 320);
733 CGRect viewFrame = screen.bounds;
735 NSNotification* notification =
736 [NSNotification notificationWithName:UIKeyboardWillShowNotification
739 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
740 @"UIKeyboardAnimationDurationUserInfoKey" : @0.25,
741 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
744 [
self setUpMockView:viewControllerMock
747 convertedFrame:viewFrame];
748 viewControllerMock.targetViewInsetBottom = 0;
749 XCTestExpectation* expectation = [
self expectationWithDescription:@"update viewport"];
750 OCMStub([viewControllerMock updateViewportMetricsIfNeeded]).andDo(^(NSInvocation* invocation) {
751 [expectation fulfill];
754 [viewControllerMock handleKeyboardNotification:notification];
755 XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 320 * screen.scale);
756 OCMVerify([viewControllerMock startKeyBoardAnimation:0.25]);
757 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
760 - (void)testEnsureBottomInsetIsZeroWhenKeyboardDismissed {
762 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
768 CGRect keyboardFrame = CGRectZero;
770 NSNotification* fakeNotification =
771 [NSNotification notificationWithName:UIKeyboardWillHideNotification
774 @"UIKeyboardFrameEndUserInfoKey" : @(keyboardFrame),
775 @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25),
776 @"UIKeyboardIsLocalUserInfoKey" : @(isLocal)
779 viewControllerMock.targetViewInsetBottom = 10;
780 [viewControllerMock handleKeyboardNotification:fakeNotification];
781 XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 0);
784 - (void)testEnsureViewportMetricsWillInvokeAndDisplayLinkWillInvalidateInViewDidDisappear {
786 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
791 [viewControllerMock viewDidDisappear:YES];
792 OCMVerify([viewControllerMock ensureViewportMetricsIsCorrect]);
793 OCMVerify([viewControllerMock invalidateKeyboardAnimationVSyncClient]);
796 - (void)testViewDidDisappearDoesntPauseEngineWhenNotTheViewController {
799 mockEngine.lifecycleChannel = lifecycleChannel;
804 id viewControllerMock = OCMPartialMock(viewControllerA);
805 OCMStub([viewControllerMock surfaceUpdated:NO]);
807 [viewControllerA viewDidDisappear:NO];
808 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
809 OCMReject([viewControllerMock surfaceUpdated:[OCMArg any]]);
812 - (void)testAppWillTerminateViewDidDestroyTheEngine {
814 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
819 OCMStub([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
821 [viewController applicationWillTerminate:nil];
822 OCMVerify([viewControllerMock goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
826 - (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController {
829 mockEngine.lifecycleChannel = lifecycleChannel;
837 OCMStub([viewControllerMock surfaceUpdated:NO]);
838 [viewController viewDidDisappear:NO];
839 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.paused"]);
840 OCMVerify([viewControllerMock surfaceUpdated:NO]);
842 XCTAssertNil(weakViewController);
846 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear {
848 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
852 [viewController viewWillAppear:YES];
857 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear {
859 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
869 [viewControllerA viewWillAppear:YES];
870 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
874 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear {
876 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
880 [viewController viewDidAppear:YES];
885 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear {
887 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
897 [viewControllerA viewDidAppear:YES];
898 OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]);
902 testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear {
905 mockEngine.lifecycleChannel = lifecycleChannel;
910 [viewController viewWillDisappear:NO];
911 OCMVerify([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
915 testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear {
918 mockEngine.lifecycleChannel = lifecycleChannel;
926 [viewControllerA viewDidDisappear:NO];
927 OCMReject([lifecycleChannel sendMessage:
@"AppLifecycleState.inactive"]);
930 - (void)testUpdateViewportMetricsIfNeeded_DoesntInvokeEngineWhenNotTheViewController {
932 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
941 [viewControllerA updateViewportMetricsIfNeeded];
942 flutter::ViewportMetrics viewportMetrics;
943 OCMVerify(never(), [
mockEngine updateViewportMetrics:viewportMetrics]);
946 - (void)testUpdateViewportMetricsIfNeeded_DoesInvokeEngineWhenIsTheViewController {
948 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
953 flutter::ViewportMetrics viewportMetrics;
954 OCMExpect([
mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
955 [viewController updateViewportMetricsIfNeeded];
959 - (void)testUpdateViewportMetricsIfNeeded_DoesNotInvokeEngineWhenShouldBeIgnoredDuringRotation {
961 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
966 UIScreen* screen = [
self setUpMockScreen];
967 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
970 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
971 OCMStub([mockCoordinator transitionDuration]).andReturn(0.5);
974 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
976 [viewController updateViewportMetricsIfNeeded];
978 OCMVerify(never(), [
mockEngine updateViewportMetrics:flutter::ViewportMetrics()]);
981 - (void)testViewWillTransitionToSize_DoesDelayEngineCallIfNonZeroDuration {
983 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
988 UIScreen* screen = [
self setUpMockScreen];
989 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
993 NSTimeInterval transitionDuration = 0.5;
994 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
995 OCMStub([mockCoordinator transitionDuration]).andReturn(transitionDuration);
997 flutter::ViewportMetrics viewportMetrics;
998 OCMExpect([
mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1000 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1002 [viewController updateViewportMetricsIfNeeded];
1003 OCMVerify(never(), [
mockEngine updateViewportMetrics:flutter::ViewportMetrics()]);
1007 XCTWaiterResult result = [XCTWaiter
1008 waitForExpectations:@[ [
self expectationWithDescription:@"Waiting for rotation duration"] ]
1009 timeout:transitionDuration];
1010 XCTAssertEqual(result, XCTWaiterResultTimedOut);
1015 - (void)testViewWillTransitionToSize_DoesNotDelayEngineCallIfZeroDuration {
1017 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1022 UIScreen* screen = [
self setUpMockScreen];
1023 OCMStub([viewControllerMock flutterScreenIfViewLoaded]).andReturn(screen);
1027 id mockCoordinator = OCMProtocolMock(
@protocol(UIViewControllerTransitionCoordinator));
1028 OCMStub([mockCoordinator transitionDuration]).andReturn(0);
1030 flutter::ViewportMetrics viewportMetrics;
1031 OCMExpect([
mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs();
1034 [viewController viewWillTransitionToSize:CGSizeZero withTransitionCoordinator:mockCoordinator];
1035 [viewController updateViewportMetricsIfNeeded];
1040 - (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController {
1042 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1051 UIView* view = viewControllerA.view;
1052 XCTAssertNotNil(view);
1056 - (void)testViewDidLoadDoesInvokeEngineWhenIsTheViewController {
1058 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1065 XCTAssertNotNil(view);
1066 OCMVerify(times(1), [
mockEngine attachView]);
1069 - (void)testViewDidLoadDoesntInvokeEngineAttachViewWhenEngineNeedsLaunch {
1071 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1077 [viewController sharedSetupWithProject:nil initialRoute:nil];
1080 XCTAssertNotNil(view);
1084 - (void)testSplashScreenViewRemoveNotCrash {
1089 [flutterViewController setSplashScreenView:[[UIView alloc] init]];
1090 [flutterViewController setSplashScreenView:nil];
1093 - (void)testInternalPluginsWeakPtrNotCrash {
1099 [vc addInternalPlugins];
1102 [(NSArray<id<FlutterKeyPrimaryResponder>>*)keyboardManager.primaryResponders firstObject];
1103 sendEvent = [keyPrimaryResponder sendEvent];
1107 sendEvent({}, nil, nil);
1112 - (void)testInternalPluginsInvokeInViewDidLoad {
1114 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1122 XCTAssertNotNil(view);
1123 [viewController viewDidLoad];
1127 - (void)testBinaryMessenger {
1131 XCTAssertNotNil(vc);
1133 OCMStub([
self.
mockEngine binaryMessenger]).andReturn(messenger);
1135 OCMVerify([
self.
mockEngine binaryMessenger]);
1138 - (void)testViewControllerIsReleased {
1140 __weak UIView* weakView;
1149 [viewController viewDidLoad];
1153 XCTAssertNil(weakViewController);
1154 XCTAssertNil(weakView);
1157 #pragma mark - Platform Brightness
1159 - (void)testItReportsLightPlatformBrightnessByDefault {
1162 OCMStub([
self.
mockEngine settingsChannel]).andReturn(settingsChannel);
1169 [vc traitCollectionDidChange:nil];
1172 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1173 return [message[@"platformBrightness"] isEqualToString:@"light"];
1177 [settingsChannel stopMocking];
1180 - (void)testItReportsPlatformBrightnessWhenViewWillAppear {
1184 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1185 OCMStub([
mockEngine settingsChannel]).andReturn(settingsChannel);
1191 [vc viewWillAppear:false];
1194 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1195 return [message[@"platformBrightness"] isEqualToString:@"light"];
1199 [settingsChannel stopMocking];
1202 - (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt {
1203 if (@available(iOS 13, *)) {
1211 OCMStub([
self.
mockEngine settingsChannel]).andReturn(settingsChannel);
1212 id mockTraitCollection =
1213 [
self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
1222 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1225 [partialMockVC traitCollectionDidChange:nil];
1228 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1229 return [message[@"platformBrightness"] isEqualToString:@"dark"];
1233 [partialMockVC stopMocking];
1234 [settingsChannel stopMocking];
1235 [mockTraitCollection stopMocking];
1240 - (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style {
1241 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1242 OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style);
1243 return mockTraitCollection;
1246 #pragma mark - Platform Contrast
1248 - (void)testItReportsNormalPlatformContrastByDefault {
1249 if (@available(iOS 13, *)) {
1257 OCMStub([
self.
mockEngine settingsChannel]).andReturn(settingsChannel);
1264 [vc traitCollectionDidChange:nil];
1267 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1268 return [message[@"platformContrast"] isEqualToString:@"normal"];
1272 [settingsChannel stopMocking];
1275 - (void)testItReportsPlatformContrastWhenViewWillAppear {
1276 if (@available(iOS 13, *)) {
1282 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1286 OCMStub([
mockEngine settingsChannel]).andReturn(settingsChannel);
1292 [vc viewWillAppear:false];
1295 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1296 return [message[@"platformContrast"] isEqualToString:@"normal"];
1300 [settingsChannel stopMocking];
1303 - (void)testItReportsHighContrastWhenTraitCollectionRequestsIt {
1304 if (@available(iOS 13, *)) {
1312 OCMStub([
self.
mockEngine settingsChannel]).andReturn(settingsChannel);
1314 id mockTraitCollection = [
self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh];
1323 OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection);
1326 [partialMockVC traitCollectionDidChange:mockTraitCollection];
1329 OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1330 return [message[@"platformContrast"] isEqualToString:@"high"];
1334 [partialMockVC stopMocking];
1335 [settingsChannel stopMocking];
1336 [mockTraitCollection stopMocking];
1339 - (void)testItReportsAlwaysUsed24HourFormat {
1342 OCMStub([
self.
mockEngine settingsChannel]).andReturn(settingsChannel);
1348 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(YES);
1349 OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1350 return [message[@"alwaysUse24HourFormat"] isEqual:@(YES)];
1352 [vc onUserSettingsChanged:nil];
1353 [mockHourFormat stopMocking];
1357 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andReturn(NO);
1358 OCMExpect([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(
id message) {
1359 return [message[@"alwaysUse24HourFormat"] isEqual:@(NO)];
1361 [vc onUserSettingsChanged:nil];
1362 [mockHourFormat stopMocking];
1365 [settingsChannel stopMocking];
1368 - (void)testItReportsAccessibilityOnOffSwitchLabelsFlagNotSet {
1369 if (@available(iOS 13, *)) {
1379 OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(NO);
1382 int32_t flags = [partialMockViewController accessibilityFlags];
1385 XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) == 0);
1388 - (void)testItReportsAccessibilityOnOffSwitchLabelsFlagSet {
1389 if (@available(iOS 13, *)) {
1399 OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(YES);
1402 int32_t flags = [partialMockViewController accessibilityFlags];
1405 XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) != 0);
1408 - (void)testAccessibilityPerformEscapePopsRoute {
1410 [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil];
1412 OCMStub([
mockEngine navigationChannel]).andReturn(mockNavigationChannel);
1419 OCMVerify([mockNavigationChannel invokeMethod:
@"popRoute" arguments:nil]);
1421 [mockNavigationChannel stopMocking];
1424 - (void)testPerformOrientationUpdateForcesOrientationChange {
1425 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1426 currentOrientation:UIInterfaceOrientationLandscapeLeft
1427 didChangeOrientation:YES
1428 resultingOrientation:UIInterfaceOrientationPortrait];
1430 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1431 currentOrientation:UIInterfaceOrientationLandscapeRight
1432 didChangeOrientation:YES
1433 resultingOrientation:UIInterfaceOrientationPortrait];
1435 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1436 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1437 didChangeOrientation:YES
1438 resultingOrientation:UIInterfaceOrientationPortrait];
1440 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1441 currentOrientation:UIInterfaceOrientationLandscapeLeft
1442 didChangeOrientation:YES
1443 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1445 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1446 currentOrientation:UIInterfaceOrientationLandscapeRight
1447 didChangeOrientation:YES
1448 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1450 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1451 currentOrientation:UIInterfaceOrientationPortrait
1452 didChangeOrientation:YES
1453 resultingOrientation:UIInterfaceOrientationPortraitUpsideDown];
1455 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1456 currentOrientation:UIInterfaceOrientationPortrait
1457 didChangeOrientation:YES
1458 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1460 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1461 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1462 didChangeOrientation:YES
1463 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1465 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1466 currentOrientation:UIInterfaceOrientationPortrait
1467 didChangeOrientation:YES
1468 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1470 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1471 currentOrientation:UIInterfaceOrientationLandscapeRight
1472 didChangeOrientation:YES
1473 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1475 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1476 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1477 didChangeOrientation:YES
1478 resultingOrientation:UIInterfaceOrientationLandscapeLeft];
1480 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1481 currentOrientation:UIInterfaceOrientationPortrait
1482 didChangeOrientation:YES
1483 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1485 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1486 currentOrientation:UIInterfaceOrientationLandscapeLeft
1487 didChangeOrientation:YES
1488 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1490 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1491 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1492 didChangeOrientation:YES
1493 resultingOrientation:UIInterfaceOrientationLandscapeRight];
1495 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1496 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1497 didChangeOrientation:YES
1498 resultingOrientation:UIInterfaceOrientationPortrait];
1501 - (void)testPerformOrientationUpdateDoesNotForceOrientationChange {
1502 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1503 currentOrientation:UIInterfaceOrientationPortrait
1504 didChangeOrientation:NO
1505 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1507 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1508 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1509 didChangeOrientation:NO
1510 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1512 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1513 currentOrientation:UIInterfaceOrientationLandscapeLeft
1514 didChangeOrientation:NO
1515 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1517 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll
1518 currentOrientation:UIInterfaceOrientationLandscapeRight
1519 didChangeOrientation:NO
1520 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1522 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1523 currentOrientation:UIInterfaceOrientationPortrait
1524 didChangeOrientation:NO
1525 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1527 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1528 currentOrientation:UIInterfaceOrientationLandscapeLeft
1529 didChangeOrientation:NO
1530 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1532 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown
1533 currentOrientation:UIInterfaceOrientationLandscapeRight
1534 didChangeOrientation:NO
1535 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1537 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait
1538 currentOrientation:UIInterfaceOrientationPortrait
1539 didChangeOrientation:NO
1540 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1542 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown
1543 currentOrientation:UIInterfaceOrientationPortraitUpsideDown
1544 didChangeOrientation:NO
1545 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1547 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1548 currentOrientation:UIInterfaceOrientationLandscapeLeft
1549 didChangeOrientation:NO
1550 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1552 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape
1553 currentOrientation:UIInterfaceOrientationLandscapeRight
1554 didChangeOrientation:NO
1555 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1557 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft
1558 currentOrientation:UIInterfaceOrientationLandscapeLeft
1559 didChangeOrientation:NO
1560 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1562 [
self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight
1563 currentOrientation:UIInterfaceOrientationLandscapeRight
1564 didChangeOrientation:NO
1565 resultingOrientation:static_cast<UIInterfaceOrientation>(0)];
1570 - (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask
1571 currentOrientation:(UIInterfaceOrientation)currentOrientation
1572 didChangeOrientation:(BOOL)didChange
1573 resultingOrientation:(UIInterfaceOrientation)resultingOrientation {
1574 id mockApplication = OCMClassMock([UIApplication
class]);
1578 __block __weak
id weakPreferences;
1584 if (@available(iOS 16.0, *)) {
1585 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1586 mockVC = OCMPartialMock(realVC);
1587 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1588 if (realVC.supportedInterfaceOrientations == mask) {
1589 OCMReject([mockWindowScene requestGeometryUpdateWithPreferences:[OCMArg any]
1590 errorHandler:[OCMArg any]]);
1594 OCMExpect([mockWindowScene
1595 requestGeometryUpdateWithPreferences:[OCMArg checkWithBlock:^BOOL(
1596 UIWindowSceneGeometryPreferencesIOS*
1598 weakPreferences = preferences;
1599 return preferences.interfaceOrientations == mask;
1601 errorHandler:[OCMArg any]]);
1603 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
1604 OCMStub([mockApplication connectedScenes]).andReturn([NSSet setWithObject:mockWindowScene]);
1606 deviceMock = OCMPartialMock([UIDevice currentDevice]);
1608 OCMReject([deviceMock setValue:[OCMArg any] forKey:
@"orientation"]);
1610 OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:
@"orientation"]);
1612 if (@available(iOS 13.0, *)) {
1613 mockWindowScene = OCMClassMock([UIWindowScene
class]);
1614 mockVC = OCMPartialMock(realVC);
1615 OCMStub([mockVC flutterWindowSceneIfViewLoaded]).andReturn(mockWindowScene);
1616 OCMStub(((UIWindowScene*)mockWindowScene).interfaceOrientation)
1617 .andReturn(currentOrientation);
1619 OCMStub([mockApplication sharedApplication]).andReturn(mockApplication);
1620 OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation);
1624 [realVC performOrientationUpdate:mask];
1625 if (@available(iOS 16.0, *)) {
1626 OCMVerifyAll(mockWindowScene);
1628 OCMVerifyAll(deviceMock);
1631 [mockWindowScene stopMocking];
1632 [deviceMock stopMocking];
1633 [mockApplication stopMocking];
1634 XCTAssertNil(weakPreferences);
1639 - (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast {
1640 id mockTraitCollection = OCMClassMock([UITraitCollection
class]);
1641 OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast);
1642 return mockTraitCollection;
1645 - (void)testWillDeallocNotification {
1646 XCTestExpectation* expectation =
1647 [[XCTestExpectation alloc] initWithDescription:@"notification called"];
1654 [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc
1656 queue:[NSOperationQueue mainQueue]
1657 usingBlock:^(NSNotification* _Nonnull note) {
1658 [expectation fulfill];
1660 XCTAssertNotNil(realVC);
1663 [
self waitForExpectations:@[ expectation ] timeout:1.0];
1666 - (void)testReleasesKeyboardManagerOnDealloc {
1671 [viewController addInternalPlugins];
1673 XCTAssertNotNil(weakKeyboardManager);
1674 [viewController deregisterNotifications];
1678 XCTAssertNil(weakKeyboardManager);
1681 - (void)testDoesntLoadViewInInit {
1684 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
1688 XCTAssertFalse([realVC isViewLoaded],
@"shouldn't have loaded since it hasn't been shown");
1689 engine.viewController = nil;
1692 - (void)testHideOverlay {
1695 [engine createShell:@"" libraryURI:@"" initialRoute:nil];
1699 XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden,
@"");
1700 [[NSNotificationCenter defaultCenter] postNotificationName:FlutterViewControllerHideHomeIndicator
1702 XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden,
@"");
1703 engine.viewController = nil;
1706 - (void)testNotifyLowMemory {
1712 OCMStub([viewControllerMock surfaceUpdated:NO]);
1713 [viewController beginAppearanceTransition:NO animated:NO];
1714 [viewController endAppearanceTransition];
1715 XCTAssertTrue(
mockEngine.didCallNotifyLowMemory);
1718 - (void)sendMessage:(
id _Nullable)message reply:(
FlutterReply _Nullable)callback {
1719 NSMutableDictionary* replyMessage = [@{
1724 self.messageSent = message;
1725 CFRunLoopPerformBlock(CFRunLoopGetCurrent(), fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode,
1727 callback(replyMessage);
1732 if (@available(iOS 13.4, *)) {
1739 OCMStub([
mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
1740 .andCall(
self,
@selector(sendMessage:reply:));
1742 mockEngine.textInputPlugin =
self.mockTextInputPlugin;
1750 [vc addInternalPlugins];
1752 [vc handlePressEvent:keyUpEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0)
1757 XCTAssert([
self.
messageSent[
@"keymap"] isEqualToString:
@"ios"]);
1758 XCTAssert([
self.
messageSent[
@"type"] isEqualToString:
@"keyup"]);
1759 XCTAssert([
self.
messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
1760 XCTAssert([
self.
messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
1761 XCTAssert([
self.
messageSent[
@"characters"] isEqualToString:
@""]);
1762 XCTAssert([
self.
messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@""]);
1763 [vc deregisterNotifications];
1767 if (@available(iOS 13.4, *)) {
1775 OCMStub([
mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
1776 .andCall(
self,
@selector(sendMessage:reply:));
1778 mockEngine.textInputPlugin =
self.mockTextInputPlugin;
1785 [vc addInternalPlugins];
1787 [vc handlePressEvent:keyDownEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0f, "A",
1793 XCTAssert([
self.
messageSent[
@"keymap"] isEqualToString:
@"ios"]);
1794 XCTAssert([
self.
messageSent[
@"type"] isEqualToString:
@"keydown"]);
1795 XCTAssert([
self.
messageSent[
@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]);
1796 XCTAssert([
self.
messageSent[
@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]);
1797 XCTAssert([
self.
messageSent[
@"characters"] isEqualToString:
@"A"]);
1798 XCTAssert([
self.
messageSent[
@"charactersIgnoringModifiers"] isEqualToString:
@"a"]);
1799 [vc deregisterNotifications];
1804 if (@available(iOS 13.4, *)) {
1810 OCMStub([keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]])
1811 .andCall(
self,
@selector(sendMessage:reply:));
1813 OCMStub([
self.
mockEngine keyEventChannel]).andReturn(keyEventChannel);
1821 [vc addInternalPlugins];
1823 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseStationary, UIKeyboardHIDUsageKeyboardA,
1824 UIKeyModifierShift, 123.0)
1827 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseCancelled, UIKeyboardHIDUsageKeyboardA,
1828 UIKeyModifierShift, 123.0)
1831 [vc handlePressEvent:keyEventWithPhase(UIPressPhaseChanged, UIKeyboardHIDUsageKeyboardA,
1832 UIKeyModifierShift, 123.0)
1837 OCMVerify(never(), [keyEventChannel sendMessage:[OCMArg any]]);
1838 [vc deregisterNotifications];
1842 if (@available(iOS 13.4, *)) {
1851 XCTAssertNotNil(vc);
1852 UIView* view = vc.view;
1853 XCTAssertNotNil(view);
1854 NSArray* gestureRecognizers = view.gestureRecognizers;
1855 XCTAssertNotNil(gestureRecognizers);
1858 for (
id gesture in gestureRecognizers) {
1859 if ([gesture isKindOfClass:[UIPanGestureRecognizer
class]]) {
1864 XCTAssertTrue(found);
1868 if (@available(iOS 13.4, *)) {
1877 XCTAssertNotNil(vc);
1879 id mockPanGestureRecognizer = OCMClassMock([UIPanGestureRecognizer
class]);
1880 XCTAssertNotNil(mockPanGestureRecognizer);
1882 [vc discreteScrollEvent:mockPanGestureRecognizer];
1885 [[mockPanGestureRecognizer verify] locationInView:[OCMArg any]];
1886 [[[
self.mockEngine verify] ignoringNonObjectArgs]
1887 dispatchPointerDataPacket:std::make_unique<flutter::PointerDataPacket>(0)];
1890 - (void)testFakeEventTimeStamp {
1894 XCTAssertNotNil(vc);
1896 flutter::PointerData pointer_data = [vc generatePointerDataForFake];
1897 int64_t current_micros = [[NSProcessInfo processInfo] systemUptime] * 1000 * 1000;
1898 int64_t interval_micros = current_micros - pointer_data.time_stamp;
1899 const int64_t tolerance_millis = 2;
1900 XCTAssertTrue(interval_micros / 1000 < tolerance_millis,
1901 @"PointerData.time_stamp should be equal to NSProcessInfo.systemUptime");
1904 - (void)testSplashScreenViewCanSetNil {
1907 [flutterViewController setSplashScreenView:nil];
1910 - (void)testLifeCycleNotificationBecameActive {
1915 UIWindow* window = [[UIWindow alloc] init];
1916 [window addSubview:flutterViewController.view];
1917 flutterViewController.view.bounds = CGRectMake(0, 0, 100, 100);
1918 [flutterViewController viewDidLayoutSubviews];
1919 NSNotification* sceneNotification =
1920 [NSNotification notificationWithName:UISceneDidActivateNotification object:nil userInfo:nil];
1921 NSNotification* applicationNotification =
1922 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
1925 id mockVC = OCMPartialMock(flutterViewController);
1926 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
1927 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
1928 #if APPLICATION_EXTENSION_API_ONLY
1929 OCMVerify([mockVC sceneBecameActive:[OCMArg any]]);
1930 OCMReject([mockVC applicationBecameActive:[OCMArg any]]);
1932 OCMReject([mockVC sceneBecameActive:[OCMArg any]]);
1933 OCMVerify([mockVC applicationBecameActive:[OCMArg any]]);
1935 XCTAssertFalse(flutterViewController.isKeyboardInOrTransitioningFromBackground);
1936 OCMVerify([mockVC surfaceUpdated:YES]);
1937 XCTestExpectation* timeoutApplicationLifeCycle =
1938 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
1939 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)),
1940 dispatch_get_main_queue(), ^{
1941 [timeoutApplicationLifeCycle fulfill];
1942 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
1943 [flutterViewController deregisterNotifications];
1945 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
1948 - (void)testLifeCycleNotificationWillResignActive {
1953 NSNotification* sceneNotification =
1954 [NSNotification notificationWithName:UISceneWillDeactivateNotification
1957 NSNotification* applicationNotification =
1958 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
1961 id mockVC = OCMPartialMock(flutterViewController);
1962 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
1963 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
1964 #if APPLICATION_EXTENSION_API_ONLY
1965 OCMVerify([mockVC sceneWillResignActive:[OCMArg any]]);
1966 OCMReject([mockVC applicationWillResignActive:[OCMArg any]]);
1968 OCMReject([mockVC sceneWillResignActive:[OCMArg any]]);
1969 OCMVerify([mockVC applicationWillResignActive:[OCMArg any]]);
1971 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
1972 [flutterViewController deregisterNotifications];
1975 - (void)testLifeCycleNotificationWillTerminate {
1980 NSNotification* sceneNotification =
1981 [NSNotification notificationWithName:UISceneDidDisconnectNotification
1984 NSNotification* applicationNotification =
1985 [NSNotification notificationWithName:UIApplicationWillTerminateNotification
1988 id mockVC = OCMPartialMock(flutterViewController);
1991 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
1992 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
1993 #if APPLICATION_EXTENSION_API_ONLY
1994 OCMVerify([mockVC sceneWillDisconnect:[OCMArg any]]);
1995 OCMReject([mockVC applicationWillTerminate:[OCMArg any]]);
1997 OCMReject([mockVC sceneWillDisconnect:[OCMArg any]]);
1998 OCMVerify([mockVC applicationWillTerminate:[OCMArg any]]);
2000 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.detached"]);
2002 [flutterViewController deregisterNotifications];
2005 - (void)testLifeCycleNotificationDidEnterBackground {
2010 NSNotification* sceneNotification =
2011 [NSNotification notificationWithName:UISceneDidEnterBackgroundNotification
2014 NSNotification* applicationNotification =
2015 [NSNotification notificationWithName:UIApplicationDidEnterBackgroundNotification
2018 id mockVC = OCMPartialMock(flutterViewController);
2019 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
2020 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
2021 #if APPLICATION_EXTENSION_API_ONLY
2022 OCMVerify([mockVC sceneDidEnterBackground:[OCMArg any]]);
2023 OCMReject([mockVC applicationDidEnterBackground:[OCMArg any]]);
2025 OCMReject([mockVC sceneDidEnterBackground:[OCMArg any]]);
2026 OCMVerify([mockVC applicationDidEnterBackground:[OCMArg any]]);
2028 XCTAssertTrue(flutterViewController.isKeyboardInOrTransitioningFromBackground);
2029 OCMVerify([mockVC surfaceUpdated:NO]);
2030 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.paused"]);
2031 [flutterViewController deregisterNotifications];
2034 - (void)testLifeCycleNotificationWillEnterForeground {
2039 NSNotification* sceneNotification =
2040 [NSNotification notificationWithName:UISceneWillEnterForegroundNotification
2043 NSNotification* applicationNotification =
2044 [NSNotification notificationWithName:UIApplicationWillEnterForegroundNotification
2047 id mockVC = OCMPartialMock(flutterViewController);
2048 [[NSNotificationCenter defaultCenter] postNotification:sceneNotification];
2049 [[NSNotificationCenter defaultCenter] postNotification:applicationNotification];
2050 #if APPLICATION_EXTENSION_API_ONLY
2051 OCMVerify([mockVC sceneWillEnterForeground:[OCMArg any]]);
2052 OCMReject([mockVC applicationWillEnterForeground:[OCMArg any]]);
2054 OCMReject([mockVC sceneWillEnterForeground:[OCMArg any]]);
2055 OCMVerify([mockVC applicationWillEnterForeground:[OCMArg any]]);
2057 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2058 [flutterViewController deregisterNotifications];
2061 - (void)testLifeCycleNotificationCancelledInvalidResumed {
2066 NSNotification* applicationDidBecomeActiveNotification =
2067 [NSNotification notificationWithName:UIApplicationDidBecomeActiveNotification
2070 NSNotification* applicationWillResignActiveNotification =
2071 [NSNotification notificationWithName:UIApplicationWillResignActiveNotification
2074 id mockVC = OCMPartialMock(flutterViewController);
2075 [[NSNotificationCenter defaultCenter] postNotification:applicationDidBecomeActiveNotification];
2076 [[NSNotificationCenter defaultCenter] postNotification:applicationWillResignActiveNotification];
2077 #if APPLICATION_EXTENSION_API_ONLY
2079 OCMVerify([mockVC goToApplicationLifecycle:
@"AppLifecycleState.inactive"]);
2082 XCTestExpectation* timeoutApplicationLifeCycle =
2083 [
self expectationWithDescription:@"timeoutApplicationLifeCycle"];
2084 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)),
2085 dispatch_get_main_queue(), ^{
2086 OCMReject([mockVC goToApplicationLifecycle:
@"AppLifecycleState.resumed"]);
2087 [timeoutApplicationLifeCycle fulfill];
2088 [flutterViewController deregisterNotifications];
2090 [
self waitForExpectationsWithTimeout:5.0 handler:nil];
2093 - (void)testSetupKeyboardAnimationVsyncClientWillCreateNewVsyncClientForFlutterViewController {
2094 id bundleMock = OCMPartialMock([NSBundle mainBundle]);
2095 OCMStub([bundleMock objectForInfoDictionaryKey:
@"CADisableMinimumFrameDurationOnPhone"])
2098 double maxFrameRate = 120;
2099 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2107 [viewController setUpKeyboardAnimationVsyncClient:callback];
2109 CADisplayLink* link = [viewController.keyboardAnimationVSyncClient getDisplayLink];
2110 XCTAssertNotNil(link);
2111 if (@available(iOS 15.0, *)) {
2112 XCTAssertEqual(link.preferredFrameRateRange.maximum, maxFrameRate);
2113 XCTAssertEqual(link.preferredFrameRateRange.preferred, maxFrameRate);
2114 XCTAssertEqual(link.preferredFrameRateRange.minimum, maxFrameRate / 2);
2116 XCTAssertEqual(link.preferredFramesPerSecond, maxFrameRate);
2121 testCreateTouchRateCorrectionVSyncClientWillCreateVsyncClientWhenRefreshRateIsLargerThan60HZ {
2123 double maxFrameRate = 120;
2124 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2130 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2134 - (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateNewVSyncClientWhenClientAlreadyExists {
2136 double maxFrameRate = 120;
2137 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2144 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2146 XCTAssertNotNil(clientBefore);
2148 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2150 XCTAssertNotNil(clientAfter);
2152 XCTAssertTrue(clientBefore == clientAfter);
2155 - (void)testCreateTouchRateCorrectionVSyncClientWillNotCreateVsyncClientWhenRefreshRateIs60HZ {
2157 double maxFrameRate = 60;
2158 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2164 [viewController createTouchRateCorrectionVSyncClientIfNeeded];
2168 - (void)testTriggerTouchRateCorrectionVSyncClientCorrectly {
2170 double maxFrameRate = 120;
2171 [[[mockDisplayLinkManager stub] andReturnValue:@(maxFrameRate)] displayRefreshRate];
2177 [viewController loadView];
2178 [viewController viewDidLoad];
2181 CADisplayLink* link = [client getDisplayLink];
2183 UITouch* fakeTouchBegan = [[UITouch alloc] init];
2184 fakeTouchBegan.phase = UITouchPhaseBegan;
2186 UITouch* fakeTouchMove = [[UITouch alloc] init];
2187 fakeTouchMove.phase = UITouchPhaseMoved;
2189 UITouch* fakeTouchEnd = [[UITouch alloc] init];
2190 fakeTouchEnd.phase = UITouchPhaseEnded;
2192 UITouch* fakeTouchCancelled = [[UITouch alloc] init];
2193 fakeTouchCancelled.phase = UITouchPhaseCancelled;
2196 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchBegan, nil]];
2197 XCTAssertFalse(link.isPaused);
2200 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd, nil]];
2201 XCTAssertTrue(link.isPaused);
2204 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchMove, nil]];
2205 XCTAssertFalse(link.isPaused);
2208 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchCancelled, nil]];
2209 XCTAssertTrue(link.isPaused);
2212 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2213 initWithObjects:fakeTouchBegan, fakeTouchEnd, nil]];
2214 XCTAssertFalse(link.isPaused);
2217 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc] initWithObjects:fakeTouchEnd,
2218 fakeTouchCancelled, nil]];
2219 XCTAssertTrue(link.isPaused);
2222 triggerTouchRateCorrectionIfNeeded:[[NSSet alloc]
2223 initWithObjects:fakeTouchMove, fakeTouchEnd, nil]];
2224 XCTAssertFalse(link.isPaused);
2227 - (void)testFlutterViewControllerStartKeyboardAnimationWillCreateVsyncClientCorrectly {
2234 [viewController startKeyBoardAnimation:0.25];
2239 testSetupKeyboardAnimationVsyncClientWillNotCreateNewVsyncClientWhenKeyboardAnimationCallbackIsNil {
2245 [viewController setUpKeyboardAnimationVsyncClient:nil];
2249 - (void)testSupportsShowingSystemContextMenuForIOS16AndAbove {
2255 BOOL supportsShowingSystemContextMenu = [viewController supportsShowingSystemContextMenu];
2256 if (@available(iOS 16.0, *)) {
2257 XCTAssertTrue(supportsShowingSystemContextMenu);
2259 XCTAssertFalse(supportsShowingSystemContextMenu);