Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
FlutterEmbedderKeyResponderTest.mm
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import <Foundation/Foundation.h>
6
7#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEmbedderKeyResponder.h"
8#include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
9#import "flutter/testing/testing.h"
10#include "third_party/googletest/googletest/include/gtest/gtest.h"
11
12// A wrap to convert FlutterKeyEvent to a ObjC class.
13@interface TestKeyEvent : NSObject
14@property(nonatomic) FlutterKeyEvent* data;
15@property(nonatomic) FlutterKeyEventCallback callback;
16@property(nonatomic) void* userData;
17- (nonnull instancetype)initWithEvent:(const FlutterKeyEvent*)event
18 callback:(nullable FlutterKeyEventCallback)callback
19 userData:(nullable void*)userData;
21- (void)respond:(BOOL)handled;
22@end
23
24@implementation TestKeyEvent
25- (instancetype)initWithEvent:(const FlutterKeyEvent*)event
26 callback:(nullable FlutterKeyEventCallback)callback
27 userData:(nullable void*)userData {
28 self = [super init];
29 _data = new FlutterKeyEvent(*event);
30 if (event->character != nullptr) {
31 size_t len = strlen(event->character);
32 char* character = new char[len + 1];
33 strlcpy(character, event->character, sizeof(character));
34 _data->character = character;
35 }
36 _callback = callback;
37 _userData = userData;
38 return self;
39}
40
42 return _callback != nil;
43}
44
45- (void)respond:(BOOL)handled {
46 NSAssert(
47 _callback != nil,
48 @"Improper call to `respond` that does not have a callback."); // Caller's responsibility
49 _callback(handled, _userData);
50}
51
52- (void)dealloc {
53 if (_data->character != nullptr) {
54 delete[] _data->character;
55 }
56 delete _data;
57}
58@end
59
60namespace flutter::testing {
61
62namespace {
63constexpr uint64_t kKeyCodeKeyA = 0x00;
64constexpr uint64_t kKeyCodeKeyW = 0x0d;
65constexpr uint64_t kKeyCodeShiftLeft = 0x38;
66constexpr uint64_t kKeyCodeShiftRight = 0x3c;
67constexpr uint64_t kKeyCodeCapsLock = 0x39;
68constexpr uint64_t kKeyCodeNumpad1 = 0x53;
69constexpr uint64_t kKeyCodeF1 = 0x7a;
70constexpr uint64_t kKeyCodeAltRight = 0x3d;
71
72using namespace ::flutter::testing::keycodes;
73
74typedef void (^ResponseCallback)(bool handled);
75
76NSEvent* keyEvent(NSEventType type,
77 NSEventModifierFlags modifierFlags,
78 NSString* characters,
79 NSString* charactersIgnoringModifiers,
80 BOOL isARepeat,
81 unsigned short keyCode) {
82 return [NSEvent keyEventWithType:type
83 location:NSZeroPoint
84 modifierFlags:modifierFlags
85 timestamp:0
86 windowNumber:0
87 context:nil
88 characters:characters
89 charactersIgnoringModifiers:charactersIgnoringModifiers
90 isARepeat:isARepeat
91 keyCode:keyCode];
92}
93
94NSEvent* keyEvent(NSEventType type,
95 NSTimeInterval timestamp,
96 NSEventModifierFlags modifierFlags,
97 NSString* characters,
98 NSString* charactersIgnoringModifiers,
99 BOOL isARepeat,
100 unsigned short keyCode) {
101 return [NSEvent keyEventWithType:type
102 location:NSZeroPoint
103 modifierFlags:modifierFlags
104 timestamp:timestamp
105 windowNumber:0
106 context:nil
107 characters:characters
108 charactersIgnoringModifiers:charactersIgnoringModifiers
109 isARepeat:isARepeat
110 keyCode:keyCode];
111}
112
113} // namespace
114
115// Test the most basic key events.
116//
117// Press, hold, and release key A on an US keyboard.
118TEST(FlutterEmbedderKeyResponderUnittests, BasicKeyEvent) {
119 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
120 __block BOOL last_handled = TRUE;
122
124 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
125 void* _Nullable user_data) {
126 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
127 callback:callback
128 userData:user_data]];
129 }];
130
131 last_handled = FALSE;
132 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 123.0f, 0x100, @"a", @"a", FALSE, 0)
133 callback:^(BOOL handled) {
134 last_handled = handled;
135 }];
136
137 EXPECT_EQ([events count], 1u);
138 event = [events lastObject].data;
139 ASSERT_NE(event, nullptr);
140 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
141 EXPECT_EQ(event->timestamp, 123000000.0f);
142 EXPECT_EQ(event->physical, kPhysicalKeyA);
143 EXPECT_EQ(event->logical, kLogicalKeyA);
144 EXPECT_STREQ(event->character, "a");
145 EXPECT_EQ(event->synthesized, false);
146
147 EXPECT_EQ(last_handled, FALSE);
148 EXPECT_TRUE([[events lastObject] hasCallback]);
149 [[events lastObject] respond:TRUE];
150 EXPECT_EQ(last_handled, TRUE);
151
152 [events removeAllObjects];
153
154 last_handled = FALSE;
155 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"a", @"a", TRUE, kKeyCodeKeyA)
156 callback:^(BOOL handled) {
157 last_handled = handled;
158 }];
159
160 EXPECT_EQ([events count], 1u);
161 event = [events lastObject].data;
162 ASSERT_NE(event, nullptr);
163 EXPECT_EQ(event->type, kFlutterKeyEventTypeRepeat);
164 EXPECT_EQ(event->physical, kPhysicalKeyA);
165 EXPECT_EQ(event->logical, kLogicalKeyA);
166 EXPECT_STREQ(event->character, "a");
167 EXPECT_EQ(event->synthesized, false);
168
169 EXPECT_EQ(last_handled, FALSE);
170 EXPECT_TRUE([[events lastObject] hasCallback]);
171 [[events lastObject] respond:TRUE];
172 EXPECT_EQ(last_handled, TRUE);
173
174 [events removeAllObjects];
175
176 last_handled = TRUE;
177 [responder handleEvent:keyEvent(NSEventTypeKeyUp, 124.0f, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA)
178 callback:^(BOOL handled) {
179 last_handled = handled;
180 }];
181
182 EXPECT_EQ([events count], 1u);
183 event = [events lastObject].data;
184 ASSERT_NE(event, nullptr);
185 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
186 EXPECT_EQ(event->timestamp, 124000000.0f);
187 EXPECT_EQ(event->physical, kPhysicalKeyA);
188 EXPECT_EQ(event->logical, kLogicalKeyA);
189 EXPECT_EQ(event->character, nullptr);
190 EXPECT_EQ(event->synthesized, false);
191
192 EXPECT_EQ(last_handled, TRUE);
193 EXPECT_TRUE([[events lastObject] hasCallback]);
194 [[events lastObject] respond:FALSE]; // Check if responding FALSE works
195 EXPECT_EQ(last_handled, FALSE);
196
197 [events removeAllObjects];
198}
199
200TEST(FlutterEmbedderKeyResponderUnittests, NonAsciiCharacters) {
201 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
203
205 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
206 void* _Nullable user_data) {
207 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
208 callback:callback
209 userData:user_data]];
210 }];
211
212 [responder
213 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x80140, @"", @"", FALSE, kKeyCodeAltRight)
214 callback:^(BOOL handled){
215 }];
216
217 EXPECT_EQ([events count], 1u);
218 event = [events lastObject].data;
219 ASSERT_NE(event, nullptr);
220 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
221 EXPECT_EQ(event->physical, kPhysicalAltRight);
222 EXPECT_EQ(event->logical, kLogicalAltRight);
223 EXPECT_STREQ(event->character, nullptr);
224 EXPECT_EQ(event->synthesized, false);
225
226 [events removeAllObjects];
227
228 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x80140, @"∑", @"w", FALSE, kKeyCodeKeyW)
229 callback:^(BOOL handled){
230 }];
231
232 EXPECT_EQ([events count], 1u);
233 event = [events lastObject].data;
234 ASSERT_NE(event, nullptr);
235 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
236 EXPECT_EQ(event->physical, kPhysicalKeyW);
237 EXPECT_EQ(event->logical, kLogicalKeyW);
238 EXPECT_STREQ(event->character, "∑");
239 EXPECT_EQ(event->synthesized, false);
240
241 [events removeAllObjects];
242
243 [responder handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x100, @"", @"", FALSE, kKeyCodeAltRight)
244 callback:^(BOOL handled){
245 }];
246
247 EXPECT_EQ([events count], 1u);
248 event = [events lastObject].data;
249 ASSERT_NE(event, nullptr);
250 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
251 EXPECT_EQ(event->physical, kPhysicalAltRight);
252 EXPECT_EQ(event->logical, kLogicalAltRight);
253 EXPECT_STREQ(event->character, nullptr);
254 EXPECT_EQ(event->synthesized, false);
255
256 [events removeAllObjects];
257
258 [responder handleEvent:keyEvent(NSEventTypeKeyUp, 0x100, @"w", @"w", FALSE, kKeyCodeKeyW)
259 callback:^(BOOL handled){
260 }];
261
262 EXPECT_EQ([events count], 1u);
263 event = [events lastObject].data;
264 ASSERT_NE(event, nullptr);
265 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
266 EXPECT_EQ(event->physical, kPhysicalKeyW);
267 EXPECT_EQ(event->logical, kLogicalKeyW);
268 EXPECT_STREQ(event->character, nullptr);
269 EXPECT_EQ(event->synthesized, false);
270
271 [events removeAllObjects];
272}
273
274TEST(FlutterEmbedderKeyResponderUnittests, MultipleCharacters) {
275 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
277
279 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
280 void* _Nullable user_data) {
281 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
282 callback:callback
283 userData:user_data]];
284 }];
285
286 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0, @"àn", @"àn", FALSE, kKeyCodeKeyA)
287 callback:^(BOOL handled){
288 }];
289
290 EXPECT_EQ([events count], 1u);
291 event = [events lastObject].data;
292 ASSERT_NE(event, nullptr);
293 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
294 EXPECT_EQ(event->physical, kPhysicalKeyA);
295 EXPECT_EQ(event->logical, 0x1400000000ull);
296 EXPECT_STREQ(event->character, "àn");
297 EXPECT_EQ(event->synthesized, false);
298
299 [events removeAllObjects];
300
301 [responder handleEvent:keyEvent(NSEventTypeKeyUp, 0, @"a", @"a", FALSE, kKeyCodeKeyA)
302 callback:^(BOOL handled){
303 }];
304
305 EXPECT_EQ([events count], 1u);
306 event = [events lastObject].data;
307 ASSERT_NE(event, nullptr);
308 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
309 EXPECT_EQ(event->physical, kPhysicalKeyA);
310 EXPECT_EQ(event->logical, 0x1400000000ull);
311 EXPECT_STREQ(event->character, nullptr);
312 EXPECT_EQ(event->synthesized, false);
313
314 [events removeAllObjects];
315}
316
317TEST(FlutterEmbedderKeyResponderUnittests, SynthesizeForDuplicateDownEvent) {
318 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
319 __block BOOL last_handled = TRUE;
321
323 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
324 void* _Nullable user_data) {
325 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
326 callback:callback
327 userData:user_data]];
328 }];
329
330 last_handled = TRUE;
331 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA)
332 callback:^(BOOL handled) {
333 last_handled = handled;
334 }];
335
336 EXPECT_EQ([events count], 1u);
337 event = [events lastObject].data;
338 ASSERT_NE(event, nullptr);
339 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
340 EXPECT_EQ(event->physical, kPhysicalKeyA);
341 EXPECT_EQ(event->logical, kLogicalKeyA);
342 EXPECT_STREQ(event->character, "a");
343 EXPECT_EQ(event->synthesized, false);
344 EXPECT_EQ(last_handled, TRUE);
345 [[events lastObject] respond:FALSE];
346 EXPECT_EQ(last_handled, FALSE);
347
348 [events removeAllObjects];
349
350 last_handled = TRUE;
351 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"à", @"à", FALSE, kKeyCodeKeyA)
352 callback:^(BOOL handled) {
353 last_handled = handled;
354 }];
355
356 EXPECT_EQ([events count], 2u);
357
358 event = [events firstObject].data;
359 ASSERT_NE(event, nullptr);
360 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
361 EXPECT_EQ(event->physical, kPhysicalKeyA);
362 EXPECT_EQ(event->logical, kLogicalKeyA);
363 EXPECT_STREQ(event->character, NULL);
364 EXPECT_EQ(event->synthesized, true);
365
366 event = [events lastObject].data;
367 ASSERT_NE(event, nullptr);
368 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
369 EXPECT_EQ(event->physical, kPhysicalKeyA);
370 EXPECT_EQ(event->logical, 0xE0ull /* à */);
371 EXPECT_STREQ(event->character, "à");
372 EXPECT_EQ(event->synthesized, false);
373 [[events lastObject] respond:FALSE];
374 EXPECT_EQ(last_handled, FALSE);
375
376 [events removeAllObjects];
377}
378
379TEST(FlutterEmbedderKeyResponderUnittests, IgnoreDuplicateUpEvent) {
380 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
382 __block BOOL last_handled = TRUE;
383
385 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
386 void* _Nullable user_data) {
387 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
388 callback:callback
389 userData:user_data]];
390 }];
391
392 last_handled = FALSE;
393 [responder handleEvent:keyEvent(NSEventTypeKeyUp, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA)
394 callback:^(BOOL handled) {
395 last_handled = handled;
396 }];
397
398 EXPECT_EQ([events count], 1u);
399 EXPECT_EQ(last_handled, TRUE);
400 event = [events lastObject].data;
401 ASSERT_NE(event, nullptr);
402 EXPECT_EQ(event->physical, 0ull);
403 EXPECT_EQ(event->logical, 0ull);
404 EXPECT_FALSE([[events lastObject] hasCallback]);
405 EXPECT_EQ(last_handled, TRUE);
406
407 [events removeAllObjects];
408}
409
410TEST(FlutterEmbedderKeyResponderUnittests, ConvertAbruptRepeatEventsToDown) {
411 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
412 __block BOOL last_handled = TRUE;
414
416 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
417 void* _Nullable user_data) {
418 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
419 callback:callback
420 userData:user_data]];
421 }];
422
423 last_handled = TRUE;
424 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"a", @"a", TRUE, kKeyCodeKeyA)
425 callback:^(BOOL handled) {
426 last_handled = handled;
427 }];
428
429 EXPECT_EQ([events count], 1u);
430 event = [events lastObject].data;
431 ASSERT_NE(event, nullptr);
432 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
433 EXPECT_EQ(event->physical, kPhysicalKeyA);
434 EXPECT_EQ(event->logical, kLogicalKeyA);
435 EXPECT_STREQ(event->character, "a");
436 EXPECT_EQ(event->synthesized, false);
437 EXPECT_EQ(last_handled, TRUE);
438 [[events lastObject] respond:FALSE];
439 EXPECT_EQ(last_handled, FALSE);
440
441 [events removeAllObjects];
442}
443
444// Press L shift, A, then release L shift then A, on an US keyboard.
445//
446// This is special because the characters for the A key will change in this
447// process.
448TEST(FlutterEmbedderKeyResponderUnittests, ToggleModifiersDuringKeyTap) {
449 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
451
453 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
454 void* _Nullable user_data) {
455 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
456 callback:callback
457 userData:user_data]];
458 }];
459
460 [responder handleEvent:keyEvent(NSEventTypeFlagsChanged, 123.0f, 0x20104, @"", @"", FALSE,
461 kKeyCodeShiftRight)
462 callback:^(BOOL handled){
463 }];
464
465 EXPECT_EQ([events count], 1u);
466 event = [events lastObject].data;
467 ASSERT_NE(event, nullptr);
468 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
469 EXPECT_EQ(event->timestamp, 123000000.0f);
470 EXPECT_EQ(event->physical, kPhysicalShiftRight);
471 EXPECT_EQ(event->logical, kLogicalShiftRight);
472 EXPECT_STREQ(event->character, nullptr);
473 EXPECT_EQ(event->synthesized, false);
474 [[events lastObject] respond:TRUE];
475
476 [events removeAllObjects];
477
478 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x20104, @"A", @"A", FALSE, kKeyCodeKeyA)
479 callback:^(BOOL handled){
480 }];
481
482 EXPECT_EQ([events count], 1u);
483 event = [events lastObject].data;
484 ASSERT_NE(event, nullptr);
485 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
486 EXPECT_EQ(event->physical, kPhysicalKeyA);
487 EXPECT_EQ(event->logical, kLogicalKeyA);
488 EXPECT_STREQ(event->character, "A");
489 EXPECT_EQ(event->synthesized, false);
490 [[events lastObject] respond:TRUE];
491
492 [events removeAllObjects];
493
494 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x20104, @"A", @"A", TRUE, kKeyCodeKeyA)
495 callback:^(BOOL handled){
496 }];
497
498 EXPECT_EQ([events count], 1u);
499 event = [events lastObject].data;
500 ASSERT_NE(event, nullptr);
501 EXPECT_EQ(event->type, kFlutterKeyEventTypeRepeat);
502 EXPECT_EQ(event->physical, kPhysicalKeyA);
503 EXPECT_EQ(event->logical, kLogicalKeyA);
504 EXPECT_STREQ(event->character, "A");
505 EXPECT_EQ(event->synthesized, false);
506 [[events lastObject] respond:TRUE];
507
508 [events removeAllObjects];
509
510 [responder
511 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x100, @"", @"", FALSE, kKeyCodeShiftRight)
512 callback:^(BOOL handled){
513 }];
514
515 EXPECT_EQ([events count], 1u);
516 event = [events lastObject].data;
517 ASSERT_NE(event, nullptr);
518 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
519 EXPECT_EQ(event->physical, kPhysicalShiftRight);
520 EXPECT_EQ(event->logical, kLogicalShiftRight);
521 EXPECT_STREQ(event->character, nullptr);
522 EXPECT_EQ(event->synthesized, false);
523 [[events lastObject] respond:TRUE];
524
525 [events removeAllObjects];
526
527 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"a", @"a", TRUE, kKeyCodeKeyA)
528 callback:^(BOOL handled){
529 }];
530
531 EXPECT_EQ([events count], 1u);
532 event = [events lastObject].data;
533 ASSERT_NE(event, nullptr);
534 EXPECT_EQ(event->type, kFlutterKeyEventTypeRepeat);
535 EXPECT_EQ(event->physical, kPhysicalKeyA);
536 EXPECT_EQ(event->logical, kLogicalKeyA);
537 EXPECT_STREQ(event->character, "a");
538 EXPECT_EQ(event->synthesized, false);
539
540 [events removeAllObjects];
541
542 [responder handleEvent:keyEvent(NSEventTypeKeyUp, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA)
543 callback:^(BOOL handled){
544 }];
545
546 EXPECT_EQ([events count], 1u);
547 event = [events lastObject].data;
548 ASSERT_NE(event, nullptr);
549 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
550 EXPECT_EQ(event->physical, kPhysicalKeyA);
551 EXPECT_EQ(event->logical, kLogicalKeyA);
552 EXPECT_STREQ(event->character, nullptr);
553 EXPECT_EQ(event->synthesized, false);
554 [[events lastObject] respond:TRUE];
555
556 [events removeAllObjects];
557}
558
559// Special modifier flags.
560//
561// Some keys in modifierFlags are not to indicate modifier state, but to mark
562// the key area that the key belongs to, such as numpad keys or function keys.
563// Ensure these flags do not obstruct other keys.
564TEST(FlutterEmbedderKeyResponderUnittests, SpecialModiferFlags) {
565 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
567
569 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
570 void* _Nullable user_data) {
571 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
572 callback:callback
573 userData:user_data]];
574 }];
575
576 // Keydown: Numpad1, F1, KeyA, ShiftLeft
577 // Then KeyUp: Numpad1, F1, KeyA, ShiftLeft
578
579 // Numpad 1
580 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x200100, @"1", @"1", FALSE, kKeyCodeNumpad1)
581 callback:^(BOOL handled){
582 }];
583
584 EXPECT_EQ([events count], 1u);
585 event = [events lastObject].data;
586 ASSERT_NE(event, nullptr);
587 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
588 EXPECT_EQ(event->physical, kPhysicalNumpad1);
589 EXPECT_EQ(event->logical, kLogicalNumpad1);
590 EXPECT_STREQ(event->character, "1");
591 EXPECT_EQ(event->synthesized, false);
592 [[events lastObject] respond:TRUE];
593
594 [events removeAllObjects];
595
596 // F1
597 [responder
598 handleEvent:keyEvent(NSEventTypeKeyDown, 0x800100, @"\uf704", @"\uf704", FALSE, kKeyCodeF1)
599 callback:^(BOOL handled){
600 }];
601
602 EXPECT_EQ([events count], 1u);
603 event = [events lastObject].data;
604 ASSERT_NE(event, nullptr);
605 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
606 EXPECT_EQ(event->physical, kPhysicalF1);
607 EXPECT_EQ(event->logical, kLogicalF1);
608 EXPECT_STREQ(event->character, nullptr);
609 EXPECT_EQ(event->synthesized, false);
610 [[events lastObject] respond:TRUE];
611
612 [events removeAllObjects];
613
614 // KeyA
615 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA)
616 callback:^(BOOL handled){
617 }];
618
619 EXPECT_EQ([events count], 1u);
620 event = [events lastObject].data;
621 ASSERT_NE(event, nullptr);
622 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
623 EXPECT_EQ(event->physical, kPhysicalKeyA);
624 EXPECT_EQ(event->logical, kLogicalKeyA);
625 EXPECT_STREQ(event->character, "a");
626 EXPECT_EQ(event->synthesized, false);
627 [[events lastObject] respond:TRUE];
628
629 [events removeAllObjects];
630
631 // ShiftLeft
632 [responder
633 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x20102, @"", @"", FALSE, kKeyCodeShiftLeft)
634 callback:^(BOOL handled){
635 }];
636
637 EXPECT_EQ([events count], 1u);
638 event = [events lastObject].data;
639 ASSERT_NE(event, nullptr);
640 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
641 EXPECT_EQ(event->physical, kPhysicalShiftLeft);
642 EXPECT_EQ(event->logical, kLogicalShiftLeft);
643 EXPECT_STREQ(event->character, nullptr);
644 EXPECT_EQ(event->synthesized, false);
645 [[events lastObject] respond:TRUE];
646
647 [events removeAllObjects];
648
649 // Numpad 1
650 [responder handleEvent:keyEvent(NSEventTypeKeyUp, 0x220102, @"1", @"1", FALSE, kKeyCodeNumpad1)
651 callback:^(BOOL handled){
652 }];
653
654 EXPECT_EQ([events count], 1u);
655 event = [events lastObject].data;
656 ASSERT_NE(event, nullptr);
657 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
658 EXPECT_EQ(event->physical, kPhysicalNumpad1);
659 EXPECT_EQ(event->logical, kLogicalNumpad1);
660 EXPECT_STREQ(event->character, nullptr);
661 EXPECT_EQ(event->synthesized, false);
662 [[events lastObject] respond:TRUE];
663
664 [events removeAllObjects];
665
666 // F1
667 [responder
668 handleEvent:keyEvent(NSEventTypeKeyUp, 0x820102, @"\uF704", @"\uF704", FALSE, kKeyCodeF1)
669 callback:^(BOOL handled){
670 }];
671
672 EXPECT_EQ([events count], 1u);
673 event = [events lastObject].data;
674 ASSERT_NE(event, nullptr);
675 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
676 EXPECT_EQ(event->physical, kPhysicalF1);
677 EXPECT_EQ(event->logical, kLogicalF1);
678 EXPECT_STREQ(event->character, nullptr);
679 EXPECT_EQ(event->synthesized, false);
680 [[events lastObject] respond:TRUE];
681
682 [events removeAllObjects];
683
684 // KeyA
685 [responder handleEvent:keyEvent(NSEventTypeKeyUp, 0x20102, @"a", @"a", FALSE, kKeyCodeKeyA)
686 callback:^(BOOL handled){
687 }];
688
689 EXPECT_EQ([events count], 1u);
690 event = [events lastObject].data;
691 ASSERT_NE(event, nullptr);
692 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
693 EXPECT_EQ(event->physical, kPhysicalKeyA);
694 EXPECT_EQ(event->logical, kLogicalKeyA);
695 EXPECT_STREQ(event->character, nullptr);
696 EXPECT_EQ(event->synthesized, false);
697 [[events lastObject] respond:TRUE];
698
699 [events removeAllObjects];
700
701 // ShiftLeft
702 [responder
703 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x100, @"", @"", FALSE, kKeyCodeShiftLeft)
704 callback:^(BOOL handled){
705 }];
706
707 EXPECT_EQ([events count], 1u);
708 event = [events lastObject].data;
709 ASSERT_NE(event, nullptr);
710 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
711 EXPECT_EQ(event->physical, kPhysicalShiftLeft);
712 EXPECT_EQ(event->logical, kLogicalShiftLeft);
713 EXPECT_STREQ(event->character, nullptr);
714 EXPECT_EQ(event->synthesized, false);
715 [[events lastObject] respond:TRUE];
716
717 [events removeAllObjects];
718}
719
720TEST(FlutterEmbedderKeyResponderUnittests, IdentifyLeftAndRightModifiers) {
721 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
723
725 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
726 void* _Nullable user_data) {
727 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
728 callback:callback
729 userData:user_data]];
730 }];
731
732 [responder
733 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x20102, @"", @"", FALSE, kKeyCodeShiftLeft)
734 callback:^(BOOL handled){
735 }];
736
737 EXPECT_EQ([events count], 1u);
738 event = [events lastObject].data;
739 ASSERT_NE(event, nullptr);
740 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
741 EXPECT_EQ(event->physical, kPhysicalShiftLeft);
742 EXPECT_EQ(event->logical, kLogicalShiftLeft);
743 EXPECT_STREQ(event->character, nullptr);
744 EXPECT_EQ(event->synthesized, false);
745 [[events lastObject] respond:TRUE];
746
747 [events removeAllObjects];
748
749 [responder
750 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x20106, @"", @"", FALSE, kKeyCodeShiftRight)
751 callback:^(BOOL handled){
752 }];
753
754 EXPECT_EQ([events count], 1u);
755 event = [events lastObject].data;
756 ASSERT_NE(event, nullptr);
757 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
758 EXPECT_EQ(event->physical, kPhysicalShiftRight);
759 EXPECT_EQ(event->logical, kLogicalShiftRight);
760 EXPECT_STREQ(event->character, nullptr);
761 EXPECT_EQ(event->synthesized, false);
762 [[events lastObject] respond:TRUE];
763
764 [events removeAllObjects];
765
766 [responder
767 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x20104, @"", @"", FALSE, kKeyCodeShiftLeft)
768 callback:^(BOOL handled){
769 }];
770
771 EXPECT_EQ([events count], 1u);
772 event = [events lastObject].data;
773 ASSERT_NE(event, nullptr);
774 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
775 EXPECT_EQ(event->physical, kPhysicalShiftLeft);
776 EXPECT_EQ(event->logical, kLogicalShiftLeft);
777 EXPECT_STREQ(event->character, nullptr);
778 EXPECT_EQ(event->synthesized, false);
779 [[events lastObject] respond:TRUE];
780
781 [events removeAllObjects];
782
783 [responder
784 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x100, @"", @"", FALSE, kKeyCodeShiftRight)
785 callback:^(BOOL handled){
786 }];
787
788 EXPECT_EQ([events count], 1u);
789 event = [events lastObject].data;
790 ASSERT_NE(event, nullptr);
791 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
792 EXPECT_EQ(event->physical, kPhysicalShiftRight);
793 EXPECT_EQ(event->logical, kLogicalShiftRight);
794 EXPECT_STREQ(event->character, nullptr);
795 EXPECT_EQ(event->synthesized, false);
796 [[events lastObject] respond:TRUE];
797
798 [events removeAllObjects];
799}
800
801// Process various cases where pair modifier key events are missed, and the
802// responder has to "guess" how to synchronize states.
803//
804// In the following comments, parentheses indicate missed events, while
805// asterisks indicate synthesized events.
806TEST(FlutterEmbedderKeyResponderUnittests, SynthesizeMissedModifierEvents) {
807 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
808 __block BOOL last_handled = TRUE;
809 id keyEventCallback = ^(BOOL handled) {
810 last_handled = handled;
811 };
813
815 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
816 void* _Nullable user_data) {
817 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
818 callback:callback
819 userData:user_data]];
820 }];
821
822 // Case 1:
823 // In: L down, (L up), L down, L up
824 // Out: L down, L up
825 last_handled = FALSE;
826 [responder
827 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x20102, @"", @"", FALSE, kKeyCodeShiftLeft)
828 callback:keyEventCallback];
829
830 EXPECT_EQ([events count], 1u);
831 event = [events lastObject].data;
832 ASSERT_NE(event, nullptr);
833 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
834 EXPECT_EQ(event->physical, kPhysicalShiftLeft);
835 EXPECT_EQ(event->logical, kLogicalShiftLeft);
836 EXPECT_STREQ(event->character, nullptr);
837 EXPECT_EQ(event->synthesized, false);
838
839 EXPECT_EQ(last_handled, FALSE);
840 EXPECT_TRUE([[events lastObject] hasCallback]);
841 [[events lastObject] respond:TRUE];
842 EXPECT_EQ(last_handled, TRUE);
843
844 [events removeAllObjects];
845
846 last_handled = FALSE;
847 [responder
848 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x20102, @"", @"", FALSE, kKeyCodeShiftLeft)
849 callback:keyEventCallback];
850
851 EXPECT_EQ([events count], 1u);
852 event = [events lastObject].data;
853 ASSERT_NE(event, nullptr);
854 EXPECT_EQ(event->physical, 0u);
855 EXPECT_EQ(event->logical, 0u);
856 EXPECT_FALSE([[events lastObject] hasCallback]);
857 EXPECT_EQ(last_handled, TRUE);
858 [events removeAllObjects];
859
860 last_handled = FALSE;
861 [responder
862 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x100, @"", @"", FALSE, kKeyCodeShiftLeft)
863 callback:keyEventCallback];
864
865 EXPECT_EQ([events count], 1u);
866 event = [events lastObject].data;
867 ASSERT_NE(event, nullptr);
868 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
869 EXPECT_EQ(event->physical, kPhysicalShiftLeft);
870 EXPECT_EQ(event->logical, kLogicalShiftLeft);
871 EXPECT_STREQ(event->character, nullptr);
872 EXPECT_EQ(event->synthesized, false);
873
874 EXPECT_EQ(last_handled, FALSE);
875 EXPECT_TRUE([[events lastObject] hasCallback]);
876 [[events lastObject] respond:TRUE];
877 EXPECT_EQ(last_handled, TRUE);
878
879 [events removeAllObjects];
880
881 // Case 2:
882 // In: (L down), L up
883 // Out:
884
885 last_handled = FALSE;
886 [responder
887 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x100, @"", @"", FALSE, kKeyCodeShiftLeft)
888 callback:keyEventCallback];
889
890 EXPECT_EQ([events count], 1u);
891 event = [events lastObject].data;
892 ASSERT_NE(event, nullptr);
893 EXPECT_EQ(event->physical, 0u);
894 EXPECT_EQ(event->logical, 0u);
895 EXPECT_FALSE([[events lastObject] hasCallback]);
896 EXPECT_EQ(last_handled, TRUE);
897 [events removeAllObjects];
898
899 // Case 3:
900 // In: L down, (L up), (R down), R up
901 // Out: L down, *L up
902
903 last_handled = FALSE;
904 [responder
905 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x20102, @"", @"", FALSE, kKeyCodeShiftLeft)
906 callback:keyEventCallback];
907
908 EXPECT_EQ([events count], 1u);
909 event = [events lastObject].data;
910 ASSERT_NE(event, nullptr);
911 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
912 EXPECT_EQ(event->physical, kPhysicalShiftLeft);
913 EXPECT_EQ(event->logical, kLogicalShiftLeft);
914 EXPECT_STREQ(event->character, nullptr);
915 EXPECT_EQ(event->synthesized, false);
916
917 EXPECT_EQ(last_handled, FALSE);
918 EXPECT_TRUE([[events lastObject] hasCallback]);
919 [[events lastObject] respond:TRUE];
920 EXPECT_EQ(last_handled, TRUE);
921
922 [events removeAllObjects];
923
924 last_handled = FALSE;
925 [responder
926 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x100, @"", @"", FALSE, kKeyCodeShiftRight)
927 callback:keyEventCallback];
928
929 EXPECT_EQ([events count], 1u);
930 event = [events lastObject].data;
931 ASSERT_NE(event, nullptr);
932 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
933 EXPECT_EQ(event->physical, kPhysicalShiftLeft);
934 EXPECT_EQ(event->logical, kLogicalShiftLeft);
935 EXPECT_STREQ(event->character, nullptr);
936 EXPECT_EQ(event->synthesized, true);
937
938 // The primary event is automatically replied with TRUE, unrelated to the received event.
939 EXPECT_EQ(last_handled, TRUE);
940 EXPECT_FALSE([[events lastObject] hasCallback]);
941
942 [events removeAllObjects];
943
944 // Case 4:
945 // In: L down, R down, (L up), R up
946 // Out: L down, R down *L up & R up
947
948 last_handled = FALSE;
949 [responder
950 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x20102, @"", @"", FALSE, kKeyCodeShiftLeft)
951 callback:keyEventCallback];
952
953 EXPECT_EQ([events count], 1u);
954 event = [events lastObject].data;
955 ASSERT_NE(event, nullptr);
956 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
957 EXPECT_EQ(event->physical, kPhysicalShiftLeft);
958 EXPECT_EQ(event->logical, kLogicalShiftLeft);
959 EXPECT_STREQ(event->character, nullptr);
960 EXPECT_EQ(event->synthesized, false);
961
962 EXPECT_EQ(last_handled, FALSE);
963 EXPECT_TRUE([[events lastObject] hasCallback]);
964 [[events lastObject] respond:TRUE];
965 EXPECT_EQ(last_handled, TRUE);
966
967 [events removeAllObjects];
968
969 last_handled = FALSE;
970 [responder
971 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x20106, @"", @"", FALSE, kKeyCodeShiftRight)
972 callback:keyEventCallback];
973
974 EXPECT_EQ([events count], 1u);
975 event = [events lastObject].data;
976 ASSERT_NE(event, nullptr);
977 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
978 EXPECT_EQ(event->physical, kPhysicalShiftRight);
979 EXPECT_EQ(event->logical, kLogicalShiftRight);
980 EXPECT_STREQ(event->character, nullptr);
981 EXPECT_EQ(event->synthesized, false);
982
983 EXPECT_EQ(last_handled, FALSE);
984 EXPECT_TRUE([[events lastObject] hasCallback]);
985 [[events lastObject] respond:TRUE];
986 EXPECT_EQ(last_handled, TRUE);
987
988 [events removeAllObjects];
989
990 last_handled = FALSE;
991 [responder
992 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x100, @"", @"", FALSE, kKeyCodeShiftRight)
993 callback:keyEventCallback];
994
995 EXPECT_EQ([events count], 2u);
996 event = [events firstObject].data;
997 ASSERT_NE(event, nullptr);
998 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
999 EXPECT_EQ(event->physical, kPhysicalShiftLeft);
1000 EXPECT_EQ(event->logical, kLogicalShiftLeft);
1001 EXPECT_STREQ(event->character, nullptr);
1002 EXPECT_EQ(event->synthesized, true);
1003
1004 EXPECT_FALSE([[events firstObject] hasCallback]);
1005
1006 event = [events lastObject].data;
1007 ASSERT_NE(event, nullptr);
1008 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
1009 EXPECT_EQ(event->physical, kPhysicalShiftRight);
1010 EXPECT_EQ(event->logical, kLogicalShiftRight);
1011 EXPECT_STREQ(event->character, nullptr);
1012 EXPECT_EQ(event->synthesized, false);
1013
1014 EXPECT_EQ(last_handled, FALSE);
1015 EXPECT_TRUE([[events lastObject] hasCallback]);
1016 [[events lastObject] respond:TRUE];
1017 EXPECT_EQ(last_handled, TRUE);
1018
1019 [events removeAllObjects];
1020}
1021
1022TEST(FlutterEmbedderKeyResponderUnittests, SynthesizeMissedModifierEventsInNormalEvents) {
1023 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
1024 __block BOOL last_handled = TRUE;
1025 id keyEventCallback = ^(BOOL handled) {
1026 last_handled = handled;
1027 };
1029
1031 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
1032 void* _Nullable user_data) {
1033 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
1034 callback:callback
1035 userData:user_data]];
1036 }];
1037
1038 // In: (LShift down), A down, (LShift up), A up
1039 // Out: *LS down & A down, *LS up & A up
1040
1041 last_handled = FALSE;
1042 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x20102, @"A", @"A", FALSE, kKeyCodeKeyA)
1043 callback:keyEventCallback];
1044
1045 EXPECT_EQ([events count], 2u);
1046 event = [events firstObject].data;
1047 ASSERT_NE(event, nullptr);
1048 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
1049 EXPECT_EQ(event->physical, kPhysicalShiftLeft);
1050 EXPECT_EQ(event->logical, kLogicalShiftLeft);
1051 EXPECT_STREQ(event->character, nullptr);
1052 EXPECT_EQ(event->synthesized, true);
1053 EXPECT_FALSE([[events firstObject] hasCallback]);
1054
1055 event = [events lastObject].data;
1056 ASSERT_NE(event, nullptr);
1057 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
1058 EXPECT_EQ(event->physical, kPhysicalKeyA);
1059 EXPECT_EQ(event->logical, kLogicalKeyA);
1060 EXPECT_STREQ(event->character, "A");
1061 EXPECT_EQ(event->synthesized, false);
1062 EXPECT_TRUE([[events lastObject] hasCallback]);
1063
1064 EXPECT_EQ(last_handled, FALSE);
1065 [[events lastObject] respond:TRUE];
1066 EXPECT_EQ(last_handled, TRUE);
1067
1068 [events removeAllObjects];
1069
1070 last_handled = FALSE;
1071 [responder handleEvent:keyEvent(NSEventTypeKeyUp, 0x100, @"a", @"a", FALSE, kKeyCodeKeyA)
1072 callback:keyEventCallback];
1073
1074 EXPECT_EQ([events count], 2u);
1075 event = [events firstObject].data;
1076 ASSERT_NE(event, nullptr);
1077 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
1078 EXPECT_EQ(event->physical, kPhysicalShiftLeft);
1079 EXPECT_EQ(event->logical, kLogicalShiftLeft);
1080 EXPECT_STREQ(event->character, nullptr);
1081 EXPECT_EQ(event->synthesized, true);
1082 EXPECT_FALSE([[events firstObject] hasCallback]);
1083
1084 event = [events lastObject].data;
1085 ASSERT_NE(event, nullptr);
1086 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
1087 EXPECT_EQ(event->physical, kPhysicalKeyA);
1088 EXPECT_EQ(event->logical, kLogicalKeyA);
1089 EXPECT_STREQ(event->character, nullptr);
1090 EXPECT_EQ(event->synthesized, false);
1091 EXPECT_TRUE([[events lastObject] hasCallback]);
1092
1093 EXPECT_EQ(last_handled, FALSE);
1094 [[events lastObject] respond:TRUE];
1095 EXPECT_EQ(last_handled, TRUE);
1096
1097 [events removeAllObjects];
1098}
1099
1100TEST(FlutterEmbedderKeyResponderUnittests, ConvertCapsLockEvents) {
1101 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
1102 __block BOOL last_handled = TRUE;
1103 id keyEventCallback = ^(BOOL handled) {
1104 last_handled = handled;
1105 };
1107
1109 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
1110 void* _Nullable user_data) {
1111 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
1112 callback:callback
1113 userData:user_data]];
1114 }];
1115
1116 // In: CapsLock down
1117 // Out: CapsLock down & *CapsLock Up
1118 last_handled = FALSE;
1119 [responder
1120 handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x10100, @"", @"", FALSE, kKeyCodeCapsLock)
1121 callback:keyEventCallback];
1122
1123 EXPECT_EQ([events count], 2u);
1124
1125 event = [events firstObject].data;
1126 ASSERT_NE(event, nullptr);
1127 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
1128 EXPECT_EQ(event->physical, kPhysicalCapsLock);
1129 EXPECT_EQ(event->logical, kLogicalCapsLock);
1130 EXPECT_STREQ(event->character, nullptr);
1131 EXPECT_EQ(event->synthesized, false);
1132 EXPECT_TRUE([[events firstObject] hasCallback]);
1133
1134 event = [events lastObject].data;
1135 ASSERT_NE(event, nullptr);
1136 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
1137 EXPECT_EQ(event->physical, kPhysicalCapsLock);
1138 EXPECT_EQ(event->logical, kLogicalCapsLock);
1139 EXPECT_STREQ(event->character, nullptr);
1140 EXPECT_EQ(event->synthesized, true);
1141 EXPECT_FALSE([[events lastObject] hasCallback]);
1142
1143 EXPECT_EQ(last_handled, FALSE);
1144 [[events firstObject] respond:TRUE];
1145 EXPECT_EQ(last_handled, TRUE);
1146
1147 [events removeAllObjects];
1148
1149 // In: CapsLock up
1150 // Out: CapsLock down & *CapsLock Up
1151 last_handled = FALSE;
1152 [responder handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x100, @"", @"", FALSE, kKeyCodeCapsLock)
1153 callback:keyEventCallback];
1154
1155 EXPECT_EQ([events count], 2u);
1156
1157 event = [events firstObject].data;
1158 ASSERT_NE(event, nullptr);
1159 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
1160 EXPECT_EQ(event->physical, kPhysicalCapsLock);
1161 EXPECT_EQ(event->logical, kLogicalCapsLock);
1162 EXPECT_STREQ(event->character, nullptr);
1163 EXPECT_EQ(event->synthesized, false);
1164 EXPECT_TRUE([[events firstObject] hasCallback]);
1165
1166 event = [events lastObject].data;
1167 ASSERT_NE(event, nullptr);
1168 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
1169 EXPECT_EQ(event->physical, kPhysicalCapsLock);
1170 EXPECT_EQ(event->logical, kLogicalCapsLock);
1171 EXPECT_STREQ(event->character, nullptr);
1172 EXPECT_EQ(event->synthesized, true);
1173 EXPECT_FALSE([[events lastObject] hasCallback]);
1174
1175 EXPECT_EQ(last_handled, FALSE);
1176 [[events firstObject] respond:TRUE];
1177 EXPECT_EQ(last_handled, TRUE);
1178
1179 [events removeAllObjects];
1180}
1181
1182// Press the CapsLock key when CapsLock state is desynchronized
1183TEST(FlutterEmbedderKeyResponderUnittests, SynchronizeCapsLockStateOnCapsLock) {
1184 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
1186 __block BOOL last_handled = TRUE;
1187 id keyEventCallback = ^(BOOL handled) {
1188 last_handled = handled;
1189 };
1190
1192 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
1193 void* _Nullable user_data) {
1194 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
1195 callback:callback
1196 userData:user_data]];
1197 }];
1198
1199 // In: CapsLock down
1200 // Out: (empty)
1201 last_handled = FALSE;
1202 [responder handleEvent:keyEvent(NSEventTypeFlagsChanged, 0x100, @"", @"", FALSE, kKeyCodeCapsLock)
1203 callback:keyEventCallback];
1204
1205 EXPECT_EQ([events count], 1u);
1206 EXPECT_EQ(last_handled, TRUE);
1207 event = [events lastObject].data;
1208 ASSERT_NE(event, nullptr);
1209 EXPECT_EQ(event->physical, 0ull);
1210 EXPECT_EQ(event->logical, 0ull);
1211 EXPECT_FALSE([[events lastObject] hasCallback]);
1212 EXPECT_EQ(last_handled, TRUE);
1213
1214 [events removeAllObjects];
1215}
1216
1217// Press the CapsLock key when CapsLock state is desynchronized
1218TEST(FlutterEmbedderKeyResponderUnittests, SynchronizeCapsLockStateOnNormalKey) {
1219 __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
1220 __block BOOL last_handled = TRUE;
1221 id keyEventCallback = ^(BOOL handled) {
1222 last_handled = handled;
1223 };
1225
1227 initWithSendEvent:^(const FlutterKeyEvent& event, _Nullable FlutterKeyEventCallback callback,
1228 void* _Nullable user_data) {
1229 [events addObject:[[TestKeyEvent alloc] initWithEvent:&event
1230 callback:callback
1231 userData:user_data]];
1232 }];
1233
1234 last_handled = FALSE;
1235 [responder handleEvent:keyEvent(NSEventTypeKeyDown, 0x10100, @"A", @"a", FALSE, kKeyCodeKeyA)
1236 callback:keyEventCallback];
1237
1238 EXPECT_EQ([events count], 3u);
1239
1240 event = events[0].data;
1241 ASSERT_NE(event, nullptr);
1242 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
1243 EXPECT_EQ(event->physical, kPhysicalCapsLock);
1244 EXPECT_EQ(event->logical, kLogicalCapsLock);
1245 EXPECT_STREQ(event->character, nullptr);
1246 EXPECT_EQ(event->synthesized, true);
1247 EXPECT_FALSE([events[0] hasCallback]);
1248
1249 event = events[1].data;
1250 ASSERT_NE(event, nullptr);
1251 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
1252 EXPECT_EQ(event->physical, kPhysicalCapsLock);
1253 EXPECT_EQ(event->logical, kLogicalCapsLock);
1254 EXPECT_STREQ(event->character, nullptr);
1255 EXPECT_EQ(event->synthesized, true);
1256 EXPECT_FALSE([events[1] hasCallback]);
1257
1258 event = events[2].data;
1259 ASSERT_NE(event, nullptr);
1260 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
1261 EXPECT_EQ(event->physical, kPhysicalKeyA);
1262 EXPECT_EQ(event->logical, kLogicalKeyA);
1263 EXPECT_STREQ(event->character, "A");
1264 EXPECT_EQ(event->synthesized, false);
1265 EXPECT_TRUE([events[2] hasCallback]);
1266
1267 EXPECT_EQ(last_handled, FALSE);
1268 [[events lastObject] respond:TRUE];
1269 EXPECT_EQ(last_handled, TRUE);
1270
1271 [events removeAllObjects];
1272}
1273
1274} // namespace flutter::testing
#define TEST(S, s, D, expected)
int count
void(* FlutterKeyEventCallback)(bool, void *)
Definition embedder.h:1153
@ kFlutterKeyEventTypeDown
Definition embedder.h:1074
@ kFlutterKeyEventTypeUp
Definition embedder.h:1073
@ kFlutterKeyEventTypeRepeat
Definition embedder.h:1075
FlKeyEvent * event
void handleEvent:callback:(NSEvent *event, [callback] FlutterAsyncKeyCallback callback)
FlutterKeyEventCallback callback
return FALSE
constexpr int kKeyCodeShiftLeft
#define EXPECT_TRUE(handle)
Definition unit_test.h:685
int BOOL