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
 
(bool) - shouldNotHoldStrongReferenceToViewDelegate
 

Detailed Description

Definition at line 420 of file FlutterKeyboardManagerTest.mm.

Method Documentation

◆ correctLogicalKeyForLayouts

- (bool) correctLogicalKeyForLayouts

Definition at line 730 of file FlutterKeyboardManagerTest.mm.

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

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

◆ doublePrimaryResponder

- (bool) doublePrimaryResponder

Definition at line 497 of file FlutterKeyboardManagerTest.mm.

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

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

◆ emptyNextResponder

- (bool) emptyNextResponder

Definition at line 603 of file FlutterKeyboardManagerTest.mm.

603  {
604  KeyboardTester* tester = [[KeyboardTester alloc] init];
605  tester.nextResponder = nil;
606 
607  [tester respondEmbedderCallsWith:false];
608  [tester respondChannelCallsWith:false];
609  [tester respondTextInputWith:false];
610  [tester.manager handleEvent:keyDownEvent(0x50)];
611 
612  // Passes if no error is thrown.
613  return true;
614 }

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

◆ getPressedState

- (bool) getPressedState

Definition at line 616 of file FlutterKeyboardManagerTest.mm.

616  {
617  KeyboardTester* tester = [[KeyboardTester alloc] init];
618 
619  [tester respondEmbedderCallsWith:false];
620  [tester respondChannelCallsWith:false];
621  [tester respondTextInputWith:false];
622  [tester.manager handleEvent:keyDownEvent(kVK_ANSI_A)];
623 
624  NSDictionary* pressingRecords = [tester.manager getPressedState];
625  EXPECT_EQ([pressingRecords count], 1u);
626  EXPECT_EQ(pressingRecords[@(kPhysicalKeyA)], @(kLogicalKeyA));
627 
628  return true;
629 }

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

◆ keyboardChannelGetPressedState

- (bool) keyboardChannelGetPressedState

Definition at line 631 of file FlutterKeyboardManagerTest.mm.

631  {
632  KeyboardTester* tester = [[KeyboardTester alloc] init];
633 
634  [tester respondEmbedderCallsWith:false];
635  [tester respondChannelCallsWith:false];
636  [tester respondTextInputWith:false];
637  [tester.manager handleEvent:keyDownEvent(kVK_ANSI_A)];
638 
639  FlutterMethodCall* getKeyboardStateMethodCall =
640  [FlutterMethodCall methodCallWithMethodName:@"getKeyboardState" arguments:nil];
641  NSData* getKeyboardStateMessage =
642  [[FlutterStandardMethodCodec sharedInstance] encodeMethodCall:getKeyboardStateMethodCall];
643  [tester sendKeyboardChannelMessage:getKeyboardStateMessage];
644 
645  id encodedResult = [tester lastKeyboardChannelResult];
646  id decoded = [[FlutterStandardMethodCodec sharedInstance] decodeEnvelope:encodedResult];
647 
648  EXPECT_EQ([decoded count], 1u);
649  EXPECT_EQ(decoded[@(kPhysicalKeyA)], @(kLogicalKeyA));
650 
651  return true;
652 }

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

◆ racingConditionBetweenKeyAndText

- (bool) racingConditionBetweenKeyAndText

Definition at line 655 of file FlutterKeyboardManagerTest.mm.

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

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

◆ shouldNotHoldStrongReferenceToViewDelegate

- (bool) shouldNotHoldStrongReferenceToViewDelegate

Definition at line 819 of file FlutterKeyboardManagerTest.mm.

819  {
820  __strong FlutterKeyboardManager* strongKeyboardManager;
821  __weak id weakViewDelegate;
822 
823  @autoreleasepool {
824  id binaryMessengerMock = OCMStrictProtocolMock(@protocol(FlutterBinaryMessenger));
825  OCMStub([binaryMessengerMock setMessageHandlerOnChannel:[OCMArg any]
826  binaryMessageHandler:[OCMArg any]]);
827 
828  id viewDelegateMock = OCMStrictProtocolMock(@protocol(FlutterKeyboardViewDelegate));
829  OCMStub([viewDelegateMock getBinaryMessenger]).andReturn(binaryMessengerMock);
830  OCMStub([viewDelegateMock subscribeToKeyboardLayoutChange:[OCMArg any]]);
831 
832  LayoutClue layoutClue;
833  OCMStub([viewDelegateMock lookUpLayoutForKeyCode:0 shift:NO])
834  .ignoringNonObjectArgs()
835  .andReturn(layoutClue);
836  FlutterKeyboardManager* keyboardManager =
837  [[FlutterKeyboardManager alloc] initWithViewDelegate:viewDelegateMock];
838  strongKeyboardManager = keyboardManager;
839  weakViewDelegate = viewDelegateMock;
840  }
841 
842  return weakViewDelegate == nil;
843 }

