| Flutter Windows Embedder |

flutter_windows_engine_unittests.cc
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
5 #include "flutter/shell/platform/windows/flutter_windows_engine.h"
6
7 #include "flutter/fml/logging.h"
8 #include "flutter/fml/macros.h"
9 #include "flutter/shell/platform/embedder/embedder.h"
10 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
11 #include "flutter/shell/platform/windows/flutter_windows_view.h"
12 #include "flutter/shell/platform/windows/public/flutter_windows.h"
13 #include "flutter/shell/platform/windows/testing/egl/mock_manager.h"
14 #include "flutter/shell/platform/windows/testing/engine_modifier.h"
15 #include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
16 #include "flutter/shell/platform/windows/testing/mock_platform_view_manager.h"
17 #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
18 #include "flutter/shell/platform/windows/testing/mock_windows_proc_table.h"
19 #include "flutter/shell/platform/windows/testing/test_keyboard.h"
20 #include "flutter/shell/platform/windows/testing/windows_test.h"
21 #include "flutter/shell/platform/windows/testing/windows_test_config_builder.h"
22 #include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
23 #include "fml/synchronization/waitable_event.h"
24 #include "gmock/gmock.h"
25 #include "gtest/gtest.h"
26
27 // winbase.h defines GetCurrentTime as a macro.
28 #undef GetCurrentTime
29
30 namespace flutter {
31 namespace testing {
32
33 using ::testing::NiceMock;
34 using ::testing::Return;
35
36 class FlutterWindowsEngineTest : public WindowsTest {};
37
38 // The engine can be run without any views.
39 TEST_F(FlutterWindowsEngineTest, RunHeadless) {
40 FlutterWindowsEngineBuilder builder{GetContext()};
41 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
42
43 EngineModifier modifier(engine.get());
44 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
45
46 ASSERT_TRUE(engine->Run());
47 ASSERT_EQ(engine->view(kImplicitViewId), nullptr);
48 ASSERT_EQ(engine->view(123), nullptr);
49 }
50
51 TEST_F(FlutterWindowsEngineTest, RunDoesExpectedInitialization) {
52 FlutterWindowsEngineBuilder builder{GetContext()};
53 builder.AddDartEntrypointArgument("arg1");
54 builder.AddDartEntrypointArgument("arg2");
55
56 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
57 EngineModifier modifier(engine.get());
58
59 // The engine should be run with expected configuration values.
60 bool run_called = false;
61 modifier.embedder_api().Run = MOCK_ENGINE_PROC(
62 Run, ([&run_called, engine_instance = engine.get()](
63 size_t version, const FlutterRendererConfig* config,
64 const FlutterProjectArgs* args, void* user_data,
65 FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
66 run_called = true;
67 *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
68
69 EXPECT_EQ(version, FLUTTER_ENGINE_VERSION);
70 EXPECT_NE(config, nullptr);
71 // We have an EGL manager, so this should be using OpenGL.
72 EXPECT_EQ(config->type, kOpenGL);
73 EXPECT_EQ(user_data, engine_instance);
74 // Spot-check arguments.
75 EXPECT_NE(args->assets_path, nullptr);
76 EXPECT_NE(args->icu_data_path, nullptr);
77 EXPECT_EQ(args->dart_entrypoint_argc, 2U);
78 EXPECT_EQ(strcmp(args->dart_entrypoint_argv[0], "arg1"), 0);
79 EXPECT_EQ(strcmp(args->dart_entrypoint_argv[1], "arg2"), 0);
80 EXPECT_NE(args->platform_message_callback, nullptr);
81 EXPECT_NE(args->custom_task_runners, nullptr);
82 EXPECT_NE(args->custom_task_runners->thread_priority_setter, nullptr);
83 EXPECT_EQ(args->custom_dart_entrypoint, nullptr);
84 EXPECT_NE(args->vsync_callback, nullptr);
85 EXPECT_EQ(args->update_semantics_callback, nullptr);
86 EXPECT_NE(args->update_semantics_callback2, nullptr);
87 EXPECT_EQ(args->update_semantics_node_callback, nullptr);
88 EXPECT_EQ(args->update_semantics_custom_action_callback, nullptr);
89
90 args->custom_task_runners->thread_priority_setter(
91 FlutterThreadPriority::kRaster);
92 EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
93 THREAD_PRIORITY_ABOVE_NORMAL);
94 return kSuccess;
95 }));
96 // Accessibility updates must do nothing when the embedder engine is mocked
97 modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
98 UpdateAccessibilityFeatures,
99 [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
100 FlutterAccessibilityFeature flags) { return kSuccess; });
101
102 // It should send locale info.
103 bool update_locales_called = false;
104 modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
105 UpdateLocales,
106 ([&update_locales_called](auto engine, const FlutterLocale** locales,
107 size_t locales_count) {
108 update_locales_called = true;
109
110 EXPECT_GT(locales_count, 0);
111 EXPECT_NE(locales, nullptr);
112
113 return kSuccess;
114 }));
115
116 // And it should send initial settings info.
117 bool settings_message_sent = false;
118 modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
119 SendPlatformMessage,
120 ([&settings_message_sent](auto engine, auto message) {
121 if (std::string(message->channel) == std::string("flutter/settings")) {
122 settings_message_sent = true;
123 }
124
125 return kSuccess;
126 }));
127
128 // And it should send display info.
129 bool notify_display_update_called = false;
130 modifier.SetFrameInterval(16600000); // 60 fps.
131 modifier.embedder_api().NotifyDisplayUpdate = MOCK_ENGINE_PROC(
132 NotifyDisplayUpdate,
133 ([&notify_display_update_called, engine_instance = engine.get()](
134 FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
135 const FlutterEngineDisplaysUpdateType update_type,
136 const FlutterEngineDisplay* embedder_displays,
137 size_t display_count) {
138 EXPECT_EQ(update_type, kFlutterEngineDisplaysUpdateTypeStartup);
139 EXPECT_EQ(display_count, 1);
140
141 FlutterEngineDisplay display = embedder_displays[0];
142
143 EXPECT_EQ(display.display_id, 0);
144 EXPECT_EQ(display.single_display, true);
145 EXPECT_EQ(std::floor(display.refresh_rate), 60.0);
146
147 notify_display_update_called = true;
148 return kSuccess;
149 }));
150
151 // Set the EGL manager to !nullptr to test ANGLE rendering.
152 modifier.SetEGLManager(std::make_unique<egl::MockManager>());
153
154 engine->Run();
155
156 EXPECT_TRUE(run_called);
157 EXPECT_TRUE(update_locales_called);
158 EXPECT_TRUE(settings_message_sent);
159 EXPECT_TRUE(notify_display_update_called);
160
161 // Ensure that deallocation doesn't call the actual Shutdown with the bogus
162 // engine pointer that the overridden Run returned.
163 modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
164 modifier.ReleaseEGLManager();
165 }
166
167 TEST_F(FlutterWindowsEngineTest, ConfiguresFrameVsync) {
168 FlutterWindowsEngineBuilder builder{GetContext()};
169 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
170 EngineModifier modifier(engine.get());
171 bool on_vsync_called = false;
172
173 modifier.embedder_api().GetCurrentTime =
174 MOCK_ENGINE_PROC(GetCurrentTime, ([]() -> uint64_t { return 1; }));
175 modifier.embedder_api().OnVsync = MOCK_ENGINE_PROC(
176 OnVsync,
177 ([&on_vsync_called, engine_instance = engine.get()](
178 FLUTTER_API_SYMBOL(FlutterEngine) engine, intptr_t baton,
179 uint64_t frame_start_time_nanos, uint64_t frame_target_time_nanos) {
180 EXPECT_EQ(baton, 1);
181 EXPECT_EQ(frame_start_time_nanos, 16600000);
182 EXPECT_EQ(frame_target_time_nanos, 33200000);
183 on_vsync_called = true;
184 return kSuccess;
185 }));
186 modifier.SetStartTime(0);
187 modifier.SetFrameInterval(16600000);
188
189 engine->OnVsync(1);
190
191 EXPECT_TRUE(on_vsync_called);
192 }
193
194 TEST_F(FlutterWindowsEngineTest, RunWithoutANGLEUsesSoftware) {
195 FlutterWindowsEngineBuilder builder{GetContext()};
196 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
197 EngineModifier modifier(engine.get());
198
199 modifier.embedder_api().NotifyDisplayUpdate =
200 MOCK_ENGINE_PROC(NotifyDisplayUpdate,
201 ([engine_instance = engine.get()](
202 FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
203 const FlutterEngineDisplaysUpdateType update_type,
204 const FlutterEngineDisplay* embedder_displays,
205 size_t display_count) { return kSuccess; }));
206
207 // The engine should be run with expected configuration values.
208 bool run_called = false;
209 modifier.embedder_api().Run = MOCK_ENGINE_PROC(
210 Run, ([&run_called, engine_instance = engine.get()](
211 size_t version, const FlutterRendererConfig* config,
212 const FlutterProjectArgs* args, void* user_data,
213 FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
214 run_called = true;
215 *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
216 // We don't have an EGL Manager, so we should be using software.
217 EXPECT_EQ(config->type, kSoftware);
218 return kSuccess;
219 }));
220 // Accessibility updates must do nothing when the embedder engine is mocked
221 modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
222 UpdateAccessibilityFeatures,
223 [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
224 FlutterAccessibilityFeature flags) { return kSuccess; });
225
226 // Stub out UpdateLocales and SendPlatformMessage as we don't have a fully
227 // initialized engine instance.
228 modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
229 UpdateLocales, ([](auto engine, const FlutterLocale** locales,
230 size_t locales_count) { return kSuccess; }));
231 modifier.embedder_api().SendPlatformMessage =
232 MOCK_ENGINE_PROC(SendPlatformMessage,
233 ([](auto engine, auto message) { return kSuccess; }));
234
235 // Set the EGL manager to nullptr to test software fallback path.
236 modifier.SetEGLManager(nullptr);
237
238 engine->Run();
239
240 EXPECT_TRUE(run_called);
241
242 // Ensure that deallocation doesn't call the actual Shutdown with the bogus
243 // engine pointer that the overridden Run returned.
244 modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
245 }
246
247 TEST_F(FlutterWindowsEngineTest, RunWithoutANGLEOnImpellerFailsToStart) {
248 FlutterWindowsEngineBuilder builder{GetContext()};
249 builder.SetSwitches({"--enable-impeller=true"});
250 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
251 EngineModifier modifier(engine.get());
252
253 modifier.embedder_api().NotifyDisplayUpdate =
254 MOCK_ENGINE_PROC(NotifyDisplayUpdate,
255 ([engine_instance = engine.get()](
256 FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
257 const FlutterEngineDisplaysUpdateType update_type,
258 const FlutterEngineDisplay* embedder_displays,
259 size_t display_count) { return kSuccess; }));
260
261 // Accessibility updates must do nothing when the embedder engine is mocked
262 modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
263 UpdateAccessibilityFeatures,
264 [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
265 FlutterAccessibilityFeature flags) { return kSuccess; });
266
267 // Stub out UpdateLocales and SendPlatformMessage as we don't have a fully
268 // initialized engine instance.
269 modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
270 UpdateLocales, ([](auto engine, const FlutterLocale** locales,
271 size_t locales_count) { return kSuccess; }));
272 modifier.embedder_api().SendPlatformMessage =
273 MOCK_ENGINE_PROC(SendPlatformMessage,
274 ([](auto engine, auto message) { return kSuccess; }));
275
276 // Set the EGL manager to nullptr to test software fallback path.
277 modifier.SetEGLManager(nullptr);
278
279 EXPECT_FALSE(engine->Run());
280 }
281
282 TEST_F(FlutterWindowsEngineTest, SendPlatformMessageWithoutResponse) {
283 FlutterWindowsEngineBuilder builder{GetContext()};
284 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
285 EngineModifier modifier(engine.get());
286
287 const char* channel = "test";
288 const std::vector<uint8_t> test_message = {1, 2, 3, 4};
289
290 // Without a response, SendPlatformMessage should be a simple pass-through.
291 bool called = false;
292 modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
293 SendPlatformMessage, ([&called, test_message](auto engine, auto message) {
294 called = true;
295 EXPECT_STREQ(message->channel, "test");
296 EXPECT_EQ(message->message_size, test_message.size());
297 EXPECT_EQ(memcmp(message->message, test_message.data(),
298 message->message_size),
299 0);
300 EXPECT_EQ(message->response_handle, nullptr);
301 return kSuccess;
302 }));
303
304 engine->SendPlatformMessage(channel, test_message.data(), test_message.size(),
305 nullptr, nullptr);
306 EXPECT_TRUE(called);
307 }
308
309 TEST_F(FlutterWindowsEngineTest, PlatformMessageRoundTrip) {
310 FlutterWindowsEngineBuilder builder{GetContext()};
311 builder.SetDartEntrypoint("hiPlatformChannels");
312
313 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
314 EngineModifier modifier(engine.get());
315 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
316
317 auto binary_messenger =
318 std::make_unique<BinaryMessengerImpl>(engine->messenger());
319
320 engine->Run();
321 bool did_call_callback = false;
322 bool did_call_reply = false;
323 bool did_call_dart_reply = false;
324 std::string channel = "hi";
325 binary_messenger->SetMessageHandler(
326 channel,
327 [&did_call_callback, &did_call_dart_reply](
328 const uint8_t* message, size_t message_size, BinaryReply reply) {
329 if (message_size == 5) {
330 EXPECT_EQ(message[0], static_cast<uint8_t>('h'));
331 char response[] = {'b', 'y', 'e'};
332 reply(reinterpret_cast<uint8_t*>(response), 3);
333 did_call_callback = true;
334 } else {
335 EXPECT_EQ(message_size, 3);
336 EXPECT_EQ(message[0], static_cast<uint8_t>('b'));
337 did_call_dart_reply = true;
338 }
339 });
340 char payload[] = {'h', 'e', 'l', 'l', 'o'};
341 binary_messenger->Send(
342 channel, reinterpret_cast<uint8_t*>(payload), 5,
343 [&did_call_reply](const uint8_t* reply, size_t reply_size) {
344 EXPECT_EQ(reply_size, 5);
345 EXPECT_EQ(reply[0], static_cast<uint8_t>('h'));
346 did_call_reply = true;
347 });
348 // Rely on timeout mechanism in CI.
349 while (!did_call_callback || !did_call_reply || !did_call_dart_reply) {
350 engine->task_runner()->ProcessTasks();
351 }
352 }
353
354 TEST_F(FlutterWindowsEngineTest, PlatformMessageRespondOnDifferentThread) {
355 FlutterWindowsEngineBuilder builder{GetContext()};
356 builder.SetDartEntrypoint("hiPlatformChannels");
357
358 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
359
360 EngineModifier modifier(engine.get());
361 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
362
363 auto binary_messenger =
364 std::make_unique<BinaryMessengerImpl>(engine->messenger());
365
366 engine->Run();
367 bool did_call_callback = false;
368 bool did_call_reply = false;
369 bool did_call_dart_reply = false;
370 std::string channel = "hi";
371 std::unique_ptr<std::thread> reply_thread;
372 binary_messenger->SetMessageHandler(
373 channel,
374 [&did_call_callback, &did_call_dart_reply, &reply_thread](
375 const uint8_t* message, size_t message_size, BinaryReply reply) {
376 if (message_size == 5) {
377 EXPECT_EQ(message[0], static_cast<uint8_t>('h'));
378 reply_thread.reset(new std::thread([reply = std::move(reply)]() {
379 char response[] = {'b', 'y', 'e'};
380 reply(reinterpret_cast<uint8_t*>(response), 3);
381 }));
382 did_call_callback = true;
383 } else {
384 EXPECT_EQ(message_size, 3);
385 EXPECT_EQ(message[0], static_cast<uint8_t>('b'));
386 did_call_dart_reply = true;
387 }
388 });
389 char payload[] = {'h', 'e', 'l', 'l', 'o'};
390 binary_messenger->Send(
391 channel, reinterpret_cast<uint8_t*>(payload), 5,
392 [&did_call_reply](const uint8_t* reply, size_t reply_size) {
393 EXPECT_EQ(reply_size, 5);
394 EXPECT_EQ(reply[0], static_cast<uint8_t>('h'));
395 did_call_reply = true;
396 });
397 // Rely on timeout mechanism in CI.
398 while (!did_call_callback || !did_call_reply || !did_call_dart_reply) {
399 engine->task_runner()->ProcessTasks();
400 }
401 ASSERT_TRUE(reply_thread);
402 reply_thread->join();
403 }
404
405 TEST_F(FlutterWindowsEngineTest, SendPlatformMessageWithResponse) {
406 FlutterWindowsEngineBuilder builder{GetContext()};
407 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
408 EngineModifier modifier(engine.get());
409
410 const char* channel = "test";
411 const std::vector<uint8_t> test_message = {1, 2, 3, 4};
412 auto* dummy_response_handle =
413 reinterpret_cast<FlutterPlatformMessageResponseHandle*>(5);
414 const FlutterDesktopBinaryReply reply_handler = [](auto... args) {};
415 void* reply_user_data = reinterpret_cast<void*>(6);
416
417 // When a response is requested, a handle should be created, passed as part
418 // of the message, and then released.
419 bool create_response_handle_called = false;
420 modifier.embedder_api().PlatformMessageCreateResponseHandle =
421 MOCK_ENGINE_PROC(
422 PlatformMessageCreateResponseHandle,
423 ([&create_response_handle_called, &reply_handler, reply_user_data,
424 dummy_response_handle](auto engine, auto reply, auto user_data,
425 auto response_handle) {
426 create_response_handle_called = true;
427 EXPECT_EQ(reply, reply_handler);
428 EXPECT_EQ(user_data, reply_user_data);
429 EXPECT_NE(response_handle, nullptr);
430 *response_handle = dummy_response_handle;
431 return kSuccess;
432 }));
433 bool release_response_handle_called = false;
434 modifier.embedder_api().PlatformMessageReleaseResponseHandle =
435 MOCK_ENGINE_PROC(
436 PlatformMessageReleaseResponseHandle,
437 ([&release_response_handle_called, dummy_response_handle](
438 auto engine, auto response_handle) {
439 release_response_handle_called = true;
440 EXPECT_EQ(response_handle, dummy_response_handle);
441 return kSuccess;
442 }));
443 bool send_message_called = false;
444 modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
445 SendPlatformMessage, ([&send_message_called, test_message,
446 dummy_response_handle](auto engine, auto message) {
447 send_message_called = true;
448 EXPECT_STREQ(message->channel, "test");
449 EXPECT_EQ(message->message_size, test_message.size());
450 EXPECT_EQ(memcmp(message->message, test_message.data(),
451 message->message_size),
452 0);
453 EXPECT_EQ(message->response_handle, dummy_response_handle);
454 return kSuccess;
455 }));
456
457 engine->SendPlatformMessage(channel, test_message.data(), test_message.size(),
458 reply_handler, reply_user_data);
459 EXPECT_TRUE(create_response_handle_called);
460 EXPECT_TRUE(release_response_handle_called);
461 EXPECT_TRUE(send_message_called);
462 }
463
464 TEST_F(FlutterWindowsEngineTest, DispatchSemanticsAction) {
465 FlutterWindowsEngineBuilder builder{GetContext()};
466 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
467 EngineModifier modifier(engine.get());
468
469 bool called = false;
470 std::string message = "Hello";
471 modifier.embedder_api().DispatchSemanticsAction = MOCK_ENGINE_PROC(
472 DispatchSemanticsAction,
473 ([&called, &message](auto engine, auto target, auto action, auto data,
474 auto data_length) {
475 called = true;
476 EXPECT_EQ(target, 42);
477 EXPECT_EQ(action, kFlutterSemanticsActionDismiss);
478 EXPECT_EQ(memcmp(data, message.c_str(), message.size()), 0);
479 EXPECT_EQ(data_length, message.size());
480 return kSuccess;
481 }));
482
483 auto data = fml::MallocMapping::Copy(message.c_str(), message.size());
484 engine->DispatchSemanticsAction(42, kFlutterSemanticsActionDismiss,
485 std::move(data));
486 EXPECT_TRUE(called);
487 }
488
489 TEST_F(FlutterWindowsEngineTest, SetsThreadPriority) {
490 WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kBackground);
491 EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
492 THREAD_PRIORITY_BELOW_NORMAL);
493
494 WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kDisplay);
495 EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
496 THREAD_PRIORITY_ABOVE_NORMAL);
497
498 WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kRaster);
499 EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
500 THREAD_PRIORITY_ABOVE_NORMAL);
501
502 // FlutterThreadPriority::kNormal does not change thread priority, reset to 0
503 // here.
504 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
505
506 WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kNormal);
507 EXPECT_EQ(GetThreadPriority(GetCurrentThread()), THREAD_PRIORITY_NORMAL);
508 }
509
510 TEST_F(FlutterWindowsEngineTest, AddPluginRegistrarDestructionCallback) {
511 FlutterWindowsEngineBuilder builder{GetContext()};
512 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
513 EngineModifier modifier(engine.get());
514
515 MockEmbedderApiForKeyboard(modifier,
516 std::make_shared<MockKeyResponseController>());
517
518 engine->Run();
519
520 // Verify that destruction handlers don't overwrite each other.
521 int result1 = 0;
522 int result2 = 0;
523 engine->AddPluginRegistrarDestructionCallback(
524 [](FlutterDesktopPluginRegistrarRef ref) {
525 auto result = reinterpret_cast<int*>(ref);
526 *result = 1;
527 },
528 reinterpret_cast<FlutterDesktopPluginRegistrarRef>(&result1));
529 engine->AddPluginRegistrarDestructionCallback(
530 [](FlutterDesktopPluginRegistrarRef ref) {
531 auto result = reinterpret_cast<int*>(ref);
532 *result = 2;
533 },
534 reinterpret_cast<FlutterDesktopPluginRegistrarRef>(&result2));
535
536 engine->Stop();
537 EXPECT_EQ(result1, 1);
538 EXPECT_EQ(result2, 2);
539 }
540
541 TEST_F(FlutterWindowsEngineTest, ScheduleFrame) {
542 FlutterWindowsEngineBuilder builder{GetContext()};
543 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
544 EngineModifier modifier(engine.get());
545
546 bool called = false;
547 modifier.embedder_api().ScheduleFrame =
548 MOCK_ENGINE_PROC(ScheduleFrame, ([&called](auto engine) {
549 called = true;
550 return kSuccess;
551 }));
552
553 engine->ScheduleFrame();
554 EXPECT_TRUE(called);
555 }
556
557 TEST_F(FlutterWindowsEngineTest, SetNextFrameCallback) {
558 FlutterWindowsEngineBuilder builder{GetContext()};
559 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
560 EngineModifier modifier(engine.get());
561
562 bool called = false;
563 modifier.embedder_api().SetNextFrameCallback = MOCK_ENGINE_PROC(
564 SetNextFrameCallback, ([&called](auto engine, auto callback, auto data) {
565 called = true;
566 return kSuccess;
567 }));
568
569 engine->SetNextFrameCallback([]() {});
570 EXPECT_TRUE(called);
571 }
572
573 TEST_F(FlutterWindowsEngineTest, GetExecutableName) {
574 FlutterWindowsEngineBuilder builder{GetContext()};
575 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
576 EXPECT_EQ(engine->GetExecutableName(), "flutter_windows_unittests.exe");
577 }
578
579 // Ensure that after setting or resetting the high contrast feature,
580 // the corresponding status flag can be retrieved from the engine.
581 TEST_F(FlutterWindowsEngineTest, UpdateHighContrastFeature) {
582 auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
583 EXPECT_CALL(*windows_proc_table, GetHighContrastEnabled)
584 .WillOnce(Return(true))
585 .WillOnce(Return(false));
586
587 FlutterWindowsEngineBuilder builder{GetContext()};
588 builder.SetWindowsProcTable(windows_proc_table);
589 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
590 EngineModifier modifier(engine.get());
591
592 std::optional<FlutterAccessibilityFeature> engine_flags;
593 modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
594 UpdateAccessibilityFeatures, ([&engine_flags](auto engine, auto flags) {
595 engine_flags = flags;
596 return kSuccess;
597 }));
598 modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
599 SendPlatformMessage,
600 [](auto engine, const auto message) { return kSuccess; });
601
602 // 1: High contrast is enabled.
603 engine->UpdateHighContrastMode();
604
605 EXPECT_TRUE(engine->high_contrast_enabled());
606 EXPECT_TRUE(engine_flags.has_value());
607 EXPECT_TRUE(
608 engine_flags.value() &
609 FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast);
610
611 // 2: High contrast is disabled.
612 engine_flags.reset();
613 engine->UpdateHighContrastMode();
614
615 EXPECT_FALSE(engine->high_contrast_enabled());
616 EXPECT_TRUE(engine_flags.has_value());
617 EXPECT_FALSE(
618 engine_flags.value() &
619 FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast);
620 }
621
622 TEST_F(FlutterWindowsEngineTest, PostRasterThreadTask) {
623 FlutterWindowsEngineBuilder builder{GetContext()};
624 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
625 EngineModifier modifier(engine.get());
626
627 modifier.embedder_api().PostRenderThreadTask = MOCK_ENGINE_PROC(
628 PostRenderThreadTask, ([](auto engine, auto callback, auto context) {
629 callback(context);
630 return kSuccess;
631 }));
632
633 bool called = false;
634 engine->PostRasterThreadTask([&called]() { called = true; });
635
636 EXPECT_TRUE(called);
637 }
638
639 class MockFlutterWindowsView : public FlutterWindowsView {
640 public:
641 MockFlutterWindowsView(FlutterWindowsEngine* engine,
642 std::unique_ptr<WindowBindingHandler> wbh)
643 : FlutterWindowsView(kImplicitViewId, engine, std::move(wbh)) {}
644 ~MockFlutterWindowsView() {}
645
646 MOCK_METHOD(void,
647 NotifyWinEventWrapper,
648 (ui::AXPlatformNodeWin*, ax::mojom::Event),
649 (override));
650 MOCK_METHOD(HWND, GetWindowHandle, (), (const, override));
651
652 private:
653 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
654 };
655
656 // Verify the view is notified of accessibility announcements.
657 TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncement) {
658 auto& context = GetContext();
659 WindowsConfigBuilder builder{context};
660 builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
661
662 bool done = false;
663 auto native_entry =
664 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
665 context.AddNativeFunction("Signal", native_entry);
666
667 EnginePtr engine{builder.RunHeadless()};
668 ASSERT_NE(engine, nullptr);
669
670 ui::AXPlatformNodeDelegateBase parent_delegate;
671 AlertPlatformNodeDelegate delegate{parent_delegate};
672
673 auto window_binding_handler =
674 std::make_unique<NiceMock<MockWindowBindingHandler>>();
675 EXPECT_CALL(*window_binding_handler, GetAlertDelegate)
676 .WillOnce(Return(&delegate));
677
678 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
679 MockFlutterWindowsView view{windows_engine,
680 std::move(window_binding_handler)};
681 EngineModifier modifier{windows_engine};
682 modifier.SetImplicitView(&view);
683
684 windows_engine->UpdateSemanticsEnabled(true);
685
686 EXPECT_CALL(view, NotifyWinEventWrapper).Times(1);
687
688 // Rely on timeout mechanism in CI.
689 while (!done) {
690 windows_engine->task_runner()->ProcessTasks();
691 }
692 }
693
694 // Verify the app can send accessibility announcements while in headless mode.
695 TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncementHeadless) {
696 auto& context = GetContext();
697 WindowsConfigBuilder builder{context};
698 builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
699
700 bool done = false;
701 auto native_entry =
702 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
703 context.AddNativeFunction("Signal", native_entry);
704
705 EnginePtr engine{builder.RunHeadless()};
706 ASSERT_NE(engine, nullptr);
707
708 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
709 windows_engine->UpdateSemanticsEnabled(true);
710
711 // Rely on timeout mechanism in CI.
712 while (!done) {
713 windows_engine->task_runner()->ProcessTasks();
714 }
715 }
716
717 // Verify the engine does not crash if it receives an accessibility event
718 // it does not support yet.
719 TEST_F(FlutterWindowsEngineTest, AccessibilityTooltip) {
720 fml::testing::LogCapture log_capture;
721
722 auto& context = GetContext();
723 WindowsConfigBuilder builder{context};
724 builder.SetDartEntrypoint("sendAccessibilityTooltipEvent");
725
726 bool done = false;
727 auto native_entry =
728 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
729 context.AddNativeFunction("Signal", native_entry);
730
731 ViewControllerPtr controller{builder.Run()};
732 ASSERT_NE(controller, nullptr);
733
734 auto engine = FlutterDesktopViewControllerGetEngine(controller.get());
735 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine);
736 windows_engine->UpdateSemanticsEnabled(true);
737
738 // Rely on timeout mechanism in CI.
739 while (!done) {
740 windows_engine->task_runner()->ProcessTasks();
741 }
742
743 // Verify no error was logged.
744 // Regression test for:
745 // https://github.com/flutter/flutter/issues/144274
746 EXPECT_EQ(log_capture.str().find("tooltip"), std::string::npos);
747 }
748
749 class MockWindowsLifecycleManager : public WindowsLifecycleManager {
750 public:
751 MockWindowsLifecycleManager(FlutterWindowsEngine* engine)
752 : WindowsLifecycleManager(engine) {}
753 virtual ~MockWindowsLifecycleManager() {}
754
755 MOCK_METHOD(
756 void,
757 Quit,
758 (std::optional<HWND>, std::optional<WPARAM>, std::optional<LPARAM>, UINT),
759 (override));
760 MOCK_METHOD(void, DispatchMessage, (HWND, UINT, WPARAM, LPARAM), (override));
761 MOCK_METHOD(bool, IsLastWindowOfProcess, (), (override));
762 MOCK_METHOD(void, SetLifecycleState, (AppLifecycleState), (override));
763
764 void BeginProcessingLifecycle() override {
765 WindowsLifecycleManager::BeginProcessingLifecycle();
766 if (begin_processing_callback) {
767 begin_processing_callback();
768 }
769 }
770
771 std::function<void()> begin_processing_callback = nullptr;
772 };
773
774 TEST_F(FlutterWindowsEngineTest, TestExit) {
775 FlutterWindowsEngineBuilder builder{GetContext()};
776 builder.SetDartEntrypoint("exitTestExit");
777 bool finished = false;
778
779 auto engine = builder.Build();
780 auto window_binding_handler =
781 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
782 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
783
784 EngineModifier modifier(engine.get());
785 modifier.SetImplicitView(&view);
786 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
787 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
788 EXPECT_CALL(*handler, SetLifecycleState(AppLifecycleState::kResumed));
789 EXPECT_CALL(*handler, Quit)
790 .WillOnce([&finished](std::optional<HWND> hwnd,
791 std::optional<WPARAM> wparam,
792 std::optional<LPARAM> lparam,
793 UINT exit_code) { finished = exit_code == 0; });
794 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
795 modifier.SetLifecycleManager(std::move(handler));
796
797 engine->lifecycle_manager()->BeginProcessingExit();
798
799 engine->Run();
800
801 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
802 0);
803
804 // The test will only succeed when this while loop exits. Otherwise it will
805 // timeout.
806 while (!finished) {
807 engine->task_runner()->ProcessTasks();
808 }
809 }
810
811 TEST_F(FlutterWindowsEngineTest, TestExitCancel) {
812 FlutterWindowsEngineBuilder builder{GetContext()};
813 builder.SetDartEntrypoint("exitTestCancel");
814 bool did_call = false;
815
816 auto engine = builder.Build();
817 auto window_binding_handler =
818 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
819 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
820
821 EngineModifier modifier(engine.get());
822 modifier.SetImplicitView(&view);
823 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
824 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
825 EXPECT_CALL(*handler, SetLifecycleState(AppLifecycleState::kResumed));
826 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
827 EXPECT_CALL(*handler, Quit).Times(0);
828 modifier.SetLifecycleManager(std::move(handler));
829 engine->lifecycle_manager()->BeginProcessingExit();
830
831 auto binary_messenger =
832 std::make_unique<BinaryMessengerImpl>(engine->messenger());
833 binary_messenger->SetMessageHandler(
834 "flutter/platform", [&did_call](const uint8_t* message,
835 size_t message_size, BinaryReply reply) {
836 std::string contents(message, message + message_size);
837 EXPECT_NE(contents.find("\"method\":\"System.exitApplication\""),
838 std::string::npos);
839 EXPECT_NE(contents.find("\"type\":\"required\""), std::string::npos);
840 EXPECT_NE(contents.find("\"exitCode\":0"), std::string::npos);
841 did_call = true;
842 char response[] = "";
843 reply(reinterpret_cast<uint8_t*>(response), 0);
844 });
845
846 engine->Run();
847
848 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
849 0);
850
851 while (!did_call) {
852 engine->task_runner()->ProcessTasks();
853 }
854 }
855
856 // Flutter consumes the first WM_CLOSE message to allow the app to cancel the
857 // exit. If the app does not cancel the exit, Flutter synthesizes a second
858 // WM_CLOSE message.
859 TEST_F(FlutterWindowsEngineTest, TestExitSecondCloseMessage) {
860 FlutterWindowsEngineBuilder builder{GetContext()};
861 builder.SetDartEntrypoint("exitTestExit");
862 bool second_close = false;
863
864 auto engine = builder.Build();
865 auto window_binding_handler =
866 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
867 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
868
869 EngineModifier modifier(engine.get());
870 modifier.SetImplicitView(&view);
871 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
872 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
873 EXPECT_CALL(*handler, SetLifecycleState(AppLifecycleState::kResumed));
874 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(true));
875 EXPECT_CALL(*handler, Quit)
876 .WillOnce([handler_ptr = handler.get()](
877 std::optional<HWND> hwnd, std::optional<WPARAM> wparam,
878 std::optional<LPARAM> lparam, UINT exit_code) {
879 handler_ptr->WindowsLifecycleManager::Quit(hwnd, wparam, lparam,
880 exit_code);
881 });
882 EXPECT_CALL(*handler, DispatchMessage)
883 .WillRepeatedly(
884 [&engine](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
885 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
886 hwnd, msg, wparam, lparam);
887 });
888 modifier.SetLifecycleManager(std::move(handler));
889 engine->lifecycle_manager()->BeginProcessingExit();
890
891 engine->Run();
892
893 // This delegate will be registered after the lifecycle manager, so it will be
894 // called only when a message is not consumed by the lifecycle manager. This
895 // should be called on the second, synthesized WM_CLOSE message that the
896 // lifecycle manager posts.
897 engine->window_proc_delegate_manager()->RegisterTopLevelWindowProcDelegate(
898 [](HWND hwnd, UINT message, WPARAM wpar, LPARAM lpar, void* user_data,
899 LRESULT* result) {
900 switch (message) {
901 case WM_CLOSE: {
902 bool* called = reinterpret_cast<bool*>(user_data);
903 *called = true;
904 return true;
905 }
906 }
907 return false;
908 },
909 reinterpret_cast<void*>(&second_close));
910
911 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
912 0);
913
914 while (!second_close) {
915 engine->task_runner()->ProcessTasks();
916 }
917 }
918
919 TEST_F(FlutterWindowsEngineTest, TestExitCloseMultiWindow) {
920 FlutterWindowsEngineBuilder builder{GetContext()};
921 builder.SetDartEntrypoint("exitTestExit");
922 bool finished = false;
923
924 auto engine = builder.Build();
925 auto window_binding_handler =
926 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
927 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
928
929 EngineModifier modifier(engine.get());
930 modifier.SetImplicitView(&view);
931 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
932 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
933 EXPECT_CALL(*handler, SetLifecycleState(AppLifecycleState::kResumed));
934 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce([&finished]() {
935 finished = true;
936 return false;
937 });
938 // Quit should not be called when there is more than one window.
939 EXPECT_CALL(*handler, Quit).Times(0);
940 modifier.SetLifecycleManager(std::move(handler));
941 engine->lifecycle_manager()->BeginProcessingExit();
942
943 engine->Run();
944
945 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
946 0);
947
948 while (!finished) {
949 engine->task_runner()->ProcessTasks();
950 }
951 }
952
953 TEST_F(FlutterWindowsEngineTest, LifecycleManagerDisabledByDefault) {
954 FlutterWindowsEngineBuilder builder{GetContext()};
955
956 auto engine = builder.Build();
957 auto window_binding_handler =
958 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
959 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
960
961 EngineModifier modifier(engine.get());
962 modifier.SetImplicitView(&view);
963 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
964 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
965 EXPECT_CALL(*handler, IsLastWindowOfProcess).Times(0);
966 modifier.SetLifecycleManager(std::move(handler));
967
968 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
969 0);
970 }
971
972 TEST_F(FlutterWindowsEngineTest, EnableApplicationLifecycle) {
973 FlutterWindowsEngineBuilder builder{GetContext()};
974
975 auto engine = builder.Build();
976 auto window_binding_handler =
977 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
978 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
979
980 EngineModifier modifier(engine.get());
981 modifier.SetImplicitView(&view);
982 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
983 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
984 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
985 modifier.SetLifecycleManager(std::move(handler));
986 engine->lifecycle_manager()->BeginProcessingExit();
987
988 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
989 0);
990 }
991
992 TEST_F(FlutterWindowsEngineTest, ApplicationLifecycleExternalWindow) {
993 FlutterWindowsEngineBuilder builder{GetContext()};
994
995 auto engine = builder.Build();
996 auto window_binding_handler =
997 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
998 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
999
1000 EngineModifier modifier(engine.get());
1001 modifier.SetImplicitView(&view);
1002 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1003 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1004 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
1005 modifier.SetLifecycleManager(std::move(handler));
1006 engine->lifecycle_manager()->BeginProcessingExit();
1007
1008 engine->lifecycle_manager()->ExternalWindowMessage(0, WM_CLOSE, 0, 0);
1009 }
1010
1011 TEST_F(FlutterWindowsEngineTest, AppStartsInResumedState) {
1012 FlutterWindowsEngineBuilder builder{GetContext()};
1013
1014 auto engine = builder.Build();
1015 auto window_binding_handler =
1016 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1017 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1018
1019 EngineModifier modifier(engine.get());
1020 modifier.SetImplicitView(&view);
1021 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1022 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1023 EXPECT_CALL(*handler, SetLifecycleState(AppLifecycleState::kResumed))
1024 .Times(1);
1025 modifier.SetLifecycleManager(std::move(handler));
1026 engine->Run();
1027 }
1028
1029 TEST_F(FlutterWindowsEngineTest, LifecycleStateTransition) {
1030 FlutterWindowsEngineBuilder builder{GetContext()};
1031
1032 auto engine = builder.Build();
1033 auto window_binding_handler =
1034 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1035 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1036
1037 EngineModifier modifier(engine.get());
1038 modifier.SetImplicitView(&view);
1039 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1040 engine->Run();
1041
1042 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1043 (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1044 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1045 AppLifecycleState::kResumed);
1046
1047 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1048 (HWND)1, WM_SIZE, SIZE_MINIMIZED, 0);
1049 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1050 AppLifecycleState::kHidden);
1051
1052 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1053 (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1054 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1055 AppLifecycleState::kInactive);
1056 }
1057
1058 TEST_F(FlutterWindowsEngineTest, ExternalWindowMessage) {
1059 FlutterWindowsEngineBuilder builder{GetContext()};
1060
1061 auto engine = builder.Build();
1062 auto window_binding_handler =
1063 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1064 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1065
1066 EngineModifier modifier(engine.get());
1067 modifier.SetImplicitView(&view);
1068 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1069 // Sets lifecycle state to resumed.
1070 engine->Run();
1071
1072 // Ensure HWND(1) is in the set of visible windows before hiding it.
1073 engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1074 TRUE, NULL);
1075 engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1076 FALSE, NULL);
1077
1078 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1079 AppLifecycleState::kHidden);
1080 }
1081
1082 TEST_F(FlutterWindowsEngineTest, InnerWindowHidden) {
1083 FlutterWindowsEngineBuilder builder{GetContext()};
1084 HWND outer = reinterpret_cast<HWND>(1);
1085 HWND inner = reinterpret_cast<HWND>(2);
1086
1087 auto engine = builder.Build();
1088 auto window_binding_handler =
1089 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1090 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1091 ON_CALL(view, GetWindowHandle).WillByDefault([=]() { return inner; });
1092
1093 EngineModifier modifier(engine.get());
1094 modifier.SetImplicitView(&view);
1095 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1096 // Sets lifecycle state to resumed.
1097 engine->Run();
1098
1099 // Show both top-level and Flutter window.
1100 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1101 outer, WM_SHOWWINDOW, TRUE, NULL);
1102 view.OnWindowStateEvent(inner, WindowStateEvent::kShow);
1103 view.OnWindowStateEvent(inner, WindowStateEvent::kFocus);
1104
1105 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1106 AppLifecycleState::kResumed);
1107
1108 // Hide Flutter window, but not top level window.
1109 view.OnWindowStateEvent(inner, WindowStateEvent::kHide);
1110
1111 // The top-level window is still visible, so we ought not enter hidden state.
1112 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1113 AppLifecycleState::kInactive);
1114 }
1115
1116 TEST_F(FlutterWindowsEngineTest, EnableLifecycleState) {
1117 FlutterWindowsEngineBuilder builder{GetContext()};
1118 builder.SetDartEntrypoint("enableLifecycleTest");
1119 bool finished = false;
1120
1121 auto engine = builder.Build();
1122 auto window_binding_handler =
1123 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1124 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1125
1126 EngineModifier modifier(engine.get());
1127 modifier.SetImplicitView(&view);
1128 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1129 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1130 EXPECT_CALL(*handler, SetLifecycleState)
1131 .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1132 handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1133 });
1134 modifier.SetLifecycleManager(std::move(handler));
1135
1136 auto binary_messenger =
1137 std::make_unique<BinaryMessengerImpl>(engine->messenger());
1138 // Mark the test only as completed on receiving an inactive state message.
1139 binary_messenger->SetMessageHandler(
1140 "flutter/unittest", [&finished](const uint8_t* message,
1141 size_t message_size, BinaryReply reply) {
1142 std::string contents(message, message + message_size);
1143 EXPECT_NE(contents.find("AppLifecycleState.inactive"),
1144 std::string::npos);
1145 finished = true;
1146 char response[] = "";
1147 reply(reinterpret_cast<uint8_t*>(response), 0);
1148 });
1149
1150 engine->Run();
1151
1152 // Test that setting the state before enabling lifecycle does nothing.
1153 HWND hwnd = reinterpret_cast<HWND>(1);
1154 view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1155 view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1156 EXPECT_FALSE(finished);
1157
1158 // Test that we can set the state afterwards.
1159
1160 engine->lifecycle_manager()->BeginProcessingLifecycle();
1161 view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1162
1163 while (!finished) {
1164 engine->task_runner()->ProcessTasks();
1165 }
1166 }
1167
1168 TEST_F(FlutterWindowsEngineTest, LifecycleStateToFrom) {
1169 FlutterWindowsEngineBuilder builder{GetContext()};
1170 builder.SetDartEntrypoint("enableLifecycleToFrom");
1171 bool enabled_lifecycle = false;
1172 bool dart_responded = false;
1173
1174 auto engine = builder.Build();
1175 auto window_binding_handler =
1176 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1177 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1178
1179 EngineModifier modifier(engine.get());
1180 modifier.SetImplicitView(&view);
1181 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1182 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1183 EXPECT_CALL(*handler, SetLifecycleState)
1184 .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1185 handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1186 });
1187 handler->begin_processing_callback = [&]() { enabled_lifecycle = true; };
1188 modifier.SetLifecycleManager(std::move(handler));
1189
1190 auto binary_messenger =
1191 std::make_unique<BinaryMessengerImpl>(engine->messenger());
1192 binary_messenger->SetMessageHandler(
1193 "flutter/unittest",
1194 [&](const uint8_t* message, size_t message_size, BinaryReply reply) {
1195 std::string contents(message, message + message_size);
1196 EXPECT_NE(contents.find("AppLifecycleState."), std::string::npos);
1197 dart_responded = true;
1198 char response[] = "";
1199 reply(reinterpret_cast<uint8_t*>(response), 0);
1200 });
1201
1202 engine->Run();
1203
1204 while (!enabled_lifecycle) {
1205 engine->task_runner()->ProcessTasks();
1206 }
1207
1208 HWND hwnd = reinterpret_cast<HWND>(1);
1209 view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1210 view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1211
1212 while (!dart_responded) {
1213 engine->task_runner()->ProcessTasks();
1214 }
1215 }
1216
1217 TEST_F(FlutterWindowsEngineTest, ChannelListenedTo) {
1218 FlutterWindowsEngineBuilder builder{GetContext()};
1219 builder.SetDartEntrypoint("enableLifecycleToFrom");
1220
1221 auto engine = builder.Build();
1222 auto window_binding_handler =
1223 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1224 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1225
1226 EngineModifier modifier(engine.get());
1227 modifier.SetImplicitView(&view);
1228 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1229
1230 bool lifecycle_began = false;
1231 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1232 EXPECT_CALL(*handler, SetLifecycleState).Times(1);
1233 handler->begin_processing_callback = [&]() { lifecycle_began = true; };
1234 modifier.SetLifecycleManager(std::move(handler));
1235
1236 engine->Run();
1237
1238 while (!lifecycle_began) {
1239 engine->task_runner()->ProcessTasks();
1240 }
1241 }
1242
1243 TEST_F(FlutterWindowsEngineTest, ReceivePlatformViewMessage) {
1244 FlutterWindowsEngineBuilder builder{GetContext()};
1245 builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1246 auto engine = builder.Build();
1247
1248 EngineModifier modifier{engine.get()};
1249 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1250
1251 bool received_call = false;
1252
1253 auto manager = std::make_unique<MockPlatformViewManager>(engine.get());
1254 EXPECT_CALL(*manager, AddPlatformView)
1255 .WillOnce([&](PlatformViewId id, std::string_view type_name) {
1256 received_call = true;
1257 return true;
1258 });
1259 modifier.SetPlatformViewPlugin(std::move(manager));
1260
1261 engine->Run();
1262
1263 while (!received_call) {
1264 engine->task_runner()->ProcessTasks();
1265 }
1266 }
1267
1268 TEST_F(FlutterWindowsEngineTest, AddViewFailureDoesNotHang) {
1269 FlutterWindowsEngineBuilder builder{GetContext()};
1270 auto engine = builder.Build();
1271
1272 EngineModifier modifier{engine.get()};
1273
1274 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1275 modifier.embedder_api().AddView = MOCK_ENGINE_PROC(
1276 AddView,
1277 [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
1278 const FlutterAddViewInfo* info) { return kInternalInconsistency; });
1279
1280 ASSERT_TRUE(engine->Run());
1281
1282 // Create the first view. This is the implicit view and isn't added to the
1283 // engine.
1284 auto implicit_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1285
1286 std::unique_ptr<FlutterWindowsView> implicit_view =
1287 engine->CreateView(std::move(implicit_window));
1288
1289 EXPECT_TRUE(implicit_view);
1290
1291 // Create a second view. The embedder attempts to add it to the engine.
1292 auto second_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1293
1294 EXPECT_DEBUG_DEATH(engine->CreateView(std::move(second_window)),
1295 "FlutterEngineAddView returned an unexpected result");
1296 }
1297
1298 TEST_F(FlutterWindowsEngineTest, RemoveViewFailureDoesNotHang) {
1299 FlutterWindowsEngineBuilder builder{GetContext()};
1300 builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1301 auto engine = builder.Build();
1302
1303 EngineModifier modifier{engine.get()};
1304
1305 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1306 modifier.embedder_api().RemoveView = MOCK_ENGINE_PROC(
1307 RemoveView,
1308 [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
1309 const FlutterRemoveViewInfo* info) { return kInternalInconsistency; });
1310
1311 ASSERT_TRUE(engine->Run());
1312 EXPECT_DEBUG_DEATH(engine->RemoveView(123),
1313 "FlutterEngineRemoveView returned an unexpected result");
1314 }
1315
1316 } // namespace testing
1317 } // namespace flutter
flutter::AlertPlatformNodeDelegate
Definition: alert_platform_node_delegate.h:18
flutter::kImplicitViewId
constexpr FlutterViewId kImplicitViewId
Definition: flutter_windows_engine.h:55
flutter::AppLifecycleState::kHidden
@ kHidden
flutter::WindowStateEvent::kHide
@ kHide
flutter::FlutterWindowsView
Definition: flutter_windows_view.h:34
flutter::FlutterEngine
Definition: flutter_engine.h:28
FlutterDesktopBinaryReply
void(* FlutterDesktopBinaryReply)(const uint8_t *data, size_t data_size, void *user_data)
Definition: flutter_messenger.h:26
flutter::testing::MockFlutterWindowsView::~MockFlutterWindowsView
~MockFlutterWindowsView()
Definition: flutter_windows_engine_unittests.cc:644
user_data
void * user_data
Definition: flutter_windows_view_unittests.cc:53
flutter::FlutterWindowsEngine
Definition: flutter_windows_engine.h:90
flutter::testing::MockFlutterWindowsView::MockFlutterWindowsView
MockFlutterWindowsView(FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > wbh)
Definition: flutter_windows_engine_unittests.cc:641
PlatformViewId
int64_t PlatformViewId
Definition: flutter_windows_internal.h:42
flutter::WindowsLifecycleManager::BeginProcessingLifecycle
virtual void BeginProcessingLifecycle()
Definition: windows_lifecycle_manager.cc:187
flutter::testing::MockWindowsLifecycleManager::begin_processing_callback
std::function< void()> begin_processing_callback
Definition: flutter_windows_engine_unittests.cc:771
flutter::testing::MockWindowsLifecycleManager
Definition: flutter_windows_engine_unittests.cc:749
flutter::FlutterEngine::Run
bool Run()
Definition: flutter_engine.cc:44
flutter::WindowsLifecycleManager::DispatchMessage
virtual void DispatchMessage(HWND window, UINT msg, WPARAM wparam, LPARAM lparam)
Definition: windows_lifecycle_manager.cc:34
flutter::testing::MockWindowsLifecycleManager::MockWindowsLifecycleManager
MockWindowsLifecycleManager(FlutterWindowsEngine *engine)
Definition: flutter_windows_engine_unittests.cc:751
flutter_windows_view.h
flutter::WindowsLifecycleManager::SetLifecycleState
virtual void SetLifecycleState(AppLifecycleState state)
Definition: windows_lifecycle_manager.cc:195
flutter::WindowStateEvent::kFocus
@ kFocus
flutter::AppLifecycleState::kInactive
@ kInactive
flutter::WindowStateEvent::kShow
@ kShow
flutter::testing::FlutterWindowsEngineTest
Definition: flutter_windows_engine_unittests.cc:36
flutter::BinaryReply
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
Definition: binary_messenger.h:17
flutter::testing::MockWindowsLifecycleManager::~MockWindowsLifecycleManager
virtual ~MockWindowsLifecycleManager()
Definition: flutter_windows_engine_unittests.cc:753
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::WindowsLifecycleManager::IsLastWindowOfProcess
virtual bool IsLastWindowOfProcess()
Definition: windows_lifecycle_manager.cc:164
flutter_windows_engine.h
flutter::WindowsLifecycleManager::Quit
virtual void Quit(std::optional< HWND > window, std::optional< WPARAM > wparam, std::optional< LPARAM > lparam, UINT exit_code)
Definition: windows_lifecycle_manager.cc:21
FlutterDesktopViewControllerGetEngine
FlutterDesktopEngineRef FlutterDesktopViewControllerGetEngine(FlutterDesktopViewControllerRef ref)
Definition: flutter_windows.cc:147
flutter::AppLifecycleState::kResumed
@ kResumed
flutter_windows.h
message
Win32Message message
Definition: keyboard_unittests.cc:137
action
int action
Definition: keyboard_key_handler_unittests.cc:116
flutter::AppLifecycleState
AppLifecycleState
Definition: app_lifecycle_state.h:32
flutter::FlutterWindowsEngine::UpdateSemanticsEnabled
void UpdateSemanticsEnabled(bool enabled)
Definition: flutter_windows_engine.cc:885
flutter::testing::MockWindowsLifecycleManager::MOCK_METHOD
MOCK_METHOD(void, Quit,(std::optional< HWND >, std::optional< WPARAM >, std::optional< LPARAM >, UINT),(override))
flutter::testing::TEST_F
TEST_F(CompositorOpenGLTest, CreateBackingStore)
Definition: compositor_opengl_unittests.cc:125
flutter::WindowsLifecycleManager
Definition: windows_lifecycle_manager.h:37
flutter::testing::MockWindowsLifecycleManager::BeginProcessingLifecycle
void BeginProcessingLifecycle() override
Definition: flutter_windows_engine_unittests.cc:764
FlutterDesktopPluginRegistrar
Definition: window_state.h:23
flutter::WindowsPlatformThreadPrioritySetter
static void WindowsPlatformThreadPrioritySetter(FlutterThreadPriority priority)
Definition: flutter_windows_engine.h:60
callback
FlutterDesktopBinaryReply callback
Definition: flutter_windows_view_unittests.cc:52