Flutter Engine
 
Loading...
Searching...
No Matches
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) - shouldNotHoldStrongReferenceToDelegate
 

Detailed Description

Definition at line 427 of file FlutterKeyboardManagerTest.mm.

Method Documentation

◆ correctLogicalKeyForLayouts

- (bool) correctLogicalKeyForLayouts

Definition at line 473 of file FlutterKeyboardManagerTest.mm.

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

◆ doublePrimaryResponder

- (bool) doublePrimaryResponder

Definition at line 473 of file FlutterKeyboardManagerTest.mm.

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

◆ emptyNextResponder

- (bool) emptyNextResponder

Definition at line 473 of file FlutterKeyboardManagerTest.mm.

609 {
610 KeyboardTester* tester = [[KeyboardTester alloc] init];
611 tester.nextResponder = nil;
612
613 [tester respondEmbedderCallsWith:false];
614 [tester respondChannelCallsWith:false];
615 [tester respondTextInputWith:false];
616 [tester.manager handleEvent:keyDownEvent(0x50) withContext:tester.eventContextMock];
617
618 // Passes if no error is thrown.
619 return true;
620}

◆ getPressedState

- (bool) getPressedState

Definition at line 473 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) withContext:tester.eventContextMock];
629
630 NSDictionary* pressingRecords = [tester.manager getPressedState];
631 EXPECT_EQ([pressingRecords count], 1u);
632 EXPECT_EQ(pressingRecords[@(kPhysicalKeyA)], @(kLogicalKeyA));
633
634 return true;
635}

◆ keyboardChannelGetPressedState

- (bool) keyboardChannelGetPressedState

Definition at line 473 of file FlutterKeyboardManagerTest.mm.

637 {
638 KeyboardTester* tester = [[KeyboardTester alloc] init];
639
640 [tester respondEmbedderCallsWith:false];
641 [tester respondChannelCallsWith:false];
642 [tester respondTextInputWith:false];
643 [tester.manager handleEvent:keyDownEvent(kVK_ANSI_A) withContext:tester.eventContextMock];
644
645 FlutterMethodCall* getKeyboardStateMethodCall =
646 [FlutterMethodCall methodCallWithMethodName:@"getKeyboardState" arguments:nil];
647 NSData* getKeyboardStateMessage =
648 [[FlutterStandardMethodCodec sharedInstance] encodeMethodCall:getKeyboardStateMethodCall];
649 [tester sendKeyboardChannelMessage:getKeyboardStateMessage];
650
651 id encodedResult = [tester lastKeyboardChannelResult];
652 id decoded = [[FlutterStandardMethodCodec sharedInstance] decodeEnvelope:encodedResult];
653
654 EXPECT_EQ([decoded count], 1u);
655 EXPECT_EQ(decoded[@(kPhysicalKeyA)], @(kLogicalKeyA));
656
657 return true;
658}
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
void sendKeyboardChannelMessage:(NSData *_Nullable message)

◆ racingConditionBetweenKeyAndText

- (bool) racingConditionBetweenKeyAndText

Definition at line 473 of file FlutterKeyboardManagerTest.mm.

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

◆ shouldNotHoldStrongReferenceToDelegate

- (bool) shouldNotHoldStrongReferenceToDelegate

Definition at line 473 of file FlutterKeyboardManagerTest.mm.

828 {
829 __strong FlutterKeyboardManager* strongKeyboardManager;
830 __weak id weakDelegate;
831
832 @autoreleasepool {
833 id binaryMessengerMock = OCMStrictProtocolMock(@protocol(FlutterBinaryMessenger));
834 OCMStub([binaryMessengerMock setMessageHandlerOnChannel:[OCMArg any]
835 binaryMessageHandler:[OCMArg any]]);
836
837 id delegateMock = OCMStrictProtocolMock(@protocol(FlutterKeyboardManagerDelegate));
838 OCMStub([delegateMock binaryMessenger]).andReturn(binaryMessengerMock);
839
840 FlutterKeyboardManager* keyboardManager =
841 [[FlutterKeyboardManager alloc] initWithDelegate:delegateMock];
842 strongKeyboardManager = keyboardManager;
843 weakDelegate = delegateMock;
844 }
845
846 return weakDelegate == nil;
847}

◆ singlePrimaryResponder

- (bool) singlePrimaryResponder

Definition at line 473 of file FlutterKeyboardManagerTest.mm.

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

◆ textInputPlugin

- (bool) textInputPlugin

Definition at line 473 of file FlutterKeyboardManagerTest.mm.

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

The documentation for this class was generated from the following file: