5 #include <Carbon/Carbon.h>
6 #import <Foundation/Foundation.h>
7 #import <OCMock/OCMock.h>
12 #include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
13 #import "flutter/testing/testing.h"
14 #include "third_party/googletest/googletest/include/gtest/gtest.h"
18 using flutter::testing::keycodes::kLogicalBracketLeft;
19 using flutter::testing::keycodes::kLogicalDigit1;
20 using flutter::testing::keycodes::kLogicalDigit2;
21 using flutter::testing::keycodes::kLogicalKeyA;
22 using flutter::testing::keycodes::kLogicalKeyM;
23 using flutter::testing::keycodes::kLogicalKeyQ;
24 using flutter::testing::keycodes::kLogicalKeyT;
25 using flutter::testing::keycodes::kPhysicalKeyA;
29 typedef BOOL (^BoolGetter)();
31 typedef void (^AsyncEmbedderCallbackHandler)(
const FlutterKeyEvent* event,
33 typedef BOOL (^TextInputCallback)(NSEvent*);
39 constexpr uint16_t kKeyCodeEmpty = 0x00;
42 constexpr uint32_t kEmbedderCall = 0x1;
43 constexpr uint32_t kChannelCall = 0x2;
44 constexpr uint32_t kTextCall = 0x4;
54 typedef const std::array<uint32_t, 256> MockLayoutData;
58 MockLayoutData kUsLayout = {
60 0x00061, 0x00041, 0x00073, 0x00053, 0x00064, 0x00044, 0x00066, 0x00046,
61 0x00068, 0x00048, 0x00067, 0x00047, 0x0007a, 0x0005a, 0x00078, 0x00058,
62 0x00063, 0x00043, 0x00076, 0x00056, 0x000a7, 0x000b1, 0x00062, 0x00042,
63 0x00071, 0x00051, 0x00077, 0x00057, 0x00065, 0x00045, 0x00072, 0x00052,
64 0x00079, 0x00059, 0x00074, 0x00054, 0x00031, 0x00021, 0x00032, 0x00040,
65 0x00033, 0x00023, 0x00034, 0x00024, 0x00036, 0x0005e, 0x00035, 0x00025,
66 0x0003d, 0x0002b, 0x00039, 0x00028, 0x00037, 0x00026, 0x0002d, 0x0005f,
67 0x00038, 0x0002a, 0x00030, 0x00029, 0x0005d, 0x0007d, 0x0006f, 0x0004f,
68 0x00075, 0x00055, 0x0005b, 0x0007b, 0x00069, 0x00049, 0x00070, 0x00050,
69 0x00000, 0x00000, 0x0006c, 0x0004c, 0x0006a, 0x0004a, 0x00027, 0x00022,
70 0x0006b, 0x0004b, 0x0003b, 0x0003a, 0x0005c, 0x0007c, 0x0002c, 0x0003c,
71 0x0002f, 0x0003f, 0x0006e, 0x0004e, 0x0006d, 0x0004d, 0x0002e, 0x0003e,
72 0x00000, 0x00000, 0x00020, 0x00020, 0x00060, 0x0007e,
75 MockLayoutData kFrenchLayout = {
77 0x00071, 0x00051, 0x00073, 0x00053, 0x00064, 0x00044, 0x00066, 0x00046,
78 0x00068, 0x00048, 0x00067, 0x00047, 0x00077, 0x00057, 0x00078, 0x00058,
79 0x00063, 0x00043, 0x00076, 0x00056, 0x00040, 0x00023, 0x00062, 0x00042,
80 0x00061, 0x00041, 0x0007a, 0x0005a, 0x00065, 0x00045, 0x00072, 0x00052,
81 0x00079, 0x00059, 0x00074, 0x00054, 0x00026, 0x00031, 0x000e9, 0x00032,
82 0x00022, 0x00033, 0x00027, 0x00034, 0x000a7, 0x00036, 0x00028, 0x00035,
83 0x0002d, 0x0005f, 0x000e7, 0x00039, 0x000e8, 0x00037, 0x00029, 0x000b0,
84 0x00021, 0x00038, 0x000e0, 0x00030, 0x00024, 0x0002a, 0x0006f, 0x0004f,
85 0x00075, 0x00055, 0x1005e, 0x100a8, 0x00069, 0x00049, 0x00070, 0x00050,
86 0x00000, 0x00000, 0x0006c, 0x0004c, 0x0006a, 0x0004a, 0x000f9, 0x00025,
87 0x0006b, 0x0004b, 0x0006d, 0x0004d, 0x10060, 0x000a3, 0x0003b, 0x0002e,
88 0x0003d, 0x0002b, 0x0006e, 0x0004e, 0x0002c, 0x0003f, 0x0003a, 0x0002f,
89 0x00000, 0x00000, 0x00020, 0x00020, 0x0003c, 0x0003e,
92 MockLayoutData kRussianLayout = {
94 0x00444, 0x00424, 0x0044b, 0x0042b, 0x00432, 0x00412, 0x00430, 0x00410,
95 0x00440, 0x00420, 0x0043f, 0x0041f, 0x0044f, 0x0042f, 0x00447, 0x00427,
96 0x00441, 0x00421, 0x0043c, 0x0041c, 0x0003e, 0x0003c, 0x00438, 0x00418,
97 0x00439, 0x00419, 0x00446, 0x00426, 0x00443, 0x00423, 0x0043a, 0x0041a,
98 0x0043d, 0x0041d, 0x00435, 0x00415, 0x00031, 0x00021, 0x00032, 0x00022,
99 0x00033, 0x02116, 0x00034, 0x00025, 0x00036, 0x0002c, 0x00035, 0x0003a,
100 0x0003d, 0x0002b, 0x00039, 0x00028, 0x00037, 0x0002e, 0x0002d, 0x0005f,
101 0x00038, 0x0003b, 0x00030, 0x00029, 0x0044a, 0x0042a, 0x00449, 0x00429,
102 0x00433, 0x00413, 0x00445, 0x00425, 0x00448, 0x00428, 0x00437, 0x00417,
103 0x00000, 0x00000, 0x00434, 0x00414, 0x0043e, 0x0041e, 0x0044d, 0x0042d,
104 0x0043b, 0x0041b, 0x00436, 0x00416, 0x00451, 0x00401, 0x00431, 0x00411,
105 0x0002f, 0x0003f, 0x00442, 0x00422, 0x0044c, 0x0042c, 0x0044e, 0x0042e,
106 0x00000, 0x00000, 0x00020, 0x00020, 0x0005d, 0x0005b,
109 MockLayoutData kKhmerLayout = {
111 0x017b6, 0x017ab, 0x0179f, 0x017c3, 0x0178a, 0x0178c, 0x01790, 0x01792,
112 0x017a0, 0x017c7, 0x01784, 0x017a2, 0x0178b, 0x0178d, 0x01781, 0x01783,
113 0x01785, 0x01787, 0x0179c, 0x017c8, 0x00000, 0x00000, 0x01794, 0x01796,
114 0x01786, 0x01788, 0x017b9, 0x017ba, 0x017c1, 0x017c2, 0x0179a, 0x017ac,
115 0x01799, 0x017bd, 0x0178f, 0x01791, 0x017e1, 0x00021, 0x017e2, 0x017d7,
116 0x017e3, 0x00022, 0x017e4, 0x017db, 0x017e6, 0x017cd, 0x017e5, 0x00025,
117 0x017b2, 0x017ce, 0x017e9, 0x017b0, 0x017e7, 0x017d0, 0x017a5, 0x017cc,
118 0x017e8, 0x017cf, 0x017e0, 0x017b3, 0x017aa, 0x017a7, 0x017c4, 0x017c5,
119 0x017bb, 0x017bc, 0x017c0, 0x017bf, 0x017b7, 0x017b8, 0x01795, 0x01797,
120 0x00000, 0x00000, 0x0179b, 0x017a1, 0x017d2, 0x01789, 0x017cb, 0x017c9,
121 0x01780, 0x01782, 0x017be, 0x017d6, 0x017ad, 0x017ae, 0x017a6, 0x017b1,
122 0x017ca, 0x017af, 0x01793, 0x0178e, 0x01798, 0x017c6, 0x017d4, 0x017d5,
123 0x00000, 0x00000, 0x00020, 0x0200b, 0x000ab, 0x000bb,
126 NSEvent* keyDownEvent(
unsigned short keyCode, NSString* chars =
@"", NSString* charsUnmod =
@"") {
127 return [NSEvent keyEventWithType:NSEventTypeKeyDown
134 charactersIgnoringModifiers:charsUnmod
139 NSEvent* keyUpEvent(
unsigned short keyCode) {
140 return [NSEvent keyEventWithType:NSEventTypeKeyUp
147 charactersIgnoringModifiers:
@""
152 id checkKeyDownEvent(
unsigned short keyCode) {
153 return [OCMArg checkWithBlock:^BOOL(
id value) {
154 if (![value isKindOfClass:[NSEvent
class]]) {
157 NSEvent*
event = value;
158 return event.keyCode == keyCode;
163 void clearEvents(std::vector<FlutterKeyEvent>& events) {
164 for (FlutterKeyEvent& event : events) {
165 if (event.character !=
nullptr) {
172 #define VERIFY_DOWN(OUT_LOGICAL, OUT_CHAR) \
173 EXPECT_EQ(events[0].type, kFlutterKeyEventTypeDown); \
174 EXPECT_EQ(events[0].logical, static_cast<uint64_t>(OUT_LOGICAL)); \
175 EXPECT_STREQ(events[0].character, (OUT_CHAR)); \
181 - (nonnull instancetype)
init;
184 - (void)respondEmbedderCallsWith:(BOOL)response;
191 - (void)recordEmbedderEventsTo:(nonnull std::vector<FlutterKeyEvent>*)storage
192 returning:(
bool)handled;
195 - (void)respondChannelCallsWith:(BOOL)response;
203 - (void)respondTextInputWith:(BOOL)response;
212 - (void)recordCallTypesTo:(nonnull NSMutableArray<NSNumber*>*)typeStorage
213 forTypes:(uint32_t)typeMask;
217 - (void)sendKeyboardChannelMessage:(NSData* _Nullable)message;
222 #pragma mark - Private
224 - (void)handleEmbedderEvent:(const FlutterKeyEvent&)event
225 callback:(nullable FlutterKeyEventCallback)callback
226 userData:(nullable
void*)userData;
228 - (void)handleChannelMessage:(NSString*)channel
229 message:(NSData* _Nullable)message
232 - (BOOL)handleTextInputKeyEvent:(NSEvent*)event;
236 AsyncEmbedderCallbackHandler _embedderHandler;
251 - (nonnull instancetype)init {
257 _nextResponder = OCMClassMock([NSResponder
class]);
267 binaryReply:[OCMArg any]])
268 .andCall(
self,
@selector(handleChannelMessage:message:binaryReply:));
269 OCMStub([
_messengerMock setMessageHandlerOnChannel:
@"flutter/keyboard"
270 binaryMessageHandler:[OCMArg any]])
271 .andCall(
self,
@selector(setKeyboardChannelHandler:handler:));
272 OCMStub([
_messengerMock sendOnChannel:
@"flutter/keyboard" message:[OCMArg any]])
273 .andCall(
self,
@selector(handleKeyboardChannelMessage:message:));
275 OCMStub([viewDelegateMock nextResponder]).andReturn(_nextResponder);
276 OCMStub([viewDelegateMock onTextInputKeyEvent:[OCMArg any]])
277 .andCall(
self,
@selector(handleTextInputKeyEvent:));
278 OCMStub([viewDelegateMock getBinaryMessenger]).andReturn(
_messengerMock);
279 OCMStub([viewDelegateMock sendKeyEvent:*(
const FlutterKeyEvent*)[OCMArg anyPointer]
282 .ignoringNonObjectArgs()
283 .andCall(
self,
@selector(handleEmbedderEvent:callback:userData:));
284 OCMStub([viewDelegateMock subscribeToKeyboardLayoutChange:[OCMArg any]])
285 .andCall(
self,
@selector(onSetKeyboardLayoutNotifier:));
286 OCMStub([viewDelegateMock lookUpLayoutForKeyCode:0 shift:
false])
287 .ignoringNonObjectArgs()
288 .andCall(
self,
@selector(lookUpLayoutForKeyCode:shift:));
294 - (id)lastKeyboardChannelResult {
298 - (void)respondEmbedderCallsWith:(BOOL)response {
306 [storage addObject:callback];
310 - (void)recordEmbedderEventsTo:(nonnull std::vector<FlutterKeyEvent>*)storage
311 returning:(
bool)handled {
313 FlutterKeyEvent newEvent = *event;
314 if (event->character !=
nullptr) {
315 size_t charLen = strlen(event->character);
316 char* newCharacter =
new char[charLen + 1];
317 strlcpy(newCharacter, event->character, charLen + 1);
318 newEvent.character = newCharacter;
320 storage->push_back(newEvent);
325 - (void)respondChannelCallsWith:(BOOL)response {
333 [storage addObject:callback];
337 - (void)respondTextInputWith:(BOOL)response {
343 - (void)recordCallTypesTo:(nonnull NSMutableArray<NSNumber*>*)typeStorage
344 forTypes:(uint32_t)typeMask {
349 - (void)sendKeyboardChannelMessage:(NSData* _Nullable)message {
350 [_messengerMock sendOnChannel:@"flutter/keyboard" message:message];
353 - (void)setLayout:(const MockLayoutData&)layout {
360 #pragma mark - Private
362 - (void)handleEmbedderEvent:(const FlutterKeyEvent&)event
363 callback:(nullable FlutterKeyEventCallback)callback
364 userData:(nullable
void*)userData {
366 [_typeStorage addObject:@(kEmbedderCall)];
368 if (callback !=
nullptr) {
369 _embedderHandler(&event, ^(BOOL handled) {
370 callback(handled, userData);
375 - (void)handleChannelMessage:(NSString*)channel
376 message:(NSData* _Nullable)message
379 [_typeStorage addObject:@(kChannelCall)];
382 NSDictionary* result = @{
383 @"handled" : @(handled),
386 callback(encodedKeyEvent);
390 - (void)handleKeyboardChannelMessage:(NSString*)channel message:(NSData* _Nullable)message {
396 - (BOOL)handleTextInputKeyEvent:(NSEvent*)event {
398 [_typeStorage addObject:@(kTextCall)];
407 - (LayoutClue)lookUpLayoutForKeyCode:(uint16_t)keyCode shift:(BOOL)shift {
408 uint32_t cluePair = (*_currentLayout)[(keyCode * 2) + (shift ? 1 : 0)];
409 const uint32_t kCharMask = 0xffff;
410 const uint32_t kDeadKeyMask = 0x10000;
411 return LayoutClue{cluePair & kCharMask, (cluePair & kDeadKeyMask) != 0};
434 TEST(FlutterKeyboardManagerUnittests, SinglePrimaryResponder) {
438 TEST(FlutterKeyboardManagerUnittests, DoublePrimaryResponder) {
442 TEST(FlutterKeyboardManagerUnittests, SingleFinalResponder) {
446 TEST(FlutterKeyboardManagerUnittests, EmptyNextResponder) {
450 TEST(FlutterKeyboardManagerUnittests, GetPressedState) {
454 TEST(FlutterKeyboardManagerUnittests, KeyboardChannelGetPressedState) {
458 TEST(FlutterKeyboardManagerUnittests, RacingConditionBetweenKeyAndText) {
462 TEST(FlutterKeyboardManagerUnittests, CorrectLogicalKeyForLayouts) {
466 TEST(FlutterKeyboardManagerUnittests, ShouldNotHoldStrongReferenceToViewDelegate) {
477 NSMutableArray<FlutterAsyncKeyCallback>* embedderCallbacks =
478 [NSMutableArray<FlutterAsyncKeyCallback> array];
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];
489 [tester.manager handleEvent:keyUpEvent(0x50)];
490 EXPECT_EQ([embedderCallbacks count], 1u);
491 embedderCallbacks[0](TRUE);
503 [tester.manager handleEvent:keyDownEvent(0x50)];
505 NSMutableArray<FlutterAsyncKeyCallback>* embedderCallbacks =
506 [NSMutableArray<FlutterAsyncKeyCallback> array];
507 NSMutableArray<FlutterAsyncKeyCallback>* channelCallbacks =
508 [NSMutableArray<FlutterAsyncKeyCallback> array];
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);
521 [embedderCallbacks removeAllObjects];
522 [channelCallbacks removeAllObjects];
527 [tester.manager handleEvent:keyDownEvent(0x50)];
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);
539 [embedderCallbacks removeAllObjects];
540 [channelCallbacks removeAllObjects];
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];
563 [tester.manager handleEvent:keyDownEvent(0x50)];
565 NSMutableArray<FlutterAsyncKeyCallback>* callbacks =
566 [NSMutableArray<FlutterAsyncKeyCallback> array];
572 [tester.manager handleEvent:keyUpEvent(0x50)];
573 EXPECT_EQ([callbacks count], 1u);
576 [callbacks removeAllObjects];
580 [tester.manager handleEvent:keyDownEvent(0x50)];
586 [tester.manager handleEvent:keyUpEvent(0x50)];
587 EXPECT_EQ([callbacks count], 1u);
590 [callbacks removeAllObjects];
594 [tester.manager handleEvent:keyDownEvent(0x50)];
595 EXPECT_EQ([callbacks count], 1u);
597 OCMVerify([tester.
nextResponder keyDown:checkKeyDownEvent(0x50)]);
598 [callbacks removeAllObjects];
610 [tester.manager handleEvent:keyDownEvent(0x50)];
622 [tester.manager handleEvent:keyDownEvent(kVK_ANSI_A)];
624 NSDictionary* pressingRecords = [tester.manager getPressedState];
625 EXPECT_EQ([pressingRecords count], 1u);
626 EXPECT_EQ(pressingRecords[@(kPhysicalKeyA)], @(kLogicalKeyA));
637 [tester.manager handleEvent:keyDownEvent(kVK_ANSI_A)];
641 NSData* getKeyboardStateMessage =
648 EXPECT_EQ([decoded count], 1u);
649 EXPECT_EQ(decoded[@(kPhysicalKeyA)], @(kLogicalKeyA));
662 NSMutableArray<FlutterAsyncKeyCallback>* keyCallbacks =
663 [NSMutableArray<FlutterAsyncKeyCallback> array];
666 NSMutableArray<NSNumber*>* allCalls = [NSMutableArray<NSNumber*> array];
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];
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];
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));
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);
709 keyCallbacks[0](
false);
710 EXPECT_EQ([keyCallbacks count], 2u);
711 EXPECT_EQ([allCalls count], 3u);
712 EXPECT_EQ(allCalls[1], @(kTextCall));
713 EXPECT_EQ(allCalls[2], @(kEmbedderCall));
717 keyCallbacks[1](
false);
718 EXPECT_EQ([keyCallbacks count], 3u);
719 EXPECT_EQ([allCalls count], 5u);
720 EXPECT_EQ(allCalls[3], @(kTextCall));
721 EXPECT_EQ(allCalls[4], @(kEmbedderCall));
724 keyCallbacks[2](
false);
725 keyCallbacks[3](
false);
734 std::vector<FlutterKeyEvent> events;
739 auto sendTap = [&](uint16_t keyCode, NSString* chars, NSString* charsUnmod) {
740 [tester.manager handleEvent:keyDownEvent(keyCode, chars, charsUnmod)];
741 [tester.manager handleEvent:keyUpEvent(keyCode)];
746 sendTap(kVK_ANSI_A,
@"a",
@"a");
749 sendTap(kVK_ANSI_A,
@"A",
@"A");
752 sendTap(kVK_ANSI_A,
@"å",
@"a");
755 sendTap(kVK_ANSI_T,
@"t",
@"t");
758 sendTap(kVK_ANSI_1,
@"1",
@"1");
761 sendTap(kVK_ANSI_1,
@"!",
@"!");
764 sendTap(kVK_ANSI_Minus,
@"-",
@"-");
767 sendTap(kVK_ANSI_Minus,
@"=",
@"=");
771 [tester setLayout:kFrenchLayout];
773 sendTap(kVK_ANSI_A,
@"q",
@"q");
776 sendTap(kVK_ANSI_A,
@"Q",
@"Q");
779 sendTap(kVK_ANSI_Semicolon,
@"m",
@"m");
782 sendTap(kVK_ANSI_M,
@",",
@",");
785 sendTap(kVK_ANSI_1,
@"&",
@"&");
788 sendTap(kVK_ANSI_1,
@"1",
@"1");
791 sendTap(kVK_ANSI_Minus,
@")",
@")");
794 sendTap(kVK_ANSI_Minus,
@"°",
@"°");
798 [tester setLayout:kRussianLayout];
800 sendTap(kVK_ANSI_A,
@"ф",
@"ф");
803 sendTap(kVK_ANSI_1,
@"1",
@"1");
806 sendTap(kVK_ANSI_LeftBracket,
@"х",
@"х");
811 [tester setLayout:kKhmerLayout];
813 sendTap(kVK_ANSI_2,
@"២",
@"២");
821 __weak
id weakViewDelegate;
825 OCMStub([binaryMessengerMock setMessageHandlerOnChannel:[OCMArg any]
826 binaryMessageHandler:[OCMArg any]]);
829 OCMStub([viewDelegateMock getBinaryMessenger]).andReturn(binaryMessengerMock);
830 OCMStub([viewDelegateMock subscribeToKeyboardLayoutChange:[OCMArg any]]);
832 LayoutClue layoutClue;
833 OCMStub([viewDelegateMock lookUpLayoutForKeyCode:0 shift:NO])
834 .ignoringNonObjectArgs()
835 .andReturn(layoutClue);
838 strongKeyboardManager = keyboardManager;
839 weakViewDelegate = viewDelegateMock;
842 return weakViewDelegate == nil;