9 #import <OCMock/OCMock.h>
18 #include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
19 #import "flutter/testing/testing.h"
21 #pragma mark - Test Helper Classes
25 @property(nonatomic) FlutterKeyEvent*
data;
26 - (nonnull instancetype)initWithEvent:(
const FlutterKeyEvent*)event;
30 - (instancetype)initWithEvent:(const FlutterKeyEvent*)event {
32 _data =
new FlutterKeyEvent(*event);
44 @property(nonatomic, assign) BOOL mouseDownCalled;
45 @property(nonatomic, assign) BOOL mouseUpCalled;
49 - (void)mouseDown:(NSEvent*)event {
53 - (void)mouseUp:(NSEvent*)event {
54 self.mouseUpCalled = YES;
75 + (void)respondFalseForSendEvent:(const FlutterKeyEvent&)event
76 callback:(nullable FlutterKeyEventCallback)callback
77 userData:(nullable
void*)userData;
80 #pragma mark - Static helper functions
82 using namespace ::flutter::testing::keycodes;
88 id MockGestureEvent(NSEventType type, NSEventPhase phase,
double magnification,
double rotation) {
89 id event = [OCMockObject mockForClass:[NSEvent class]];
90 NSPoint locationInWindow = NSMakePoint(0, 0);
93 NSTimeInterval timestamp = 1;
94 NSUInteger modifierFlags = 0;
95 [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(type)] type];
96 [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(phase)] phase];
97 [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(locationInWindow)] locationInWindow];
98 [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(deltaX)] deltaX];
99 [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(deltaY)] deltaY];
100 [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(timestamp)] timestamp];
101 [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(modifierFlags)] modifierFlags];
102 [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(magnification)] magnification];
103 [(NSEvent*)[[event stub] andReturnValue:OCMOCK_VALUE(rotation)] rotation];
109 NSString* fixtures = @(testing::GetFixturesPath());
111 initWithAssetsPath:fixtures
112 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
113 return [[
FlutterEngine alloc] initWithName:@"test" project:project allowHeadlessExecution:true];
116 NSResponder* mockResponder() {
117 NSResponder* mock = OCMStrictClassMock([NSResponder
class]);
118 OCMStub([mock keyDown:[OCMArg any]]).andDo(nil);
119 OCMStub([mock keyUp:[OCMArg any]]).andDo(nil);
120 OCMStub([mock flagsChanged:[OCMArg any]]).andDo(nil);
124 NSEvent* CreateMouseEvent(NSEventModifierFlags modifierFlags) {
125 return [NSEvent mouseEventWithType:NSEventTypeMouseMoved
127 modifierFlags:modifierFlags
138 #pragma mark - gtest tests
143 [viewControllerMock loadView];
144 auto subViews = [viewControllerMock.view subviews];
146 EXPECT_EQ([subViews count], 1u);
147 EXPECT_EQ(subViews[0], viewControllerMock.flutterView);
149 NSTextField* textField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)];
150 [viewControllerMock.view addSubview:textField];
152 subViews = [viewControllerMock.view subviews];
153 EXPECT_EQ([subViews count], 2u);
155 auto accessibilityChildren = viewControllerMock.view.accessibilityChildren;
157 EXPECT_EQ([accessibilityChildren count], 1u);
158 EXPECT_EQ(accessibilityChildren[0], viewControllerMock.flutterView);
163 [viewControllerMock loadView];
164 EXPECT_EQ([viewControllerMock.flutterView acceptsFirstMouse:nil], YES);
172 [viewController loadView];
173 [engine setViewController:viewController];
175 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600)
176 styleMask:NSBorderlessWindowMask
177 backing:NSBackingStoreBuffered
179 window.contentView = viewController.view;
180 NSView* dummyView = [[NSView alloc] initWithFrame:CGRectZero];
181 [viewController.view addSubview:dummyView];
183 [dummyView addSubview:viewController.textInputPlugin];
185 EXPECT_TRUE([window makeFirstResponder:viewController.textInputPlugin]);
186 EXPECT_EQ([window firstResponder], viewController.textInputPlugin);
187 EXPECT_FALSE(viewController.textInputPlugin.superview == viewController.view);
190 EXPECT_TRUE(viewController.textInputPlugin.superview == viewController.view);
194 NSString* fixtures = @(testing::GetFixturesPath());
196 initWithAssetsPath:fixtures
197 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
200 ASSERT_EQ(viewController.
mouseTrackingMode, kFlutterMouseTrackingModeInActiveApp);
203 TEST(FlutterViewControllerTest, TestKeyEventsAreSentToFramework) {
207 TEST(FlutterViewControllerTest, TestKeyEventsArePropagatedIfNotHandled) {
211 TEST(FlutterViewControllerTest, TestKeyEventsAreNotPropagatedIfHandled) {
215 TEST(FlutterViewControllerTest, TestCtrlTabKeyEventIsPropagated) {
219 TEST(FlutterViewControllerTest, TestKeyEquivalentIsPassedToTextInputPlugin) {
223 TEST(FlutterViewControllerTest, TestFlagsChangedEventsArePropagatedIfNotHandled) {
228 TEST(FlutterViewControllerTest, TestKeyboardIsRestartedOnEngineRestart) {
232 TEST(FlutterViewControllerTest, TestTrackpadGesturesAreSentToFramework) {
236 TEST(FlutterViewControllerTest, TestMouseDownUpEventsSentToNextResponder) {
240 TEST(FlutterViewControllerTest, TestModifierKeysAreSynthesizedOnMouseMove) {
244 TEST(FlutterViewControllerTest, testViewWillAppearCalledMultipleTimes) {
248 TEST(FlutterViewControllerTest, testFlutterViewIsConfigured) {
252 TEST(FlutterViewControllerTest, testLookupKeyAssets) {
256 TEST(FlutterViewControllerTest, testLookupKeyAssetsWithPackage) {
260 TEST(FlutterViewControllerTest, testViewControllerIsReleased) {
266 #pragma mark - FlutterViewControllerTestObjC
274 [engineMock binaryMessenger])
275 .andReturn(binaryMessengerMock);
276 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {}
280 @selector(respondFalseForSendEvent:callback:userData:));
284 NSDictionary* expectedEvent = @{
285 @"keymap" :
@"macos",
286 @"type" :
@"keydown",
288 @"modifiers" : @(538968064),
289 @"characters" :
@".",
290 @"charactersIgnoringModifiers" :
@".",
293 CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 65, TRUE);
294 NSEvent*
event = [NSEvent eventWithCGEvent:cgEvent];
295 [viewController viewWillAppear];
296 [viewController keyDown:event];
299 [binaryMessengerMock sendOnChannel:
@"flutter/keyevent"
300 message:encodedKeyEvent
301 binaryReply:[OCMArg any]]);
311 __block
bool called =
false;
312 __block FlutterKeyEvent last_event;
313 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {}
316 .andDo((^(NSInvocation* invocation) {
317 FlutterKeyEvent* event;
318 [invocation getArgument:&event atIndex:2];
326 NSEvent*
event = [NSEvent keyEventWithType:NSEventTypeKeyDown
328 modifierFlags:0x40101
333 charactersIgnoringModifiers:@""
336 const uint64_t kPhysicalKeyTab = 0x7002b;
338 [viewController viewWillAppear];
340 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600)
341 styleMask:NSBorderlessWindowMask
342 backing:NSBackingStoreBuffered
344 window.contentView = viewController.view;
345 [window makeFirstResponder:viewController.flutterView];
346 [viewController.view performKeyEquivalent:event];
349 EXPECT_EQ(last_event.type, kFlutterKeyEventTypeDown);
350 EXPECT_EQ(last_event.physical, kPhysicalKeyTab);
356 __block
bool called =
false;
357 __block FlutterKeyEvent last_event;
358 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {}
361 .andDo((^(NSInvocation* invocation) {
362 FlutterKeyEvent* event;
363 [invocation getArgument:&event atIndex:2];
371 NSEvent*
event = [NSEvent keyEventWithType:NSEventTypeKeyDown
373 modifierFlags:0x40101
378 charactersIgnoringModifiers:@""
381 const uint64_t kPhysicalKeyTab = 0x7002b;
383 [viewController viewWillAppear];
385 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600)
386 styleMask:NSBorderlessWindowMask
387 backing:NSBackingStoreBuffered
389 window.contentView = viewController.view;
391 [viewController.view addSubview:viewController.textInputPlugin];
395 [window makeFirstResponder:viewController.textInputPlugin];
397 [viewController.view performKeyEquivalent:event];
400 EXPECT_EQ(last_event.type, kFlutterKeyEventTypeDown);
401 EXPECT_EQ(last_event.physical, kPhysicalKeyTab);
409 [engineMock binaryMessenger])
410 .andReturn(binaryMessengerMock);
411 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {}
415 @selector(respondFalseForSendEvent:callback:userData:));
419 id responderMock = flutter::testing::mockResponder();
420 viewController.nextResponder = responderMock;
421 NSDictionary* expectedEvent = @{
422 @"keymap" :
@"macos",
423 @"type" :
@"keydown",
425 @"modifiers" : @(538968064),
426 @"characters" :
@".",
427 @"charactersIgnoringModifiers" :
@".",
430 CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 65, TRUE);
431 NSEvent*
event = [NSEvent eventWithCGEvent:cgEvent];
433 [binaryMessengerMock sendOnChannel:
@"flutter/keyevent"
434 message:encodedKeyEvent
435 binaryReply:[OCMArg any]])
436 .andDo((^(NSInvocation* invocation) {
438 [invocation getArgument:&handler atIndex:4];
439 NSDictionary* reply = @{
440 @"handled" : @(
false),
443 handler(encodedReply);
445 [viewController viewWillAppear];
446 [viewController keyDown:event];
449 [responderMock keyDown:[OCMArg any]]);
451 [binaryMessengerMock sendOnChannel:
@"flutter/keyevent"
452 message:encodedKeyEvent
453 binaryReply:[OCMArg any]]);
464 OCMStub([engineMock renderer]).andReturn(renderer_);
469 [viewController loadView];
473 OCMVerify([engineMock renderer]);
485 [engineMock binaryMessenger])
486 .andReturn(binaryMessengerMock);
487 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {}
491 @selector(respondFalseForSendEvent:callback:userData:));
495 id responderMock = flutter::testing::mockResponder();
496 viewController.nextResponder = responderMock;
497 NSDictionary* expectedEvent = @{
498 @"keymap" :
@"macos",
499 @"type" :
@"keydown",
501 @"modifiers" : @(537001986),
504 CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 56, TRUE);
505 CGEventSetType(cgEvent, kCGEventFlagsChanged);
506 NSEvent*
event = [NSEvent eventWithCGEvent:cgEvent];
508 [binaryMessengerMock sendOnChannel:
@"flutter/keyevent"
509 message:encodedKeyEvent
510 binaryReply:[OCMArg any]])
511 .andDo((^(NSInvocation* invocation) {
513 [invocation getArgument:&handler atIndex:4];
514 NSDictionary* reply = @{
515 @"handled" : @(
false),
518 handler(encodedReply);
520 [viewController viewWillAppear];
521 [viewController flagsChanged:event];
524 [binaryMessengerMock sendOnChannel:
@"flutter/keyevent"
525 message:encodedKeyEvent
526 binaryReply:[OCMArg any]]);
527 }
@catch (NSException* e) {
528 NSLog(
@"%@", e.reason);
538 [engineMock binaryMessenger])
539 .andReturn(binaryMessengerMock);
540 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {}
544 @selector(respondFalseForSendEvent:callback:userData:));
548 id responderMock = flutter::testing::mockResponder();
549 viewController.nextResponder = responderMock;
550 NSDictionary* expectedEvent = @{
551 @"keymap" :
@"macos",
552 @"type" :
@"keydown",
554 @"modifiers" : @(538968064),
555 @"characters" :
@".",
556 @"charactersIgnoringModifiers" :
@".",
559 CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 65, TRUE);
560 NSEvent*
event = [NSEvent eventWithCGEvent:cgEvent];
562 [binaryMessengerMock sendOnChannel:
@"flutter/keyevent"
563 message:encodedKeyEvent
564 binaryReply:[OCMArg any]])
565 .andDo((^(NSInvocation* invocation) {
567 [invocation getArgument:&handler atIndex:4];
568 NSDictionary* reply = @{
569 @"handled" : @(
true),
572 handler(encodedReply);
574 [viewController viewWillAppear];
575 [viewController keyDown:event];
578 never(), [responderMock keyDown:[OCMArg any]]);
580 [binaryMessengerMock sendOnChannel:
@"flutter/keyevent"
581 message:encodedKeyEvent
582 binaryReply:[OCMArg any]]);
593 [engineMock binaryMessenger])
594 .andReturn(binaryMessengerMock);
595 __block
bool called =
false;
596 __block FlutterKeyEvent last_event;
597 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {}
600 .andDo((^(NSInvocation* invocation) {
601 FlutterKeyEvent* event;
602 [invocation getArgument:&event atIndex:2];
610 [viewController viewWillAppear];
611 NSEvent* keyADown = [NSEvent keyEventWithType:NSEventTypeKeyDown
618 charactersIgnoringModifiers:@"a"
621 const uint64_t kPhysicalKeyA = 0x70004;
628 [viewController keyDown:keyADown];
630 EXPECT_EQ(last_event.type, kFlutterKeyEventTypeDown);
631 EXPECT_EQ(last_event.physical, kPhysicalKeyA);
636 [viewController keyDown:keyADown];
638 EXPECT_EQ(last_event.type, kFlutterKeyEventTypeDown);
639 EXPECT_EQ(last_event.physical, kPhysicalKeyA);
643 + (void)respondFalseForSendEvent:(const FlutterKeyEvent&)event
644 callback:(nullable FlutterKeyEventCallback)callback
645 userData:(nullable
void*)userData {
646 if (callback !=
nullptr) {
647 callback(
false, userData);
655 OCMStub([engineMock renderer]).andReturn(renderer_);
656 __block
bool called =
false;
657 __block FlutterPointerEvent last_event;
658 OCMStub([[engineMock ignoringNonObjectArgs] sendPointerEvent:FlutterPointerEvent{}])
659 .andDo((^(NSInvocation* invocation) {
660 FlutterPointerEvent* event;
661 [invocation getArgument:&event atIndex:2];
669 [viewController loadView];
673 CGEventRef cgEventStart = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 1, 0);
674 CGEventSetType(cgEventStart, kCGEventScrollWheel);
675 CGEventSetIntegerValueField(cgEventStart, kCGScrollWheelEventScrollPhase, kCGScrollPhaseBegan);
676 CGEventSetIntegerValueField(cgEventStart, kCGScrollWheelEventIsContinuous, 1);
679 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventStart]];
681 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
682 EXPECT_EQ(last_event.phase, kPanZoomStart);
683 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
684 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
687 CGEventRef cgEventUpdate = CGEventCreateCopy(cgEventStart);
688 CGEventSetIntegerValueField(cgEventUpdate, kCGScrollWheelEventScrollPhase, kCGScrollPhaseChanged);
689 CGEventSetIntegerValueField(cgEventUpdate, kCGScrollWheelEventDeltaAxis2, 1);
690 CGEventSetIntegerValueField(cgEventUpdate, kCGScrollWheelEventDeltaAxis1, 2);
693 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventUpdate]];
695 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
696 EXPECT_EQ(last_event.phase, kPanZoomUpdate);
697 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
698 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
699 EXPECT_EQ(last_event.pan_x, 8 * viewController.flutterView.layer.contentsScale);
700 EXPECT_EQ(last_event.pan_y, 16 * viewController.flutterView.layer.contentsScale);
704 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventUpdate]];
706 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
707 EXPECT_EQ(last_event.phase, kPanZoomUpdate);
708 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
709 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
710 EXPECT_EQ(last_event.pan_x, 16 * viewController.flutterView.layer.contentsScale);
711 EXPECT_EQ(last_event.pan_y, 32 * viewController.flutterView.layer.contentsScale);
714 CGEventRef cgEventEnd = CGEventCreateCopy(cgEventStart);
715 CGEventSetIntegerValueField(cgEventEnd, kCGScrollWheelEventScrollPhase, kCGScrollPhaseEnded);
718 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventEnd]];
720 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
721 EXPECT_EQ(last_event.phase, kPanZoomEnd);
722 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
723 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
726 CGEventRef cgEventMomentumStart = CGEventCreateCopy(cgEventStart);
727 CGEventSetIntegerValueField(cgEventMomentumStart, kCGScrollWheelEventScrollPhase, 0);
728 CGEventSetIntegerValueField(cgEventMomentumStart, kCGScrollWheelEventMomentumPhase,
729 kCGMomentumScrollPhaseBegin);
732 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventMomentumStart]];
733 EXPECT_FALSE(called);
736 CGEventRef cgEventMomentumUpdate = CGEventCreateCopy(cgEventStart);
737 CGEventSetIntegerValueField(cgEventMomentumUpdate, kCGScrollWheelEventScrollPhase, 0);
738 CGEventSetIntegerValueField(cgEventMomentumUpdate, kCGScrollWheelEventMomentumPhase,
739 kCGMomentumScrollPhaseContinue);
742 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventMomentumUpdate]];
743 EXPECT_FALSE(called);
746 id touchMock = OCMClassMock([NSTouch
class]);
747 NSSet* touchSet = [NSSet setWithObject:touchMock];
748 id touchEventMock1 = OCMClassMock([NSEvent
class]);
749 OCMStub([touchEventMock1 allTouches]).andReturn(touchSet);
750 CGPoint touchLocation = {0, 0};
751 OCMStub([touchEventMock1 locationInWindow]).andReturn(touchLocation);
752 OCMStub([(NSEvent*)touchEventMock1 timestamp]).andReturn(0.150);
756 [viewController touchesBeganWithEvent:touchEventMock1];
757 EXPECT_FALSE(called);
760 id touchEventMock2 = OCMClassMock([NSEvent
class]);
761 OCMStub([touchEventMock2 allTouches]).andReturn(touchSet);
762 OCMStub([touchEventMock2 locationInWindow]).andReturn(touchLocation);
763 OCMStub([(NSEvent*)touchEventMock2 timestamp]).andReturn(0.005);
767 [viewController touchesBeganWithEvent:touchEventMock2];
769 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindScrollInertiaCancel);
770 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
773 CGEventRef cgEventMomentumEnd = CGEventCreateCopy(cgEventStart);
774 CGEventSetIntegerValueField(cgEventMomentumEnd, kCGScrollWheelEventScrollPhase, 0);
775 CGEventSetIntegerValueField(cgEventMomentumEnd, kCGScrollWheelEventMomentumPhase,
776 kCGMomentumScrollPhaseEnd);
779 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventMomentumEnd]];
780 EXPECT_FALSE(called);
783 CGEventRef cgEventMayBegin = CGEventCreateCopy(cgEventStart);
784 CGEventSetIntegerValueField(cgEventMayBegin, kCGScrollWheelEventScrollPhase,
785 kCGScrollPhaseMayBegin);
788 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventMayBegin]];
790 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
791 EXPECT_EQ(last_event.phase, kPanZoomStart);
792 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
793 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
796 CGEventRef cgEventCancel = CGEventCreateCopy(cgEventStart);
797 CGEventSetIntegerValueField(cgEventCancel, kCGScrollWheelEventScrollPhase,
798 kCGScrollPhaseCancelled);
801 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventCancel]];
803 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
804 EXPECT_EQ(last_event.phase, kPanZoomEnd);
805 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
806 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
809 CGEventRef cgEventDiscrete = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 1, 0);
810 CGEventSetType(cgEventDiscrete, kCGEventScrollWheel);
811 CGEventSetIntegerValueField(cgEventDiscrete, kCGScrollWheelEventIsContinuous, 0);
812 CGEventSetIntegerValueField(cgEventDiscrete, kCGScrollWheelEventDeltaAxis2, 1);
813 CGEventSetIntegerValueField(cgEventDiscrete, kCGScrollWheelEventDeltaAxis1, 2);
816 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventDiscrete]];
818 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindScroll);
820 EXPECT_EQ(last_event.scroll_delta_x, -40 * viewController.flutterView.layer.contentsScale);
821 EXPECT_EQ(last_event.scroll_delta_y, -80 * viewController.flutterView.layer.contentsScale);
825 CGEventRef cgEventDiscreteShift =
826 CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 1, 0);
827 CGEventSetType(cgEventDiscreteShift, kCGEventScrollWheel);
828 CGEventSetFlags(cgEventDiscreteShift, kCGEventFlagMaskShift);
829 CGEventSetIntegerValueField(cgEventDiscreteShift, kCGScrollWheelEventIsContinuous, 0);
830 CGEventSetIntegerValueField(cgEventDiscreteShift, kCGScrollWheelEventDeltaAxis2,
832 CGEventSetIntegerValueField(cgEventDiscreteShift, kCGScrollWheelEventDeltaAxis1,
836 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventDiscreteShift]];
838 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindScroll);
840 EXPECT_FLOAT_EQ(last_event.scroll_delta_x, 0.0 * viewController.flutterView.layer.contentsScale);
841 EXPECT_FLOAT_EQ(last_event.scroll_delta_y,
842 -80.0 * viewController.flutterView.layer.contentsScale);
847 [viewController magnifyWithEvent:flutter::testing::MockGestureEvent(NSEventTypeMagnify,
848 NSEventPhaseBegan, 1, 0)];
850 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
851 EXPECT_EQ(last_event.phase, kPanZoomStart);
852 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
853 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
857 [viewController magnifyWithEvent:flutter::testing::MockGestureEvent(NSEventTypeMagnify,
858 NSEventPhaseChanged, 1, 0)];
860 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
861 EXPECT_EQ(last_event.phase, kPanZoomUpdate);
862 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
863 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
864 EXPECT_EQ(last_event.pan_x, 0);
865 EXPECT_EQ(last_event.pan_y, 0);
866 EXPECT_EQ(last_event.scale, 2);
868 EXPECT_EQ(last_event.rotation, 0);
872 [viewController magnifyWithEvent:flutter::testing::MockGestureEvent(NSEventTypeMagnify,
873 NSEventPhaseChanged, 1, 0)];
875 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
876 EXPECT_EQ(last_event.phase, kPanZoomUpdate);
877 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
878 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
879 EXPECT_EQ(last_event.pan_x, 0);
880 EXPECT_EQ(last_event.pan_y, 0);
881 EXPECT_EQ(last_event.scale, 4);
883 EXPECT_EQ(last_event.rotation, 0);
887 [viewController magnifyWithEvent:flutter::testing::MockGestureEvent(NSEventTypeMagnify,
888 NSEventPhaseEnded, 0, 0)];
890 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
891 EXPECT_EQ(last_event.phase, kPanZoomEnd);
892 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
893 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
898 [viewController rotateWithEvent:flutter::testing::MockGestureEvent(NSEventTypeRotate,
899 NSEventPhaseBegan, 1, 0)];
901 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
902 EXPECT_EQ(last_event.phase, kPanZoomStart);
903 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
904 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
908 [viewController rotateWithEvent:flutter::testing::MockGestureEvent(
909 NSEventTypeRotate, NSEventPhaseChanged, 0, -180)];
911 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
912 EXPECT_EQ(last_event.phase, kPanZoomUpdate);
913 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
914 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
915 EXPECT_EQ(last_event.pan_x, 0);
916 EXPECT_EQ(last_event.pan_y, 0);
917 EXPECT_EQ(last_event.scale, 1);
918 EXPECT_EQ(last_event.rotation, M_PI);
922 [viewController rotateWithEvent:flutter::testing::MockGestureEvent(
923 NSEventTypeRotate, NSEventPhaseChanged, 0, -360)];
925 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
926 EXPECT_EQ(last_event.phase, kPanZoomUpdate);
927 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
928 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
929 EXPECT_EQ(last_event.pan_x, 0);
930 EXPECT_EQ(last_event.pan_y, 0);
931 EXPECT_EQ(last_event.scale, 1);
932 EXPECT_EQ(last_event.rotation, 3 * M_PI);
936 [viewController rotateWithEvent:flutter::testing::MockGestureEvent(NSEventTypeRotate,
937 NSEventPhaseEnded, 0, 0)];
939 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
940 EXPECT_EQ(last_event.phase, kPanZoomEnd);
941 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
942 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
952 [viewController viewWillAppear];
953 [viewController viewWillAppear];
961 [key isEqualToString:
@"Contents/Frameworks/App.framework/Resources/flutter_assets/test.png"]);
969 EXPECT_TRUE([packageKey
971 @"Contents/Frameworks/App.framework/Resources/flutter_assets/packages/test/test.png"]);
975 static void SwizzledNoop(
id self,
SEL _cmd) {}
989 Method mouseDown = class_getInstanceMethod([NSResponder
class],
@selector(mouseDown:));
990 Method mouseUp = class_getInstanceMethod([NSResponder
class],
@selector(mouseUp:));
991 IMP noopImp = (IMP)SwizzledNoop;
992 IMP origMouseDown = method_setImplementation(mouseDown, noopImp);
993 IMP origMouseUp = method_setImplementation(mouseUp, noopImp);
1004 NSEvent* mouseEvent = flutter::testing::CreateMouseEvent(0x00);
1005 [view mouseDown:mouseEvent];
1010 [view mouseUp:mouseEvent];
1015 method_setImplementation(mouseDown, origMouseDown);
1016 method_setImplementation(mouseUp, origMouseUp);
1025 [engineMock binaryMessenger])
1026 .andReturn(binaryMessengerMock);
1030 OCMStub([engineMock renderer]).andReturn(renderer_);
1033 __block NSMutableArray<KeyEventWrapper*>* events = [NSMutableArray array];
1034 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:FlutterKeyEvent {}
1037 .andDo((^(NSInvocation* invocation) {
1038 FlutterKeyEvent* event;
1039 [invocation getArgument:&event atIndex:2];
1043 __block NSMutableArray<NSDictionary*>* channelEvents = [NSMutableArray array];
1044 OCMStub([binaryMessengerMock sendOnChannel:
@"flutter/keyevent"
1045 message:[OCMArg any]
1046 binaryReply:[OCMArg any]])
1047 .andDo((^(NSInvocation* invocation) {
1049 [invocation getArgument:&data atIndex:3];
1051 [channelEvents addObject:event];
1057 [viewController loadView];
1058 [viewController viewWillAppear];
1061 NSEvent* mouseEvent = flutter::testing::CreateMouseEvent(0x00);
1062 [viewController mouseMoved:mouseEvent];
1063 EXPECT_EQ([events count], 0u);
1067 FlutterKeyEvent* event;
1068 NSDictionary* channelEvent;
1069 NSNumber* logicalKey;
1070 NSNumber* physicalKey;
1071 NSEventModifierFlags flag = [flutter::keyCodeToModifierFlag[keyCode] unsignedLongValue];
1075 flag |= NSEventModifierFlagShift;
1078 flag |= NSEventModifierFlagControl;
1081 flag |= NSEventModifierFlagOption;
1084 flag |= NSEventModifierFlagCommand;
1088 NSEvent* mouseEvent = flutter::testing::CreateMouseEvent(flag);
1089 [viewController mouseMoved:mouseEvent];
1090 EXPECT_EQ([events count], 1u);
1091 event = events[0].data;
1092 logicalKey = [flutter::keyCodeToLogicalKey objectForKey:keyCode];
1093 physicalKey = [flutter::keyCodeToPhysicalKey objectForKey:keyCode];
1094 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
1095 EXPECT_EQ(event->logical, logicalKey.unsignedLongLongValue);
1096 EXPECT_EQ(event->physical, physicalKey.unsignedLongLongValue);
1097 EXPECT_EQ(event->synthesized,
true);
1099 channelEvent = channelEvents[0];
1100 EXPECT_TRUE([channelEvent[
@"type"] isEqual:
@"keydown"]);
1101 EXPECT_TRUE([channelEvent[
@"keyCode"] isEqual:keyCode]);
1102 EXPECT_TRUE([channelEvent[
@"modifiers"] isEqual:@(flag)]);
1105 mouseEvent = flutter::testing::CreateMouseEvent(0x00);
1106 [viewController mouseMoved:mouseEvent];
1107 EXPECT_EQ([events count], 2u);
1108 event = events[1].data;
1109 logicalKey = [flutter::keyCodeToLogicalKey objectForKey:keyCode];
1110 physicalKey = [flutter::keyCodeToPhysicalKey objectForKey:keyCode];
1111 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
1112 EXPECT_EQ(event->logical, logicalKey.unsignedLongLongValue);
1113 EXPECT_EQ(event->physical, physicalKey.unsignedLongLongValue);
1114 EXPECT_EQ(event->synthesized,
true);
1116 channelEvent = channelEvents[1];
1117 EXPECT_TRUE([channelEvent[
@"type"] isEqual:
@"keyup"]);
1118 EXPECT_TRUE([channelEvent[
@"keyCode"] isEqual:keyCode]);
1119 EXPECT_TRUE([channelEvent[
@"modifiers"] isEqual:@(0)]);
1121 [events removeAllObjects];
1122 [channelEvents removeAllObjects];
1134 OCMStub([engineMock renderer]).andReturn(renderer_);
1139 [viewController loadView];
1140 weakController = viewController;
1143 EXPECT_EQ(weakController, nil);