Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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>
7
25#include "gmock/gmock.h"
26#include "gtest/gtest.h"
27
28// winbase.h defines GetCurrentTime as a macro.
29#undef GetCurrentTime
30
31namespace {
32// Process the next win32 message if there is one. This can be used to
33// pump the Windows platform thread task runner.
34void PumpMessage() {
35 ::MSG msg;
36 if (::GetMessage(&msg, nullptr, 0, 0)) {
37 ::TranslateMessage(&msg);
39 }
40}
41} // namespace
42
43namespace flutter {
44namespace testing {
45
46using ::testing::_;
47using ::testing::DoAll;
48using ::testing::NiceMock;
49using ::testing::Return;
50using ::testing::SetArgPointee;
51
53
54// The engine can be run without any views.
56 FlutterWindowsEngineBuilder builder{GetContext()};
57 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
58
59 EngineModifier modifier(engine.get());
60 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
61
62 ASSERT_TRUE(engine->Run());
63 ASSERT_EQ(engine->view(kImplicitViewId), nullptr);
64 ASSERT_EQ(engine->view(123), nullptr);
65}
66
67TEST_F(FlutterWindowsEngineTest, TaskRunnerDelayedTask) {
68 bool finished = false;
69 auto runner = std::make_unique<TaskRunner>(
70 [] {
71 return static_cast<uint64_t>(
73 },
74 [&](const FlutterTask*) { finished = true; });
75 runner->PostFlutterTask(
77 static_cast<uint64_t>((fml::TimePoint::Now().ToEpochDelta() +
79 .ToNanoseconds()));
81 while (!finished) {
82 PumpMessage();
83 }
84 auto duration = fml::TimePoint::Now() - start;
85 EXPECT_GE(duration, fml::TimeDelta::FromMilliseconds(50));
86}
87
88// https://github.com/flutter/flutter/issues/173843)
89TEST_F(FlutterWindowsEngineTest, TaskRunnerDoesNotDeadlock) {
90 auto runner = std::make_unique<TaskRunner>(
91 [] {
92 return static_cast<uint64_t>(
94 },
95 [&](const FlutterTask*) {});
96
97 struct RunnerHolder {
98 void PostTaskLoop() {
99 runner->PostTask([this] { PostTaskLoop(); });
100 }
101 std::unique_ptr<TaskRunner> runner;
102 };
103
104 RunnerHolder container{.runner = std::move(runner)};
105 // Spam flutter tasks.
106 container.PostTaskLoop();
107
108 const LPCWSTR class_name = L"FlutterTestWindowClass";
109 WNDCLASS wc = {0};
110 wc.lpfnWndProc = DefWindowProc;
111 wc.lpszClassName = class_name;
112 RegisterClass(&wc);
113
114 HWND window;
115 container.runner->PostTask([&] {
116 window = CreateWindowEx(0, class_name, L"Empty Window", WS_OVERLAPPEDWINDOW,
117 CW_USEDEFAULT, CW_USEDEFAULT, 800, 600, nullptr,
118 nullptr, nullptr, nullptr);
119 ShowWindow(window, SW_SHOW);
120 });
121
122 while (true) {
123 ::MSG msg;
124 if (::GetMessage(&msg, nullptr, 0, 0)) {
125 if (msg.message == WM_PAINT) {
126 break;
127 }
128 ::TranslateMessage(&msg);
129 ::DispatchMessage(&msg);
130 }
131 }
132
133 DestroyWindow(window);
134 UnregisterClassW(class_name, nullptr);
135}
136
137TEST_F(FlutterWindowsEngineTest, RunDoesExpectedInitialization) {
138 FlutterWindowsEngineBuilder builder{GetContext()};
139 builder.AddDartEntrypointArgument("arg1");
140 builder.AddDartEntrypointArgument("arg2");
141
142 auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
143
144 HMONITOR mock_monitor = reinterpret_cast<HMONITOR>(1);
145
146 MONITORINFOEXW monitor_info = {};
147 monitor_info.cbSize = sizeof(MONITORINFOEXW);
148 monitor_info.rcMonitor = {0, 0, 1920, 1080};
149 monitor_info.rcWork = {0, 0, 1920, 1080};
150 monitor_info.dwFlags = MONITORINFOF_PRIMARY;
151 wcscpy_s(monitor_info.szDevice, L"\\\\.\\DISPLAY1");
152
153 EXPECT_CALL(*windows_proc_table, GetMonitorInfoW(mock_monitor, _))
154 .WillRepeatedly(DoAll(SetArgPointee<1>(monitor_info), Return(TRUE)));
155
156 EXPECT_CALL(*windows_proc_table, EnumDisplayMonitors(nullptr, nullptr, _, _))
157 .WillRepeatedly([&](HDC hdc, LPCRECT lprcClip, MONITORENUMPROC lpfnEnum,
158 LPARAM dwData) {
159 lpfnEnum(mock_monitor, nullptr, &monitor_info.rcMonitor, dwData);
160 return TRUE;
161 });
162
163 EXPECT_CALL(*windows_proc_table, GetDpiForMonitor(mock_monitor, _))
164 .WillRepeatedly(Return(96));
165
166 // Mock locale information
167 EXPECT_CALL(*windows_proc_table, GetThreadPreferredUILanguages(_, _, _, _))
168 .WillRepeatedly(
169 [](DWORD flags, PULONG count, PZZWSTR languages, PULONG length) {
170 // We need to mock the locale information twice because the first
171 // call is to get the size and the second call is to fill the
172 // buffer.
173 if (languages == nullptr) {
174 // First call is to get the size
175 *count = 1; // One language
176 *length = 10; // "fr-FR\0\0" (double null-terminated)
177 return TRUE;
178 } else {
179 // Second call is to fill the buffer
180 *count = 1;
181 // Fill with "fr-FR\0\0" (double null-terminated)
182 wchar_t* lang_buffer = languages;
183 wcscpy(lang_buffer, L"fr-FR");
184 // Move past the first null terminator to add the second
185 lang_buffer += wcslen(L"fr-FR") + 1;
186 *lang_buffer = L'\0';
187 return TRUE;
188 }
189 });
190
191 builder.SetWindowsProcTable(windows_proc_table);
192
193 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
194 EngineModifier modifier(engine.get());
195
196 // The engine should be run with expected configuration values.
197 bool run_called = false;
198 modifier.embedder_api().Run = MOCK_ENGINE_PROC(
199 Run, ([&run_called, engine_instance = engine.get()](
200 size_t version, const FlutterRendererConfig* config,
201 const FlutterProjectArgs* args, void* user_data,
202 FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
203 run_called = true;
204 *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
205
206 EXPECT_EQ(version, FLUTTER_ENGINE_VERSION);
207 EXPECT_NE(config, nullptr);
208 // We have an EGL manager, so this should be using OpenGL.
209 EXPECT_EQ(config->type, kOpenGL);
210 EXPECT_EQ(user_data, engine_instance);
211 // Spot-check arguments.
212 EXPECT_NE(args->assets_path, nullptr);
213 EXPECT_NE(args->icu_data_path, nullptr);
214 EXPECT_EQ(args->dart_entrypoint_argc, 2U);
215 EXPECT_EQ(strcmp(args->dart_entrypoint_argv[0], "arg1"), 0);
216 EXPECT_EQ(strcmp(args->dart_entrypoint_argv[1], "arg2"), 0);
217 EXPECT_NE(args->platform_message_callback, nullptr);
218 EXPECT_NE(args->custom_task_runners, nullptr);
219 EXPECT_NE(args->custom_task_runners->thread_priority_setter, nullptr);
220 EXPECT_EQ(args->custom_dart_entrypoint, nullptr);
221 EXPECT_NE(args->vsync_callback, nullptr);
222 EXPECT_EQ(args->update_semantics_callback, nullptr);
223 EXPECT_NE(args->update_semantics_callback2, nullptr);
224 EXPECT_EQ(args->update_semantics_node_callback, nullptr);
225 EXPECT_EQ(args->update_semantics_custom_action_callback, nullptr);
226 EXPECT_NE(args->view_focus_change_request_callback, nullptr);
227
228 args->custom_task_runners->thread_priority_setter(
230 EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
231 THREAD_PRIORITY_ABOVE_NORMAL);
232 return kSuccess;
233 }));
234 // Accessibility updates must do nothing when the embedder engine is mocked
236 UpdateAccessibilityFeatures,
238 FlutterAccessibilityFeature flags) { return kSuccess; });
239
240 // It should send locale info.
241 bool update_locales_called = false;
243 UpdateLocales,
244 ([&update_locales_called](auto engine, const FlutterLocale** locales,
245 size_t locales_count) {
246 update_locales_called = true;
247
248 EXPECT_GT(locales_count, 0);
249 EXPECT_NE(locales, nullptr);
250
251 return kSuccess;
252 }));
253
254 // And it should send initial settings info.
255 bool settings_message_sent = false;
257 SendPlatformMessage,
258 ([&settings_message_sent](auto engine, auto message) {
259 if (std::string(message->channel) == std::string("flutter/settings")) {
260 settings_message_sent = true;
261 }
262
263 return kSuccess;
264 }));
265
266 // And it should send display info.
267 bool notify_display_update_called = false;
268
270 NotifyDisplayUpdate,
271 ([&notify_display_update_called](
273 const FlutterEngineDisplaysUpdateType update_type,
274 const FlutterEngineDisplay* embedder_displays,
275 size_t display_count) {
276 EXPECT_EQ(update_type, kFlutterEngineDisplaysUpdateTypeStartup);
277 notify_display_update_called = true;
278 return kSuccess;
279 }));
280
281 // Set the EGL manager to !nullptr to test ANGLE rendering.
282 modifier.SetEGLManager(std::make_unique<egl::MockManager>());
283
284 engine->Run();
285
286 EXPECT_TRUE(run_called);
287 EXPECT_TRUE(update_locales_called);
288 EXPECT_TRUE(settings_message_sent);
289 EXPECT_TRUE(notify_display_update_called);
290
291 // Ensure that deallocation doesn't call the actual Shutdown with the bogus
292 // engine pointer that the overridden Run returned.
293 modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
294 modifier.ReleaseEGLManager();
295}
296
297TEST_F(FlutterWindowsEngineTest, ConfiguresFrameVsync) {
298 FlutterWindowsEngineBuilder builder{GetContext()};
299 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
300 EngineModifier modifier(engine.get());
301 bool on_vsync_called = false;
302
303 modifier.embedder_api().GetCurrentTime =
304 MOCK_ENGINE_PROC(GetCurrentTime, ([]() -> uint64_t { return 1; }));
306 OnVsync,
307 ([&on_vsync_called, engine_instance = engine.get()](
308 FLUTTER_API_SYMBOL(FlutterEngine) engine, intptr_t baton,
309 uint64_t frame_start_time_nanos, uint64_t frame_target_time_nanos) {
310 EXPECT_EQ(baton, 1);
311 EXPECT_EQ(frame_start_time_nanos, 16600000);
312 EXPECT_EQ(frame_target_time_nanos, 33200000);
313 on_vsync_called = true;
314 return kSuccess;
315 }));
316 modifier.SetStartTime(0);
317 modifier.SetFrameInterval(16600000);
318
319 engine->OnVsync(1);
320
321 EXPECT_TRUE(on_vsync_called);
322}
323
324TEST_F(FlutterWindowsEngineTest, RunWithoutANGLEUsesSoftware) {
325 FlutterWindowsEngineBuilder builder{GetContext()};
326 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
327 EngineModifier modifier(engine.get());
328
330 MOCK_ENGINE_PROC(NotifyDisplayUpdate,
331 ([engine_instance = engine.get()](
333 const FlutterEngineDisplaysUpdateType update_type,
334 const FlutterEngineDisplay* embedder_displays,
335 size_t display_count) { return kSuccess; }));
336
337 // The engine should be run with expected configuration values.
338 bool run_called = false;
339 modifier.embedder_api().Run = MOCK_ENGINE_PROC(
340 Run, ([&run_called, engine_instance = engine.get()](
341 size_t version, const FlutterRendererConfig* config,
342 const FlutterProjectArgs* args, void* user_data,
343 FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
344 run_called = true;
345 *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
346 // We don't have an EGL Manager, so we should be using software.
347 EXPECT_EQ(config->type, kSoftware);
348 return kSuccess;
349 }));
350 // Accessibility updates must do nothing when the embedder engine is mocked
352 UpdateAccessibilityFeatures,
354 FlutterAccessibilityFeature flags) { return kSuccess; });
355
356 // Stub out UpdateLocales and SendPlatformMessage as we don't have a fully
357 // initialized engine instance.
359 UpdateLocales, ([](auto engine, const FlutterLocale** locales,
360 size_t locales_count) { return kSuccess; }));
362 MOCK_ENGINE_PROC(SendPlatformMessage,
363 ([](auto engine, auto message) { return kSuccess; }));
364
365 // Set the EGL manager to nullptr to test software fallback path.
366 modifier.SetEGLManager(nullptr);
367
368 engine->Run();
369
370 EXPECT_TRUE(run_called);
371
372 // Ensure that deallocation doesn't call the actual Shutdown with the bogus
373 // engine pointer that the overridden Run returned.
374 modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
375}
376
377TEST_F(FlutterWindowsEngineTest, RunWithoutANGLEOnImpellerFailsToStart) {
378 FlutterWindowsEngineBuilder builder{GetContext()};
379 builder.SetSwitches({"--enable-impeller=true"});
380 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
381 EngineModifier modifier(engine.get());
382
384 MOCK_ENGINE_PROC(NotifyDisplayUpdate,
385 ([engine_instance = engine.get()](
387 const FlutterEngineDisplaysUpdateType update_type,
388 const FlutterEngineDisplay* embedder_displays,
389 size_t display_count) { return kSuccess; }));
390
391 // Accessibility updates must do nothing when the embedder engine is mocked
393 UpdateAccessibilityFeatures,
395 FlutterAccessibilityFeature flags) { return kSuccess; });
396
397 // Stub out UpdateLocales and SendPlatformMessage as we don't have a fully
398 // initialized engine instance.
400 UpdateLocales, ([](auto engine, const FlutterLocale** locales,
401 size_t locales_count) { return kSuccess; }));
403 MOCK_ENGINE_PROC(SendPlatformMessage,
404 ([](auto engine, auto message) { return kSuccess; }));
405
406 // Set the EGL manager to nullptr to test software fallback path.
407 modifier.SetEGLManager(nullptr);
408
409 EXPECT_FALSE(engine->Run());
410}
411
412TEST_F(FlutterWindowsEngineTest, SendPlatformMessageWithoutResponse) {
413 FlutterWindowsEngineBuilder builder{GetContext()};
414 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
415 EngineModifier modifier(engine.get());
416
417 const char* channel = "test";
418 const std::vector<uint8_t> test_message = {1, 2, 3, 4};
419
420 // Without a response, SendPlatformMessage should be a simple pass-through.
421 bool called = false;
423 SendPlatformMessage, ([&called, test_message](auto engine, auto message) {
424 called = true;
425 EXPECT_STREQ(message->channel, "test");
426 EXPECT_EQ(message->message_size, test_message.size());
427 EXPECT_EQ(memcmp(message->message, test_message.data(),
428 message->message_size),
429 0);
430 EXPECT_EQ(message->response_handle, nullptr);
431 return kSuccess;
432 }));
433
434 engine->SendPlatformMessage(channel, test_message.data(), test_message.size(),
435 nullptr, nullptr);
436 EXPECT_TRUE(called);
437}
438
439TEST_F(FlutterWindowsEngineTest, PlatformMessageRoundTrip) {
440 FlutterWindowsEngineBuilder builder{GetContext()};
441 builder.SetDartEntrypoint("hiPlatformChannels");
442
443 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
444 EngineModifier modifier(engine.get());
445 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
446
447 auto binary_messenger =
448 std::make_unique<BinaryMessengerImpl>(engine->messenger());
449
450 engine->Run();
451 bool did_call_callback = false;
452 bool did_call_reply = false;
453 bool did_call_dart_reply = false;
454 std::string channel = "hi";
455 binary_messenger->SetMessageHandler(
456 channel,
457 [&did_call_callback, &did_call_dart_reply](
458 const uint8_t* message, size_t message_size, BinaryReply reply) {
459 if (message_size == 5) {
460 EXPECT_EQ(message[0], static_cast<uint8_t>('h'));
461 char response[] = {'b', 'y', 'e'};
462 reply(reinterpret_cast<uint8_t*>(response), 3);
463 did_call_callback = true;
464 } else {
465 EXPECT_EQ(message_size, 3);
466 EXPECT_EQ(message[0], static_cast<uint8_t>('b'));
467 did_call_dart_reply = true;
468 }
469 });
470 char payload[] = {'h', 'e', 'l', 'l', 'o'};
471 binary_messenger->Send(
472 channel, reinterpret_cast<uint8_t*>(payload), 5,
473 [&did_call_reply](const uint8_t* reply, size_t reply_size) {
474 EXPECT_EQ(reply_size, 5);
475 EXPECT_EQ(reply[0], static_cast<uint8_t>('h'));
476 did_call_reply = true;
477 });
478 // Rely on timeout mechanism in CI.
479 while (!did_call_callback || !did_call_reply || !did_call_dart_reply) {
480 engine->task_runner()->ProcessTasks();
481 }
482}
483
484TEST_F(FlutterWindowsEngineTest, PlatformMessageRespondOnDifferentThread) {
485 FlutterWindowsEngineBuilder builder{GetContext()};
486 builder.SetDartEntrypoint("hiPlatformChannels");
487
488 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
489
490 EngineModifier modifier(engine.get());
491 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
492
493 auto binary_messenger =
494 std::make_unique<BinaryMessengerImpl>(engine->messenger());
495
496 engine->Run();
497 bool did_call_callback = false;
498 bool did_call_reply = false;
499 bool did_call_dart_reply = false;
500 std::string channel = "hi";
501 std::unique_ptr<std::thread> reply_thread;
502 binary_messenger->SetMessageHandler(
503 channel,
504 [&did_call_callback, &did_call_dart_reply, &reply_thread](
505 const uint8_t* message, size_t message_size, BinaryReply reply) {
506 if (message_size == 5) {
507 EXPECT_EQ(message[0], static_cast<uint8_t>('h'));
508 reply_thread.reset(new std::thread([reply = std::move(reply)]() {
509 char response[] = {'b', 'y', 'e'};
510 reply(reinterpret_cast<uint8_t*>(response), 3);
511 }));
512 did_call_callback = true;
513 } else {
514 EXPECT_EQ(message_size, 3);
515 EXPECT_EQ(message[0], static_cast<uint8_t>('b'));
516 did_call_dart_reply = true;
517 }
518 });
519 char payload[] = {'h', 'e', 'l', 'l', 'o'};
520 binary_messenger->Send(
521 channel, reinterpret_cast<uint8_t*>(payload), 5,
522 [&did_call_reply](const uint8_t* reply, size_t reply_size) {
523 EXPECT_EQ(reply_size, 5);
524 EXPECT_EQ(reply[0], static_cast<uint8_t>('h'));
525 did_call_reply = true;
526 });
527 // Rely on timeout mechanism in CI.
528 while (!did_call_callback || !did_call_reply || !did_call_dart_reply) {
529 engine->task_runner()->ProcessTasks();
530 }
531 ASSERT_TRUE(reply_thread);
532 reply_thread->join();
533}
534
535TEST_F(FlutterWindowsEngineTest, SendPlatformMessageWithResponse) {
536 FlutterWindowsEngineBuilder builder{GetContext()};
537 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
538 EngineModifier modifier(engine.get());
539
540 const char* channel = "test";
541 const std::vector<uint8_t> test_message = {1, 2, 3, 4};
542 auto* dummy_response_handle =
543 reinterpret_cast<FlutterPlatformMessageResponseHandle*>(5);
544 const FlutterDesktopBinaryReply reply_handler = [](auto... args) {};
545 void* reply_user_data = reinterpret_cast<void*>(6);
546
547 // When a response is requested, a handle should be created, passed as part
548 // of the message, and then released.
549 bool create_response_handle_called = false;
552 PlatformMessageCreateResponseHandle,
553 ([&create_response_handle_called, &reply_handler, reply_user_data,
554 dummy_response_handle](auto engine, auto reply, auto user_data,
555 auto response_handle) {
556 create_response_handle_called = true;
557 EXPECT_EQ(reply, reply_handler);
558 EXPECT_EQ(user_data, reply_user_data);
559 EXPECT_NE(response_handle, nullptr);
560 *response_handle = dummy_response_handle;
561 return kSuccess;
562 }));
563 bool release_response_handle_called = false;
566 PlatformMessageReleaseResponseHandle,
567 ([&release_response_handle_called, dummy_response_handle](
568 auto engine, auto response_handle) {
569 release_response_handle_called = true;
570 EXPECT_EQ(response_handle, dummy_response_handle);
571 return kSuccess;
572 }));
573 bool send_message_called = false;
575 SendPlatformMessage, ([&send_message_called, test_message,
576 dummy_response_handle](auto engine, auto message) {
577 send_message_called = true;
578 EXPECT_STREQ(message->channel, "test");
579 EXPECT_EQ(message->message_size, test_message.size());
580 EXPECT_EQ(memcmp(message->message, test_message.data(),
581 message->message_size),
582 0);
583 EXPECT_EQ(message->response_handle, dummy_response_handle);
584 return kSuccess;
585 }));
586
587 engine->SendPlatformMessage(channel, test_message.data(), test_message.size(),
588 reply_handler, reply_user_data);
589 EXPECT_TRUE(create_response_handle_called);
590 EXPECT_TRUE(release_response_handle_called);
591 EXPECT_TRUE(send_message_called);
592}
593
595 FlutterWindowsEngineBuilder builder{GetContext()};
596 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
597 EngineModifier modifier(engine.get());
598
599 bool called = false;
600 std::string message = "Hello";
602 SendSemanticsAction, ([&called, &message](auto engine, auto info) {
603 called = true;
604 EXPECT_EQ(info->view_id, 456);
605 EXPECT_EQ(info->node_id, 42);
606 EXPECT_EQ(info->action, kFlutterSemanticsActionDismiss);
607 EXPECT_EQ(memcmp(info->data, message.c_str(), message.size()), 0);
608 EXPECT_EQ(info->data_length, message.size());
609 return kSuccess;
610 }));
611
612 auto data = fml::MallocMapping::Copy(message.c_str(), message.size());
613 engine->DispatchSemanticsAction(456, 42, kFlutterSemanticsActionDismiss,
614 std::move(data));
615 EXPECT_TRUE(called);
616}
617
618TEST_F(FlutterWindowsEngineTest, SetsThreadPriority) {
620 EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
621 THREAD_PRIORITY_BELOW_NORMAL);
622
624 EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
625 THREAD_PRIORITY_ABOVE_NORMAL);
626
628 EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
629 THREAD_PRIORITY_ABOVE_NORMAL);
630
631 // FlutterThreadPriority::kNormal does not change thread priority, reset to 0
632 // here.
633 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
634
636 EXPECT_EQ(GetThreadPriority(GetCurrentThread()), THREAD_PRIORITY_NORMAL);
637}
638
639TEST_F(FlutterWindowsEngineTest, AddPluginRegistrarDestructionCallback) {
640 FlutterWindowsEngineBuilder builder{GetContext()};
641 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
642 EngineModifier modifier(engine.get());
643
645 std::make_shared<MockKeyResponseController>());
646
647 engine->Run();
648
649 // Verify that destruction handlers don't overwrite each other.
650 int result1 = 0;
651 int result2 = 0;
652 engine->AddPluginRegistrarDestructionCallback(
654 auto result = reinterpret_cast<int*>(ref);
655 *result = 1;
656 },
657 reinterpret_cast<FlutterDesktopPluginRegistrarRef>(&result1));
658 engine->AddPluginRegistrarDestructionCallback(
660 auto result = reinterpret_cast<int*>(ref);
661 *result = 2;
662 },
663 reinterpret_cast<FlutterDesktopPluginRegistrarRef>(&result2));
664
665 engine->Stop();
666 EXPECT_EQ(result1, 1);
667 EXPECT_EQ(result2, 2);
668}
669
671 FlutterWindowsEngineBuilder builder{GetContext()};
672 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
673 EngineModifier modifier(engine.get());
674
675 bool called = false;
676 modifier.embedder_api().ScheduleFrame =
677 MOCK_ENGINE_PROC(ScheduleFrame, ([&called](auto engine) {
678 called = true;
679 return kSuccess;
680 }));
681
682 engine->ScheduleFrame();
683 EXPECT_TRUE(called);
684}
685
686TEST_F(FlutterWindowsEngineTest, SetNextFrameCallback) {
687 FlutterWindowsEngineBuilder builder{GetContext()};
688 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
689 EngineModifier modifier(engine.get());
690
691 bool called = false;
693 SetNextFrameCallback, ([&called](auto engine, auto callback, auto data) {
694 called = true;
695 return kSuccess;
696 }));
697
698 engine->SetNextFrameCallback([]() {});
699 EXPECT_TRUE(called);
700}
701
702TEST_F(FlutterWindowsEngineTest, GetExecutableName) {
703 FlutterWindowsEngineBuilder builder{GetContext()};
704 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
705 EXPECT_EQ(engine->GetExecutableName(), "flutter_windows_unittests.exe");
706}
707
708// Ensure that after setting or resetting the high contrast feature,
709// the corresponding status flag can be retrieved from the engine.
710TEST_F(FlutterWindowsEngineTest, UpdateHighContrastFeature) {
711 auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
712 EXPECT_CALL(*windows_proc_table, GetHighContrastEnabled)
713 .WillOnce(Return(true))
714 .WillOnce(Return(false));
715
716 FlutterWindowsEngineBuilder builder{GetContext()};
717 builder.SetWindowsProcTable(windows_proc_table);
718 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
719 EngineModifier modifier(engine.get());
720
721 std::optional<FlutterAccessibilityFeature> engine_flags;
723 UpdateAccessibilityFeatures, ([&engine_flags](auto engine, auto flags) {
724 engine_flags = flags;
725 return kSuccess;
726 }));
728 SendPlatformMessage,
729 [](auto engine, const auto message) { return kSuccess; });
730
731 // 1: High contrast is enabled.
732 engine->UpdateHighContrastMode();
733
734 EXPECT_TRUE(engine->high_contrast_enabled());
735 EXPECT_TRUE(engine_flags.has_value());
736 EXPECT_TRUE(
737 engine_flags.value() &
739
740 // 2: High contrast is disabled.
741 engine_flags.reset();
742 engine->UpdateHighContrastMode();
743
744 EXPECT_FALSE(engine->high_contrast_enabled());
745 EXPECT_TRUE(engine_flags.has_value());
746 EXPECT_FALSE(
747 engine_flags.value() &
749}
750
751TEST_F(FlutterWindowsEngineTest, PostRasterThreadTask) {
752 FlutterWindowsEngineBuilder builder{GetContext()};
753 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
754 EngineModifier modifier(engine.get());
755
757 PostRenderThreadTask, ([](auto engine, auto callback, auto context) {
758 callback(context);
759 return kSuccess;
760 }));
761
762 bool called = false;
763 engine->PostRasterThreadTask([&called]() { called = true; });
764
765 EXPECT_TRUE(called);
766}
767
768class MockFlutterWindowsView : public FlutterWindowsView {
769 public:
771 std::unique_ptr<WindowBindingHandler> wbh)
773 engine,
774 std::move(wbh),
775 false,
776 BoxConstraints()) {}
778
780 NotifyWinEventWrapper,
781 (ui::AXPlatformNodeWin*, ax::mojom::Event),
782 (override));
783 MOCK_METHOD(HWND, GetWindowHandle, (), (const, override));
784 MOCK_METHOD(bool, Focus, (), (override));
785
786 private:
787 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
788};
789
790// Verify the view is notified of accessibility announcements.
791TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncement) {
792 auto& context = GetContext();
793 WindowsConfigBuilder builder{context};
794 builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
795
796 bool done = false;
797 auto native_entry =
798 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
799 context.AddNativeFunction("Signal", native_entry);
800
801 EnginePtr engine{builder.RunHeadless()};
802 ASSERT_NE(engine, nullptr);
803
804 ui::AXPlatformNodeDelegateBase parent_delegate;
805 AlertPlatformNodeDelegate delegate{parent_delegate};
806
807 auto window_binding_handler =
808 std::make_unique<NiceMock<MockWindowBindingHandler>>();
809 EXPECT_CALL(*window_binding_handler, GetAlertDelegate)
810 .WillOnce(Return(&delegate));
811
812 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
813 MockFlutterWindowsView view{windows_engine,
814 std::move(window_binding_handler)};
815 EngineModifier modifier{windows_engine};
816 modifier.SetImplicitView(&view);
817
818 windows_engine->UpdateSemanticsEnabled(true);
819
820 EXPECT_CALL(view, NotifyWinEventWrapper).Times(1);
821
822 // Rely on timeout mechanism in CI.
823 while (!done) {
824 windows_engine->task_runner()->ProcessTasks();
825 }
826}
827
828// Verify the app can send accessibility announcements while in headless mode.
829TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncementHeadless) {
830 auto& context = GetContext();
831 WindowsConfigBuilder builder{context};
832 builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
833
834 bool done = false;
835 auto native_entry =
836 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
837 context.AddNativeFunction("Signal", native_entry);
838
839 EnginePtr engine{builder.RunHeadless()};
840 ASSERT_NE(engine, nullptr);
841
842 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
843 windows_engine->UpdateSemanticsEnabled(true);
844
845 // Rely on timeout mechanism in CI.
846 while (!done) {
847 windows_engine->task_runner()->ProcessTasks();
848 }
849}
850
851// Verify the engine does not crash if it receives an accessibility event
852// it does not support yet.
853TEST_F(FlutterWindowsEngineTest, AccessibilityTooltip) {
854 fml::testing::LogCapture log_capture;
855
856 auto& context = GetContext();
857 WindowsConfigBuilder builder{context};
858 builder.SetDartEntrypoint("sendAccessibilityTooltipEvent");
859
860 bool done = false;
861 auto native_entry =
862 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
863 context.AddNativeFunction("Signal", native_entry);
864
865 ViewControllerPtr controller{builder.Run()};
866 ASSERT_NE(controller, nullptr);
867
868 auto engine = FlutterDesktopViewControllerGetEngine(controller.get());
869 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine);
870 windows_engine->UpdateSemanticsEnabled(true);
871
872 // Rely on timeout mechanism in CI.
873 while (!done) {
874 windows_engine->task_runner()->ProcessTasks();
875 }
876
877 // Verify no error was logged.
878 // Regression test for:
879 // https://github.com/flutter/flutter/issues/144274
880 EXPECT_EQ(log_capture.str().find("tooltip"), std::string::npos);
881}
882
884 public:
888
890 void,
891 Quit,
892 (std::optional<HWND>, std::optional<WPARAM>, std::optional<LPARAM>, UINT),
893 (override));
894 MOCK_METHOD(void, DispatchMessage, (HWND, UINT, WPARAM, LPARAM), (override));
895 MOCK_METHOD(bool, IsLastWindowOfProcess, (), (override));
897
904
905 std::function<void()> begin_processing_callback = nullptr;
906};
907
909 FlutterWindowsEngineBuilder builder{GetContext()};
910 builder.SetDartEntrypoint("exitTestExit");
911 bool finished = false;
912
913 auto engine = builder.Build();
914 auto window_binding_handler =
915 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
916 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
917
918 EngineModifier modifier(engine.get());
919 modifier.SetImplicitView(&view);
920 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
921 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
922 EXPECT_CALL(*handler, Quit)
923 .WillOnce([&finished](std::optional<HWND> hwnd,
924 std::optional<WPARAM> wparam,
925 std::optional<LPARAM> lparam,
926 UINT exit_code) { finished = exit_code == 0; });
927 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
928 modifier.SetLifecycleManager(std::move(handler));
929
930 engine->lifecycle_manager()->BeginProcessingExit();
931
932 engine->Run();
933
934 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
935 0);
936
937 // The test will only succeed when this while loop exits. Otherwise it will
938 // timeout.
939 while (!finished) {
940 engine->task_runner()->ProcessTasks();
941 }
942}
943
945 FlutterWindowsEngineBuilder builder{GetContext()};
946 builder.SetDartEntrypoint("exitTestCancel");
947 bool did_call = false;
948
949 auto engine = builder.Build();
950 auto window_binding_handler =
951 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
952 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
953
954 EngineModifier modifier(engine.get());
955 modifier.SetImplicitView(&view);
956 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
957 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
958 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
959 EXPECT_CALL(*handler, Quit).Times(0);
960 modifier.SetLifecycleManager(std::move(handler));
961 engine->lifecycle_manager()->BeginProcessingExit();
962
963 auto binary_messenger =
964 std::make_unique<BinaryMessengerImpl>(engine->messenger());
965 binary_messenger->SetMessageHandler(
966 "flutter/platform", [&did_call](const uint8_t* message,
967 size_t message_size, BinaryReply reply) {
968 std::string contents(message, message + message_size);
969 EXPECT_NE(contents.find("\"method\":\"System.exitApplication\""),
970 std::string::npos);
971 EXPECT_NE(contents.find("\"type\":\"required\""), std::string::npos);
972 EXPECT_NE(contents.find("\"exitCode\":0"), std::string::npos);
973 did_call = true;
974 char response[] = "";
975 reply(reinterpret_cast<uint8_t*>(response), 0);
976 });
977
978 engine->Run();
979
980 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
981 0);
982
983 while (!did_call) {
984 engine->task_runner()->ProcessTasks();
985 }
986}
987
988// Flutter consumes the first WM_CLOSE message to allow the app to cancel the
989// exit. If the app does not cancel the exit, Flutter synthesizes a second
990// WM_CLOSE message.
991TEST_F(FlutterWindowsEngineTest, TestExitSecondCloseMessage) {
992 FlutterWindowsEngineBuilder builder{GetContext()};
993 builder.SetDartEntrypoint("exitTestExit");
994 bool second_close = false;
995
996 auto engine = builder.Build();
997 auto window_binding_handler =
998 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
999 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1000
1001 EngineModifier modifier(engine.get());
1002 modifier.SetImplicitView(&view);
1003 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1004 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1005 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(true));
1006 EXPECT_CALL(*handler, Quit)
1007 .WillOnce([handler_ptr = handler.get()](
1008 std::optional<HWND> hwnd, std::optional<WPARAM> wparam,
1009 std::optional<LPARAM> lparam, UINT exit_code) {
1010 handler_ptr->WindowsLifecycleManager::Quit(hwnd, wparam, lparam,
1011 exit_code);
1012 });
1013 EXPECT_CALL(*handler, DispatchMessage)
1014 .WillRepeatedly(
1015 [&engine](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
1016 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1017 hwnd, msg, wparam, lparam);
1018 });
1019 modifier.SetLifecycleManager(std::move(handler));
1020 engine->lifecycle_manager()->BeginProcessingExit();
1021
1022 engine->Run();
1023
1024 // This delegate will be registered after the lifecycle manager, so it will be
1025 // called only when a message is not consumed by the lifecycle manager. This
1026 // should be called on the second, synthesized WM_CLOSE message that the
1027 // lifecycle manager posts.
1028 engine->window_proc_delegate_manager()->RegisterTopLevelWindowProcDelegate(
1029 [](HWND hwnd, UINT message, WPARAM wpar, LPARAM lpar, void* user_data,
1030 LRESULT* result) {
1031 switch (message) {
1032 case WM_CLOSE: {
1033 bool* called = reinterpret_cast<bool*>(user_data);
1034 *called = true;
1035 return true;
1036 }
1037 }
1038 return false;
1039 },
1040 reinterpret_cast<void*>(&second_close));
1041
1042 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1043 0);
1044
1045 while (!second_close) {
1046 engine->task_runner()->ProcessTasks();
1047 }
1048}
1049
1050TEST_F(FlutterWindowsEngineTest, TestExitCloseMultiWindow) {
1051 FlutterWindowsEngineBuilder builder{GetContext()};
1052 builder.SetDartEntrypoint("exitTestExit");
1053 bool finished = false;
1054
1055 auto engine = builder.Build();
1056 auto window_binding_handler =
1057 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1058 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1059
1060 EngineModifier modifier(engine.get());
1061 modifier.SetImplicitView(&view);
1062 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1063 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1064 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce([&finished]() {
1065 finished = true;
1066 return false;
1067 });
1068 // Quit should not be called when there is more than one window.
1069 EXPECT_CALL(*handler, Quit).Times(0);
1070 modifier.SetLifecycleManager(std::move(handler));
1071 engine->lifecycle_manager()->BeginProcessingExit();
1072
1073 engine->Run();
1074
1075 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1076 0);
1077
1078 while (!finished) {
1079 engine->task_runner()->ProcessTasks();
1080 }
1081}
1082
1083TEST_F(FlutterWindowsEngineTest, LifecycleManagerDisabledByDefault) {
1084 FlutterWindowsEngineBuilder builder{GetContext()};
1085
1086 auto engine = builder.Build();
1087 auto window_binding_handler =
1088 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1089 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1090
1091 EngineModifier modifier(engine.get());
1092 modifier.SetImplicitView(&view);
1093 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1094 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1095 EXPECT_CALL(*handler, IsLastWindowOfProcess).Times(0);
1096 modifier.SetLifecycleManager(std::move(handler));
1097
1098 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1099 0);
1100}
1101
1102TEST_F(FlutterWindowsEngineTest, EnableApplicationLifecycle) {
1103 FlutterWindowsEngineBuilder builder{GetContext()};
1104
1105 auto engine = builder.Build();
1106 auto window_binding_handler =
1107 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1108 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1109
1110 EngineModifier modifier(engine.get());
1111 modifier.SetImplicitView(&view);
1112 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1113 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1114 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
1115 modifier.SetLifecycleManager(std::move(handler));
1116 engine->lifecycle_manager()->BeginProcessingExit();
1117
1118 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1119 0);
1120}
1121
1122TEST_F(FlutterWindowsEngineTest, ApplicationLifecycleExternalWindow) {
1123 FlutterWindowsEngineBuilder builder{GetContext()};
1124
1125 auto engine = builder.Build();
1126 auto window_binding_handler =
1127 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1128 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1129
1130 EngineModifier modifier(engine.get());
1131 modifier.SetImplicitView(&view);
1132 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1133 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1134 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
1135 modifier.SetLifecycleManager(std::move(handler));
1136 engine->lifecycle_manager()->BeginProcessingExit();
1137
1138 engine->lifecycle_manager()->ExternalWindowMessage(0, WM_CLOSE, 0, 0);
1139}
1140
1141TEST_F(FlutterWindowsEngineTest, LifecycleStateTransition) {
1142 FlutterWindowsEngineBuilder builder{GetContext()};
1143
1144 auto engine = builder.Build();
1145 auto window_binding_handler =
1146 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1147 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1148
1149 EngineModifier modifier(engine.get());
1150 modifier.SetImplicitView(&view);
1151 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1152 engine->Run();
1153
1154 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1155 (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1156
1157 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1158 PumpMessage();
1159 }
1160
1161 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1163
1164 engine->lifecycle_manager()->OnWindowStateEvent((HWND)1,
1166
1167 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1168 PumpMessage();
1169 }
1170
1171 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1173
1174 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1175 (HWND)1, WM_SIZE, SIZE_MINIMIZED, 0);
1176
1177 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1178 PumpMessage();
1179 }
1180
1181 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1183
1184 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1185 (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1186
1187 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1188 PumpMessage();
1189 }
1190
1191 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1193}
1194
1195TEST_F(FlutterWindowsEngineTest, ExternalWindowMessage) {
1196 FlutterWindowsEngineBuilder builder{GetContext()};
1197
1198 auto engine = builder.Build();
1199 auto window_binding_handler =
1200 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1201 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1202
1203 EngineModifier modifier(engine.get());
1204 modifier.SetImplicitView(&view);
1205 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1206 // Sets lifecycle state to resumed.
1207 engine->Run();
1208
1209 // Ensure HWND(1) is in the set of visible windows before hiding it.
1210 engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1211 TRUE, NULL);
1212 engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1213 FALSE, NULL);
1214
1215 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1216 PumpMessage();
1217 }
1218
1219 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1221}
1222
1224 FlutterWindowsEngineBuilder builder{GetContext()};
1225 HWND outer = reinterpret_cast<HWND>(1);
1226 HWND inner = reinterpret_cast<HWND>(2);
1227
1228 auto engine = builder.Build();
1229 auto window_binding_handler =
1230 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1231 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1232 ON_CALL(view, GetWindowHandle).WillByDefault([=]() { return inner; });
1233
1234 EngineModifier modifier(engine.get());
1235 modifier.SetImplicitView(&view);
1236 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1237 // Sets lifecycle state to resumed.
1238 engine->Run();
1239
1240 // Show both top-level and Flutter window.
1241 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1242 outer, WM_SHOWWINDOW, TRUE, NULL);
1243 view.OnWindowStateEvent(inner, WindowStateEvent::kShow);
1244 view.OnWindowStateEvent(inner, WindowStateEvent::kFocus);
1245
1246 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1247 PumpMessage();
1248 }
1249
1250 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1252
1253 // Hide Flutter window, but not top level window.
1254 view.OnWindowStateEvent(inner, WindowStateEvent::kHide);
1255
1256 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1257 PumpMessage();
1258 }
1259
1260 // The top-level window is still visible, so we ought not enter hidden state.
1261 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1263}
1264
1265TEST_F(FlutterWindowsEngineTest, EnableLifecycleState) {
1266 FlutterWindowsEngineBuilder builder{GetContext()};
1267 builder.SetDartEntrypoint("enableLifecycleTest");
1268 bool finished = false;
1269
1270 auto engine = builder.Build();
1271 auto window_binding_handler =
1272 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1273 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1274
1275 EngineModifier modifier(engine.get());
1276 modifier.SetImplicitView(&view);
1277 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1278 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1279 EXPECT_CALL(*handler, SetLifecycleState)
1280 .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1281 handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1282 });
1283 modifier.SetLifecycleManager(std::move(handler));
1284
1285 auto binary_messenger =
1286 std::make_unique<BinaryMessengerImpl>(engine->messenger());
1287 // Mark the test only as completed on receiving an inactive state message.
1288 binary_messenger->SetMessageHandler(
1289 "flutter/unittest", [&finished](const uint8_t* message,
1290 size_t message_size, BinaryReply reply) {
1291 std::string contents(message, message + message_size);
1292 EXPECT_NE(contents.find("AppLifecycleState.inactive"),
1293 std::string::npos);
1294 finished = true;
1295 char response[] = "";
1296 reply(reinterpret_cast<uint8_t*>(response), 0);
1297 });
1298
1299 engine->Run();
1300
1301 // Test that setting the state before enabling lifecycle does nothing.
1302 HWND hwnd = reinterpret_cast<HWND>(1);
1303 view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1304 view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1305 EXPECT_FALSE(finished);
1306
1307 // Test that we can set the state afterwards.
1308
1309 engine->lifecycle_manager()->BeginProcessingLifecycle();
1310 view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1311
1312 while (!finished) {
1313 engine->task_runner()->ProcessTasks();
1314 }
1315}
1316
1317TEST_F(FlutterWindowsEngineTest, LifecycleStateToFrom) {
1318 FlutterWindowsEngineBuilder builder{GetContext()};
1319 builder.SetDartEntrypoint("enableLifecycleToFrom");
1320 bool enabled_lifecycle = false;
1321 bool dart_responded = false;
1322
1323 auto engine = builder.Build();
1324 auto window_binding_handler =
1325 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1326 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1327
1328 EngineModifier modifier(engine.get());
1329 modifier.SetImplicitView(&view);
1330 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1331 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1332 EXPECT_CALL(*handler, SetLifecycleState)
1333 .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1334 handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1335 });
1336 handler->begin_processing_callback = [&]() { enabled_lifecycle = true; };
1337 modifier.SetLifecycleManager(std::move(handler));
1338
1339 auto binary_messenger =
1340 std::make_unique<BinaryMessengerImpl>(engine->messenger());
1341 binary_messenger->SetMessageHandler(
1342 "flutter/unittest",
1343 [&](const uint8_t* message, size_t message_size, BinaryReply reply) {
1344 std::string contents(message, message + message_size);
1345 EXPECT_NE(contents.find("AppLifecycleState."), std::string::npos);
1346 dart_responded = true;
1347 char response[] = "";
1348 reply(reinterpret_cast<uint8_t*>(response), 0);
1349 });
1350
1351 engine->Run();
1352
1353 while (!enabled_lifecycle) {
1354 engine->task_runner()->ProcessTasks();
1355 }
1356
1357 HWND hwnd = reinterpret_cast<HWND>(1);
1358 view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1359 view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1360
1361 while (!dart_responded) {
1362 engine->task_runner()->ProcessTasks();
1363 }
1364}
1365
1367 FlutterWindowsEngineBuilder builder{GetContext()};
1368 builder.SetDartEntrypoint("enableLifecycleToFrom");
1369
1370 auto engine = builder.Build();
1371 auto window_binding_handler =
1372 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1373 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1374
1375 EngineModifier modifier(engine.get());
1376 modifier.SetImplicitView(&view);
1377 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1378
1379 bool lifecycle_began = false;
1380 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1381 handler->begin_processing_callback = [&]() { lifecycle_began = true; };
1382 modifier.SetLifecycleManager(std::move(handler));
1383
1384 engine->Run();
1385
1386 while (!lifecycle_began) {
1387 engine->task_runner()->ProcessTasks();
1388 }
1389}
1390
1391TEST_F(FlutterWindowsEngineTest, ReceivePlatformViewMessage) {
1392 FlutterWindowsEngineBuilder builder{GetContext()};
1393 builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1394 auto engine = builder.Build();
1395
1396 EngineModifier modifier{engine.get()};
1397 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1398
1399 bool received_call = false;
1400
1401 auto manager = std::make_unique<MockPlatformViewManager>(engine.get());
1402 EXPECT_CALL(*manager, AddPlatformView)
1403 .WillOnce([&](PlatformViewId id, std::string_view type_name) {
1404 received_call = true;
1405 return true;
1406 });
1407 modifier.SetPlatformViewPlugin(std::move(manager));
1408
1409 engine->Run();
1410
1411 while (!received_call) {
1412 engine->task_runner()->ProcessTasks();
1413 }
1414}
1415
1416TEST_F(FlutterWindowsEngineTest, AddViewFailureDoesNotHang) {
1417 FlutterWindowsEngineBuilder builder{GetContext()};
1418 auto engine = builder.Build();
1419
1420 EngineModifier modifier{engine.get()};
1421
1422 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1423 modifier.embedder_api().AddView = MOCK_ENGINE_PROC(
1424 AddView,
1426 const FlutterAddViewInfo* info) { return kInternalInconsistency; });
1427
1428 ASSERT_TRUE(engine->Run());
1429
1430 // Create the first view. This is the implicit view and isn't added to the
1431 // engine.
1432 auto implicit_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1433
1434 std::unique_ptr<FlutterWindowsView> implicit_view =
1435 engine->CreateView(std::move(implicit_window),
1436 /*is_sized_to_content=*/false, BoxConstraints());
1437
1438 EXPECT_TRUE(implicit_view);
1439
1440 // Create a second view. The embedder attempts to add it to the engine.
1441 auto second_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1442
1443 EXPECT_DEBUG_DEATH(
1444 engine->CreateView(std::move(second_window),
1445 /*is_sized_to_content=*/false, BoxConstraints()),
1446 "FlutterEngineAddView returned an unexpected result");
1447}
1448
1449TEST_F(FlutterWindowsEngineTest, RemoveViewFailureDoesNotHang) {
1450 FlutterWindowsEngineBuilder builder{GetContext()};
1451 builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1452 auto engine = builder.Build();
1453
1454 EngineModifier modifier{engine.get()};
1455
1456 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1457 modifier.embedder_api().RemoveView = MOCK_ENGINE_PROC(
1458 RemoveView,
1460 const FlutterRemoveViewInfo* info) { return kInternalInconsistency; });
1461
1462 ASSERT_TRUE(engine->Run());
1463 EXPECT_DEBUG_DEATH(engine->RemoveView(123),
1464 "FlutterEngineRemoveView returned an unexpected result");
1465}
1466
1468 auto& context = GetContext();
1469 WindowsConfigBuilder builder{context};
1470 builder.SetDartEntrypoint("mergedUIThread");
1471 builder.SetUIThreadPolicy(FlutterDesktopUIThreadPolicy::RunOnPlatformThread);
1472
1473 std::optional<std::thread::id> ui_thread_id;
1474
1475 auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1476 ui_thread_id = std::this_thread::get_id();
1477 });
1478 context.AddNativeFunction("Signal", native_entry);
1479
1480 EnginePtr engine{builder.RunHeadless()};
1481 while (!ui_thread_id) {
1482 PumpMessage();
1483 }
1484 ASSERT_EQ(*ui_thread_id, std::this_thread::get_id());
1485}
1486
1487TEST_F(FlutterWindowsEngineTest, OnViewFocusChangeRequest) {
1488 FlutterWindowsEngineBuilder builder{GetContext()};
1489 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
1490 auto window_binding_handler =
1491 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1492 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1493
1494 EngineModifier modifier(engine.get());
1495 modifier.SetImplicitView(&view);
1496
1498 request.view_id = kImplicitViewId;
1499
1500 EXPECT_CALL(view, Focus()).WillOnce(Return(true));
1501 modifier.OnViewFocusChangeRequest(&request);
1502}
1503
1504TEST_F(FlutterWindowsEngineTest, UpdateSemanticsMultiView) {
1505 auto& context = GetContext();
1506 WindowsConfigBuilder builder{context};
1507 builder.SetDartEntrypoint("sendSemanticsTreeInfo");
1508
1509 // Setup: a signal for when we have send out all of our semantics updates
1510 bool done = false;
1511 auto native_entry =
1512 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
1513 context.AddNativeFunction("Signal", native_entry);
1514
1515 // Setup: Create the engine and two views + enable semantics
1516 EnginePtr engine{builder.RunHeadless()};
1517 ASSERT_NE(engine, nullptr);
1518
1519 auto window_binding_handler1 =
1520 std::make_unique<NiceMock<MockWindowBindingHandler>>();
1521 auto window_binding_handler2 =
1522 std::make_unique<NiceMock<MockWindowBindingHandler>>();
1523
1524 // The following mocks are required by
1525 // FlutterWindowsView::CreateWindowMetricsEvent so that we create a valid
1526 // view.
1527 EXPECT_CALL(*window_binding_handler1, GetPhysicalWindowBounds)
1528 .WillRepeatedly(testing::Return(PhysicalWindowBounds{100, 100}));
1529 EXPECT_CALL(*window_binding_handler1, GetDpiScale)
1530 .WillRepeatedly(testing::Return(96.0));
1531 EXPECT_CALL(*window_binding_handler2, GetPhysicalWindowBounds)
1532 .WillRepeatedly(testing::Return(PhysicalWindowBounds{200, 200}));
1533 EXPECT_CALL(*window_binding_handler2, GetDpiScale)
1534 .WillRepeatedly(testing::Return(96.0));
1535
1536 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
1537 EngineModifier modifier{windows_engine};
1538 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1539
1540 // We want to avoid adding an implicit view as the first view
1541 modifier.SetNextViewId(kImplicitViewId + 1);
1542
1543 auto view1 = windows_engine->CreateView(std::move(window_binding_handler1),
1544 /*is_sized_to_content=*/false,
1545 BoxConstraints());
1546 auto view2 = windows_engine->CreateView(std::move(window_binding_handler2),
1547 /*is_sized_to_content=*/false,
1548 BoxConstraints());
1549
1550 // Act: UpdateSemanticsEnabled will trigger the semantics updates
1551 // to get sent.
1552 windows_engine->UpdateSemanticsEnabled(true);
1553
1554 while (!done) {
1555 windows_engine->task_runner()->ProcessTasks();
1556 }
1557
1558 auto accessibility_bridge1 = view1->accessibility_bridge().lock();
1559 auto accessibility_bridge2 = view2->accessibility_bridge().lock();
1560
1561 // Expect: that the semantics trees are updated with their
1562 // respective nodes.
1563 while (
1564 !accessibility_bridge1->GetPlatformNodeFromTree(view1->view_id() + 1)) {
1565 windows_engine->task_runner()->ProcessTasks();
1566 }
1567
1568 while (
1569 !accessibility_bridge2->GetPlatformNodeFromTree(view2->view_id() + 1)) {
1570 windows_engine->task_runner()->ProcessTasks();
1571 }
1572
1573 // Rely on timeout mechanism in CI.
1574 auto tree1 = accessibility_bridge1->GetTree();
1575 auto tree2 = accessibility_bridge2->GetTree();
1576 EXPECT_NE(tree1->GetFromId(view1->view_id() + 1), nullptr);
1577 EXPECT_NE(tree2->GetFromId(view2->view_id() + 1), nullptr);
1578}
1579
1580} // namespace testing
1581} // namespace flutter
void SetLifecycleManager(std::unique_ptr< WindowsLifecycleManager > &&handler)
void SetFrameInterval(uint64_t frame_interval_nanos)
void OnViewFocusChangeRequest(const FlutterViewFocusChangeRequest *request)
void SetEGLManager(std::unique_ptr< egl::Manager > egl_manager)
void SetStartTime(uint64_t start_time_nanos)
Reset the start_time field that is used to align vsync events.
FlutterEngineProcTable & embedder_api()
void SetImplicitView(FlutterWindowsView *view)
virtual void Quit(std::optional< HWND > window, std::optional< WPARAM > wparam, std::optional< LPARAM > lparam, UINT exit_code)
virtual void SetLifecycleState(AppLifecycleState state)
void SetSwitches(std::vector< std::string > switches)
void SetWindowsProcTable(std::shared_ptr< WindowsProcTable > windows_proc_table)
MOCK_METHOD(bool, Focus,(),(override))
MOCK_METHOD(void, NotifyWinEventWrapper,(ui::AXPlatformNodeWin *, ax::mojom::Event),(override))
MockFlutterWindowsView(FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > wbh)
MOCK_METHOD(HWND, GetWindowHandle,(),(const, override))
MOCK_METHOD(void, DispatchMessage,(HWND, UINT, WPARAM, LPARAM),(override))
MOCK_METHOD(void, Quit,(std::optional< HWND >, std::optional< WPARAM >, std::optional< LPARAM >, UINT),(override))
MOCK_METHOD(void, SetLifecycleState,(AppLifecycleState),(override))
MOCK_METHOD(bool, IsLastWindowOfProcess,(),(override))
void SetDartEntrypoint(std::string_view entrypoint)
static MallocMapping Copy(const T *begin, const T *end)
Definition mapping.h:162
static constexpr TimeDelta FromMilliseconds(int64_t millis)
Definition time_delta.h:46
constexpr int64_t ToNanoseconds() const
Definition time_delta.h:61
constexpr TimeDelta ToEpochDelta() const
Definition time_point.h:52
static TimePoint Now()
Definition time_point.cc:49
std::string type_name
#define FLUTTER_API_SYMBOL(symbol)
Definition embedder.h:67
@ kOpenGL
Definition embedder.h:80
FlutterAccessibilityFeature
Definition embedder.h:91
@ kFlutterAccessibilityFeatureHighContrast
Request that UI be rendered with darker colors.
Definition embedder.h:105
@ kInternalInconsistency
Definition embedder.h:76
FlutterEngineDisplaysUpdateType
Definition embedder.h:2348
@ kFlutterEngineDisplaysUpdateTypeStartup
Definition embedder.h:2354
@ kBackground
Suitable for threads that shouldn't disrupt high priority work.
Definition embedder.h:378
@ kDisplay
Suitable for threads which generate data for the display.
Definition embedder.h:382
@ kNormal
Default priority level.
Definition embedder.h:380
@ kRaster
Suitable for thread which raster data.
Definition embedder.h:384
@ kFlutterSemanticsActionDismiss
A request that the node should be dismissed.
Definition embedder.h:167
#define FLUTTER_ENGINE_VERSION
Definition embedder.h:70
GLFWwindow * window
Definition main.cc:60
FlutterEngine engine
Definition main.cc:84
FlView * view
const char * message
return TRUE
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
const gchar * channel
const gchar FlBinaryMessengerMessageHandler handler
void(* FlutterDesktopBinaryReply)(const uint8_t *data, size_t data_size, void *user_data)
@ RunOnPlatformThread
int64_t PlatformViewId
FlutterDesktopBinaryReply callback
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName)
Definition macros.h:27
size_t length
TEST_F(DisplayListTest, Defaults)
std::unique_ptr< FlutterDesktopEngine, EngineDeleter > EnginePtr
std::unique_ptr< FlutterDesktopViewController, ViewControllerDeleter > ViewControllerPtr
void MockEmbedderApiForKeyboard(EngineModifier &modifier, std::shared_ptr< MockKeyResponseController > response_controller)
constexpr int64_t kImplicitViewId
UINT GetDpiForMonitor(HMONITOR monitor)
Definition dpi_utils.cc:132
it will be possible to load the file into Perfetto s trace viewer use test Running tests that layout and measure text will not yield consistent results across various platforms Enabling this option will make font resolution default to the Ahem test font on all disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font manager
static void DispatchSemanticsAction(JNIEnv *env, jobject jcaller, jlong shell_holder, jint id, jint action, jobject args, jint args_position)
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
static void WindowsPlatformThreadPrioritySetter(FlutterThreadPriority priority)
static void ScheduleFrame(JNIEnv *env, jobject jcaller, jlong shell_holder)
Definition ref_ptr.h:261
#define MOCK_ENGINE_PROC(proc, mock_impl)
FlutterEngineScheduleFrameFnPtr ScheduleFrame
Definition embedder.h:3776
FlutterEnginePlatformMessageReleaseResponseHandleFnPtr PlatformMessageReleaseResponseHandle
Definition embedder.h:3752
FlutterEngineUpdateLocalesFnPtr UpdateLocales
Definition embedder.h:3769
FlutterEngineRunsAOTCompiledDartCodeFnPtr RunsAOTCompiledDartCode
Definition embedder.h:3770
FlutterEngineSetNextFrameCallbackFnPtr SetNextFrameCallback
Definition embedder.h:3777
FlutterEngineShutdownFnPtr Shutdown
Definition embedder.h:3741
FlutterEngineOnVsyncFnPtr OnVsync
Definition embedder.h:3761
FlutterEngineGetCurrentTimeFnPtr GetCurrentTime
Definition embedder.h:3767
FlutterEngineNotifyDisplayUpdateFnPtr NotifyDisplayUpdate
Definition embedder.h:3775
FlutterEngineSendPlatformMessageFnPtr SendPlatformMessage
Definition embedder.h:3748
FlutterEnginePlatformMessageCreateResponseHandleFnPtr PlatformMessageCreateResponseHandle
Definition embedder.h:3750
FlutterEngineRunFnPtr Run
Definition embedder.h:3740
FlutterEngineUpdateAccessibilityFeaturesFnPtr UpdateAccessibilityFeatures
Definition embedder.h:3759
FlutterEnginePostRenderThreadTaskFnPtr PostRenderThreadTask
Definition embedder.h:3766
FlutterEngineSendSemanticsActionFnPtr SendSemanticsAction
Definition embedder.h:3781
FlutterRendererType type
Definition embedder.h:1038
FlutterViewId view_id
The identifier of the view that received the focus event.
Definition embedder.h:1257
std::string str() const
Definition logging.cc:122
FlutterDesktopEngineRef FlutterDesktopViewControllerGetEngine(FlutterDesktopViewControllerRef controller)
const size_t start
#define CREATE_NATIVE_ENTRY(native_entry)
struct tagMSG MSG
LONG_PTR LRESULT
unsigned int UINT
LONG_PTR LPARAM
#define GetCurrentTime()
#define GetMessage
UINT_PTR WPARAM
#define DispatchMessage
unsigned long DWORD