Flutter macOS Embedder
FlutterKeyboardManagerUnittestsObjC Class Reference
Inheritance diagram for FlutterKeyboardManagerUnittestsObjC:

Instance Methods

(bool) - singlePrimaryResponder
 
(bool) - doublePrimaryResponder
 
(bool) - textInputPlugin
 
(bool) - emptyNextResponder
 
(bool) - getPressedState
 
(bool) - keyboardChannelGetPressedState
 
(bool) - racingConditionBetweenKeyAndText
 
(bool) - correctLogicalKeyForLayouts
 

Detailed Description

Definition at line 418 of file FlutterKeyboardManagerTest.mm.

Method Documentation

◆ correctLogicalKeyForLayouts

- (bool) correctLogicalKeyForLayouts

Definition at line 721 of file FlutterKeyboardManagerTest.mm.

721  {
722  KeyboardTester* tester = [[KeyboardTester alloc] init];
723  tester.nextResponder = nil;
724 
725  std::vector<FlutterKeyEvent> events;
726  [tester recordEmbedderEventsTo:&events returning:true];
727  [tester respondChannelCallsWith:false];
728  [tester respondTextInputWith:false];
729 
730  auto sendTap = [&](uint16_t keyCode, NSString* chars, NSString* charsUnmod) {
731  [tester.manager handleEvent:keyDownEvent(keyCode, chars, charsUnmod)];
732  [tester.manager handleEvent:keyUpEvent(keyCode)];
733  };
734 
735  /* US keyboard layout */
736 
737  sendTap(kVK_ANSI_A, @"a", @"a"); // KeyA
738  VERIFY_DOWN(kLogicalKeyA, "a");
739 
740  sendTap(kVK_ANSI_A, @"A", @"A"); // Shift-KeyA
741  VERIFY_DOWN(kLogicalKeyA, "A");
742 
743  sendTap(kVK_ANSI_A, @"Ã¥", @"a"); // Option-KeyA
744  VERIFY_DOWN(kLogicalKeyA, "Ã¥");
745 
746  sendTap(kVK_ANSI_T, @"t", @"t"); // KeyT
747  VERIFY_DOWN(kLogicalKeyT, "t");
748 
749  sendTap(kVK_ANSI_1, @"1", @"1"); // Digit1
750  VERIFY_DOWN(kLogicalDigit1, "1");
751 
752  sendTap(kVK_ANSI_1, @"!", @"!"); // Shift-Digit1
753  VERIFY_DOWN(kLogicalDigit1, "!");
754 
755  sendTap(kVK_ANSI_Minus, @"-", @"-"); // Minus
756  VERIFY_DOWN('-', "-");
757 
758  sendTap(kVK_ANSI_Minus, @"=", @"="); // Shift-Minus
759  VERIFY_DOWN('=', "=");
760 
761  /* French keyboard layout */
762  [tester setLayout:kFrenchLayout];
763 
764  sendTap(kVK_ANSI_A, @"q", @"q"); // KeyA
765  VERIFY_DOWN(kLogicalKeyQ, "q");
766 
767  sendTap(kVK_ANSI_A, @"Q", @"Q"); // Shift-KeyA
768  VERIFY_DOWN(kLogicalKeyQ, "Q");
769 
770  sendTap(kVK_ANSI_Semicolon, @"m", @"m"); // ; but prints M
771  VERIFY_DOWN(kLogicalKeyM, "m");
772 
773  sendTap(kVK_ANSI_M, @",", @","); // M but prints ,
774  VERIFY_DOWN(',', ",");
775 
776  sendTap(kVK_ANSI_1, @"&", @"&"); // Digit1
777  VERIFY_DOWN(kLogicalDigit1, "&");
778 
779  sendTap(kVK_ANSI_1, @"1", @"1"); // Shift-Digit1
780  VERIFY_DOWN(kLogicalDigit1, "1");
781 
782  sendTap(kVK_ANSI_Minus, @")", @")"); // Minus
783  VERIFY_DOWN(')', ")");
784 
785  sendTap(kVK_ANSI_Minus, @"°", @"°"); // Shift-Minus
786  VERIFY_DOWN(L'°', "°");
787 
788  /* Russian keyboard layout */
789  [tester setLayout:kRussianLayout];
790 
791  sendTap(kVK_ANSI_A, @"Ñ„", @"Ñ„"); // KeyA
792  VERIFY_DOWN(kLogicalKeyA, "Ñ„");
793 
794  sendTap(kVK_ANSI_1, @"1", @"1"); // Digit1
795  VERIFY_DOWN(kLogicalDigit1, "1");
796 
797  sendTap(kVK_ANSI_LeftBracket, @"Ñ…", @"Ñ…");
798  VERIFY_DOWN(kLogicalBracketLeft, "Ñ…");
799 
800  /* Khmer keyboard layout */
801  // Regression test for https://github.com/flutter/flutter/issues/108729
802  [tester setLayout:kKhmerLayout];
803 
804  sendTap(kVK_ANSI_2, @"២", @"២"); // Digit2
805  VERIFY_DOWN(kLogicalDigit2, "២");
806 
807  return TRUE;
808 }

