[macOS] Fix tests failing on Sonoma (flutter/engine#46461)

1. Using arbitrary struct passed as const reference  to `OCMStub` now fails in OCMock. Down the line this will result with `object_getClass` being called in [`OCMArg.m`](https://github.com/erikdoe/ocmock/blob/master/Source/OCMock/OCMArg.m#L129-L133) with the address of the reference, which is not a valid class instance. This seems to have worked pre-sonoma, but it seems like a weird thing to rely on.

2. `NSResponder` mock can not be set to view controller anymore. The controller will try to access an ivar of the `NSResponder`, but mocked responder does not have the ivar of original objects which will result on invalid selector being called on a `NSMutableArray` one of the ivar of mock objects. Solution for this is to inherit from `NSResponder` and forward calls to mocked object.

3. Adding `flutter::kModifierFlagShiftLeft` to a modifier flag containing `kCGEventFlagMaskShift`. The assertion was introduced in https://github.com/flutter/engine/pull/46230 but i missed the test failure because of the problems above.

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
Matej Knopp 2023-10-18 07:52:19 +03:00 committed by GitHub
parent 0aa1f4977e
commit 6054dbd0cb
2 changed files with 46 additions and 5 deletions

View File

@ -276,7 +276,9 @@ void clearEvents(std::vector<FlutterKeyEvent>& events) {
OCMStub([viewDelegateMock onTextInputKeyEvent:[OCMArg any]])
.andCall(self, @selector(handleTextInputKeyEvent:));
OCMStub([viewDelegateMock getBinaryMessenger]).andReturn(_messengerMock);
OCMStub([viewDelegateMock sendKeyEvent:FlutterKeyEvent {} callback:nil userData:nil])
OCMStub([viewDelegateMock sendKeyEvent:*(const FlutterKeyEvent*)[OCMArg anyPointer]
callback:nil
userData:nil])
.ignoringNonObjectArgs()
.andCall(self, @selector(handleEmbedderEvent:callback:userData:));
OCMStub([viewDelegateMock subscribeToKeyboardLayoutChange:[OCMArg any]])

View File

@ -38,6 +38,42 @@
}
@end
/// Responder wrapper that forwards key events to another responder. This is a necessary middle step
/// for mocking responder because when setting the responder to controller AppKit will access ivars
/// of the objects, which means it must extend NSResponder instead of just implementing the
/// selectors.
@interface FlutterResponderWrapper : NSResponder {
NSResponder* _responder;
}
@end
@implementation FlutterResponderWrapper
- (instancetype)initWithResponder:(NSResponder*)responder {
if (self = [super init]) {
_responder = responder;
}
return self;
}
- (void)keyDown:(NSEvent*)event {
[_responder keyDown:event];
}
- (void)keyUp:(NSEvent*)event {
[_responder keyUp:event];
}
- (BOOL)performKeyEquivalent:(NSEvent*)event {
return [_responder performKeyEquivalent:event];
}
- (void)flagsChanged:(NSEvent*)event {
[_responder flagsChanged:event];
}
@end
// A FlutterViewController subclass for testing that mouseDown/mouseUp get called when
// mouse events are sent to the associated view.
@interface MouseEventFlutterViewController : FlutterViewController
@ -417,7 +453,8 @@ TEST(FlutterViewControllerTest, testViewControllerIsReleased) {
nibName:@""
bundle:nil];
id responderMock = flutter::testing::mockResponder();
viewController.nextResponder = responderMock;
id responderWrapper = [[FlutterResponderWrapper alloc] initWithResponder:responderMock];
viewController.nextResponder = responderWrapper;
NSDictionary* expectedEvent = @{
@"keymap" : @"macos",
@"type" : @"keydown",
@ -493,7 +530,8 @@ TEST(FlutterViewControllerTest, testViewControllerIsReleased) {
nibName:@""
bundle:nil];
id responderMock = flutter::testing::mockResponder();
viewController.nextResponder = responderMock;
id responderWrapper = [[FlutterResponderWrapper alloc] initWithResponder:responderMock];
viewController.nextResponder = responderWrapper;
NSDictionary* expectedEvent = @{
@"keymap" : @"macos",
@"type" : @"keydown",
@ -546,7 +584,8 @@ TEST(FlutterViewControllerTest, testViewControllerIsReleased) {
nibName:@""
bundle:nil];
id responderMock = flutter::testing::mockResponder();
viewController.nextResponder = responderMock;
id responderWrapper = [[FlutterResponderWrapper alloc] initWithResponder:responderMock];
viewController.nextResponder = responderWrapper;
NSDictionary* expectedEvent = @{
@"keymap" : @"macos",
@"type" : @"keydown",
@ -825,7 +864,7 @@ TEST(FlutterViewControllerTest, testViewControllerIsReleased) {
CGEventRef cgEventDiscreteShift =
CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 1, 0);
CGEventSetType(cgEventDiscreteShift, kCGEventScrollWheel);
CGEventSetFlags(cgEventDiscreteShift, kCGEventFlagMaskShift);
CGEventSetFlags(cgEventDiscreteShift, kCGEventFlagMaskShift | flutter::kModifierFlagShiftLeft);
CGEventSetIntegerValueField(cgEventDiscreteShift, kCGScrollWheelEventIsContinuous, 0);
CGEventSetIntegerValueField(cgEventDiscreteShift, kCGScrollWheelEventDeltaAxis2,
0); // scroll_delta_x