15 #include "flutter/fml/synchronization/waitable_event.h"
16 #include "flutter/lib/ui/window/platform_message.h"
26 #include "flutter/shell/platform/embedder/embedder.h"
27 #include "flutter/shell/platform/embedder/embedder_engine.h"
28 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
29 #include "flutter/testing/stream_capture.h"
30 #include "flutter/testing/test_dart_native_resolver.h"
31 #include "gtest/gtest.h"
53 arguments:(nullable id)args {
54 return viewIdentifier == 42 ? [[NSView alloc] init] : nil;
63 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication* _Nonnull)sender {
65 return NSTerminateCancel;
73 @property(nonatomic, strong, readonly) NSPointerArray* registeredDelegates;
76 - (BOOL)hasDelegate:(nonnull NSObject<FlutterAppLifecycleDelegate>*)delegate;
89 std::vector<void*> _delegates;
92 - (void)addApplicationLifecycleDelegate:(nonnull NSObject<FlutterAppLifecycleDelegate>*)delegate {
93 _delegates.push_back((__bridge
void*)delegate);
96 - (void)removeApplicationLifecycleDelegate:
97 (nonnull NSObject<FlutterAppLifecycleDelegate>*)delegate {
98 auto delegateIndex = std::find(_delegates.begin(), _delegates.end(), (__bridge
void*)delegate);
99 NSAssert(delegateIndex != _delegates.end(),
100 @"Attempting to unregister a delegate that was not registered.");
101 _delegates.erase(delegateIndex);
105 return std::find(_delegates.begin(), _delegates.end(), (__bridge
void*)delegate) !=
117 + (void)registerWithRegistrar:(id<FlutterPluginRegistrar>)registrar {
127 - (NSArray<NSScreen*>*)screens {
128 id mockScreen = OCMClassMock([NSScreen
class]);
129 OCMStub([mockScreen backingScaleFactor]).andReturn(2.0);
130 OCMStub([mockScreen deviceDescription]).andReturn(@{
131 @"NSScreenNumber" : [NSNumber numberWithInt:10]
133 OCMStub([mockScreen frame]).andReturn(NSMakeRect(10, 20, 30, 40));
134 return [NSArray arrayWithObject:mockScreen];
144 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
145 ASSERT_TRUE(engine.running);
150 std::string executable_name = [[engine executableName] UTF8String];
151 ASSERT_FALSE(executable_name.empty());
154 fml::AutoResetWaitableEvent latch;
155 AddNativeCallback(
"NotifyStringValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
156 const auto dart_string = tonic::DartConverter<std::string>::FromDart(
157 Dart_GetNativeArgument(args, 0));
158 EXPECT_EQ(executable_name, dart_string);
163 EXPECT_TRUE([engine runWithEntrypoint:
@"executableNameNotNull"]);
168 #ifndef FLUTTER_RELEASE
170 setenv(
"FLUTTER_ENGINE_SWITCHES",
"2", 1);
171 setenv(
"FLUTTER_ENGINE_SWITCH_1",
"abc", 1);
172 setenv(
"FLUTTER_ENGINE_SWITCH_2",
"foo=\"bar, baz\"", 1);
175 std::vector<std::string> switches = engine.switches;
176 ASSERT_EQ(switches.size(), 2UL);
177 EXPECT_EQ(switches[0],
"--abc");
178 EXPECT_EQ(switches[1],
"--foo=\"bar, baz\"");
180 unsetenv(
"FLUTTER_ENGINE_SWITCHES");
181 unsetenv(
"FLUTTER_ENGINE_SWITCH_1");
182 unsetenv(
"FLUTTER_ENGINE_SWITCH_2");
184 #endif // !FLUTTER_RELEASE
188 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
190 NSData* test_message = [@"a message" dataUsingEncoding:NSUTF8StringEncoding];
193 engine.embedderAPI.SendPlatformMessage = MOCK_ENGINE_PROC(
194 SendPlatformMessage, ([&called, test_message](
auto engine,
auto message) {
196 EXPECT_STREQ(message->channel,
"test");
197 EXPECT_EQ(memcmp(message->message, test_message.bytes, message->message_size), 0);
201 [engine.binaryMessenger sendOnChannel:@"test" message:test_message];
207 fml::AutoResetWaitableEvent latch;
208 AddNativeCallback(
"SignalNativeTest",
209 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); }));
212 StreamCapture stdout_capture(&std::cout);
216 EXPECT_TRUE([engine runWithEntrypoint:
@"canLogToStdout"]);
217 ASSERT_TRUE(engine.running);
221 stdout_capture.Stop();
224 EXPECT_TRUE(stdout_capture.GetOutput().find(
"Hello logging") != std::string::npos);
231 fml::AutoResetWaitableEvent latch;
232 AddNativeCallback(
"SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
234 EXPECT_TRUE(rootLayer.backgroundColor != nil);
235 if (rootLayer.backgroundColor != nil) {
236 NSColor* actualBackgroundColor =
237 [NSColor colorWithCGColor:rootLayer.backgroundColor];
238 EXPECT_EQ(actualBackgroundColor, [NSColor blackColor]);
244 EXPECT_TRUE([engine runWithEntrypoint:
@"backgroundTest"]);
245 ASSERT_TRUE(engine.running);
250 [viewController loadView];
251 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
260 fml::AutoResetWaitableEvent latch;
261 AddNativeCallback(
"SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
263 EXPECT_TRUE(rootLayer.backgroundColor != nil);
264 if (rootLayer.backgroundColor != nil) {
265 NSColor* actualBackgroundColor =
266 [NSColor colorWithCGColor:rootLayer.backgroundColor];
267 EXPECT_EQ(actualBackgroundColor, [NSColor whiteColor]);
273 EXPECT_TRUE([engine runWithEntrypoint:
@"backgroundTest"]);
274 ASSERT_TRUE(engine.running);
279 [viewController loadView];
280 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
289 auto original_init = engine.embedderAPI.Initialize;
290 std::function<void(
const FlutterSemanticsUpdate2*,
void*)> update_semantics_callback;
291 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
292 Initialize, ([&update_semantics_callback, &original_init](
293 size_t version,
const FlutterRendererConfig* config,
294 const FlutterProjectArgs* args,
void*
user_data,
auto engine_out) {
295 update_semantics_callback = args->update_semantics_callback2;
296 return original_init(version, config, args,
user_data, engine_out);
298 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
303 [viewController loadView];
305 bool enabled_called =
false;
306 engine.embedderAPI.UpdateSemanticsEnabled =
307 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&enabled_called](
auto engine,
bool enabled) {
308 enabled_called = enabled;
311 engine.semanticsEnabled = YES;
312 EXPECT_TRUE(enabled_called);
314 FlutterSemanticsNode2 root;
316 root.flags =
static_cast<FlutterSemanticsFlag
>(0);
317 root.actions =
static_cast<FlutterSemanticsAction
>(0);
318 root.text_selection_base = -1;
319 root.text_selection_extent = -1;
323 root.increased_value =
"";
324 root.decreased_value =
"";
326 root.child_count = 1;
327 int32_t children[] = {1};
328 root.children_in_traversal_order = children;
329 root.custom_accessibility_actions_count = 0;
331 FlutterSemanticsNode2 child1;
333 child1.flags =
static_cast<FlutterSemanticsFlag
>(0);
334 child1.actions =
static_cast<FlutterSemanticsAction
>(0);
335 child1.text_selection_base = -1;
336 child1.text_selection_extent = -1;
337 child1.label =
"child 1";
340 child1.increased_value =
"";
341 child1.decreased_value =
"";
343 child1.child_count = 0;
344 child1.custom_accessibility_actions_count = 0;
346 FlutterSemanticsUpdate2 update;
347 update.node_count = 2;
348 FlutterSemanticsNode2* nodes[] = {&root, &child1};
349 update.nodes = nodes;
350 update.custom_action_count = 0;
351 update_semantics_callback(&update, (__bridge
void*)engine);
354 EXPECT_EQ([engine.viewController.flutterView.accessibilityChildren count], 1u);
355 NSAccessibilityElement* native_root = engine.viewController.flutterView.accessibilityChildren[0];
356 std::string root_label = [native_root.accessibilityLabel UTF8String];
357 EXPECT_TRUE(root_label ==
"root");
358 EXPECT_EQ(native_root.accessibilityRole, NSAccessibilityGroupRole);
359 EXPECT_EQ([native_root.accessibilityChildren count], 1u);
360 NSAccessibilityElement* native_child1 = native_root.accessibilityChildren[0];
361 std::string child1_value = [native_child1.accessibilityValue UTF8String];
362 EXPECT_TRUE(child1_value ==
"child 1");
363 EXPECT_EQ(native_child1.accessibilityRole, NSAccessibilityStaticTextRole);
364 EXPECT_EQ([native_child1.accessibilityChildren count], 0u);
366 bool semanticsEnabled =
true;
367 engine.embedderAPI.UpdateSemanticsEnabled =
368 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&semanticsEnabled](
auto engine,
bool enabled) {
369 semanticsEnabled = enabled;
372 engine.semanticsEnabled = NO;
373 EXPECT_FALSE(semanticsEnabled);
375 EXPECT_EQ([engine.viewController.flutterView.accessibilityChildren count], 0u);
377 [engine setViewController:nil];
383 auto original_init = engine.embedderAPI.Initialize;
384 std::function<void(
const FlutterSemanticsUpdate2*,
void*)> update_semantics_callback;
385 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
386 Initialize, ([&update_semantics_callback, &original_init](
387 size_t version,
const FlutterRendererConfig* config,
388 const FlutterProjectArgs* args,
void*
user_data,
auto engine_out) {
389 update_semantics_callback = args->update_semantics_callback2;
390 return original_init(version, config, args,
user_data, engine_out);
392 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
395 bool enabled_called =
false;
396 engine.embedderAPI.UpdateSemanticsEnabled =
397 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&enabled_called](
auto engine,
bool enabled) {
398 enabled_called = enabled;
401 engine.semanticsEnabled = YES;
402 EXPECT_TRUE(enabled_called);
404 FlutterSemanticsNode2 root;
406 root.flags =
static_cast<FlutterSemanticsFlag
>(0);
407 root.actions =
static_cast<FlutterSemanticsAction
>(0);
408 root.text_selection_base = -1;
409 root.text_selection_extent = -1;
413 root.increased_value =
"";
414 root.decreased_value =
"";
416 root.child_count = 1;
417 int32_t children[] = {1};
418 root.children_in_traversal_order = children;
419 root.custom_accessibility_actions_count = 0;
421 FlutterSemanticsNode2 child1;
423 child1.flags =
static_cast<FlutterSemanticsFlag
>(0);
424 child1.actions =
static_cast<FlutterSemanticsAction
>(0);
425 child1.text_selection_base = -1;
426 child1.text_selection_extent = -1;
427 child1.label =
"child 1";
430 child1.increased_value =
"";
431 child1.decreased_value =
"";
433 child1.child_count = 0;
434 child1.custom_accessibility_actions_count = 0;
436 FlutterSemanticsUpdate2 update;
437 update.node_count = 2;
438 FlutterSemanticsNode2* nodes[] = {&root, &child1};
439 update.nodes = nodes;
440 update.custom_action_count = 0;
443 update_semantics_callback(&update, (__bridge
void*)engine);
446 EXPECT_EQ(engine.viewController, nil);
449 bool semanticsEnabled =
true;
450 engine.embedderAPI.UpdateSemanticsEnabled =
451 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&semanticsEnabled](
auto engine,
bool enabled) {
452 semanticsEnabled = enabled;
455 engine.semanticsEnabled = NO;
456 EXPECT_FALSE(semanticsEnabled);
458 EXPECT_EQ(engine.viewController, nil);
463 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
466 bool enabled_called =
false;
467 engine.embedderAPI.UpdateSemanticsEnabled =
468 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&enabled_called](
auto engine,
bool enabled) {
469 enabled_called = enabled;
472 engine.semanticsEnabled = YES;
473 EXPECT_TRUE(enabled_called);
483 EXPECT_NE(viewController.accessibilityBridge.lock(),
nullptr);
487 fml::AutoResetWaitableEvent latch;
488 bool latch_called =
false;
489 AddNativeCallback(
"SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
495 EXPECT_TRUE([engine runWithEntrypoint:
@"nativeCallback"]);
496 ASSERT_TRUE(engine.running);
499 ASSERT_TRUE(latch_called);
503 NSString* fixtures = @(flutter::testing::GetFixturesPath());
505 initWithAssetsPath:fixtures
506 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
512 [viewController loadView];
513 [viewController viewDidLoad];
514 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
516 EXPECT_TRUE([engine runWithEntrypoint:
@"canCompositePlatformViews"]);
519 withId:@"factory_id"];
520 [engine.platformViewController
524 @"viewType" : @"factory_id",
529 [engine.testThreadSynchronizer blockUntilFrameAvailable];
531 CALayer* rootLayer = viewController.flutterView.layer;
534 EXPECT_EQ(rootLayer.sublayers.count, 2u);
535 EXPECT_EQ(viewController.flutterView.subviews.count, 1u);
544 auto original_init = engine.embedderAPI.Initialize;
546 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
547 Initialize, ([&compositor, &original_init](
548 size_t version,
const FlutterRendererConfig* config,
549 const FlutterProjectArgs* args,
void*
user_data,
auto engine_out) {
550 compositor = *args->compositor;
551 return original_init(version, config, args,
user_data, engine_out);
557 [viewController loadView];
559 EXPECT_TRUE([engine runWithEntrypoint:
@"empty"]);
561 FlutterBackingStoreConfig config = {
562 .struct_size =
sizeof(FlutterBackingStoreConfig),
563 .size = FlutterSize{10, 10},
565 FlutterBackingStore backing_store = {};
566 EXPECT_NE(compositor.create_backing_store_callback,
nullptr);
568 compositor.create_backing_store_callback(&config, &backing_store, compositor.user_data));
571 .type = kFlutterLayerContentTypeBackingStore,
572 .backing_store = &backing_store,
574 std::vector<FlutterLayer*> layers = {&layer};
576 FlutterPresentViewInfo info = {
577 .struct_size =
sizeof(FlutterPresentViewInfo),
579 .layers =
const_cast<const FlutterLayer**
>(layers.data()),
583 EXPECT_NE(compositor.present_view_callback,
nullptr);
584 EXPECT_FALSE(compositor.present_view_callback(&info));
585 EXPECT_TRUE(compositor.collect_backing_store_callback(&backing_store, compositor.user_data));
587 (void)viewController;
592 NSString* fixtures = @(flutter::testing::GetFixturesPath());
594 initWithAssetsPath:fixtures
595 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
597 project.dartEntrypointArguments = @[ @"arg1", @"arg2" ];
601 auto original_init = engine.embedderAPI.Initialize;
602 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
603 Initialize, ([&called, &original_init](
size_t version,
const FlutterRendererConfig* config,
604 const FlutterProjectArgs* args,
void*
user_data,
607 EXPECT_EQ(args->dart_entrypoint_argc, 2);
608 NSString* arg1 = [[NSString alloc] initWithCString:args->dart_entrypoint_argv[0]
609 encoding:NSUTF8StringEncoding];
610 NSString* arg2 = [[NSString alloc] initWithCString:args->dart_entrypoint_argv[1]
611 encoding:NSUTF8StringEncoding];
613 EXPECT_TRUE([arg1 isEqualToString:
@"arg1"]);
614 EXPECT_TRUE([arg2 isEqualToString:
@"arg2"]);
616 return original_init(version, config, args,
user_data, engine_out);
619 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
631 id<FlutterBinaryMessenger> binaryMessenger = nil;
634 NSString* fixtures = @(flutter::testing::GetFixturesPath());
636 initWithAssetsPath:fixtures
637 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
640 allowHeadlessExecution:YES];
647 EXPECT_NE(binaryMessenger, nil);
648 EXPECT_EQ(weakEngine, nil);
655 id<FlutterTextureRegistry> textureRegistry;
658 NSString* fixtures = @(flutter::testing::GetFixturesPath());
660 initWithAssetsPath:fixtures
661 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
664 allowHeadlessExecution:YES];
665 id<FlutterPluginRegistrar> registrar = [engine registrarForPlugin:@"MyPlugin"];
666 textureRegistry = registrar.textures;
671 EXPECT_NE(textureRegistry, nil);
672 EXPECT_EQ(weakEngine, nil);
676 NSString* fixtures = @(flutter::testing::GetFixturesPath());
678 initWithAssetsPath:fixtures
679 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
682 allowHeadlessExecution:YES];
684 EXPECT_EQ([engine valuePublishedByPlugin:
@"NoSuchPlugin"], nil);
688 NSString* fixtures = @(flutter::testing::GetFixturesPath());
690 initWithAssetsPath:fixtures
691 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
694 allowHeadlessExecution:YES];
695 NSString* pluginName =
@"MyPlugin";
697 [engine registrarForPlugin:pluginName];
701 EXPECT_EQ([engine valuePublishedByPlugin:pluginName], [NSNull
null]);
705 NSString* fixtures = @(flutter::testing::GetFixturesPath());
707 initWithAssetsPath:fixtures
708 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
711 allowHeadlessExecution:YES];
712 NSString* pluginName =
@"MyPlugin";
713 id<FlutterPluginRegistrar> registrar = [engine registrarForPlugin:pluginName];
715 NSString* firstValue =
@"A published value";
716 NSArray* secondValue = @[ @"A different published value" ];
718 [registrar publish:firstValue];
719 EXPECT_EQ([engine valuePublishedByPlugin:pluginName], firstValue);
721 [registrar publish:secondValue];
722 EXPECT_EQ([engine valuePublishedByPlugin:pluginName], secondValue);
733 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
735 NSString* channel =
@"_test_";
736 NSData* channel_data = [channel dataUsingEncoding:NSUTF8StringEncoding];
741 engine.embedderAPI.SendPlatformMessage = MOCK_ENGINE_PROC(
742 SendPlatformMessage, ([](
auto engine_,
auto message_) {
743 if (strcmp(message_->channel,
"test/send_message") == 0) {
745 std::string message = R
"|({"method": "a"})|";
746 std::string channel(reinterpret_cast<const char*>(message_->message),
747 message_->message_size);
748 reinterpret_cast<EmbedderEngine*>(engine_)
751 ->HandlePlatformMessage(std::make_unique<PlatformMessage>(
752 channel.c_str(), fml::MallocMapping::Copy(message.c_str(), message.length()),
753 fml::RefPtr<PlatformMessageResponse>()));
758 __block
int record = 0;
768 [engine.binaryMessenger sendOnChannel:@"test/send_message" message:channel_data];
769 EXPECT_EQ(record, 1);
779 [engine.binaryMessenger sendOnChannel:@"test/send_message" message:channel_data];
780 EXPECT_EQ(record, 11);
784 [engine.binaryMessenger sendOnChannel:@"test/send_message" message:channel_data];
785 EXPECT_EQ(record, 21);
792 __block
bool calledAfterClear =
false;
793 __block
bool valueAfterClear;
795 calledAfterClear =
true;
796 NSNumber* valueNumber = [result valueForKey:@"value"];
797 valueAfterClear = [valueNumber boolValue];
801 [engineMock handleMethodCall:methodCallAfterClear result:resultAfterClear];
802 EXPECT_TRUE(calledAfterClear);
803 EXPECT_FALSE(valueAfterClear);
810 __block
bool called =
false;
814 NSNumber* valueNumber = [result valueForKey:@"value"];
815 value = [valueNumber boolValue];
819 [engineMock handleMethodCall:methodCall result:result];
828 binaryMessenger:engine.binaryMessenger
830 __block BOOL didCallCallback = NO;
834 didCallCallback = YES;
836 EXPECT_TRUE([engine runWithEntrypoint:
@"sendFooMessage"]);
839 while (!didCallCallback) {
840 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
848 binaryMessenger:engine.binaryMessenger
850 __block BOOL didCallCallback = NO;
852 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
854 dispatch_async(dispatch_get_main_queue(), ^{
855 didCallCallback = YES;
859 EXPECT_TRUE([engine runWithEntrypoint:
@"sendFooMessage"]);
861 while (!didCallCallback) {
862 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
870 std::thread rasterThread([&threadSynchronizer] {
872 size:CGSizeMake(100, 100)
881 NSString* fixtures = @(flutter::testing::GetFixturesPath());
883 initWithAssetsPath:fixtures
884 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
894 engine = viewController1.
engine;
918 allowHeadlessExecution:NO];
945 __block NSString* nextResponse =
@"exit";
946 __block BOOL triedToTerminate = NO;
949 terminator:^(id sender) {
950 triedToTerminate = TRUE;
953 OCMStub([engineMock terminationHandler]).andReturn(terminationHandler);
956 [engineMock binaryMessenger])
957 .andReturn(binaryMessengerMock);
958 OCMStub([engineMock sendOnChannel:
@"flutter/platform"
960 binaryReply:[OCMArg any]])
961 .andDo((^(NSInvocation* invocation) {
962 [invocation retainArguments];
964 NSData* returnedMessage;
965 [invocation getArgument:&callback atIndex:4];
966 if ([nextResponse isEqualToString:
@"error"]) {
973 NSDictionary* responseDict = @{
@"response" : nextResponse};
977 callback(returnedMessage);
979 __block NSString* calledAfterTerminate =
@"";
981 NSDictionary* resultDict = result;
982 calledAfterTerminate = resultDict[@"response"];
989 triedToTerminate = NO;
990 calledAfterTerminate =
@"";
991 nextResponse =
@"cancel";
992 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
993 EXPECT_STREQ([calledAfterTerminate UTF8String],
"");
994 EXPECT_TRUE(triedToTerminate);
997 terminationHandler.acceptingRequests = YES;
998 triedToTerminate = NO;
999 calledAfterTerminate =
@"";
1000 nextResponse =
@"exit";
1001 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1002 EXPECT_STREQ([calledAfterTerminate UTF8String],
"exit");
1003 EXPECT_TRUE(triedToTerminate);
1005 triedToTerminate = NO;
1006 calledAfterTerminate =
@"";
1007 nextResponse =
@"cancel";
1008 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1009 EXPECT_STREQ([calledAfterTerminate UTF8String],
"cancel");
1010 EXPECT_FALSE(triedToTerminate);
1013 triedToTerminate = NO;
1014 calledAfterTerminate =
@"";
1015 nextResponse =
@"error";
1016 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1017 EXPECT_STREQ([calledAfterTerminate UTF8String],
"");
1018 EXPECT_TRUE(triedToTerminate);
1022 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1024 [NSApplication sharedApplication].delegate = plainDelegate;
1031 EXPECT_EQ([[[NSApplication sharedApplication] delegate] applicationShouldTerminate:NSApp],
1034 [NSApplication sharedApplication].delegate = previousDelegate;
1038 __block BOOL announced = NO;
1041 OCMStub([engineMock announceAccessibilityMessage:[OCMArg any]
1042 withPriority:NSAccessibilityPriorityMedium])
1043 .andDo((^(NSInvocation* invocation) {
1045 [invocation retainArguments];
1047 [invocation getArgument:&message atIndex:2];
1048 EXPECT_EQ(message,
@"error message");
1051 NSDictionary<NSString*, id>* annotatedEvent =
1052 @{
@"type" :
@"announce",
1053 @"data" : @{
@"message" :
@"error message"}};
1055 [engineMock handleAccessibilityEvent:annotatedEvent];
1057 EXPECT_TRUE(announced);
1067 .andDo((^(NSInvocation* invocation) {
1071 .andDo((^(NSInvocation* invocation) {
1075 .andDo((^(NSInvocation* invocation) {
1079 .andDo((^(NSInvocation* invocation) {
1083 .andDo((^(NSInvocation* invocation) {
1087 __block NSApplicationOcclusionState visibility = NSApplicationOcclusionStateVisible;
1088 id mockApplication = OCMPartialMock([NSApplication sharedApplication]);
1089 OCMStub((NSApplicationOcclusionState)[mockApplication occlusionState])
1090 .andDo(^(NSInvocation* invocation) {
1091 [invocation setReturnValue:&visibility];
1094 NSNotification* willBecomeActive =
1095 [[NSNotification alloc] initWithName:NSApplicationWillBecomeActiveNotification
1098 NSNotification* willResignActive =
1099 [[NSNotification alloc] initWithName:NSApplicationWillResignActiveNotification
1103 NSNotification* didChangeOcclusionState;
1104 didChangeOcclusionState =
1105 [[NSNotification alloc] initWithName:NSApplicationDidChangeOcclusionStateNotification
1109 [engineMock handleDidChangeOcclusionState:didChangeOcclusionState];
1112 [engineMock handleWillBecomeActive:willBecomeActive];
1115 [engineMock handleWillResignActive:willResignActive];
1119 [engineMock handleDidChangeOcclusionState:didChangeOcclusionState];
1122 [engineMock handleWillBecomeActive:willBecomeActive];
1125 [engineMock handleWillResignActive:willResignActive];
1128 [mockApplication stopMocking];
1132 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1134 [NSApplication sharedApplication].delegate = fakeAppDelegate;
1139 [[engine registrarForPlugin:@"TestPlugin"] addApplicationDelegate:plugin];
1141 EXPECT_TRUE([fakeAppDelegate hasDelegate:plugin]);
1143 [NSApplication sharedApplication].delegate = previousDelegate;
1147 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1149 [NSApplication sharedApplication].delegate = fakeAppDelegate;
1156 [[engine registrarForPlugin:@"TestPlugin"] addApplicationDelegate:plugin];
1157 EXPECT_TRUE([fakeAppDelegate hasDelegate:plugin]);
1162 EXPECT_FALSE([fakeAppDelegate hasDelegate:plugin]);
1164 [NSApplication sharedApplication].delegate = previousDelegate;
1170 auto original_update_displays = engine.embedderAPI.NotifyDisplayUpdate;
1171 engine.embedderAPI.NotifyDisplayUpdate = MOCK_ENGINE_PROC(
1172 NotifyDisplayUpdate, ([&updated, &original_update_displays](
1173 auto engine,
auto update_type,
auto* displays,
auto display_count) {
1175 return original_update_displays(engine, update_type, displays, display_count);
1178 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1179 EXPECT_TRUE(updated);
1182 [[NSNotificationCenter defaultCenter]
1183 postNotificationName:NSApplicationDidChangeScreenParametersNotification
1185 EXPECT_TRUE(updated);
1191 auto original_set_viewport_metrics = engine.embedderAPI.SendWindowMetricsEvent;
1192 engine.embedderAPI.SendWindowMetricsEvent = MOCK_ENGINE_PROC(
1193 SendWindowMetricsEvent,
1194 ([&updated, &original_set_viewport_metrics](
auto engine,
auto* window_metrics) {
1196 return original_set_viewport_metrics(engine, window_metrics);
1199 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1202 [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
1205 EXPECT_FALSE(updated);
1210 [viewController loadView];
1211 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
1213 [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
1215 EXPECT_TRUE(updated);
1219 NSString* fixtures = @(testing::GetFixturesPath());
1221 initWithAssetsPath:fixtures
1222 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
1223 project.rootIsolateCreateCallback = FlutterEngineTest::IsolateCreateCallback;
1226 allowHeadlessExecution:true];
1228 auto original_update_displays = engine.embedderAPI.NotifyDisplayUpdate;
1229 engine.embedderAPI.NotifyDisplayUpdate = MOCK_ENGINE_PROC(
1230 NotifyDisplayUpdate, ([&updated, &original_update_displays](
1231 auto engine,
auto update_type,
auto* displays,
auto display_count) {
1232 EXPECT_EQ(display_count, 1UL);
1233 EXPECT_EQ(displays->display_id, 10UL);
1234 EXPECT_EQ(displays->width, 60UL);
1235 EXPECT_EQ(displays->height, 80UL);
1236 EXPECT_EQ(displays->device_pixel_ratio, 2UL);
1238 return original_update_displays(engine, update_type, displays, display_count);
1240 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1241 EXPECT_TRUE(updated);