| Flutter macOS Embedder |

Instance Methods | Class Methods | List of all members
FlutterViewControllerTestObjC Class Reference
Inheritance diagram for FlutterViewControllerTestObjC:

## Instance Methods
(bool) | - testKeyEventsAreSentToFramework:
(bool) | - testKeyEventsArePropagatedIfNotHandled:
(bool) | - testKeyEventsAreNotPropagatedIfHandled:
(bool) | - testCtrlTabKeyEventIsPropagated:
(bool) | - testKeyEquivalentIsPassedToTextInputPlugin:
(bool) | - testFlagsChangedEventsArePropagatedIfNotHandled:
(bool) | - testKeyboardIsRestartedOnEngineRestart:
(bool) | - testTrackpadGesturesAreSentToFramework:
(bool) | - mouseAndGestureEventsAreHandledSeparately:
(bool) | - testMouseDownUpEventsSentToNextResponder:
(bool) | - testModifierKeysAreSynthesizedOnMouseMove:
(bool) | - testViewWillAppearCalledMultipleTimes:
(bool) | - testFlutterViewIsConfigured:
(bool) | - testLookupKeyAssets
(bool) | - testLookupKeyAssetsWithPackage
(bool) | - testViewControllerIsReleased

## Class Methods
(void) | + respondFalseForSendEvent:callback:userData:

## Detailed Description

Definition at line 98 of file FlutterViewControllerTest.mm.

## Method Documentation

## ◆ mouseAndGestureEventsAreHandledSeparately:

- (bool) mouseAndGestureEventsAreHandledSeparately: |  | (id) | engineMock |

Definition at line 1017 of file FlutterViewControllerTest.mm.

1017 :(id)engineMock {
1018 FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
1019 nibName:@""
1020 bundle:nil];
1021 [viewController loadView];
1022
1023 // Test for pan events.
1024 // Start gesture.
1025 CGEventRef cgEventStart = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 1, 0);
1026 CGEventSetType(cgEventStart, kCGEventScrollWheel);
1027 CGEventSetIntegerValueField(cgEventStart, kCGScrollWheelEventScrollPhase, kCGScrollPhaseBegan);
1028 CGEventSetIntegerValueField(cgEventStart, kCGScrollWheelEventIsContinuous, 1);
1029 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventStart]];
1030 CFRelease(cgEventStart);
1031
1032 CGEventRef cgEventUpdate = CGEventCreateCopy(cgEventStart);
1033 CGEventSetIntegerValueField(cgEventUpdate, kCGScrollWheelEventScrollPhase, kCGScrollPhaseChanged);
1034 CGEventSetIntegerValueField(cgEventUpdate, kCGScrollWheelEventDeltaAxis2, 1); // pan_x
1035 CGEventSetIntegerValueField(cgEventUpdate, kCGScrollWheelEventDeltaAxis1, 2); // pan_y
1036 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventUpdate]];
1037 CFRelease(cgEventUpdate);
1038
1039 NSEvent* mouseEvent = flutter::testing::CreateMouseEvent(0x00);
1040 [viewController mouseEntered:mouseEvent];
1041 [viewController mouseExited:mouseEvent];
1042
1043 // End gesture.
1044 CGEventRef cgEventEnd = CGEventCreateCopy(cgEventStart);
1045 CGEventSetIntegerValueField(cgEventEnd, kCGScrollWheelEventScrollPhase, kCGScrollPhaseEnded);
1046 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventEnd]];
1047 CFRelease(cgEventEnd);
1048
1049 return true;
1050 }

## ◆ respondFalseForSendEvent:callback:userData:

+ (void) respondFalseForSendEvent: |  | (const FlutterKeyEvent&) | event
callback: |  | (nullable FlutterKeyEventCallback) | callback
userData: |  | (nullable void*) | userData

Definition at line 707 of file FlutterViewControllerTest.mm.

707 :(const FlutterKeyEvent&)event
708 callback:(nullable FlutterKeyEventCallback)callback
709 userData:(nullable void*)userData {
710 if (callback != nullptr) {
711 callback(false, userData);
712 }
713 }

## ◆ testCtrlTabKeyEventIsPropagated:

- (bool) testCtrlTabKeyEventIsPropagated: |  | (id) | mockEngine |

Definition at line 378 of file FlutterViewControllerTest.mm.

378 :(id)engineMock {
379 __block bool called = false;
380 __block FlutterKeyEvent last_event;
381 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:kDefaultFlutterKeyEvent
382 callback:nil
383 userData:nil])
384 .andDo((^(NSInvocation* invocation) {
385 FlutterKeyEvent* event;
386 [invocation getArgument:&event atIndex:2];
387 called = true;
388 last_event = *event;
389 }));
390 FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
391 nibName:@""
392 bundle:nil];
393 // Ctrl+tab
394 NSEvent* event = [NSEvent keyEventWithType:NSEventTypeKeyDown
395 location:NSZeroPoint
396 modifierFlags:0x40101
397 timestamp:0
398 windowNumber:0
399 context:nil
400 characters:@""
401 charactersIgnoringModifiers:@""
402 isARepeat:NO
403 keyCode:48];
404 const uint64_t kPhysicalKeyTab = 0x7002b;
405
406 [viewController viewWillAppear]; // Initializes the event channel.
407 // Creates a NSWindow so that FlutterView view can be first responder.
408 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600)
409 styleMask:NSBorderlessWindowMask
410 backing:NSBackingStoreBuffered
411 defer:NO];
412 window.contentView = viewController.view;
413 [window makeFirstResponder:viewController.flutterView];
414 [viewController.view performKeyEquivalent:event];
415
416 EXPECT_TRUE(called);
417 EXPECT_EQ(last_event.type, kFlutterKeyEventTypeDown);
418 EXPECT_EQ(last_event.physical, kPhysicalKeyTab);
419 return true;
420 }

References kDefaultFlutterKeyEvent.

## ◆ testFlagsChangedEventsArePropagatedIfNotHandled:

- (bool) testFlagsChangedEventsArePropagatedIfNotHandled: |  | (id) | mockEngine |

Definition at line 546 of file FlutterViewControllerTest.mm.

