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"
28 #include "flutter/shell/platform/embedder/embedder.h"
29 #include "flutter/shell/platform/embedder/embedder_engine.h"
30 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
31 #include "flutter/testing/stream_capture.h"
32 #include "flutter/testing/test_dart_native_resolver.h"
33 #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);
104 - (BOOL)hasDelegate:(nonnull NSObject<FlutterAppLifecycleDelegate>*)delegate {
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());
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"]);
166 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
170 #ifndef FLUTTER_RELEASE
172 setenv(
"FLUTTER_ENGINE_SWITCHES",
"2", 1);
173 setenv(
"FLUTTER_ENGINE_SWITCH_1",
"abc", 1);
174 setenv(
"FLUTTER_ENGINE_SWITCH_2",
"foo=\"bar, baz\"", 1);
177 std::vector<std::string> switches = engine.switches;
178 ASSERT_EQ(switches.size(), 2UL);
179 EXPECT_EQ(switches[0],
"--abc");
180 EXPECT_EQ(switches[1],
"--foo=\"bar, baz\"");
182 unsetenv(
"FLUTTER_ENGINE_SWITCHES");
183 unsetenv(
"FLUTTER_ENGINE_SWITCH_1");
184 unsetenv(
"FLUTTER_ENGINE_SWITCH_2");
190 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
192 NSData* test_message = [@"a message" dataUsingEncoding:NSUTF8StringEncoding];
195 engine.embedderAPI.SendPlatformMessage = MOCK_ENGINE_PROC(
196 SendPlatformMessage, ([&called, test_message](
auto engine,
auto message) {
198 EXPECT_STREQ(message->channel,
"test");
199 EXPECT_EQ(memcmp(message->message, test_message.bytes, message->message_size), 0);
203 [engine.binaryMessenger sendOnChannel:@"test" message:test_message];
210 AddNativeCallback(
"SignalNativeTest",
211 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { signaled = YES; }));
214 FlutterStringOutputWriter* writer = [[FlutterStringOutputWriter alloc] init];
215 writer.expectedOutput =
@"Hello logging";
216 FlutterLogger.outputWriter = writer;
220 EXPECT_TRUE([engine runWithEntrypoint:
@"canLogToStdout"]);
221 ASSERT_TRUE(engine.running);
224 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
228 EXPECT_TRUE(writer.gotExpectedOutput);
236 AddNativeCallback(
"SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
238 EXPECT_TRUE(rootLayer.backgroundColor != nil);
239 if (rootLayer.backgroundColor != nil) {
240 NSColor* actualBackgroundColor =
241 [NSColor colorWithCGColor:rootLayer.backgroundColor];
242 EXPECT_EQ(actualBackgroundColor, [NSColor blackColor]);
248 EXPECT_TRUE([engine runWithEntrypoint:
@"backgroundTest"]);
249 ASSERT_TRUE(engine.running);
254 [viewController loadView];
255 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
258 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
267 AddNativeCallback(
"SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
269 EXPECT_TRUE(rootLayer.backgroundColor != nil);
270 if (rootLayer.backgroundColor != nil) {
271 NSColor* actualBackgroundColor =
272 [NSColor colorWithCGColor:rootLayer.backgroundColor];
273 EXPECT_EQ(actualBackgroundColor, [NSColor whiteColor]);
279 EXPECT_TRUE([engine runWithEntrypoint:
@"backgroundTest"]);
280 ASSERT_TRUE(engine.running);
285 [viewController loadView];
286 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
290 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
297 auto original_init = engine.embedderAPI.Initialize;
298 std::function<void(
const FlutterSemanticsUpdate2*,
void*)> update_semantics_callback;
299 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
300 Initialize, ([&update_semantics_callback, &original_init](
301 size_t version,
const FlutterRendererConfig* config,
302 const FlutterProjectArgs* args,
void*
user_data,
auto engine_out) {
303 update_semantics_callback = args->update_semantics_callback2;
304 return original_init(version, config, args,
user_data, engine_out);
306 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
311 [viewController loadView];
313 bool enabled_called =
false;
314 engine.embedderAPI.UpdateSemanticsEnabled =
315 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&enabled_called](
auto engine,
bool enabled) {
316 enabled_called = enabled;
319 engine.semanticsEnabled = YES;
320 EXPECT_TRUE(enabled_called);
322 FlutterSemanticsNode2 root;
323 FlutterSemanticsFlags flags = FlutterSemanticsFlags{0};
324 FlutterSemanticsFlags child_flags = FlutterSemanticsFlags{0};
326 root.flags2 = &flags;
328 root.actions =
static_cast<FlutterSemanticsAction
>(0);
329 root.text_selection_base = -1;
330 root.text_selection_extent = -1;
334 root.increased_value =
"";
335 root.decreased_value =
"";
337 root.child_count = 1;
338 int32_t children[] = {1};
339 root.children_in_traversal_order = children;
340 root.custom_accessibility_actions_count = 0;
342 FlutterSemanticsNode2 child1;
344 child1.flags2 = &child_flags;
346 child1.actions =
static_cast<FlutterSemanticsAction
>(0);
347 child1.text_selection_base = -1;
348 child1.text_selection_extent = -1;
349 child1.label =
"child 1";
352 child1.increased_value =
"";
353 child1.decreased_value =
"";
355 child1.child_count = 0;
356 child1.custom_accessibility_actions_count = 0;
358 FlutterSemanticsUpdate2 update;
359 update.node_count = 2;
360 FlutterSemanticsNode2* nodes[] = {&root, &child1};
361 update.nodes = nodes;
362 update.custom_action_count = 0;
363 update_semantics_callback(&update, (__bridge
void*)engine);
366 EXPECT_EQ([engine.
viewController.flutterView.accessibilityChildren count], 1u);
367 NSAccessibilityElement* native_root = engine.
viewController.flutterView.accessibilityChildren[0];
368 std::string root_label = [native_root.accessibilityLabel UTF8String];
369 EXPECT_TRUE(root_label ==
"root");
370 EXPECT_EQ(native_root.accessibilityRole, NSAccessibilityGroupRole);
371 EXPECT_EQ([native_root.accessibilityChildren count], 1u);
372 NSAccessibilityElement* native_child1 = native_root.accessibilityChildren[0];
373 std::string child1_value = [native_child1.accessibilityValue UTF8String];
374 EXPECT_TRUE(child1_value ==
"child 1");
375 EXPECT_EQ(native_child1.accessibilityRole, NSAccessibilityStaticTextRole);
376 EXPECT_EQ([native_child1.accessibilityChildren count], 0u);
378 bool semanticsEnabled =
true;
379 engine.embedderAPI.UpdateSemanticsEnabled =
380 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&semanticsEnabled](
auto engine,
bool enabled) {
381 semanticsEnabled = enabled;
384 engine.semanticsEnabled = NO;
385 EXPECT_FALSE(semanticsEnabled);
387 EXPECT_EQ([engine.
viewController.flutterView.accessibilityChildren count], 0u);
389 [engine setViewController:nil];
395 auto original_init = engine.embedderAPI.Initialize;
396 std::function<void(
const FlutterSemanticsUpdate2*,
void*)> update_semantics_callback;
397 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
398 Initialize, ([&update_semantics_callback, &original_init](
399 size_t version,
const FlutterRendererConfig* config,
400 const FlutterProjectArgs* args,
void*
user_data,
auto engine_out) {
401 update_semantics_callback = args->update_semantics_callback2;
402 return original_init(version, config, args,
user_data, engine_out);
404 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
407 bool enabled_called =
false;
408 engine.embedderAPI.UpdateSemanticsEnabled =
409 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&enabled_called](
auto engine,
bool enabled) {
410 enabled_called = enabled;
413 engine.semanticsEnabled = YES;
414 EXPECT_TRUE(enabled_called);
416 FlutterSemanticsNode2 root;
417 FlutterSemanticsFlags flags = FlutterSemanticsFlags{0};
418 FlutterSemanticsFlags child_flags = FlutterSemanticsFlags{0};
420 root.flags2 = &flags;
422 root.actions =
static_cast<FlutterSemanticsAction
>(0);
423 root.text_selection_base = -1;
424 root.text_selection_extent = -1;
428 root.increased_value =
"";
429 root.decreased_value =
"";
431 root.child_count = 1;
432 int32_t children[] = {1};
433 root.children_in_traversal_order = children;
434 root.custom_accessibility_actions_count = 0;
436 FlutterSemanticsNode2 child1;
438 child1.flags2 = &child_flags;
440 child1.actions =
static_cast<FlutterSemanticsAction
>(0);
441 child1.text_selection_base = -1;
442 child1.text_selection_extent = -1;
443 child1.label =
"child 1";
446 child1.increased_value =
"";
447 child1.decreased_value =
"";
449 child1.child_count = 0;
450 child1.custom_accessibility_actions_count = 0;
452 FlutterSemanticsUpdate2 update;
453 update.node_count = 2;
454 FlutterSemanticsNode2* nodes[] = {&root, &child1};
455 update.nodes = nodes;
456 update.custom_action_count = 0;
459 update_semantics_callback(&update, (__bridge
void*)engine);
465 bool semanticsEnabled =
true;
466 engine.embedderAPI.UpdateSemanticsEnabled =
467 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&semanticsEnabled](
auto engine,
bool enabled) {
468 semanticsEnabled = enabled;
471 engine.semanticsEnabled = NO;
472 EXPECT_FALSE(semanticsEnabled);
479 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
482 bool enabled_called =
false;
483 engine.embedderAPI.UpdateSemanticsEnabled =
484 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&enabled_called](
auto engine,
bool enabled) {
485 enabled_called = enabled;
488 engine.semanticsEnabled = YES;
489 EXPECT_TRUE(enabled_called);
499 EXPECT_NE(viewController.accessibilityBridge.lock(),
nullptr);
503 BOOL latch_called = NO;
504 AddNativeCallback(
"SignalNativeTest",
505 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch_called = YES; }));
508 EXPECT_TRUE([engine runWithEntrypoint:
@"nativeCallback"]);
509 ASSERT_TRUE(engine.running);
511 while (!latch_called) {
512 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
514 ASSERT_TRUE(latch_called);
518 NSString* fixtures = @(flutter::testing::GetFixturesPath());
520 initWithAssetsPath:fixtures
521 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
527 [viewController loadView];
528 [viewController viewDidLoad];
529 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
531 EXPECT_TRUE([engine runWithEntrypoint:
@"canCompositePlatformViews"]);
534 withId:@"factory_id"];
535 [engine.platformViewController
539 @"viewType" : @"factory_id",
545 CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
546 CALayer* rootLayer = viewController.flutterView.layer;
547 while (rootLayer.sublayers.count == 0) {
548 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
549 if (CFAbsoluteTimeGetCurrent() - start > 1) {
555 EXPECT_EQ(rootLayer.sublayers.count, 2u);
556 EXPECT_EQ(viewController.flutterView.subviews.count, 1u);
565 auto original_init = engine.embedderAPI.Initialize;
567 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
568 Initialize, ([&compositor, &original_init](
569 size_t version,
const FlutterRendererConfig* config,
570 const FlutterProjectArgs* args,
void*
user_data,
auto engine_out) {
571 compositor = *args->compositor;
572 return original_init(version, config, args,
user_data, engine_out);
578 [viewController loadView];
580 EXPECT_TRUE([engine runWithEntrypoint:
@"empty"]);
582 FlutterBackingStoreConfig config = {
583 .struct_size =
sizeof(FlutterBackingStoreConfig),
584 .size = FlutterSize{10, 10},
586 FlutterBackingStore backing_store = {};
587 EXPECT_NE(compositor.create_backing_store_callback,
nullptr);
589 compositor.create_backing_store_callback(&config, &backing_store, compositor.user_data));
592 .type = kFlutterLayerContentTypeBackingStore,
593 .backing_store = &backing_store,
595 std::vector<FlutterLayer*> layers = {&layer};
597 FlutterPresentViewInfo info = {
598 .struct_size =
sizeof(FlutterPresentViewInfo),
600 .layers =
const_cast<const FlutterLayer**
>(layers.data()),
604 EXPECT_NE(compositor.present_view_callback,
nullptr);
605 EXPECT_FALSE(compositor.present_view_callback(&info));
606 EXPECT_TRUE(compositor.collect_backing_store_callback(&backing_store, compositor.user_data));
608 (void)viewController;
613 NSString* fixtures = @(flutter::testing::GetFixturesPath());
615 initWithAssetsPath:fixtures
616 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
618 project.dartEntrypointArguments = @[ @"arg1", @"arg2" ];
622 auto original_init = engine.embedderAPI.Initialize;
623 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
624 Initialize, ([&called, &original_init](
size_t version,
const FlutterRendererConfig* config,
625 const FlutterProjectArgs* args,
void*
user_data,
628 EXPECT_EQ(args->dart_entrypoint_argc, 2);
629 NSString* arg1 = [[NSString alloc] initWithCString:args->dart_entrypoint_argv[0]
630 encoding:NSUTF8StringEncoding];
631 NSString* arg2 = [[NSString alloc] initWithCString:args->dart_entrypoint_argv[1]
632 encoding:NSUTF8StringEncoding];
634 EXPECT_TRUE([arg1 isEqualToString:
@"arg1"]);
635 EXPECT_TRUE([arg2 isEqualToString:
@"arg2"]);
637 return original_init(version, config, args,
user_data, engine_out);
640 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
653 id<FlutterBinaryMessenger> binaryMessenger = nil;
656 NSString* fixtures = @(flutter::testing::GetFixturesPath());
658 initWithAssetsPath:fixtures
659 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
662 allowHeadlessExecution:YES];
669 EXPECT_NE(binaryMessenger, nil);
670 EXPECT_EQ(weakEngine, nil);
677 id<FlutterTextureRegistry> textureRegistry;
680 NSString* fixtures = @(flutter::testing::GetFixturesPath());
682 initWithAssetsPath:fixtures
683 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
686 allowHeadlessExecution:YES];
687 id<FlutterPluginRegistrar> registrar = [engine registrarForPlugin:@"MyPlugin"];
688 textureRegistry = registrar.textures;
693 EXPECT_NE(textureRegistry, nil);
694 EXPECT_EQ(weakEngine, nil);
698 NSString* fixtures = @(flutter::testing::GetFixturesPath());
700 initWithAssetsPath:fixtures
701 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
704 allowHeadlessExecution:YES];
706 EXPECT_EQ([engine valuePublishedByPlugin:
@"NoSuchPlugin"], nil);
710 NSString* fixtures = @(flutter::testing::GetFixturesPath());
712 initWithAssetsPath:fixtures
713 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
716 allowHeadlessExecution:YES];
717 NSString* pluginName =
@"MyPlugin";
719 [engine registrarForPlugin:pluginName];
723 EXPECT_EQ([engine valuePublishedByPlugin:pluginName], [NSNull
null]);
727 NSString* fixtures = @(flutter::testing::GetFixturesPath());
729 initWithAssetsPath:fixtures
730 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
733 allowHeadlessExecution:YES];
734 NSString* pluginName =
@"MyPlugin";
735 id<FlutterPluginRegistrar> registrar = [engine registrarForPlugin:pluginName];
737 NSString* firstValue =
@"A published value";
738 NSArray* secondValue = @[ @"A different published value" ];
740 [registrar publish:firstValue];
741 EXPECT_EQ([engine valuePublishedByPlugin:pluginName], firstValue);
743 [registrar publish:secondValue];
744 EXPECT_EQ([engine valuePublishedByPlugin:pluginName], secondValue);
748 NSString* fixtures = @(flutter::testing::GetFixturesPath());
750 initWithAssetsPath:fixtures
751 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
757 id<FlutterPluginRegistrar> registrar = [engine registrarForPlugin:@"MyPlugin"];
759 EXPECT_EQ([registrar viewController], viewController);
770 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
772 NSString* channel =
@"_test_";
773 NSData* channel_data = [channel dataUsingEncoding:NSUTF8StringEncoding];
778 engine.embedderAPI.SendPlatformMessage = MOCK_ENGINE_PROC(
779 SendPlatformMessage, ([](
auto engine_,
auto message_) {
780 if (strcmp(message_->channel,
"test/send_message") == 0) {
782 std::string message = R
"|({"method": "a"})|";
783 std::string channel(reinterpret_cast<const char*>(message_->message),
784 message_->message_size);
785 reinterpret_cast<EmbedderEngine*>(engine_)
788 ->HandlePlatformMessage(std::make_unique<PlatformMessage>(
789 channel.c_str(), fml::MallocMapping::Copy(message.c_str(), message.length()),
790 fml::RefPtr<PlatformMessageResponse>()));
795 __block
int record = 0;
805 [engine.binaryMessenger sendOnChannel:@"test/send_message" message:channel_data];
806 EXPECT_EQ(record, 1);
816 [engine.binaryMessenger sendOnChannel:@"test/send_message" message:channel_data];
817 EXPECT_EQ(record, 11);
821 [engine.binaryMessenger sendOnChannel:@"test/send_message" message:channel_data];
822 EXPECT_EQ(record, 21);
829 __block
bool calledAfterClear =
false;
830 __block
bool valueAfterClear;
832 calledAfterClear =
true;
833 NSNumber* valueNumber = [result valueForKey:@"value"];
834 valueAfterClear = [valueNumber boolValue];
838 [engineMock handleMethodCall:methodCallAfterClear result:resultAfterClear];
839 EXPECT_TRUE(calledAfterClear);
840 EXPECT_FALSE(valueAfterClear);
847 __block
bool called =
false;
851 NSNumber* valueNumber = [result valueForKey:@"value"];
852 value = [valueNumber boolValue];
856 [engineMock handleMethodCall:methodCall result:result];
865 binaryMessenger:engine.binaryMessenger
867 __block BOOL didCallCallback = NO;
871 didCallCallback = YES;
873 EXPECT_TRUE([engine runWithEntrypoint:
@"sendFooMessage"]);
876 while (!didCallCallback) {
877 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
885 binaryMessenger:engine.binaryMessenger
887 __block BOOL didCallCallback = NO;
889 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
891 dispatch_async(dispatch_get_main_queue(), ^{
892 didCallCallback = YES;
896 EXPECT_TRUE([engine runWithEntrypoint:
@"sendFooMessage"]);
898 while (!didCallCallback) {
899 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
907 std::optional<int64_t> engineId;
908 AddNativeCallback(
"NotifyEngineId", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
909 const auto argument = Dart_GetNativeArgument(args, 0);
910 if (!Dart_IsNull(argument)) {
911 const auto id = tonic::DartConverter<int64_t>::FromDart(argument);
917 EXPECT_TRUE([engine runWithEntrypoint:
@"testEngineId"]);
919 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
922 EXPECT_TRUE(engineId.has_value());
923 if (!engineId.has_value()) {
926 EXPECT_EQ(engine, [
FlutterEngine engineForIdentifier:*engineId]);
931 FlutterResizeSynchronizer* threadSynchronizer = [[FlutterResizeSynchronizer alloc] init];
932 [threadSynchronizer shutDown];
934 std::thread rasterThread([&threadSynchronizer] {
935 [threadSynchronizer performCommitForSize:CGSizeMake(100, 100)
945 NSString* fixtures = @(flutter::testing::GetFixturesPath());
947 initWithAssetsPath:fixtures
948 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
958 engine = viewController1.
engine;
982 allowHeadlessExecution:NO];
1008 NSString* fixtures = @(flutter::testing::GetFixturesPath());
1010 initWithAssetsPath:fixtures
1011 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
1017 [viewController loadView];
1018 [viewController viewDidLoad];
1019 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
1021 EXPECT_TRUE([engine runWithEntrypoint:
@"drawIntoAllViews"]);
1023 CFTimeInterval start = CACurrentMediaTime();
1024 while (engine.macOSCompositor->DebugNumViews() == 0) {
1025 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
1026 if (CACurrentMediaTime() - start > 1) {
1031 EXPECT_EQ(engine.macOSCompositor->DebugNumViews(), 1u);
1034 EXPECT_EQ(engine.macOSCompositor->DebugNumViews(), 0u);
1042 __block NSString* nextResponse =
@"exit";
1043 __block BOOL triedToTerminate = NO;
1046 terminator:^(id sender) {
1047 triedToTerminate = TRUE;
1050 OCMStub([engineMock terminationHandler]).andReturn(terminationHandler);
1053 [engineMock binaryMessenger])
1054 .andReturn(binaryMessengerMock);
1055 OCMStub([engineMock sendOnChannel:
@"flutter/platform"
1056 message:[OCMArg any]
1057 binaryReply:[OCMArg any]])
1058 .andDo((^(NSInvocation* invocation) {
1059 [invocation retainArguments];
1061 NSData* returnedMessage;
1062 [invocation getArgument:&callback atIndex:4];
1063 if ([nextResponse isEqualToString:
@"error"]) {
1070 NSDictionary* responseDict = @{
@"response" : nextResponse};
1074 callback(returnedMessage);
1076 __block NSString* calledAfterTerminate =
@"";
1078 NSDictionary* resultDict = result;
1079 calledAfterTerminate = resultDict[@"response"];
1086 triedToTerminate = NO;
1087 calledAfterTerminate =
@"";
1088 nextResponse =
@"cancel";
1089 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1090 EXPECT_STREQ([calledAfterTerminate UTF8String],
"");
1091 EXPECT_TRUE(triedToTerminate);
1095 triedToTerminate = NO;
1096 calledAfterTerminate =
@"";
1097 nextResponse =
@"exit";
1098 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1099 EXPECT_STREQ([calledAfterTerminate UTF8String],
"exit");
1100 EXPECT_TRUE(triedToTerminate);
1102 triedToTerminate = NO;
1103 calledAfterTerminate =
@"";
1104 nextResponse =
@"cancel";
1105 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1106 EXPECT_STREQ([calledAfterTerminate UTF8String],
"cancel");
1107 EXPECT_FALSE(triedToTerminate);
1110 triedToTerminate = NO;
1111 calledAfterTerminate =
@"";
1112 nextResponse =
@"error";
1113 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1114 EXPECT_STREQ([calledAfterTerminate UTF8String],
"");
1115 EXPECT_TRUE(triedToTerminate);
1119 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1121 [NSApplication sharedApplication].delegate = plainDelegate;
1128 EXPECT_EQ([[[NSApplication sharedApplication] delegate] applicationShouldTerminate:NSApp],
1131 [NSApplication sharedApplication].delegate = previousDelegate;
1135 __block BOOL announced = NO;
1138 OCMStub([engineMock announceAccessibilityMessage:[OCMArg any]
1139 withPriority:NSAccessibilityPriorityMedium])
1140 .andDo((^(NSInvocation* invocation) {
1142 [invocation retainArguments];
1144 [invocation getArgument:&message atIndex:2];
1145 EXPECT_EQ(message,
@"error message");
1148 NSDictionary<NSString*, id>* annotatedEvent =
1149 @{
@"type" :
@"announce",
1150 @"data" : @{
@"message" :
@"error message"}};
1152 [engineMock handleAccessibilityEvent:annotatedEvent];
1154 EXPECT_TRUE(announced);
1164 .andDo((^(NSInvocation* invocation) {
1168 .andDo((^(NSInvocation* invocation) {
1172 .andDo((^(NSInvocation* invocation) {
1176 .andDo((^(NSInvocation* invocation) {
1180 .andDo((^(NSInvocation* invocation) {
1184 __block NSApplicationOcclusionState visibility = NSApplicationOcclusionStateVisible;
1185 id mockApplication = OCMPartialMock([NSApplication sharedApplication]);
1186 OCMStub((NSApplicationOcclusionState)[mockApplication occlusionState])
1187 .andDo(^(NSInvocation* invocation) {
1188 [invocation setReturnValue:&visibility];
1191 NSNotification* willBecomeActive =
1192 [[NSNotification alloc] initWithName:NSApplicationWillBecomeActiveNotification
1195 NSNotification* willResignActive =
1196 [[NSNotification alloc] initWithName:NSApplicationWillResignActiveNotification
1200 NSNotification* didChangeOcclusionState;
1201 didChangeOcclusionState =
1202 [[NSNotification alloc] initWithName:NSApplicationDidChangeOcclusionStateNotification
1206 [engineMock handleDidChangeOcclusionState:didChangeOcclusionState];
1209 [engineMock handleWillBecomeActive:willBecomeActive];
1212 [engineMock handleWillResignActive:willResignActive];
1216 [engineMock handleDidChangeOcclusionState:didChangeOcclusionState];
1219 [engineMock handleWillBecomeActive:willBecomeActive];
1222 [engineMock handleWillResignActive:willResignActive];
1225 [mockApplication stopMocking];
1229 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1231 [NSApplication sharedApplication].delegate = fakeAppDelegate;
1236 [[engine registrarForPlugin:@"TestPlugin"] addApplicationDelegate:plugin];
1238 EXPECT_TRUE([fakeAppDelegate hasDelegate:plugin]);
1240 [NSApplication sharedApplication].delegate = previousDelegate;
1244 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1246 [NSApplication sharedApplication].delegate = fakeAppDelegate;
1253 [[engine registrarForPlugin:@"TestPlugin"] addApplicationDelegate:plugin];
1254 EXPECT_TRUE([fakeAppDelegate hasDelegate:plugin]);
1259 EXPECT_FALSE([fakeAppDelegate hasDelegate:plugin]);
1261 [NSApplication sharedApplication].delegate = previousDelegate;
1267 auto original_update_displays = engine.embedderAPI.NotifyDisplayUpdate;
1268 engine.embedderAPI.NotifyDisplayUpdate = MOCK_ENGINE_PROC(
1269 NotifyDisplayUpdate, ([&updated, &original_update_displays](
1270 auto engine,
auto update_type,
auto* displays,
auto display_count) {
1272 return original_update_displays(engine, update_type, displays, display_count);
1275 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1276 EXPECT_TRUE(updated);
1279 [[NSNotificationCenter defaultCenter]
1280 postNotificationName:NSApplicationDidChangeScreenParametersNotification
1282 EXPECT_TRUE(updated);
1288 auto original_set_viewport_metrics = engine.embedderAPI.SendWindowMetricsEvent;
1289 engine.embedderAPI.SendWindowMetricsEvent = MOCK_ENGINE_PROC(
1290 SendWindowMetricsEvent,
1291 ([&updated, &original_set_viewport_metrics](
auto engine,
auto* window_metrics) {
1293 return original_set_viewport_metrics(engine, window_metrics);
1296 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1299 [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
1302 EXPECT_FALSE(updated);
1307 [viewController loadView];
1308 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
1310 [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
1312 EXPECT_TRUE(updated);
1316 NSString* fixtures = @(testing::GetFixturesPath());
1318 initWithAssetsPath:fixtures
1319 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
1320 project.rootIsolateCreateCallback = FlutterEngineTest::IsolateCreateCallback;
1323 allowHeadlessExecution:true];
1325 auto original_update_displays = engine.embedderAPI.NotifyDisplayUpdate;
1326 engine.embedderAPI.NotifyDisplayUpdate = MOCK_ENGINE_PROC(
1327 NotifyDisplayUpdate, ([&updated, &original_update_displays](
1328 auto engine,
auto update_type,
auto* displays,
auto display_count) {
1329 EXPECT_EQ(display_count, 1UL);
1330 EXPECT_EQ(displays->display_id, 10UL);
1331 EXPECT_EQ(displays->width, 60UL);
1332 EXPECT_EQ(displays->height, 80UL);
1333 EXPECT_EQ(displays->device_pixel_ratio, 2UL);
1335 return original_update_displays(engine, update_type, displays, display_count);
1337 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1338 EXPECT_TRUE(updated);
1344 __block BOOL expectedValue;
1348 OCMStub([channelMock messageChannelWithName:
@"flutter/settings"
1349 binaryMessenger:[OCMArg any]
1350 codec:[OCMArg any]])
1351 .andReturn(channelMock);
1352 OCMStub([channelMock sendMessage:[OCMArg any]]).andDo((^(NSInvocation* invocation) {
1354 [invocation getArgument:&message atIndex:2];
1355 EXPECT_EQ(message[
@"alwaysUse24HourFormat"], @(expectedValue));
1359 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andDo((^(NSInvocation* invocation) {
1360 [invocation setReturnValue:&expectedValue];
1366 expectedValue = YES;
1367 EXPECT_TRUE([engineMock runWithEntrypoint:
@"main"]);
1368 [engineMock shutDownEngine];
1372 EXPECT_TRUE([engineMock runWithEntrypoint:
@"main"]);
1373 [engineMock shutDownEngine];
1376 [mockHourFormat stopMocking];
1377 [engineMock stopMocking];
1378 [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()