15 #include "flutter/fml/synchronization/waitable_event.h"
16 #include "flutter/lib/ui/window/platform_message.h"
20 #import "flutter/shell/platform/darwin/common/test_utils_swift/test_utils_swift.h"
21 #import "flutter/shell/platform/darwin/macos/InternalFlutterSwift/InternalFlutterSwift.h"
29 #include "flutter/shell/platform/embedder/embedder.h"
30 #include "flutter/shell/platform/embedder/embedder_engine.h"
31 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
32 #include "flutter/testing/stream_capture.h"
33 #include "flutter/testing/test_dart_native_resolver.h"
34 #include "gtest/gtest.h"
54 arguments:(nullable id)args {
55 return viewIdentifier == 42 ? [[NSView alloc] init] : nil;
64 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication* _Nonnull)sender {
66 return NSTerminateCancel;
74 @property(nonatomic, strong, readonly) NSPointerArray* registeredDelegates;
77 - (BOOL)hasDelegate:(nonnull NSObject<FlutterAppLifecycleDelegate>*)delegate;
90 std::vector<void*> _delegates;
93 - (void)addApplicationLifecycleDelegate:(nonnull NSObject<FlutterAppLifecycleDelegate>*)delegate {
94 _delegates.push_back((__bridge
void*)delegate);
97 - (void)removeApplicationLifecycleDelegate:
98 (nonnull NSObject<FlutterAppLifecycleDelegate>*)delegate {
99 auto delegateIndex = std::find(_delegates.begin(), _delegates.end(), (__bridge
void*)delegate);
100 NSAssert(delegateIndex != _delegates.end(),
101 @"Attempting to unregister a delegate that was not registered.");
102 _delegates.erase(delegateIndex);
105 - (BOOL)hasDelegate:(nonnull NSObject<FlutterAppLifecycleDelegate>*)delegate {
106 return std::find(_delegates.begin(), _delegates.end(), (__bridge
void*)delegate) !=
118 + (void)registerWithRegistrar:(id<FlutterPluginRegistrar>)registrar {
128 - (NSArray<NSScreen*>*)screens {
129 id mockScreen = OCMClassMock([NSScreen
class]);
130 OCMStub([mockScreen backingScaleFactor]).andReturn(2.0);
131 OCMStub([mockScreen deviceDescription]).andReturn(@{
132 @"NSScreenNumber" : [NSNumber numberWithInt:10]
134 OCMStub([mockScreen frame]).andReturn(NSMakeRect(10, 20, 30, 40));
135 return [NSArray arrayWithObject:mockScreen];
145 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
146 ASSERT_TRUE(engine.running);
151 std::string executable_name = [[engine executableName] UTF8String];
152 ASSERT_FALSE(executable_name.empty());
156 AddNativeCallback(
"NotifyStringValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
157 const auto dart_string = tonic::DartConverter<std::string>::FromDart(
158 Dart_GetNativeArgument(args, 0));
159 EXPECT_EQ(executable_name, dart_string);
164 EXPECT_TRUE([engine runWithEntrypoint:
@"executableNameNotNull"]);
167 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
171 #ifndef FLUTTER_RELEASE
173 setenv(
"FLUTTER_ENGINE_SWITCHES",
"2", 1);
174 setenv(
"FLUTTER_ENGINE_SWITCH_1",
"abc", 1);
175 setenv(
"FLUTTER_ENGINE_SWITCH_2",
"foo=\"bar, baz\"", 1);
178 std::vector<std::string> switches = engine.switches;
179 ASSERT_EQ(switches.size(), 2UL);
180 EXPECT_EQ(switches[0],
"--abc");
181 EXPECT_EQ(switches[1],
"--foo=\"bar, baz\"");
183 unsetenv(
"FLUTTER_ENGINE_SWITCHES");
184 unsetenv(
"FLUTTER_ENGINE_SWITCH_1");
185 unsetenv(
"FLUTTER_ENGINE_SWITCH_2");
190 NSString* fixtures = @(flutter::testing::GetFixturesPath());
192 initWithAssetsPath:fixtures
193 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
194 EXPECT_TRUE([project enableSDFs]);
199 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
201 NSData* test_message = [@"a message" dataUsingEncoding:NSUTF8StringEncoding];
204 engine.embedderAPI.SendPlatformMessage = MOCK_ENGINE_PROC(
205 SendPlatformMessage, ([&called, test_message](
auto engine,
auto message) {
207 EXPECT_STREQ(message->channel,
"test");
208 EXPECT_EQ(memcmp(message->message, test_message.bytes, message->message_size), 0);
212 [engine.binaryMessenger sendOnChannel:@"test" message:test_message];
219 AddNativeCallback(
"SignalNativeTest",
220 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { signaled = YES; }));
223 FlutterStringOutputWriter* writer = [[FlutterStringOutputWriter alloc] init];
224 writer.expectedOutput =
@"Hello logging";
225 FlutterLogger.outputWriter = writer;
229 EXPECT_TRUE([engine runWithEntrypoint:
@"canLogToStdout"]);
230 ASSERT_TRUE(engine.running);
233 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
237 EXPECT_TRUE(writer.gotExpectedOutput);
245 AddNativeCallback(
"SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
247 EXPECT_TRUE(rootLayer.backgroundColor != nil);
248 if (rootLayer.backgroundColor != nil) {
249 NSColor* actualBackgroundColor =
250 [NSColor colorWithCGColor:rootLayer.backgroundColor];
251 EXPECT_EQ(actualBackgroundColor, [NSColor blackColor]);
257 EXPECT_TRUE([engine runWithEntrypoint:
@"backgroundTest"]);
258 ASSERT_TRUE(engine.running);
263 [viewController loadView];
264 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
267 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
276 AddNativeCallback(
"SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
278 EXPECT_TRUE(rootLayer.backgroundColor != nil);
279 if (rootLayer.backgroundColor != nil) {
280 NSColor* actualBackgroundColor =
281 [NSColor colorWithCGColor:rootLayer.backgroundColor];
282 EXPECT_EQ(actualBackgroundColor, [NSColor whiteColor]);
288 EXPECT_TRUE([engine runWithEntrypoint:
@"backgroundTest"]);
289 ASSERT_TRUE(engine.running);
294 [viewController loadView];
295 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
299 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
306 auto original_init = engine.embedderAPI.Initialize;
307 std::function<void(
const FlutterSemanticsUpdate2*,
void*)> update_semantics_callback;
308 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
309 Initialize, ([&update_semantics_callback, &original_init](
310 size_t version,
const FlutterRendererConfig* config,
311 const FlutterProjectArgs* args,
void*
user_data,
auto engine_out) {
312 update_semantics_callback = args->update_semantics_callback2;
313 return original_init(version, config, args,
user_data, engine_out);
315 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
320 [viewController loadView];
322 bool enabled_called =
false;
323 engine.embedderAPI.UpdateSemanticsEnabled =
324 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&enabled_called](
auto engine,
bool enabled) {
325 enabled_called = enabled;
328 engine.semanticsEnabled = YES;
329 EXPECT_TRUE(enabled_called);
331 FlutterSemanticsNode2 root;
332 FlutterSemanticsFlags flags = FlutterSemanticsFlags{0};
333 FlutterSemanticsFlags child_flags = FlutterSemanticsFlags{0};
335 root.flags2 = &flags;
337 root.actions =
static_cast<FlutterSemanticsAction
>(0);
338 root.text_selection_base = -1;
339 root.text_selection_extent = -1;
343 root.increased_value =
"";
344 root.decreased_value =
"";
346 root.child_count = 1;
347 int32_t children[] = {1};
348 root.children_in_traversal_order = children;
349 root.custom_accessibility_actions_count = 0;
350 root.identifier =
"";
352 FlutterSemanticsNode2 child1;
354 child1.flags2 = &child_flags;
356 child1.actions =
static_cast<FlutterSemanticsAction
>(0);
357 child1.text_selection_base = -1;
358 child1.text_selection_extent = -1;
359 child1.label =
"child 1";
362 child1.increased_value =
"";
363 child1.decreased_value =
"";
365 child1.child_count = 0;
366 child1.custom_accessibility_actions_count = 0;
367 child1.identifier =
"";
369 FlutterSemanticsUpdate2 update;
370 update.node_count = 2;
371 FlutterSemanticsNode2* nodes[] = {&root, &child1};
372 update.nodes = nodes;
373 update.custom_action_count = 0;
374 update_semantics_callback(&update, (__bridge
void*)engine);
377 EXPECT_EQ([engine.
viewController.flutterView.accessibilityChildren count], 1u);
378 NSAccessibilityElement* native_root = engine.
viewController.flutterView.accessibilityChildren[0];
379 std::string root_label = [native_root.accessibilityLabel UTF8String];
380 EXPECT_TRUE(root_label ==
"root");
381 EXPECT_EQ(native_root.accessibilityRole, NSAccessibilityGroupRole);
382 EXPECT_EQ([native_root.accessibilityChildren count], 1u);
383 NSAccessibilityElement* native_child1 = native_root.accessibilityChildren[0];
384 std::string child1_value = [native_child1.accessibilityValue UTF8String];
385 EXPECT_TRUE(child1_value ==
"child 1");
386 EXPECT_EQ(native_child1.accessibilityRole, NSAccessibilityStaticTextRole);
387 EXPECT_EQ([native_child1.accessibilityChildren count], 0u);
389 bool semanticsEnabled =
true;
390 engine.embedderAPI.UpdateSemanticsEnabled =
391 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&semanticsEnabled](
auto engine,
bool enabled) {
392 semanticsEnabled = enabled;
395 engine.semanticsEnabled = NO;
396 EXPECT_FALSE(semanticsEnabled);
398 EXPECT_EQ([engine.
viewController.flutterView.accessibilityChildren count], 0u);
400 [engine setViewController:nil];
406 auto original_init = engine.embedderAPI.Initialize;
407 std::function<void(
const FlutterSemanticsUpdate2*,
void*)> update_semantics_callback;
408 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
409 Initialize, ([&update_semantics_callback, &original_init](
410 size_t version,
const FlutterRendererConfig* config,
411 const FlutterProjectArgs* args,
void*
user_data,
auto engine_out) {
412 update_semantics_callback = args->update_semantics_callback2;
413 return original_init(version, config, args,
user_data, engine_out);
415 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
418 bool enabled_called =
false;
419 engine.embedderAPI.UpdateSemanticsEnabled =
420 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&enabled_called](
auto engine,
bool enabled) {
421 enabled_called = enabled;
424 engine.semanticsEnabled = YES;
425 EXPECT_TRUE(enabled_called);
427 FlutterSemanticsNode2 root;
428 FlutterSemanticsFlags flags = FlutterSemanticsFlags{0};
429 FlutterSemanticsFlags child_flags = FlutterSemanticsFlags{0};
431 root.flags2 = &flags;
433 root.actions =
static_cast<FlutterSemanticsAction
>(0);
434 root.text_selection_base = -1;
435 root.text_selection_extent = -1;
439 root.increased_value =
"";
440 root.decreased_value =
"";
442 root.child_count = 1;
443 int32_t children[] = {1};
444 root.children_in_traversal_order = children;
445 root.custom_accessibility_actions_count = 0;
447 FlutterSemanticsNode2 child1;
449 child1.flags2 = &child_flags;
451 child1.actions =
static_cast<FlutterSemanticsAction
>(0);
452 child1.text_selection_base = -1;
453 child1.text_selection_extent = -1;
454 child1.label =
"child 1";
457 child1.increased_value =
"";
458 child1.decreased_value =
"";
460 child1.child_count = 0;
461 child1.custom_accessibility_actions_count = 0;
463 FlutterSemanticsUpdate2 update;
464 update.node_count = 2;
465 FlutterSemanticsNode2* nodes[] = {&root, &child1};
466 update.nodes = nodes;
467 update.custom_action_count = 0;
470 update_semantics_callback(&update, (__bridge
void*)engine);
476 bool semanticsEnabled =
true;
477 engine.embedderAPI.UpdateSemanticsEnabled =
478 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&semanticsEnabled](
auto engine,
bool enabled) {
479 semanticsEnabled = enabled;
482 engine.semanticsEnabled = NO;
483 EXPECT_FALSE(semanticsEnabled);
490 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
493 bool enabled_called =
false;
494 engine.embedderAPI.UpdateSemanticsEnabled =
495 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&enabled_called](
auto engine,
bool enabled) {
496 enabled_called = enabled;
499 engine.semanticsEnabled = YES;
500 EXPECT_TRUE(enabled_called);
510 EXPECT_NE(viewController.accessibilityBridge.lock(),
nullptr);
514 BOOL latch_called = NO;
515 AddNativeCallback(
"SignalNativeTest",
516 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch_called = YES; }));
519 EXPECT_TRUE([engine runWithEntrypoint:
@"nativeCallback"]);
520 ASSERT_TRUE(engine.running);
522 while (!latch_called) {
523 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
525 ASSERT_TRUE(latch_called);
529 NSString* fixtures = @(flutter::testing::GetFixturesPath());
531 initWithAssetsPath:fixtures
532 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
538 [viewController loadView];
539 [viewController viewDidLoad];
540 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
542 EXPECT_TRUE([engine runWithEntrypoint:
@"canCompositePlatformViews"]);
545 withId:@"factory_id"];
546 [engine.platformViewController
550 @"viewType" : @"factory_id",
556 CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
557 CALayer* rootLayer = viewController.flutterView.layer;
558 while (rootLayer.sublayers.count == 0) {
559 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
560 if (CFAbsoluteTimeGetCurrent() - start > 1) {
566 EXPECT_EQ(rootLayer.sublayers.count, 2u);
567 EXPECT_EQ(viewController.flutterView.subviews.count, 1u);
576 auto original_init = engine.embedderAPI.Initialize;
578 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
579 Initialize, ([&compositor, &original_init](
580 size_t version,
const FlutterRendererConfig* config,
581 const FlutterProjectArgs* args,
void*
user_data,
auto engine_out) {
582 compositor = *args->compositor;
583 return original_init(version, config, args,
user_data, engine_out);
589 [viewController loadView];
591 EXPECT_TRUE([engine runWithEntrypoint:
@"empty"]);
593 FlutterBackingStoreConfig config = {
594 .struct_size =
sizeof(FlutterBackingStoreConfig),
595 .size = FlutterSize{10, 10},
597 FlutterBackingStore backing_store = {};
598 EXPECT_NE(compositor.create_backing_store_callback,
nullptr);
600 compositor.create_backing_store_callback(&config, &backing_store, compositor.user_data));
603 .type = kFlutterLayerContentTypeBackingStore,
604 .backing_store = &backing_store,
606 std::vector<FlutterLayer*> layers = {&layer};
608 FlutterPresentViewInfo info = {
609 .struct_size =
sizeof(FlutterPresentViewInfo),
611 .layers =
const_cast<const FlutterLayer**
>(layers.data()),
615 EXPECT_NE(compositor.present_view_callback,
nullptr);
616 EXPECT_FALSE(compositor.present_view_callback(&info));
617 EXPECT_TRUE(compositor.collect_backing_store_callback(&backing_store, compositor.user_data));
619 (void)viewController;
624 NSString* fixtures = @(flutter::testing::GetFixturesPath());
626 initWithAssetsPath:fixtures
627 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
629 project.dartEntrypointArguments = @[ @"arg1", @"arg2" ];
633 auto original_init = engine.embedderAPI.Initialize;
634 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
635 Initialize, ([&called, &original_init](
size_t version,
const FlutterRendererConfig* config,
636 const FlutterProjectArgs* args,
void*
user_data,
639 EXPECT_EQ(args->dart_entrypoint_argc, 2);
640 NSString* arg1 = [[NSString alloc] initWithCString:args->dart_entrypoint_argv[0]
641 encoding:NSUTF8StringEncoding];
642 NSString* arg2 = [[NSString alloc] initWithCString:args->dart_entrypoint_argv[1]
643 encoding:NSUTF8StringEncoding];
645 EXPECT_TRUE([arg1 isEqualToString:
@"arg1"]);
646 EXPECT_TRUE([arg2 isEqualToString:
@"arg2"]);
648 return original_init(version, config, args,
user_data, engine_out);
651 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
664 id<FlutterBinaryMessenger> binaryMessenger = nil;
667 NSString* fixtures = @(flutter::testing::GetFixturesPath());
669 initWithAssetsPath:fixtures
670 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
673 allowHeadlessExecution:YES];
680 EXPECT_NE(binaryMessenger, nil);
681 EXPECT_EQ(weakEngine, nil);
688 id<FlutterTextureRegistry> textureRegistry;
691 NSString* fixtures = @(flutter::testing::GetFixturesPath());
693 initWithAssetsPath:fixtures
694 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
697 allowHeadlessExecution:YES];
698 id<FlutterPluginRegistrar> registrar = [engine registrarForPlugin:@"MyPlugin"];
699 textureRegistry = registrar.textures;
704 EXPECT_NE(textureRegistry, nil);
705 EXPECT_EQ(weakEngine, nil);
709 NSString* fixtures = @(flutter::testing::GetFixturesPath());
711 initWithAssetsPath:fixtures
712 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
715 allowHeadlessExecution:YES];
717 EXPECT_EQ([engine valuePublishedByPlugin:
@"NoSuchPlugin"], nil);
721 NSString* fixtures = @(flutter::testing::GetFixturesPath());
723 initWithAssetsPath:fixtures
724 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
727 allowHeadlessExecution:YES];
728 NSString* pluginName =
@"MyPlugin";
730 [engine registrarForPlugin:pluginName];
734 EXPECT_EQ([engine valuePublishedByPlugin:pluginName], [NSNull
null]);
738 NSString* fixtures = @(flutter::testing::GetFixturesPath());
740 initWithAssetsPath:fixtures
741 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
744 allowHeadlessExecution:YES];
745 NSString* pluginName =
@"MyPlugin";
746 id<FlutterPluginRegistrar> registrar = [engine registrarForPlugin:pluginName];
748 NSString* firstValue =
@"A published value";
749 NSArray* secondValue = @[ @"A different published value" ];
751 [registrar publish:firstValue];
752 EXPECT_EQ([engine valuePublishedByPlugin:pluginName], firstValue);
754 [registrar publish:secondValue];
755 EXPECT_EQ([engine valuePublishedByPlugin:pluginName], secondValue);
759 NSString* fixtures = @(flutter::testing::GetFixturesPath());
761 initWithAssetsPath:fixtures
762 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
768 id<FlutterPluginRegistrar> registrar = [engine registrarForPlugin:@"MyPlugin"];
770 EXPECT_EQ([registrar viewController], viewController);
781 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
783 NSString* channel =
@"_test_";
784 NSData* channel_data = [channel dataUsingEncoding:NSUTF8StringEncoding];
789 engine.embedderAPI.SendPlatformMessage = MOCK_ENGINE_PROC(
790 SendPlatformMessage, ([](
auto engine_,
auto message_) {
791 if (strcmp(message_->channel,
"test/send_message") == 0) {
793 std::string message = R
"|({"method": "a"})|";
794 std::string channel(reinterpret_cast<const char*>(message_->message),
795 message_->message_size);
796 reinterpret_cast<EmbedderEngine*>(engine_)
799 ->HandlePlatformMessage(std::make_unique<PlatformMessage>(
800 channel.c_str(), fml::MallocMapping::Copy(message.c_str(), message.length()),
801 fml::RefPtr<PlatformMessageResponse>()));
806 __block
int record = 0;
816 [engine.binaryMessenger sendOnChannel:@"test/send_message" message:channel_data];
817 EXPECT_EQ(record, 1);
827 [engine.binaryMessenger sendOnChannel:@"test/send_message" message:channel_data];
828 EXPECT_EQ(record, 11);
832 [engine.binaryMessenger sendOnChannel:@"test/send_message" message:channel_data];
833 EXPECT_EQ(record, 21);
840 __block
bool calledAfterClear =
false;
841 __block
bool valueAfterClear;
843 calledAfterClear =
true;
844 NSNumber* valueNumber = [result valueForKey:@"value"];
845 valueAfterClear = [valueNumber boolValue];
849 [engineMock handleMethodCall:methodCallAfterClear result:resultAfterClear];
850 EXPECT_TRUE(calledAfterClear);
851 EXPECT_FALSE(valueAfterClear);
858 __block
bool called =
false;
862 NSNumber* valueNumber = [result valueForKey:@"value"];
863 value = [valueNumber boolValue];
867 [engineMock handleMethodCall:methodCall result:result];
876 binaryMessenger:engine.binaryMessenger
878 __block BOOL didCallCallback = NO;
882 didCallCallback = YES;
884 EXPECT_TRUE([engine runWithEntrypoint:
@"sendFooMessage"]);
887 while (!didCallCallback) {
888 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
896 binaryMessenger:engine.binaryMessenger
898 __block BOOL didCallCallback = NO;
900 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
902 dispatch_async(dispatch_get_main_queue(), ^{
903 didCallCallback = YES;
907 EXPECT_TRUE([engine runWithEntrypoint:
@"sendFooMessage"]);
909 while (!didCallCallback) {
910 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
918 std::optional<int64_t> engineId;
919 AddNativeCallback(
"NotifyEngineId", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
920 const auto argument = Dart_GetNativeArgument(args, 0);
921 if (!Dart_IsNull(argument)) {
922 const auto id = tonic::DartConverter<int64_t>::FromDart(argument);
928 EXPECT_TRUE([engine runWithEntrypoint:
@"testEngineId"]);
930 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
933 EXPECT_TRUE(engineId.has_value());
934 if (!engineId.has_value()) {
937 EXPECT_EQ(engine, [
FlutterEngine engineForIdentifier:*engineId]);
942 FlutterResizeSynchronizer* threadSynchronizer = [[FlutterResizeSynchronizer alloc] init];
943 [threadSynchronizer shutDown];
945 std::thread rasterThread([&threadSynchronizer] {
946 [threadSynchronizer performCommitForSize:CGSizeMake(100, 100)
956 NSString* fixtures = @(flutter::testing::GetFixturesPath());
958 initWithAssetsPath:fixtures
959 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
969 engine = viewController1.
engine;
993 allowHeadlessExecution:NO];
1019 NSString* fixtures = @(flutter::testing::GetFixturesPath());
1021 initWithAssetsPath:fixtures
1022 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
1028 [viewController loadView];
1029 [viewController viewDidLoad];
1030 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
1032 EXPECT_TRUE([engine runWithEntrypoint:
@"drawIntoAllViews"]);
1034 CFTimeInterval start = CACurrentMediaTime();
1035 while (engine.macOSCompositor->DebugNumViews() == 0) {
1036 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
1037 if (CACurrentMediaTime() - start > 1) {
1042 EXPECT_EQ(engine.macOSCompositor->DebugNumViews(), 1u);
1045 EXPECT_EQ(engine.macOSCompositor->DebugNumViews(), 0u);
1053 __block NSString* nextResponse =
@"exit";
1054 __block BOOL triedToTerminate = NO;
1057 terminator:^(id sender) {
1058 triedToTerminate = TRUE;
1061 OCMStub([engineMock terminationHandler]).andReturn(terminationHandler);
1064 [engineMock binaryMessenger])
1065 .andReturn(binaryMessengerMock);
1066 OCMStub([engineMock sendOnChannel:
@"flutter/platform"
1067 message:[OCMArg any]
1068 binaryReply:[OCMArg any]])
1069 .andDo((^(NSInvocation* invocation) {
1070 [invocation retainArguments];
1072 NSData* returnedMessage;
1073 [invocation getArgument:&callback atIndex:4];
1074 if ([nextResponse isEqualToString:
@"error"]) {
1081 NSDictionary* responseDict = @{
@"response" : nextResponse};
1085 callback(returnedMessage);
1087 __block NSString* calledAfterTerminate =
@"";
1089 NSDictionary* resultDict = result;
1090 calledAfterTerminate = resultDict[@"response"];
1097 triedToTerminate = NO;
1098 calledAfterTerminate =
@"";
1099 nextResponse =
@"cancel";
1100 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1101 EXPECT_STREQ([calledAfterTerminate UTF8String],
"");
1102 EXPECT_TRUE(triedToTerminate);
1106 triedToTerminate = NO;
1107 calledAfterTerminate =
@"";
1108 nextResponse =
@"exit";
1109 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1110 EXPECT_STREQ([calledAfterTerminate UTF8String],
"exit");
1111 EXPECT_TRUE(triedToTerminate);
1113 triedToTerminate = NO;
1114 calledAfterTerminate =
@"";
1115 nextResponse =
@"cancel";
1116 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1117 EXPECT_STREQ([calledAfterTerminate UTF8String],
"cancel");
1118 EXPECT_FALSE(triedToTerminate);
1121 triedToTerminate = NO;
1122 calledAfterTerminate =
@"";
1123 nextResponse =
@"error";
1124 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1125 EXPECT_STREQ([calledAfterTerminate UTF8String],
"");
1126 EXPECT_TRUE(triedToTerminate);
1130 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1132 [NSApplication sharedApplication].delegate = plainDelegate;
1139 EXPECT_EQ([[[NSApplication sharedApplication] delegate] applicationShouldTerminate:NSApp],
1142 [NSApplication sharedApplication].delegate = previousDelegate;
1146 __block BOOL announced = NO;
1149 OCMStub([engineMock announceAccessibilityMessage:[OCMArg any]
1150 withPriority:NSAccessibilityPriorityMedium])
1151 .andDo((^(NSInvocation* invocation) {
1153 [invocation retainArguments];
1155 [invocation getArgument:&message atIndex:2];
1156 EXPECT_EQ(message,
@"error message");
1159 NSDictionary<NSString*, id>* annotatedEvent =
1160 @{
@"type" :
@"announce",
1161 @"data" : @{
@"message" :
@"error message"}};
1163 [engineMock handleAccessibilityEvent:annotatedEvent];
1165 EXPECT_TRUE(announced);
1175 .andDo((^(NSInvocation* invocation) {
1179 .andDo((^(NSInvocation* invocation) {
1183 .andDo((^(NSInvocation* invocation) {
1187 .andDo((^(NSInvocation* invocation) {
1191 .andDo((^(NSInvocation* invocation) {
1195 __block NSApplicationOcclusionState visibility = NSApplicationOcclusionStateVisible;
1196 id mockApplication = OCMPartialMock([NSApplication sharedApplication]);
1197 OCMStub((NSApplicationOcclusionState)[mockApplication occlusionState])
1198 .andDo(^(NSInvocation* invocation) {
1199 [invocation setReturnValue:&visibility];
1205 __block BOOL windowVisible = YES;
1206 id mockWindow = OCMClassMock([NSWindow
class]);
1207 OCMStub([mockWindow isVisible]).andDo(^(NSInvocation* invocation) {
1208 [invocation setReturnValue:&windowVisible];
1210 OCMStub([mockApplication windows]).andReturn(@[ mockWindow ]);
1212 NSNotification* willBecomeActive =
1213 [[NSNotification alloc] initWithName:NSApplicationWillBecomeActiveNotification
1216 NSNotification* willResignActive =
1217 [[NSNotification alloc] initWithName:NSApplicationWillResignActiveNotification
1221 NSNotification* didChangeOcclusionState;
1222 didChangeOcclusionState =
1223 [[NSNotification alloc] initWithName:NSApplicationDidChangeOcclusionStateNotification
1227 [engineMock handleDidChangeOcclusionState:didChangeOcclusionState];
1230 [engineMock handleWillBecomeActive:willBecomeActive];
1233 [engineMock handleWillResignActive:willResignActive];
1237 [engineMock handleDidChangeOcclusionState:didChangeOcclusionState];
1242 [engineMock handleWillBecomeActive:willBecomeActive];
1247 [engineMock handleWillResignActive:willResignActive];
1253 [engineMock handleDidChangeOcclusionState:didChangeOcclusionState];
1260 [engineMock handleWillBecomeActive:willBecomeActive];
1263 [mockWindow stopMocking];
1264 [mockApplication stopMocking];
1268 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1270 [NSApplication sharedApplication].delegate = fakeAppDelegate;
1275 [[engine registrarForPlugin:@"TestPlugin"] addApplicationDelegate:plugin];
1277 EXPECT_TRUE([fakeAppDelegate hasDelegate:plugin]);
1279 [NSApplication sharedApplication].delegate = previousDelegate;
1283 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1285 [NSApplication sharedApplication].delegate = fakeAppDelegate;
1292 [[engine registrarForPlugin:@"TestPlugin"] addApplicationDelegate:plugin];
1293 EXPECT_TRUE([fakeAppDelegate hasDelegate:plugin]);
1298 EXPECT_FALSE([fakeAppDelegate hasDelegate:plugin]);
1300 [NSApplication sharedApplication].delegate = previousDelegate;
1306 auto original_update_displays = engine.embedderAPI.NotifyDisplayUpdate;
1307 engine.embedderAPI.NotifyDisplayUpdate = MOCK_ENGINE_PROC(
1308 NotifyDisplayUpdate, ([&updated, &original_update_displays](
1309 auto engine,
auto update_type,
auto* displays,
auto display_count) {
1311 return original_update_displays(engine, update_type, displays, display_count);
1314 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1315 EXPECT_TRUE(updated);
1318 [[NSNotificationCenter defaultCenter]
1319 postNotificationName:NSApplicationDidChangeScreenParametersNotification
1321 EXPECT_TRUE(updated);
1327 auto original_set_viewport_metrics = engine.embedderAPI.SendWindowMetricsEvent;
1328 engine.embedderAPI.SendWindowMetricsEvent = MOCK_ENGINE_PROC(
1329 SendWindowMetricsEvent,
1330 ([&updated, &original_set_viewport_metrics](
auto engine,
auto* window_metrics) {
1332 return original_set_viewport_metrics(engine, window_metrics);
1335 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1338 [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
1341 EXPECT_FALSE(updated);
1346 [viewController loadView];
1347 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
1349 [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
1351 EXPECT_TRUE(updated);
1355 NSString* fixtures = @(testing::GetFixturesPath());
1357 initWithAssetsPath:fixtures
1358 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
1359 project.rootIsolateCreateCallback = FlutterEngineTest::IsolateCreateCallback;
1362 allowHeadlessExecution:true];
1364 auto original_update_displays = engine.embedderAPI.NotifyDisplayUpdate;
1365 engine.embedderAPI.NotifyDisplayUpdate = MOCK_ENGINE_PROC(
1366 NotifyDisplayUpdate, ([&updated, &original_update_displays](
1367 auto engine,
auto update_type,
auto* displays,
auto display_count) {
1368 EXPECT_EQ(display_count, 1UL);
1369 EXPECT_EQ(displays->display_id, 10UL);
1370 EXPECT_EQ(displays->width, 60UL);
1371 EXPECT_EQ(displays->height, 80UL);
1372 EXPECT_EQ(displays->device_pixel_ratio, 2UL);
1374 return original_update_displays(engine, update_type, displays, display_count);
1376 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1377 EXPECT_TRUE(updated);
1383 __block BOOL expectedValue;
1387 OCMStub([channelMock messageChannelWithName:
@"flutter/settings"
1388 binaryMessenger:[OCMArg any]
1389 codec:[OCMArg any]])
1390 .andReturn(channelMock);
1391 OCMStub([channelMock sendMessage:[OCMArg any]]).andDo((^(NSInvocation* invocation) {
1393 [invocation getArgument:&message atIndex:2];
1394 EXPECT_EQ(message[
@"alwaysUse24HourFormat"], @(expectedValue));
1398 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andDo((^(NSInvocation* invocation) {
1399 [invocation setReturnValue:&expectedValue];
1405 expectedValue = YES;
1406 EXPECT_TRUE([engineMock runWithEntrypoint:
@"main"]);
1407 [engineMock shutDownEngine];
1411 EXPECT_TRUE([engineMock runWithEntrypoint:
@"main"]);
1412 [engineMock shutDownEngine];
1415 [mockHourFormat stopMocking];
1416 [engineMock stopMocking];
1417 [channelMock stopMocking];
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
void(^ FlutterResult)(id _Nullable result)
int64_t FlutterViewIdentifier
flutter::FlutterCompositor * macOSCompositor
void setMessageHandler:(FlutterMessageHandler _Nullable handler)
id< FlutterBinaryMessenger > binaryMessenger
FlutterViewController * viewController
instancetype errorWithCode:message:details:(NSString *code,[message] NSString *_Nullable message,[details] id _Nullable details)
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
void setMethodCallHandler:(FlutterMethodCallHandler _Nullable handler)
instancetype methodChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMethodCodec > *codec)
NSColor * backgroundColor
FlutterViewIdentifier viewIdentifier
id CreateMockFlutterEngine(NSString *pasteboardString)
TEST_F(FlutterEngineTest, ReportsHourFormat)
instancetype sharedInstance()