546 :(id)engineMock {
547 id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
548 OCMStub( // NOLINT(google-objc-avoid-throwing-exception)
549 [engineMock binaryMessenger])
550 .andReturn(binaryMessengerMock);
551 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:kDefaultFlutterKeyEvent
552 callback:nil
553 userData:nil])
554 .andCall([FlutterViewControllerTestObjC class],
555 @selector(respondFalseForSendEvent:callback:userData:));
556 FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
557 nibName:@""
558 bundle:nil];
559 id responderMock = flutter::testing::mockResponder();
560 id responderWrapper = [[FlutterResponderWrapper alloc] initWithResponder:responderMock];
561 viewController.nextResponder = responderWrapper;
562 NSDictionary* expectedEvent = @{
563 @"keymap" : @"macos",
564 @"type" : @"keydown",
565 @"keyCode" : @(56), // SHIFT key
566 @"modifiers" : @(537001986),
567 };
568 NSData* encodedKeyEvent = [[FlutterJSONMessageCodec sharedInstance] encode:expectedEvent];
569 CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 56, TRUE); // SHIFT key
570 CGEventSetType(cgEvent, kCGEventFlagsChanged);
571 NSEvent* event = [NSEvent eventWithCGEvent:cgEvent];
572 OCMExpect( // NOLINT(google-objc-avoid-throwing-exception)
573 [binaryMessengerMock sendOnChannel:@"flutter/keyevent"
574 message:encodedKeyEvent
575 binaryReply:[OCMArg any]])
576 .andDo((^(NSInvocation* invocation) {
577 FlutterBinaryReply handler;
578 [invocation getArgument:&handler atIndex:4];
579 NSDictionary* reply = @{
580 @"handled" : @(false),
581 };
582 NSData* encodedReply = [[FlutterJSONMessageCodec sharedInstance] encode:reply];
583 handler(encodedReply);
584 }));
585 [viewController viewWillAppear]; // Initializes the event channel.
586 [viewController flagsChanged:event];
587 @try {
588 OCMVerify( // NOLINT(google-objc-avoid-throwing-exception)
589 [binaryMessengerMock sendOnChannel:@"flutter/keyevent"
590 message:encodedKeyEvent
591 binaryReply:[OCMArg any]]);
592 } @catch (NSException* e) {
593 NSLog(@"%@", e.reason);
594 return false;
595 }
596 return true;
597 }

References FlutterBinaryReply, kDefaultFlutterKeyEvent, and <FlutterMessageCodec>::sharedInstance.

## ◆ testFlutterViewIsConfigured:

- (bool) testFlutterViewIsConfigured: |  | (id) | mockEngine |

Definition at line 527 of file FlutterViewControllerTest.mm.

527 :(id)engineMock {
528 FlutterRenderer* renderer_ = [[FlutterRenderer alloc] initWithFlutterEngine:engineMock];
529 OCMStub([engineMock renderer]).andReturn(renderer_);
530
531 FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
532 nibName:@""
533 bundle:nil];
534 [viewController loadView];
535
536 @try {
537 // Make sure "renderer" was called during "loadView", which means "flutterView" is created
538 OCMVerify([engineMock renderer]);
539 } @catch (...) {
540 return false;
541 }
542
543 return true;
544 }

## ◆ testKeyboardIsRestartedOnEngineRestart:

- (bool) testKeyboardIsRestartedOnEngineRestart: |  | (id) | mockEngine |

Definition at line 654 of file FlutterViewControllerTest.mm.

654 :(id)engineMock {
655 id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
656 OCMStub( // NOLINT(google-objc-avoid-throwing-exception)
657 [engineMock binaryMessenger])
658 .andReturn(binaryMessengerMock);
659 __block bool called = false;
660 __block FlutterKeyEvent last_event;
661 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:kDefaultFlutterKeyEvent
662 callback:nil
663 userData:nil])
664 .andDo((^(NSInvocation* invocation) {
665 FlutterKeyEvent* event;
666 [invocation getArgument:&event atIndex:2];
667 called = true;
668 last_event = *event;
669 }));
670
671 FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
672 nibName:@""
673 bundle:nil];
674 [viewController viewWillAppear];
675 NSEvent* keyADown = [NSEvent keyEventWithType:NSEventTypeKeyDown
676 location:NSZeroPoint
677 modifierFlags:0x100
678 timestamp:0
679 windowNumber:0
680 context:nil
681 characters:@"a"
682 charactersIgnoringModifiers:@"a"
683 isARepeat:FALSE
684 keyCode:0];
685 const uint64_t kPhysicalKeyA = 0x70004;
686
687 // Send KeyA key down event twice. Without restarting the keyboard during
688 // onPreEngineRestart, the second event received will be an empty event with
689 // physical key 0x0 because duplicate key down events are ignored.
690
691 called = false;
692 [viewController keyDown:keyADown];
693 EXPECT_TRUE(called);
694 EXPECT_EQ(last_event.type, kFlutterKeyEventTypeDown);
695 EXPECT_EQ(last_event.physical, kPhysicalKeyA);
696
697 [viewController onPreEngineRestart];
698
699 called = false;
700 [viewController keyDown:keyADown];
701 EXPECT_TRUE(called);
702 EXPECT_EQ(last_event.type, kFlutterKeyEventTypeDown);
703 EXPECT_EQ(last_event.physical, kPhysicalKeyA);
704 return true;
705 }

References kDefaultFlutterKeyEvent, and FlutterViewController::onPreEngineRestart.

## ◆ testKeyEquivalentIsPassedToTextInputPlugin:

- (bool) testKeyEquivalentIsPassedToTextInputPlugin: |  | (id) | mockEngine |

Definition at line 422 of file FlutterViewControllerTest.mm.

422 :(id)engineMock {
423 __block bool called = false;
424 __block FlutterKeyEvent last_event;
425 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:kDefaultFlutterKeyEvent
426 callback:nil
427 userData:nil])
428 .andDo((^(NSInvocation* invocation) {
429 FlutterKeyEvent* event;
430 [invocation getArgument:&event atIndex:2];
431 called = true;
432 last_event = *event;
433 }));
434 FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
435 nibName:@""
436 bundle:nil];
437 // Ctrl+tab
438 NSEvent* event = [NSEvent keyEventWithType:NSEventTypeKeyDown
439 location:NSZeroPoint
440 modifierFlags:0x40101
441 timestamp:0
442 windowNumber:0
443 context:nil
444 characters:@""
445 charactersIgnoringModifiers:@""
446 isARepeat:NO
447 keyCode:48];
448 const uint64_t kPhysicalKeyTab = 0x7002b;
449
450 [viewController viewWillAppear]; // Initializes the event channel.
451
452 NSWindow* window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 800, 600)
453 styleMask:NSBorderlessWindowMask
454 backing:NSBackingStoreBuffered
455 defer:NO];
456 window.contentView = viewController.view;
457
458 [viewController.view addSubview:viewController.textInputPlugin];
459
460 // Make the textInputPlugin first responder. This should still result in
461 // view controller reporting the key event.
462 [window makeFirstResponder:viewController.textInputPlugin];
463
464 [viewController.view performKeyEquivalent:event];
465
466 EXPECT_TRUE(called);
467 EXPECT_EQ(last_event.type, kFlutterKeyEventTypeDown);
468 EXPECT_EQ(last_event.physical, kPhysicalKeyTab);
469 return true;
470 }

