| 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 99 of file FlutterViewControllerTest.mm.

## Method Documentation

## ◆ mouseAndGestureEventsAreHandledSeparately:

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

Definition at line 1018 of file FlutterViewControllerTest.mm.

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

## ◆ respondFalseForSendEvent:callback:userData:

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

Definition at line 708 of file FlutterViewControllerTest.mm.

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

## ◆ testCtrlTabKeyEventIsPropagated:

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

Definition at line 379 of file FlutterViewControllerTest.mm.

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

References kDefaultFlutterKeyEvent.

## ◆ testFlagsChangedEventsArePropagatedIfNotHandled:

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

Definition at line 547 of file FlutterViewControllerTest.mm.

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

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

## ◆ testFlutterViewIsConfigured:

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

Definition at line 528 of file FlutterViewControllerTest.mm.

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

## ◆ testKeyboardIsRestartedOnEngineRestart:

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

Definition at line 655 of file FlutterViewControllerTest.mm.

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

References kDefaultFlutterKeyEvent, and FlutterViewController::onPreEngineRestart.

## ◆ testKeyEquivalentIsPassedToTextInputPlugin:

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

Definition at line 423 of file FlutterViewControllerTest.mm.

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

References kDefaultFlutterKeyEvent.

## ◆ testKeyEventsAreNotPropagatedIfHandled:

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

Definition at line 600 of file FlutterViewControllerTest.mm.

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

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

## ◆ testKeyEventsArePropagatedIfNotHandled:

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

Definition at line 473 of file FlutterViewControllerTest.mm.

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

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

## ◆ testKeyEventsAreSentToFramework:

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

Definition at line 341 of file FlutterViewControllerTest.mm.

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

References kDefaultFlutterKeyEvent, and <FlutterMessageCodec>::sharedInstance.

## ◆ testLookupKeyAssets

- (bool) testLookupKeyAssets |  |  |

Definition at line 1060 of file FlutterViewControllerTest.mm.

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

References FlutterViewController::lookupKeyForAsset:.

## ◆ testLookupKeyAssetsWithPackage

- (bool) testLookupKeyAssetsWithPackage |  |  |

Definition at line 1068 of file FlutterViewControllerTest.mm.

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

References FlutterViewController::lookupKeyForAsset:fromPackage:.

## ◆ testModifierKeysAreSynthesizedOnMouseMove:

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

Definition at line 1132 of file FlutterViewControllerTest.mm.

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

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 1090 of file FlutterViewControllerTest.mm.

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

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

## ◆ testTrackpadGesturesAreSentToFramework:

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

Definition at line 716 of file FlutterViewControllerTest.mm.

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

References kDefaultFlutterPointerEvent, and flutter::kModifierFlagShiftLeft.

## ◆ testViewControllerIsReleased

- (bool) testViewControllerIsReleased |  |  |

Definition at line 1238 of file FlutterViewControllerTest.mm.

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

References flutter::testing::CreateMockFlutterEngine().

## ◆ testViewWillAppearCalledMultipleTimes:

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

Definition at line 1051 of file FlutterViewControllerTest.mm.

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

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

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

FlutterViewControllerTestObjC
Definition: FlutterViewControllerTest.mm:99
kDefaultFlutterKeyEvent
static const FlutterKeyEvent kDefaultFlutterKeyEvent
Definition: FlutterViewControllerTest.mm:26
FlutterViewController
Definition: FlutterViewController.h:73
FlutterResponderWrapper
Definition: FlutterViewControllerTest.mm:50
MouseEventFlutterViewController
Definition: FlutterViewControllerTest.mm:84
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:85
KeyEventWrapper
Definition: FlutterViewControllerTest.mm:29
FlutterRenderer
Definition: FlutterRenderer.h:18
flutter::kModifierFlagControlLeft
@ kModifierFlagControlLeft
Definition: KeyCodeMap_Internal.h:80
-[FlutterViewController onPreEngineRestart]
void onPreEngineRestart()
Definition: FlutterViewController.mm:490
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:86
kDefaultFlutterPointerEvent
static const FlutterPointerEvent kDefaultFlutterPointerEvent
Definition: FlutterViewControllerTest.mm:25
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