References KeyboardTester::nextResponder, KeyboardTester::recordEmbedderEventsTo:returning:, KeyboardTester::respondChannelCallsWith:, KeyboardTester::respondTextInputWith:, and VERIFY_DOWN.

◆ doublePrimaryResponder

- (bool) doublePrimaryResponder

Definition at line 488 of file FlutterKeyboardManagerTest.mm.

488  {
489  KeyboardTester* tester = [[KeyboardTester alloc] init];
490 
491  // Send a down event first so we can send an up event later.
492  [tester respondEmbedderCallsWith:false];
493  [tester respondChannelCallsWith:false];
494  [tester.manager handleEvent:keyDownEvent(0x50)];
495 
496  NSMutableArray<FlutterAsyncKeyCallback>* embedderCallbacks =
497  [NSMutableArray<FlutterAsyncKeyCallback> array];
498  NSMutableArray<FlutterAsyncKeyCallback>* channelCallbacks =
499  [NSMutableArray<FlutterAsyncKeyCallback> array];
500  [tester recordEmbedderCallsTo:embedderCallbacks];
501  [tester recordChannelCallsTo:channelCallbacks];
502 
503  // Case: Both responders report TRUE.
504  [tester.manager handleEvent:keyUpEvent(0x50)];
505  EXPECT_EQ([embedderCallbacks count], 1u);
506  EXPECT_EQ([channelCallbacks count], 1u);
507  embedderCallbacks[0](TRUE);
508  channelCallbacks[0](TRUE);
509  EXPECT_EQ([embedderCallbacks count], 1u);
510  EXPECT_EQ([channelCallbacks count], 1u);
511  // [tester.nextResponder keyUp:] should not be called, otherwise an error will be thrown.
512  [embedderCallbacks removeAllObjects];
513  [channelCallbacks removeAllObjects];
514 
515  // Case: One responder reports TRUE.
516  [tester respondEmbedderCallsWith:false];
517  [tester respondChannelCallsWith:false];
518  [tester.manager handleEvent:keyDownEvent(0x50)];
519 
520  [tester recordEmbedderCallsTo:embedderCallbacks];
521  [tester recordChannelCallsTo:channelCallbacks];
522  [tester.manager handleEvent:keyUpEvent(0x50)];
523  EXPECT_EQ([embedderCallbacks count], 1u);
524  EXPECT_EQ([channelCallbacks count], 1u);
525  embedderCallbacks[0](FALSE);
526  channelCallbacks[0](TRUE);
527  EXPECT_EQ([embedderCallbacks count], 1u);
528  EXPECT_EQ([channelCallbacks count], 1u);
529  // [tester.nextResponder keyUp:] should not be called, otherwise an error will be thrown.
530  [embedderCallbacks removeAllObjects];
531  [channelCallbacks removeAllObjects];
532 
533  // Case: Both responders report FALSE.
534  [tester.manager handleEvent:keyDownEvent(0x53)];
535  EXPECT_EQ([embedderCallbacks count], 1u);
536  EXPECT_EQ([channelCallbacks count], 1u);
537  embedderCallbacks[0](FALSE);
538  channelCallbacks[0](FALSE);
539  EXPECT_EQ([embedderCallbacks count], 1u);
540  EXPECT_EQ([channelCallbacks count], 1u);
541  OCMVerify([tester.nextResponder keyDown:checkKeyDownEvent(0x53)]);
542  [embedderCallbacks removeAllObjects];
543  [channelCallbacks removeAllObjects];
544 
545  return true;
546 }

References KeyboardTester::nextResponder, KeyboardTester::recordChannelCallsTo:, KeyboardTester::recordEmbedderCallsTo:, KeyboardTester::respondChannelCallsWith:, and KeyboardTester::respondEmbedderCallsWith:.

◆ emptyNextResponder