References kDefaultFlutterKeyEvent.

## ◆ testKeyEventsAreNotPropagatedIfHandled:

- (bool) testKeyEventsAreNotPropagatedIfHandled: |  | (id) | mockEngine |

Definition at line 599 of file FlutterViewControllerTest.mm.

599 :(id)engineMock {
600 id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
601 OCMStub( // NOLINT(google-objc-avoid-throwing-exception)
602 [engineMock binaryMessenger])
603 .andReturn(binaryMessengerMock);
604 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:kDefaultFlutterKeyEvent
605 callback:nil
606 userData:nil])
607 .andCall([FlutterViewControllerTestObjC class],
608 @selector(respondFalseForSendEvent:callback:userData:));
609 FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
610 nibName:@""
611 bundle:nil];
612 id responderMock = flutter::testing::mockResponder();
613 id responderWrapper = [[FlutterResponderWrapper alloc] initWithResponder:responderMock];
614 viewController.nextResponder = responderWrapper;
615 NSDictionary* expectedEvent = @{
616 @"keymap" : @"macos",
617 @"type" : @"keydown",
618 @"keyCode" : @(65),
619 @"modifiers" : @(538968064),
620 @"characters" : @".",
621 @"charactersIgnoringModifiers" : @".",
622 };
623 NSData* encodedKeyEvent = [[FlutterJSONMessageCodec sharedInstance] encode:expectedEvent];
624 CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 65, TRUE);
625 NSEvent* event = [NSEvent eventWithCGEvent:cgEvent];
626 OCMExpect( // NOLINT(google-objc-avoid-throwing-exception)
627 [binaryMessengerMock sendOnChannel:@"flutter/keyevent"
628 message:encodedKeyEvent
629 binaryReply:[OCMArg any]])
630 .andDo((^(NSInvocation* invocation) {
631 FlutterBinaryReply handler;
632 [invocation getArgument:&handler atIndex:4];
633 NSDictionary* reply = @{
634 @"handled" : @(true),
635 };
636 NSData* encodedReply = [[FlutterJSONMessageCodec sharedInstance] encode:reply];
637 handler(encodedReply);
638 }));
639 [viewController viewWillAppear]; // Initializes the event channel.
640 [viewController keyDown:event];
641 @try {
642 OCMVerify( // NOLINT(google-objc-avoid-throwing-exception)
643 never(), [responderMock keyDown:[OCMArg any]]);
644 OCMVerify( // NOLINT(google-objc-avoid-throwing-exception)
645 [binaryMessengerMock sendOnChannel:@"flutter/keyevent"
646 message:encodedKeyEvent
647 binaryReply:[OCMArg any]]);
648 } @catch (...) {
649 return false;
650 }
651 return true;
652 }

References FlutterBinaryReply, kDefaultFlutterKeyEvent, and <FlutterMessageCodec>::sharedInstance.

## ◆ testKeyEventsArePropagatedIfNotHandled:

- (bool) testKeyEventsArePropagatedIfNotHandled: |  | (id) | mockEngine |

Definition at line 472 of file FlutterViewControllerTest.mm.

472 :(id)engineMock {
473 id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
474 OCMStub( // NOLINT(google-objc-avoid-throwing-exception)
475 [engineMock binaryMessenger])
476 .andReturn(binaryMessengerMock);
477 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:kDefaultFlutterKeyEvent
478 callback:nil
479 userData:nil])
480 .andCall([FlutterViewControllerTestObjC class],
481 @selector(respondFalseForSendEvent:callback:userData:));
482 FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
483 nibName:@""
484 bundle:nil];
485 id responderMock = flutter::testing::mockResponder();
486 id responderWrapper = [[FlutterResponderWrapper alloc] initWithResponder:responderMock];
487 viewController.nextResponder = responderWrapper;
488 NSDictionary* expectedEvent = @{
489 @"keymap" : @"macos",
490 @"type" : @"keydown",
491 @"keyCode" : @(65),
492 @"modifiers" : @(538968064),
493 @"characters" : @".",
494 @"charactersIgnoringModifiers" : @".",
495 };
496 NSData* encodedKeyEvent = [[FlutterJSONMessageCodec sharedInstance] encode:expectedEvent];
497 CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 65, TRUE);
498 NSEvent* event = [NSEvent eventWithCGEvent:cgEvent];
499 OCMExpect( // NOLINT(google-objc-avoid-throwing-exception)
500 [binaryMessengerMock sendOnChannel:@"flutter/keyevent"
501 message:encodedKeyEvent
502 binaryReply:[OCMArg any]])
503 .andDo((^(NSInvocation* invocation) {
504 FlutterBinaryReply handler;
505 [invocation getArgument:&handler atIndex:4];
506 NSDictionary* reply = @{
507 @"handled" : @(false),
508 };
509 NSData* encodedReply = [[FlutterJSONMessageCodec sharedInstance] encode:reply];
510 handler(encodedReply);
511 }));
512 [viewController viewWillAppear]; // Initializes the event channel.
513 [viewController keyDown:event];
514 @try {
515 OCMVerify( // NOLINT(google-objc-avoid-throwing-exception)
516 [responderMock keyDown:[OCMArg any]]);
517 OCMVerify( // NOLINT(google-objc-avoid-throwing-exception)
518 [binaryMessengerMock sendOnChannel:@"flutter/keyevent"
519 message:encodedKeyEvent
520 binaryReply:[OCMArg any]]);
521 } @catch (...) {
522 return false;
523 }
524 return true;
525 }