◆ singlePrimaryResponder

- (bool) singlePrimaryResponder

Definition at line 475 of file FlutterKeyboardManagerTest.mm.

475  {
476  KeyboardTester* tester = [[KeyboardTester alloc] init];
477  NSMutableArray<FlutterAsyncKeyCallback>* embedderCallbacks =
478  [NSMutableArray<FlutterAsyncKeyCallback> array];
479  [tester recordEmbedderCallsTo:embedderCallbacks];
480 
481  // Case: The responder reports FALSE
482  [tester.manager handleEvent:keyDownEvent(0x50)];
483  EXPECT_EQ([embedderCallbacks count], 1u);
484  embedderCallbacks[0](FALSE);
485  OCMVerify([tester.nextResponder keyDown:checkKeyDownEvent(0x50)]);
486  [embedderCallbacks removeAllObjects];
487 
488  // Case: The responder reports TRUE
489  [tester.manager handleEvent:keyUpEvent(0x50)];
490  EXPECT_EQ([embedderCallbacks count], 1u);
491  embedderCallbacks[0](TRUE);
492  // [owner.nextResponder keyUp:] should not be called, otherwise an error will be thrown.
493 
494  return true;
495 }

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

◆ textInputPlugin

- (bool) textInputPlugin

Definition at line 557 of file FlutterKeyboardManagerTest.mm.

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

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:304
-[KeyboardTester recordChannelCallsTo:]
void recordChannelCallsTo:(nonnull NSMutableArray< FlutterAsyncKeyCallback > *storage)
Definition: FlutterKeyboardManagerTest.mm:331
+[FlutterMethodCall methodCallWithMethodName:arguments:]
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
FlutterKeyboardViewDelegate-p
Definition: FlutterKeyboardViewDelegate.h:42
-[KeyboardTester respondTextInputWith:]
void respondTextInputWith:(BOOL response)
Definition: FlutterKeyboardManagerTest.mm:337
-[KeyboardTester recordEmbedderEventsTo:returning:]
void recordEmbedderEventsTo:returning:(nonnull std::vector< FlutterKeyEvent > *storage,[returning] bool handled)
Definition: FlutterKeyboardManagerTest.mm:310
-[KeyboardTester recordCallTypesTo:forTypes:]
void recordCallTypesTo:forTypes:(nonnull NSMutableArray< NSNumber * > *typeStorage,[forTypes] uint32_t typeMask)
Definition: FlutterKeyboardManagerTest.mm:343
-[KeyboardTester respondEmbedderCallsWith:]
void respondEmbedderCallsWith:(BOOL response)
Definition: FlutterKeyboardManagerTest.mm:298
KeyboardTester::nextResponder
NSResponder * nextResponder
Definition: FlutterKeyboardManagerTest.mm:220
FlutterMethodCall
Definition: FlutterCodecs.h:220
-[KeyboardTester lastKeyboardChannelResult]
id lastKeyboardChannelResult()
Definition: FlutterKeyboardManagerTest.mm:294
VERIFY_DOWN
#define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR)
Definition: FlutterKeyboardManagerTest.mm:172
-[KeyboardTester respondChannelCallsWith:]
void respondChannelCallsWith:(BOOL response)
Definition: FlutterKeyboardManagerTest.mm:325
FlutterKeyboardManager
Definition: FlutterKeyboardManager.h:27
FlutterBinaryMessenger-p
Definition: FlutterBinaryMessenger.h:49
FlutterStandardMethodCodec
Definition: FlutterCodecs.h:469
KeyboardTester
Definition: FlutterKeyboardManagerTest.mm:180
-[KeyboardTester sendKeyboardChannelMessage:]
void sendKeyboardChannelMessage:(NSData *_Nullable message)
Definition: FlutterKeyboardManagerTest.mm:349