23
23
24
24
using namespace flutter ::testing;
25
25
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
-
63
26
namespace {
64
27
65
28
typedef void (^KeyCallbackSetter)(FlutterUIPressProxy* press, FlutterAsyncKeyCallback callback)
@@ -68,52 +31,28 @@ typedef void (^KeyCallbackSetter)(FlutterUIPressProxy* press, FlutterAsyncKeyCal
68
31
69
32
} // namespace
70
33
34
+ // These tests were designed to run on iOS 13.4 or later.
35
+ API_AVAILABLE (ios(13.4 ))
71
36
@interface FlutterKeyboardManagerTest : XCTestCase
72
- @property (nonatomic , strong ) id mockEngine;
73
- - (FlutterViewController*)mockOwnerWithPressesBeginOnlyNext API_AVAILABLE(ios(13.4 ));
74
37
@end
75
38
76
39
@implementation FlutterKeyboardManagerTest
77
40
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 {
109
42
id <FlutterKeyPrimaryResponder> mock =
110
43
OCMStrictProtocolMock (@protocol (FlutterKeyPrimaryResponder));
111
44
OCMStub ([mock handlePress: [OCMArg any ] callback: [OCMArg any ]])
112
45
.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;
117
56
CFRunLoopPerformBlock (CFRunLoopGetCurrent (),
118
57
fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode , ^() {
119
58
callbackSetter (press, callback);
@@ -122,8 +61,7 @@ - (id)checkKeyDownEvent:(UIKeyboardHIDUsage)keyCode API_AVAILABLE(ios(13.4)) {
122
61
return mock;
123
62
}
124
63
125
- - (id <FlutterKeySecondaryResponder>)mockSecondaryResponder : (BoolGetter)resultGetter
126
- API_AVAILABLE(ios(13.4 )) {
64
+ - (id <FlutterKeySecondaryResponder>)mockSecondaryResponder:(BoolGetter)resultGetter {
127
65
id <FlutterKeySecondaryResponder> mock =
128
66
OCMStrictProtocolMock (@protocol (FlutterKeySecondaryResponder));
129
67
OCMStub ([mock handlePress: [OCMArg any ]]).andDo ((^(NSInvocation * invocation) {
@@ -133,32 +71,27 @@ - (id)checkKeyDownEvent:(UIKeyboardHIDUsage)keyCode API_AVAILABLE(ios(13.4)) {
133
71
return mock;
134
72
}
135
73
136
- - (FlutterViewController*) mockOwnerWithPressesBeginOnlyNext API_AVAILABLE(ios( 13.4 )) {
74
+ - (void )testNextResponderShouldThrowOnPressesEnded {
137
75
// The nextResponder is a strict mock and hasn't stubbed pressesEnded.
138
76
// An error will be thrown on pressesEnded.
139
77
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] );
141
79
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 ];
144
84
FlutterViewController* owner = OCMPartialMock (viewController);
145
85
OCMStub ([owner nextResponder ]).andReturn (nextResponder);
146
- return owner;
147
- }
148
86
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 ];
159
92
}
160
93
161
- - (void )testSinglePrimaryResponder API_AVAILABLE(ios( 13.4 )) {
94
+ - (void )testSinglePrimaryResponder {
162
95
FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc ] init ];
163
96
__block BOOL primaryResponse = FALSE ;
164
97
__block int callbackCount = 0 ;
@@ -190,7 +123,7 @@ - (void)testSinglePrimaryResponder API_AVAILABLE(ios(13.4)) {
190
123
XCTAssertFalse (completeHandled);
191
124
}
192
125
193
- - (void )testDoublePrimaryResponder API_AVAILABLE(ios( 13.4 )) {
126
+ - (void )testDoublePrimaryResponder {
194
127
FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc ] init ];
195
128
196
129
__block BOOL callback1Response = FALSE ;
@@ -253,7 +186,7 @@ - (void)testDoublePrimaryResponder API_AVAILABLE(ios(13.4)) {
253
186
XCTAssertFalse (somethingWasHandled);
254
187
}
255
188
256
- - (void )testSingleSecondaryResponder API_AVAILABLE(ios( 13.4 )) {
189
+ - (void )testSingleSecondaryResponder {
257
190
FlutterKeyboardManager* manager = [[FlutterKeyboardManager alloc ] init ];
258
191
259
192
__block BOOL primaryResponse = FALSE ;
@@ -308,7 +241,7 @@ - (void)testSingleSecondaryResponder API_AVAILABLE(ios(13.4)) {
308
241
XCTAssertFalse (completeHandled);
309
242
}
310
243
311
- - (void )testEventsProcessedSequentially API_AVAILABLE(ios( 13.4 )) {
244
+ - (void )testEventsProcessedSequentially {
312
245
constexpr UIKeyboardHIDUsage keyId1 = (UIKeyboardHIDUsage)0x50 ;
313
246
constexpr UIKeyboardHIDUsage keyId2 = (UIKeyboardHIDUsage)0x51 ;
314
247
FlutterUIPressProxy* event1 = keyDownEvent (keyId1);
0 commit comments