References FlutterBinaryReply, kDefaultFlutterKeyEvent, and <FlutterMessageCodec>::sharedInstance.

## ◆ testKeyEventsAreSentToFramework:

- (bool) testKeyEventsAreSentToFramework: |  | (id) | mockEngine |

Definition at line 340 of file FlutterViewControllerTest.mm.

340 :(id)engineMock {
341 id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
342 OCMStub( // NOLINT(google-objc-avoid-throwing-exception)
343 [engineMock binaryMessenger])
344 .andReturn(binaryMessengerMock);
345 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:kDefaultFlutterKeyEvent
346 callback:nil
347 userData:nil])
348 .andCall([FlutterViewControllerTestObjC class],
349 @selector(respondFalseForSendEvent:callback:userData:));
350 FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
351 nibName:@""
352 bundle:nil];
353 NSDictionary* expectedEvent = @{
354 @"keymap" : @"macos",
355 @"type" : @"keydown",
356 @"keyCode" : @(65),
357 @"modifiers" : @(538968064),
358 @"characters" : @".",
359 @"charactersIgnoringModifiers" : @".",
360 };
361 NSData* encodedKeyEvent = [[FlutterJSONMessageCodec sharedInstance] encode:expectedEvent];
362 CGEventRef cgEvent = CGEventCreateKeyboardEvent(NULL, 65, TRUE);
363 NSEvent* event = [NSEvent eventWithCGEvent:cgEvent];
364 [viewController viewWillAppear]; // Initializes the event channel.
365 [viewController keyDown:event];
366 @try {
367 OCMVerify( // NOLINT(google-objc-avoid-throwing-exception)
368 [binaryMessengerMock sendOnChannel:@"flutter/keyevent"
369 message:encodedKeyEvent
370 binaryReply:[OCMArg any]]);
371 } @catch (...) {
372 return false;
373 }
374 return true;
375 }

References kDefaultFlutterKeyEvent, and <FlutterMessageCodec>::sharedInstance.

## ◆ testLookupKeyAssets

- (bool) testLookupKeyAssets |  |  |

Definition at line 1061 of file FlutterViewControllerTest.mm.

1061 {
1062 FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:nil];
1063 NSString* key = [viewController lookupKeyForAsset:@"test.png"];
1064 EXPECT_TRUE(
1065 [key isEqualToString:@"Contents/Frameworks/App.framework/Resources/flutter_assets/test.png"]);
1066 return true;
1067 }

References FlutterViewController::lookupKeyForAsset:.

## ◆ testLookupKeyAssetsWithPackage

- (bool) testLookupKeyAssetsWithPackage |  |  |

Definition at line 1069 of file FlutterViewControllerTest.mm.

1069 {
1070 FlutterViewController* viewController = [[FlutterViewController alloc] initWithProject:nil];
1071
1072 NSString* packageKey = [viewController lookupKeyForAsset:@"test.png" fromPackage:@"test"];
1073 EXPECT_TRUE([packageKey
1074 isEqualToString:
1075 @"Contents/Frameworks/App.framework/Resources/flutter_assets/packages/test/test.png"]);
1076 return true;
1077 }

References FlutterViewController::lookupKeyForAsset:fromPackage:.

## ◆ testModifierKeysAreSynthesizedOnMouseMove:

- (bool) testModifierKeysAreSynthesizedOnMouseMove: |  | (id) | mockEngine |

Definition at line 1133 of file FlutterViewControllerTest.mm.

1133 :(id)engineMock {
1134 id binaryMessengerMock = OCMProtocolMock(@protocol(FlutterBinaryMessenger));
1135 OCMStub( // NOLINT(google-objc-avoid-throwing-exception)
1136 [engineMock binaryMessenger])
1137 .andReturn(binaryMessengerMock);
1138
1139 // Need to return a real renderer to allow view controller to load.
1140 FlutterRenderer* renderer_ = [[FlutterRenderer alloc] initWithFlutterEngine:engineMock];
1141 OCMStub([engineMock renderer]).andReturn(renderer_);
1142
1143 // Capture calls to sendKeyEvent
1144 __block NSMutableArray<KeyEventWrapper*>* events = [NSMutableArray array];
1145 OCMStub([[engineMock ignoringNonObjectArgs] sendKeyEvent:kDefaultFlutterKeyEvent
1146 callback:nil
1147 userData:nil])
1148 .andDo((^(NSInvocation* invocation) {
1149 FlutterKeyEvent* event;
1150 [invocation getArgument:&event atIndex:2];
1151 [events addObject:[[KeyEventWrapper alloc] initWithEvent:event]];
1152 }));
1153
1154 __block NSMutableArray<NSDictionary*>* channelEvents = [NSMutableArray array];
1155 OCMStub([binaryMessengerMock sendOnChannel:@"flutter/keyevent"
1156 message:[OCMArg any]
1157 binaryReply:[OCMArg any]])
1158 .andDo((^(NSInvocation* invocation) {
1159 NSData* data;
1160 [invocation getArgument:&data atIndex:3];
1161 id event = [[FlutterJSONMessageCodec sharedInstance] decode:data];
1162 [channelEvents addObject:event];
1163 }));
1164
1165 FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
1166 nibName:@""
1167 bundle:nil];
1168 [viewController loadView];
1169 [viewController viewWillAppear];
1170
1171 // Zeroed modifier flag should not synthesize events.
1172 NSEvent* mouseEvent = flutter::testing::CreateMouseEvent(0x00);
1173 [viewController mouseMoved:mouseEvent];
1174 EXPECT_EQ([events count], 0u);
1175
1176 // For each modifier key, check that key events are synthesized.
1177 for (NSNumber* keyCode in flutter::keyCodeToModifierFlag) {
1178 FlutterKeyEvent* event;
1179 NSDictionary* channelEvent;
1180 NSNumber* logicalKey;
1181 NSNumber* physicalKey;
1182 NSEventModifierFlags flag = [flutter::keyCodeToModifierFlag[keyCode] unsignedLongValue];
1183
1184 // Cocoa event always contain combined flags.
1185 if (flag & (flutter::kModifierFlagShiftLeft | flutter::kModifierFlagShiftRight)) {
1186 flag |= NSEventModifierFlagShift;
1187 }
1188 if (flag & (flutter::kModifierFlagControlLeft | flutter::kModifierFlagControlRight)) {
1189 flag |= NSEventModifierFlagControl;
1190 }
1191 if (flag & (flutter::kModifierFlagAltLeft | flutter::kModifierFlagAltRight)) {
1192 flag |= NSEventModifierFlagOption;
1193 }
1194 if (flag & (flutter::kModifierFlagMetaLeft | flutter::kModifierFlagMetaRight)) {
1195 flag |= NSEventModifierFlagCommand;
1196 }
1197
1198 // Should synthesize down event.
1199 NSEvent* mouseEvent = flutter::testing::CreateMouseEvent(flag);
1200 [viewController mouseMoved:mouseEvent];
1201 EXPECT_EQ([events count], 1u);
1202 event = events[0].data;
1203 logicalKey = [flutter::keyCodeToLogicalKey objectForKey:keyCode];
1204 physicalKey = [flutter::keyCodeToPhysicalKey objectForKey:keyCode];
1205 EXPECT_EQ(event->type, kFlutterKeyEventTypeDown);
1206 EXPECT_EQ(event->logical, logicalKey.unsignedLongLongValue);
1207 EXPECT_EQ(event->physical, physicalKey.unsignedLongLongValue);
1208 EXPECT_EQ(event->synthesized, true);
1209
1210 channelEvent = channelEvents[0];
1211 EXPECT_TRUE([channelEvent[@"type"] isEqual:@"keydown"]);
1212 EXPECT_TRUE([channelEvent[@"keyCode"] isEqual:keyCode]);
1213 EXPECT_TRUE([channelEvent[@"modifiers"] isEqual:@(flag)]);
1214
1215 // Should synthesize up event.
1216 mouseEvent = flutter::testing::CreateMouseEvent(0x00);
1217 [viewController mouseMoved:mouseEvent];
1218 EXPECT_EQ([events count], 2u);
1219 event = events[1].data;
1220 logicalKey = [flutter::keyCodeToLogicalKey objectForKey:keyCode];
1221 physicalKey = [flutter::keyCodeToPhysicalKey objectForKey:keyCode];
1222 EXPECT_EQ(event->type, kFlutterKeyEventTypeUp);
1223 EXPECT_EQ(event->logical, logicalKey.unsignedLongLongValue);
1224 EXPECT_EQ(event->physical, physicalKey.unsignedLongLongValue);
1225 EXPECT_EQ(event->synthesized, true);
1226
1227 channelEvent = channelEvents[1];
1228 EXPECT_TRUE([channelEvent[@"type"] isEqual:@"keyup"]);
1229 EXPECT_TRUE([channelEvent[@"keyCode"] isEqual:keyCode]);
1230 EXPECT_TRUE([channelEvent[@"modifiers"] isEqual:@(0)]);
1231
1232 [events removeAllObjects];
1233 [channelEvents removeAllObjects];
1234 };
1235
1236 return true;
1237 }

