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 FlutterLogger.outputWriter = writer;
219 EXPECT_TRUE([engine runWithEntrypoint:
@"canLogToStdout"]);
220 ASSERT_TRUE(engine.running);
223 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
227 EXPECT_TRUE([writer.lastLine containsString:
@"Hello logging"]);
235 AddNativeCallback(
"SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
237 EXPECT_TRUE(rootLayer.backgroundColor != nil);
238 if (rootLayer.backgroundColor != nil) {
239 NSColor* actualBackgroundColor =
240 [NSColor colorWithCGColor:rootLayer.backgroundColor];
241 EXPECT_EQ(actualBackgroundColor, [NSColor blackColor]);
247 EXPECT_TRUE([engine runWithEntrypoint:
@"backgroundTest"]);
248 ASSERT_TRUE(engine.running);
253 [viewController loadView];
254 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
257 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
266 AddNativeCallback(
"SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
268 EXPECT_TRUE(rootLayer.backgroundColor != nil);
269 if (rootLayer.backgroundColor != nil) {
270 NSColor* actualBackgroundColor =
271 [NSColor colorWithCGColor:rootLayer.backgroundColor];
272 EXPECT_EQ(actualBackgroundColor, [NSColor whiteColor]);
278 EXPECT_TRUE([engine runWithEntrypoint:
@"backgroundTest"]);
279 ASSERT_TRUE(engine.running);
284 [viewController loadView];
285 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
289 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
296 auto original_init = engine.embedderAPI.Initialize;
297 std::function<void(
const FlutterSemanticsUpdate2*,
void*)> update_semantics_callback;
298 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
299 Initialize, ([&update_semantics_callback, &original_init](
300 size_t version,
const FlutterRendererConfig* config,
301 const FlutterProjectArgs* args,
void*
user_data,
auto engine_out) {
302 update_semantics_callback = args->update_semantics_callback2;
303 return original_init(version, config, args,
user_data, engine_out);
305 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
310 [viewController loadView];
312 bool enabled_called =
false;
313 engine.embedderAPI.UpdateSemanticsEnabled =
314 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&enabled_called](
auto engine,
bool enabled) {
315 enabled_called = enabled;
318 engine.semanticsEnabled = YES;
319 EXPECT_TRUE(enabled_called);
321 FlutterSemanticsNode2 root;
322 FlutterSemanticsFlags flags = FlutterSemanticsFlags{0};
323 FlutterSemanticsFlags child_flags = FlutterSemanticsFlags{0};
325 root.flags2 = &flags;
326 root.actions =
static_cast<FlutterSemanticsAction
>(0);
327 root.text_selection_base = -1;
328 root.text_selection_extent = -1;
332 root.increased_value =
"";
333 root.decreased_value =
"";
335 root.child_count = 1;
336 int32_t children[] = {1};
337 root.children_in_traversal_order = children;
338 root.custom_accessibility_actions_count = 0;
340 FlutterSemanticsNode2 child1;
342 child1.flags2 = &child_flags;
343 child1.actions =
static_cast<FlutterSemanticsAction
>(0);
344 child1.text_selection_base = -1;
345 child1.text_selection_extent = -1;
346 child1.label =
"child 1";
349 child1.increased_value =
"";
350 child1.decreased_value =
"";
352 child1.child_count = 0;
353 child1.custom_accessibility_actions_count = 0;
355 FlutterSemanticsUpdate2 update;
356 update.node_count = 2;
357 FlutterSemanticsNode2* nodes[] = {&root, &child1};
358 update.nodes = nodes;
359 update.custom_action_count = 0;
360 update_semantics_callback(&update, (__bridge
void*)engine);
363 EXPECT_EQ([engine.
viewController.flutterView.accessibilityChildren count], 1u);
364 NSAccessibilityElement* native_root = engine.
viewController.flutterView.accessibilityChildren[0];
365 std::string root_label = [native_root.accessibilityLabel UTF8String];
366 EXPECT_TRUE(root_label ==
"root");
367 EXPECT_EQ(native_root.accessibilityRole, NSAccessibilityGroupRole);
368 EXPECT_EQ([native_root.accessibilityChildren count], 1u);
369 NSAccessibilityElement* native_child1 = native_root.accessibilityChildren[0];
370 std::string child1_value = [native_child1.accessibilityValue UTF8String];
371 EXPECT_TRUE(child1_value ==
"child 1");
372 EXPECT_EQ(native_child1.accessibilityRole, NSAccessibilityStaticTextRole);
373 EXPECT_EQ([native_child1.accessibilityChildren count], 0u);
375 bool semanticsEnabled =
true;
376 engine.embedderAPI.UpdateSemanticsEnabled =
377 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&semanticsEnabled](
auto engine,
bool enabled) {
378 semanticsEnabled = enabled;
381 engine.semanticsEnabled = NO;
382 EXPECT_FALSE(semanticsEnabled);
384 EXPECT_EQ([engine.
viewController.flutterView.accessibilityChildren count], 0u);
386 [engine setViewController:nil];
392 auto original_init = engine.embedderAPI.Initialize;
393 std::function<void(
const FlutterSemanticsUpdate2*,
void*)> update_semantics_callback;
394 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
395 Initialize, ([&update_semantics_callback, &original_init](
396 size_t version,
const FlutterRendererConfig* config,
397 const FlutterProjectArgs* args,
void*
user_data,
auto engine_out) {
398 update_semantics_callback = args->update_semantics_callback2;
399 return original_init(version, config, args,
user_data, engine_out);
401 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
404 bool enabled_called =
false;
405 engine.embedderAPI.UpdateSemanticsEnabled =
406 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&enabled_called](
auto engine,
bool enabled) {
407 enabled_called = enabled;
410 engine.semanticsEnabled = YES;
411 EXPECT_TRUE(enabled_called);
413 FlutterSemanticsNode2 root;
414 FlutterSemanticsFlags flags = FlutterSemanticsFlags{0};
415 FlutterSemanticsFlags child_flags = FlutterSemanticsFlags{0};
417 root.flags2 = &flags;
418 root.actions =
static_cast<FlutterSemanticsAction
>(0);
419 root.text_selection_base = -1;
420 root.text_selection_extent = -1;
424 root.increased_value =
"";
425 root.decreased_value =
"";
427 root.child_count = 1;
428 int32_t children[] = {1};
429 root.children_in_traversal_order = children;
430 root.custom_accessibility_actions_count = 0;
432 FlutterSemanticsNode2 child1;
434 child1.flags2 = &child_flags;
435 child1.actions =
static_cast<FlutterSemanticsAction
>(0);
436 child1.text_selection_base = -1;
437 child1.text_selection_extent = -1;
438 child1.label =
"child 1";
441 child1.increased_value =
"";
442 child1.decreased_value =
"";
444 child1.child_count = 0;
445 child1.custom_accessibility_actions_count = 0;
447 FlutterSemanticsUpdate2 update;
448 update.node_count = 2;
449 FlutterSemanticsNode2* nodes[] = {&root, &child1};
450 update.nodes = nodes;
451 update.custom_action_count = 0;
454 update_semantics_callback(&update, (__bridge
void*)engine);
460 bool semanticsEnabled =
true;
461 engine.embedderAPI.UpdateSemanticsEnabled =
462 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&semanticsEnabled](
auto engine,
bool enabled) {
463 semanticsEnabled = enabled;
466 engine.semanticsEnabled = NO;
467 EXPECT_FALSE(semanticsEnabled);
474 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
477 bool enabled_called =
false;
478 engine.embedderAPI.UpdateSemanticsEnabled =
479 MOCK_ENGINE_PROC(UpdateSemanticsEnabled, ([&enabled_called](
auto engine,
bool enabled) {
480 enabled_called = enabled;
483 engine.semanticsEnabled = YES;
484 EXPECT_TRUE(enabled_called);
494 EXPECT_NE(viewController.accessibilityBridge.lock(),
nullptr);
498 BOOL latch_called = NO;
499 AddNativeCallback(
"SignalNativeTest",
500 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch_called = YES; }));
503 EXPECT_TRUE([engine runWithEntrypoint:
@"nativeCallback"]);
504 ASSERT_TRUE(engine.running);
506 while (!latch_called) {
507 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
509 ASSERT_TRUE(latch_called);
513 NSString* fixtures = @(flutter::testing::GetFixturesPath());
515 initWithAssetsPath:fixtures
516 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
522 [viewController loadView];
523 [viewController viewDidLoad];
524 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
526 EXPECT_TRUE([engine runWithEntrypoint:
@"canCompositePlatformViews"]);
529 withId:@"factory_id"];
530 [engine.platformViewController
534 @"viewType" : @"factory_id",
540 CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
541 CALayer* rootLayer = viewController.flutterView.layer;
542 while (rootLayer.sublayers.count == 0) {
543 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
544 if (CFAbsoluteTimeGetCurrent() - start > 1) {
550 EXPECT_EQ(rootLayer.sublayers.count, 2u);
551 EXPECT_EQ(viewController.flutterView.subviews.count, 1u);
560 auto original_init = engine.embedderAPI.Initialize;
562 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
563 Initialize, ([&compositor, &original_init](
564 size_t version,
const FlutterRendererConfig* config,
565 const FlutterProjectArgs* args,
void*
user_data,
auto engine_out) {
566 compositor = *args->compositor;
567 return original_init(version, config, args,
user_data, engine_out);
573 [viewController loadView];
575 EXPECT_TRUE([engine runWithEntrypoint:
@"empty"]);
577 FlutterBackingStoreConfig config = {
578 .struct_size =
sizeof(FlutterBackingStoreConfig),
579 .size = FlutterSize{10, 10},
581 FlutterBackingStore backing_store = {};
582 EXPECT_NE(compositor.create_backing_store_callback,
nullptr);
584 compositor.create_backing_store_callback(&config, &backing_store, compositor.user_data));
587 .type = kFlutterLayerContentTypeBackingStore,
588 .backing_store = &backing_store,
590 std::vector<FlutterLayer*> layers = {&layer};
592 FlutterPresentViewInfo info = {
593 .struct_size =
sizeof(FlutterPresentViewInfo),
595 .layers =
const_cast<const FlutterLayer**
>(layers.data()),
599 EXPECT_NE(compositor.present_view_callback,
nullptr);
600 EXPECT_FALSE(compositor.present_view_callback(&info));
601 EXPECT_TRUE(compositor.collect_backing_store_callback(&backing_store, compositor.user_data));
603 (void)viewController;
608 NSString* fixtures = @(flutter::testing::GetFixturesPath());
610 initWithAssetsPath:fixtures
611 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
613 project.dartEntrypointArguments = @[ @"arg1", @"arg2" ];
617 auto original_init = engine.embedderAPI.Initialize;
618 engine.embedderAPI.Initialize = MOCK_ENGINE_PROC(
619 Initialize, ([&called, &original_init](
size_t version,
const FlutterRendererConfig* config,
620 const FlutterProjectArgs* args,
void*
user_data,
623 EXPECT_EQ(args->dart_entrypoint_argc, 2);
624 NSString* arg1 = [[NSString alloc] initWithCString:args->dart_entrypoint_argv[0]
625 encoding:NSUTF8StringEncoding];
626 NSString* arg2 = [[NSString alloc] initWithCString:args->dart_entrypoint_argv[1]
627 encoding:NSUTF8StringEncoding];
629 EXPECT_TRUE([arg1 isEqualToString:
@"arg1"]);
630 EXPECT_TRUE([arg2 isEqualToString:
@"arg2"]);
632 return original_init(version, config, args,
user_data, engine_out);
635 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
648 id<FlutterBinaryMessenger> binaryMessenger = nil;
651 NSString* fixtures = @(flutter::testing::GetFixturesPath());
653 initWithAssetsPath:fixtures
654 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
657 allowHeadlessExecution:YES];
664 EXPECT_NE(binaryMessenger, nil);
665 EXPECT_EQ(weakEngine, nil);
672 id<FlutterTextureRegistry> textureRegistry;
675 NSString* fixtures = @(flutter::testing::GetFixturesPath());
677 initWithAssetsPath:fixtures
678 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
681 allowHeadlessExecution:YES];
682 id<FlutterPluginRegistrar> registrar = [engine registrarForPlugin:@"MyPlugin"];
683 textureRegistry = registrar.textures;
688 EXPECT_NE(textureRegistry, nil);
689 EXPECT_EQ(weakEngine, nil);
693 NSString* fixtures = @(flutter::testing::GetFixturesPath());
695 initWithAssetsPath:fixtures
696 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
699 allowHeadlessExecution:YES];
701 EXPECT_EQ([engine valuePublishedByPlugin:
@"NoSuchPlugin"], nil);
705 NSString* fixtures = @(flutter::testing::GetFixturesPath());
707 initWithAssetsPath:fixtures
708 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
711 allowHeadlessExecution:YES];
712 NSString* pluginName =
@"MyPlugin";
714 [engine registrarForPlugin:pluginName];
718 EXPECT_EQ([engine valuePublishedByPlugin:pluginName], [NSNull
null]);
722 NSString* fixtures = @(flutter::testing::GetFixturesPath());
724 initWithAssetsPath:fixtures
725 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
728 allowHeadlessExecution:YES];
729 NSString* pluginName =
@"MyPlugin";
730 id<FlutterPluginRegistrar> registrar = [engine registrarForPlugin:pluginName];
732 NSString* firstValue =
@"A published value";
733 NSArray* secondValue = @[ @"A different published value" ];
735 [registrar publish:firstValue];
736 EXPECT_EQ([engine valuePublishedByPlugin:pluginName], firstValue);
738 [registrar publish:secondValue];
739 EXPECT_EQ([engine valuePublishedByPlugin:pluginName], secondValue);
750 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
752 NSString* channel =
@"_test_";
753 NSData* channel_data = [channel dataUsingEncoding:NSUTF8StringEncoding];
758 engine.embedderAPI.SendPlatformMessage = MOCK_ENGINE_PROC(
759 SendPlatformMessage, ([](
auto engine_,
auto message_) {
760 if (strcmp(message_->channel,
"test/send_message") == 0) {
762 std::string message = R
"|({"method": "a"})|";
763 std::string channel(reinterpret_cast<const char*>(message_->message),
764 message_->message_size);
765 reinterpret_cast<EmbedderEngine*>(engine_)
768 ->HandlePlatformMessage(std::make_unique<PlatformMessage>(
769 channel.c_str(), fml::MallocMapping::Copy(message.c_str(), message.length()),
770 fml::RefPtr<PlatformMessageResponse>()));
775 __block
int record = 0;
785 [engine.binaryMessenger sendOnChannel:@"test/send_message" message:channel_data];
786 EXPECT_EQ(record, 1);
796 [engine.binaryMessenger sendOnChannel:@"test/send_message" message:channel_data];
797 EXPECT_EQ(record, 11);
801 [engine.binaryMessenger sendOnChannel:@"test/send_message" message:channel_data];
802 EXPECT_EQ(record, 21);
809 __block
bool calledAfterClear =
false;
810 __block
bool valueAfterClear;
812 calledAfterClear =
true;
813 NSNumber* valueNumber = [result valueForKey:@"value"];
814 valueAfterClear = [valueNumber boolValue];
818 [engineMock handleMethodCall:methodCallAfterClear result:resultAfterClear];
819 EXPECT_TRUE(calledAfterClear);
820 EXPECT_FALSE(valueAfterClear);
827 __block
bool called =
false;
831 NSNumber* valueNumber = [result valueForKey:@"value"];
832 value = [valueNumber boolValue];
836 [engineMock handleMethodCall:methodCall result:result];
845 binaryMessenger:engine.binaryMessenger
847 __block BOOL didCallCallback = NO;
851 didCallCallback = YES;
853 EXPECT_TRUE([engine runWithEntrypoint:
@"sendFooMessage"]);
856 while (!didCallCallback) {
857 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
865 binaryMessenger:engine.binaryMessenger
867 __block BOOL didCallCallback = NO;
869 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
871 dispatch_async(dispatch_get_main_queue(), ^{
872 didCallCallback = YES;
876 EXPECT_TRUE([engine runWithEntrypoint:
@"sendFooMessage"]);
878 while (!didCallCallback) {
879 [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
887 std::optional<int64_t> engineId;
888 AddNativeCallback(
"NotifyEngineId", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
889 const auto argument = Dart_GetNativeArgument(args, 0);
890 if (!Dart_IsNull(argument)) {
891 const auto id = tonic::DartConverter<int64_t>::FromDart(argument);
897 EXPECT_TRUE([engine runWithEntrypoint:
@"testEngineId"]);
899 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
902 EXPECT_TRUE(engineId.has_value());
903 if (!engineId.has_value()) {
906 EXPECT_EQ(engine, [
FlutterEngine engineForIdentifier:*engineId]);
911 FlutterResizeSynchronizer* threadSynchronizer = [[FlutterResizeSynchronizer alloc] init];
912 [threadSynchronizer shutDown];
914 std::thread rasterThread([&threadSynchronizer] {
915 [threadSynchronizer performCommitForSize:CGSizeMake(100, 100)
925 NSString* fixtures = @(flutter::testing::GetFixturesPath());
927 initWithAssetsPath:fixtures
928 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
938 engine = viewController1.
engine;
962 allowHeadlessExecution:NO];
988 NSString* fixtures = @(flutter::testing::GetFixturesPath());
990 initWithAssetsPath:fixtures
991 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
997 [viewController loadView];
998 [viewController viewDidLoad];
999 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
1001 EXPECT_TRUE([engine runWithEntrypoint:
@"drawIntoAllViews"]);
1003 CFTimeInterval start = CACurrentMediaTime();
1004 while (engine.macOSCompositor->DebugNumViews() == 0) {
1005 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1, YES);
1006 if (CACurrentMediaTime() - start > 1) {
1011 EXPECT_EQ(engine.macOSCompositor->DebugNumViews(), 1u);
1014 EXPECT_EQ(engine.macOSCompositor->DebugNumViews(), 0u);
1022 __block NSString* nextResponse =
@"exit";
1023 __block BOOL triedToTerminate = NO;
1026 terminator:^(id sender) {
1027 triedToTerminate = TRUE;
1030 OCMStub([engineMock terminationHandler]).andReturn(terminationHandler);
1033 [engineMock binaryMessenger])
1034 .andReturn(binaryMessengerMock);
1035 OCMStub([engineMock sendOnChannel:
@"flutter/platform"
1036 message:[OCMArg any]
1037 binaryReply:[OCMArg any]])
1038 .andDo((^(NSInvocation* invocation) {
1039 [invocation retainArguments];
1041 NSData* returnedMessage;
1042 [invocation getArgument:&callback atIndex:4];
1043 if ([nextResponse isEqualToString:
@"error"]) {
1050 NSDictionary* responseDict = @{
@"response" : nextResponse};
1054 callback(returnedMessage);
1056 __block NSString* calledAfterTerminate =
@"";
1058 NSDictionary* resultDict = result;
1059 calledAfterTerminate = resultDict[@"response"];
1066 triedToTerminate = NO;
1067 calledAfterTerminate =
@"";
1068 nextResponse =
@"cancel";
1069 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1070 EXPECT_STREQ([calledAfterTerminate UTF8String],
"");
1071 EXPECT_TRUE(triedToTerminate);
1075 triedToTerminate = NO;
1076 calledAfterTerminate =
@"";
1077 nextResponse =
@"exit";
1078 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1079 EXPECT_STREQ([calledAfterTerminate UTF8String],
"exit");
1080 EXPECT_TRUE(triedToTerminate);
1082 triedToTerminate = NO;
1083 calledAfterTerminate =
@"";
1084 nextResponse =
@"cancel";
1085 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1086 EXPECT_STREQ([calledAfterTerminate UTF8String],
"cancel");
1087 EXPECT_FALSE(triedToTerminate);
1090 triedToTerminate = NO;
1091 calledAfterTerminate =
@"";
1092 nextResponse =
@"error";
1093 [engineMock handleMethodCall:methodExitApplication result:appExitResult];
1094 EXPECT_STREQ([calledAfterTerminate UTF8String],
"");
1095 EXPECT_TRUE(triedToTerminate);
1099 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1101 [NSApplication sharedApplication].delegate = plainDelegate;
1108 EXPECT_EQ([[[NSApplication sharedApplication] delegate] applicationShouldTerminate:NSApp],
1111 [NSApplication sharedApplication].delegate = previousDelegate;
1115 __block BOOL announced = NO;
1118 OCMStub([engineMock announceAccessibilityMessage:[OCMArg any]
1119 withPriority:NSAccessibilityPriorityMedium])
1120 .andDo((^(NSInvocation* invocation) {
1122 [invocation retainArguments];
1124 [invocation getArgument:&message atIndex:2];
1125 EXPECT_EQ(message,
@"error message");
1128 NSDictionary<NSString*, id>* annotatedEvent =
1129 @{
@"type" :
@"announce",
1130 @"data" : @{
@"message" :
@"error message"}};
1132 [engineMock handleAccessibilityEvent:annotatedEvent];
1134 EXPECT_TRUE(announced);
1144 .andDo((^(NSInvocation* invocation) {
1148 .andDo((^(NSInvocation* invocation) {
1152 .andDo((^(NSInvocation* invocation) {
1156 .andDo((^(NSInvocation* invocation) {
1160 .andDo((^(NSInvocation* invocation) {
1164 __block NSApplicationOcclusionState visibility = NSApplicationOcclusionStateVisible;
1165 id mockApplication = OCMPartialMock([NSApplication sharedApplication]);
1166 OCMStub((NSApplicationOcclusionState)[mockApplication occlusionState])
1167 .andDo(^(NSInvocation* invocation) {
1168 [invocation setReturnValue:&visibility];
1171 NSNotification* willBecomeActive =
1172 [[NSNotification alloc] initWithName:NSApplicationWillBecomeActiveNotification
1175 NSNotification* willResignActive =
1176 [[NSNotification alloc] initWithName:NSApplicationWillResignActiveNotification
1180 NSNotification* didChangeOcclusionState;
1181 didChangeOcclusionState =
1182 [[NSNotification alloc] initWithName:NSApplicationDidChangeOcclusionStateNotification
1186 [engineMock handleDidChangeOcclusionState:didChangeOcclusionState];
1189 [engineMock handleWillBecomeActive:willBecomeActive];
1192 [engineMock handleWillResignActive:willResignActive];
1196 [engineMock handleDidChangeOcclusionState:didChangeOcclusionState];
1199 [engineMock handleWillBecomeActive:willBecomeActive];
1202 [engineMock handleWillResignActive:willResignActive];
1205 [mockApplication stopMocking];
1209 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1211 [NSApplication sharedApplication].delegate = fakeAppDelegate;
1216 [[engine registrarForPlugin:@"TestPlugin"] addApplicationDelegate:plugin];
1218 EXPECT_TRUE([fakeAppDelegate hasDelegate:plugin]);
1220 [NSApplication sharedApplication].delegate = previousDelegate;
1224 id<NSApplicationDelegate> previousDelegate = [[NSApplication sharedApplication] delegate];
1226 [NSApplication sharedApplication].delegate = fakeAppDelegate;
1233 [[engine registrarForPlugin:@"TestPlugin"] addApplicationDelegate:plugin];
1234 EXPECT_TRUE([fakeAppDelegate hasDelegate:plugin]);
1239 EXPECT_FALSE([fakeAppDelegate hasDelegate:plugin]);
1241 [NSApplication sharedApplication].delegate = previousDelegate;
1247 auto original_update_displays = engine.embedderAPI.NotifyDisplayUpdate;
1248 engine.embedderAPI.NotifyDisplayUpdate = MOCK_ENGINE_PROC(
1249 NotifyDisplayUpdate, ([&updated, &original_update_displays](
1250 auto engine,
auto update_type,
auto* displays,
auto display_count) {
1252 return original_update_displays(engine, update_type, displays, display_count);
1255 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1256 EXPECT_TRUE(updated);
1259 [[NSNotificationCenter defaultCenter]
1260 postNotificationName:NSApplicationDidChangeScreenParametersNotification
1262 EXPECT_TRUE(updated);
1268 auto original_set_viewport_metrics = engine.embedderAPI.SendWindowMetricsEvent;
1269 engine.embedderAPI.SendWindowMetricsEvent = MOCK_ENGINE_PROC(
1270 SendWindowMetricsEvent,
1271 ([&updated, &original_set_viewport_metrics](
auto engine,
auto* window_metrics) {
1273 return original_set_viewport_metrics(engine, window_metrics);
1276 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1279 [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
1282 EXPECT_FALSE(updated);
1287 [viewController loadView];
1288 viewController.flutterView.frame = CGRectMake(0, 0, 800, 600);
1290 [[NSNotificationCenter defaultCenter] postNotificationName:NSWindowDidChangeScreenNotification
1292 EXPECT_TRUE(updated);
1296 NSString* fixtures = @(testing::GetFixturesPath());
1298 initWithAssetsPath:fixtures
1299 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
1300 project.rootIsolateCreateCallback = FlutterEngineTest::IsolateCreateCallback;
1303 allowHeadlessExecution:true];
1305 auto original_update_displays = engine.embedderAPI.NotifyDisplayUpdate;
1306 engine.embedderAPI.NotifyDisplayUpdate = MOCK_ENGINE_PROC(
1307 NotifyDisplayUpdate, ([&updated, &original_update_displays](
1308 auto engine,
auto update_type,
auto* displays,
auto display_count) {
1309 EXPECT_EQ(display_count, 1UL);
1310 EXPECT_EQ(displays->display_id, 10UL);
1311 EXPECT_EQ(displays->width, 60UL);
1312 EXPECT_EQ(displays->height, 80UL);
1313 EXPECT_EQ(displays->device_pixel_ratio, 2UL);
1315 return original_update_displays(engine, update_type, displays, display_count);
1317 EXPECT_TRUE([engine runWithEntrypoint:
@"main"]);
1318 EXPECT_TRUE(updated);
1324 __block BOOL expectedValue;
1328 OCMStub([channelMock messageChannelWithName:
@"flutter/settings"
1329 binaryMessenger:[OCMArg any]
1330 codec:[OCMArg any]])
1331 .andReturn(channelMock);
1332 OCMStub([channelMock sendMessage:[OCMArg any]]).andDo((^(NSInvocation* invocation) {
1334 [invocation getArgument:&message atIndex:2];
1335 EXPECT_EQ(message[
@"alwaysUse24HourFormat"], @(expectedValue));
1339 OCMStub([mockHourFormat isAlwaysUse24HourFormat]).andDo((^(NSInvocation* invocation) {
1340 [invocation setReturnValue:&expectedValue];
1346 expectedValue = YES;
1347 EXPECT_TRUE([engineMock runWithEntrypoint:
@"main"]);
1348 [engineMock shutDownEngine];
1352 EXPECT_TRUE([engineMock runWithEntrypoint:
@"main"]);
1353 [engineMock shutDownEngine];
1356 [mockHourFormat stopMocking];
1357 [engineMock stopMocking];
1358 [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()