5 #import <Foundation/Foundation.h>
6 #import <OCMock/OCMock.h>
7 #import <XCTest/XCTest.h>
8 #include <_types/_uint64_t.h>
14 #include "flutter/shell/platform/embedder/embedder.h"
15 #include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
17 using namespace flutter::testing::keycodes;
22 #define XCTAssertStrEqual(value, expected) \
23 XCTAssertTrue(strcmp(value, expected) == 0, \
24 @"String \"%s\" not equal to the expected value of \"%s\"", value, expected)
28 @property(nonatomic) FlutterKeyEvent* data;
29 @property(nonatomic) FlutterKeyEventCallback callback;
30 @property(nonatomic, nullable)
void* userData;
34 - (instancetype)initWithEvent:(const FlutterKeyEvent*)event
35 callback:(nullable FlutterKeyEventCallback)callback
36 userData:(nullable
void*)userData {
38 _data =
new FlutterKeyEvent(*event);
39 if (event->character !=
nullptr) {
40 size_t len = strlen(event->character);
41 char* character =
new char[len + 1];
42 strlcpy(character, event->character, len + 1);
43 _data->character = character;
51 return _callback != nil;
54 - (void)respond:(BOOL)handled {
57 @"Improper call to `respond` that does not have a callback.");
58 _callback(handled, _userData);
62 if (_data->character !=
nullptr) {
63 delete[] _data->character;
71 constexpr UIKeyboardHIDUsage kKeyCodeUndefined = (UIKeyboardHIDUsage)0x03;
73 constexpr UIKeyboardHIDUsage kKeyCodeKeyA = (UIKeyboardHIDUsage)0x04;
75 constexpr UIKeyboardHIDUsage kKeyCodePeriod = (UIKeyboardHIDUsage)0x37;
77 constexpr UIKeyboardHIDUsage kKeyCodeKeyW = (UIKeyboardHIDUsage)0x1a;
79 constexpr UIKeyboardHIDUsage kKeyCodeShiftLeft = (UIKeyboardHIDUsage)0xe1;
81 constexpr UIKeyboardHIDUsage kKeyCodeShiftRight = (UIKeyboardHIDUsage)0xe5;
83 constexpr UIKeyboardHIDUsage kKeyCodeNumpad1 = (UIKeyboardHIDUsage)0x59;
85 constexpr UIKeyboardHIDUsage kKeyCodeCapsLock = (UIKeyboardHIDUsage)0x39;
87 constexpr UIKeyboardHIDUsage kKeyCodeF1 = (UIKeyboardHIDUsage)0x3a;
89 constexpr UIKeyboardHIDUsage kKeyCodeCommandLeft = (UIKeyboardHIDUsage)0xe3;
91 constexpr UIKeyboardHIDUsage kKeyCodeAltRight = (UIKeyboardHIDUsage)0xe6;
93 constexpr UIKeyboardHIDUsage kKeyCodeEject = (UIKeyboardHIDUsage)0xb8;
95 constexpr uint64_t kPhysicalKeyUndefined = 0x00070003;
97 constexpr uint64_t kLogicalKeyUndefined = 0x1300000003;
99 constexpr uint64_t kModifierFlagNone = 0x0;
101 typedef
void (^ResponseCallback)(
bool handled);
111 if (@available(iOS 13.4, *)) {
113 XCTSkip(
@"Required API not present for test.");
124 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
125 __block BOOL last_handled = TRUE;
126 FlutterKeyEvent* event;
129 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
130 void* _Nullable user_data) {
131 [events addObject:[[
TestKeyEvent alloc] initWithEvent:&event
133 userData:user_data]];
136 last_handled = FALSE;
137 [responder
handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f, "a", "a")
139 last_handled = handled;
142 XCTAssertEqual([events count], 1u);
143 event = [events lastObject].data;
144 XCTAssertNotEqual(event,
nullptr);
145 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
146 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
147 XCTAssertEqual(event->timestamp, 123000000.0f);
148 XCTAssertEqual(event->physical, kPhysicalKeyA);
149 XCTAssertEqual(event->logical, kLogicalKeyA);
151 XCTAssertEqual(event->synthesized,
false);
153 XCTAssertEqual(last_handled, FALSE);
154 XCTAssert([[events lastObject] hasCallback]);
155 [[events lastObject] respond:TRUE];
156 XCTAssertEqual(last_handled, TRUE);
158 [events removeAllObjects];
161 [responder
handlePress:keyUpEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f)
163 last_handled = handled;
166 XCTAssertEqual([events count], 1u);
167 event = [events lastObject].data;
168 XCTAssertNotEqual(event,
nullptr);
169 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
170 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
171 XCTAssertEqual(event->timestamp, 123000000.0f);
172 XCTAssertEqual(event->physical, kPhysicalKeyA);
173 XCTAssertEqual(event->logical, kLogicalKeyA);
174 XCTAssertEqual(event->character,
nullptr);
175 XCTAssertEqual(event->synthesized,
false);
177 XCTAssertEqual(last_handled, TRUE);
178 XCTAssert([[events lastObject] hasCallback]);
179 [[events lastObject] respond:FALSE];
180 XCTAssertEqual(last_handled, FALSE);
182 [events removeAllObjects];
186 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
187 __block BOOL last_handled = TRUE;
188 FlutterKeyEvent* event;
191 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
192 void* _Nullable user_data) {
193 [events addObject:[[
TestKeyEvent alloc] initWithEvent:&event
195 userData:user_data]];
198 last_handled = FALSE;
201 [responder
handlePress:keyDownEvent(kKeyCodeEject, kModifierFlagNone, 123.0f)
203 last_handled = handled;
206 XCTAssertEqual([events count], 1u);
207 event = [events lastObject].data;
208 XCTAssertNotEqual(event,
nullptr);
209 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
210 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
211 XCTAssertEqual(event->physical, kKeyCodeEject |
kIosPlane);
212 XCTAssertEqual(event->logical, kKeyCodeEject |
kIosPlane);
213 XCTAssertEqual(event->character,
nullptr);
214 XCTAssertEqual(event->synthesized,
false);
216 XCTAssertEqual(last_handled, FALSE);
217 XCTAssert([[events lastObject] hasCallback]);
218 [[events lastObject] respond:TRUE];
219 XCTAssertEqual(last_handled, TRUE);
221 [events removeAllObjects];
224 [responder
handlePress:keyUpEvent(kKeyCodeEject, kModifierFlagNone, 123.0f)
226 last_handled = handled;
229 XCTAssertEqual([events count], 1u);
230 event = [events lastObject].data;
231 XCTAssertNotEqual(event,
nullptr);
232 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
233 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
234 XCTAssertEqual(event->physical, kKeyCodeEject |
kIosPlane);
235 XCTAssertEqual(event->logical, kKeyCodeEject |
kIosPlane);
236 XCTAssertEqual(event->character,
nullptr);
237 XCTAssertEqual(event->synthesized,
false);
239 XCTAssertEqual(last_handled, TRUE);
240 XCTAssert([[events lastObject] hasCallback]);
241 [[events lastObject] respond:FALSE];
242 XCTAssertEqual(last_handled, FALSE);
244 [events removeAllObjects];
248 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
249 FlutterKeyEvent* event;
252 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
253 void* _Nullable user_data) {
254 [events addObject:[[
TestKeyEvent alloc] initWithEvent:&event
256 userData:user_data]];
261 [responder
handlePress:keyDownEvent(kKeyCodeAltRight, kModifierFlagAltAny, 123.0f)
265 XCTAssertEqual([events count], 1u);
266 event = [events lastObject].data;
267 XCTAssertNotEqual(event,
nullptr);
268 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
269 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
270 XCTAssertEqual(event->physical, kPhysicalAltRight);
271 XCTAssertEqual(event->logical, kLogicalAltRight);
272 XCTAssertEqual(event->character,
nullptr);
273 XCTAssertEqual(event->synthesized,
false);
275 [events removeAllObjects];
278 [responder
handlePress:keyDownEvent(kKeyCodeKeyW, kModifierFlagAltAny, 123.0f, "â ˆ‘", "w")
282 XCTAssertEqual([events count], 1u);
283 event = [events lastObject].data;
284 XCTAssertNotEqual(event,
nullptr);
285 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
286 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
287 XCTAssertEqual(event->physical, kPhysicalKeyW);
288 XCTAssertEqual(event->logical, kLogicalKeyW);
290 XCTAssertEqual(event->synthesized,
false);
292 [events removeAllObjects];
296 [responder
handlePress:keyUpEvent(kKeyCodeAltRight, kModifierFlagAltAny, 123.0f)
300 XCTAssertEqual([events count], 1u);
301 event = [events lastObject].data;
302 XCTAssertNotEqual(event,
nullptr);
303 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
304 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
305 XCTAssertEqual(event->physical, kPhysicalAltRight);
306 XCTAssertEqual(event->logical, kLogicalAltRight);
307 XCTAssertEqual(event->character,
nullptr);
308 XCTAssertEqual(event->synthesized,
false);
310 [events removeAllObjects];
317 [responder
handlePress:keyUpEvent(kKeyCodeKeyW, kModifierFlagAltAny, 123.0f)
321 XCTAssertEqual([events count], 1u);
322 event = [events lastObject].data;
323 XCTAssertNotEqual(event,
nullptr);
324 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
325 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
326 XCTAssertEqual(event->physical, kPhysicalKeyW);
327 XCTAssertEqual(event->logical, kLogicalKeyW);
328 XCTAssertEqual(event->character,
nullptr);
329 XCTAssertEqual(event->synthesized,
false);
331 [events removeAllObjects];
335 [responder
handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f, "Ã ¥", "a")
339 XCTAssertEqual([events count], 1u);
340 event = [events lastObject].data;
341 XCTAssertNotEqual(event,
nullptr);
342 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
343 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
344 XCTAssertEqual(event->physical, kPhysicalKeyA);
345 XCTAssertEqual(event->logical, kLogicalKeyA);
347 XCTAssertEqual(event->synthesized,
false);
350 - (void)testIgnoreDuplicateDownEvent
API_AVAILABLE(ios(13.4)) {
351 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
352 __block BOOL last_handled = TRUE;
353 FlutterKeyEvent* event;
356 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
357 void* _Nullable user_data) {
358 [events addObject:[[
TestKeyEvent alloc] initWithEvent:&event
360 userData:user_data]];
363 last_handled = FALSE;
364 [responder
handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f, "a", "a")
366 last_handled = handled;
369 XCTAssertEqual([events count], 1u);
370 event = [events lastObject].data;
371 XCTAssertNotEqual(event,
nullptr);
372 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
373 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
374 XCTAssertEqual(event->physical, kPhysicalKeyA);
375 XCTAssertEqual(event->logical, kLogicalKeyA);
377 XCTAssertEqual(event->synthesized,
false);
378 XCTAssertEqual(last_handled, FALSE);
379 [[events lastObject] respond:TRUE];
380 XCTAssertEqual(last_handled, TRUE);
382 [events removeAllObjects];
384 last_handled = FALSE;
385 [responder
handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f, "a", "a")
387 last_handled = handled;
390 XCTAssertEqual([events count], 1u);
391 event = [events lastObject].data;
392 XCTAssertNotEqual(event,
nullptr);
393 XCTAssertEqual(event->physical, 0ull);
394 XCTAssertEqual(event->logical, 0ull);
395 XCTAssertEqual(event->synthesized,
false);
396 XCTAssertFalse([[events lastObject] hasCallback]);
397 XCTAssertEqual(last_handled, TRUE);
399 [events removeAllObjects];
401 last_handled = FALSE;
402 [responder
handlePress:keyUpEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f)
404 last_handled = handled;
407 XCTAssertEqual([events count], 1u);
408 event = [events lastObject].data;
409 XCTAssertNotEqual(event,
nullptr);
410 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
411 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
412 XCTAssertEqual(event->physical, kPhysicalKeyA);
413 XCTAssertEqual(event->logical, kLogicalKeyA);
414 XCTAssertEqual(event->character,
nullptr);
415 XCTAssertEqual(event->synthesized,
false);
416 XCTAssertEqual(last_handled, FALSE);
417 [[events lastObject] respond:TRUE];
418 XCTAssertEqual(last_handled, TRUE);
420 [events removeAllObjects];
424 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
425 __block BOOL last_handled = TRUE;
426 FlutterKeyEvent* event;
429 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
430 void* _Nullable user_data) {
431 [events addObject:[[
TestKeyEvent alloc] initWithEvent:&event
433 userData:user_data]];
436 last_handled = FALSE;
437 [responder
handlePress:keyUpEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f)
439 last_handled = handled;
442 XCTAssertEqual([events count], 1u);
443 event = [events lastObject].data;
444 XCTAssertNotEqual(event,
nullptr);
445 XCTAssertEqual(event->physical, 0ull);
446 XCTAssertEqual(event->logical, 0ull);
447 XCTAssertEqual(event->synthesized,
false);
448 XCTAssertFalse([[events lastObject] hasCallback]);
449 XCTAssertEqual(last_handled, TRUE);
451 [events removeAllObjects];
458 - (void)testToggleModifiersDuringKeyTap
API_AVAILABLE(ios(13.4)) {
459 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
460 FlutterKeyEvent* event;
463 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
464 void* _Nullable user_data) {
465 [events addObject:[[
TestKeyEvent alloc] initWithEvent:&event
467 userData:user_data]];
470 [responder
handlePress:keyDownEvent(kKeyCodeShiftRight, kModifierFlagShiftAny, 123.0f)
474 XCTAssertEqual([events count], 1u);
476 event = [events lastObject].data;
477 XCTAssertNotEqual(event,
nullptr);
478 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
479 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
480 XCTAssertEqual(event->timestamp, 123000000.0f);
481 XCTAssertEqual(event->physical, kPhysicalShiftRight);
482 XCTAssertEqual(event->logical, kLogicalShiftRight);
483 XCTAssertEqual(event->character,
nullptr);
484 XCTAssertEqual(event->synthesized,
false);
485 [[events lastObject] respond:TRUE];
487 [events removeAllObjects];
489 [responder
handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagShiftAny, 123.0f, "A", "A")
493 XCTAssertEqual([events count], 1u);
494 event = [events lastObject].data;
495 XCTAssertNotEqual(event,
nullptr);
496 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
497 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
498 XCTAssertEqual(event->physical, kPhysicalKeyA);
499 XCTAssertEqual(event->logical, kLogicalKeyA);
501 XCTAssertEqual(event->synthesized,
false);
502 [[events lastObject] respond:TRUE];
504 [events removeAllObjects];
506 [responder
handlePress:keyUpEvent(kKeyCodeShiftRight, kModifierFlagNone, 123.0f)
510 XCTAssertEqual([events count], 1u);
511 event = [events lastObject].data;
512 XCTAssertNotEqual(event,
nullptr);
513 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
514 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
515 XCTAssertEqual(event->physical, kPhysicalShiftRight);
516 XCTAssertEqual(event->logical, kLogicalShiftRight);
517 XCTAssertEqual(event->character,
nullptr);
518 XCTAssertEqual(event->synthesized,
false);
519 [[events lastObject] respond:TRUE];
521 [events removeAllObjects];
523 [responder
handlePress:keyUpEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f)
527 XCTAssertEqual([events count], 1u);
528 event = [events lastObject].data;
529 XCTAssertNotEqual(event,
nullptr);
530 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
531 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
532 XCTAssertEqual(event->physical, kPhysicalKeyA);
533 XCTAssertEqual(event->logical, kLogicalKeyA);
534 XCTAssertEqual(event->character,
nullptr);
535 XCTAssertEqual(event->synthesized,
false);
536 [[events lastObject] respond:TRUE];
538 [events removeAllObjects];
547 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
548 FlutterKeyEvent* event;
549 __block BOOL last_handled = TRUE;
550 id keyEventCallback = ^(BOOL handled) {
551 last_handled = handled;
555 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
556 void* _Nullable user_data) {
557 [events addObject:[[
TestKeyEvent alloc] initWithEvent:&event
559 userData:user_data]];
567 [responder
handlePress:keyDownEvent(kKeyCodeNumpad1, kModifierFlagNumPadKey, 123.0, "1", "1")
570 XCTAssertEqual([events count], 1u);
571 event = [events lastObject].data;
572 XCTAssertNotEqual(event,
nullptr);
573 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
574 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
575 XCTAssertEqual(event->physical, kPhysicalNumpad1);
576 XCTAssertEqual(event->logical, kLogicalNumpad1);
578 XCTAssertEqual(event->synthesized,
false);
579 [[events lastObject] respond:TRUE];
581 [events removeAllObjects];
585 [responder
handlePress:keyDownEvent(kKeyCodeUndefined, kModifierFlagNone, 123.0)
588 XCTAssertEqual([events count], 1u);
589 event = [events lastObject].data;
590 XCTAssertNotEqual(event,
nullptr);
591 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
592 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
593 XCTAssertEqual(event->physical, kPhysicalKeyUndefined);
594 XCTAssertEqual(event->logical, kLogicalKeyUndefined);
595 XCTAssertEqual(event->character,
nullptr);
596 XCTAssertEqual(event->synthesized,
false);
597 [[events lastObject] respond:TRUE];
599 [events removeAllObjects];
603 [responder
handlePress:keyDownEvent(kKeyCodeF1, kModifierFlagNone, 123.0f, "\\^P", "\\^P")
606 XCTAssertEqual([events count], 1u);
607 event = [events lastObject].data;
608 XCTAssertNotEqual(event,
nullptr);
609 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
610 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
611 XCTAssertEqual(event->physical, kPhysicalF1);
612 XCTAssertEqual(event->logical, kLogicalF1);
613 XCTAssertEqual(event->character,
nullptr);
614 XCTAssertEqual(event->synthesized,
false);
615 [[events lastObject] respond:TRUE];
617 [events removeAllObjects];
621 [responder
handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f, "a", "a")
624 XCTAssertEqual([events count], 1u);
625 event = [events lastObject].data;
626 XCTAssertNotEqual(event,
nullptr);
627 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
628 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
629 XCTAssertEqual(event->physical, kPhysicalKeyA);
630 XCTAssertEqual(event->logical, kLogicalKeyA);
632 XCTAssertEqual(event->synthesized,
false);
633 [[events lastObject] respond:TRUE];
635 [events removeAllObjects];
639 [responder
handlePress:keyDownEvent(kKeyCodeShiftLeft, kModifierFlagShiftAny, 123.0f)
642 XCTAssertEqual([events count], 1u);
643 event = [events lastObject].data;
644 XCTAssertNotEqual(event,
nullptr);
645 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
646 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
647 XCTAssertEqual(event->physical, kPhysicalShiftLeft);
648 XCTAssertEqual(event->logical, kLogicalShiftLeft);
649 XCTAssertEqual(event->character,
nullptr);
650 XCTAssertEqual(event->synthesized,
false);
652 [events removeAllObjects];
656 [responder
handlePress:keyUpEvent(kKeyCodeNumpad1, kModifierFlagNumPadKey, 123.0f)
659 XCTAssertEqual([events count], 2u);
663 event = [events firstObject].data;
664 XCTAssertNotEqual(event,
nullptr);
665 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
666 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
667 XCTAssertEqual(event->physical, kPhysicalShiftLeft);
668 XCTAssertEqual(event->logical, kLogicalShiftLeft);
669 XCTAssertEqual(event->character,
nullptr);
670 XCTAssertEqual(event->synthesized,
true);
672 event = [events lastObject].data;
673 XCTAssertNotEqual(event,
nullptr);
674 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
675 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
676 XCTAssertEqual(event->physical, kPhysicalNumpad1);
677 XCTAssertEqual(event->logical, kLogicalNumpad1);
678 XCTAssertEqual(event->character,
nullptr);
679 XCTAssertEqual(event->synthesized,
false);
680 [[events lastObject] respond:TRUE];
682 [events removeAllObjects];
686 [responder
handlePress:keyUpEvent(kKeyCodeF1, kModifierFlagNone, 123.0f)
689 XCTAssertEqual([events count], 1u);
690 event = [events lastObject].data;
691 XCTAssertNotEqual(event,
nullptr);
692 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
693 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
694 XCTAssertEqual(event->physical, kPhysicalF1);
695 XCTAssertEqual(event->logical, kLogicalF1);
696 XCTAssertEqual(event->character,
nullptr);
697 XCTAssertEqual(event->synthesized,
false);
698 [[events lastObject] respond:TRUE];
700 [events removeAllObjects];
704 [responder
handlePress:keyUpEvent(kKeyCodeUndefined, kModifierFlagNone, 123.0)
707 XCTAssertEqual([events count], 1u);
708 event = [events lastObject].data;
709 XCTAssertNotEqual(event,
nullptr);
710 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
711 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
712 XCTAssertEqual(event->physical, kPhysicalKeyUndefined);
713 XCTAssertEqual(event->logical, kLogicalKeyUndefined);
714 XCTAssertEqual(event->synthesized,
false);
715 [[events lastObject] respond:TRUE];
717 [events removeAllObjects];
721 [responder
handlePress:keyUpEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f)
724 XCTAssertEqual([events count], 1u);
725 event = [events lastObject].data;
726 XCTAssertNotEqual(event,
nullptr);
727 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
728 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
729 XCTAssertEqual(event->physical, kPhysicalKeyA);
730 XCTAssertEqual(event->logical, kLogicalKeyA);
731 XCTAssertEqual(event->character,
nullptr);
732 XCTAssertEqual(event->synthesized,
false);
733 [[events lastObject] respond:TRUE];
735 [events removeAllObjects];
739 [responder
handlePress:keyUpEvent(kKeyCodeShiftLeft, kModifierFlagShiftAny, 123.0f)
742 XCTAssertEqual([events count], 1u);
743 event = [events lastObject].data;
744 XCTAssertNotEqual(event,
nullptr);
745 XCTAssertEqual(event->physical, 0ull);
746 XCTAssertEqual(event->logical, 0ull);
747 XCTAssertEqual(event->synthesized,
false);
748 XCTAssertFalse([[events lastObject] hasCallback]);
749 XCTAssertEqual(last_handled, TRUE);
751 [events removeAllObjects];
754 - (void)testIdentifyLeftAndRightModifiers
API_AVAILABLE(ios(13.4)) {
755 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
756 FlutterKeyEvent* event;
759 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
760 void* _Nullable user_data) {
761 [events addObject:[[
TestKeyEvent alloc] initWithEvent:&event
763 userData:user_data]];
766 [responder
handlePress:keyDownEvent(kKeyCodeShiftLeft, kModifierFlagShiftAny, 123.0f)
770 XCTAssertEqual([events count], 1u);
771 event = [events lastObject].data;
772 XCTAssertNotEqual(event,
nullptr);
773 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
774 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
775 XCTAssertEqual(event->physical, kPhysicalShiftLeft);
776 XCTAssertEqual(event->logical, kLogicalShiftLeft);
777 XCTAssertEqual(event->character,
nullptr);
778 XCTAssertEqual(event->synthesized,
false);
779 [[events lastObject] respond:TRUE];
781 [events removeAllObjects];
783 [responder
handlePress:keyDownEvent(kKeyCodeShiftRight, kModifierFlagShiftAny, 123.0f)
787 XCTAssertEqual([events count], 1u);
788 event = [events lastObject].data;
789 XCTAssertNotEqual(event,
nullptr);
790 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
791 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
792 XCTAssertEqual(event->physical, kPhysicalShiftRight);
793 XCTAssertEqual(event->logical, kLogicalShiftRight);
794 XCTAssertEqual(event->character,
nullptr);
795 XCTAssertEqual(event->synthesized,
false);
796 [[events lastObject] respond:TRUE];
798 [events removeAllObjects];
800 [responder
handlePress:keyUpEvent(kKeyCodeShiftLeft, kModifierFlagShiftAny, 123.0f)
804 XCTAssertEqual([events count], 1u);
805 event = [events lastObject].data;
806 XCTAssertNotEqual(event,
nullptr);
807 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
808 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
809 XCTAssertEqual(event->physical, kPhysicalShiftLeft);
810 XCTAssertEqual(event->logical, kLogicalShiftLeft);
811 XCTAssertEqual(event->character,
nullptr);
812 XCTAssertEqual(event->synthesized,
false);
813 [[events lastObject] respond:TRUE];
815 [events removeAllObjects];
817 [responder
handlePress:keyUpEvent(kKeyCodeShiftRight, kModifierFlagShiftAny, 123.0f)
821 XCTAssertEqual([events count], 1u);
822 event = [events lastObject].data;
823 XCTAssertNotEqual(event,
nullptr);
824 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
825 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
826 XCTAssertEqual(event->physical, kPhysicalShiftRight);
827 XCTAssertEqual(event->logical, kLogicalShiftRight);
828 XCTAssertEqual(event->character,
nullptr);
829 XCTAssertEqual(event->synthesized,
false);
830 [[events lastObject] respond:TRUE];
832 [events removeAllObjects];
836 - (void)testSynchronizeCapsLockStateOnCapsLock
API_AVAILABLE(ios(13.4)) {
837 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
838 __block BOOL last_handled = TRUE;
839 id keyEventCallback = ^(BOOL handled) {
840 last_handled = handled;
842 FlutterKeyEvent* event;
845 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
846 void* _Nullable user_data) {
847 [events addObject:[[
TestKeyEvent alloc] initWithEvent:&event
849 userData:user_data]];
852 last_handled = FALSE;
853 [responder
handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagCapsLock, 123.0f, "A", "A")
856 XCTAssertEqual([events count], 3u);
858 event = events[0].data;
859 XCTAssertNotEqual(event,
nullptr);
860 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
861 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
862 XCTAssertEqual(event->physical, kPhysicalCapsLock);
863 XCTAssertEqual(event->logical, kLogicalCapsLock);
864 XCTAssertEqual(event->character,
nullptr);
865 XCTAssertEqual(event->synthesized,
true);
866 XCTAssertFalse([events[0] hasCallback]);
868 event = events[1].data;
869 XCTAssertNotEqual(event,
nullptr);
870 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
871 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
872 XCTAssertEqual(event->physical, kPhysicalCapsLock);
873 XCTAssertEqual(event->logical, kLogicalCapsLock);
874 XCTAssertEqual(event->character,
nullptr);
875 XCTAssertEqual(event->synthesized,
true);
876 XCTAssertFalse([events[1] hasCallback]);
878 event = events[2].data;
879 XCTAssertNotEqual(event,
nullptr);
880 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
881 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
882 XCTAssertEqual(event->physical, kPhysicalKeyA);
883 XCTAssertEqual(event->logical, kLogicalKeyA);
885 XCTAssertEqual(event->synthesized,
false);
886 XCTAssert([events[2] hasCallback]);
888 XCTAssertEqual(last_handled, FALSE);
889 [[events lastObject] respond:TRUE];
890 XCTAssertEqual(last_handled, TRUE);
892 [events removeAllObjects];
895 [responder
handlePress:keyUpEvent(kKeyCodeKeyA, kModifierFlagCapsLock, 123.0f)
897 XCTAssertEqual([events count], 1u);
898 event = [events lastObject].data;
899 XCTAssertNotEqual(event,
nullptr);
900 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
901 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
902 XCTAssertEqual(event->physical, kPhysicalKeyA);
903 XCTAssertEqual(event->logical, kLogicalKeyA);
904 XCTAssertEqual(event->synthesized,
false);
906 [events removeAllObjects];
910 last_handled = FALSE;
911 [responder
handlePress:keyDownEvent(kKeyCodeCapsLock, kModifierFlagCapsLock, 123.0f)
914 XCTAssertEqual([events count], 1u);
915 event = [events firstObject].data;
916 XCTAssertNotEqual(event,
nullptr);
917 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
918 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
919 XCTAssertEqual(event->physical, kPhysicalCapsLock);
920 XCTAssertEqual(event->logical, kLogicalCapsLock);
921 XCTAssertEqual(event->character,
nullptr);
922 XCTAssertEqual(event->synthesized,
false);
923 XCTAssert([[events firstObject] hasCallback]);
925 [events removeAllObjects];
931 last_handled = FALSE;
932 [responder
handlePress:keyUpEvent(kKeyCodeCapsLock, kModifierFlagCapsLock, 123.0f)
935 XCTAssertEqual([events count], 1u);
936 event = [events firstObject].data;
937 XCTAssertNotEqual(event,
nullptr);
938 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
939 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
940 XCTAssertEqual(event->physical, kPhysicalCapsLock);
941 XCTAssertEqual(event->logical, kLogicalCapsLock);
942 XCTAssertEqual(event->character,
nullptr);
943 XCTAssertEqual(event->synthesized,
false);
944 XCTAssert([[events firstObject] hasCallback]);
946 [events removeAllObjects];
948 last_handled = FALSE;
949 [responder
handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagNone, 123.0f, "a", "a")
954 XCTAssertEqual([events count], 1u);
955 event = [events lastObject].data;
956 XCTAssertNotEqual(event,
nullptr);
957 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
958 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
959 XCTAssertEqual(event->physical, kPhysicalKeyA);
960 XCTAssertEqual(event->logical, kLogicalKeyA);
962 XCTAssertEqual(event->synthesized,
false);
963 XCTAssert([[events firstObject] hasCallback]);
967 - (void)testSynchronizeCapsLockStateOnNormalKey
API_AVAILABLE(ios(13.4)) {
968 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
969 __block BOOL last_handled = TRUE;
970 id keyEventCallback = ^(BOOL handled) {
971 last_handled = handled;
973 FlutterKeyEvent* event;
976 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
977 void* _Nullable user_data) {
978 [events addObject:[[
TestKeyEvent alloc] initWithEvent:&event
980 userData:user_data]];
983 last_handled = FALSE;
984 [responder
handlePress:keyDownEvent(kKeyCodeKeyA, kModifierFlagCapsLock, 123.0f, "A", "a")
987 XCTAssertEqual([events count], 3u);
989 event = events[0].data;
990 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
991 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
992 XCTAssertEqual(event->physical, kPhysicalCapsLock);
993 XCTAssertEqual(event->logical, kLogicalCapsLock);
994 XCTAssertEqual(event->character,
nullptr);
995 XCTAssertEqual(event->synthesized,
true);
996 XCTAssertFalse([events[0] hasCallback]);
998 event = events[1].data;
999 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
1000 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
1001 XCTAssertEqual(event->physical, kPhysicalCapsLock);
1002 XCTAssertEqual(event->logical, kLogicalCapsLock);
1003 XCTAssertEqual(event->character,
nullptr);
1004 XCTAssertEqual(event->synthesized,
true);
1005 XCTAssertFalse([events[1] hasCallback]);
1007 event = events[2].data;
1008 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
1009 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
1010 XCTAssertEqual(event->physical, kPhysicalKeyA);
1011 XCTAssertEqual(event->logical, kLogicalKeyA);
1013 XCTAssertEqual(event->synthesized,
false);
1014 XCTAssert([events[2] hasCallback]);
1016 XCTAssertEqual(last_handled, FALSE);
1017 [[events lastObject] respond:TRUE];
1018 XCTAssertEqual(last_handled, TRUE);
1020 [events removeAllObjects];
1025 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
1026 id keyEventCallback = ^(BOOL handled) {
1028 FlutterKeyEvent* event;
1031 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
1032 void* _Nullable user_data) {
1033 [events addObject:[[
TestKeyEvent alloc] initWithEvent:&event callback:nil userData:nil]];
1034 callback(true, user_data);
1038 [responder
handlePress:keyDownEvent(kKeyCodeCommandLeft, kModifierFlagMetaAny, 123.0f, "", "")
1040 XCTAssertEqual([events count], 1u);
1041 event = events[0].data;
1042 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
1043 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
1044 XCTAssertEqual(event->physical, kPhysicalMetaLeft);
1045 XCTAssertEqual(event->logical, kLogicalMetaLeft);
1046 XCTAssertEqual(event->character,
nullptr);
1047 XCTAssertEqual(event->synthesized,
false);
1048 [events removeAllObjects];
1051 [responder
handlePress:keyDownEvent(kKeyCodePeriod, kModifierFlagMetaAny, 123.0f,
1052 "UIKeyInputEscape", "UIKeyInputEscape")
1054 XCTAssertEqual([events count], 1u);
1055 event = events[0].data;
1056 XCTAssertEqual(event->type, kFlutterKeyEventTypeDown);
1057 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
1058 XCTAssertEqual(event->physical, kPhysicalPeriod);
1059 XCTAssertEqual(event->logical, kLogicalEscape);
1060 XCTAssertEqual(event->character,
nullptr);
1061 XCTAssertEqual(event->synthesized,
false);
1062 [events removeAllObjects];
1065 [responder
handlePress:keyUpEvent(kKeyCodePeriod, kModifierFlagMetaAny, 123.0f,
1066 "UIKeyInputEscape", "UIKeyInputEscape")
1068 XCTAssertEqual([events count], 1u);
1069 event = events[0].data;
1070 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
1071 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
1072 XCTAssertEqual(event->physical, kPhysicalPeriod);
1073 XCTAssertEqual(event->logical, kLogicalEscape);
1074 XCTAssertEqual(event->character,
nullptr);
1075 XCTAssertEqual(event->synthesized,
false);
1076 [events removeAllObjects];
1079 [responder
handlePress:keyUpEvent(kKeyCodeCommandLeft, kModifierFlagMetaAny, 123.0f, "", "")
1081 XCTAssertEqual([events count], 1u);
1082 event = events[0].data;
1083 XCTAssertEqual(event->type, kFlutterKeyEventTypeUp);
1084 XCTAssertEqual(event->device_type, kFlutterKeyEventDeviceTypeKeyboard);
1085 XCTAssertEqual(event->physical, kPhysicalMetaLeft);
1086 XCTAssertEqual(event->logical, kLogicalMetaLeft);
1087 XCTAssertEqual(event->character,
nullptr);
1088 XCTAssertEqual(event->synthesized,
false);
1089 [events removeAllObjects];