References KeyEventWrapper::data, kDefaultFlutterKeyEvent, flutter::keyCodeToModifierFlag, flutter::kModifierFlagAltLeft, flutter::kModifierFlagAltRight, flutter::kModifierFlagControlLeft, flutter::kModifierFlagControlRight, flutter::kModifierFlagMetaLeft, flutter::kModifierFlagMetaRight, flutter::kModifierFlagShiftLeft, flutter::kModifierFlagShiftRight, and <FlutterMessageCodec>::sharedInstance.

## ◆ testMouseDownUpEventsSentToNextResponder:

- (bool) testMouseDownUpEventsSentToNextResponder: |  | (id) | mockEngine |

Definition at line 1091 of file FlutterViewControllerTest.mm.

1091 :(id)engineMock {
1092 if (@available(macOS 13.3.1, *)) {
1093 // This workaround is disabled for macOS 13.3.1 onwards, since the underlying AppKit bug is
1094 // fixed.
1095 return true;
1096 }
1097
1098 // The root cause of the above bug is NSResponder mouseDown/mouseUp methods that don't correctly
1099 // walk the responder chain calling the appropriate method on the next responder under certain
1100 // conditions. Simulate this by swizzling out the default implementations and replacing them with
1101 // no-ops.
1102 Method mouseDown = class_getInstanceMethod([NSResponder class], @selector(mouseDown:));
1103 Method mouseUp = class_getInstanceMethod([NSResponder class], @selector(mouseUp:));
1104 IMP noopImp = (IMP)SwizzledNoop;
1105 IMP origMouseDown = method_setImplementation(mouseDown, noopImp);
1106 IMP origMouseUp = method_setImplementation(mouseUp, noopImp);
1107
1108 // Verify that mouseDown/mouseUp trigger mouseDown/mouseUp calls on FlutterViewController.
1109 MouseEventFlutterViewController* viewController =
1110 [[MouseEventFlutterViewController alloc] initWithEngine:engineMock nibName:@"" bundle:nil];
1111 FlutterView* view = (FlutterView*)[viewController view];
1112
1113 EXPECT_FALSE(viewController.mouseDownCalled);
1114 EXPECT_FALSE(viewController.mouseUpCalled);
1115
1116 NSEvent* mouseEvent = flutter::testing::CreateMouseEvent(0x00);
1117 [view mouseDown:mouseEvent];
1118 EXPECT_TRUE(viewController.mouseDownCalled);
1119 EXPECT_FALSE(viewController.mouseUpCalled);
1120
1121 viewController.mouseDownCalled = NO;
1122 [view mouseUp:mouseEvent];
1123 EXPECT_FALSE(viewController.mouseDownCalled);
1124 EXPECT_TRUE(viewController.mouseUpCalled);
1125
1126 // Restore the original NSResponder mouseDown/mouseUp implementations.
1127 method_setImplementation(mouseDown, origMouseDown);
1128 method_setImplementation(mouseUp, origMouseUp);
1129
1130 return true;
1131 }

References MouseEventFlutterViewController::mouseDownCalled, and MouseEventFlutterViewController::mouseUpCalled.

## ◆ testTrackpadGesturesAreSentToFramework:

- (bool) testTrackpadGesturesAreSentToFramework: |  | (id) | mockEngine |

Definition at line 715 of file FlutterViewControllerTest.mm.

