Flutter macOS Embedder
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 
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;
20 - (BOOL)hasCallback;
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 
41 - (BOOL)hasCallback {
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 
60 namespace flutter::testing {
61 
62 namespace {
63 constexpr uint64_t kKeyCodeKeyA = 0x00;
64 constexpr uint64_t kKeyCodeKeyW = 0x0d;
65 constexpr uint64_t kKeyCodeShiftLeft = 0x38;
66 constexpr uint64_t kKeyCodeShiftRight = 0x3c;
67 constexpr uint64_t kKeyCodeCapsLock = 0x39;
68 constexpr uint64_t kKeyCodeNumpad1 = 0x53;
69 constexpr uint64_t kKeyCodeF1 = 0x7a;
70 constexpr uint64_t kKeyCodeAltRight = 0x3d;
71 
72 using namespace ::flutter::testing::keycodes;
73 
74 typedef void (^ResponseCallback)(bool handled);
75 
76 NSEvent* 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 
94 NSEvent* 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.
118 TEST(FlutterEmbedderKeyResponderUnittests, BasicKeyEvent) {
119  __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
120  __block BOOL last_handled = TRUE;
121  FlutterKeyEvent* event;
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 
200 TEST(FlutterEmbedderKeyResponderUnittests, NonAsciiCharacters) {
201  __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
202  FlutterKeyEvent* event;
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 
274 TEST(FlutterEmbedderKeyResponderUnittests, MultipleCharacters) {
275  __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
276  FlutterKeyEvent* event;
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 
317 TEST(FlutterEmbedderKeyResponderUnittests, SynthesizeForDuplicateDownEvent) {
318  __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
319  __block BOOL last_handled = TRUE;
320  FlutterKeyEvent* event;
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 
379 TEST(FlutterEmbedderKeyResponderUnittests, IgnoreDuplicateUpEvent) {
380  __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
381  FlutterKeyEvent* event;
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 
410 TEST(FlutterEmbedderKeyResponderUnittests, ConvertAbruptRepeatEventsToDown) {
411  __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
412  __block BOOL last_handled = TRUE;
413  FlutterKeyEvent* event;
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.
448 TEST(FlutterEmbedderKeyResponderUnittests, ToggleModifiersDuringKeyTap) {
449  __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
450  FlutterKeyEvent* event;
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.
564 TEST(FlutterEmbedderKeyResponderUnittests, SpecialModiferFlags) {
565  __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
566  FlutterKeyEvent* event;
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 
720 TEST(FlutterEmbedderKeyResponderUnittests, IdentifyLeftAndRightModifiers) {
721  __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
722  FlutterKeyEvent* event;
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.
806 TEST(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  };
812  FlutterKeyEvent* event;
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 
1022 TEST(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  };
1028  FlutterKeyEvent* event;
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 
1100 TEST(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  };
1106  FlutterKeyEvent* event;
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
1183 TEST(FlutterEmbedderKeyResponderUnittests, SynchronizeCapsLockStateOnCapsLock) {
1184  __block NSMutableArray<TestKeyEvent*>* events = [[NSMutableArray<TestKeyEvent*> alloc] init];
1185  FlutterKeyEvent* event;
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
1218 TEST(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  };
1224  FlutterKeyEvent* event;
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
flutter::testing::TEST
TEST(FlutterEmbedderKeyResponderUnittests, SynchronizeCapsLockStateOnNormalKey)
Definition: FlutterEmbedderKeyResponderTest.mm:1218
TestKeyEvent::data
FlutterKeyEvent * data
Definition: FlutterEmbedderKeyResponderTest.mm:14
-[TestKeyEvent hasCallback]
BOOL hasCallback()
Definition: FlutterEmbedderKeyResponderTest.mm:41
TestKeyEvent
Definition: FlutterEmbedderKeyResponderTest.mm:13
-[FlutterKeyPrimaryResponder-p handleEvent:callback:]
void handleEvent:callback:(nonnull NSEvent *event,[callback] nonnull FlutterAsyncKeyCallback callback)
flutter::testing
Definition: AccessibilityBridgeMacTest.mm:13
FlutterEmbedderKeyResponder.h
TestKeyEvent::callback
FlutterKeyEventCallback callback
Definition: FlutterEmbedderKeyResponderTest.mm:15
TestKeyEvent::userData
void * userData
Definition: FlutterEmbedderKeyResponderTest.mm:16
FlutterEmbedderKeyResponder
Definition: FlutterEmbedderKeyResponder.h:23