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)
774
776 NotifyWinEventWrapper,
777 (ui::AXPlatformNodeWin*, ax::mojom::Event),
778 (override));
779 MOCK_METHOD(HWND, GetWindowHandle, (), (const, override));
780 MOCK_METHOD(bool, Focus, (), (override));
781
782 private:
783 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
784};
785
786// Verify the view is notified of accessibility announcements.
787TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncement) {
788 auto& context = GetContext();
789 WindowsConfigBuilder builder{context};
790 builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
791
792 bool done = false;
793 auto native_entry =
794 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
795 context.AddNativeFunction("Signal", native_entry);
796
797 EnginePtr engine{builder.RunHeadless()};
798 ASSERT_NE(engine, nullptr);
799
800 ui::AXPlatformNodeDelegateBase parent_delegate;
801 AlertPlatformNodeDelegate delegate{parent_delegate};
802
803 auto window_binding_handler =
804 std::make_unique<NiceMock<MockWindowBindingHandler>>();
805 EXPECT_CALL(*window_binding_handler, GetAlertDelegate)
806 .WillOnce(Return(&delegate));
807
808 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
809 MockFlutterWindowsView view{windows_engine,
810 std::move(window_binding_handler)};
811 EngineModifier modifier{windows_engine};
812 modifier.SetImplicitView(&view);
813
814 windows_engine->UpdateSemanticsEnabled(true);
815
816 EXPECT_CALL(view, NotifyWinEventWrapper).Times(1);
817
818 // Rely on timeout mechanism in CI.
819 while (!done) {
820 windows_engine->task_runner()->ProcessTasks();
821 }
822}
823
824// Verify the app can send accessibility announcements while in headless mode.
825TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncementHeadless) {
826 auto& context = GetContext();
827 WindowsConfigBuilder builder{context};
828 builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
829
830 bool done = false;
831 auto native_entry =
832 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
833 context.AddNativeFunction("Signal", native_entry);
834
835 EnginePtr engine{builder.RunHeadless()};
836 ASSERT_NE(engine, nullptr);
837
838 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
839 windows_engine->UpdateSemanticsEnabled(true);
840
841 // Rely on timeout mechanism in CI.
842 while (!done) {
843 windows_engine->task_runner()->ProcessTasks();
844 }
845}
846
847// Verify the engine does not crash if it receives an accessibility event
848// it does not support yet.
849TEST_F(FlutterWindowsEngineTest, AccessibilityTooltip) {
850 fml::testing::LogCapture log_capture;
851
852 auto& context = GetContext();
853 WindowsConfigBuilder builder{context};
854 builder.SetDartEntrypoint("sendAccessibilityTooltipEvent");
855
856 bool done = false;
857 auto native_entry =
858 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
859 context.AddNativeFunction("Signal", native_entry);
860
861 ViewControllerPtr controller{builder.Run()};
862 ASSERT_NE(controller, nullptr);
863
864 auto engine = FlutterDesktopViewControllerGetEngine(controller.get());
865 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine);
866 windows_engine->UpdateSemanticsEnabled(true);
867
868 // Rely on timeout mechanism in CI.
869 while (!done) {
870 windows_engine->task_runner()->ProcessTasks();
871 }
872
873 // Verify no error was logged.
874 // Regression test for:
875 // https://github.com/flutter/flutter/issues/144274
876 EXPECT_EQ(log_capture.str().find("tooltip"), std::string::npos);
877}
878
880 public:
884
886 void,
887 Quit,
888 (std::optional<HWND>, std::optional<WPARAM>, std::optional<LPARAM>, UINT),
889 (override));
890 MOCK_METHOD(void, DispatchMessage, (HWND, UINT, WPARAM, LPARAM), (override));
891 MOCK_METHOD(bool, IsLastWindowOfProcess, (), (override));
893
900
901 std::function<void()> begin_processing_callback = nullptr;
902};
903
905 FlutterWindowsEngineBuilder builder{GetContext()};
906 builder.SetDartEntrypoint("exitTestExit");
907 bool finished = false;
908
909 auto engine = builder.Build();
910 auto window_binding_handler =
911 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
912 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
913
914 EngineModifier modifier(engine.get());
915 modifier.SetImplicitView(&view);
916 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
917 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
918 EXPECT_CALL(*handler, Quit)
919 .WillOnce([&finished](std::optional<HWND> hwnd,
920 std::optional<WPARAM> wparam,
921 std::optional<LPARAM> lparam,
922 UINT exit_code) { finished = exit_code == 0; });
923 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
924 modifier.SetLifecycleManager(std::move(handler));
925
926 engine->lifecycle_manager()->BeginProcessingExit();
927
928 engine->Run();
929
930 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
931 0);
932
933 // The test will only succeed when this while loop exits. Otherwise it will
934 // timeout.
935 while (!finished) {
936 engine->task_runner()->ProcessTasks();
937 }
938}
939
941 FlutterWindowsEngineBuilder builder{GetContext()};
942 builder.SetDartEntrypoint("exitTestCancel");
943 bool did_call = false;
944
945 auto engine = builder.Build();
946 auto window_binding_handler =
947 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
948 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
949
950 EngineModifier modifier(engine.get());
951 modifier.SetImplicitView(&view);
952 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
953 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
954 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
955 EXPECT_CALL(*handler, Quit).Times(0);
956 modifier.SetLifecycleManager(std::move(handler));
957 engine->lifecycle_manager()->BeginProcessingExit();
958
959 auto binary_messenger =
960 std::make_unique<BinaryMessengerImpl>(engine->messenger());
961 binary_messenger->SetMessageHandler(
962 "flutter/platform", [&did_call](const uint8_t* message,
963 size_t message_size, BinaryReply reply) {
964 std::string contents(message, message + message_size);
965 EXPECT_NE(contents.find("\"method\":\"System.exitApplication\""),
966 std::string::npos);
967 EXPECT_NE(contents.find("\"type\":\"required\""), std::string::npos);
968 EXPECT_NE(contents.find("\"exitCode\":0"), std::string::npos);
969 did_call = true;
970 char response[] = "";
971 reply(reinterpret_cast<uint8_t*>(response), 0);
972 });
973
974 engine->Run();
975
976 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
977 0);
978
979 while (!did_call) {
980 engine->task_runner()->ProcessTasks();
981 }
982}
983
984// Flutter consumes the first WM_CLOSE message to allow the app to cancel the
985// exit. If the app does not cancel the exit, Flutter synthesizes a second
986// WM_CLOSE message.
987TEST_F(FlutterWindowsEngineTest, TestExitSecondCloseMessage) {
988 FlutterWindowsEngineBuilder builder{GetContext()};
989 builder.SetDartEntrypoint("exitTestExit");
990 bool second_close = false;
991
992 auto engine = builder.Build();
993 auto window_binding_handler =
994 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
995 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
996
997 EngineModifier modifier(engine.get());
998 modifier.SetImplicitView(&view);
999 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1000 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1001 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(true));
1002 EXPECT_CALL(*handler, Quit)
1003 .WillOnce([handler_ptr = handler.get()](
1004 std::optional<HWND> hwnd, std::optional<WPARAM> wparam,
1005 std::optional<LPARAM> lparam, UINT exit_code) {
1006 handler_ptr->WindowsLifecycleManager::Quit(hwnd, wparam, lparam,
1007 exit_code);
1008 });
1009 EXPECT_CALL(*handler, DispatchMessage)
1010 .WillRepeatedly(
1011 [&engine](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
1012 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1013 hwnd, msg, wparam, lparam);
1014 });
1015 modifier.SetLifecycleManager(std::move(handler));
1016 engine->lifecycle_manager()->BeginProcessingExit();
1017
1018 engine->Run();
1019
1020 // This delegate will be registered after the lifecycle manager, so it will be
1021 // called only when a message is not consumed by the lifecycle manager. This
1022 // should be called on the second, synthesized WM_CLOSE message that the
1023 // lifecycle manager posts.
1024 engine->window_proc_delegate_manager()->RegisterTopLevelWindowProcDelegate(
1025 [](HWND hwnd, UINT message, WPARAM wpar, LPARAM lpar, void* user_data,
1026 LRESULT* result) {
1027 switch (message) {
1028 case WM_CLOSE: {
1029 bool* called = reinterpret_cast<bool*>(user_data);
1030 *called = true;
1031 return true;
1032 }
1033 }
1034 return false;
1035 },
1036 reinterpret_cast<void*>(&second_close));
1037
1038 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1039 0);
1040
1041 while (!second_close) {
1042 engine->task_runner()->ProcessTasks();
1043 }
1044}
1045
1046TEST_F(FlutterWindowsEngineTest, TestExitCloseMultiWindow) {
1047 FlutterWindowsEngineBuilder builder{GetContext()};
1048 builder.SetDartEntrypoint("exitTestExit");
1049 bool finished = false;
1050
1051 auto engine = builder.Build();
1052 auto window_binding_handler =
1053 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1054 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1055
1056 EngineModifier modifier(engine.get());
1057 modifier.SetImplicitView(&view);
1058 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1059 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1060 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce([&finished]() {
1061 finished = true;
1062 return false;
1063 });
1064 // Quit should not be called when there is more than one window.
1065 EXPECT_CALL(*handler, Quit).Times(0);
1066 modifier.SetLifecycleManager(std::move(handler));
1067 engine->lifecycle_manager()->BeginProcessingExit();
1068
1069 engine->Run();
1070
1071 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1072 0);
1073
1074 while (!finished) {
1075 engine->task_runner()->ProcessTasks();
1076 }
1077}
1078
1079TEST_F(FlutterWindowsEngineTest, LifecycleManagerDisabledByDefault) {
1080 FlutterWindowsEngineBuilder builder{GetContext()};
1081
1082 auto engine = builder.Build();
1083 auto window_binding_handler =
1084 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1085 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1086
1087 EngineModifier modifier(engine.get());
1088 modifier.SetImplicitView(&view);
1089 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1090 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1091 EXPECT_CALL(*handler, IsLastWindowOfProcess).Times(0);
1092 modifier.SetLifecycleManager(std::move(handler));
1093
1094 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1095 0);
1096}
1097
1098TEST_F(FlutterWindowsEngineTest, EnableApplicationLifecycle) {
1099 FlutterWindowsEngineBuilder builder{GetContext()};
1100
1101 auto engine = builder.Build();
1102 auto window_binding_handler =
1103 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1104 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1105
1106 EngineModifier modifier(engine.get());
1107 modifier.SetImplicitView(&view);
1108 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1109 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1110 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
1111 modifier.SetLifecycleManager(std::move(handler));
1112 engine->lifecycle_manager()->BeginProcessingExit();
1113
1114 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1115 0);
1116}
1117
1118TEST_F(FlutterWindowsEngineTest, ApplicationLifecycleExternalWindow) {
1119 FlutterWindowsEngineBuilder builder{GetContext()};
1120
1121 auto engine = builder.Build();
1122 auto window_binding_handler =
1123 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1124 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1125
1126 EngineModifier modifier(engine.get());
1127 modifier.SetImplicitView(&view);
1128 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1129 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1130 EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
1131 modifier.SetLifecycleManager(std::move(handler));
1132 engine->lifecycle_manager()->BeginProcessingExit();
1133
1134 engine->lifecycle_manager()->ExternalWindowMessage(0, WM_CLOSE, 0, 0);
1135}
1136
1137TEST_F(FlutterWindowsEngineTest, LifecycleStateTransition) {
1138 FlutterWindowsEngineBuilder builder{GetContext()};
1139
1140 auto engine = builder.Build();
1141 auto window_binding_handler =
1142 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1143 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1144
1145 EngineModifier modifier(engine.get());
1146 modifier.SetImplicitView(&view);
1147 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1148 engine->Run();
1149
1150 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1151 (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1152
1153 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1154 PumpMessage();
1155 }
1156
1157 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1159
1160 engine->lifecycle_manager()->OnWindowStateEvent((HWND)1,
1162
1163 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1164 PumpMessage();
1165 }
1166
1167 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1169
1170 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1171 (HWND)1, WM_SIZE, SIZE_MINIMIZED, 0);
1172
1173 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1174 PumpMessage();
1175 }
1176
1177 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1179
1180 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1181 (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1182
1183 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1184 PumpMessage();
1185 }
1186
1187 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1189}
1190
1191TEST_F(FlutterWindowsEngineTest, ExternalWindowMessage) {
1192 FlutterWindowsEngineBuilder builder{GetContext()};
1193
1194 auto engine = builder.Build();
1195 auto window_binding_handler =
1196 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1197 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1198
1199 EngineModifier modifier(engine.get());
1200 modifier.SetImplicitView(&view);
1201 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1202 // Sets lifecycle state to resumed.
1203 engine->Run();
1204
1205 // Ensure HWND(1) is in the set of visible windows before hiding it.
1206 engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1207 TRUE, NULL);
1208 engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1209 FALSE, NULL);
1210
1211 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1212 PumpMessage();
1213 }
1214
1215 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1217}
1218
1220 FlutterWindowsEngineBuilder builder{GetContext()};
1221 HWND outer = reinterpret_cast<HWND>(1);
1222 HWND inner = reinterpret_cast<HWND>(2);
1223
1224 auto engine = builder.Build();
1225 auto window_binding_handler =
1226 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1227 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1228 ON_CALL(view, GetWindowHandle).WillByDefault([=]() { return inner; });
1229
1230 EngineModifier modifier(engine.get());
1231 modifier.SetImplicitView(&view);
1232 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1233 // Sets lifecycle state to resumed.
1234 engine->Run();
1235
1236 // Show both top-level and Flutter window.
1237 engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1238 outer, WM_SHOWWINDOW, TRUE, NULL);
1239 view.OnWindowStateEvent(inner, WindowStateEvent::kShow);
1240 view.OnWindowStateEvent(inner, WindowStateEvent::kFocus);
1241
1242 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1243 PumpMessage();
1244 }
1245
1246 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1248
1249 // Hide Flutter window, but not top level window.
1250 view.OnWindowStateEvent(inner, WindowStateEvent::kHide);
1251
1252 while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1253 PumpMessage();
1254 }
1255
1256 // The top-level window is still visible, so we ought not enter hidden state.
1257 EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1259}
1260
1261TEST_F(FlutterWindowsEngineTest, EnableLifecycleState) {
1262 FlutterWindowsEngineBuilder builder{GetContext()};
1263 builder.SetDartEntrypoint("enableLifecycleTest");
1264 bool finished = false;
1265
1266 auto engine = builder.Build();
1267 auto window_binding_handler =
1268 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1269 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1270
1271 EngineModifier modifier(engine.get());
1272 modifier.SetImplicitView(&view);
1273 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1274 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1275 EXPECT_CALL(*handler, SetLifecycleState)
1276 .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1277 handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1278 });
1279 modifier.SetLifecycleManager(std::move(handler));
1280
1281 auto binary_messenger =
1282 std::make_unique<BinaryMessengerImpl>(engine->messenger());
1283 // Mark the test only as completed on receiving an inactive state message.
1284 binary_messenger->SetMessageHandler(
1285 "flutter/unittest", [&finished](const uint8_t* message,
1286 size_t message_size, BinaryReply reply) {
1287 std::string contents(message, message + message_size);
1288 EXPECT_NE(contents.find("AppLifecycleState.inactive"),
1289 std::string::npos);
1290 finished = true;
1291 char response[] = "";
1292 reply(reinterpret_cast<uint8_t*>(response), 0);
1293 });
1294
1295 engine->Run();
1296
1297 // Test that setting the state before enabling lifecycle does nothing.
1298 HWND hwnd = reinterpret_cast<HWND>(1);
1299 view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1300 view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1301 EXPECT_FALSE(finished);
1302
1303 // Test that we can set the state afterwards.
1304
1305 engine->lifecycle_manager()->BeginProcessingLifecycle();
1306 view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1307
1308 while (!finished) {
1309 engine->task_runner()->ProcessTasks();
1310 }
1311}
1312
1313TEST_F(FlutterWindowsEngineTest, LifecycleStateToFrom) {
1314 FlutterWindowsEngineBuilder builder{GetContext()};
1315 builder.SetDartEntrypoint("enableLifecycleToFrom");
1316 bool enabled_lifecycle = false;
1317 bool dart_responded = false;
1318
1319 auto engine = builder.Build();
1320 auto window_binding_handler =
1321 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1322 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1323
1324 EngineModifier modifier(engine.get());
1325 modifier.SetImplicitView(&view);
1326 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1327 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1328 EXPECT_CALL(*handler, SetLifecycleState)
1329 .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1330 handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1331 });
1332 handler->begin_processing_callback = [&]() { enabled_lifecycle = true; };
1333 modifier.SetLifecycleManager(std::move(handler));
1334
1335 auto binary_messenger =
1336 std::make_unique<BinaryMessengerImpl>(engine->messenger());
1337 binary_messenger->SetMessageHandler(
1338 "flutter/unittest",
1339 [&](const uint8_t* message, size_t message_size, BinaryReply reply) {
1340 std::string contents(message, message + message_size);
1341 EXPECT_NE(contents.find("AppLifecycleState."), std::string::npos);
1342 dart_responded = true;
1343 char response[] = "";
1344 reply(reinterpret_cast<uint8_t*>(response), 0);
1345 });
1346
1347 engine->Run();
1348
1349 while (!enabled_lifecycle) {
1350 engine->task_runner()->ProcessTasks();
1351 }
1352
1353 HWND hwnd = reinterpret_cast<HWND>(1);
1354 view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1355 view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1356
1357 while (!dart_responded) {
1358 engine->task_runner()->ProcessTasks();
1359 }
1360}
1361
1363 FlutterWindowsEngineBuilder builder{GetContext()};
1364 builder.SetDartEntrypoint("enableLifecycleToFrom");
1365
1366 auto 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 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1374
1375 bool lifecycle_began = false;
1376 auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1377 handler->begin_processing_callback = [&]() { lifecycle_began = true; };
1378 modifier.SetLifecycleManager(std::move(handler));
1379
1380 engine->Run();
1381
1382 while (!lifecycle_began) {
1383 engine->task_runner()->ProcessTasks();
1384 }
1385}
1386
1387TEST_F(FlutterWindowsEngineTest, ReceivePlatformViewMessage) {
1388 FlutterWindowsEngineBuilder builder{GetContext()};
1389 builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1390 auto engine = builder.Build();
1391
1392 EngineModifier modifier{engine.get()};
1393 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1394
1395 bool received_call = false;
1396
1397 auto manager = std::make_unique<MockPlatformViewManager>(engine.get());
1398 EXPECT_CALL(*manager, AddPlatformView)
1399 .WillOnce([&](PlatformViewId id, std::string_view type_name) {
1400 received_call = true;
1401 return true;
1402 });
1403 modifier.SetPlatformViewPlugin(std::move(manager));
1404
1405 engine->Run();
1406
1407 while (!received_call) {
1408 engine->task_runner()->ProcessTasks();
1409 }
1410}
1411
1412TEST_F(FlutterWindowsEngineTest, AddViewFailureDoesNotHang) {
1413 FlutterWindowsEngineBuilder builder{GetContext()};
1414 auto engine = builder.Build();
1415
1416 EngineModifier modifier{engine.get()};
1417
1418 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1419 modifier.embedder_api().AddView = MOCK_ENGINE_PROC(
1420 AddView,
1422 const FlutterAddViewInfo* info) { return kInternalInconsistency; });
1423
1424 ASSERT_TRUE(engine->Run());
1425
1426 // Create the first view. This is the implicit view and isn't added to the
1427 // engine.
1428 auto implicit_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1429
1430 std::unique_ptr<FlutterWindowsView> implicit_view =
1431 engine->CreateView(std::move(implicit_window));
1432
1433 EXPECT_TRUE(implicit_view);
1434
1435 // Create a second view. The embedder attempts to add it to the engine.
1436 auto second_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1437
1438 EXPECT_DEBUG_DEATH(engine->CreateView(std::move(second_window)),
1439 "FlutterEngineAddView returned an unexpected result");
1440}
1441
1442TEST_F(FlutterWindowsEngineTest, RemoveViewFailureDoesNotHang) {
1443 FlutterWindowsEngineBuilder builder{GetContext()};
1444 builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1445 auto engine = builder.Build();
1446
1447 EngineModifier modifier{engine.get()};
1448
1449 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1450 modifier.embedder_api().RemoveView = MOCK_ENGINE_PROC(
1451 RemoveView,
1453 const FlutterRemoveViewInfo* info) { return kInternalInconsistency; });
1454
1455 ASSERT_TRUE(engine->Run());
1456 EXPECT_DEBUG_DEATH(engine->RemoveView(123),
1457 "FlutterEngineRemoveView returned an unexpected result");
1458}
1459
1461 auto& context = GetContext();
1462 WindowsConfigBuilder builder{context};
1463 builder.SetDartEntrypoint("mergedUIThread");
1464 builder.SetUIThreadPolicy(FlutterDesktopUIThreadPolicy::RunOnPlatformThread);
1465
1466 std::optional<std::thread::id> ui_thread_id;
1467
1468 auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1469 ui_thread_id = std::this_thread::get_id();
1470 });
1471 context.AddNativeFunction("Signal", native_entry);
1472
1473 EnginePtr engine{builder.RunHeadless()};
1474 while (!ui_thread_id) {
1475 PumpMessage();
1476 }
1477 ASSERT_EQ(*ui_thread_id, std::this_thread::get_id());
1478}
1479
1480TEST_F(FlutterWindowsEngineTest, OnViewFocusChangeRequest) {
1481 FlutterWindowsEngineBuilder builder{GetContext()};
1482 std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
1483 auto window_binding_handler =
1484 std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1485 MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1486
1487 EngineModifier modifier(engine.get());
1488 modifier.SetImplicitView(&view);
1489
1491 request.view_id = kImplicitViewId;
1492
1493 EXPECT_CALL(view, Focus()).WillOnce(Return(true));
1494 modifier.OnViewFocusChangeRequest(&request);
1495}
1496
1497TEST_F(FlutterWindowsEngineTest, UpdateSemanticsMultiView) {
1498 auto& context = GetContext();
1499 WindowsConfigBuilder builder{context};
1500 builder.SetDartEntrypoint("sendSemanticsTreeInfo");
1501
1502 // Setup: a signal for when we have send out all of our semantics updates
1503 bool done = false;
1504 auto native_entry =
1505 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
1506 context.AddNativeFunction("Signal", native_entry);
1507
1508 // Setup: Create the engine and two views + enable semantics
1509 EnginePtr engine{builder.RunHeadless()};
1510 ASSERT_NE(engine, nullptr);
1511
1512 auto window_binding_handler1 =
1513 std::make_unique<NiceMock<MockWindowBindingHandler>>();
1514 auto window_binding_handler2 =
1515 std::make_unique<NiceMock<MockWindowBindingHandler>>();
1516
1517 // The following mocks are required by
1518 // FlutterWindowsView::CreateWindowMetricsEvent so that we create a valid
1519 // view.
1520 EXPECT_CALL(*window_binding_handler1, GetPhysicalWindowBounds)
1521 .WillRepeatedly(testing::Return(PhysicalWindowBounds{100, 100}));
1522 EXPECT_CALL(*window_binding_handler1, GetDpiScale)
1523 .WillRepeatedly(testing::Return(96.0));
1524 EXPECT_CALL(*window_binding_handler2, GetPhysicalWindowBounds)
1525 .WillRepeatedly(testing::Return(PhysicalWindowBounds{200, 200}));
1526 EXPECT_CALL(*window_binding_handler2, GetDpiScale)
1527 .WillRepeatedly(testing::Return(96.0));
1528
1529 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
1530 EngineModifier modifier{windows_engine};
1531 modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1532
1533 // We want to avoid adding an implicit view as the first view
1534 modifier.SetNextViewId(kImplicitViewId + 1);
1535
1536 auto view1 = windows_engine->CreateView(std::move(window_binding_handler1));
1537 auto view2 = windows_engine->CreateView(std::move(window_binding_handler2));
1538
1539 // Act: UpdateSemanticsEnabled will trigger the semantics updates
1540 // to get sent.
1541 windows_engine->UpdateSemanticsEnabled(true);
1542
1543 while (!done) {
1544 windows_engine->task_runner()->ProcessTasks();
1545 }
1546
1547 auto accessibility_bridge1 = view1->accessibility_bridge().lock();
1548 auto accessibility_bridge2 = view2->accessibility_bridge().lock();
1549
1550 // Expect: that the semantics trees are updated with their
1551 // respective nodes.
1552 while (
1553 !accessibility_bridge1->GetPlatformNodeFromTree(view1->view_id() + 1)) {
1554 windows_engine->task_runner()->ProcessTasks();
1555 }
1556
1557 while (
1558 !accessibility_bridge2->GetPlatformNodeFromTree(view2->view_id() + 1)) {
1559 windows_engine->task_runner()->ProcessTasks();
1560 }
1561
1562 // Rely on timeout mechanism in CI.
1563 auto tree1 = accessibility_bridge1->GetTree();
1564 auto tree2 = accessibility_bridge2->GetTree();
1565 EXPECT_NE(tree1->GetFromId(view1->view_id() + 1), nullptr);
1566 EXPECT_NE(tree2->GetFromId(view2->view_id() + 1), nullptr);
1567}
1568
1569} // namespace testing
1570} // 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
#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:2318
@ kFlutterEngineDisplaysUpdateTypeStartup
Definition embedder.h:2324
@ kBackground
Suitable for threads that shouldn't disrupt high priority work.
Definition embedder.h:371
@ kDisplay
Suitable for threads which generate data for the display.
Definition embedder.h:375
@ kNormal
Default priority level.
Definition embedder.h:373
@ kRaster
Suitable for thread which raster data.
Definition embedder.h:377
@ kFlutterSemanticsActionDismiss
A request that the node should be dismissed.
Definition embedder.h:160
#define FLUTTER_ENGINE_VERSION
Definition embedder.h:70
GLFWwindow * window
Definition main.cc:60
FlutterEngine engine
Definition main.cc:84
FlView * view
return TRUE
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
const gchar * channel
const gchar FlBinaryMessengerMessageHandler handler
G_BEGIN_DECLS GBytes * message
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:3746
FlutterEnginePlatformMessageReleaseResponseHandleFnPtr PlatformMessageReleaseResponseHandle
Definition embedder.h:3722
FlutterEngineUpdateLocalesFnPtr UpdateLocales
Definition embedder.h:3739
FlutterEngineRunsAOTCompiledDartCodeFnPtr RunsAOTCompiledDartCode
Definition embedder.h:3740
FlutterEngineSetNextFrameCallbackFnPtr SetNextFrameCallback
Definition embedder.h:3747
FlutterEngineShutdownFnPtr Shutdown
Definition embedder.h:3711
FlutterEngineOnVsyncFnPtr OnVsync
Definition embedder.h:3731
FlutterEngineGetCurrentTimeFnPtr GetCurrentTime
Definition embedder.h:3737
FlutterEngineNotifyDisplayUpdateFnPtr NotifyDisplayUpdate
Definition embedder.h:3745
FlutterEngineSendPlatformMessageFnPtr SendPlatformMessage
Definition embedder.h:3718
FlutterEnginePlatformMessageCreateResponseHandleFnPtr PlatformMessageCreateResponseHandle
Definition embedder.h:3720
FlutterEngineRunFnPtr Run
Definition embedder.h:3710
FlutterEngineUpdateAccessibilityFeaturesFnPtr UpdateAccessibilityFeatures
Definition embedder.h:3729
FlutterEnginePostRenderThreadTaskFnPtr PostRenderThreadTask
Definition embedder.h:3736
FlutterEngineSendSemanticsActionFnPtr SendSemanticsAction
Definition embedder.h:3751
FlutterRendererType type
Definition embedder.h:1031
FlutterViewId view_id
The identifier of the view that received the focus event.
Definition embedder.h:1227
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