Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
Instance Methods | List of all members
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 466 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}
void respondChannelCallsWith:(BOOL response)
void setLayout:(const MockLayoutData &layout)
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 466 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}
int count
void recordChannelCallsTo:(nonnull NSMutableArray< FlutterAsyncKeyCallback > *storage)
void respondEmbedderCallsWith:(BOOL response)
void recordEmbedderCallsTo:(nonnull NSMutableArray< FlutterAsyncKeyCallback > *storage)
return FALSE

◆ emptyNextResponder

- (bool) emptyNextResponder

Definition at line 466 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}

◆ getPressedState

- (bool) getPressedState

Definition at line 466 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}

◆ keyboardChannelGetPressedState

- (bool) keyboardChannelGetPressedState

Definition at line 466 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}
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
void sendKeyboardChannelMessage:(NSData *_Nullable message)

◆ racingConditionBetweenKeyAndText

- (bool) racingConditionBetweenKeyAndText

Definition at line 466 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}
void recordCallTypesTo:forTypes:(nonnull NSMutableArray< NSNumber * > *typeStorage,[forTypes] uint32_t typeMask)

◆ shouldNotHoldStrongReferenceToViewDelegate

- (bool) shouldNotHoldStrongReferenceToViewDelegate

Definition at line 466 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}
SIT bool any(const Vec< 1, T > &x)
Definition SkVx.h:530

◆ singlePrimaryResponder

- (bool) singlePrimaryResponder

Definition at line 466 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}

◆ textInputPlugin

- (bool) textInputPlugin

Definition at line 466 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}

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