715 :(id)engineMock {
716 // Need to return a real renderer to allow view controller to load.
717 FlutterRenderer* renderer_ = [[FlutterRenderer alloc] initWithFlutterEngine:engineMock];
718 OCMStub([engineMock renderer]).andReturn(renderer_);
719 __block bool called = false;
720 __block FlutterPointerEvent last_event;
721 OCMStub([[engineMock ignoringNonObjectArgs] sendPointerEvent:kDefaultFlutterPointerEvent])
722 .andDo((^(NSInvocation* invocation) {
723 FlutterPointerEvent* event;
724 [invocation getArgument:&event atIndex:2];
725 called = true;
726 last_event = *event;
727 }));
728
729 FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
730 nibName:@""
731 bundle:nil];
732 [viewController loadView];
733
734 // Test for pan events.
735 // Start gesture.
736 CGEventRef cgEventStart = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 1, 0);
737 CGEventSetType(cgEventStart, kCGEventScrollWheel);
738 CGEventSetIntegerValueField(cgEventStart, kCGScrollWheelEventScrollPhase, kCGScrollPhaseBegan);
739 CGEventSetIntegerValueField(cgEventStart, kCGScrollWheelEventIsContinuous, 1);
740
741 called = false;
742 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventStart]];
743 EXPECT_TRUE(called);
744 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
745 EXPECT_EQ(last_event.phase, kPanZoomStart);
746 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
747 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
748
749 // Update gesture.
750 CGEventRef cgEventUpdate = CGEventCreateCopy(cgEventStart);
751 CGEventSetIntegerValueField(cgEventUpdate, kCGScrollWheelEventScrollPhase, kCGScrollPhaseChanged);
752 CGEventSetIntegerValueField(cgEventUpdate, kCGScrollWheelEventDeltaAxis2, 1); // pan_x
753 CGEventSetIntegerValueField(cgEventUpdate, kCGScrollWheelEventDeltaAxis1, 2); // pan_y
754
755 called = false;
756 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventUpdate]];
757 EXPECT_TRUE(called);
758 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
759 EXPECT_EQ(last_event.phase, kPanZoomUpdate);
760 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
761 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
762 EXPECT_EQ(last_event.pan_x, 8 * viewController.flutterView.layer.contentsScale);
763 EXPECT_EQ(last_event.pan_y, 16 * viewController.flutterView.layer.contentsScale);
764
765 // Make sure the pan values accumulate.
766 called = false;
767 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventUpdate]];
768 EXPECT_TRUE(called);
769 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
770 EXPECT_EQ(last_event.phase, kPanZoomUpdate);
771 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
772 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
773 EXPECT_EQ(last_event.pan_x, 16 * viewController.flutterView.layer.contentsScale);
774 EXPECT_EQ(last_event.pan_y, 32 * viewController.flutterView.layer.contentsScale);
775
776 // End gesture.
777 CGEventRef cgEventEnd = CGEventCreateCopy(cgEventStart);
778 CGEventSetIntegerValueField(cgEventEnd, kCGScrollWheelEventScrollPhase, kCGScrollPhaseEnded);
779
780 called = false;
781 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventEnd]];
782 EXPECT_TRUE(called);
783 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
784 EXPECT_EQ(last_event.phase, kPanZoomEnd);
785 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
786 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
787
788 // Start system momentum.
789 CGEventRef cgEventMomentumStart = CGEventCreateCopy(cgEventStart);
790 CGEventSetIntegerValueField(cgEventMomentumStart, kCGScrollWheelEventScrollPhase, 0);
791 CGEventSetIntegerValueField(cgEventMomentumStart, kCGScrollWheelEventMomentumPhase,
792 kCGMomentumScrollPhaseBegin);
793
794 called = false;
795 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventMomentumStart]];
796 EXPECT_FALSE(called);
797
798 // Advance system momentum.
799 CGEventRef cgEventMomentumUpdate = CGEventCreateCopy(cgEventStart);
800 CGEventSetIntegerValueField(cgEventMomentumUpdate, kCGScrollWheelEventScrollPhase, 0);
801 CGEventSetIntegerValueField(cgEventMomentumUpdate, kCGScrollWheelEventMomentumPhase,
802 kCGMomentumScrollPhaseContinue);
803
804 called = false;
805 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventMomentumUpdate]];
806 EXPECT_FALSE(called);
807
808 // Mock a touch on the trackpad.
809 id touchMock = OCMClassMock([NSTouch class]);
810 NSSet* touchSet = [NSSet setWithObject:touchMock];
811 id touchEventMock1 = OCMClassMock([NSEvent class]);
812 OCMStub([touchEventMock1 allTouches]).andReturn(touchSet);
813 CGPoint touchLocation = {0, 0};
814 OCMStub([touchEventMock1 locationInWindow]).andReturn(touchLocation);
815 OCMStub([(NSEvent*)touchEventMock1 timestamp]).andReturn(0.150); // 150 milliseconds.
816
817 // Scroll inertia cancel event should not be issued (timestamp too far in the future).
818 called = false;
819 [viewController touchesBeganWithEvent:touchEventMock1];
820 EXPECT_FALSE(called);
821
822 // Mock another touch on the trackpad.
823 id touchEventMock2 = OCMClassMock([NSEvent class]);
824 OCMStub([touchEventMock2 allTouches]).andReturn(touchSet);
825 OCMStub([touchEventMock2 locationInWindow]).andReturn(touchLocation);
826 OCMStub([(NSEvent*)touchEventMock2 timestamp]).andReturn(0.005); // 5 milliseconds.
827
828 // Scroll inertia cancel event should be issued.
829 called = false;
830 [viewController touchesBeganWithEvent:touchEventMock2];
831 EXPECT_TRUE(called);
832 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindScrollInertiaCancel);
833 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
834
835 // End system momentum.
836 CGEventRef cgEventMomentumEnd = CGEventCreateCopy(cgEventStart);
837 CGEventSetIntegerValueField(cgEventMomentumEnd, kCGScrollWheelEventScrollPhase, 0);
838 CGEventSetIntegerValueField(cgEventMomentumEnd, kCGScrollWheelEventMomentumPhase,
839 kCGMomentumScrollPhaseEnd);
840
841 called = false;
842 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventMomentumEnd]];
843 EXPECT_FALSE(called);
844
845 // May-begin and cancel are used while macOS determines which type of gesture to choose.
846 CGEventRef cgEventMayBegin = CGEventCreateCopy(cgEventStart);
847 CGEventSetIntegerValueField(cgEventMayBegin, kCGScrollWheelEventScrollPhase,
848 kCGScrollPhaseMayBegin);
849
850 called = false;
851 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventMayBegin]];
852 EXPECT_TRUE(called);
853 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
854 EXPECT_EQ(last_event.phase, kPanZoomStart);
855 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
856 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
857
858 // Cancel gesture.
859 CGEventRef cgEventCancel = CGEventCreateCopy(cgEventStart);
860 CGEventSetIntegerValueField(cgEventCancel, kCGScrollWheelEventScrollPhase,
861 kCGScrollPhaseCancelled);
862
863 called = false;
864 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventCancel]];
865 EXPECT_TRUE(called);
866 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
867 EXPECT_EQ(last_event.phase, kPanZoomEnd);
868 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
869 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
870
871 // A discrete scroll event should use the PointerSignal system.
872 CGEventRef cgEventDiscrete = CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 1, 0);
873 CGEventSetType(cgEventDiscrete, kCGEventScrollWheel);
874 CGEventSetIntegerValueField(cgEventDiscrete, kCGScrollWheelEventIsContinuous, 0);
875 CGEventSetIntegerValueField(cgEventDiscrete, kCGScrollWheelEventDeltaAxis2, 1); // scroll_delta_x
876 CGEventSetIntegerValueField(cgEventDiscrete, kCGScrollWheelEventDeltaAxis1, 2); // scroll_delta_y
877
878 called = false;
879 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventDiscrete]];
880 EXPECT_TRUE(called);
881 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindScroll);
882 // pixelsPerLine is 40.0 and direction is reversed.
883 EXPECT_EQ(last_event.scroll_delta_x, -40 * viewController.flutterView.layer.contentsScale);
884 EXPECT_EQ(last_event.scroll_delta_y, -80 * viewController.flutterView.layer.contentsScale);
885
886 // A discrete scroll event should use the PointerSignal system, and flip the
887 // direction when shift is pressed.
888 CGEventRef cgEventDiscreteShift =
889 CGEventCreateScrollWheelEvent(NULL, kCGScrollEventUnitPixel, 1, 0);
890 CGEventSetType(cgEventDiscreteShift, kCGEventScrollWheel);
891 CGEventSetFlags(cgEventDiscreteShift, kCGEventFlagMaskShift | flutter::kModifierFlagShiftLeft);
892 CGEventSetIntegerValueField(cgEventDiscreteShift, kCGScrollWheelEventIsContinuous, 0);
893 CGEventSetIntegerValueField(cgEventDiscreteShift, kCGScrollWheelEventDeltaAxis2,
894 0); // scroll_delta_x
895 CGEventSetIntegerValueField(cgEventDiscreteShift, kCGScrollWheelEventDeltaAxis1,
896 2); // scroll_delta_y
897
898 called = false;
899 [viewController scrollWheel:[NSEvent eventWithCGEvent:cgEventDiscreteShift]];
900 EXPECT_TRUE(called);
901 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindScroll);
902 // pixelsPerLine is 40.0, direction is reversed and axes have been flipped back.
903 EXPECT_FLOAT_EQ(last_event.scroll_delta_x, 0.0 * viewController.flutterView.layer.contentsScale);
904 EXPECT_FLOAT_EQ(last_event.scroll_delta_y,
905 -80.0 * viewController.flutterView.layer.contentsScale);
906
907 // Test for scale events.
908 // Start gesture.
909 called = false;
910 [viewController magnifyWithEvent:flutter::testing::MockGestureEvent(NSEventTypeMagnify,
911 NSEventPhaseBegan, 1, 0)];
912 EXPECT_TRUE(called);
913 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
914 EXPECT_EQ(last_event.phase, kPanZoomStart);
915 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
916 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
917
918 // Update gesture.
919 called = false;
920 [viewController magnifyWithEvent:flutter::testing::MockGestureEvent(NSEventTypeMagnify,
921 NSEventPhaseChanged, 1, 0)];
922 EXPECT_TRUE(called);
923 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
924 EXPECT_EQ(last_event.phase, kPanZoomUpdate);
925 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
926 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
927 EXPECT_EQ(last_event.pan_x, 0);
928 EXPECT_EQ(last_event.pan_y, 0);
929 EXPECT_EQ(last_event.scale, 2); // macOS uses logarithmic scaling values, the linear value for
930 // flutter here should be 2^1 = 2.
931 EXPECT_EQ(last_event.rotation, 0);
932
933 // Make sure the scale values accumulate.
934 called = false;
935 [viewController magnifyWithEvent:flutter::testing::MockGestureEvent(NSEventTypeMagnify,
936 NSEventPhaseChanged, 1, 0)];
937 EXPECT_TRUE(called);
938 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
939 EXPECT_EQ(last_event.phase, kPanZoomUpdate);
940 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
941 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
942 EXPECT_EQ(last_event.pan_x, 0);
943 EXPECT_EQ(last_event.pan_y, 0);
944 EXPECT_EQ(last_event.scale, 4); // macOS uses logarithmic scaling values, the linear value for
945 // flutter here should be 2^(1+1) = 2.
946 EXPECT_EQ(last_event.rotation, 0);
947
948 // End gesture.
949 called = false;
950 [viewController magnifyWithEvent:flutter::testing::MockGestureEvent(NSEventTypeMagnify,
951 NSEventPhaseEnded, 0, 0)];
952 EXPECT_TRUE(called);
953 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
954 EXPECT_EQ(last_event.phase, kPanZoomEnd);
955 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
956 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
957
958 // Test for rotation events.
959 // Start gesture.
960 called = false;
961 [viewController rotateWithEvent:flutter::testing::MockGestureEvent(NSEventTypeRotate,
962 NSEventPhaseBegan, 1, 0)];
963 EXPECT_TRUE(called);
964 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
965 EXPECT_EQ(last_event.phase, kPanZoomStart);
966 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
967 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
968
969 // Update gesture.
970 called = false;
971 [viewController rotateWithEvent:flutter::testing::MockGestureEvent(
972 NSEventTypeRotate, NSEventPhaseChanged, 0, -180)]; // degrees
973 EXPECT_TRUE(called);
974 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
975 EXPECT_EQ(last_event.phase, kPanZoomUpdate);
976 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
977 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
978 EXPECT_EQ(last_event.pan_x, 0);
979 EXPECT_EQ(last_event.pan_y, 0);
980 EXPECT_EQ(last_event.scale, 1);
981 EXPECT_EQ(last_event.rotation, M_PI); // radians
982
983 // Make sure the rotation values accumulate.
984 called = false;
985 [viewController rotateWithEvent:flutter::testing::MockGestureEvent(
986 NSEventTypeRotate, NSEventPhaseChanged, 0, -360)]; // degrees
987 EXPECT_TRUE(called);
988 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
989 EXPECT_EQ(last_event.phase, kPanZoomUpdate);
990 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
991 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
992 EXPECT_EQ(last_event.pan_x, 0);
993 EXPECT_EQ(last_event.pan_y, 0);
994 EXPECT_EQ(last_event.scale, 1);
995 EXPECT_EQ(last_event.rotation, 3 * M_PI); // radians
996
997 // End gesture.
998 called = false;
999 [viewController rotateWithEvent:flutter::testing::MockGestureEvent(NSEventTypeRotate,
1000 NSEventPhaseEnded, 0, 0)];
1001 EXPECT_TRUE(called);
1002 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
1003 EXPECT_EQ(last_event.phase, kPanZoomEnd);
1004 EXPECT_EQ(last_event.device_kind, kFlutterPointerDeviceKindTrackpad);
1005 EXPECT_EQ(last_event.signal_kind, kFlutterPointerSignalKindNone);
1006
1007 // Test that stray NSEventPhaseCancelled event does not crash
1008 called = false;
1009 [viewController rotateWithEvent:flutter::testing::MockGestureEvent(NSEventTypeRotate,
1010 NSEventPhaseCancelled, 0, 0)];
1011 EXPECT_FALSE(called);
1012
1013 return true;
1014 }