- (bool) emptyNextResponder

Definition at line 594 of file FlutterKeyboardManagerTest.mm.

594  {
595  KeyboardTester* tester = [[KeyboardTester alloc] init];
596  tester.nextResponder = nil;
597 
598  [tester respondEmbedderCallsWith:false];
599  [tester respondChannelCallsWith:false];
600  [tester respondTextInputWith:false];
601  [tester.manager handleEvent:keyDownEvent(0x50)];
602 
603  // Passes if no error is thrown.
604  return true;
605 }

References KeyboardTester::nextResponder, KeyboardTester::respondChannelCallsWith:, KeyboardTester::respondEmbedderCallsWith:, and KeyboardTester::respondTextInputWith:.

◆ getPressedState

- (bool) getPressedState

Definition at line 607 of file FlutterKeyboardManagerTest.mm.

607  {
608  KeyboardTester* tester = [[KeyboardTester alloc] init];
609 
610  [tester respondEmbedderCallsWith:false];
611  [tester respondChannelCallsWith:false];
612  [tester respondTextInputWith:false];
613  [tester.manager handleEvent:keyDownEvent(kVK_ANSI_A)];
614 
615  NSDictionary* pressingRecords = [tester.manager getPressedState];
616  EXPECT_EQ([pressingRecords count], 1u);
617  EXPECT_EQ(pressingRecords[@(kPhysicalKeyA)], @(kLogicalKeyA));
618 
619  return true;
620 }

References KeyboardTester::respondChannelCallsWith:, KeyboardTester::respondEmbedderCallsWith:, and KeyboardTester::respondTextInputWith:.

◆ keyboardChannelGetPressedState

- (bool) keyboardChannelGetPressedState

Definition at line 622 of file FlutterKeyboardManagerTest.mm.

622  {
623  KeyboardTester* tester = [[KeyboardTester alloc] init];
624 
625  [tester respondEmbedderCallsWith:false];
626  [tester respondChannelCallsWith:false];
627  [tester respondTextInputWith:false];
628  [tester.manager handleEvent:keyDownEvent(kVK_ANSI_A)];
629 
630  FlutterMethodCall* getKeyboardStateMethodCall =
631  [FlutterMethodCall methodCallWithMethodName:@"getKeyboardState" arguments:nil];
632  NSData* getKeyboardStateMessage =
633  [[FlutterStandardMethodCodec sharedInstance] encodeMethodCall:getKeyboardStateMethodCall];
634  [tester sendKeyboardChannelMessage:getKeyboardStateMessage];
635 
636  id encodedResult = [tester lastKeyboardChannelResult];
637  id decoded = [[FlutterStandardMethodCodec sharedInstance] decodeEnvelope:encodedResult];
638 
639  EXPECT_EQ([decoded count], 1u);
640  EXPECT_EQ(decoded[@(kPhysicalKeyA)], @(kLogicalKeyA));
641 
642  return true;
643 }

References KeyboardTester::lastKeyboardChannelResult, FlutterMethodCall::methodCallWithMethodName:arguments:, KeyboardTester::respondChannelCallsWith:, KeyboardTester::respondEmbedderCallsWith:, KeyboardTester::respondTextInputWith:, and KeyboardTester::sendKeyboardChannelMessage:.

◆ racingConditionBetweenKeyAndText

- (bool) racingConditionBetweenKeyAndText

Definition at line 646 of file FlutterKeyboardManagerTest.mm.

