2323
2424using namespace flutter ::testing;
2525
26- // / Sometimes we have to use a custom mock to avoid retain cycles in ocmock.
27- @interface FlutterEnginePartialMock : FlutterEngine
28- @property (nonatomic , strong ) FlutterBasicMessageChannel* lifecycleChannel;
29- @property (nonatomic , weak ) FlutterViewController* viewController;
30- @property (nonatomic , assign ) BOOL didCallNotifyLowMemory;
31- @end
32-
33- @interface FlutterEngine ()
34- - (BOOL )createShell : (NSString *)entrypoint
35- libraryURI : (NSString *)libraryURI
36- initialRoute : (NSString *)initialRoute ;
37- - (void )dispatchPointerDataPacket : (std::unique_ptr<flutter::PointerDataPacket>)packet ;
38- @end
39-
40- @interface FlutterEngine (TestLowMemory)
41- - (void )notifyLowMemory ;
42- @end
43-
44- extern NSNotificationName const FlutterViewControllerWillDealloc;
45-
46- // / A simple mock class for FlutterEngine.
47- // /
48- // / OCMockClass can't be used for FlutterEngine sometimes because OCMock retains arguments to
49- // / invocations and since the init for FlutterViewController calls a method on the
50- // / FlutterEngine it creates a retain cycle that stops us from testing behaviors related to
51- // / deleting FlutterViewControllers.
52- @interface MockEngine : NSObject
53- @end
54-
55- @interface FlutterKeyboardManagerUnittestsObjC : NSObject
56- - (bool )nextResponderShouldThrowOnPressesEnded ;
57- - (bool )singlePrimaryResponder ;
58- - (bool )doublePrimaryResponder ;
59- - (bool )singleSecondaryResponder ;
60- - (bool )emptyNextResponder ;
61- @end
62-
6326namespace {
6427
6528typedef void (^KeyCallbackSetter)(FlutterUIPressProxy* press, FlutterAsyncKeyCallback callback)
@@ -68,52 +31,28 @@ typedef void (^KeyCallbackSetter)(FlutterUIPressProxy* press, FlutterAsyncKeyCal
6831
6932} // namespace
7033
34+ // These tests were designed to run on iOS 13.4 or later.
35+ API_AVAILABLE (ios(13.4 ))
7136@interface FlutterKeyboardManagerTest : XCTestCase
72- @property (nonatomic , strong ) id mockEngine;
73- - (FlutterViewController*)mockOwnerWithPressesBeginOnlyNext API_AVAILABLE(ios(13.4 ));
7437@end
7538
7639@implementation FlutterKeyboardManagerTest
7740
78- - (void )setUp {
79- // All of these tests were designed to run on iOS 13.4 or later.
80- if (@available (iOS 13.4 , *)) {
81- } else {
82- XCTSkip (@" Required API not present for test." );
83- }
84-
85- [super setUp ];
86- self.mockEngine = OCMClassMock ([FlutterEngine class ]);
87- }
88-
89- - (void )tearDown {
90- // We stop mocking here to avoid retain cycles that stop
91- // FlutterViewControllers from deallocing.
92- [self .mockEngine stopMocking ];
93- self.mockEngine = nil ;
94- [super tearDown ];
95- }
96-
97- - (id )checkKeyDownEvent : (UIKeyboardHIDUsage)keyCode API_AVAILABLE(ios(13.4 )) {
98- return [OCMArg checkWithBlock: ^BOOL (id value) {
99- if (![value isKindOfClass: [FlutterUIPressProxy class ]]) {
100- return NO ;
101- }
102- FlutterUIPressProxy* press = value;
103- return press.key .keyCode == keyCode;
104- }];
105- }
106-
107- - (id <FlutterKeyPrimaryResponder>)mockPrimaryResponder : (KeyCallbackSetter)callbackSetter
108- API_AVAILABLE(ios(13.4 )) {
41+ - (id <FlutterKeyPrimaryResponder>)mockPrimaryResponder:(KeyCallbackSetter)callbackSetter {
10942 id <FlutterKeyPrimaryResponder> mock =
11043 OCMStrictProtocolMock (@protocol (FlutterKeyPrimaryResponder));
11144 OCMStub ([mock handlePress: [OCMArg any ] callback: [OCMArg any ]])
11245 .andDo ((^(NSInvocation * invocation) {
113- FlutterUIPressProxy* press;
114- FlutterAsyncKeyCallback callback;
115- [invocation getArgument: &press atIndex: 2 ];
116- [invocation getArgument: &callback atIndex: 3 ];
46+ __unsafe_unretained FlutterUIPressProxy* pressUnsafe;
47+ __unsafe_unretained FlutterAsyncKeyCallback callbackUnsafe;
48+
49+ [invocation getArgument: &pressUnsafe atIndex: 2 ];
50+ [invocation getArgument: &callbackUnsafe atIndex: 3 ];
51+
52+ // Retain the unretained parameters so they can
53+ // be run in the perform block when this invocation goes out of scope.
54+ FlutterUIPressProxy* press = pressUnsafe;
55+ FlutterAsyncKeyCallback callback = callbackUnsafe;
11756 CFRunLoopPerformBlock (CFRunLoopGetCurrent (),
11857 fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode , ^() {
11958 callbackSetter (press, callback);
@@ -122,8 +61,7 @@ - (id)checkKeyDownEvent:(UIKeyboardHIDUsage)keyCode API_AVAILABLE(ios(13.4)) {
12261 return mock;
12362}
12463
125- - (id <FlutterKeySecondaryResponder>)mockSecondaryResponder : (BoolGetter)resultGetter
126- API_AVAILABLE(ios(13.4 )) {
64+ - (id <FlutterKeySecondaryResponder>)mockSecondaryResponder:(BoolGetter)resultGetter {
12765 id <FlutterKeySecondaryResponder> mock =
12866 OCMStrictProtocolMock (@protocol (FlutterKeySecondaryResponder));
12967 OCMStub ([mock handlePress: [OCMArg any ]]).andDo ((^(NSInvocation * invocation) {
@@ -133,32 +71,27 @@ - (id)checkKeyDownEvent:(UIKeyboardHIDUsage)keyCode API_AVAILABLE(ios(13.4)) {
13371 return mock;
13472}
13573
136- - (FlutterViewController*) mockOwnerWithPressesBeginOnlyNext API_AVAILABLE(ios( 13.4 )) {
74+ - (void )testNextResponderShouldThrowOnPressesEnded {
13775 // The nextResponder is a strict mock and hasn't stubbed pressesEnded.
13876 // An error will be thrown on pressesEnded.
13977 UIResponder* nextResponder = OCMStrictClassMock ([UIResponder class ]);
140- OCMStub ([nextResponder pressesBegan: [OCMArg any ] withEvent: [OCMArg any ]]). andDo ( nil );
78+ OCMStub ([nextResponder pressesBegan: OCMOCK_ANY withEvent: OCMOCK_ANY] );
14179
142- FlutterViewController* viewController =
143- [[FlutterViewController alloc ] initWithEngine: self .mockEngine nibName: nil bundle: nil ];
80+ id mockEngine = OCMClassMock ([FlutterEngine class ]);
81+ FlutterViewController* viewController = [[FlutterViewController alloc ] initWithEngine: mockEngine
82+ nibName: nil
83+ bundle: nil ];
14484 FlutterViewController* owner = OCMPartialMock (viewController);
14585 OCMStub ([owner nextResponder ]).andReturn (nextResponder);
146- return owner;
147- }
14886
149- // Verify that the nextResponder returned from mockOwnerWithPressesBeginOnlyNext()
150- // throws exception when pressesEnded is called.
151- - (bool )testNextResponderShouldThrowOnPressesEnded API_AVAILABLE(ios(13.4 )) {
152- FlutterViewController* owner = [self mockOwnerWithPressesBeginOnlyNext ];
153- @try {
154- [owner.nextResponder pressesEnded: [NSSet init ] withEvent: [[UIPressesEvent alloc ] init ]];
155- return false ;
156- } @catch (...) {
157- return true ;
158- }
87+ XCTAssertThrowsSpecificNamed ([owner.nextResponder pressesEnded: [[NSSet alloc ] init ]
88+ withEvent: [[UIPressesEvent alloc ] init ]],
89+ NSException , NSInternalInconsistencyException );
90+
91+ [mockEngine stopMocking ];
15992}
16093
161- - (void )testSinglePrimaryResponder API_AVAILABLE(ios( 13.4 )) {
94+ - (void )testSinglePrimaryResponder {
16295 FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc ] init ];
16396 __block BOOL primaryResponse = FALSE ;
16497 __block int callbackCount = 0 ;
@@ -190,7 +123,7 @@ - (void)testSinglePrimaryResponder API_AVAILABLE(ios(13.4)) {
190123 XCTAssertFalse (completeHandled);
191124}
192125
193- - (void )testDoublePrimaryResponder API_AVAILABLE(ios( 13.4 )) {
126+ - (void )testDoublePrimaryResponder {
194127 FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc ] init ];
195128
196129 __block BOOL callback1Response = FALSE ;
@@ -253,7 +186,7 @@ - (void)testDoublePrimaryResponder API_AVAILABLE(ios(13.4)) {
253186 XCTAssertFalse (somethingWasHandled);
254187}
255188
256- - (void )testSingleSecondaryResponder API_AVAILABLE(ios( 13.4 )) {
189+ - (void )testSingleSecondaryResponder {
257190 FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc ] init ];
258191
259192 __block BOOL primaryResponse = FALSE ;
@@ -308,7 +241,7 @@ - (void)testSingleSecondaryResponder API_AVAILABLE(ios(13.4)) {
308241 XCTAssertFalse (completeHandled);
309242}
310243
311- - (void )testEventsProcessedSequentially API_AVAILABLE(ios( 13.4 )) {
244+ - (void )testEventsProcessedSequentially {
312245 constexpr UIKeyboardHIDUsage keyId1 = (UIKeyboardHIDUsage)0x50 ;
313246 constexpr UIKeyboardHIDUsage keyId2 = (UIKeyboardHIDUsage)0x51 ;
314247 FlutterUIPressProxy* event1 = keyDownEvent (keyId1);
0 commit comments