7
7
8
8
#include < objc/objc.h>
9
9
10
+ #include < algorithm>
10
11
#include < functional>
11
12
#include < thread>
13
+ #include < vector>
12
14
13
15
#include " flutter/fml/synchronization/waitable_event.h"
14
16
#include " flutter/lib/ui/window/platform_message.h"
15
17
#include " flutter/shell/platform/common/accessibility_bridge.h"
16
18
#import " flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
17
19
#import " flutter/shell/platform/darwin/common/framework/Source/FlutterBinaryMessengerRelay.h"
18
20
#import " flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h"
21
+ #import " flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppLifecycleDelegate.h"
22
+ #import " flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginMacOS.h"
19
23
#import " flutter/shell/platform/darwin/macos/framework/Source/FlutterEngineTestUtils.h"
20
24
#import " flutter/shell/platform/darwin/macos/framework/Source/FlutterViewControllerTestUtils.h"
21
25
#include " flutter/shell/platform/embedder/embedder.h"
@@ -59,6 +63,60 @@ - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication* _Nonnu
59
63
}
60
64
@end
61
65
66
+ #pragma mark -
67
+
68
+ @interface FakeLifecycleProvider : NSObject <FlutterAppLifecycleProvider, NSApplicationDelegate >
69
+
70
+ @property (nonatomic , strong , readonly ) NSPointerArray * registeredDelegates;
71
+
72
+ // True if the given delegate is currently registered.
73
+ - (BOOL )hasDelegate : (nonnull NSObject <FlutterAppLifecycleDelegate>*)delegate ;
74
+ @end
75
+
76
+ @implementation FakeLifecycleProvider {
77
+ /* *
78
+ * All currently registered delegates.
79
+ *
80
+ * This does not use NSPointerArray or any other weak-pointer
81
+ * system, because a weak pointer will be nil'd out at the start of dealloc, which will break
82
+ * queries. E.g., if a delegate is dealloc'd without being unregistered, a weak pointer array
83
+ * would no longer contain that pointer even though removeApplicationLifecycleDelegate: was never
84
+ * called, causing tests to pass incorrectly.
85
+ */
86
+ std::vector<void *> _delegates;
87
+ }
88
+
89
+ - (void )addApplicationLifecycleDelegate : (nonnull NSObject <FlutterAppLifecycleDelegate>*)delegate {
90
+ _delegates.push_back ((__bridge void *)delegate);
91
+ }
92
+
93
+ - (void )removeApplicationLifecycleDelegate :
94
+ (nonnull NSObject <FlutterAppLifecycleDelegate>*)delegate {
95
+ auto delegateIndex = std::find (_delegates.begin (), _delegates.end (), (__bridge void *)delegate);
96
+ NSAssert (delegateIndex != _delegates.end(),
97
+ @"Attempting to unregister a delegate that was not registered.");
98
+ _delegates.erase (delegateIndex);
99
+ }
100
+
101
+ - (BOOL )hasDelegate : (nonnull NSObject <FlutterAppLifecycleDelegate>*)delegate {
102
+ return std::find (_delegates.begin (), _delegates.end (), (__bridge void *)delegate) !=
103
+ _delegates.end ();
104
+ }
105
+
106
+ @end
107
+
108
+ #pragma mark -
109
+
110
+ @interface FakeAppDelegatePlugin : NSObject <FlutterPlugin>
111
+ @end
112
+
113
+ @implementation FakeAppDelegatePlugin
114
+ + (void )registerWithRegistrar : (id <FlutterPluginRegistrar>)registrar {
115
+ }
116
+ @end
117
+
118
+ #pragma mark -
119
+
62
120
namespace flutter ::testing {
63
121
64
122
TEST_F (FlutterEngineTest, CanLaunch) {
@@ -515,7 +573,7 @@ - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication* _Nonnu
515
573
ICUDataPath: [fixtures stringByAppendingString: @" /icudtl.dat" ]];
516
574
FlutterEngine* engine = [[FlutterEngine alloc ] initWithName: @" test"
517
575
project: project
518
- allowHeadlessExecution: true ];
576
+ allowHeadlessExecution: YES ];
519
577
weakEngine = engine;
520
578
binaryMessenger = engine.binaryMessenger ;
521
579
}
@@ -539,7 +597,7 @@ - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication* _Nonnu
539
597
ICUDataPath: [fixtures stringByAppendingString: @" /icudtl.dat" ]];
540
598
FlutterEngine* engine = [[FlutterEngine alloc ] initWithName: @" test"
541
599
project: project
542
- allowHeadlessExecution: true ];
600
+ allowHeadlessExecution: YES ];
543
601
id <FlutterPluginRegistrar> registrar = [engine registrarForPlugin: @" MyPlugin" ];
544
602
textureRegistry = registrar.textures ;
545
603
}
@@ -956,6 +1014,42 @@ - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication* _Nonnu
956
1014
[mockApplication stopMocking ];
957
1015
}
958
1016
1017
+ TEST_F (FlutterEngineTest, ForwardsPluginDelegateRegistration) {
1018
+ id <NSApplicationDelegate > previousDelegate = [[NSApplication sharedApplication ] delegate ];
1019
+ FakeLifecycleProvider* fakeAppDelegate = [[FakeLifecycleProvider alloc ] init ];
1020
+ [NSApplication sharedApplication ].delegate = fakeAppDelegate;
1021
+
1022
+ FakeAppDelegatePlugin* plugin = [[FakeAppDelegatePlugin alloc ] init ];
1023
+ FlutterEngine* engine = CreateMockFlutterEngine (nil );
1024
+
1025
+ [[engine registrarForPlugin: @" TestPlugin" ] addApplicationDelegate: plugin];
1026
+
1027
+ EXPECT_TRUE ([fakeAppDelegate hasDelegate: plugin]);
1028
+
1029
+ [NSApplication sharedApplication ].delegate = previousDelegate;
1030
+ }
1031
+
1032
+ TEST_F (FlutterEngineTest, UnregistersPluginsOnEngineDestruction) {
1033
+ id <NSApplicationDelegate > previousDelegate = [[NSApplication sharedApplication ] delegate ];
1034
+ FakeLifecycleProvider* fakeAppDelegate = [[FakeLifecycleProvider alloc ] init ];
1035
+ [NSApplication sharedApplication ].delegate = fakeAppDelegate;
1036
+
1037
+ FakeAppDelegatePlugin* plugin = [[FakeAppDelegatePlugin alloc ] init ];
1038
+
1039
+ @autoreleasepool {
1040
+ FlutterEngine* engine = [[FlutterEngine alloc ] initWithName: @" test" project: nil ];
1041
+
1042
+ [[engine registrarForPlugin: @" TestPlugin" ] addApplicationDelegate: plugin];
1043
+ EXPECT_TRUE ([fakeAppDelegate hasDelegate: plugin]);
1044
+ }
1045
+
1046
+ // When the engine is released, it should unregister any plugins it had
1047
+ // registered on its behalf.
1048
+ EXPECT_FALSE ([fakeAppDelegate hasDelegate: plugin]);
1049
+
1050
+ [NSApplication sharedApplication ].delegate = previousDelegate;
1051
+ }
1052
+
959
1053
} // namespace flutter::testing
960
1054
961
1055
// NOLINTEND(clang-analyzer-core.StackAddressEscape)
0 commit comments