646  {
647  KeyboardTester* tester = [[KeyboardTester alloc] init];
648 
649  // Use Vietnamese IME (GoTiengViet, Telex mode) to type "uco".
650 
651  // The events received by the framework. The engine might receive
652  // a channel message "setEditingState" from the framework.
653  NSMutableArray<FlutterAsyncKeyCallback>* keyCallbacks =
654  [NSMutableArray<FlutterAsyncKeyCallback> array];
655  [tester recordEmbedderCallsTo:keyCallbacks];
656 
657  NSMutableArray<NSNumber*>* allCalls = [NSMutableArray<NSNumber*> array];
658  [tester recordCallTypesTo:allCalls forTypes:(kEmbedderCall | kTextCall)];
659 
660  // Tap key U, which is converted by IME into a pure text message "ư".
661 
662  [tester.manager handleEvent:keyDownEvent(kKeyCodeEmpty, @"ư", @"ư")];
663  EXPECT_EQ([keyCallbacks count], 1u);
664  EXPECT_EQ([allCalls count], 1u);
665  EXPECT_EQ(allCalls[0], @(kEmbedderCall));
666  keyCallbacks[0](false);
667  EXPECT_EQ([keyCallbacks count], 1u);
668  EXPECT_EQ([allCalls count], 2u);
669  EXPECT_EQ(allCalls[1], @(kTextCall));
670  [keyCallbacks removeAllObjects];
671  [allCalls removeAllObjects];
672 
673  [tester.manager handleEvent:keyUpEvent(kKeyCodeEmpty)];
674  EXPECT_EQ([keyCallbacks count], 1u);
675  keyCallbacks[0](false);
676  EXPECT_EQ([keyCallbacks count], 1u);
677  EXPECT_EQ([allCalls count], 2u);
678  [keyCallbacks removeAllObjects];
679  [allCalls removeAllObjects];
680 
681  // Tap key O, which is converted to normal KeyO events, but the responses are
682  // slow.
683 
684  [tester.manager handleEvent:keyDownEvent(kVK_ANSI_O, @"o", @"o")];
685  [tester.manager handleEvent:keyUpEvent(kVK_ANSI_O)];
686  EXPECT_EQ([keyCallbacks count], 1u);
687  EXPECT_EQ([allCalls count], 1u);
688  EXPECT_EQ(allCalls[0], @(kEmbedderCall));
689 
690  // Tap key C, which results in two Backspace messages first - and here they
691  // arrive before the key O messages are responded.
692 
693  [tester.manager handleEvent:keyDownEvent(kVK_Delete)];
694  [tester.manager handleEvent:keyUpEvent(kVK_Delete)];
695  EXPECT_EQ([keyCallbacks count], 1u);
696  EXPECT_EQ([allCalls count], 1u);
697 
698  // The key O down is responded, which releases a text call (for KeyO down) and
699  // an embedder call (for KeyO up) immediately.
700  keyCallbacks[0](false);
701  EXPECT_EQ([keyCallbacks count], 2u);
702  EXPECT_EQ([allCalls count], 3u);
703  EXPECT_EQ(allCalls[1], @(kTextCall)); // The order is important!
704  EXPECT_EQ(allCalls[2], @(kEmbedderCall));
705 
706  // The key O up is responded, which releases a text call (for KeyO up) and
707  // an embedder call (for Backspace down) immediately.
708  keyCallbacks[1](false);
709  EXPECT_EQ([keyCallbacks count], 3u);
710  EXPECT_EQ([allCalls count], 5u);
711  EXPECT_EQ(allCalls[3], @(kTextCall)); // The order is important!
712  EXPECT_EQ(allCalls[4], @(kEmbedderCall));
713 
714  // Finish up callbacks.
715  keyCallbacks[2](false);
716  keyCallbacks[3](false);
717 
718  return true;
719 }

References KeyboardTester::recordCallTypesTo:forTypes:, and KeyboardTester::recordEmbedderCallsTo:.

◆ singlePrimaryResponder

- (bool) singlePrimaryResponder

Definition at line 466 of file FlutterKeyboardManagerTest.mm.

466  {
467  KeyboardTester* tester = [[KeyboardTester alloc] init];
468  NSMutableArray<FlutterAsyncKeyCallback>* embedderCallbacks =
469  [NSMutableArray<FlutterAsyncKeyCallback> array];
470  [tester recordEmbedderCallsTo:embedderCallbacks];
471 
472  // Case: The responder reports FALSE
473  [tester.manager handleEvent:keyDownEvent(0x50)];
474  EXPECT_EQ([embedderCallbacks count], 1u);
475  embedderCallbacks[0](FALSE);
476  OCMVerify([tester.nextResponder keyDown:checkKeyDownEvent(0x50)]);
477  [embedderCallbacks removeAllObjects];
478 
479  // Case: The responder reports TRUE
480  [tester.manager handleEvent:keyUpEvent(0x50)];
481  EXPECT_EQ([embedderCallbacks count], 1u);
482  embedderCallbacks[0](TRUE);
483  // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown.
484 
485  return true;
486 }

References KeyboardTester::nextResponder, and KeyboardTester::recordEmbedderCallsTo:.

◆ textInputPlugin

- (bool) textInputPlugin

Definition at line 548 of file FlutterKeyboardManagerTest.mm.

