Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
FlutterWindowControllerTest.mm
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
7
14#import "third_party/googletest/googletest/include/gtest/gtest.h"
15
16namespace flutter::testing {
17
19 public:
21
22 void SetUp() {
24
25 [GetFlutterEngine() runWithEntrypoint:@"testWindowController"];
26
27 signalled_ = false;
28
29 AddNativeCallback("SignalNativeTest", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
31 signalled_ = true;
32 }));
33
34 while (!signalled_) {
35 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
36 }
37 }
38
39 void TearDown() {
40 [GetFlutterEngine().windowController closeAllWindows];
42 }
43
44 protected:
46 if (isolate_) {
47 return *isolate_;
48 } else {
49 FML_LOG(ERROR) << "Isolate is not set.";
51 }
52 }
53
54 std::optional<flutter::Isolate> isolate_;
56};
57
58class FlutterWindowControllerRetainTest : public ::testing::Test {};
59
60TEST_F(FlutterWindowControllerTest, CreateRegularWindow) {
62 .has_size = true,
63 .size = {.width = 800, .height = 600},
64 .on_should_close = [] {},
65 .on_will_close = [] {},
66 .notify_listeners = [] {},
67 };
68
69 FlutterEngine* engine = GetFlutterEngine();
70 int64_t engineId = reinterpret_cast<int64_t>(engine);
71
72 {
73 IsolateScope isolate_scope(isolate());
74 int64_t handle = InternalFlutter_WindowController_CreateRegularWindow(engineId, &request);
75 EXPECT_EQ(handle, 1);
76
77 FlutterViewController* viewController = [engine viewControllerForIdentifier:handle];
78 EXPECT_NE(viewController, nil);
79 CGSize size = viewController.view.frame.size;
80 EXPECT_EQ(size.width, 800);
81 EXPECT_EQ(size.height, 600);
82 }
83}
84
85TEST_F(FlutterWindowControllerTest, CreateTooltipWindow) {
86 IsolateScope isolate_scope(isolate());
87 FlutterEngine* engine = GetFlutterEngine();
88 int64_t engineId = reinterpret_cast<int64_t>(engine);
89
90 auto request = FlutterWindowCreationRequest{
91 .has_size = true,
92 .size = {.width = 800, .height = 600},
93 .on_should_close = [] {},
94 .on_will_close = [] {},
95 .notify_listeners = [] {},
96 };
97 int64_t parentViewId = InternalFlutter_WindowController_CreateRegularWindow(engineId, &request);
98 EXPECT_EQ(parentViewId, 1);
99
100 auto position_callback = [](const FlutterWindowSize& child_size,
101 const FlutterWindowRect& parent_rect,
102 const FlutterWindowRect& output_rect) -> FlutterWindowRect* {
103 FlutterWindowRect* rect = static_cast<FlutterWindowRect*>(malloc(sizeof(FlutterWindowRect)));
104 rect->left = parent_rect.left + 10;
105 rect->top = parent_rect.top + 10;
106 rect->width = child_size.width;
107 rect->height = child_size.height;
108 return rect;
109 };
110
112 .has_constraints = true,
113 .constraints{
114 .max_width = 1000,
115 .max_height = 1000,
116 },
117 .parent_view_id = parentViewId,
118 .on_should_close = [] {},
119 .on_will_close = [] {},
120 .notify_listeners = [] {},
121 .on_get_window_position = position_callback,
122 };
123
124 const int64_t tooltipViewId =
126 EXPECT_NE(tooltipViewId, 0);
127}
128
129TEST_F(FlutterWindowControllerRetainTest, WindowControllerDoesNotRetainEngine) {
131 .has_size = true,
132 .size = {.width = 800, .height = 600},
133 .on_should_close = [] {},
134 .on_will_close = [] {},
135 .notify_listeners = [] {},
136 };
137
138 __weak FlutterEngine* weakEngine = nil;
139 @autoreleasepool {
140 NSString* fixtures = @(flutter::testing::GetFixturesPath());
141 NSLog(@"Fixtures path: %@", fixtures);
142 FlutterDartProject* project = [[FlutterDartProject alloc]
143 initWithAssetsPath:fixtures
144 ICUDataPath:[fixtures stringByAppendingString:@"/icudtl.dat"]];
145
146 static std::optional<flutter::Isolate> isolate;
147 isolate = std::nullopt;
148
149 project.rootIsolateCreateCallback = [](void*) { isolate = flutter::Isolate::Current(); };
150 FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"test"
151 project:project
152 allowHeadlessExecution:YES];
153 weakEngine = engine;
154 [engine runWithEntrypoint:@"testWindowControllerRetainCycle"];
155
156 int64_t engineId = reinterpret_cast<int64_t>(engine);
157
158 {
159 FML_DCHECK(isolate.has_value());
160 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
161 IsolateScope isolateScope(*isolate);
162 int64_t handle = InternalFlutter_WindowController_CreateRegularWindow(engineId, &request);
163 EXPECT_EQ(handle, 1);
164 }
165
166 [engine.windowController closeAllWindows];
167 [engine shutDownEngine];
168 }
169 EXPECT_EQ(weakEngine, nil);
170}
171
172TEST_F(FlutterWindowControllerTest, DestroyRegularWindow) {
174 .has_size = true,
175 .size = {.width = 800, .height = 600},
176 .on_should_close = [] {},
177 .on_will_close = [] {},
178 .notify_listeners = [] {},
179 };
180
181 FlutterEngine* engine = GetFlutterEngine();
182 int64_t engine_id = reinterpret_cast<int64_t>(engine);
183
184 IsolateScope isolate_scope(isolate());
185 int64_t handle = InternalFlutter_WindowController_CreateRegularWindow(engine_id, &request);
186 FlutterViewController* viewController = [engine viewControllerForIdentifier:handle];
187
188 InternalFlutter_Window_Destroy(engine_id, (__bridge void*)viewController.view.window);
189 viewController = [engine viewControllerForIdentifier:handle];
190 EXPECT_EQ(viewController, nil);
191}
192
193TEST_F(FlutterWindowControllerTest, InternalFlutterWindowGetHandle) {
195 .has_size = true,
196 .size = {.width = 800, .height = 600},
197 .on_should_close = [] {},
198 .on_will_close = [] {},
199 .notify_listeners = [] {},
200 };
201
202 FlutterEngine* engine = GetFlutterEngine();
203 int64_t engine_id = reinterpret_cast<int64_t>(engine);
204
205 IsolateScope isolate_scope(isolate());
206 int64_t handle = InternalFlutter_WindowController_CreateRegularWindow(engine_id, &request);
207 FlutterViewController* viewController = [engine viewControllerForIdentifier:handle];
208
209 void* window_handle = InternalFlutter_Window_GetHandle(engine_id, handle);
210 EXPECT_EQ(window_handle, (__bridge void*)viewController.view.window);
211}
212
215 .has_size = true,
216 .size = {.width = 800, .height = 600},
217 .on_should_close = [] {},
218 .on_will_close = [] {},
219 .notify_listeners = [] {},
220 };
221
222 FlutterEngine* engine = GetFlutterEngine();
223 int64_t engine_id = reinterpret_cast<int64_t>(engine);
224
225 IsolateScope isolate_scope(isolate());
226 int64_t handle = InternalFlutter_WindowController_CreateRegularWindow(engine_id, &request);
227
228 FlutterViewController* viewController = [engine viewControllerForIdentifier:handle];
229 NSWindow* window = viewController.view.window;
230 void* windowHandle = (__bridge void*)window;
231
232 EXPECT_EQ(window.zoomed, NO);
233 EXPECT_EQ(window.miniaturized, NO);
234 EXPECT_EQ(window.styleMask & NSWindowStyleMaskFullScreen, 0u);
235
236 InternalFlutter_Window_SetMaximized(windowHandle, true);
237 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.5, false);
238 EXPECT_EQ(window.zoomed, YES);
239
240 InternalFlutter_Window_SetMaximized(windowHandle, false);
241 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.5, false);
242 EXPECT_EQ(window.zoomed, NO);
243
244 // FullScreen toggle does not seem to work when the application is not run from a bundle.
245
247 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.5, false);
248 EXPECT_EQ(window.miniaturized, YES);
249}
250
251TEST_F(FlutterWindowControllerTest, ClosesAllWindowsOnEngineRestart) {
253 .has_size = true,
254 .size = {.width = 800, .height = 600},
255 .on_should_close = [] {},
256 .on_will_close = [] {},
257 .notify_listeners = [] {},
258 };
259 FlutterEngine* engine = GetFlutterEngine();
260 int64_t engine_id = reinterpret_cast<int64_t>(engine);
261
262 IsolateScope isolate_scope(isolate());
263
264 // Create multiple windows
265 int64_t handle1 = InternalFlutter_WindowController_CreateRegularWindow(engine_id, &request);
266 int64_t handle2 = InternalFlutter_WindowController_CreateRegularWindow(engine_id, &request);
267 int64_t handle3 = InternalFlutter_WindowController_CreateRegularWindow(engine_id, &request);
268
269 // Verify windows are created
270 FlutterViewController* viewController1 = [engine viewControllerForIdentifier:handle1];
271 FlutterViewController* viewController2 = [engine viewControllerForIdentifier:handle2];
272 FlutterViewController* viewController3 = [engine viewControllerForIdentifier:handle3];
273 EXPECT_NE(viewController1, nil);
274 EXPECT_NE(viewController2, nil);
275 EXPECT_NE(viewController3, nil);
276
277 // Close all windows on engine restart
278 [engine engineCallbackOnPreEngineRestart];
279
280 // Verify all windows are closed and view controllers are disposed
281 viewController1 = [engine viewControllerForIdentifier:handle1];
282 viewController2 = [engine viewControllerForIdentifier:handle2];
283 viewController3 = [engine viewControllerForIdentifier:handle3];
284 EXPECT_EQ(viewController1, nil);
285 EXPECT_EQ(viewController2, nil);
286 EXPECT_EQ(viewController3, nil);
287};
288
289TEST_F(FlutterWindowControllerTest, ViewMetricsRespectPositionCallbackConstraints) {
290 IsolateScope isolate_scope(isolate());
291 FlutterEngine* engine = GetFlutterEngine();
292 int64_t engineId = reinterpret_cast<int64_t>(engine);
293
294 // Create parent window.
295 auto parentRequest = FlutterWindowCreationRequest{
296 .has_size = true,
297 .size = {.width = 800, .height = 600},
298 .on_should_close = [] {},
299 .on_will_close = [] {},
300 .notify_listeners = [] {},
301 };
302 int64_t parentViewId =
304 EXPECT_EQ(parentViewId, 1);
305
306 auto position_callback = [](const FlutterWindowSize& child_size,
307 const FlutterWindowRect& parent_rect,
308 const FlutterWindowRect& output_rect) -> FlutterWindowRect* {
309 FlutterWindowRect* rect = static_cast<FlutterWindowRect*>(malloc(sizeof(FlutterWindowRect)));
310 rect->left = parent_rect.left;
311 rect->top = parent_rect.top;
312 rect->width = 500;
313 rect->height = 400;
314 return rect;
315 };
316
317 auto tooltipRequest = FlutterWindowCreationRequest{
318 .has_constraints = true,
319 .constraints{
320 .min_width = 0,
321 .min_height = 0,
322 .max_width = 1000,
323 .max_height = 1000,
324 },
325 .parent_view_id = parentViewId,
326 .on_should_close = [] {},
327 .on_will_close = [] {},
328 .notify_listeners = [] {},
329 .on_get_window_position = position_callback,
330 };
331
332 const int64_t tooltipViewId =
334 EXPECT_NE(tooltipViewId, 0);
335
336 FlutterViewController* viewController = [engine viewControllerForIdentifier:tooltipViewId];
337 FlutterView* flutterView = viewController.flutterView;
338
339 EXPECT_EQ(flutterView.sizedToContents, YES);
340
341 CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false);
342
343 [flutterView.sizingDelegate viewDidUpdateContents:flutterView withSize:NSMakeSize(1000, 1000)];
344
345 // The constraints from request are 1000x1000, but additional constraints came from the positioner
346 // and must be respected.
347 CGSize maxSize = flutterView.maximumContentSize;
348 EXPECT_LE(maxSize.width, 500);
349 EXPECT_LE(maxSize.height, 400);
350}
351} // namespace flutter::testing
FLUTTER_DARWIN_EXPORT int64_t InternalFlutter_WindowController_CreateRegularWindow(int64_t engine_id, const FlutterWindowCreationRequest *request)
FLUTTER_DARWIN_EXPORT void * InternalFlutter_Window_GetHandle(int64_t engine_id, FlutterViewIdentifier view_id)
FLUTTER_DARWIN_EXPORT int64_t InternalFlutter_WindowController_CreateTooltipWindow(int64_t engine_id, const FlutterWindowCreationRequest *request)
FLUTTER_DARWIN_EXPORT void InternalFlutter_Window_SetMaximized(void *window, bool maximized)
FLUTTER_DARWIN_EXPORT void InternalFlutter_Window_Destroy(int64_t engine_id, void *window)
FLUTTER_DARWIN_EXPORT void InternalFlutter_Window_Minimize(void *window)
static Isolate Current()
void AddNativeCallback(const char *name, Dart_NativeFunction function)
GLFWwindow * window
Definition main.cc:60
FlutterEngine engine
Definition main.cc:84
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
#define FML_LOG(severity)
Definition logging.h:101
#define FML_UNREACHABLE()
Definition logging.h:128
#define FML_DCHECK(condition)
Definition logging.h:122
void(* rootIsolateCreateCallback)(void *_Nullable)
FlutterViewController * viewController
TEST_F(DisplayListTest, Defaults)
const char * GetFixturesPath()
Returns the directory containing the test fixture for the target if this target has fixtures configur...
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
#define CREATE_NATIVE_ENTRY(native_entry)