References kDefaultFlutterPointerEvent, and flutter::kModifierFlagShiftLeft.

## ◆ testViewControllerIsReleased

- (bool) testViewControllerIsReleased |  |  |

Definition at line 1239 of file FlutterViewControllerTest.mm.

1239 {
1240 __weak FlutterViewController* weakController;
1241 @autoreleasepool {
1242 id engineMock = flutter::testing::CreateMockFlutterEngine(@"");
1243
1244 FlutterRenderer* renderer_ = [[FlutterRenderer alloc] initWithFlutterEngine:engineMock];
1245 OCMStub([engineMock renderer]).andReturn(renderer_);
1246
1247 FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
1248 nibName:@""
1249 bundle:nil];
1250 [viewController loadView];
1251 weakController = viewController;
1252
1253 [engineMock shutDownEngine];
1254 }
1255
1256 EXPECT_EQ(weakController, nil);
1257 return true;
1258 }

References flutter::testing::CreateMockFlutterEngine().

## ◆ testViewWillAppearCalledMultipleTimes:

- (bool) testViewWillAppearCalledMultipleTimes: |  | (id) | mockEngine |

Definition at line 1052 of file FlutterViewControllerTest.mm.

1052 :(id)engineMock {
1053 FlutterViewController* viewController = [[FlutterViewController alloc] initWithEngine:engineMock
1054 nibName:@""
1055 bundle:nil];
1056 [viewController viewWillAppear];
1057 [viewController viewWillAppear];
1058 return true;
1059 }

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