548  {
549  KeyboardTester* tester = [[KeyboardTester alloc] init];
550 
551  // Send a down event first so we can send an up event later.
552  [tester respondEmbedderCallsWith:false];
553  [tester respondChannelCallsWith:false];
554  [tester.manager handleEvent:keyDownEvent(0x50)];
555 
556  NSMutableArray<FlutterAsyncKeyCallback>* callbacks =
557  [NSMutableArray<FlutterAsyncKeyCallback> array];
558  [tester recordEmbedderCallsTo:callbacks];
559 
560  // Case: Primary responder responds TRUE. The event shouldn't be handled by
561  // the secondary responder.
562  [tester respondTextInputWith:FALSE];
563  [tester.manager handleEvent:keyUpEvent(0x50)];
564  EXPECT_EQ([callbacks count], 1u);
565  callbacks[0](TRUE);
566  // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown.
567  [callbacks removeAllObjects];
568 
569  // Send a down event first so we can send an up event later.
570  [tester respondEmbedderCallsWith:false];
571  [tester.manager handleEvent:keyDownEvent(0x50)];
572 
573  // Case: Primary responder responds FALSE. The secondary responder returns
574  // TRUE.
575  [tester recordEmbedderCallsTo:callbacks];
576  [tester respondTextInputWith:TRUE];
577  [tester.manager handleEvent:keyUpEvent(0x50)];
578  EXPECT_EQ([callbacks count], 1u);
579  callbacks[0](FALSE);
580  // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown.
581  [callbacks removeAllObjects];
582 
583  // Case: Primary responder responds FALSE. The secondary responder returns FALSE.
584  [tester respondTextInputWith:FALSE];
585  [tester.manager handleEvent:keyDownEvent(0x50)];
586  EXPECT_EQ([callbacks count], 1u);
587  callbacks[0](FALSE);
588  OCMVerify([tester.nextResponder keyDown:checkKeyDownEvent(0x50)]);
589  [callbacks removeAllObjects];
590 
591  return true;
592 }

References KeyboardTester::nextResponder, KeyboardTester::recordEmbedderCallsTo:, KeyboardTester::respondChannelCallsWith:, KeyboardTester::respondEmbedderCallsWith:, and KeyboardTester::respondTextInputWith:.


The documentation for this class was generated from the following file:
-[KeyboardTester recordEmbedderCallsTo:]
void recordEmbedderCallsTo:(nonnull NSMutableArray< FlutterAsyncKeyCallback > *storage)
Definition: FlutterKeyboardManagerTest.mm:302
-[KeyboardTester recordChannelCallsTo:]
void recordChannelCallsTo:(nonnull NSMutableArray< FlutterAsyncKeyCallback > *storage)
Definition: FlutterKeyboardManagerTest.mm:329
+[FlutterMethodCall methodCallWithMethodName:arguments:]
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
-[KeyboardTester respondTextInputWith:]
void respondTextInputWith:(BOOL response)
Definition: FlutterKeyboardManagerTest.mm:335
-[KeyboardTester recordEmbedderEventsTo:returning:]
void recordEmbedderEventsTo:returning:(nonnull std::vector< FlutterKeyEvent > *storage,[returning] bool handled)
Definition: FlutterKeyboardManagerTest.mm:308
-[KeyboardTester recordCallTypesTo:forTypes:]
void recordCallTypesTo:forTypes:(nonnull NSMutableArray< NSNumber * > *typeStorage,[forTypes] uint32_t typeMask)
Definition: FlutterKeyboardManagerTest.mm:341
-[KeyboardTester respondEmbedderCallsWith:]
void respondEmbedderCallsWith:(BOOL response)
Definition: FlutterKeyboardManagerTest.mm:296
KeyboardTester::nextResponder
NSResponder * nextResponder
Definition: FlutterKeyboardManagerTest.mm:220
FlutterMethodCall
Definition: FlutterCodecs.h:220
-[KeyboardTester lastKeyboardChannelResult]
id lastKeyboardChannelResult()
Definition: FlutterKeyboardManagerTest.mm:292
VERIFY_DOWN
#define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR)
Definition: FlutterKeyboardManagerTest.mm:172
-[KeyboardTester respondChannelCallsWith:]
void respondChannelCallsWith:(BOOL response)
Definition: FlutterKeyboardManagerTest.mm:323
FlutterStandardMethodCodec
Definition: FlutterCodecs.h:467
KeyboardTester
Definition: FlutterKeyboardManagerTest.mm:180
-[KeyboardTester sendKeyboardChannelMessage:]
void sendKeyboardChannelMessage:(NSData *_Nullable message)
Definition: FlutterKeyboardManagerTest.mm:347