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