5 #include <Carbon/Carbon.h>
6 #import <Foundation/Foundation.h>
7 #import <OCMock/OCMock.h>
13 #include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
14 #import "flutter/testing/testing.h"
15 #include "third_party/googletest/googletest/include/gtest/gtest.h"
19 using flutter::testing::keycodes::kLogicalBracketLeft;
20 using flutter::testing::keycodes::kLogicalDigit1;
21 using flutter::testing::keycodes::kLogicalDigit2;
22 using flutter::testing::keycodes::kLogicalKeyA;
23 using flutter::testing::keycodes::kLogicalKeyM;
24 using flutter::testing::keycodes::kLogicalKeyQ;
25 using flutter::testing::keycodes::kLogicalKeyT;
26 using flutter::testing::keycodes::kPhysicalKeyA;
30 typedef BOOL (^BoolGetter)();
32 typedef void (^AsyncEmbedderCallbackHandler)(
const FlutterKeyEvent* event,
34 typedef BOOL (^TextInputCallback)(NSEvent*);
40 constexpr uint16_t kKeyCodeEmpty = 0x00;
43 constexpr uint32_t kEmbedderCall = 0x1;
44 constexpr uint32_t kChannelCall = 0x2;
45 constexpr uint32_t kTextCall = 0x4;
55 typedef const std::array<uint32_t, 256> MockLayoutData;
59 MockLayoutData kUsLayout = {
61 0x00061, 0x00041, 0x00073, 0x00053, 0x00064, 0x00044, 0x00066, 0x00046,
62 0x00068, 0x00048, 0x00067, 0x00047, 0x0007a, 0x0005a, 0x00078, 0x00058,
63 0x00063, 0x00043, 0x00076, 0x00056, 0x000a7, 0x000b1, 0x00062, 0x00042,
64 0x00071, 0x00051, 0x00077, 0x00057, 0x00065, 0x00045, 0x00072, 0x00052,
65 0x00079, 0x00059, 0x00074, 0x00054, 0x00031, 0x00021, 0x00032, 0x00040,
66 0x00033, 0x00023, 0x00034, 0x00024, 0x00036, 0x0005e, 0x00035, 0x00025,
67 0x0003d, 0x0002b, 0x00039, 0x00028, 0x00037, 0x00026, 0x0002d, 0x0005f,
68 0x00038, 0x0002a, 0x00030, 0x00029, 0x0005d, 0x0007d, 0x0006f, 0x0004f,
69 0x00075, 0x00055, 0x0005b, 0x0007b, 0x00069, 0x00049, 0x00070, 0x00050,
70 0x00000, 0x00000, 0x0006c, 0x0004c, 0x0006a, 0x0004a, 0x00027, 0x00022,
71 0x0006b, 0x0004b, 0x0003b, 0x0003a, 0x0005c, 0x0007c, 0x0002c, 0x0003c,
72 0x0002f, 0x0003f, 0x0006e, 0x0004e, 0x0006d, 0x0004d, 0x0002e, 0x0003e,
73 0x00000, 0x00000, 0x00020, 0x00020, 0x00060, 0x0007e,
76 MockLayoutData kFrenchLayout = {
78 0x00071, 0x00051, 0x00073, 0x00053, 0x00064, 0x00044, 0x00066, 0x00046,
79 0x00068, 0x00048, 0x00067, 0x00047, 0x00077, 0x00057, 0x00078, 0x00058,
80 0x00063, 0x00043, 0x00076, 0x00056, 0x00040, 0x00023, 0x00062, 0x00042,
81 0x00061, 0x00041, 0x0007a, 0x0005a, 0x00065, 0x00045, 0x00072, 0x00052,
82 0x00079, 0x00059, 0x00074, 0x00054, 0x00026, 0x00031, 0x000e9, 0x00032,
83 0x00022, 0x00033, 0x00027, 0x00034, 0x000a7, 0x00036, 0x00028, 0x00035,
84 0x0002d, 0x0005f, 0x000e7, 0x00039, 0x000e8, 0x00037, 0x00029, 0x000b0,
85 0x00021, 0x00038, 0x000e0, 0x00030, 0x00024, 0x0002a, 0x0006f, 0x0004f,
86 0x00075, 0x00055, 0x1005e, 0x100a8, 0x00069, 0x00049, 0x00070, 0x00050,
87 0x00000, 0x00000, 0x0006c, 0x0004c, 0x0006a, 0x0004a, 0x000f9, 0x00025,
88 0x0006b, 0x0004b, 0x0006d, 0x0004d, 0x10060, 0x000a3, 0x0003b, 0x0002e,
89 0x0003d, 0x0002b, 0x0006e, 0x0004e, 0x0002c, 0x0003f, 0x0003a, 0x0002f,
90 0x00000, 0x00000, 0x00020, 0x00020, 0x0003c, 0x0003e,
93 MockLayoutData kRussianLayout = {
95 0x00444, 0x00424, 0x0044b, 0x0042b, 0x00432, 0x00412, 0x00430, 0x00410,
96 0x00440, 0x00420, 0x0043f, 0x0041f, 0x0044f, 0x0042f, 0x00447, 0x00427,
97 0x00441, 0x00421, 0x0043c, 0x0041c, 0x0003e, 0x0003c, 0x00438, 0x00418,
98 0x00439, 0x00419, 0x00446, 0x00426, 0x00443, 0x00423, 0x0043a, 0x0041a,
99 0x0043d, 0x0041d, 0x00435, 0x00415, 0x00031, 0x00021, 0x00032, 0x00022,
100 0x00033, 0x02116, 0x00034, 0x00025, 0x00036, 0x0002c, 0x00035, 0x0003a,
101 0x0003d, 0x0002b, 0x00039, 0x00028, 0x00037, 0x0002e, 0x0002d, 0x0005f,
102 0x00038, 0x0003b, 0x00030, 0x00029, 0x0044a, 0x0042a, 0x00449, 0x00429,
103 0x00433, 0x00413, 0x00445, 0x00425, 0x00448, 0x00428, 0x00437, 0x00417,
104 0x00000, 0x00000, 0x00434, 0x00414, 0x0043e, 0x0041e, 0x0044d, 0x0042d,
105 0x0043b, 0x0041b, 0x00436, 0x00416, 0x00451, 0x00401, 0x00431, 0x00411,
106 0x0002f, 0x0003f, 0x00442, 0x00422, 0x0044c, 0x0042c, 0x0044e, 0x0042e,
107 0x00000, 0x00000, 0x00020, 0x00020, 0x0005d, 0x0005b,
110 MockLayoutData kKhmerLayout = {
112 0x017b6, 0x017ab, 0x0179f, 0x017c3, 0x0178a, 0x0178c, 0x01790, 0x01792,
113 0x017a0, 0x017c7, 0x01784, 0x017a2, 0x0178b, 0x0178d, 0x01781, 0x01783,
114 0x01785, 0x01787, 0x0179c, 0x017c8, 0x00000, 0x00000, 0x01794, 0x01796,
115 0x01786, 0x01788, 0x017b9, 0x017ba, 0x017c1, 0x017c2, 0x0179a, 0x017ac,
116 0x01799, 0x017bd, 0x0178f, 0x01791, 0x017e1, 0x00021, 0x017e2, 0x017d7,
117 0x017e3, 0x00022, 0x017e4, 0x017db, 0x017e6, 0x017cd, 0x017e5, 0x00025,
118 0x017b2, 0x017ce, 0x017e9, 0x017b0, 0x017e7, 0x017d0, 0x017a5, 0x017cc,
119 0x017e8, 0x017cf, 0x017e0, 0x017b3, 0x017aa, 0x017a7, 0x017c4, 0x017c5,
120 0x017bb, 0x017bc, 0x017c0, 0x017bf, 0x017b7, 0x017b8, 0x01795, 0x01797,
121 0x00000, 0x00000, 0x0179b, 0x017a1, 0x017d2, 0x01789, 0x017cb, 0x017c9,
122 0x01780, 0x01782, 0x017be, 0x017d6, 0x017ad, 0x017ae, 0x017a6, 0x017b1,
123 0x017ca, 0x017af, 0x01793, 0x0178e, 0x01798, 0x017c6, 0x017d4, 0x017d5,
124 0x00000, 0x00000, 0x00020, 0x0200b, 0x000ab, 0x000bb,
127 NSEvent* keyDownEvent(
unsigned short keyCode, NSString* chars =
@"", NSString* charsUnmod =
@"") {
128 return [NSEvent keyEventWithType:NSEventTypeKeyDown
135 charactersIgnoringModifiers:charsUnmod
140 NSEvent* keyUpEvent(
unsigned short keyCode) {
141 return [NSEvent keyEventWithType:NSEventTypeKeyUp
148 charactersIgnoringModifiers:
@""
153 id checkKeyDownEvent(
unsigned short keyCode) {
154 return [OCMArg checkWithBlock:^BOOL(
id value) {
155 if (![value isKindOfClass:[NSEvent
class]]) {
158 NSEvent*
event = value;
159 return event.keyCode == keyCode;
164 void clearEvents(std::vector<FlutterKeyEvent>& events) {
165 for (FlutterKeyEvent& event : events) {
166 if (event.character !=
nullptr) {
173 #define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR) \
174 EXPECT_EQ(events[0].type, kFlutterKeyEventTypeDown); \
175 EXPECT_EQ(events[0].logical, static_cast<uint64_t>(OUT_LOGICAL)); \
176 EXPECT_STREQ(events[0].character, (OUT_CHAR)); \
182 - (nonnull instancetype)
init;
185 - (void)respondEmbedderCallsWith:(BOOL)response;
192 - (void)recordEmbedderEventsTo:(nonnull std::vector<FlutterKeyEvent>*)storage
193 returning:(
bool)handled;
196 - (void)respondChannelCallsWith:(BOOL)response;
204 - (void)respondTextInputWith:(BOOL)response;
213 - (void)recordCallTypesTo:(nonnull NSMutableArray<NSNumber*>*)typeStorage
214 forTypes:(uint32_t)typeMask;
218 - (void)sendKeyboardChannelMessage:(NSData* _Nullable)message;
221 @property(readonly, nonatomic, strong) id<FlutterKeyboardManagerEventContext>
eventContextMock;
224 #pragma mark - Private
226 - (void)handleEmbedderEvent:(const FlutterKeyEvent&)event
227 callback:(nullable FlutterKeyEventCallback)callback
228 userData:(nullable
void*)userData;
230 - (void)handleChannelMessage:(NSString*)channel
231 message:(NSData* _Nullable)message
234 - (BOOL)handleTextInputKeyEvent:(NSEvent*)event;
238 AsyncEmbedderCallbackHandler _embedderHandler;
255 - (nonnull instancetype)init {
261 _nextResponder = OCMClassMock([NSResponder
class]);
271 binaryReply:[OCMArg any]])
272 .andCall(
self,
@selector(handleChannelMessage:message:binaryReply:));
273 OCMStub([
_messengerMock setMessageHandlerOnChannel:
@"flutter/keyboard"
274 binaryMessageHandler:[OCMArg any]])
275 .andCall(
self,
@selector(setKeyboardChannelHandler:handler:));
276 OCMStub([
_messengerMock sendOnChannel:
@"flutter/keyboard" message:[OCMArg any]])
277 .andCall(
self,
@selector(handleKeyboardChannelMessage:message:));
279 OCMStub([managerDelegateMock binaryMessenger]).andReturn(
_messengerMock);
280 OCMStub([managerDelegateMock sendKeyEvent:*(
const FlutterKeyEvent*)[OCMArg anyPointer]
283 .ignoringNonObjectArgs()
284 .andCall(
self,
@selector(handleEmbedderEvent:callback:userData:));
289 .andCall(
self,
@selector(handleTextInputKeyEvent:));
292 OCMStub([keyboardLayoutMock lookUpLayoutForKeyCode:0 shift:
false])
293 .ignoringNonObjectArgs()
294 .andCall(
self,
@selector(lookUpLayoutForKeyCode:shift:));
295 OCMStub([keyboardLayoutMock setDelegate:[OCMArg any]])
296 .andCall(
self,
@selector(onSetKeyboardLayoutDelegate:));
299 keyboardLayout:keyboardLayoutMock];
303 - (id)lastKeyboardChannelResult {
307 - (void)respondEmbedderCallsWith:(BOOL)response {
315 [storage addObject:callback];
319 - (void)recordEmbedderEventsTo:(nonnull std::vector<FlutterKeyEvent>*)storage
320 returning:(
bool)handled {
322 FlutterKeyEvent newEvent = *event;
323 if (event->character !=
nullptr) {
324 size_t charLen = strlen(event->character);
325 char* newCharacter =
new char[charLen + 1];
326 strlcpy(newCharacter, event->character, charLen + 1);
327 newEvent.character = newCharacter;
329 storage->push_back(newEvent);
334 - (void)respondChannelCallsWith:(BOOL)response {
342 [storage addObject:callback];
346 - (void)respondTextInputWith:(BOOL)response {
352 - (void)recordCallTypesTo:(nonnull NSMutableArray<NSNumber*>*)typeStorage
353 forTypes:(uint32_t)typeMask {
358 - (void)sendKeyboardChannelMessage:(NSData* _Nullable)message {
359 [_messengerMock sendOnChannel:@"flutter/keyboard" message:message];
362 - (void)setLayout:(const MockLayoutData&)layout {
364 [_keyboardLayoutDelegate keyboardLayoutDidChange];
367 #pragma mark - Private
369 - (void)handleEmbedderEvent:(const FlutterKeyEvent&)event
370 callback:(nullable FlutterKeyEventCallback)callback
371 userData:(nullable
void*)userData {
373 [_typeStorage addObject:@(kEmbedderCall)];
375 if (callback !=
nullptr) {
376 _embedderHandler(&event, ^(BOOL handled) {
377 callback(handled, userData);
382 - (void)handleChannelMessage:(NSString*)channel
383 message:(NSData* _Nullable)message
386 [_typeStorage addObject:@(kChannelCall)];
389 NSDictionary* result = @{
390 @"handled" : @(handled),
393 callback(encodedKeyEvent);
397 - (void)handleKeyboardChannelMessage:(NSString*)channel message:(NSData* _Nullable)message {
403 - (BOOL)handleTextInputKeyEvent:(NSEvent*)event {
405 [_typeStorage addObject:@(kTextCall)];
414 - (LayoutClue)lookUpLayoutForKeyCode:(uint16_t)keyCode shift:(BOOL)shift {
415 uint32_t cluePair = (*_currentLayout)[(keyCode * 2) + (shift ? 1 : 0)];
416 const uint32_t kCharMask = 0xffff;
417 const uint32_t kDeadKeyMask = 0x10000;
418 return LayoutClue{cluePair & kCharMask, (cluePair & kDeadKeyMask) != 0};
441 TEST(FlutterKeyboardManagerUnittests, SinglePrimaryResponder) {
445 TEST(FlutterKeyboardManagerUnittests, DoublePrimaryResponder) {
449 TEST(FlutterKeyboardManagerUnittests, SingleFinalResponder) {
453 TEST(FlutterKeyboardManagerUnittests, EmptyNextResponder) {
457 TEST(FlutterKeyboardManagerUnittests, GetPressedState) {
461 TEST(FlutterKeyboardManagerUnittests, KeyboardChannelGetPressedState) {
465 TEST(FlutterKeyboardManagerUnittests, RacingConditionBetweenKeyAndText) {
469 TEST(FlutterKeyboardManagerUnittests, CorrectLogicalKeyForLayouts) {
473 TEST(FlutterKeyboardManagerUnittests, ShouldNotHoldStrongReferenceToDelegate) {
483 NSMutableArray<FlutterAsyncKeyCallback>* embedderCallbacks =
484 [NSMutableArray<FlutterAsyncKeyCallback> array];
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];
495 [tester.manager handleEvent:keyUpEvent(0x50) withContext:tester.eventContextMock];
496 EXPECT_EQ([embedderCallbacks count], 1u);
497 embedderCallbacks[0](TRUE);
509 [tester.manager handleEvent:keyDownEvent(0x50) withContext:tester.eventContextMock];
511 NSMutableArray<FlutterAsyncKeyCallback>* embedderCallbacks =
512 [NSMutableArray<FlutterAsyncKeyCallback> array];
513 NSMutableArray<FlutterAsyncKeyCallback>* channelCallbacks =
514 [NSMutableArray<FlutterAsyncKeyCallback> array];
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);
527 [embedderCallbacks removeAllObjects];
528 [channelCallbacks removeAllObjects];
533 [tester.manager handleEvent:keyDownEvent(0x50) withContext:tester.eventContextMock];
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);
545 [embedderCallbacks removeAllObjects];
546 [channelCallbacks removeAllObjects];
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];
569 [tester.manager handleEvent:keyDownEvent(0x50) withContext:tester.eventContextMock];
571 NSMutableArray<FlutterAsyncKeyCallback>* callbacks =
572 [NSMutableArray<FlutterAsyncKeyCallback> array];
578 [tester.manager handleEvent:keyUpEvent(0x50) withContext:tester.eventContextMock];
579 EXPECT_EQ([callbacks count], 1u);
582 [callbacks removeAllObjects];
586 [tester.manager handleEvent:keyDownEvent(0x50) withContext:tester.eventContextMock];
592 [tester.manager handleEvent:keyUpEvent(0x50) withContext:tester.eventContextMock];
593 EXPECT_EQ([callbacks count], 1u);
596 [callbacks removeAllObjects];
600 [tester.manager handleEvent:keyDownEvent(0x50) withContext:tester.eventContextMock];
601 EXPECT_EQ([callbacks count], 1u);
603 OCMVerify([tester.
nextResponder keyDown:checkKeyDownEvent(0x50)]);
604 [callbacks removeAllObjects];
616 [tester.manager handleEvent:keyDownEvent(0x50) withContext:tester.eventContextMock];
628 [tester.manager handleEvent:keyDownEvent(kVK_ANSI_A) withContext:tester.eventContextMock];
630 NSDictionary* pressingRecords = [tester.manager getPressedState];
631 EXPECT_EQ([pressingRecords count], 1u);
632 EXPECT_EQ(pressingRecords[@(kPhysicalKeyA)], @(kLogicalKeyA));
643 [tester.manager handleEvent:keyDownEvent(kVK_ANSI_A) withContext:tester.eventContextMock];
647 NSData* getKeyboardStateMessage =
654 EXPECT_EQ([decoded count], 1u);
655 EXPECT_EQ(decoded[@(kPhysicalKeyA)], @(kLogicalKeyA));
668 NSMutableArray<FlutterAsyncKeyCallback>* keyCallbacks =
669 [NSMutableArray<FlutterAsyncKeyCallback> array];
672 NSMutableArray<NSNumber*>* allCalls = [NSMutableArray<NSNumber*> array];
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];
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];
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));
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);
717 keyCallbacks[0](
false);
718 EXPECT_EQ([keyCallbacks count], 2u);
719 EXPECT_EQ([allCalls count], 3u);
720 EXPECT_EQ(allCalls[1], @(kTextCall));
721 EXPECT_EQ(allCalls[2], @(kEmbedderCall));
725 keyCallbacks[1](
false);
726 EXPECT_EQ([keyCallbacks count], 3u);
727 EXPECT_EQ([allCalls count], 5u);
728 EXPECT_EQ(allCalls[3], @(kTextCall));
729 EXPECT_EQ(allCalls[4], @(kEmbedderCall));
732 keyCallbacks[2](
false);
733 keyCallbacks[3](
false);
742 std::vector<FlutterKeyEvent> events;
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];
755 sendTap(kVK_ANSI_A,
@"a",
@"a");
758 sendTap(kVK_ANSI_A,
@"A",
@"A");
761 sendTap(kVK_ANSI_A,
@"Ã¥",
@"a");
764 sendTap(kVK_ANSI_T,
@"t",
@"t");
767 sendTap(kVK_ANSI_1,
@"1",
@"1");
770 sendTap(kVK_ANSI_1,
@"!",
@"!");
773 sendTap(kVK_ANSI_Minus,
@"-",
@"-");
776 sendTap(kVK_ANSI_Minus,
@"=",
@"=");
780 [tester setLayout:kFrenchLayout];
782 sendTap(kVK_ANSI_A,
@"q",
@"q");
785 sendTap(kVK_ANSI_A,
@"Q",
@"Q");
788 sendTap(kVK_ANSI_Semicolon,
@"m",
@"m");
791 sendTap(kVK_ANSI_M,
@",",
@",");
794 sendTap(kVK_ANSI_1,
@"&",
@"&");
797 sendTap(kVK_ANSI_1,
@"1",
@"1");
800 sendTap(kVK_ANSI_Minus,
@")",
@")");
803 sendTap(kVK_ANSI_Minus,
@"°",
@"°");
807 [tester setLayout:kRussianLayout];
809 sendTap(kVK_ANSI_A,
@"Ñ„",
@"Ñ„");
812 sendTap(kVK_ANSI_1,
@"1",
@"1");
815 sendTap(kVK_ANSI_LeftBracket,
@"Ñ…",
@"Ñ…");
820 [tester setLayout:kKhmerLayout];
822 sendTap(kVK_ANSI_2,
@"២",
@"២");
830 __weak
id weakDelegate;
834 OCMStub([binaryMessengerMock setMessageHandlerOnChannel:[OCMArg any]
835 binaryMessageHandler:[OCMArg any]]);
838 OCMStub([delegateMock binaryMessenger]).andReturn(binaryMessengerMock);
842 strongKeyboardManager = keyboardManager;
843 weakDelegate = delegateMock;
846 return weakDelegate == nil;