5 #import <Foundation/Foundation.h>
6 #import <OCMock/OCMock.h>
7 #import <UIKit/UIKit.h>
8 #import <XCTest/XCTest.h>
9 #include <_types/_uint32_t.h>
11 #include "flutter/fml/platform/darwin/message_loop_darwin.h"
30 @property(nonatomic, assign) BOOL didCallNotifyLowMemory;
34 - (BOOL)createShell:(NSString*)entrypoint
35 libraryURI:(NSString*)libraryURI
36 initialRoute:(NSString*)initialRoute;
37 - (void)dispatchPointerDataPacket:(std::unique_ptr<
flutter::PointerDataPacket>)packet;
67 typedef BOOL (^BoolGetter)();
72 @property(nonatomic, strong)
id mockEngine;
80 if (@available(iOS 13.4, *)) {
82 XCTSkip(
@"Required API not present for test.");
92 [
self.mockEngine stopMocking];
93 self.mockEngine = nil;
97 - (id)checkKeyDownEvent:(UIKeyboardHIDUsage)keyCode
API_AVAILABLE(ios(13.4)) {
98 return [OCMArg checkWithBlock:^BOOL(id value) {
102 FlutterUIPressProxy* press = value;
103 return press.key.keyCode == keyCode;
107 - (id<FlutterKeyPrimaryResponder>)mockPrimaryResponder:(KeyCallbackSetter)callbackSetter
109 id<FlutterKeyPrimaryResponder> mock =
111 OCMStub([mock handlePress:[OCMArg any] callback:[OCMArg any]])
112 .andDo((^(NSInvocation* invocation) {
115 [invocation getArgument:&press atIndex:2];
116 [invocation getArgument:&callback atIndex:3];
117 CFRunLoopPerformBlock(CFRunLoopGetCurrent(),
118 fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode, ^() {
119 callbackSetter(press, callback);
125 - (id<FlutterKeySecondaryResponder>)mockSecondaryResponder:(BoolGetter)resultGetter
127 id<FlutterKeySecondaryResponder> mock =
129 OCMStub([mock handlePress:[OCMArg any]]).andDo((^(NSInvocation* invocation) {
130 BOOL result = resultGetter();
131 [invocation setReturnValue:&result];
139 UIResponder* nextResponder = OCMStrictClassMock([UIResponder
class]);
140 OCMStub([nextResponder pressesBegan:[OCMArg any] withEvent:[OCMArg any]]).andDo(nil);
145 OCMStub([owner nextResponder]).andReturn(nextResponder);
151 - (bool)testNextResponderShouldThrowOnPressesEnded
API_AVAILABLE(ios(13.4)) {
154 [owner.nextResponder pressesEnded:[NSSet init] withEvent:[[UIPressesEvent alloc] init]];
161 - (void)testSinglePrimaryResponder
API_AVAILABLE(ios(13.4)) {
163 __block BOOL primaryResponse = FALSE;
164 __block
int callbackCount = 0;
166 FlutterAsyncKeyCallback callback) {
168 callback(primaryResponse);
170 constexpr UIKeyboardHIDUsage keyId = (UIKeyboardHIDUsage)0x50;
172 __block
bool completeHandled =
true;
173 primaryResponse = TRUE;
176 completeHandled = false;
178 XCTAssertEqual(callbackCount, 1);
179 XCTAssertTrue(completeHandled);
180 completeHandled =
true;
184 primaryResponse = FALSE;
187 completeHandled = false;
189 XCTAssertEqual(callbackCount, 1);
190 XCTAssertFalse(completeHandled);
193 - (void)testDoublePrimaryResponder
API_AVAILABLE(ios(13.4)) {
196 __block BOOL callback1Response = FALSE;
197 __block
int callback1Count = 0;
199 FlutterAsyncKeyCallback callback) {
201 callback(callback1Response);
204 __block BOOL callback2Response = FALSE;
205 __block
int callback2Count = 0;
207 FlutterAsyncKeyCallback callback) {
209 callback(callback2Response);
213 __block
bool somethingWasHandled =
true;
214 constexpr UIKeyboardHIDUsage keyId = (UIKeyboardHIDUsage)0x50;
215 callback1Response = TRUE;
216 callback2Response = TRUE;
219 somethingWasHandled = false;
221 XCTAssertEqual(callback1Count, 1);
222 XCTAssertEqual(callback2Count, 1);
223 XCTAssertTrue(somethingWasHandled);
225 somethingWasHandled =
true;
230 callback1Response = TRUE;
231 callback2Response = FALSE;
234 somethingWasHandled = false;
236 XCTAssertEqual(callback1Count, 1);
237 XCTAssertEqual(callback2Count, 1);
238 XCTAssertTrue(somethingWasHandled);
240 somethingWasHandled =
true;
245 callback1Response = FALSE;
246 callback2Response = FALSE;
249 somethingWasHandled = false;
251 XCTAssertEqual(callback1Count, 1);
252 XCTAssertEqual(callback2Count, 1);
253 XCTAssertFalse(somethingWasHandled);
256 - (void)testSingleSecondaryResponder
API_AVAILABLE(ios(13.4)) {
259 __block BOOL primaryResponse = FALSE;
260 __block
int callbackCount = 0;
262 FlutterAsyncKeyCallback callback) {
264 callback(primaryResponse);
267 __block BOOL secondaryResponse;
269 return secondaryResponse;
274 constexpr UIKeyboardHIDUsage keyId = (UIKeyboardHIDUsage)0x50;
275 secondaryResponse = FALSE;
276 primaryResponse = TRUE;
277 __block
bool completeHandled =
true;
280 completeHandled = false;
282 XCTAssertEqual(callbackCount, 1);
283 XCTAssertTrue(completeHandled);
284 completeHandled =
true;
289 secondaryResponse = TRUE;
290 primaryResponse = FALSE;
293 completeHandled = false;
295 XCTAssertEqual(callbackCount, 1);
296 XCTAssertTrue(completeHandled);
297 completeHandled =
true;
301 secondaryResponse = FALSE;
302 primaryResponse = FALSE;
305 completeHandled = false;
307 XCTAssertEqual(callbackCount, 1);
308 XCTAssertFalse(completeHandled);
311 - (void)testEventsProcessedSequentially
API_AVAILABLE(ios(13.4)) {
312 constexpr UIKeyboardHIDUsage keyId1 = (UIKeyboardHIDUsage)0x50;
313 constexpr UIKeyboardHIDUsage keyId2 = (UIKeyboardHIDUsage)0x51;
318 __block
bool key1Handled =
true;
319 __block
bool key2Handled =
true;
323 FlutterAsyncKeyCallback callback) {
324 if (press == event1) {
325 key1Callback = callback;
326 } else if (press == event2) {
327 key2Callback = callback;
332 CFRunLoopTimerRef timer0 = CFRunLoopTimerCreateWithHandler(
333 kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), 0, 0, 0, ^(CFRunLoopTimerRef timerRef) {
339 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer0, kCFRunLoopCommonModes);
340 CFRunLoopTimerRef timer1 = CFRunLoopTimerCreateWithHandler(
341 kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + 1, 0, 0, 0, ^(CFRunLoopTimerRef timerRef) {
343 XCTAssertFalse(key1Handled);
349 CFRunLoopStop(CFRunLoopGetCurrent());
351 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer1, kCFRunLoopCommonModes);
355 CFRunLoopTimerRef timer2 = CFRunLoopTimerCreateWithHandler(
356 kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + 2, 0, 0, 0, ^(CFRunLoopTimerRef timerRef) {
358 XCTAssertTrue(key1Callback != nil);
359 XCTAssertTrue(key2Callback == nil);
362 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer2,
363 fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode);
364 CFRunLoopTimerRef timer3 = CFRunLoopTimerCreateWithHandler(
365 kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + 3, 0, 0, 0, ^(CFRunLoopTimerRef timerRef) {
367 XCTAssertTrue(key1Callback != nil);
368 XCTAssertTrue(key2Callback != nil);
371 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer3,
372 fml::MessageLoopDarwin::kMessageLoopCFRunLoopMode);
376 XCTAssertFalse(key2Handled);
377 XCTAssertFalse(key1Handled);