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