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];
944 NSString* fixtures = @(flutter::testing::GetFixturesPath());
946 initWithAssetsPath:fixtures
947 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
953 [viewController loadView];
954 [viewController viewDidLoad];
955 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
957 EXPECT_TRUE([engine runWithEntrypoint:
@"drawIntoAllViews"]);
958 [engine.testThreadSynchronizer blockUntilFrameAvailable];
959 EXPECT_EQ(engine.macOSCompositor->DebugNumViews(), 1u);
962 EXPECT_EQ(engine.macOSCompositor->DebugNumViews(), 0u);
970 __block NSString* nextResponse =
@"exit";
971 __block BOOL triedToTerminate = NO;
974 terminator:^(id sender) {
975 triedToTerminate = TRUE;
978 OCMStub([engineMock terminationHandler]).andReturn(terminationHandler);
981 [engineMock binaryMessenger])
982 .andReturn(binaryMessengerMock);
983 OCMStub([engineMock sendOnChannel:
@"flutter/platform"
985 binaryReply:[OCMArg any]])
986 .andDo((^(NSInvocation* invocation) {
987 [invocation retainArguments];
989 NSData* returnedMessage;
990 [invocation getArgument:&callback atIndex:4];
991 if ([nextResponse isEqualToString:
@"error"]) {
998 NSDictionary* responseDict = @{
@"response" : nextResponse};
1002 callback(returnedMessage);
1004 __block NSString* calledAfterTerminate =
@"";
1006 NSDictionary* resultDict = result;
1007 calledAfterTerminate = resultDict[@"response"];
1014 triedToTerminate = NO;
1015 calledAfterTerminate =
@"";
1016 nextResponse =
@"cancel";
1017 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1018 EXPECT_STREQ([calledAfterTerminate UTF8String],
"");
1019 EXPECT_TRUE(triedToTerminate);
1022 terminationHandler.acceptingRequests = YES;
1023 triedToTerminate = NO;
1024 calledAfterTerminate =
@"";
1025 nextResponse =
@"exit";
1026 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1027 EXPECT_STREQ([calledAfterTerminate UTF8String],
"exit");
1028 EXPECT_TRUE(triedToTerminate);
1030 triedToTerminate = NO;
1031 calledAfterTerminate =
@"";
1032 nextResponse =
@"cancel";
1033 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1034 EXPECT_STREQ([calledAfterTerminate UTF8String],
"cancel");
1035 EXPECT_FALSE(triedToTerminate);
1038 triedToTerminate = NO;
1039 calledAfterTerminate =
@"";
1040 nextResponse =
@"error";
1041 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1042 EXPECT_STREQ([calledAfterTerminate UTF8String],
"");
1043 EXPECT_TRUE(triedToTerminate);
1047 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1049 [NSApplication sharedApplication].delegate = plainDelegate;
1056 EXPECT_EQ([[[NSApplication sharedApplication] delegate] applicationShouldTerminate:NSApp],
1059 [NSApplication sharedApplication].delegate = previousDelegate;
1063 __block BOOL announced = NO;
1066 OCMStub([engineMock announceAccessibilityMessage:[OCMArg any]
1067 withPriority:NSAccessibilityPriorityMedium])
1068 .andDo((^(NSInvocation* invocation) {
1070 [invocation retainArguments];
1072 [invocation getArgument:&message atIndex:2];
1073 EXPECT_EQ(message,
@"error message");
1076 NSDictionary<NSString*, id>* annotatedEvent =
1077 @{
@"type" :
@"announce",
1078 @"data" : @{
@"message" :
@"error message"}};
1080 [engineMock handleAccessibilityEvent:annotatedEvent];
1082 EXPECT_TRUE(announced);
1092 .andDo((^(NSInvocation* invocation) {
1096 .andDo((^(NSInvocation* invocation) {
1100 .andDo((^(NSInvocation* invocation) {
1104 .andDo((^(NSInvocation* invocation) {
1108 .andDo((^(NSInvocation* invocation) {
1112 __block NSApplicationOcclusionState visibility = NSApplicationOcclusionStateVisible;
1113 id mockApplication = OCMPartialMock([NSApplication sharedApplication]);
1114 OCMStub((NSApplicationOcclusionState)[mockApplication occlusionState])
1115 .andDo(^(NSInvocation* invocation) {
1116 [invocation setReturnValue:&visibility];
1119 NSNotification* willBecomeActive =
1120 [[NSNotification alloc] initWithName:NSApplicationWillBecomeActiveNotification
1123 NSNotification* willResignActive =
1124 [[NSNotification alloc] initWithName:NSApplicationWillResignActiveNotification
1128 NSNotification* didChangeOcclusionState;
1129 didChangeOcclusionState =
1130 [[NSNotification alloc] initWithName:NSApplicationDidChangeOcclusionStateNotification
1134 [engineMock handleDidChangeOcclusionState:didChangeOcclusionState];
1137 [engineMock handleWillBecomeActive:willBecomeActive];
1140 [engineMock handleWillResignActive:willResignActive];
1144 [engineMock handleDidChangeOcclusionState:didChangeOcclusionState];
1147 [engineMock handleWillBecomeActive:willBecomeActive];
1150 [engineMock handleWillResignActive:willResignActive];
1153 [mockApplication stopMocking];
1157 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1159 [NSApplication sharedApplication].delegate = fakeAppDelegate;
1164 [[engine registrarForPlugin:@"TestPlugin"] addApplicationDelegate:plugin];
1166 EXPECT_TRUE([fakeAppDelegate hasDelegate:plugin]);
1168 [NSApplication sharedApplication].delegate = previousDelegate;
1172 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1174 [NSApplication sharedApplication].delegate = fakeAppDelegate;
1181 [[engine registrarForPlugin:@"TestPlugin"] addApplicationDelegate:plugin];
1182 EXPECT_TRUE([fakeAppDelegate hasDelegate:plugin]);
1187 EXPECT_FALSE([fakeAppDelegate hasDelegate:plugin]);
1189 [NSApplication sharedApplication].delegate = previousDelegate;
1195 auto original_update_displays = engine.embedderAPI.NotifyDisplayUpdate;
1196 engine.embedderAPI.NotifyDisplayUpdate = MOCK_ENGINE_PROC(
1197 NotifyDisplayUpdate, ([&updated, &original_update_displays](
1198 auto engine,
auto update_type,
auto* displays,
auto display_count) {
1200 return original_update_displays(engine, update_type, displays, display_count);
1203 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1204 EXPECT_TRUE(updated);
1207 [[NSNotificationCenter defaultCenter]
1208 postNotificationName:NSApplicationDidChangeScreenParametersNotification
1210 EXPECT_TRUE(updated);
1216 auto original_set_viewport_metrics = engine.embedderAPI.SendWindowMetricsEvent;
1217 engine.embedderAPI.SendWindowMetricsEvent = MOCK_ENGINE_PROC(
1218 SendWindowMetricsEvent,
1219 ([&updated, &original_set_viewport_metrics](
auto engine,
auto* window_metrics) {
1221 return original_set_viewport_metrics(engine, window_metrics);
1224 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1227 [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
1230 EXPECT_FALSE(updated);
1235 [viewController loadView];
1236 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
1238 [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
1240 EXPECT_TRUE(updated);
1244 NSString* fixtures = @(testing::GetFixturesPath());
1246 initWithAssetsPath:fixtures
1247 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
1248 project.rootIsolateCreateCallback = FlutterEngineTest::IsolateCreateCallback;
1251 allowHeadlessExecution:true];
1253 auto original_update_displays = engine.embedderAPI.NotifyDisplayUpdate;
1254 engine.embedderAPI.NotifyDisplayUpdate = MOCK_ENGINE_PROC(
1255 NotifyDisplayUpdate, ([&updated, &original_update_displays](
1256 auto engine,
auto update_type,
auto* displays,
auto display_count) {
1257 EXPECT_EQ(display_count, 1UL);
1258 EXPECT_EQ(displays->display_id, 10UL);
1259 EXPECT_EQ(displays->width, 60UL);
1260 EXPECT_EQ(displays->height, 80UL);
1261 EXPECT_EQ(displays->device_pixel_ratio, 2UL);
1263 return original_update_displays(engine, update_type, displays, display_count);
1265 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1266 EXPECT_TRUE(updated);
1272 __block BOOL expectedValue;
1276 OCMStub([channelMock messageChannelWithName:
@"flutter/settings"
1277 binaryMessenger:[OCMArg any]
1278 codec:[OCMArg any]])
1279 .andReturn(channelMock);
1280 OCMStub([channelMock sendMessage:[OCMArg any]]).andDo((^(NSInvocation* invocation) {
1282 [invocation getArgument:&message atIndex:2];
1283 EXPECT_EQ(message[
@"alwaysUse24HourFormat"], @(expectedValue));
1287 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andDo((^(NSInvocation* invocation) {
1288 [invocation setReturnValue:&expectedValue];
1294 expectedValue = YES;
1295 EXPECT_TRUE([engineMock runWithEntrypoint:
@"main"]);
1296 [engineMock shutDownEngine];
1300 EXPECT_TRUE([engineMock runWithEntrypoint:
@"main"]);
1301 [engineMock shutDownEngine];
1304 [mockHourFormat stopMocking];
1305 [engineMock stopMocking];
1306 [channelMock stopMocking];