- shell/platform/darwin/macos/framework/Source/FlutterViewControllerTest.mm

FlutterViewControllerTestObjC
Definition: FlutterViewControllerTest.mm:98
kDefaultFlutterKeyEvent
static const FlutterKeyEvent kDefaultFlutterKeyEvent
Definition: FlutterViewControllerTest.mm:25
FlutterViewController
Definition: FlutterViewController.h:73
FlutterResponderWrapper
Definition: FlutterViewControllerTest.mm:49
MouseEventFlutterViewController
Definition: FlutterViewControllerTest.mm:83
flutter::testing::CreateMockFlutterEngine
id CreateMockFlutterEngine(NSString *pasteboardString)
Definition: FlutterEngineTestUtils.mm:76
flutter::kModifierFlagMetaLeft
@ kModifierFlagMetaLeft
Definition: KeyCodeMap_Internal.h:83
flutter::kModifierFlagAltRight
@ kModifierFlagAltRight
Definition: KeyCodeMap_Internal.h:86
flutter::kModifierFlagMetaRight
@ kModifierFlagMetaRight
Definition: KeyCodeMap_Internal.h:84
-[FlutterViewController lookupKeyForAsset:]
nonnull NSString * lookupKeyForAsset:(nonnull NSString *asset)
MouseEventFlutterViewController::mouseDownCalled
BOOL mouseDownCalled
Definition: FlutterViewControllerTest.mm:84
KeyEventWrapper
Definition: FlutterViewControllerTest.mm:28
FlutterRenderer
Definition: FlutterRenderer.h:18
flutter::kModifierFlagControlLeft
@ kModifierFlagControlLeft
Definition: KeyCodeMap_Internal.h:80
-[FlutterViewController onPreEngineRestart]
void onPreEngineRestart()
Definition: FlutterViewController.mm:488
flutter::kModifierFlagAltLeft
@ kModifierFlagAltLeft
Definition: KeyCodeMap_Internal.h:85
-[FlutterViewController lookupKeyForAsset:fromPackage:]
nonnull NSString * lookupKeyForAsset:fromPackage:(nonnull NSString *asset,[fromPackage] nonnull NSString *package)
flutter::keyCodeToModifierFlag
const NSDictionary * keyCodeToModifierFlag
Definition: KeyCodeMap.g.mm:223
flutter::kModifierFlagShiftRight
@ kModifierFlagShiftRight
Definition: KeyCodeMap_Internal.h:82
MouseEventFlutterViewController::mouseUpCalled
BOOL mouseUpCalled
Definition: FlutterViewControllerTest.mm:85
kDefaultFlutterPointerEvent
static const FlutterPointerEvent kDefaultFlutterPointerEvent
Definition: FlutterViewControllerTest.mm:24
FlutterView
Definition: FlutterView.h:35
flutter::kModifierFlagShiftLeft
@ kModifierFlagShiftLeft
Definition: KeyCodeMap_Internal.h:81
FlutterBinaryMessenger-p
Definition: FlutterBinaryMessenger.h:49
flutter::kModifierFlagControlRight
@ kModifierFlagControlRight
Definition: KeyCodeMap_Internal.h:87
FlutterBinaryReply
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
+[FlutterMessageCodec-p sharedInstance]
instancetype sharedInstance()
FlutterJSONMessageCodec
Definition: FlutterCodecs.h:81