From 85f5de65fa0d4e43e0468fe1755ca6605389b6ec Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 4 Apr 2022 14:42:04 -0700 Subject: [PATCH 1/7] fix set clipboardData to null --- .../framework/Source/FlutterPlatformPlugin.mm | 6 ++- .../Source/FlutterPlatformPluginTest.mm | 49 +++++++++++++++++-- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm index 257cb4420c776..bf9317828aee1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm @@ -84,6 +84,7 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } else if ([method isEqualToString:@"Clipboard.getData"]) { result([self getClipboardData:args]); } else if ([method isEqualToString:@"Clipboard.setData"]) { + NSLog(@"Clipboard.setData called"); [self setClipboardData:args]; result(nil); } else if ([method isEqualToString:@"Clipboard.hasStrings"]) { @@ -267,8 +268,9 @@ - (NSDictionary*)getClipboardData:(NSString*)format { - (void)setClipboardData:(NSDictionary*)data { UIPasteboard* pasteboard = [UIPasteboard generalPasteboard]; - if (data[@"text"]) { - pasteboard.string = data[@"text"]; + id copyText = data[@"text"]; + if ([copyText isKindOfClass:NSString.class]) { + pasteboard.string = copyText; } else { pasteboard.string = @"null"; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm index 23c7d2f942aee..1bd88ae438693 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm @@ -16,7 +16,8 @@ @interface FlutterPlatformPluginTest : XCTestCase @implementation FlutterPlatformPluginTest -- (void)testHasStrings { +- (void)testClipboardHasCorrectStrings { + [UIPasteboard generalPasteboard].string = nil; FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil]; std::unique_ptr> _weakFactory = std::make_unique>(engine); @@ -29,7 +30,7 @@ - (void)testHasStrings { calledSet = true; }; FlutterMethodCall* methodCallSet = - [FlutterMethodCall methodCallWithMethodName:@"Clipboard.setClipboardData" + [FlutterMethodCall methodCallWithMethodName:@"Clipboard.setData" arguments:@{@"text" : @"some string"}]; [plugin handleMethodCall:methodCallSet result:resultSet]; XCTAssertEqual(calledSet, true); @@ -39,7 +40,7 @@ - (void)testHasStrings { __block bool value; FlutterResult result = ^(id result) { called = true; - value = result[@"value"]; + value = [result[@"value"] boolValue]; }; FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"Clipboard.hasStrings" arguments:nil]; @@ -47,6 +48,48 @@ - (void)testHasStrings { XCTAssertEqual(called, true); XCTAssertEqual(value, true); + + // Call getData and expect it to be "null" + __block NSString* text; + FlutterResult getDataResult = ^(id result) { + text = result[@"text"]; + }; + FlutterMethodCall* methodCallGetData = + [FlutterMethodCall methodCallWithMethodName:@"Clipboard.getData" arguments:@"text/plain"]; + [plugin handleMethodCall:methodCallGetData result:getDataResult]; + + XCTAssertEqualObjects(text, @"some string"); +} + +- (void)testClipboardSetDataToNullDoNotCrash { + [UIPasteboard generalPasteboard].string = nil; + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test" project:nil]; + std::unique_ptr> _weakFactory = + std::make_unique>(engine); + FlutterPlatformPlugin* plugin = + [[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakPtr()]; + + // Set some string to the pasteboard. + __block bool calledSet = false; + FlutterResult resultSet = ^(id result) { + calledSet = true; + }; + FlutterMethodCall* methodCallSet = + [FlutterMethodCall methodCallWithMethodName:@"Clipboard.setData" + arguments:@{@"text" : [NSNull null]}]; + [plugin handleMethodCall:methodCallSet result:resultSet]; + XCTAssertEqual(calledSet, true); + + // Call getData and expect it to be "null" + __block NSString* value; + FlutterResult result = ^(id result) { + value = result[@"text"]; + }; + FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"Clipboard.getData" + arguments:@"text/plain"]; + [plugin handleMethodCall:methodCall result:result]; + + XCTAssertEqualObjects(value, @"null"); } - (void)testPopSystemNavigator { From 2a5e493e3dbe09e8b8e4a4c3c1323d4cb2c78aa4 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 4 Apr 2022 14:43:22 -0700 Subject: [PATCH 2/7] remove test log --- .../darwin/ios/framework/Source/FlutterPlatformPlugin.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm index bf9317828aee1..fa9e6f519e33e 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm @@ -84,7 +84,6 @@ - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { } else if ([method isEqualToString:@"Clipboard.getData"]) { result([self getClipboardData:args]); } else if ([method isEqualToString:@"Clipboard.setData"]) { - NSLog(@"Clipboard.setData called"); [self setClipboardData:args]; result(nil); } else if ([method isEqualToString:@"Clipboard.hasStrings"]) { From ad488ccb84ed077390dcd8194aeb916d44476ab8 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 4 Apr 2022 14:46:45 -0700 Subject: [PATCH 3/7] fix a comment --- .../darwin/ios/framework/Source/FlutterPlatformPluginTest.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm index 1bd88ae438693..d134390674040 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm @@ -49,7 +49,7 @@ - (void)testClipboardHasCorrectStrings { XCTAssertEqual(called, true); XCTAssertEqual(value, true); - // Call getData and expect it to be "null" + // Call getData and expect it to be "some string" __block NSString* text; FlutterResult getDataResult = ^(id result) { text = result[@"text"]; From c81748e27d6369baad83d687499027e3636d8ad8 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 4 Apr 2022 15:22:06 -0700 Subject: [PATCH 4/7] review --- .../framework/Source/FlutterPlatformPlugin.mm | 2 +- .../Source/FlutterPlatformPluginTest.mm | 49 +- .../Source/FlutterViewControllerTest.mm | 2298 ++++++++--------- 3 files changed, 1170 insertions(+), 1179 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm index fa9e6f519e33e..23264b5cf61c6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.mm @@ -268,7 +268,7 @@ - (NSDictionary*)getClipboardData:(NSString*)format { - (void)setClipboardData:(NSDictionary*)data { UIPasteboard* pasteboard = [UIPasteboard generalPasteboard]; id copyText = data[@"text"]; - if ([copyText isKindOfClass:NSString.class]) { + if ([copyText isKindOfClass:[NSString class]]) { pasteboard.string = copyText; } else { pasteboard.string = @"null"; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm index d134390674040..ec9f1f49ffd68 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm @@ -24,41 +24,35 @@ - (void)testClipboardHasCorrectStrings { FlutterPlatformPlugin* plugin = [[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakPtr()]; - // Set some string to the pasteboard. - __block bool calledSet = false; + XCTestExpectation *setStringExpectation = [self expectationWithDescription:@"setString"]; FlutterResult resultSet = ^(id result) { - calledSet = true; + [setStringExpectation fulfill]; }; FlutterMethodCall* methodCallSet = [FlutterMethodCall methodCallWithMethodName:@"Clipboard.setData" arguments:@{@"text" : @"some string"}]; [plugin handleMethodCall:methodCallSet result:resultSet]; - XCTAssertEqual(calledSet, true); + [self waitForExpectationsWithTimeout:1 handler:nil]; - // Call hasStrings and expect it to be true. - __block bool called = false; - __block bool value; + XCTestExpectation *hasStringsExpectation = [self expectationWithDescription:@"hasStrings"]; FlutterResult result = ^(id result) { - called = true; - value = [result[@"value"] boolValue]; + XCTAssertTrue([result[@"value"] boolValue]); + [hasStringsExpectation fulfill]; }; FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"Clipboard.hasStrings" arguments:nil]; [plugin handleMethodCall:methodCall result:result]; + [self waitForExpectationsWithTimeout:1 handler:nil]; - XCTAssertEqual(called, true); - XCTAssertEqual(value, true); - - // Call getData and expect it to be "some string" - __block NSString* text; + XCTestExpectation *getDataExpectation = [self expectationWithDescription:@"getData"]; FlutterResult getDataResult = ^(id result) { - text = result[@"text"]; + XCTAssertEqualObjects(result[@"text"], @"some string"); + [getDataExpectation fulfill]; }; FlutterMethodCall* methodCallGetData = [FlutterMethodCall methodCallWithMethodName:@"Clipboard.getData" arguments:@"text/plain"]; [plugin handleMethodCall:methodCallGetData result:getDataResult]; - - XCTAssertEqualObjects(text, @"some string"); + [self waitForExpectationsWithTimeout:1 handler:nil]; } - (void)testClipboardSetDataToNullDoNotCrash { @@ -69,27 +63,24 @@ - (void)testClipboardSetDataToNullDoNotCrash { FlutterPlatformPlugin* plugin = [[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakPtr()]; - // Set some string to the pasteboard. - __block bool calledSet = false; + XCTestExpectation *setStringExpectation = [self expectationWithDescription:@"setData"]; FlutterResult resultSet = ^(id result) { - calledSet = true; + [setStringExpectation fulfill]; }; FlutterMethodCall* methodCallSet = [FlutterMethodCall methodCallWithMethodName:@"Clipboard.setData" arguments:@{@"text" : [NSNull null]}]; [plugin handleMethodCall:methodCallSet result:resultSet]; - XCTAssertEqual(calledSet, true); - // Call getData and expect it to be "null" - __block NSString* value; + XCTestExpectation *getDataExpectation = [self expectationWithDescription:@"getData"]; FlutterResult result = ^(id result) { - value = result[@"text"]; + XCTAssertEqualObjects(result[@"text"], @"null"); + [getDataExpectation fulfill]; }; FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:@"Clipboard.getData" arguments:@"text/plain"]; [plugin handleMethodCall:methodCall result:result]; - - XCTAssertEqualObjects(value, @"null"); + [self waitForExpectationsWithTimeout:1 handler:nil]; } - (void)testPopSystemNavigator { @@ -109,14 +100,14 @@ - (void)testPopSystemNavigator { id navigationControllerMock = OCMPartialMock(navigationController); OCMStub([navigationControllerMock popViewControllerAnimated:YES]); // Set some string to the pasteboard. - __block bool calledSet = false; + XCTestExpectation *navigationPopCalled = [self expectationWithDescription:@"SystemNavigator.pop"]; FlutterResult resultSet = ^(id result) { - calledSet = true; + [navigationPopCalled fulfill]; }; FlutterMethodCall* methodCallSet = [FlutterMethodCall methodCallWithMethodName:@"SystemNavigator.pop" arguments:@(YES)]; [plugin handleMethodCall:methodCallSet result:resultSet]; - XCTAssertEqual(calledSet, true); + [self waitForExpectationsWithTimeout:1 handler:nil]; OCMVerify([navigationControllerMock popViewControllerAnimated:YES]); } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm index 24d194fa86971..9560b278feb28 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm @@ -1,1149 +1,1149 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import -#import - -#include "flutter/fml/platform/darwin/message_loop_darwin.h" -#import "flutter/lib/ui/window/platform_configuration.h" -#include "flutter/lib/ui/window/pointer_data.h" -#import "flutter/lib/ui/window/viewport_metrics.h" -#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" -#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" -#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" -#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEmbedderKeyResponder.h" -#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterFakeKeyEvents.h" -#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" -#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" -#import "flutter/shell/platform/embedder/embedder.h" - -FLUTTER_ASSERT_ARC - -@interface FlutterEngine () -- (FlutterTextInputPlugin*)textInputPlugin; -- (void)sendKeyEvent:(const FlutterKeyEvent&)event - callback:(nullable FlutterKeyEventCallback)callback - userData:(nullable void*)userData; -@end - -/// Sometimes we have to use a custom mock to avoid retain cycles in OCMock. -/// Used for testing low memory notification. -@interface FlutterEnginePartialMock : FlutterEngine -@property(nonatomic, strong) FlutterBasicMessageChannel* lifecycleChannel; -@property(nonatomic, strong) FlutterBasicMessageChannel* keyEventChannel; -@property(nonatomic, weak) FlutterViewController* viewController; -@property(nonatomic, strong) FlutterTextInputPlugin* textInputPlugin; -@property(nonatomic, assign) BOOL didCallNotifyLowMemory; -- (FlutterTextInputPlugin*)textInputPlugin; -- (void)sendKeyEvent:(const FlutterKeyEvent&)event - callback:(nullable FlutterKeyEventCallback)callback - userData:(nullable void*)userData; -@end - -@implementation FlutterEnginePartialMock -@synthesize viewController; -@synthesize lifecycleChannel; -@synthesize keyEventChannel; -@synthesize textInputPlugin; - -- (void)notifyLowMemory { - _didCallNotifyLowMemory = YES; -} - -- (void)sendKeyEvent:(const FlutterKeyEvent&)event - callback:(FlutterKeyEventCallback)callback - userData:(void*)userData API_AVAILABLE(ios(9.0)) { - if (callback == nil) - return; - // NSAssert(callback != nullptr, @"Invalid callback"); - // Response is async, so we have to post it to the run loop instead of calling - // it directly. - CFRunLoopPerformBlock(CFRunLoopGetCurrent(), fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode, - ^() { - callback(true, userData); - }); -} -@end - -@interface FlutterEngine () -- (BOOL)createShell:(NSString*)entrypoint - libraryURI:(NSString*)libraryURI - initialRoute:(NSString*)initialRoute; -- (void)dispatchPointerDataPacket:(std::unique_ptr)packet; -- (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics; -- (void)attachView; -@end - -@interface FlutterEngine (TestLowMemory) -- (void)notifyLowMemory; -@end - -extern NSNotificationName const FlutterViewControllerWillDealloc; - -/// A simple mock class for FlutterEngine. -/// -/// OCMClassMock can't be used for FlutterEngine sometimes because OCMock retains arguments to -/// invocations and since the init for FlutterViewController calls a method on the -/// FlutterEngine it creates a retain cycle that stops us from testing behaviors related to -/// deleting FlutterViewControllers. -/// -/// Used for testing deallocation. -@interface MockEngine : NSObject -@end - -@implementation MockEngine -- (FlutterViewController*)viewController { - return nil; -} -- (void)setViewController:(FlutterViewController*)viewController { - // noop -} -@end - -// The following conditional compilation defines an API 13 concept on earlier API targets so that -// a compiler compiling against API 12 or below does not blow up due to non-existent members. -#if __IPHONE_OS_VERSION_MAX_ALLOWED < 130000 -typedef enum UIAccessibilityContrast : NSInteger { - UIAccessibilityContrastUnspecified = 0, - UIAccessibilityContrastNormal = 1, - UIAccessibilityContrastHigh = 2 -} UIAccessibilityContrast; - -@interface UITraitCollection (MethodsFromNewerSDK) -- (UIAccessibilityContrast)accessibilityContrast; -@end -#endif - -@interface FlutterKeyboardManager (Tests) -@property(nonatomic, retain, readonly) - NSMutableArray>* primaryResponders; -@end - -@interface FlutterEmbedderKeyResponder (Tests) -@property(nonatomic, copy, readonly) FlutterSendKeyEvent sendEvent; -@end - -@interface FlutterViewController (Tests) - -@property(nonatomic, assign) double targetViewInsetBottom; - -- (void)surfaceUpdated:(BOOL)appeared; -- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences; -- (void)handlePressEvent:(FlutterUIPressProxy*)press - nextAction:(void (^)())next API_AVAILABLE(ios(13.4)); -- (void)scrollEvent:(UIPanGestureRecognizer*)recognizer; -- (void)updateViewportMetrics; -- (void)onUserSettingsChanged:(NSNotification*)notification; -- (void)applicationWillTerminate:(NSNotification*)notification; -- (void)goToApplicationLifecycle:(nonnull NSString*)state; -- (void)keyboardWillChangeFrame:(NSNotification*)notification; -- (void)keyboardWillBeHidden:(NSNotification*)notification; -- (void)startKeyBoardAnimation:(NSTimeInterval)duration; -- (void)ensureViewportMetricsIsCorrect; -- (void)invalidateDisplayLink; -- (void)addInternalPlugins; -- (flutter::PointerData)generatePointerDataForFake; -@end - -@interface FlutterViewControllerTest : XCTestCase -@property(nonatomic, strong) id mockEngine; -@property(nonatomic, strong) id mockTextInputPlugin; -@property(nonatomic, strong) id messageSent; -- (void)sendMessage:(id _Nullable)message reply:(FlutterReply _Nullable)callback; -@end - -@implementation FlutterViewControllerTest - -- (void)setUp { - self.mockEngine = OCMClassMock([FlutterEngine class]); - self.mockTextInputPlugin = OCMClassMock([FlutterTextInputPlugin class]); - OCMStub([self.mockEngine textInputPlugin]).andReturn(self.mockTextInputPlugin); - self.messageSent = nil; -} - -- (void)tearDown { - // We stop mocking here to avoid retain cycles that stop - // FlutterViewControllers from deallocing. - [self.mockEngine stopMocking]; - self.mockEngine = nil; - self.mockTextInputPlugin = nil; - self.messageSent = nil; -} - -- (void)testkeyboardWillChangeFrameWillStartKeyboardAnimation { - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - - CGFloat width = UIScreen.mainScreen.bounds.size.width; - CGRect keyboardFrame = CGRectMake(0, 100, width, 400); - BOOL isLocal = YES; - NSNotification* notification = [NSNotification - notificationWithName:@"" - object:nil - userInfo:@{ - @"UIKeyboardFrameEndUserInfoKey" : [NSValue valueWithCGRect:keyboardFrame], - @"UIKeyboardAnimationDurationUserInfoKey" : [NSNumber numberWithDouble:0.25], - @"UIKeyboardIsLocalUserInfoKey" : [NSNumber numberWithBool:isLocal] - }]; - - XCTestExpectation* expectation = [self expectationWithDescription:@"update viewport"]; - OCMStub([mockEngine updateViewportMetrics:flutter::ViewportMetrics()]) - .ignoringNonObjectArgs() - .andDo(^(NSInvocation* invocation) { - [expectation fulfill]; - }); - id viewControllerMock = OCMPartialMock(viewController); - [viewControllerMock keyboardWillChangeFrame:notification]; - OCMVerify([viewControllerMock startKeyBoardAnimation:0.25]); - [self waitForExpectationsWithTimeout:5.0 handler:nil]; -} - -- (void)testEnsureBottomInsetIsZeroWhenKeyboardDismissed { - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - - FlutterViewController* viewControllerMock = OCMPartialMock(viewController); - CGRect keyboardFrame = CGRectMake(0, 0, 0, 0); - BOOL isLocal = YES; - NSNotification* fakeNotification = [NSNotification - notificationWithName:@"" - object:nil - userInfo:@{ - @"UIKeyboardFrameEndUserInfoKey" : [NSValue valueWithCGRect:keyboardFrame], - @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25), - @"UIKeyboardIsLocalUserInfoKey" : @(isLocal) - }]; - - viewControllerMock.targetViewInsetBottom = 10; - [viewControllerMock keyboardWillBeHidden:fakeNotification]; - XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 0); -} - -- (void)testEnsureViewportMetricsWillInvokeAndDisplayLinkWillInvalidateInViewDidDisappear { - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - id viewControllerMock = OCMPartialMock(viewController); - [viewControllerMock viewDidDisappear:YES]; - OCMVerify([viewControllerMock ensureViewportMetricsIsCorrect]); - OCMVerify([viewControllerMock invalidateDisplayLink]); -} - -- (void)testViewDidDisappearDoesntPauseEngineWhenNotTheViewController { - id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); - FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; - mockEngine.lifecycleChannel = lifecycleChannel; - FlutterViewController* viewControllerA = - [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; - FlutterViewController* viewControllerB = - [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; - id viewControllerMock = OCMPartialMock(viewControllerA); - OCMStub([viewControllerMock surfaceUpdated:NO]); - mockEngine.viewController = viewControllerB; - [viewControllerA viewDidDisappear:NO]; - OCMReject([lifecycleChannel sendMessage:@"AppLifecycleState.paused"]); - OCMReject([viewControllerMock surfaceUpdated:[OCMArg any]]); -} - -- (void)testAppWillTerminateViewDidDestroyTheEngine { - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - id viewControllerMock = OCMPartialMock(viewController); - OCMStub([viewControllerMock goToApplicationLifecycle:@"AppLifecycleState.detached"]); - OCMStub([mockEngine destroyContext]); - [viewController applicationWillTerminate:nil]; - OCMVerify([viewControllerMock goToApplicationLifecycle:@"AppLifecycleState.detached"]); - OCMVerify([mockEngine destroyContext]); -} - -- (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController { - id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); - FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; - mockEngine.lifecycleChannel = lifecycleChannel; - __weak FlutterViewController* weakViewController; - @autoreleasepool { - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - weakViewController = viewController; - id viewControllerMock = OCMPartialMock(viewController); - OCMStub([viewControllerMock surfaceUpdated:NO]); - [viewController viewDidDisappear:NO]; - OCMVerify([lifecycleChannel sendMessage:@"AppLifecycleState.paused"]); - OCMVerify([viewControllerMock surfaceUpdated:NO]); - } - XCTAssertNil(weakViewController); -} - -- (void) - testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear { - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - [viewController viewWillAppear:YES]; - OCMVerify([viewController onUserSettingsChanged:nil]); -} - -- (void) - testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear { - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - mockEngine.viewController = nil; - FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - mockEngine.viewController = nil; - mockEngine.viewController = viewControllerB; - [viewControllerA viewWillAppear:YES]; - OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]); -} - -- (void) - testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear { - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - [viewController viewDidAppear:YES]; - OCMVerify([viewController onUserSettingsChanged:nil]); -} - -- (void) - testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear { - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - mockEngine.viewController = nil; - FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - mockEngine.viewController = nil; - mockEngine.viewController = viewControllerB; - [viewControllerA viewDidAppear:YES]; - OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]); -} - -- (void) - testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear { - id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); - FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; - mockEngine.lifecycleChannel = lifecycleChannel; - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - mockEngine.viewController = viewController; - [viewController viewWillDisappear:NO]; - OCMVerify([lifecycleChannel sendMessage:@"AppLifecycleState.inactive"]); -} - -- (void) - testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear { - id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); - FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; - mockEngine.lifecycleChannel = lifecycleChannel; - FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - mockEngine.viewController = viewControllerB; - [viewControllerA viewDidDisappear:NO]; - OCMReject([lifecycleChannel sendMessage:@"AppLifecycleState.inactive"]); -} - -- (void)testUpdateViewportMetricsDoesntInvokeEngineWhenNotTheViewController { - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - mockEngine.viewController = nil; - FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - mockEngine.viewController = viewControllerB; - [viewControllerA updateViewportMetrics]; - flutter::ViewportMetrics viewportMetrics; - OCMVerify(never(), [mockEngine updateViewportMetrics:viewportMetrics]); -} - -- (void)testUpdateViewportMetricsDoesInvokeEngineWhenIsTheViewController { - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - mockEngine.viewController = viewController; - flutter::ViewportMetrics viewportMetrics; - OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs(); - [viewController updateViewportMetrics]; - OCMVerifyAll(mockEngine); -} - -- (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController { - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - mockEngine.viewController = nil; - FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - mockEngine.viewController = viewControllerB; - UIView* view = viewControllerA.view; - XCTAssertNotNil(view); - OCMVerify(never(), [mockEngine attachView]); -} - -- (void)testViewDidLoadDoesInvokeEngineWhenIsTheViewController { - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - mockEngine.viewController = nil; - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - mockEngine.viewController = viewController; - UIView* view = viewController.view; - XCTAssertNotNil(view); - OCMVerify([mockEngine attachView]); -} - -- (void)testInternalPluginsWeakPtrNotCrash { - FlutterSendKeyEvent sendEvent; - @autoreleasepool { - FlutterViewController* vc = [[FlutterViewController alloc] initWithProject:nil - nibName:nil - bundle:nil]; - [vc addInternalPlugins]; - FlutterKeyboardManager* keyboardManager = vc.keyboardManager; - FlutterEmbedderKeyResponder* keyPrimaryResponder = (FlutterEmbedderKeyResponder*) - [(NSArray>*)keyboardManager.primaryResponders firstObject]; - sendEvent = [keyPrimaryResponder sendEvent]; - } - - if (sendEvent) { - sendEvent({}, nil, nil); - } -} - -// Regression test for https://github.com/flutter/engine/pull/32098. -- (void)testInternalPluginsInvokeInViewDidLoad { - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - [viewController viewDidLoad]; - OCMVerify([viewController addInternalPlugins]); -} - -- (void)testBinaryMessenger { - FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine - nibName:nil - bundle:nil]; - XCTAssertNotNil(vc); - id messenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); - OCMStub([self.mockEngine binaryMessenger]).andReturn(messenger); - XCTAssertEqual(vc.binaryMessenger, messenger); - OCMVerify([self.mockEngine binaryMessenger]); -} - -#pragma mark - Platform Brightness - -- (void)testItReportsLightPlatformBrightnessByDefault { - // Setup test. - id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); - OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); - - FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine - nibName:nil - bundle:nil]; - - // Exercise behavior under test. - [vc traitCollectionDidChange:nil]; - - // Verify behavior. - OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { - return [message[@"platformBrightness"] isEqualToString:@"light"]; - }]]); - - // Clean up mocks - [settingsChannel stopMocking]; -} - -- (void)testItReportsPlatformBrightnessWhenViewWillAppear { - // Setup test. - id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel); - FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - - // Exercise behavior under test. - [vc viewWillAppear:false]; - - // Verify behavior. - OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { - return [message[@"platformBrightness"] isEqualToString:@"light"]; - }]]); - - // Clean up mocks - [settingsChannel stopMocking]; -} - -- (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt { - if (@available(iOS 13, *)) { - // noop - } else { - return; - } - - // Setup test. - id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); - OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); - - FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine - nibName:nil - bundle:nil]; - id mockTraitCollection = - [self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]; - - // We partially mock the real FlutterViewController to act as the OS and report - // the UITraitCollection of our choice. Mocking the object under test is not - // desirable, but given that the OS does not offer a DI approach to providing - // our own UITraitCollection, this seems to be the least bad option. - id partialMockVC = OCMPartialMock(realVC); - OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection); - - // Exercise behavior under test. - [partialMockVC traitCollectionDidChange:nil]; - - // Verify behavior. - OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { - return [message[@"platformBrightness"] isEqualToString:@"dark"]; - }]]); - - // Clean up mocks - [partialMockVC stopMocking]; - [settingsChannel stopMocking]; - [mockTraitCollection stopMocking]; -} - -// Creates a mocked UITraitCollection with nil values for everything except userInterfaceStyle, -// which is set to the given "style". -- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style { - id mockTraitCollection = OCMClassMock([UITraitCollection class]); - OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style); - return mockTraitCollection; -} - -#pragma mark - Platform Contrast - -- (void)testItReportsNormalPlatformContrastByDefault { - if (@available(iOS 13, *)) { - // noop - } else { - return; - } - - // Setup test. - id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); - OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); - - FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine - nibName:nil - bundle:nil]; - - // Exercise behavior under test. - [vc traitCollectionDidChange:nil]; - - // Verify behavior. - OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { - return [message[@"platformContrast"] isEqualToString:@"normal"]; - }]]); - - // Clean up mocks - [settingsChannel stopMocking]; -} - -- (void)testItReportsPlatformContrastWhenViewWillAppear { - if (@available(iOS 13, *)) { - // noop - } else { - return; - } - FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); - [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - - // Setup test. - id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); - OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel); - FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - - // Exercise behavior under test. - [vc viewWillAppear:false]; - - // Verify behavior. - OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { - return [message[@"platformContrast"] isEqualToString:@"normal"]; - }]]); - - // Clean up mocks - [settingsChannel stopMocking]; -} - -- (void)testItReportsHighContrastWhenTraitCollectionRequestsIt { - if (@available(iOS 13, *)) { - // noop - } else { - return; - } - - // Setup test. - id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); - OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); - - FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine - nibName:nil - bundle:nil]; - id mockTraitCollection = [self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh]; - - // We partially mock the real FlutterViewController to act as the OS and report - // the UITraitCollection of our choice. Mocking the object under test is not - // desirable, but given that the OS does not offer a DI approach to providing - // our own UITraitCollection, this seems to be the least bad option. - id partialMockVC = OCMPartialMock(realVC); - OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection); - - // Exercise behavior under test. - [partialMockVC traitCollectionDidChange:mockTraitCollection]; - - // Verify behavior. - OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { - return [message[@"platformContrast"] isEqualToString:@"high"]; - }]]); - - // Clean up mocks - [partialMockVC stopMocking]; - [settingsChannel stopMocking]; - [mockTraitCollection stopMocking]; -} - -- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagNotSet { - if (@available(iOS 13, *)) { - // noop - } else { - return; - } - - // Setup test. - FlutterViewController* viewController = - [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; - id partialMockViewController = OCMPartialMock(viewController); - OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(NO); - - // Exercise behavior under test. - int32_t flags = [partialMockViewController accessibilityFlags]; - - // Verify behavior. - XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) == 0); -} - -- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagSet { - if (@available(iOS 13, *)) { - // noop - } else { - return; - } - - // Setup test. - FlutterViewController* viewController = - [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; - id partialMockViewController = OCMPartialMock(viewController); - OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(YES); - - // Exercise behavior under test. - int32_t flags = [partialMockViewController accessibilityFlags]; - - // Verify behavior. - XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) != 0); -} - -- (void)testPerformOrientationUpdateForcesOrientationChange { - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait - currentOrientation:UIInterfaceOrientationLandscapeLeft - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationPortrait]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait - currentOrientation:UIInterfaceOrientationLandscapeRight - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationPortrait]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait - currentOrientation:UIInterfaceOrientationPortraitUpsideDown - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationPortrait]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown - currentOrientation:UIInterfaceOrientationLandscapeLeft - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown - currentOrientation:UIInterfaceOrientationLandscapeRight - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown - currentOrientation:UIInterfaceOrientationPortrait - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape - currentOrientation:UIInterfaceOrientationPortrait - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationLandscapeLeft]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape - currentOrientation:UIInterfaceOrientationPortraitUpsideDown - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationLandscapeLeft]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft - currentOrientation:UIInterfaceOrientationPortrait - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationLandscapeLeft]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft - currentOrientation:UIInterfaceOrientationLandscapeRight - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationLandscapeLeft]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft - currentOrientation:UIInterfaceOrientationPortraitUpsideDown - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationLandscapeLeft]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight - currentOrientation:UIInterfaceOrientationPortrait - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationLandscapeRight]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight - currentOrientation:UIInterfaceOrientationLandscapeLeft - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationLandscapeRight]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight - currentOrientation:UIInterfaceOrientationPortraitUpsideDown - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationLandscapeRight]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown - currentOrientation:UIInterfaceOrientationPortraitUpsideDown - didChangeOrientation:YES - resultingOrientation:UIInterfaceOrientationPortrait]; -} - -- (void)testPerformOrientationUpdateDoesNotForceOrientationChange { - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll - currentOrientation:UIInterfaceOrientationPortrait - didChangeOrientation:NO - resultingOrientation:static_cast(0)]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll - currentOrientation:UIInterfaceOrientationPortraitUpsideDown - didChangeOrientation:NO - resultingOrientation:static_cast(0)]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll - currentOrientation:UIInterfaceOrientationLandscapeLeft - didChangeOrientation:NO - resultingOrientation:static_cast(0)]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll - currentOrientation:UIInterfaceOrientationLandscapeRight - didChangeOrientation:NO - resultingOrientation:static_cast(0)]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown - currentOrientation:UIInterfaceOrientationPortrait - didChangeOrientation:NO - resultingOrientation:static_cast(0)]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown - currentOrientation:UIInterfaceOrientationLandscapeLeft - didChangeOrientation:NO - resultingOrientation:static_cast(0)]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown - currentOrientation:UIInterfaceOrientationLandscapeRight - didChangeOrientation:NO - resultingOrientation:static_cast(0)]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait - currentOrientation:UIInterfaceOrientationPortrait - didChangeOrientation:NO - resultingOrientation:static_cast(0)]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown - currentOrientation:UIInterfaceOrientationPortraitUpsideDown - didChangeOrientation:NO - resultingOrientation:static_cast(0)]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape - currentOrientation:UIInterfaceOrientationLandscapeLeft - didChangeOrientation:NO - resultingOrientation:static_cast(0)]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape - currentOrientation:UIInterfaceOrientationLandscapeRight - didChangeOrientation:NO - resultingOrientation:static_cast(0)]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft - currentOrientation:UIInterfaceOrientationLandscapeLeft - didChangeOrientation:NO - resultingOrientation:static_cast(0)]; - - [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight - currentOrientation:UIInterfaceOrientationLandscapeRight - didChangeOrientation:NO - resultingOrientation:static_cast(0)]; -} - -// Perform an orientation update test that fails when the expected outcome -// for an orientation update is not met -- (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask - currentOrientation:(UIInterfaceOrientation)currentOrientation - didChangeOrientation:(BOOL)didChange - resultingOrientation:(UIInterfaceOrientation)resultingOrientation { - id deviceMock = OCMPartialMock([UIDevice currentDevice]); - if (!didChange) { - OCMReject([deviceMock setValue:[OCMArg any] forKey:@"orientation"]); - } else { - OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]); - } - - FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine - nibName:nil - bundle:nil]; - id mockApplication = OCMClassMock([UIApplication class]); - OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); - OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation); - - [realVC performOrientationUpdate:mask]; - OCMVerifyAll(deviceMock); - [deviceMock stopMocking]; - [mockApplication stopMocking]; -} - -// Creates a mocked UITraitCollection with nil values for everything except accessibilityContrast, -// which is set to the given "contrast". -- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast { - id mockTraitCollection = OCMClassMock([UITraitCollection class]); - OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast); - return mockTraitCollection; -} - -- (void)testWillDeallocNotification { - XCTestExpectation* expectation = - [[XCTestExpectation alloc] initWithDescription:@"notification called"]; - id engine = [[MockEngine alloc] init]; - @autoreleasepool { - FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; - [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification* _Nonnull note) { - [expectation fulfill]; - }]; - realVC = nil; - } - [self waitForExpectations:@[ expectation ] timeout:1.0]; -} - -- (void)testReleasesKeyboardManagerOnDealloc { - __weak FlutterKeyboardManager* weakKeyboardManager = nil; - @autoreleasepool { - FlutterViewController* viewController = [[FlutterViewController alloc] init]; - - [viewController addInternalPlugins]; - weakKeyboardManager = viewController.keyboardManager; - XCTAssertNotNil(weakKeyboardManager); - [viewController deregisterNotifications]; - viewController = nil; - } - // View controller has released the keyboard manager. - XCTAssertNil(weakKeyboardManager); -} - -- (void)testDoesntLoadViewInInit { - FlutterDartProject* project = [[FlutterDartProject alloc] init]; - FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; - [engine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; - XCTAssertFalse([realVC isViewLoaded], @"shouldn't have loaded since it hasn't been shown"); - engine.viewController = nil; -} - -- (void)testHideOverlay { - FlutterDartProject* project = [[FlutterDartProject alloc] init]; - FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; - [engine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; - XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden, @""); - [[NSNotificationCenter defaultCenter] postNotificationName:FlutterViewControllerHideHomeIndicator - object:nil]; - XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden, @""); - engine.viewController = nil; -} - -- (void)testHideA11yElements { - FlutterDartProject* project = [[FlutterDartProject alloc] init]; - FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; - [engine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine - nibName:nil - bundle:nil]; - XCTAssertFalse(realVC.view.accessibilityElementsHidden); - [[NSNotificationCenter defaultCenter] - postNotificationName:UIApplicationWillResignActiveNotification - object:nil]; - XCTAssertTrue(realVC.view.accessibilityElementsHidden); - [[NSNotificationCenter defaultCenter] - postNotificationName:UIApplicationDidBecomeActiveNotification - object:nil]; - XCTAssertFalse(realVC.view.accessibilityElementsHidden); - engine.viewController = nil; -} - -- (void)testNotifyLowMemory { - FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; - FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - id viewControllerMock = OCMPartialMock(viewController); - OCMStub([viewControllerMock surfaceUpdated:NO]); - [viewController beginAppearanceTransition:NO animated:NO]; - [viewController endAppearanceTransition]; - XCTAssertTrue(mockEngine.didCallNotifyLowMemory); -} - -- (void)sendMessage:(id _Nullable)message reply:(FlutterReply _Nullable)callback { - NSMutableDictionary* replyMessage = [@{ - @"handled" : @YES, - } mutableCopy]; - // Response is async, so we have to post it to the run loop instead of calling - // it directly. - self.messageSent = message; - CFRunLoopPerformBlock(CFRunLoopGetCurrent(), fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode, - ^() { - callback(replyMessage); - }); -} - -- (void)testValidKeyUpEvent API_AVAILABLE(ios(13.4)) { - if (@available(iOS 13.4, *)) { - // noop - } else { - return; - } - FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; - mockEngine.keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); - OCMStub([mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) - .andCall(self, @selector(sendMessage:reply:)); - OCMStub([self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES); - mockEngine.textInputPlugin = self.mockTextInputPlugin; - - FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - - // Allocate the keyboard manager in the view controller by adding the internal - // plugins. - [vc addInternalPlugins]; - - [vc handlePressEvent:keyUpEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0) - nextAction:^(){ - }]; - - XCTAssert(self.messageSent != nil); - XCTAssert([self.messageSent[@"keymap"] isEqualToString:@"ios"]); - XCTAssert([self.messageSent[@"type"] isEqualToString:@"keyup"]); - XCTAssert([self.messageSent[@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]); - XCTAssert([self.messageSent[@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]); - XCTAssert([self.messageSent[@"characters"] isEqualToString:@""]); - XCTAssert([self.messageSent[@"charactersIgnoringModifiers"] isEqualToString:@""]); - [vc deregisterNotifications]; -} - -- (void)testValidKeyDownEvent API_AVAILABLE(ios(13.4)) { - if (@available(iOS 13.4, *)) { - // noop - } else { - return; - } - - FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; - mockEngine.keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); - OCMStub([mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) - .andCall(self, @selector(sendMessage:reply:)); - OCMStub([self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES); - mockEngine.textInputPlugin = self.mockTextInputPlugin; - - __strong FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine - nibName:nil - bundle:nil]; - // Allocate the keyboard manager in the view controller by adding the internal - // plugins. - [vc addInternalPlugins]; - - [vc handlePressEvent:keyDownEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0f, "A", - "a") - nextAction:^(){ - }]; - - XCTAssert(self.messageSent != nil); - XCTAssert([self.messageSent[@"keymap"] isEqualToString:@"ios"]); - XCTAssert([self.messageSent[@"type"] isEqualToString:@"keydown"]); - XCTAssert([self.messageSent[@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]); - XCTAssert([self.messageSent[@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]); - XCTAssert([self.messageSent[@"characters"] isEqualToString:@"A"]); - XCTAssert([self.messageSent[@"charactersIgnoringModifiers"] isEqualToString:@"a"]); - [vc deregisterNotifications]; - vc = nil; -} - -- (void)testIgnoredKeyEvents API_AVAILABLE(ios(13.4)) { - if (@available(iOS 13.4, *)) { - // noop - } else { - return; - } - id keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); - OCMStub([keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) - .andCall(self, @selector(sendMessage:reply:)); - OCMStub([self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES); - OCMStub([self.mockEngine keyEventChannel]).andReturn(keyEventChannel); - - FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine - nibName:nil - bundle:nil]; - - // Allocate the keyboard manager in the view controller by adding the internal - // plugins. - [vc addInternalPlugins]; - - [vc handlePressEvent:keyEventWithPhase(UIPressPhaseStationary, UIKeyboardHIDUsageKeyboardA, - UIKeyModifierShift, 123.0) - nextAction:^(){ - }]; - [vc handlePressEvent:keyEventWithPhase(UIPressPhaseCancelled, UIKeyboardHIDUsageKeyboardA, - UIKeyModifierShift, 123.0) - nextAction:^(){ - }]; - [vc handlePressEvent:keyEventWithPhase(UIPressPhaseChanged, UIKeyboardHIDUsageKeyboardA, - UIKeyModifierShift, 123.0) - nextAction:^(){ - }]; - - XCTAssert(self.messageSent == nil); - OCMVerify(never(), [keyEventChannel sendMessage:[OCMArg any]]); - [vc deregisterNotifications]; -} - -- (void)testPanGestureRecognizer API_AVAILABLE(ios(13.4)) { - if (@available(iOS 13.4, *)) { - // noop - } else { - return; - } - - FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine - nibName:nil - bundle:nil]; - XCTAssertNotNil(vc); - UIView* view = vc.view; - XCTAssertNotNil(view); - NSArray* gestureRecognizers = view.gestureRecognizers; - XCTAssertNotNil(gestureRecognizers); - - BOOL found = NO; - for (id gesture in gestureRecognizers) { - if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) { - found = YES; - break; - } - } - XCTAssertTrue(found); -} - -- (void)testMouseSupport API_AVAILABLE(ios(13.4)) { - if (@available(iOS 13.4, *)) { - // noop - } else { - return; - } - - FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine - nibName:nil - bundle:nil]; - XCTAssertNotNil(vc); - - id mockPanGestureRecognizer = OCMClassMock([UIPanGestureRecognizer class]); - XCTAssertNotNil(mockPanGestureRecognizer); - - [vc scrollEvent:mockPanGestureRecognizer]; - - [[[self.mockEngine verify] ignoringNonObjectArgs] - dispatchPointerDataPacket:std::make_unique(0)]; -} - -- (void)testFakeEventTimeStamp { - FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine - nibName:nil - bundle:nil]; - XCTAssertNotNil(vc); - - flutter::PointerData pointer_data = [vc generatePointerDataForFake]; - int64_t current_micros = [[NSProcessInfo processInfo] systemUptime] * 1000 * 1000; - int64_t interval_micros = current_micros - pointer_data.time_stamp; - const int64_t tolerance_millis = 2; - XCTAssertTrue(interval_micros / 1000 < tolerance_millis, - @"PointerData.time_stamp should be equal to NSProcessInfo.systemUptime"); -} -@end +//// Copyright 2013 The Flutter Authors. All rights reserved. +//// Use of this source code is governed by a BSD-style license that can be +//// found in the LICENSE file. +// +//#import +//#import +// +//#include "flutter/fml/platform/darwin/message_loop_darwin.h" +//#import "flutter/lib/ui/window/platform_configuration.h" +//#include "flutter/lib/ui/window/pointer_data.h" +//#import "flutter/lib/ui/window/viewport_metrics.h" +//#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" +//#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" +//#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +//#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEmbedderKeyResponder.h" +//#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterFakeKeyEvents.h" +//#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" +//#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" +//#import "flutter/shell/platform/embedder/embedder.h" +// +//FLUTTER_ASSERT_ARC +// +//@interface FlutterEngine () +//- (FlutterTextInputPlugin*)textInputPlugin; +//- (void)sendKeyEvent:(const FlutterKeyEvent&)event +// callback:(nullable FlutterKeyEventCallback)callback +// userData:(nullable void*)userData; +//@end +// +///// Sometimes we have to use a custom mock to avoid retain cycles in OCMock. +///// Used for testing low memory notification. +//@interface FlutterEnginePartialMock : FlutterEngine +//@property(nonatomic, strong) FlutterBasicMessageChannel* lifecycleChannel; +//@property(nonatomic, strong) FlutterBasicMessageChannel* keyEventChannel; +//@property(nonatomic, weak) FlutterViewController* viewController; +//@property(nonatomic, strong) FlutterTextInputPlugin* textInputPlugin; +//@property(nonatomic, assign) BOOL didCallNotifyLowMemory; +//- (FlutterTextInputPlugin*)textInputPlugin; +//- (void)sendKeyEvent:(const FlutterKeyEvent&)event +// callback:(nullable FlutterKeyEventCallback)callback +// userData:(nullable void*)userData; +//@end +// +//@implementation FlutterEnginePartialMock +//@synthesize viewController; +//@synthesize lifecycleChannel; +//@synthesize keyEventChannel; +//@synthesize textInputPlugin; +// +//- (void)notifyLowMemory { +// _didCallNotifyLowMemory = YES; +//} +// +//- (void)sendKeyEvent:(const FlutterKeyEvent&)event +// callback:(FlutterKeyEventCallback)callback +// userData:(void*)userData API_AVAILABLE(ios(9.0)) { +// if (callback == nil) +// return; +// // NSAssert(callback != nullptr, @"Invalid callback"); +// // Response is async, so we have to post it to the run loop instead of calling +// // it directly. +// CFRunLoopPerformBlock(CFRunLoopGetCurrent(), fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode, +// ^() { +// callback(true, userData); +// }); +//} +//@end +// +//@interface FlutterEngine () +//- (BOOL)createShell:(NSString*)entrypoint +// libraryURI:(NSString*)libraryURI +// initialRoute:(NSString*)initialRoute; +//- (void)dispatchPointerDataPacket:(std::unique_ptr)packet; +//- (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics; +//- (void)attachView; +//@end +// +//@interface FlutterEngine (TestLowMemory) +//- (void)notifyLowMemory; +//@end +// +//extern NSNotificationName const FlutterViewControllerWillDealloc; +// +///// A simple mock class for FlutterEngine. +///// +///// OCMClassMock can't be used for FlutterEngine sometimes because OCMock retains arguments to +///// invocations and since the init for FlutterViewController calls a method on the +///// FlutterEngine it creates a retain cycle that stops us from testing behaviors related to +///// deleting FlutterViewControllers. +///// +///// Used for testing deallocation. +//@interface MockEngine : NSObject +//@end +// +//@implementation MockEngine +//- (FlutterViewController*)viewController { +// return nil; +//} +//- (void)setViewController:(FlutterViewController*)viewController { +// // noop +//} +//@end +// +//// The following conditional compilation defines an API 13 concept on earlier API targets so that +//// a compiler compiling against API 12 or below does not blow up due to non-existent members. +//#if __IPHONE_OS_VERSION_MAX_ALLOWED < 130000 +//typedef enum UIAccessibilityContrast : NSInteger { +// UIAccessibilityContrastUnspecified = 0, +// UIAccessibilityContrastNormal = 1, +// UIAccessibilityContrastHigh = 2 +//} UIAccessibilityContrast; +// +//@interface UITraitCollection (MethodsFromNewerSDK) +//- (UIAccessibilityContrast)accessibilityContrast; +//@end +//#endif +// +//@interface FlutterKeyboardManager (Tests) +//@property(nonatomic, retain, readonly) +// NSMutableArray>* primaryResponders; +//@end +// +//@interface FlutterEmbedderKeyResponder (Tests) +//@property(nonatomic, copy, readonly) FlutterSendKeyEvent sendEvent; +//@end +// +//@interface FlutterViewController (Tests) +// +//@property(nonatomic, assign) double targetViewInsetBottom; +// +//- (void)surfaceUpdated:(BOOL)appeared; +//- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences; +//- (void)handlePressEvent:(FlutterUIPressProxy*)press +// nextAction:(void (^)())next API_AVAILABLE(ios(13.4)); +//- (void)scrollEvent:(UIPanGestureRecognizer*)recognizer; +//- (void)updateViewportMetrics; +//- (void)onUserSettingsChanged:(NSNotification*)notification; +//- (void)applicationWillTerminate:(NSNotification*)notification; +//- (void)goToApplicationLifecycle:(nonnull NSString*)state; +//- (void)keyboardWillChangeFrame:(NSNotification*)notification; +//- (void)keyboardWillBeHidden:(NSNotification*)notification; +//- (void)startKeyBoardAnimation:(NSTimeInterval)duration; +//- (void)ensureViewportMetricsIsCorrect; +//- (void)invalidateDisplayLink; +//- (void)addInternalPlugins; +//- (flutter::PointerData)generatePointerDataForFake; +//@end +// +//@interface FlutterViewControllerTest : XCTestCase +//@property(nonatomic, strong) id mockEngine; +//@property(nonatomic, strong) id mockTextInputPlugin; +//@property(nonatomic, strong) id messageSent; +//- (void)sendMessage:(id _Nullable)message reply:(FlutterReply _Nullable)callback; +//@end +// +//@implementation FlutterViewControllerTest +// +//- (void)setUp { +// self.mockEngine = OCMClassMock([FlutterEngine class]); +// self.mockTextInputPlugin = OCMClassMock([FlutterTextInputPlugin class]); +// OCMStub([self.mockEngine textInputPlugin]).andReturn(self.mockTextInputPlugin); +// self.messageSent = nil; +//} +// +//- (void)tearDown { +// // We stop mocking here to avoid retain cycles that stop +// // FlutterViewControllers from deallocing. +// [self.mockEngine stopMocking]; +// self.mockEngine = nil; +// self.mockTextInputPlugin = nil; +// self.messageSent = nil; +//} +// +//- (void)testkeyboardWillChangeFrameWillStartKeyboardAnimation { +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// +// CGFloat width = UIScreen.mainScreen.bounds.size.width; +// CGRect keyboardFrame = CGRectMake(0, 100, width, 400); +// BOOL isLocal = YES; +// NSNotification* notification = [NSNotification +// notificationWithName:@"" +// object:nil +// userInfo:@{ +// @"UIKeyboardFrameEndUserInfoKey" : [NSValue valueWithCGRect:keyboardFrame], +// @"UIKeyboardAnimationDurationUserInfoKey" : [NSNumber numberWithDouble:0.25], +// @"UIKeyboardIsLocalUserInfoKey" : [NSNumber numberWithBool:isLocal] +// }]; +// +// XCTestExpectation* expectation = [self expectationWithDescription:@"update viewport"]; +// OCMStub([mockEngine updateViewportMetrics:flutter::ViewportMetrics()]) +// .ignoringNonObjectArgs() +// .andDo(^(NSInvocation* invocation) { +// [expectation fulfill]; +// }); +// id viewControllerMock = OCMPartialMock(viewController); +// [viewControllerMock keyboardWillChangeFrame:notification]; +// OCMVerify([viewControllerMock startKeyBoardAnimation:0.25]); +// [self waitForExpectationsWithTimeout:5.0 handler:nil]; +//} +// +//- (void)testEnsureBottomInsetIsZeroWhenKeyboardDismissed { +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// +// FlutterViewController* viewControllerMock = OCMPartialMock(viewController); +// CGRect keyboardFrame = CGRectMake(0, 0, 0, 0); +// BOOL isLocal = YES; +// NSNotification* fakeNotification = [NSNotification +// notificationWithName:@"" +// object:nil +// userInfo:@{ +// @"UIKeyboardFrameEndUserInfoKey" : [NSValue valueWithCGRect:keyboardFrame], +// @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25), +// @"UIKeyboardIsLocalUserInfoKey" : @(isLocal) +// }]; +// +// viewControllerMock.targetViewInsetBottom = 10; +// [viewControllerMock keyboardWillBeHidden:fakeNotification]; +// XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 0); +//} +// +//- (void)testEnsureViewportMetricsWillInvokeAndDisplayLinkWillInvalidateInViewDidDisappear { +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// id viewControllerMock = OCMPartialMock(viewController); +// [viewControllerMock viewDidDisappear:YES]; +// OCMVerify([viewControllerMock ensureViewportMetricsIsCorrect]); +// OCMVerify([viewControllerMock invalidateDisplayLink]); +//} +// +//- (void)testViewDidDisappearDoesntPauseEngineWhenNotTheViewController { +// id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); +// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; +// mockEngine.lifecycleChannel = lifecycleChannel; +// FlutterViewController* viewControllerA = +// [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; +// FlutterViewController* viewControllerB = +// [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; +// id viewControllerMock = OCMPartialMock(viewControllerA); +// OCMStub([viewControllerMock surfaceUpdated:NO]); +// mockEngine.viewController = viewControllerB; +// [viewControllerA viewDidDisappear:NO]; +// OCMReject([lifecycleChannel sendMessage:@"AppLifecycleState.paused"]); +// OCMReject([viewControllerMock surfaceUpdated:[OCMArg any]]); +//} +// +//- (void)testAppWillTerminateViewDidDestroyTheEngine { +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// id viewControllerMock = OCMPartialMock(viewController); +// OCMStub([viewControllerMock goToApplicationLifecycle:@"AppLifecycleState.detached"]); +// OCMStub([mockEngine destroyContext]); +// [viewController applicationWillTerminate:nil]; +// OCMVerify([viewControllerMock goToApplicationLifecycle:@"AppLifecycleState.detached"]); +// OCMVerify([mockEngine destroyContext]); +//} +// +//- (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController { +// id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); +// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; +// mockEngine.lifecycleChannel = lifecycleChannel; +// __weak FlutterViewController* weakViewController; +// @autoreleasepool { +// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// weakViewController = viewController; +// id viewControllerMock = OCMPartialMock(viewController); +// OCMStub([viewControllerMock surfaceUpdated:NO]); +// [viewController viewDidDisappear:NO]; +// OCMVerify([lifecycleChannel sendMessage:@"AppLifecycleState.paused"]); +// OCMVerify([viewControllerMock surfaceUpdated:NO]); +// } +// XCTAssertNil(weakViewController); +//} +// +//- (void) +// testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear { +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// [viewController viewWillAppear:YES]; +// OCMVerify([viewController onUserSettingsChanged:nil]); +//} +// +//- (void) +// testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear { +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// mockEngine.viewController = nil; +// FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// mockEngine.viewController = nil; +// mockEngine.viewController = viewControllerB; +// [viewControllerA viewWillAppear:YES]; +// OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]); +//} +// +//- (void) +// testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear { +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// [viewController viewDidAppear:YES]; +// OCMVerify([viewController onUserSettingsChanged:nil]); +//} +// +//- (void) +// testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear { +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// mockEngine.viewController = nil; +// FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// mockEngine.viewController = nil; +// mockEngine.viewController = viewControllerB; +// [viewControllerA viewDidAppear:YES]; +// OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]); +//} +// +//- (void) +// testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear { +// id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); +// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; +// mockEngine.lifecycleChannel = lifecycleChannel; +// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// mockEngine.viewController = viewController; +// [viewController viewWillDisappear:NO]; +// OCMVerify([lifecycleChannel sendMessage:@"AppLifecycleState.inactive"]); +//} +// +//- (void) +// testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear { +// id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); +// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; +// mockEngine.lifecycleChannel = lifecycleChannel; +// FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// mockEngine.viewController = viewControllerB; +// [viewControllerA viewDidDisappear:NO]; +// OCMReject([lifecycleChannel sendMessage:@"AppLifecycleState.inactive"]); +//} +// +//- (void)testUpdateViewportMetricsDoesntInvokeEngineWhenNotTheViewController { +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// mockEngine.viewController = nil; +// FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// mockEngine.viewController = viewControllerB; +// [viewControllerA updateViewportMetrics]; +// flutter::ViewportMetrics viewportMetrics; +// OCMVerify(never(), [mockEngine updateViewportMetrics:viewportMetrics]); +//} +// +//- (void)testUpdateViewportMetricsDoesInvokeEngineWhenIsTheViewController { +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// mockEngine.viewController = viewController; +// flutter::ViewportMetrics viewportMetrics; +// OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs(); +// [viewController updateViewportMetrics]; +// OCMVerifyAll(mockEngine); +//} +// +//- (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController { +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// mockEngine.viewController = nil; +// FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// mockEngine.viewController = viewControllerB; +// UIView* view = viewControllerA.view; +// XCTAssertNotNil(view); +// OCMVerify(never(), [mockEngine attachView]); +//} +// +//- (void)testViewDidLoadDoesInvokeEngineWhenIsTheViewController { +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// mockEngine.viewController = nil; +// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// mockEngine.viewController = viewController; +// UIView* view = viewController.view; +// XCTAssertNotNil(view); +// OCMVerify([mockEngine attachView]); +//} +// +//- (void)testInternalPluginsWeakPtrNotCrash { +// FlutterSendKeyEvent sendEvent; +// @autoreleasepool { +// FlutterViewController* vc = [[FlutterViewController alloc] initWithProject:nil +// nibName:nil +// bundle:nil]; +// [vc addInternalPlugins]; +// FlutterKeyboardManager* keyboardManager = vc.keyboardManager; +// FlutterEmbedderKeyResponder* keyPrimaryResponder = (FlutterEmbedderKeyResponder*) +// [(NSArray>*)keyboardManager.primaryResponders firstObject]; +// sendEvent = [keyPrimaryResponder sendEvent]; +// } +// +// if (sendEvent) { +// sendEvent({}, nil, nil); +// } +//} +// +//// Regression test for https://github.com/flutter/engine/pull/32098. +//- (void)testInternalPluginsInvokeInViewDidLoad { +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// [viewController viewDidLoad]; +// OCMVerify([viewController addInternalPlugins]); +//} +// +//- (void)testBinaryMessenger { +// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine +// nibName:nil +// bundle:nil]; +// XCTAssertNotNil(vc); +// id messenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); +// OCMStub([self.mockEngine binaryMessenger]).andReturn(messenger); +// XCTAssertEqual(vc.binaryMessenger, messenger); +// OCMVerify([self.mockEngine binaryMessenger]); +//} +// +//#pragma mark - Platform Brightness +// +//- (void)testItReportsLightPlatformBrightnessByDefault { +// // Setup test. +// id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); +// OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); +// +// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine +// nibName:nil +// bundle:nil]; +// +// // Exercise behavior under test. +// [vc traitCollectionDidChange:nil]; +// +// // Verify behavior. +// OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { +// return [message[@"platformBrightness"] isEqualToString:@"light"]; +// }]]); +// +// // Clean up mocks +// [settingsChannel stopMocking]; +//} +// +//- (void)testItReportsPlatformBrightnessWhenViewWillAppear { +// // Setup test. +// id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel); +// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// +// // Exercise behavior under test. +// [vc viewWillAppear:false]; +// +// // Verify behavior. +// OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { +// return [message[@"platformBrightness"] isEqualToString:@"light"]; +// }]]); +// +// // Clean up mocks +// [settingsChannel stopMocking]; +//} +// +//- (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt { +// if (@available(iOS 13, *)) { +// // noop +// } else { +// return; +// } +// +// // Setup test. +// id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); +// OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); +// +// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine +// nibName:nil +// bundle:nil]; +// id mockTraitCollection = +// [self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]; +// +// // We partially mock the real FlutterViewController to act as the OS and report +// // the UITraitCollection of our choice. Mocking the object under test is not +// // desirable, but given that the OS does not offer a DI approach to providing +// // our own UITraitCollection, this seems to be the least bad option. +// id partialMockVC = OCMPartialMock(realVC); +// OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection); +// +// // Exercise behavior under test. +// [partialMockVC traitCollectionDidChange:nil]; +// +// // Verify behavior. +// OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { +// return [message[@"platformBrightness"] isEqualToString:@"dark"]; +// }]]); +// +// // Clean up mocks +// [partialMockVC stopMocking]; +// [settingsChannel stopMocking]; +// [mockTraitCollection stopMocking]; +//} +// +//// Creates a mocked UITraitCollection with nil values for everything except userInterfaceStyle, +//// which is set to the given "style". +//- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style { +// id mockTraitCollection = OCMClassMock([UITraitCollection class]); +// OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style); +// return mockTraitCollection; +//} +// +//#pragma mark - Platform Contrast +// +//- (void)testItReportsNormalPlatformContrastByDefault { +// if (@available(iOS 13, *)) { +// // noop +// } else { +// return; +// } +// +// // Setup test. +// id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); +// OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); +// +// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine +// nibName:nil +// bundle:nil]; +// +// // Exercise behavior under test. +// [vc traitCollectionDidChange:nil]; +// +// // Verify behavior. +// OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { +// return [message[@"platformContrast"] isEqualToString:@"normal"]; +// }]]); +// +// // Clean up mocks +// [settingsChannel stopMocking]; +//} +// +//- (void)testItReportsPlatformContrastWhenViewWillAppear { +// if (@available(iOS 13, *)) { +// // noop +// } else { +// return; +// } +// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); +// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; +// +// // Setup test. +// id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); +// OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel); +// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// +// // Exercise behavior under test. +// [vc viewWillAppear:false]; +// +// // Verify behavior. +// OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { +// return [message[@"platformContrast"] isEqualToString:@"normal"]; +// }]]); +// +// // Clean up mocks +// [settingsChannel stopMocking]; +//} +// +//- (void)testItReportsHighContrastWhenTraitCollectionRequestsIt { +// if (@available(iOS 13, *)) { +// // noop +// } else { +// return; +// } +// +// // Setup test. +// id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); +// OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); +// +// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine +// nibName:nil +// bundle:nil]; +// id mockTraitCollection = [self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh]; +// +// // We partially mock the real FlutterViewController to act as the OS and report +// // the UITraitCollection of our choice. Mocking the object under test is not +// // desirable, but given that the OS does not offer a DI approach to providing +// // our own UITraitCollection, this seems to be the least bad option. +// id partialMockVC = OCMPartialMock(realVC); +// OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection); +// +// // Exercise behavior under test. +// [partialMockVC traitCollectionDidChange:mockTraitCollection]; +// +// // Verify behavior. +// OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { +// return [message[@"platformContrast"] isEqualToString:@"high"]; +// }]]); +// +// // Clean up mocks +// [partialMockVC stopMocking]; +// [settingsChannel stopMocking]; +// [mockTraitCollection stopMocking]; +//} +// +//- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagNotSet { +// if (@available(iOS 13, *)) { +// // noop +// } else { +// return; +// } +// +// // Setup test. +// FlutterViewController* viewController = +// [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; +// id partialMockViewController = OCMPartialMock(viewController); +// OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(NO); +// +// // Exercise behavior under test. +// int32_t flags = [partialMockViewController accessibilityFlags]; +// +// // Verify behavior. +// XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) == 0); +//} +// +//- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagSet { +// if (@available(iOS 13, *)) { +// // noop +// } else { +// return; +// } +// +// // Setup test. +// FlutterViewController* viewController = +// [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; +// id partialMockViewController = OCMPartialMock(viewController); +// OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(YES); +// +// // Exercise behavior under test. +// int32_t flags = [partialMockViewController accessibilityFlags]; +// +// // Verify behavior. +// XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) != 0); +//} +// +//- (void)testPerformOrientationUpdateForcesOrientationChange { +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait +// currentOrientation:UIInterfaceOrientationLandscapeLeft +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationPortrait]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait +// currentOrientation:UIInterfaceOrientationLandscapeRight +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationPortrait]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait +// currentOrientation:UIInterfaceOrientationPortraitUpsideDown +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationPortrait]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown +// currentOrientation:UIInterfaceOrientationLandscapeLeft +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown +// currentOrientation:UIInterfaceOrientationLandscapeRight +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown +// currentOrientation:UIInterfaceOrientationPortrait +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape +// currentOrientation:UIInterfaceOrientationPortrait +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationLandscapeLeft]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape +// currentOrientation:UIInterfaceOrientationPortraitUpsideDown +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationLandscapeLeft]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft +// currentOrientation:UIInterfaceOrientationPortrait +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationLandscapeLeft]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft +// currentOrientation:UIInterfaceOrientationLandscapeRight +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationLandscapeLeft]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft +// currentOrientation:UIInterfaceOrientationPortraitUpsideDown +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationLandscapeLeft]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight +// currentOrientation:UIInterfaceOrientationPortrait +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationLandscapeRight]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight +// currentOrientation:UIInterfaceOrientationLandscapeLeft +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationLandscapeRight]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight +// currentOrientation:UIInterfaceOrientationPortraitUpsideDown +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationLandscapeRight]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown +// currentOrientation:UIInterfaceOrientationPortraitUpsideDown +// didChangeOrientation:YES +// resultingOrientation:UIInterfaceOrientationPortrait]; +//} +// +//- (void)testPerformOrientationUpdateDoesNotForceOrientationChange { +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll +// currentOrientation:UIInterfaceOrientationPortrait +// didChangeOrientation:NO +// resultingOrientation:static_cast(0)]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll +// currentOrientation:UIInterfaceOrientationPortraitUpsideDown +// didChangeOrientation:NO +// resultingOrientation:static_cast(0)]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll +// currentOrientation:UIInterfaceOrientationLandscapeLeft +// didChangeOrientation:NO +// resultingOrientation:static_cast(0)]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll +// currentOrientation:UIInterfaceOrientationLandscapeRight +// didChangeOrientation:NO +// resultingOrientation:static_cast(0)]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown +// currentOrientation:UIInterfaceOrientationPortrait +// didChangeOrientation:NO +// resultingOrientation:static_cast(0)]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown +// currentOrientation:UIInterfaceOrientationLandscapeLeft +// didChangeOrientation:NO +// resultingOrientation:static_cast(0)]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown +// currentOrientation:UIInterfaceOrientationLandscapeRight +// didChangeOrientation:NO +// resultingOrientation:static_cast(0)]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait +// currentOrientation:UIInterfaceOrientationPortrait +// didChangeOrientation:NO +// resultingOrientation:static_cast(0)]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown +// currentOrientation:UIInterfaceOrientationPortraitUpsideDown +// didChangeOrientation:NO +// resultingOrientation:static_cast(0)]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape +// currentOrientation:UIInterfaceOrientationLandscapeLeft +// didChangeOrientation:NO +// resultingOrientation:static_cast(0)]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape +// currentOrientation:UIInterfaceOrientationLandscapeRight +// didChangeOrientation:NO +// resultingOrientation:static_cast(0)]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft +// currentOrientation:UIInterfaceOrientationLandscapeLeft +// didChangeOrientation:NO +// resultingOrientation:static_cast(0)]; +// +// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight +// currentOrientation:UIInterfaceOrientationLandscapeRight +// didChangeOrientation:NO +// resultingOrientation:static_cast(0)]; +//} +// +//// Perform an orientation update test that fails when the expected outcome +//// for an orientation update is not met +//- (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask +// currentOrientation:(UIInterfaceOrientation)currentOrientation +// didChangeOrientation:(BOOL)didChange +// resultingOrientation:(UIInterfaceOrientation)resultingOrientation { +// id deviceMock = OCMPartialMock([UIDevice currentDevice]); +// if (!didChange) { +// OCMReject([deviceMock setValue:[OCMArg any] forKey:@"orientation"]); +// } else { +// OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]); +// } +// +// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine +// nibName:nil +// bundle:nil]; +// id mockApplication = OCMClassMock([UIApplication class]); +// OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); +// OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation); +// +// [realVC performOrientationUpdate:mask]; +// OCMVerifyAll(deviceMock); +// [deviceMock stopMocking]; +// [mockApplication stopMocking]; +//} +// +//// Creates a mocked UITraitCollection with nil values for everything except accessibilityContrast, +//// which is set to the given "contrast". +//- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast { +// id mockTraitCollection = OCMClassMock([UITraitCollection class]); +// OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast); +// return mockTraitCollection; +//} +// +//- (void)testWillDeallocNotification { +// XCTestExpectation* expectation = +// [[XCTestExpectation alloc] initWithDescription:@"notification called"]; +// id engine = [[MockEngine alloc] init]; +// @autoreleasepool { +// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine +// nibName:nil +// bundle:nil]; +// [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc +// object:nil +// queue:[NSOperationQueue mainQueue] +// usingBlock:^(NSNotification* _Nonnull note) { +// [expectation fulfill]; +// }]; +// realVC = nil; +// } +// [self waitForExpectations:@[ expectation ] timeout:1.0]; +//} +// +//- (void)testReleasesKeyboardManagerOnDealloc { +// __weak FlutterKeyboardManager* weakKeyboardManager = nil; +// @autoreleasepool { +// FlutterViewController* viewController = [[FlutterViewController alloc] init]; +// +// [viewController addInternalPlugins]; +// weakKeyboardManager = viewController.keyboardManager; +// XCTAssertNotNil(weakKeyboardManager); +// [viewController deregisterNotifications]; +// viewController = nil; +// } +// // View controller has released the keyboard manager. +// XCTAssertNil(weakKeyboardManager); +//} +// +//- (void)testDoesntLoadViewInInit { +// FlutterDartProject* project = [[FlutterDartProject alloc] init]; +// FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; +// [engine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine +// nibName:nil +// bundle:nil]; +// XCTAssertFalse([realVC isViewLoaded], @"shouldn't have loaded since it hasn't been shown"); +// engine.viewController = nil; +//} +// +//- (void)testHideOverlay { +// FlutterDartProject* project = [[FlutterDartProject alloc] init]; +// FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; +// [engine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine +// nibName:nil +// bundle:nil]; +// XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden, @""); +// [[NSNotificationCenter defaultCenter] postNotificationName:FlutterViewControllerHideHomeIndicator +// object:nil]; +// XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden, @""); +// engine.viewController = nil; +//} +// +//- (void)testHideA11yElements { +// FlutterDartProject* project = [[FlutterDartProject alloc] init]; +// FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; +// [engine createShell:@"" libraryURI:@"" initialRoute:nil]; +// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine +// nibName:nil +// bundle:nil]; +// XCTAssertFalse(realVC.view.accessibilityElementsHidden); +// [[NSNotificationCenter defaultCenter] +// postNotificationName:UIApplicationWillResignActiveNotification +// object:nil]; +// XCTAssertTrue(realVC.view.accessibilityElementsHidden); +// [[NSNotificationCenter defaultCenter] +// postNotificationName:UIApplicationDidBecomeActiveNotification +// object:nil]; +// XCTAssertFalse(realVC.view.accessibilityElementsHidden); +// engine.viewController = nil; +//} +// +//- (void)testNotifyLowMemory { +// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; +// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// id viewControllerMock = OCMPartialMock(viewController); +// OCMStub([viewControllerMock surfaceUpdated:NO]); +// [viewController beginAppearanceTransition:NO animated:NO]; +// [viewController endAppearanceTransition]; +// XCTAssertTrue(mockEngine.didCallNotifyLowMemory); +//} +// +//- (void)sendMessage:(id _Nullable)message reply:(FlutterReply _Nullable)callback { +// NSMutableDictionary* replyMessage = [@{ +// @"handled" : @YES, +// } mutableCopy]; +// // Response is async, so we have to post it to the run loop instead of calling +// // it directly. +// self.messageSent = message; +// CFRunLoopPerformBlock(CFRunLoopGetCurrent(), fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode, +// ^() { +// callback(replyMessage); +// }); +//} +// +//- (void)testValidKeyUpEvent API_AVAILABLE(ios(13.4)) { +// if (@available(iOS 13.4, *)) { +// // noop +// } else { +// return; +// } +// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; +// mockEngine.keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); +// OCMStub([mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) +// .andCall(self, @selector(sendMessage:reply:)); +// OCMStub([self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES); +// mockEngine.textInputPlugin = self.mockTextInputPlugin; +// +// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// +// // Allocate the keyboard manager in the view controller by adding the internal +// // plugins. +// [vc addInternalPlugins]; +// +// [vc handlePressEvent:keyUpEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0) +// nextAction:^(){ +// }]; +// +// XCTAssert(self.messageSent != nil); +// XCTAssert([self.messageSent[@"keymap"] isEqualToString:@"ios"]); +// XCTAssert([self.messageSent[@"type"] isEqualToString:@"keyup"]); +// XCTAssert([self.messageSent[@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]); +// XCTAssert([self.messageSent[@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]); +// XCTAssert([self.messageSent[@"characters"] isEqualToString:@""]); +// XCTAssert([self.messageSent[@"charactersIgnoringModifiers"] isEqualToString:@""]); +// [vc deregisterNotifications]; +//} +// +//- (void)testValidKeyDownEvent API_AVAILABLE(ios(13.4)) { +// if (@available(iOS 13.4, *)) { +// // noop +// } else { +// return; +// } +// +// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; +// mockEngine.keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); +// OCMStub([mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) +// .andCall(self, @selector(sendMessage:reply:)); +// OCMStub([self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES); +// mockEngine.textInputPlugin = self.mockTextInputPlugin; +// +// __strong FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine +// nibName:nil +// bundle:nil]; +// // Allocate the keyboard manager in the view controller by adding the internal +// // plugins. +// [vc addInternalPlugins]; +// +// [vc handlePressEvent:keyDownEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0f, "A", +// "a") +// nextAction:^(){ +// }]; +// +// XCTAssert(self.messageSent != nil); +// XCTAssert([self.messageSent[@"keymap"] isEqualToString:@"ios"]); +// XCTAssert([self.messageSent[@"type"] isEqualToString:@"keydown"]); +// XCTAssert([self.messageSent[@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]); +// XCTAssert([self.messageSent[@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]); +// XCTAssert([self.messageSent[@"characters"] isEqualToString:@"A"]); +// XCTAssert([self.messageSent[@"charactersIgnoringModifiers"] isEqualToString:@"a"]); +// [vc deregisterNotifications]; +// vc = nil; +//} +// +//- (void)testIgnoredKeyEvents API_AVAILABLE(ios(13.4)) { +// if (@available(iOS 13.4, *)) { +// // noop +// } else { +// return; +// } +// id keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); +// OCMStub([keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) +// .andCall(self, @selector(sendMessage:reply:)); +// OCMStub([self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES); +// OCMStub([self.mockEngine keyEventChannel]).andReturn(keyEventChannel); +// +// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine +// nibName:nil +// bundle:nil]; +// +// // Allocate the keyboard manager in the view controller by adding the internal +// // plugins. +// [vc addInternalPlugins]; +// +// [vc handlePressEvent:keyEventWithPhase(UIPressPhaseStationary, UIKeyboardHIDUsageKeyboardA, +// UIKeyModifierShift, 123.0) +// nextAction:^(){ +// }]; +// [vc handlePressEvent:keyEventWithPhase(UIPressPhaseCancelled, UIKeyboardHIDUsageKeyboardA, +// UIKeyModifierShift, 123.0) +// nextAction:^(){ +// }]; +// [vc handlePressEvent:keyEventWithPhase(UIPressPhaseChanged, UIKeyboardHIDUsageKeyboardA, +// UIKeyModifierShift, 123.0) +// nextAction:^(){ +// }]; +// +// XCTAssert(self.messageSent == nil); +// OCMVerify(never(), [keyEventChannel sendMessage:[OCMArg any]]); +// [vc deregisterNotifications]; +//} +// +//- (void)testPanGestureRecognizer API_AVAILABLE(ios(13.4)) { +// if (@available(iOS 13.4, *)) { +// // noop +// } else { +// return; +// } +// +// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine +// nibName:nil +// bundle:nil]; +// XCTAssertNotNil(vc); +// UIView* view = vc.view; +// XCTAssertNotNil(view); +// NSArray* gestureRecognizers = view.gestureRecognizers; +// XCTAssertNotNil(gestureRecognizers); +// +// BOOL found = NO; +// for (id gesture in gestureRecognizers) { +// if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) { +// found = YES; +// break; +// } +// } +// XCTAssertTrue(found); +//} +// +//- (void)testMouseSupport API_AVAILABLE(ios(13.4)) { +// if (@available(iOS 13.4, *)) { +// // noop +// } else { +// return; +// } +// +// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine +// nibName:nil +// bundle:nil]; +// XCTAssertNotNil(vc); +// +// id mockPanGestureRecognizer = OCMClassMock([UIPanGestureRecognizer class]); +// XCTAssertNotNil(mockPanGestureRecognizer); +// +// [vc scrollEvent:mockPanGestureRecognizer]; +// +// [[[self.mockEngine verify] ignoringNonObjectArgs] +// dispatchPointerDataPacket:std::make_unique(0)]; +//} +// +//- (void)testFakeEventTimeStamp { +// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine +// nibName:nil +// bundle:nil]; +// XCTAssertNotNil(vc); +// +// flutter::PointerData pointer_data = [vc generatePointerDataForFake]; +// int64_t current_micros = [[NSProcessInfo processInfo] systemUptime] * 1000 * 1000; +// int64_t interval_micros = current_micros - pointer_data.time_stamp; +// const int64_t tolerance_millis = 2; +// XCTAssertTrue(interval_micros / 1000 < tolerance_millis, +// @"PointerData.time_stamp should be equal to NSProcessInfo.systemUptime"); +//} +//@end From 82308de711313811f1ef232a5d336464c08d4167 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 4 Apr 2022 15:22:36 -0700 Subject: [PATCH 5/7] review --- .../Source/FlutterPlatformPluginTest.mm | 12 ++-- .../Source/FlutterViewControllerTest.mm | 60 ++++++++++++------- 2 files changed, 45 insertions(+), 27 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm index ec9f1f49ffd68..f5fbdf33524a6 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformPluginTest.mm @@ -24,7 +24,7 @@ - (void)testClipboardHasCorrectStrings { FlutterPlatformPlugin* plugin = [[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakPtr()]; - XCTestExpectation *setStringExpectation = [self expectationWithDescription:@"setString"]; + XCTestExpectation* setStringExpectation = [self expectationWithDescription:@"setString"]; FlutterResult resultSet = ^(id result) { [setStringExpectation fulfill]; }; @@ -34,7 +34,7 @@ - (void)testClipboardHasCorrectStrings { [plugin handleMethodCall:methodCallSet result:resultSet]; [self waitForExpectationsWithTimeout:1 handler:nil]; - XCTestExpectation *hasStringsExpectation = [self expectationWithDescription:@"hasStrings"]; + XCTestExpectation* hasStringsExpectation = [self expectationWithDescription:@"hasStrings"]; FlutterResult result = ^(id result) { XCTAssertTrue([result[@"value"] boolValue]); [hasStringsExpectation fulfill]; @@ -44,7 +44,7 @@ - (void)testClipboardHasCorrectStrings { [plugin handleMethodCall:methodCall result:result]; [self waitForExpectationsWithTimeout:1 handler:nil]; - XCTestExpectation *getDataExpectation = [self expectationWithDescription:@"getData"]; + XCTestExpectation* getDataExpectation = [self expectationWithDescription:@"getData"]; FlutterResult getDataResult = ^(id result) { XCTAssertEqualObjects(result[@"text"], @"some string"); [getDataExpectation fulfill]; @@ -63,7 +63,7 @@ - (void)testClipboardSetDataToNullDoNotCrash { FlutterPlatformPlugin* plugin = [[FlutterPlatformPlugin alloc] initWithEngine:_weakFactory->GetWeakPtr()]; - XCTestExpectation *setStringExpectation = [self expectationWithDescription:@"setData"]; + XCTestExpectation* setStringExpectation = [self expectationWithDescription:@"setData"]; FlutterResult resultSet = ^(id result) { [setStringExpectation fulfill]; }; @@ -72,7 +72,7 @@ - (void)testClipboardSetDataToNullDoNotCrash { arguments:@{@"text" : [NSNull null]}]; [plugin handleMethodCall:methodCallSet result:resultSet]; - XCTestExpectation *getDataExpectation = [self expectationWithDescription:@"getData"]; + XCTestExpectation* getDataExpectation = [self expectationWithDescription:@"getData"]; FlutterResult result = ^(id result) { XCTAssertEqualObjects(result[@"text"], @"null"); [getDataExpectation fulfill]; @@ -100,7 +100,7 @@ - (void)testPopSystemNavigator { id navigationControllerMock = OCMPartialMock(navigationController); OCMStub([navigationControllerMock popViewControllerAnimated:YES]); // Set some string to the pasteboard. - XCTestExpectation *navigationPopCalled = [self expectationWithDescription:@"SystemNavigator.pop"]; + XCTestExpectation* navigationPopCalled = [self expectationWithDescription:@"SystemNavigator.pop"]; FlutterResult resultSet = ^(id result) { [navigationPopCalled fulfill]; }; diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm index 9560b278feb28..e1e54d89d839d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm @@ -18,7 +18,7 @@ //#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" //#import "flutter/shell/platform/embedder/embedder.h" // -//FLUTTER_ASSERT_ARC +// FLUTTER_ASSERT_ARC // //@interface FlutterEngine () //- (FlutterTextInputPlugin*)textInputPlugin; @@ -79,7 +79,7 @@ //- (void)notifyLowMemory; //@end // -//extern NSNotificationName const FlutterViewControllerWillDealloc; +// extern NSNotificationName const FlutterViewControllerWillDealloc; // ///// A simple mock class for FlutterEngine. ///// @@ -104,7 +104,7 @@ //// The following conditional compilation defines an API 13 concept on earlier API targets so that //// a compiler compiling against API 12 or below does not blow up due to non-existent members. //#if __IPHONE_OS_VERSION_MAX_ALLOWED < 130000 -//typedef enum UIAccessibilityContrast : NSInteger { +// typedef enum UIAccessibilityContrast : NSInteger { // UIAccessibilityContrastUnspecified = 0, // UIAccessibilityContrastNormal = 1, // UIAccessibilityContrastHigh = 2 @@ -274,7 +274,8 @@ // mockEngine.lifecycleChannel = lifecycleChannel; // __weak FlutterViewController* weakViewController; // @autoreleasepool { -// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine +// FlutterViewController* viewController = [[FlutterViewController alloc] +// initWithEngine:mockEngine // nibName:nil // bundle:nil]; // weakViewController = viewController; @@ -288,7 +289,8 @@ //} // //- (void) -// testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear { +// testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear +// { // FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); // [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; // FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine @@ -299,14 +301,17 @@ //} // //- (void) -// testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear { +// testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear +// { // FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); // [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine +// FlutterViewController* viewControllerA = [[FlutterViewController alloc] +// initWithEngine:mockEngine // nibName:nil // bundle:nil]; // mockEngine.viewController = nil; -// FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine +// FlutterViewController* viewControllerB = [[FlutterViewController alloc] +// initWithEngine:mockEngine // nibName:nil // bundle:nil]; // mockEngine.viewController = nil; @@ -316,7 +321,8 @@ //} // //- (void) -// testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear { +// testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear +// { // FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); // [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; // FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine @@ -327,14 +333,17 @@ //} // //- (void) -// testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear { +// testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear +// { // FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); // [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine +// FlutterViewController* viewControllerA = [[FlutterViewController alloc] +// initWithEngine:mockEngine // nibName:nil // bundle:nil]; // mockEngine.viewController = nil; -// FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine +// FlutterViewController* viewControllerB = [[FlutterViewController alloc] +// initWithEngine:mockEngine // nibName:nil // bundle:nil]; // mockEngine.viewController = nil; @@ -344,7 +353,8 @@ //} // //- (void) -// testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear { +// testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear +// { // id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); // FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; // mockEngine.lifecycleChannel = lifecycleChannel; @@ -357,14 +367,17 @@ //} // //- (void) -// testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear { +// testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear +// { // id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); // FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; // mockEngine.lifecycleChannel = lifecycleChannel; -// FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine +// FlutterViewController* viewControllerA = [[FlutterViewController alloc] +// initWithEngine:mockEngine // nibName:nil // bundle:nil]; -// FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine +// FlutterViewController* viewControllerB = [[FlutterViewController alloc] +// initWithEngine:mockEngine // nibName:nil // bundle:nil]; // mockEngine.viewController = viewControllerB; @@ -375,11 +388,13 @@ //- (void)testUpdateViewportMetricsDoesntInvokeEngineWhenNotTheViewController { // FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); // [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine +// FlutterViewController* viewControllerA = [[FlutterViewController alloc] +// initWithEngine:mockEngine // nibName:nil // bundle:nil]; // mockEngine.viewController = nil; -// FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine +// FlutterViewController* viewControllerB = [[FlutterViewController alloc] +// initWithEngine:mockEngine // nibName:nil // bundle:nil]; // mockEngine.viewController = viewControllerB; @@ -404,11 +419,13 @@ //- (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController { // FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); // [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine +// FlutterViewController* viewControllerA = [[FlutterViewController alloc] +// initWithEngine:mockEngine // nibName:nil // bundle:nil]; // mockEngine.viewController = nil; -// FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine +// FlutterViewController* viewControllerB = [[FlutterViewController alloc] +// initWithEngine:mockEngine // nibName:nil // bundle:nil]; // mockEngine.viewController = viewControllerB; @@ -926,7 +943,8 @@ // nibName:nil // bundle:nil]; // XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden, @""); -// [[NSNotificationCenter defaultCenter] postNotificationName:FlutterViewControllerHideHomeIndicator +// [[NSNotificationCenter defaultCenter] +// postNotificationName:FlutterViewControllerHideHomeIndicator // object:nil]; // XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden, @""); // engine.viewController = nil; From c7aeab6491d9181a71a62f199b0fdf81d7387e0f Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 4 Apr 2022 16:23:05 -0700 Subject: [PATCH 6/7] cleanup --- .../Source/FlutterViewControllerTest.mm | 2334 ++++++++--------- 1 file changed, 1167 insertions(+), 1167 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm index e1e54d89d839d..14de18960a470 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm @@ -1,1167 +1,1167 @@ -//// Copyright 2013 The Flutter Authors. All rights reserved. -//// Use of this source code is governed by a BSD-style license that can be -//// found in the LICENSE file. -// -//#import -//#import -// -//#include "flutter/fml/platform/darwin/message_loop_darwin.h" -//#import "flutter/lib/ui/window/platform_configuration.h" -//#include "flutter/lib/ui/window/pointer_data.h" -//#import "flutter/lib/ui/window/viewport_metrics.h" -//#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" -//#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" -//#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" -//#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEmbedderKeyResponder.h" -//#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterFakeKeyEvents.h" -//#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" -//#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" -//#import "flutter/shell/platform/embedder/embedder.h" -// -// FLUTTER_ASSERT_ARC -// -//@interface FlutterEngine () -//- (FlutterTextInputPlugin*)textInputPlugin; -//- (void)sendKeyEvent:(const FlutterKeyEvent&)event -// callback:(nullable FlutterKeyEventCallback)callback -// userData:(nullable void*)userData; -//@end -// -///// Sometimes we have to use a custom mock to avoid retain cycles in OCMock. -///// Used for testing low memory notification. -//@interface FlutterEnginePartialMock : FlutterEngine -//@property(nonatomic, strong) FlutterBasicMessageChannel* lifecycleChannel; -//@property(nonatomic, strong) FlutterBasicMessageChannel* keyEventChannel; -//@property(nonatomic, weak) FlutterViewController* viewController; -//@property(nonatomic, strong) FlutterTextInputPlugin* textInputPlugin; -//@property(nonatomic, assign) BOOL didCallNotifyLowMemory; -//- (FlutterTextInputPlugin*)textInputPlugin; -//- (void)sendKeyEvent:(const FlutterKeyEvent&)event -// callback:(nullable FlutterKeyEventCallback)callback -// userData:(nullable void*)userData; -//@end -// -//@implementation FlutterEnginePartialMock -//@synthesize viewController; -//@synthesize lifecycleChannel; -//@synthesize keyEventChannel; -//@synthesize textInputPlugin; -// -//- (void)notifyLowMemory { -// _didCallNotifyLowMemory = YES; -//} -// -//- (void)sendKeyEvent:(const FlutterKeyEvent&)event -// callback:(FlutterKeyEventCallback)callback -// userData:(void*)userData API_AVAILABLE(ios(9.0)) { -// if (callback == nil) -// return; -// // NSAssert(callback != nullptr, @"Invalid callback"); -// // Response is async, so we have to post it to the run loop instead of calling -// // it directly. -// CFRunLoopPerformBlock(CFRunLoopGetCurrent(), fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode, -// ^() { -// callback(true, userData); -// }); -//} -//@end -// -//@interface FlutterEngine () -//- (BOOL)createShell:(NSString*)entrypoint -// libraryURI:(NSString*)libraryURI -// initialRoute:(NSString*)initialRoute; -//- (void)dispatchPointerDataPacket:(std::unique_ptr)packet; -//- (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics; -//- (void)attachView; -//@end -// -//@interface FlutterEngine (TestLowMemory) -//- (void)notifyLowMemory; -//@end -// -// extern NSNotificationName const FlutterViewControllerWillDealloc; -// -///// A simple mock class for FlutterEngine. -///// -///// OCMClassMock can't be used for FlutterEngine sometimes because OCMock retains arguments to -///// invocations and since the init for FlutterViewController calls a method on the -///// FlutterEngine it creates a retain cycle that stops us from testing behaviors related to -///// deleting FlutterViewControllers. -///// -///// Used for testing deallocation. -//@interface MockEngine : NSObject -//@end -// -//@implementation MockEngine -//- (FlutterViewController*)viewController { -// return nil; -//} -//- (void)setViewController:(FlutterViewController*)viewController { -// // noop -//} -//@end -// -//// The following conditional compilation defines an API 13 concept on earlier API targets so that -//// a compiler compiling against API 12 or below does not blow up due to non-existent members. -//#if __IPHONE_OS_VERSION_MAX_ALLOWED < 130000 -// typedef enum UIAccessibilityContrast : NSInteger { -// UIAccessibilityContrastUnspecified = 0, -// UIAccessibilityContrastNormal = 1, -// UIAccessibilityContrastHigh = 2 -//} UIAccessibilityContrast; -// -//@interface UITraitCollection (MethodsFromNewerSDK) -//- (UIAccessibilityContrast)accessibilityContrast; -//@end -//#endif -// -//@interface FlutterKeyboardManager (Tests) -//@property(nonatomic, retain, readonly) -// NSMutableArray>* primaryResponders; -//@end -// -//@interface FlutterEmbedderKeyResponder (Tests) -//@property(nonatomic, copy, readonly) FlutterSendKeyEvent sendEvent; -//@end -// -//@interface FlutterViewController (Tests) -// -//@property(nonatomic, assign) double targetViewInsetBottom; -// -//- (void)surfaceUpdated:(BOOL)appeared; -//- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences; -//- (void)handlePressEvent:(FlutterUIPressProxy*)press -// nextAction:(void (^)())next API_AVAILABLE(ios(13.4)); -//- (void)scrollEvent:(UIPanGestureRecognizer*)recognizer; -//- (void)updateViewportMetrics; -//- (void)onUserSettingsChanged:(NSNotification*)notification; -//- (void)applicationWillTerminate:(NSNotification*)notification; -//- (void)goToApplicationLifecycle:(nonnull NSString*)state; -//- (void)keyboardWillChangeFrame:(NSNotification*)notification; -//- (void)keyboardWillBeHidden:(NSNotification*)notification; -//- (void)startKeyBoardAnimation:(NSTimeInterval)duration; -//- (void)ensureViewportMetricsIsCorrect; -//- (void)invalidateDisplayLink; -//- (void)addInternalPlugins; -//- (flutter::PointerData)generatePointerDataForFake; -//@end -// -//@interface FlutterViewControllerTest : XCTestCase -//@property(nonatomic, strong) id mockEngine; -//@property(nonatomic, strong) id mockTextInputPlugin; -//@property(nonatomic, strong) id messageSent; -//- (void)sendMessage:(id _Nullable)message reply:(FlutterReply _Nullable)callback; -//@end -// -//@implementation FlutterViewControllerTest -// -//- (void)setUp { -// self.mockEngine = OCMClassMock([FlutterEngine class]); -// self.mockTextInputPlugin = OCMClassMock([FlutterTextInputPlugin class]); -// OCMStub([self.mockEngine textInputPlugin]).andReturn(self.mockTextInputPlugin); -// self.messageSent = nil; -//} -// -//- (void)tearDown { -// // We stop mocking here to avoid retain cycles that stop -// // FlutterViewControllers from deallocing. -// [self.mockEngine stopMocking]; -// self.mockEngine = nil; -// self.mockTextInputPlugin = nil; -// self.messageSent = nil; -//} -// -//- (void)testkeyboardWillChangeFrameWillStartKeyboardAnimation { -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// -// CGFloat width = UIScreen.mainScreen.bounds.size.width; -// CGRect keyboardFrame = CGRectMake(0, 100, width, 400); -// BOOL isLocal = YES; -// NSNotification* notification = [NSNotification -// notificationWithName:@"" -// object:nil -// userInfo:@{ -// @"UIKeyboardFrameEndUserInfoKey" : [NSValue valueWithCGRect:keyboardFrame], -// @"UIKeyboardAnimationDurationUserInfoKey" : [NSNumber numberWithDouble:0.25], -// @"UIKeyboardIsLocalUserInfoKey" : [NSNumber numberWithBool:isLocal] -// }]; -// -// XCTestExpectation* expectation = [self expectationWithDescription:@"update viewport"]; -// OCMStub([mockEngine updateViewportMetrics:flutter::ViewportMetrics()]) -// .ignoringNonObjectArgs() -// .andDo(^(NSInvocation* invocation) { -// [expectation fulfill]; -// }); -// id viewControllerMock = OCMPartialMock(viewController); -// [viewControllerMock keyboardWillChangeFrame:notification]; -// OCMVerify([viewControllerMock startKeyBoardAnimation:0.25]); -// [self waitForExpectationsWithTimeout:5.0 handler:nil]; -//} -// -//- (void)testEnsureBottomInsetIsZeroWhenKeyboardDismissed { -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// -// FlutterViewController* viewControllerMock = OCMPartialMock(viewController); -// CGRect keyboardFrame = CGRectMake(0, 0, 0, 0); -// BOOL isLocal = YES; -// NSNotification* fakeNotification = [NSNotification -// notificationWithName:@"" -// object:nil -// userInfo:@{ -// @"UIKeyboardFrameEndUserInfoKey" : [NSValue valueWithCGRect:keyboardFrame], -// @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25), -// @"UIKeyboardIsLocalUserInfoKey" : @(isLocal) -// }]; -// -// viewControllerMock.targetViewInsetBottom = 10; -// [viewControllerMock keyboardWillBeHidden:fakeNotification]; -// XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 0); -//} -// -//- (void)testEnsureViewportMetricsWillInvokeAndDisplayLinkWillInvalidateInViewDidDisappear { -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// id viewControllerMock = OCMPartialMock(viewController); -// [viewControllerMock viewDidDisappear:YES]; -// OCMVerify([viewControllerMock ensureViewportMetricsIsCorrect]); -// OCMVerify([viewControllerMock invalidateDisplayLink]); -//} -// -//- (void)testViewDidDisappearDoesntPauseEngineWhenNotTheViewController { -// id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); -// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; -// mockEngine.lifecycleChannel = lifecycleChannel; -// FlutterViewController* viewControllerA = -// [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; -// FlutterViewController* viewControllerB = -// [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; -// id viewControllerMock = OCMPartialMock(viewControllerA); -// OCMStub([viewControllerMock surfaceUpdated:NO]); -// mockEngine.viewController = viewControllerB; -// [viewControllerA viewDidDisappear:NO]; -// OCMReject([lifecycleChannel sendMessage:@"AppLifecycleState.paused"]); -// OCMReject([viewControllerMock surfaceUpdated:[OCMArg any]]); -//} -// -//- (void)testAppWillTerminateViewDidDestroyTheEngine { -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// id viewControllerMock = OCMPartialMock(viewController); -// OCMStub([viewControllerMock goToApplicationLifecycle:@"AppLifecycleState.detached"]); -// OCMStub([mockEngine destroyContext]); -// [viewController applicationWillTerminate:nil]; -// OCMVerify([viewControllerMock goToApplicationLifecycle:@"AppLifecycleState.detached"]); -// OCMVerify([mockEngine destroyContext]); -//} -// -//- (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController { -// id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); -// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; -// mockEngine.lifecycleChannel = lifecycleChannel; -// __weak FlutterViewController* weakViewController; -// @autoreleasepool { -// FlutterViewController* viewController = [[FlutterViewController alloc] -// initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// weakViewController = viewController; -// id viewControllerMock = OCMPartialMock(viewController); -// OCMStub([viewControllerMock surfaceUpdated:NO]); -// [viewController viewDidDisappear:NO]; -// OCMVerify([lifecycleChannel sendMessage:@"AppLifecycleState.paused"]); -// OCMVerify([viewControllerMock surfaceUpdated:NO]); -// } -// XCTAssertNil(weakViewController); -//} -// -//- (void) -// testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear -// { -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// [viewController viewWillAppear:YES]; -// OCMVerify([viewController onUserSettingsChanged:nil]); -//} -// -//- (void) -// testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear -// { -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewControllerA = [[FlutterViewController alloc] -// initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// mockEngine.viewController = nil; -// FlutterViewController* viewControllerB = [[FlutterViewController alloc] -// initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// mockEngine.viewController = nil; -// mockEngine.viewController = viewControllerB; -// [viewControllerA viewWillAppear:YES]; -// OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]); -//} -// -//- (void) -// testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear -// { -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// [viewController viewDidAppear:YES]; -// OCMVerify([viewController onUserSettingsChanged:nil]); -//} -// -//- (void) -// testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear -// { -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewControllerA = [[FlutterViewController alloc] -// initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// mockEngine.viewController = nil; -// FlutterViewController* viewControllerB = [[FlutterViewController alloc] -// initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// mockEngine.viewController = nil; -// mockEngine.viewController = viewControllerB; -// [viewControllerA viewDidAppear:YES]; -// OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]); -//} -// -//- (void) -// testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear -// { -// id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); -// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; -// mockEngine.lifecycleChannel = lifecycleChannel; -// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// mockEngine.viewController = viewController; -// [viewController viewWillDisappear:NO]; -// OCMVerify([lifecycleChannel sendMessage:@"AppLifecycleState.inactive"]); -//} -// -//- (void) -// testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear -// { -// id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); -// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; -// mockEngine.lifecycleChannel = lifecycleChannel; -// FlutterViewController* viewControllerA = [[FlutterViewController alloc] -// initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// FlutterViewController* viewControllerB = [[FlutterViewController alloc] -// initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// mockEngine.viewController = viewControllerB; -// [viewControllerA viewDidDisappear:NO]; -// OCMReject([lifecycleChannel sendMessage:@"AppLifecycleState.inactive"]); -//} -// -//- (void)testUpdateViewportMetricsDoesntInvokeEngineWhenNotTheViewController { -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewControllerA = [[FlutterViewController alloc] -// initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// mockEngine.viewController = nil; -// FlutterViewController* viewControllerB = [[FlutterViewController alloc] -// initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// mockEngine.viewController = viewControllerB; -// [viewControllerA updateViewportMetrics]; -// flutter::ViewportMetrics viewportMetrics; -// OCMVerify(never(), [mockEngine updateViewportMetrics:viewportMetrics]); -//} -// -//- (void)testUpdateViewportMetricsDoesInvokeEngineWhenIsTheViewController { -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// mockEngine.viewController = viewController; -// flutter::ViewportMetrics viewportMetrics; -// OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs(); -// [viewController updateViewportMetrics]; -// OCMVerifyAll(mockEngine); -//} -// -//- (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController { -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewControllerA = [[FlutterViewController alloc] -// initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// mockEngine.viewController = nil; -// FlutterViewController* viewControllerB = [[FlutterViewController alloc] -// initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// mockEngine.viewController = viewControllerB; -// UIView* view = viewControllerA.view; -// XCTAssertNotNil(view); -// OCMVerify(never(), [mockEngine attachView]); -//} -// -//- (void)testViewDidLoadDoesInvokeEngineWhenIsTheViewController { -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// mockEngine.viewController = nil; -// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// mockEngine.viewController = viewController; -// UIView* view = viewController.view; -// XCTAssertNotNil(view); -// OCMVerify([mockEngine attachView]); -//} -// -//- (void)testInternalPluginsWeakPtrNotCrash { -// FlutterSendKeyEvent sendEvent; -// @autoreleasepool { -// FlutterViewController* vc = [[FlutterViewController alloc] initWithProject:nil -// nibName:nil -// bundle:nil]; -// [vc addInternalPlugins]; -// FlutterKeyboardManager* keyboardManager = vc.keyboardManager; -// FlutterEmbedderKeyResponder* keyPrimaryResponder = (FlutterEmbedderKeyResponder*) -// [(NSArray>*)keyboardManager.primaryResponders firstObject]; -// sendEvent = [keyPrimaryResponder sendEvent]; -// } -// -// if (sendEvent) { -// sendEvent({}, nil, nil); -// } -//} -// -//// Regression test for https://github.com/flutter/engine/pull/32098. -//- (void)testInternalPluginsInvokeInViewDidLoad { -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// [viewController viewDidLoad]; -// OCMVerify([viewController addInternalPlugins]); -//} -// -//- (void)testBinaryMessenger { -// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine -// nibName:nil -// bundle:nil]; -// XCTAssertNotNil(vc); -// id messenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); -// OCMStub([self.mockEngine binaryMessenger]).andReturn(messenger); -// XCTAssertEqual(vc.binaryMessenger, messenger); -// OCMVerify([self.mockEngine binaryMessenger]); -//} -// -//#pragma mark - Platform Brightness -// -//- (void)testItReportsLightPlatformBrightnessByDefault { -// // Setup test. -// id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); -// OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); -// -// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine -// nibName:nil -// bundle:nil]; -// -// // Exercise behavior under test. -// [vc traitCollectionDidChange:nil]; -// -// // Verify behavior. -// OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { -// return [message[@"platformBrightness"] isEqualToString:@"light"]; -// }]]); -// -// // Clean up mocks -// [settingsChannel stopMocking]; -//} -// -//- (void)testItReportsPlatformBrightnessWhenViewWillAppear { -// // Setup test. -// id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel); -// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// -// // Exercise behavior under test. -// [vc viewWillAppear:false]; -// -// // Verify behavior. -// OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { -// return [message[@"platformBrightness"] isEqualToString:@"light"]; -// }]]); -// -// // Clean up mocks -// [settingsChannel stopMocking]; -//} -// -//- (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt { -// if (@available(iOS 13, *)) { -// // noop -// } else { -// return; -// } -// -// // Setup test. -// id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); -// OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); -// -// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine -// nibName:nil -// bundle:nil]; -// id mockTraitCollection = -// [self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]; -// -// // We partially mock the real FlutterViewController to act as the OS and report -// // the UITraitCollection of our choice. Mocking the object under test is not -// // desirable, but given that the OS does not offer a DI approach to providing -// // our own UITraitCollection, this seems to be the least bad option. -// id partialMockVC = OCMPartialMock(realVC); -// OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection); -// -// // Exercise behavior under test. -// [partialMockVC traitCollectionDidChange:nil]; -// -// // Verify behavior. -// OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { -// return [message[@"platformBrightness"] isEqualToString:@"dark"]; -// }]]); -// -// // Clean up mocks -// [partialMockVC stopMocking]; -// [settingsChannel stopMocking]; -// [mockTraitCollection stopMocking]; -//} -// -//// Creates a mocked UITraitCollection with nil values for everything except userInterfaceStyle, -//// which is set to the given "style". -//- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style { -// id mockTraitCollection = OCMClassMock([UITraitCollection class]); -// OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style); -// return mockTraitCollection; -//} -// -//#pragma mark - Platform Contrast -// -//- (void)testItReportsNormalPlatformContrastByDefault { -// if (@available(iOS 13, *)) { -// // noop -// } else { -// return; -// } -// -// // Setup test. -// id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); -// OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); -// -// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine -// nibName:nil -// bundle:nil]; -// -// // Exercise behavior under test. -// [vc traitCollectionDidChange:nil]; -// -// // Verify behavior. -// OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { -// return [message[@"platformContrast"] isEqualToString:@"normal"]; -// }]]); -// -// // Clean up mocks -// [settingsChannel stopMocking]; -//} -// -//- (void)testItReportsPlatformContrastWhenViewWillAppear { -// if (@available(iOS 13, *)) { -// // noop -// } else { -// return; -// } -// FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); -// [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; -// -// // Setup test. -// id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); -// OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel); -// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// -// // Exercise behavior under test. -// [vc viewWillAppear:false]; -// -// // Verify behavior. -// OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { -// return [message[@"platformContrast"] isEqualToString:@"normal"]; -// }]]); -// -// // Clean up mocks -// [settingsChannel stopMocking]; -//} -// -//- (void)testItReportsHighContrastWhenTraitCollectionRequestsIt { -// if (@available(iOS 13, *)) { -// // noop -// } else { -// return; -// } -// -// // Setup test. -// id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); -// OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); -// -// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine -// nibName:nil -// bundle:nil]; -// id mockTraitCollection = [self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh]; -// -// // We partially mock the real FlutterViewController to act as the OS and report -// // the UITraitCollection of our choice. Mocking the object under test is not -// // desirable, but given that the OS does not offer a DI approach to providing -// // our own UITraitCollection, this seems to be the least bad option. -// id partialMockVC = OCMPartialMock(realVC); -// OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection); -// -// // Exercise behavior under test. -// [partialMockVC traitCollectionDidChange:mockTraitCollection]; -// -// // Verify behavior. -// OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { -// return [message[@"platformContrast"] isEqualToString:@"high"]; -// }]]); -// -// // Clean up mocks -// [partialMockVC stopMocking]; -// [settingsChannel stopMocking]; -// [mockTraitCollection stopMocking]; -//} -// -//- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagNotSet { -// if (@available(iOS 13, *)) { -// // noop -// } else { -// return; -// } -// -// // Setup test. -// FlutterViewController* viewController = -// [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; -// id partialMockViewController = OCMPartialMock(viewController); -// OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(NO); -// -// // Exercise behavior under test. -// int32_t flags = [partialMockViewController accessibilityFlags]; -// -// // Verify behavior. -// XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) == 0); -//} -// -//- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagSet { -// if (@available(iOS 13, *)) { -// // noop -// } else { -// return; -// } -// -// // Setup test. -// FlutterViewController* viewController = -// [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; -// id partialMockViewController = OCMPartialMock(viewController); -// OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(YES); -// -// // Exercise behavior under test. -// int32_t flags = [partialMockViewController accessibilityFlags]; -// -// // Verify behavior. -// XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) != 0); -//} -// -//- (void)testPerformOrientationUpdateForcesOrientationChange { -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait -// currentOrientation:UIInterfaceOrientationLandscapeLeft -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationPortrait]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait -// currentOrientation:UIInterfaceOrientationLandscapeRight -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationPortrait]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait -// currentOrientation:UIInterfaceOrientationPortraitUpsideDown -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationPortrait]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown -// currentOrientation:UIInterfaceOrientationLandscapeLeft -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown -// currentOrientation:UIInterfaceOrientationLandscapeRight -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown -// currentOrientation:UIInterfaceOrientationPortrait -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape -// currentOrientation:UIInterfaceOrientationPortrait -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationLandscapeLeft]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape -// currentOrientation:UIInterfaceOrientationPortraitUpsideDown -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationLandscapeLeft]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft -// currentOrientation:UIInterfaceOrientationPortrait -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationLandscapeLeft]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft -// currentOrientation:UIInterfaceOrientationLandscapeRight -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationLandscapeLeft]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft -// currentOrientation:UIInterfaceOrientationPortraitUpsideDown -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationLandscapeLeft]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight -// currentOrientation:UIInterfaceOrientationPortrait -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationLandscapeRight]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight -// currentOrientation:UIInterfaceOrientationLandscapeLeft -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationLandscapeRight]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight -// currentOrientation:UIInterfaceOrientationPortraitUpsideDown -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationLandscapeRight]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown -// currentOrientation:UIInterfaceOrientationPortraitUpsideDown -// didChangeOrientation:YES -// resultingOrientation:UIInterfaceOrientationPortrait]; -//} -// -//- (void)testPerformOrientationUpdateDoesNotForceOrientationChange { -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll -// currentOrientation:UIInterfaceOrientationPortrait -// didChangeOrientation:NO -// resultingOrientation:static_cast(0)]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll -// currentOrientation:UIInterfaceOrientationPortraitUpsideDown -// didChangeOrientation:NO -// resultingOrientation:static_cast(0)]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll -// currentOrientation:UIInterfaceOrientationLandscapeLeft -// didChangeOrientation:NO -// resultingOrientation:static_cast(0)]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll -// currentOrientation:UIInterfaceOrientationLandscapeRight -// didChangeOrientation:NO -// resultingOrientation:static_cast(0)]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown -// currentOrientation:UIInterfaceOrientationPortrait -// didChangeOrientation:NO -// resultingOrientation:static_cast(0)]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown -// currentOrientation:UIInterfaceOrientationLandscapeLeft -// didChangeOrientation:NO -// resultingOrientation:static_cast(0)]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown -// currentOrientation:UIInterfaceOrientationLandscapeRight -// didChangeOrientation:NO -// resultingOrientation:static_cast(0)]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait -// currentOrientation:UIInterfaceOrientationPortrait -// didChangeOrientation:NO -// resultingOrientation:static_cast(0)]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown -// currentOrientation:UIInterfaceOrientationPortraitUpsideDown -// didChangeOrientation:NO -// resultingOrientation:static_cast(0)]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape -// currentOrientation:UIInterfaceOrientationLandscapeLeft -// didChangeOrientation:NO -// resultingOrientation:static_cast(0)]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape -// currentOrientation:UIInterfaceOrientationLandscapeRight -// didChangeOrientation:NO -// resultingOrientation:static_cast(0)]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft -// currentOrientation:UIInterfaceOrientationLandscapeLeft -// didChangeOrientation:NO -// resultingOrientation:static_cast(0)]; -// -// [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight -// currentOrientation:UIInterfaceOrientationLandscapeRight -// didChangeOrientation:NO -// resultingOrientation:static_cast(0)]; -//} -// -//// Perform an orientation update test that fails when the expected outcome -//// for an orientation update is not met -//- (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask -// currentOrientation:(UIInterfaceOrientation)currentOrientation -// didChangeOrientation:(BOOL)didChange -// resultingOrientation:(UIInterfaceOrientation)resultingOrientation { -// id deviceMock = OCMPartialMock([UIDevice currentDevice]); -// if (!didChange) { -// OCMReject([deviceMock setValue:[OCMArg any] forKey:@"orientation"]); -// } else { -// OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]); -// } -// -// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine -// nibName:nil -// bundle:nil]; -// id mockApplication = OCMClassMock([UIApplication class]); -// OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); -// OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation); -// -// [realVC performOrientationUpdate:mask]; -// OCMVerifyAll(deviceMock); -// [deviceMock stopMocking]; -// [mockApplication stopMocking]; -//} -// -//// Creates a mocked UITraitCollection with nil values for everything except accessibilityContrast, -//// which is set to the given "contrast". -//- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast { -// id mockTraitCollection = OCMClassMock([UITraitCollection class]); -// OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast); -// return mockTraitCollection; -//} -// -//- (void)testWillDeallocNotification { -// XCTestExpectation* expectation = -// [[XCTestExpectation alloc] initWithDescription:@"notification called"]; -// id engine = [[MockEngine alloc] init]; -// @autoreleasepool { -// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine -// nibName:nil -// bundle:nil]; -// [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc -// object:nil -// queue:[NSOperationQueue mainQueue] -// usingBlock:^(NSNotification* _Nonnull note) { -// [expectation fulfill]; -// }]; -// realVC = nil; -// } -// [self waitForExpectations:@[ expectation ] timeout:1.0]; -//} -// -//- (void)testReleasesKeyboardManagerOnDealloc { -// __weak FlutterKeyboardManager* weakKeyboardManager = nil; -// @autoreleasepool { -// FlutterViewController* viewController = [[FlutterViewController alloc] init]; -// -// [viewController addInternalPlugins]; -// weakKeyboardManager = viewController.keyboardManager; -// XCTAssertNotNil(weakKeyboardManager); -// [viewController deregisterNotifications]; -// viewController = nil; -// } -// // View controller has released the keyboard manager. -// XCTAssertNil(weakKeyboardManager); -//} -// -//- (void)testDoesntLoadViewInInit { -// FlutterDartProject* project = [[FlutterDartProject alloc] init]; -// FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; -// [engine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine -// nibName:nil -// bundle:nil]; -// XCTAssertFalse([realVC isViewLoaded], @"shouldn't have loaded since it hasn't been shown"); -// engine.viewController = nil; -//} -// -//- (void)testHideOverlay { -// FlutterDartProject* project = [[FlutterDartProject alloc] init]; -// FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; -// [engine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine -// nibName:nil -// bundle:nil]; -// XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden, @""); -// [[NSNotificationCenter defaultCenter] -// postNotificationName:FlutterViewControllerHideHomeIndicator -// object:nil]; -// XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden, @""); -// engine.viewController = nil; -//} -// -//- (void)testHideA11yElements { -// FlutterDartProject* project = [[FlutterDartProject alloc] init]; -// FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; -// [engine createShell:@"" libraryURI:@"" initialRoute:nil]; -// FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine -// nibName:nil -// bundle:nil]; -// XCTAssertFalse(realVC.view.accessibilityElementsHidden); -// [[NSNotificationCenter defaultCenter] -// postNotificationName:UIApplicationWillResignActiveNotification -// object:nil]; -// XCTAssertTrue(realVC.view.accessibilityElementsHidden); -// [[NSNotificationCenter defaultCenter] -// postNotificationName:UIApplicationDidBecomeActiveNotification -// object:nil]; -// XCTAssertFalse(realVC.view.accessibilityElementsHidden); -// engine.viewController = nil; -//} -// -//- (void)testNotifyLowMemory { -// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; -// FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// id viewControllerMock = OCMPartialMock(viewController); -// OCMStub([viewControllerMock surfaceUpdated:NO]); -// [viewController beginAppearanceTransition:NO animated:NO]; -// [viewController endAppearanceTransition]; -// XCTAssertTrue(mockEngine.didCallNotifyLowMemory); -//} -// -//- (void)sendMessage:(id _Nullable)message reply:(FlutterReply _Nullable)callback { -// NSMutableDictionary* replyMessage = [@{ -// @"handled" : @YES, -// } mutableCopy]; -// // Response is async, so we have to post it to the run loop instead of calling -// // it directly. -// self.messageSent = message; -// CFRunLoopPerformBlock(CFRunLoopGetCurrent(), fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode, -// ^() { -// callback(replyMessage); -// }); -//} -// -//- (void)testValidKeyUpEvent API_AVAILABLE(ios(13.4)) { -// if (@available(iOS 13.4, *)) { -// // noop -// } else { -// return; -// } -// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; -// mockEngine.keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); -// OCMStub([mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) -// .andCall(self, @selector(sendMessage:reply:)); -// OCMStub([self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES); -// mockEngine.textInputPlugin = self.mockTextInputPlugin; -// -// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// -// // Allocate the keyboard manager in the view controller by adding the internal -// // plugins. -// [vc addInternalPlugins]; -// -// [vc handlePressEvent:keyUpEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0) -// nextAction:^(){ -// }]; -// -// XCTAssert(self.messageSent != nil); -// XCTAssert([self.messageSent[@"keymap"] isEqualToString:@"ios"]); -// XCTAssert([self.messageSent[@"type"] isEqualToString:@"keyup"]); -// XCTAssert([self.messageSent[@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]); -// XCTAssert([self.messageSent[@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]); -// XCTAssert([self.messageSent[@"characters"] isEqualToString:@""]); -// XCTAssert([self.messageSent[@"charactersIgnoringModifiers"] isEqualToString:@""]); -// [vc deregisterNotifications]; -//} -// -//- (void)testValidKeyDownEvent API_AVAILABLE(ios(13.4)) { -// if (@available(iOS 13.4, *)) { -// // noop -// } else { -// return; -// } -// -// FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; -// mockEngine.keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); -// OCMStub([mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) -// .andCall(self, @selector(sendMessage:reply:)); -// OCMStub([self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES); -// mockEngine.textInputPlugin = self.mockTextInputPlugin; -// -// __strong FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine -// nibName:nil -// bundle:nil]; -// // Allocate the keyboard manager in the view controller by adding the internal -// // plugins. -// [vc addInternalPlugins]; -// -// [vc handlePressEvent:keyDownEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0f, "A", -// "a") -// nextAction:^(){ -// }]; -// -// XCTAssert(self.messageSent != nil); -// XCTAssert([self.messageSent[@"keymap"] isEqualToString:@"ios"]); -// XCTAssert([self.messageSent[@"type"] isEqualToString:@"keydown"]); -// XCTAssert([self.messageSent[@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]); -// XCTAssert([self.messageSent[@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]); -// XCTAssert([self.messageSent[@"characters"] isEqualToString:@"A"]); -// XCTAssert([self.messageSent[@"charactersIgnoringModifiers"] isEqualToString:@"a"]); -// [vc deregisterNotifications]; -// vc = nil; -//} -// -//- (void)testIgnoredKeyEvents API_AVAILABLE(ios(13.4)) { -// if (@available(iOS 13.4, *)) { -// // noop -// } else { -// return; -// } -// id keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); -// OCMStub([keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) -// .andCall(self, @selector(sendMessage:reply:)); -// OCMStub([self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES); -// OCMStub([self.mockEngine keyEventChannel]).andReturn(keyEventChannel); -// -// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine -// nibName:nil -// bundle:nil]; -// -// // Allocate the keyboard manager in the view controller by adding the internal -// // plugins. -// [vc addInternalPlugins]; -// -// [vc handlePressEvent:keyEventWithPhase(UIPressPhaseStationary, UIKeyboardHIDUsageKeyboardA, -// UIKeyModifierShift, 123.0) -// nextAction:^(){ -// }]; -// [vc handlePressEvent:keyEventWithPhase(UIPressPhaseCancelled, UIKeyboardHIDUsageKeyboardA, -// UIKeyModifierShift, 123.0) -// nextAction:^(){ -// }]; -// [vc handlePressEvent:keyEventWithPhase(UIPressPhaseChanged, UIKeyboardHIDUsageKeyboardA, -// UIKeyModifierShift, 123.0) -// nextAction:^(){ -// }]; -// -// XCTAssert(self.messageSent == nil); -// OCMVerify(never(), [keyEventChannel sendMessage:[OCMArg any]]); -// [vc deregisterNotifications]; -//} -// -//- (void)testPanGestureRecognizer API_AVAILABLE(ios(13.4)) { -// if (@available(iOS 13.4, *)) { -// // noop -// } else { -// return; -// } -// -// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine -// nibName:nil -// bundle:nil]; -// XCTAssertNotNil(vc); -// UIView* view = vc.view; -// XCTAssertNotNil(view); -// NSArray* gestureRecognizers = view.gestureRecognizers; -// XCTAssertNotNil(gestureRecognizers); -// -// BOOL found = NO; -// for (id gesture in gestureRecognizers) { -// if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) { -// found = YES; -// break; -// } -// } -// XCTAssertTrue(found); -//} -// -//- (void)testMouseSupport API_AVAILABLE(ios(13.4)) { -// if (@available(iOS 13.4, *)) { -// // noop -// } else { -// return; -// } -// -// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine -// nibName:nil -// bundle:nil]; -// XCTAssertNotNil(vc); -// -// id mockPanGestureRecognizer = OCMClassMock([UIPanGestureRecognizer class]); -// XCTAssertNotNil(mockPanGestureRecognizer); -// -// [vc scrollEvent:mockPanGestureRecognizer]; -// -// [[[self.mockEngine verify] ignoringNonObjectArgs] -// dispatchPointerDataPacket:std::make_unique(0)]; -//} -// -//- (void)testFakeEventTimeStamp { -// FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine -// nibName:nil -// bundle:nil]; -// XCTAssertNotNil(vc); -// -// flutter::PointerData pointer_data = [vc generatePointerDataForFake]; -// int64_t current_micros = [[NSProcessInfo processInfo] systemUptime] * 1000 * 1000; -// int64_t interval_micros = current_micros - pointer_data.time_stamp; -// const int64_t tolerance_millis = 2; -// XCTAssertTrue(interval_micros / 1000 < tolerance_millis, -// @"PointerData.time_stamp should be equal to NSProcessInfo.systemUptime"); -//} -//@end +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import +#import + +#include "flutter/fml/platform/darwin/message_loop_darwin.h" +#import "flutter/lib/ui/window/platform_configuration.h" +#include "flutter/lib/ui/window/pointer_data.h" +#import "flutter/lib/ui/window/viewport_metrics.h" +#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" +#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" +#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterEmbedderKeyResponder.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterFakeKeyEvents.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h" +#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" +#import "flutter/shell/platform/embedder/embedder.h" + + FLUTTER_ASSERT_ARC + +@interface FlutterEngine () +- (FlutterTextInputPlugin*)textInputPlugin; +- (void)sendKeyEvent:(const FlutterKeyEvent&)event + callback:(nullable FlutterKeyEventCallback)callback + userData:(nullable void*)userData; +@end + +/// Sometimes we have to use a custom mock to avoid retain cycles in OCMock. +/// Used for testing low memory notification. +@interface FlutterEnginePartialMock : FlutterEngine +@property(nonatomic, strong) FlutterBasicMessageChannel* lifecycleChannel; +@property(nonatomic, strong) FlutterBasicMessageChannel* keyEventChannel; +@property(nonatomic, weak) FlutterViewController* viewController; +@property(nonatomic, strong) FlutterTextInputPlugin* textInputPlugin; +@property(nonatomic, assign) BOOL didCallNotifyLowMemory; +- (FlutterTextInputPlugin*)textInputPlugin; +- (void)sendKeyEvent:(const FlutterKeyEvent&)event + callback:(nullable FlutterKeyEventCallback)callback + userData:(nullable void*)userData; +@end + +@implementation FlutterEnginePartialMock +@synthesize viewController; +@synthesize lifecycleChannel; +@synthesize keyEventChannel; +@synthesize textInputPlugin; + +- (void)notifyLowMemory { + _didCallNotifyLowMemory = YES; +} + +- (void)sendKeyEvent:(const FlutterKeyEvent&)event + callback:(FlutterKeyEventCallback)callback + userData:(void*)userData API_AVAILABLE(ios(9.0)) { + if (callback == nil) + return; + // NSAssert(callback != nullptr, @"Invalid callback"); + // Response is async, so we have to post it to the run loop instead of calling + // it directly. + CFRunLoopPerformBlock(CFRunLoopGetCurrent(), fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode, + ^() { + callback(true, userData); + }); +} +@end + +@interface FlutterEngine () +- (BOOL)createShell:(NSString*)entrypoint + libraryURI:(NSString*)libraryURI + initialRoute:(NSString*)initialRoute; +- (void)dispatchPointerDataPacket:(std::unique_ptr)packet; +- (void)updateViewportMetrics:(flutter::ViewportMetrics)viewportMetrics; +- (void)attachView; +@end + +@interface FlutterEngine (TestLowMemory) +- (void)notifyLowMemory; +@end + + extern NSNotificationName const FlutterViewControllerWillDealloc; + +/// A simple mock class for FlutterEngine. +/// +/// OCMClassMock can't be used for FlutterEngine sometimes because OCMock retains arguments to +/// invocations and since the init for FlutterViewController calls a method on the +/// FlutterEngine it creates a retain cycle that stops us from testing behaviors related to +/// deleting FlutterViewControllers. +/// +/// Used for testing deallocation. +@interface MockEngine : NSObject +@end + +@implementation MockEngine +- (FlutterViewController*)viewController { + return nil; +} +- (void)setViewController:(FlutterViewController*)viewController { + // noop +} +@end + +// The following conditional compilation defines an API 13 concept on earlier API targets so that +// a compiler compiling against API 12 or below does not blow up due to non-existent members. +#if __IPHONE_OS_VERSION_MAX_ALLOWED < 130000 + typedef enum UIAccessibilityContrast : NSInteger { + UIAccessibilityContrastUnspecified = 0, + UIAccessibilityContrastNormal = 1, + UIAccessibilityContrastHigh = 2 +} UIAccessibilityContrast; + +@interface UITraitCollection (MethodsFromNewerSDK) +- (UIAccessibilityContrast)accessibilityContrast; +@end +#endif + +@interface FlutterKeyboardManager (Tests) +@property(nonatomic, retain, readonly) + NSMutableArray>* primaryResponders; +@end + +@interface FlutterEmbedderKeyResponder (Tests) +@property(nonatomic, copy, readonly) FlutterSendKeyEvent sendEvent; +@end + +@interface FlutterViewController (Tests) + +@property(nonatomic, assign) double targetViewInsetBottom; + +- (void)surfaceUpdated:(BOOL)appeared; +- (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences; +- (void)handlePressEvent:(FlutterUIPressProxy*)press + nextAction:(void (^)())next API_AVAILABLE(ios(13.4)); +- (void)scrollEvent:(UIPanGestureRecognizer*)recognizer; +- (void)updateViewportMetrics; +- (void)onUserSettingsChanged:(NSNotification*)notification; +- (void)applicationWillTerminate:(NSNotification*)notification; +- (void)goToApplicationLifecycle:(nonnull NSString*)state; +- (void)keyboardWillChangeFrame:(NSNotification*)notification; +- (void)keyboardWillBeHidden:(NSNotification*)notification; +- (void)startKeyBoardAnimation:(NSTimeInterval)duration; +- (void)ensureViewportMetricsIsCorrect; +- (void)invalidateDisplayLink; +- (void)addInternalPlugins; +- (flutter::PointerData)generatePointerDataForFake; +@end + +@interface FlutterViewControllerTest : XCTestCase +@property(nonatomic, strong) id mockEngine; +@property(nonatomic, strong) id mockTextInputPlugin; +@property(nonatomic, strong) id messageSent; +- (void)sendMessage:(id _Nullable)message reply:(FlutterReply _Nullable)callback; +@end + +@implementation FlutterViewControllerTest + +- (void)setUp { + self.mockEngine = OCMClassMock([FlutterEngine class]); + self.mockTextInputPlugin = OCMClassMock([FlutterTextInputPlugin class]); + OCMStub([self.mockEngine textInputPlugin]).andReturn(self.mockTextInputPlugin); + self.messageSent = nil; +} + +- (void)tearDown { + // We stop mocking here to avoid retain cycles that stop + // FlutterViewControllers from deallocing. + [self.mockEngine stopMocking]; + self.mockEngine = nil; + self.mockTextInputPlugin = nil; + self.messageSent = nil; +} + +- (void)testkeyboardWillChangeFrameWillStartKeyboardAnimation { + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + + CGFloat width = UIScreen.mainScreen.bounds.size.width; + CGRect keyboardFrame = CGRectMake(0, 100, width, 400); + BOOL isLocal = YES; + NSNotification* notification = [NSNotification + notificationWithName:@"" + object:nil + userInfo:@{ + @"UIKeyboardFrameEndUserInfoKey" : [NSValue valueWithCGRect:keyboardFrame], + @"UIKeyboardAnimationDurationUserInfoKey" : [NSNumber numberWithDouble:0.25], + @"UIKeyboardIsLocalUserInfoKey" : [NSNumber numberWithBool:isLocal] + }]; + + XCTestExpectation* expectation = [self expectationWithDescription:@"update viewport"]; + OCMStub([mockEngine updateViewportMetrics:flutter::ViewportMetrics()]) + .ignoringNonObjectArgs() + .andDo(^(NSInvocation* invocation) { + [expectation fulfill]; + }); + id viewControllerMock = OCMPartialMock(viewController); + [viewControllerMock keyboardWillChangeFrame:notification]; + OCMVerify([viewControllerMock startKeyBoardAnimation:0.25]); + [self waitForExpectationsWithTimeout:5.0 handler:nil]; +} + +- (void)testEnsureBottomInsetIsZeroWhenKeyboardDismissed { + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + + FlutterViewController* viewControllerMock = OCMPartialMock(viewController); + CGRect keyboardFrame = CGRectMake(0, 0, 0, 0); + BOOL isLocal = YES; + NSNotification* fakeNotification = [NSNotification + notificationWithName:@"" + object:nil + userInfo:@{ + @"UIKeyboardFrameEndUserInfoKey" : [NSValue valueWithCGRect:keyboardFrame], + @"UIKeyboardAnimationDurationUserInfoKey" : @(0.25), + @"UIKeyboardIsLocalUserInfoKey" : @(isLocal) + }]; + + viewControllerMock.targetViewInsetBottom = 10; + [viewControllerMock keyboardWillBeHidden:fakeNotification]; + XCTAssertTrue(viewControllerMock.targetViewInsetBottom == 0); +} + +- (void)testEnsureViewportMetricsWillInvokeAndDisplayLinkWillInvalidateInViewDidDisappear { + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + id viewControllerMock = OCMPartialMock(viewController); + [viewControllerMock viewDidDisappear:YES]; + OCMVerify([viewControllerMock ensureViewportMetricsIsCorrect]); + OCMVerify([viewControllerMock invalidateDisplayLink]); +} + +- (void)testViewDidDisappearDoesntPauseEngineWhenNotTheViewController { + id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); + FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; + mockEngine.lifecycleChannel = lifecycleChannel; + FlutterViewController* viewControllerA = + [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; + FlutterViewController* viewControllerB = + [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; + id viewControllerMock = OCMPartialMock(viewControllerA); + OCMStub([viewControllerMock surfaceUpdated:NO]); + mockEngine.viewController = viewControllerB; + [viewControllerA viewDidDisappear:NO]; + OCMReject([lifecycleChannel sendMessage:@"AppLifecycleState.paused"]); + OCMReject([viewControllerMock surfaceUpdated:[OCMArg any]]); +} + +- (void)testAppWillTerminateViewDidDestroyTheEngine { + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + id viewControllerMock = OCMPartialMock(viewController); + OCMStub([viewControllerMock goToApplicationLifecycle:@"AppLifecycleState.detached"]); + OCMStub([mockEngine destroyContext]); + [viewController applicationWillTerminate:nil]; + OCMVerify([viewControllerMock goToApplicationLifecycle:@"AppLifecycleState.detached"]); + OCMVerify([mockEngine destroyContext]); +} + +- (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController { + id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); + FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; + mockEngine.lifecycleChannel = lifecycleChannel; + __weak FlutterViewController* weakViewController; + @autoreleasepool { + FlutterViewController* viewController = [[FlutterViewController alloc] + initWithEngine:mockEngine + nibName:nil + bundle:nil]; + weakViewController = viewController; + id viewControllerMock = OCMPartialMock(viewController); + OCMStub([viewControllerMock surfaceUpdated:NO]); + [viewController viewDidDisappear:NO]; + OCMVerify([lifecycleChannel sendMessage:@"AppLifecycleState.paused"]); + OCMVerify([viewControllerMock surfaceUpdated:NO]); + } + XCTAssertNil(weakViewController); +} + +- (void) + testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear + { + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + [viewController viewWillAppear:YES]; + OCMVerify([viewController onUserSettingsChanged:nil]); +} + +- (void) + testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear + { + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* viewControllerA = [[FlutterViewController alloc] + initWithEngine:mockEngine + nibName:nil + bundle:nil]; + mockEngine.viewController = nil; + FlutterViewController* viewControllerB = [[FlutterViewController alloc] + initWithEngine:mockEngine + nibName:nil + bundle:nil]; + mockEngine.viewController = nil; + mockEngine.viewController = viewControllerB; + [viewControllerA viewWillAppear:YES]; + OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]); +} + +- (void) + testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear + { + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + [viewController viewDidAppear:YES]; + OCMVerify([viewController onUserSettingsChanged:nil]); +} + +- (void) + testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear + { + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* viewControllerA = [[FlutterViewController alloc] + initWithEngine:mockEngine + nibName:nil + bundle:nil]; + mockEngine.viewController = nil; + FlutterViewController* viewControllerB = [[FlutterViewController alloc] + initWithEngine:mockEngine + nibName:nil + bundle:nil]; + mockEngine.viewController = nil; + mockEngine.viewController = viewControllerB; + [viewControllerA viewDidAppear:YES]; + OCMVerify(never(), [viewControllerA onUserSettingsChanged:nil]); +} + +- (void) + testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear + { + id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); + FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; + mockEngine.lifecycleChannel = lifecycleChannel; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + mockEngine.viewController = viewController; + [viewController viewWillDisappear:NO]; + OCMVerify([lifecycleChannel sendMessage:@"AppLifecycleState.inactive"]); +} + +- (void) + testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear + { + id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); + FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; + mockEngine.lifecycleChannel = lifecycleChannel; + FlutterViewController* viewControllerA = [[FlutterViewController alloc] + initWithEngine:mockEngine + nibName:nil + bundle:nil]; + FlutterViewController* viewControllerB = [[FlutterViewController alloc] + initWithEngine:mockEngine + nibName:nil + bundle:nil]; + mockEngine.viewController = viewControllerB; + [viewControllerA viewDidDisappear:NO]; + OCMReject([lifecycleChannel sendMessage:@"AppLifecycleState.inactive"]); +} + +- (void)testUpdateViewportMetricsDoesntInvokeEngineWhenNotTheViewController { + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* viewControllerA = [[FlutterViewController alloc] + initWithEngine:mockEngine + nibName:nil + bundle:nil]; + mockEngine.viewController = nil; + FlutterViewController* viewControllerB = [[FlutterViewController alloc] + initWithEngine:mockEngine + nibName:nil + bundle:nil]; + mockEngine.viewController = viewControllerB; + [viewControllerA updateViewportMetrics]; + flutter::ViewportMetrics viewportMetrics; + OCMVerify(never(), [mockEngine updateViewportMetrics:viewportMetrics]); +} + +- (void)testUpdateViewportMetricsDoesInvokeEngineWhenIsTheViewController { + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + mockEngine.viewController = viewController; + flutter::ViewportMetrics viewportMetrics; + OCMExpect([mockEngine updateViewportMetrics:viewportMetrics]).ignoringNonObjectArgs(); + [viewController updateViewportMetrics]; + OCMVerifyAll(mockEngine); +} + +- (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController { + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* viewControllerA = [[FlutterViewController alloc] + initWithEngine:mockEngine + nibName:nil + bundle:nil]; + mockEngine.viewController = nil; + FlutterViewController* viewControllerB = [[FlutterViewController alloc] + initWithEngine:mockEngine + nibName:nil + bundle:nil]; + mockEngine.viewController = viewControllerB; + UIView* view = viewControllerA.view; + XCTAssertNotNil(view); + OCMVerify(never(), [mockEngine attachView]); +} + +- (void)testViewDidLoadDoesInvokeEngineWhenIsTheViewController { + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + mockEngine.viewController = nil; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + mockEngine.viewController = viewController; + UIView* view = viewController.view; + XCTAssertNotNil(view); + OCMVerify([mockEngine attachView]); +} + +- (void)testInternalPluginsWeakPtrNotCrash { + FlutterSendKeyEvent sendEvent; + @autoreleasepool { + FlutterViewController* vc = [[FlutterViewController alloc] initWithProject:nil + nibName:nil + bundle:nil]; + [vc addInternalPlugins]; + FlutterKeyboardManager* keyboardManager = vc.keyboardManager; + FlutterEmbedderKeyResponder* keyPrimaryResponder = (FlutterEmbedderKeyResponder*) + [(NSArray>*)keyboardManager.primaryResponders firstObject]; + sendEvent = [keyPrimaryResponder sendEvent]; + } + + if (sendEvent) { + sendEvent({}, nil, nil); + } +} + +// Regression test for https://github.com/flutter/engine/pull/32098. +- (void)testInternalPluginsInvokeInViewDidLoad { + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + [viewController viewDidLoad]; + OCMVerify([viewController addInternalPlugins]); +} + +- (void)testBinaryMessenger { + FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine + nibName:nil + bundle:nil]; + XCTAssertNotNil(vc); + id messenger = OCMProtocolMock(@protocol(FlutterBinaryMessenger)); + OCMStub([self.mockEngine binaryMessenger]).andReturn(messenger); + XCTAssertEqual(vc.binaryMessenger, messenger); + OCMVerify([self.mockEngine binaryMessenger]); +} + +#pragma mark - Platform Brightness + +- (void)testItReportsLightPlatformBrightnessByDefault { + // Setup test. + id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); + OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); + + FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine + nibName:nil + bundle:nil]; + + // Exercise behavior under test. + [vc traitCollectionDidChange:nil]; + + // Verify behavior. + OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { + return [message[@"platformBrightness"] isEqualToString:@"light"]; + }]]); + + // Clean up mocks + [settingsChannel stopMocking]; +} + +- (void)testItReportsPlatformBrightnessWhenViewWillAppear { + // Setup test. + id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel); + FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + + // Exercise behavior under test. + [vc viewWillAppear:false]; + + // Verify behavior. + OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { + return [message[@"platformBrightness"] isEqualToString:@"light"]; + }]]); + + // Clean up mocks + [settingsChannel stopMocking]; +} + +- (void)testItReportsDarkPlatformBrightnessWhenTraitCollectionRequestsIt { + if (@available(iOS 13, *)) { + // noop + } else { + return; + } + + // Setup test. + id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); + OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); + + FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine + nibName:nil + bundle:nil]; + id mockTraitCollection = + [self fakeTraitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]; + + // We partially mock the real FlutterViewController to act as the OS and report + // the UITraitCollection of our choice. Mocking the object under test is not + // desirable, but given that the OS does not offer a DI approach to providing + // our own UITraitCollection, this seems to be the least bad option. + id partialMockVC = OCMPartialMock(realVC); + OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection); + + // Exercise behavior under test. + [partialMockVC traitCollectionDidChange:nil]; + + // Verify behavior. + OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { + return [message[@"platformBrightness"] isEqualToString:@"dark"]; + }]]); + + // Clean up mocks + [partialMockVC stopMocking]; + [settingsChannel stopMocking]; + [mockTraitCollection stopMocking]; +} + +// Creates a mocked UITraitCollection with nil values for everything except userInterfaceStyle, +// which is set to the given "style". +- (UITraitCollection*)fakeTraitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)style { + id mockTraitCollection = OCMClassMock([UITraitCollection class]); + OCMStub([mockTraitCollection userInterfaceStyle]).andReturn(style); + return mockTraitCollection; +} + +#pragma mark - Platform Contrast + +- (void)testItReportsNormalPlatformContrastByDefault { + if (@available(iOS 13, *)) { + // noop + } else { + return; + } + + // Setup test. + id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); + OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); + + FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine + nibName:nil + bundle:nil]; + + // Exercise behavior under test. + [vc traitCollectionDidChange:nil]; + + // Verify behavior. + OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { + return [message[@"platformContrast"] isEqualToString:@"normal"]; + }]]); + + // Clean up mocks + [settingsChannel stopMocking]; +} + +- (void)testItReportsPlatformContrastWhenViewWillAppear { + if (@available(iOS 13, *)) { + // noop + } else { + return; + } + FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); + [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; + + // Setup test. + id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); + OCMStub([mockEngine settingsChannel]).andReturn(settingsChannel); + FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + + // Exercise behavior under test. + [vc viewWillAppear:false]; + + // Verify behavior. + OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { + return [message[@"platformContrast"] isEqualToString:@"normal"]; + }]]); + + // Clean up mocks + [settingsChannel stopMocking]; +} + +- (void)testItReportsHighContrastWhenTraitCollectionRequestsIt { + if (@available(iOS 13, *)) { + // noop + } else { + return; + } + + // Setup test. + id settingsChannel = OCMClassMock([FlutterBasicMessageChannel class]); + OCMStub([self.mockEngine settingsChannel]).andReturn(settingsChannel); + + FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine + nibName:nil + bundle:nil]; + id mockTraitCollection = [self fakeTraitCollectionWithContrast:UIAccessibilityContrastHigh]; + + // We partially mock the real FlutterViewController to act as the OS and report + // the UITraitCollection of our choice. Mocking the object under test is not + // desirable, but given that the OS does not offer a DI approach to providing + // our own UITraitCollection, this seems to be the least bad option. + id partialMockVC = OCMPartialMock(realVC); + OCMStub([partialMockVC traitCollection]).andReturn(mockTraitCollection); + + // Exercise behavior under test. + [partialMockVC traitCollectionDidChange:mockTraitCollection]; + + // Verify behavior. + OCMVerify([settingsChannel sendMessage:[OCMArg checkWithBlock:^BOOL(id message) { + return [message[@"platformContrast"] isEqualToString:@"high"]; + }]]); + + // Clean up mocks + [partialMockVC stopMocking]; + [settingsChannel stopMocking]; + [mockTraitCollection stopMocking]; +} + +- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagNotSet { + if (@available(iOS 13, *)) { + // noop + } else { + return; + } + + // Setup test. + FlutterViewController* viewController = + [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; + id partialMockViewController = OCMPartialMock(viewController); + OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(NO); + + // Exercise behavior under test. + int32_t flags = [partialMockViewController accessibilityFlags]; + + // Verify behavior. + XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) == 0); +} + +- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagSet { + if (@available(iOS 13, *)) { + // noop + } else { + return; + } + + // Setup test. + FlutterViewController* viewController = + [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; + id partialMockViewController = OCMPartialMock(viewController); + OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(YES); + + // Exercise behavior under test. + int32_t flags = [partialMockViewController accessibilityFlags]; + + // Verify behavior. + XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) != 0); +} + +- (void)testPerformOrientationUpdateForcesOrientationChange { + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortrait]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortrait]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortrait]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortraitUpsideDown]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeLeft]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeLeft]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeLeft]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeLeft]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeLeft]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeRight]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeRight]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationLandscapeRight]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:YES + resultingOrientation:UIInterfaceOrientationPortrait]; +} + +- (void)testPerformOrientationUpdateDoesNotForceOrientationChange { + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:NO + resultingOrientation:static_cast(0)]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:NO + resultingOrientation:static_cast(0)]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:NO + resultingOrientation:static_cast(0)]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAll + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:NO + resultingOrientation:static_cast(0)]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:NO + resultingOrientation:static_cast(0)]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:NO + resultingOrientation:static_cast(0)]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskAllButUpsideDown + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:NO + resultingOrientation:static_cast(0)]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait + currentOrientation:UIInterfaceOrientationPortrait + didChangeOrientation:NO + resultingOrientation:static_cast(0)]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortraitUpsideDown + currentOrientation:UIInterfaceOrientationPortraitUpsideDown + didChangeOrientation:NO + resultingOrientation:static_cast(0)]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:NO + resultingOrientation:static_cast(0)]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscape + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:NO + resultingOrientation:static_cast(0)]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeLeft + currentOrientation:UIInterfaceOrientationLandscapeLeft + didChangeOrientation:NO + resultingOrientation:static_cast(0)]; + + [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskLandscapeRight + currentOrientation:UIInterfaceOrientationLandscapeRight + didChangeOrientation:NO + resultingOrientation:static_cast(0)]; +} + +// Perform an orientation update test that fails when the expected outcome +// for an orientation update is not met +- (void)orientationTestWithOrientationUpdate:(UIInterfaceOrientationMask)mask + currentOrientation:(UIInterfaceOrientation)currentOrientation + didChangeOrientation:(BOOL)didChange + resultingOrientation:(UIInterfaceOrientation)resultingOrientation { + id deviceMock = OCMPartialMock([UIDevice currentDevice]); + if (!didChange) { + OCMReject([deviceMock setValue:[OCMArg any] forKey:@"orientation"]); + } else { + OCMExpect([deviceMock setValue:@(resultingOrientation) forKey:@"orientation"]); + } + + FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:self.mockEngine + nibName:nil + bundle:nil]; + id mockApplication = OCMClassMock([UIApplication class]); + OCMStub([mockApplication sharedApplication]).andReturn(mockApplication); + OCMStub([mockApplication statusBarOrientation]).andReturn(currentOrientation); + + [realVC performOrientationUpdate:mask]; + OCMVerifyAll(deviceMock); + [deviceMock stopMocking]; + [mockApplication stopMocking]; +} + +// Creates a mocked UITraitCollection with nil values for everything except accessibilityContrast, +// which is set to the given "contrast". +- (UITraitCollection*)fakeTraitCollectionWithContrast:(UIAccessibilityContrast)contrast { + id mockTraitCollection = OCMClassMock([UITraitCollection class]); + OCMStub([mockTraitCollection accessibilityContrast]).andReturn(contrast); + return mockTraitCollection; +} + +- (void)testWillDeallocNotification { + XCTestExpectation* expectation = + [[XCTestExpectation alloc] initWithDescription:@"notification called"]; + id engine = [[MockEngine alloc] init]; + @autoreleasepool { + FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine + nibName:nil + bundle:nil]; + [[NSNotificationCenter defaultCenter] addObserverForName:FlutterViewControllerWillDealloc + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification* _Nonnull note) { + [expectation fulfill]; + }]; + realVC = nil; + } + [self waitForExpectations:@[ expectation ] timeout:1.0]; +} + +- (void)testReleasesKeyboardManagerOnDealloc { + __weak FlutterKeyboardManager* weakKeyboardManager = nil; + @autoreleasepool { + FlutterViewController* viewController = [[FlutterViewController alloc] init]; + + [viewController addInternalPlugins]; + weakKeyboardManager = viewController.keyboardManager; + XCTAssertNotNil(weakKeyboardManager); + [viewController deregisterNotifications]; + viewController = nil; + } + // View controller has released the keyboard manager. + XCTAssertNil(weakKeyboardManager); +} + +- (void)testDoesntLoadViewInInit { + FlutterDartProject* project = [[FlutterDartProject alloc] init]; + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; + [engine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine + nibName:nil + bundle:nil]; + XCTAssertFalse([realVC isViewLoaded], @"shouldn't have loaded since it hasn't been shown"); + engine.viewController = nil; +} + +- (void)testHideOverlay { + FlutterDartProject* project = [[FlutterDartProject alloc] init]; + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; + [engine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine + nibName:nil + bundle:nil]; + XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden, @""); + [[NSNotificationCenter defaultCenter] + postNotificationName:FlutterViewControllerHideHomeIndicator + object:nil]; + XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden, @""); + engine.viewController = nil; +} + +- (void)testHideA11yElements { + FlutterDartProject* project = [[FlutterDartProject alloc] init]; + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"foobar" project:project]; + [engine createShell:@"" libraryURI:@"" initialRoute:nil]; + FlutterViewController* realVC = [[FlutterViewController alloc] initWithEngine:engine + nibName:nil + bundle:nil]; + XCTAssertFalse(realVC.view.accessibilityElementsHidden); + [[NSNotificationCenter defaultCenter] + postNotificationName:UIApplicationWillResignActiveNotification + object:nil]; + XCTAssertTrue(realVC.view.accessibilityElementsHidden); + [[NSNotificationCenter defaultCenter] + postNotificationName:UIApplicationDidBecomeActiveNotification + object:nil]; + XCTAssertFalse(realVC.view.accessibilityElementsHidden); + engine.viewController = nil; +} + +- (void)testNotifyLowMemory { + FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + id viewControllerMock = OCMPartialMock(viewController); + OCMStub([viewControllerMock surfaceUpdated:NO]); + [viewController beginAppearanceTransition:NO animated:NO]; + [viewController endAppearanceTransition]; + XCTAssertTrue(mockEngine.didCallNotifyLowMemory); +} + +- (void)sendMessage:(id _Nullable)message reply:(FlutterReply _Nullable)callback { + NSMutableDictionary* replyMessage = [@{ + @"handled" : @YES, + } mutableCopy]; + // Response is async, so we have to post it to the run loop instead of calling + // it directly. + self.messageSent = message; + CFRunLoopPerformBlock(CFRunLoopGetCurrent(), fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode, + ^() { + callback(replyMessage); + }); +} + +- (void)testValidKeyUpEvent API_AVAILABLE(ios(13.4)) { + if (@available(iOS 13.4, *)) { + // noop + } else { + return; + } + FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; + mockEngine.keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); + OCMStub([mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) + .andCall(self, @selector(sendMessage:reply:)); + OCMStub([self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES); + mockEngine.textInputPlugin = self.mockTextInputPlugin; + + FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + + // Allocate the keyboard manager in the view controller by adding the internal + // plugins. + [vc addInternalPlugins]; + + [vc handlePressEvent:keyUpEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0) + nextAction:^(){ + }]; + + XCTAssert(self.messageSent != nil); + XCTAssert([self.messageSent[@"keymap"] isEqualToString:@"ios"]); + XCTAssert([self.messageSent[@"type"] isEqualToString:@"keyup"]); + XCTAssert([self.messageSent[@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]); + XCTAssert([self.messageSent[@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]); + XCTAssert([self.messageSent[@"characters"] isEqualToString:@""]); + XCTAssert([self.messageSent[@"charactersIgnoringModifiers"] isEqualToString:@""]); + [vc deregisterNotifications]; +} + +- (void)testValidKeyDownEvent API_AVAILABLE(ios(13.4)) { + if (@available(iOS 13.4, *)) { + // noop + } else { + return; + } + + FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; + mockEngine.keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); + OCMStub([mockEngine.keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) + .andCall(self, @selector(sendMessage:reply:)); + OCMStub([self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES); + mockEngine.textInputPlugin = self.mockTextInputPlugin; + + __strong FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:mockEngine + nibName:nil + bundle:nil]; + // Allocate the keyboard manager in the view controller by adding the internal + // plugins. + [vc addInternalPlugins]; + + [vc handlePressEvent:keyDownEvent(UIKeyboardHIDUsageKeyboardA, UIKeyModifierShift, 123.0f, "A", + "a") + nextAction:^(){ + }]; + + XCTAssert(self.messageSent != nil); + XCTAssert([self.messageSent[@"keymap"] isEqualToString:@"ios"]); + XCTAssert([self.messageSent[@"type"] isEqualToString:@"keydown"]); + XCTAssert([self.messageSent[@"keyCode"] isEqualToNumber:[NSNumber numberWithInt:4]]); + XCTAssert([self.messageSent[@"modifiers"] isEqualToNumber:[NSNumber numberWithInt:0]]); + XCTAssert([self.messageSent[@"characters"] isEqualToString:@"A"]); + XCTAssert([self.messageSent[@"charactersIgnoringModifiers"] isEqualToString:@"a"]); + [vc deregisterNotifications]; + vc = nil; +} + +- (void)testIgnoredKeyEvents API_AVAILABLE(ios(13.4)) { + if (@available(iOS 13.4, *)) { + // noop + } else { + return; + } + id keyEventChannel = OCMClassMock([FlutterBasicMessageChannel class]); + OCMStub([keyEventChannel sendMessage:[OCMArg any] reply:[OCMArg any]]) + .andCall(self, @selector(sendMessage:reply:)); + OCMStub([self.mockTextInputPlugin handlePress:[OCMArg any]]).andReturn(YES); + OCMStub([self.mockEngine keyEventChannel]).andReturn(keyEventChannel); + + FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine + nibName:nil + bundle:nil]; + + // Allocate the keyboard manager in the view controller by adding the internal + // plugins. + [vc addInternalPlugins]; + + [vc handlePressEvent:keyEventWithPhase(UIPressPhaseStationary, UIKeyboardHIDUsageKeyboardA, + UIKeyModifierShift, 123.0) + nextAction:^(){ + }]; + [vc handlePressEvent:keyEventWithPhase(UIPressPhaseCancelled, UIKeyboardHIDUsageKeyboardA, + UIKeyModifierShift, 123.0) + nextAction:^(){ + }]; + [vc handlePressEvent:keyEventWithPhase(UIPressPhaseChanged, UIKeyboardHIDUsageKeyboardA, + UIKeyModifierShift, 123.0) + nextAction:^(){ + }]; + + XCTAssert(self.messageSent == nil); + OCMVerify(never(), [keyEventChannel sendMessage:[OCMArg any]]); + [vc deregisterNotifications]; +} + +- (void)testPanGestureRecognizer API_AVAILABLE(ios(13.4)) { + if (@available(iOS 13.4, *)) { + // noop + } else { + return; + } + + FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine + nibName:nil + bundle:nil]; + XCTAssertNotNil(vc); + UIView* view = vc.view; + XCTAssertNotNil(view); + NSArray* gestureRecognizers = view.gestureRecognizers; + XCTAssertNotNil(gestureRecognizers); + + BOOL found = NO; + for (id gesture in gestureRecognizers) { + if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) { + found = YES; + break; + } + } + XCTAssertTrue(found); +} + +- (void)testMouseSupport API_AVAILABLE(ios(13.4)) { + if (@available(iOS 13.4, *)) { + // noop + } else { + return; + } + + FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine + nibName:nil + bundle:nil]; + XCTAssertNotNil(vc); + + id mockPanGestureRecognizer = OCMClassMock([UIPanGestureRecognizer class]); + XCTAssertNotNil(mockPanGestureRecognizer); + + [vc scrollEvent:mockPanGestureRecognizer]; + + [[[self.mockEngine verify] ignoringNonObjectArgs] + dispatchPointerDataPacket:std::make_unique(0)]; +} + +- (void)testFakeEventTimeStamp { + FlutterViewController* vc = [[FlutterViewController alloc] initWithEngine:self.mockEngine + nibName:nil + bundle:nil]; + XCTAssertNotNil(vc); + + flutter::PointerData pointer_data = [vc generatePointerDataForFake]; + int64_t current_micros = [[NSProcessInfo processInfo] systemUptime] * 1000 * 1000; + int64_t interval_micros = current_micros - pointer_data.time_stamp; + const int64_t tolerance_millis = 2; + XCTAssertTrue(interval_micros / 1000 < tolerance_millis, + @"PointerData.time_stamp should be equal to NSProcessInfo.systemUptime"); +} +@end From 5235d2b82464948e2ef020cab0284244d416fc82 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Mon, 4 Apr 2022 16:23:47 -0700 Subject: [PATCH 7/7] cleanup --- .../Source/FlutterViewControllerTest.mm | 60 +++++++------------ 1 file changed, 21 insertions(+), 39 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm index 14de18960a470..24d194fa86971 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm @@ -18,7 +18,7 @@ #import "flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h" #import "flutter/shell/platform/embedder/embedder.h" - FLUTTER_ASSERT_ARC +FLUTTER_ASSERT_ARC @interface FlutterEngine () - (FlutterTextInputPlugin*)textInputPlugin; @@ -79,7 +79,7 @@ @interface FlutterEngine (TestLowMemory) - (void)notifyLowMemory; @end - extern NSNotificationName const FlutterViewControllerWillDealloc; +extern NSNotificationName const FlutterViewControllerWillDealloc; /// A simple mock class for FlutterEngine. /// @@ -104,7 +104,7 @@ - (void)setViewController:(FlutterViewController*)viewController { // The following conditional compilation defines an API 13 concept on earlier API targets so that // a compiler compiling against API 12 or below does not blow up due to non-existent members. #if __IPHONE_OS_VERSION_MAX_ALLOWED < 130000 - typedef enum UIAccessibilityContrast : NSInteger { +typedef enum UIAccessibilityContrast : NSInteger { UIAccessibilityContrastUnspecified = 0, UIAccessibilityContrastNormal = 1, UIAccessibilityContrastHigh = 2 @@ -274,8 +274,7 @@ - (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController { mockEngine.lifecycleChannel = lifecycleChannel; __weak FlutterViewController* weakViewController; @autoreleasepool { - FlutterViewController* viewController = [[FlutterViewController alloc] - initWithEngine:mockEngine + FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine nibName:nil bundle:nil]; weakViewController = viewController; @@ -289,8 +288,7 @@ - (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController { } - (void) - testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear - { + testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillAppear { FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine @@ -301,17 +299,14 @@ - (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController { } - (void) - testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear - { + testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillAppear { FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewControllerA = [[FlutterViewController alloc] - initWithEngine:mockEngine + FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine nibName:nil bundle:nil]; mockEngine.viewController = nil; - FlutterViewController* viewControllerB = [[FlutterViewController alloc] - initWithEngine:mockEngine + FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine nibName:nil bundle:nil]; mockEngine.viewController = nil; @@ -321,8 +316,7 @@ - (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController { } - (void) - testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear - { + testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewDidAppear { FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:mockEngine @@ -333,17 +327,14 @@ - (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController { } - (void) - testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear - { + testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewDidAppear { FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewControllerA = [[FlutterViewController alloc] - initWithEngine:mockEngine + FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine nibName:nil bundle:nil]; mockEngine.viewController = nil; - FlutterViewController* viewControllerB = [[FlutterViewController alloc] - initWithEngine:mockEngine + FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine nibName:nil bundle:nil]; mockEngine.viewController = nil; @@ -353,8 +344,7 @@ - (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController { } - (void) - testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear - { + testEngineConfigSyncMethodWillExecuteWhenViewControllerInEngineIsCurrentViewControllerInViewWillDisappear { id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; mockEngine.lifecycleChannel = lifecycleChannel; @@ -367,17 +357,14 @@ - (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController { } - (void) - testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear - { + testEngineConfigSyncMethodWillNotExecuteWhenViewControllerInEngineIsNotCurrentViewControllerInViewWillDisappear { id lifecycleChannel = OCMClassMock([FlutterBasicMessageChannel class]); FlutterEnginePartialMock* mockEngine = [[FlutterEnginePartialMock alloc] init]; mockEngine.lifecycleChannel = lifecycleChannel; - FlutterViewController* viewControllerA = [[FlutterViewController alloc] - initWithEngine:mockEngine + FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine nibName:nil bundle:nil]; - FlutterViewController* viewControllerB = [[FlutterViewController alloc] - initWithEngine:mockEngine + FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine nibName:nil bundle:nil]; mockEngine.viewController = viewControllerB; @@ -388,13 +375,11 @@ - (void)testViewDidDisappearDoesPauseEngineWhenIsTheViewController { - (void)testUpdateViewportMetricsDoesntInvokeEngineWhenNotTheViewController { FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewControllerA = [[FlutterViewController alloc] - initWithEngine:mockEngine + FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine nibName:nil bundle:nil]; mockEngine.viewController = nil; - FlutterViewController* viewControllerB = [[FlutterViewController alloc] - initWithEngine:mockEngine + FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine nibName:nil bundle:nil]; mockEngine.viewController = viewControllerB; @@ -419,13 +404,11 @@ - (void)testUpdateViewportMetricsDoesInvokeEngineWhenIsTheViewController { - (void)testViewDidLoadDoesntInvokeEngineWhenNotTheViewController { FlutterEngine* mockEngine = OCMPartialMock([[FlutterEngine alloc] init]); [mockEngine createShell:@"" libraryURI:@"" initialRoute:nil]; - FlutterViewController* viewControllerA = [[FlutterViewController alloc] - initWithEngine:mockEngine + FlutterViewController* viewControllerA = [[FlutterViewController alloc] initWithEngine:mockEngine nibName:nil bundle:nil]; mockEngine.viewController = nil; - FlutterViewController* viewControllerB = [[FlutterViewController alloc] - initWithEngine:mockEngine + FlutterViewController* viewControllerB = [[FlutterViewController alloc] initWithEngine:mockEngine nibName:nil bundle:nil]; mockEngine.viewController = viewControllerB; @@ -943,8 +926,7 @@ - (void)testHideOverlay { nibName:nil bundle:nil]; XCTAssertFalse(realVC.prefersHomeIndicatorAutoHidden, @""); - [[NSNotificationCenter defaultCenter] - postNotificationName:FlutterViewControllerHideHomeIndicator + [[NSNotificationCenter defaultCenter] postNotificationName:FlutterViewControllerHideHomeIndicator object:nil]; XCTAssertTrue(realVC.prefersHomeIndicatorAutoHidden, @""); engine.viewController = nil;