Flutter Windows Embedder
flutter_windows_engine_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <thread>
7 
8 #include "flutter/fml/logging.h"
9 #include "flutter/fml/macros.h"
10 #include "flutter/shell/platform/embedder/embedder.h"
11 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
14 #include "flutter/shell/platform/windows/testing/egl/mock_manager.h"
15 #include "flutter/shell/platform/windows/testing/engine_modifier.h"
16 #include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
17 #include "flutter/shell/platform/windows/testing/mock_platform_view_manager.h"
18 #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
19 #include "flutter/shell/platform/windows/testing/mock_windows_proc_table.h"
20 #include "flutter/shell/platform/windows/testing/test_keyboard.h"
21 #include "flutter/shell/platform/windows/testing/windows_test.h"
22 #include "flutter/shell/platform/windows/testing/windows_test_config_builder.h"
23 #include "flutter/third_party/accessibility/ax/platform/ax_platform_node_win.h"
24 #include "fml/synchronization/waitable_event.h"
25 #include "gmock/gmock.h"
26 #include "gtest/gtest.h"
27 
28 // winbase.h defines GetCurrentTime as a macro.
29 #undef GetCurrentTime
30 
31 namespace {
32 // Process the next win32 message if there is one. This can be used to
33 // pump the Windows platform thread task runner.
34 void PumpMessage() {
35  ::MSG msg;
36  if (::GetMessage(&msg, nullptr, 0, 0)) {
37  ::TranslateMessage(&msg);
38  ::DispatchMessage(&msg);
39  }
40 }
41 } // namespace
42 
43 namespace flutter {
44 namespace testing {
45 
46 using ::testing::_;
47 using ::testing::DoAll;
48 using ::testing::NiceMock;
49 using ::testing::Return;
50 using ::testing::SetArgPointee;
51 
52 class FlutterWindowsEngineTest : public WindowsTest {};
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 
67 TEST_F(FlutterWindowsEngineTest, TaskRunnerDelayedTask) {
68  bool finished = false;
69  auto runner = std::make_unique<TaskRunner>(
70  [] {
71  return static_cast<uint64_t>(
72  fml::TimePoint::Now().ToEpochDelta().ToNanoseconds());
73  },
74  [&](const FlutterTask*) { finished = true; });
75  runner->PostFlutterTask(
76  FlutterTask{},
77  static_cast<uint64_t>((fml::TimePoint::Now().ToEpochDelta() +
78  fml::TimeDelta::FromMilliseconds(50))
79  .ToNanoseconds()));
80  auto start = fml::TimePoint::Now();
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)
89 TEST_F(FlutterWindowsEngineTest, TaskRunnerDoesNotDeadlock) {
90  auto runner = std::make_unique<TaskRunner>(
91  [] {
92  return static_cast<uint64_t>(
93  fml::TimePoint::Now().ToEpochDelta().ToNanoseconds());
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 
137 TEST_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(
229  FlutterThreadPriority::kRaster);
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
235  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
236  UpdateAccessibilityFeatures,
237  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
238  FlutterAccessibilityFeature flags) { return kSuccess; });
239 
240  // It should send locale info.
241  bool update_locales_called = false;
242  modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
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;
256  modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
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 
269  modifier.embedder_api().NotifyDisplayUpdate = MOCK_ENGINE_PROC(
270  NotifyDisplayUpdate,
271  ([&notify_display_update_called](
272  FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
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 
297 TEST_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; }));
305  modifier.embedder_api().OnVsync = MOCK_ENGINE_PROC(
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 
324 TEST_F(FlutterWindowsEngineTest, RunSkiaWithoutANGLEUsesSoftware) {
325  FlutterWindowsEngineBuilder builder{GetContext()};
326  builder.SetImpellerSwitch(DisabledImpeller);
327  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
328  EngineModifier modifier(engine.get());
329 
330  modifier.embedder_api().NotifyDisplayUpdate =
331  MOCK_ENGINE_PROC(NotifyDisplayUpdate,
332  ([engine_instance = engine.get()](
333  FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
334  const FlutterEngineDisplaysUpdateType update_type,
335  const FlutterEngineDisplay* embedder_displays,
336  size_t display_count) { return kSuccess; }));
337 
338  // The engine should be run with expected configuration values.
339  bool run_called = false;
340  modifier.embedder_api().Run = MOCK_ENGINE_PROC(
341  Run, ([&run_called, engine_instance = engine.get()](
342  size_t version, const FlutterRendererConfig* config,
343  const FlutterProjectArgs* args, void* user_data,
344  FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
345  run_called = true;
346  *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
347  // We don't have an EGL Manager, so we should be using software.
348  EXPECT_EQ(config->type, kSoftware);
349  return kSuccess;
350  }));
351  // Accessibility updates must do nothing when the embedder engine is mocked
352  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
353  UpdateAccessibilityFeatures,
354  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
355  FlutterAccessibilityFeature flags) { return kSuccess; });
356 
357  // Stub out UpdateLocales and SendPlatformMessage as we don't have a fully
358  // initialized engine instance.
359  modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
360  UpdateLocales, ([](auto engine, const FlutterLocale** locales,
361  size_t locales_count) { return kSuccess; }));
362  modifier.embedder_api().SendPlatformMessage =
363  MOCK_ENGINE_PROC(SendPlatformMessage,
364  ([](auto engine, auto message) { return kSuccess; }));
365 
366  // Set the EGL manager to nullptr to test software fallback path.
367  modifier.SetEGLManager(nullptr);
368 
369  engine->Run();
370 
371  EXPECT_TRUE(run_called);
372 
373  // Ensure that deallocation doesn't call the actual Shutdown with the bogus
374  // engine pointer that the overridden Run returned.
375  modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
376 }
377 
378 TEST_F(FlutterWindowsEngineTest, RunWithoutANGLEOnImpellerFailsToStart) {
379  FlutterWindowsEngineBuilder builder{GetContext()};
380  builder.SetSwitches({"--enable-impeller=true"});
381  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
382  EngineModifier modifier(engine.get());
383 
384  modifier.embedder_api().NotifyDisplayUpdate =
385  MOCK_ENGINE_PROC(NotifyDisplayUpdate,
386  ([engine_instance = engine.get()](
387  FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
388  const FlutterEngineDisplaysUpdateType update_type,
389  const FlutterEngineDisplay* embedder_displays,
390  size_t display_count) { return kSuccess; }));
391 
392  // Accessibility updates must do nothing when the embedder engine is mocked
393  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
394  UpdateAccessibilityFeatures,
395  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
396  FlutterAccessibilityFeature flags) { return kSuccess; });
397 
398  // Stub out UpdateLocales and SendPlatformMessage as we don't have a fully
399  // initialized engine instance.
400  modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
401  UpdateLocales, ([](auto engine, const FlutterLocale** locales,
402  size_t locales_count) { return kSuccess; }));
403  modifier.embedder_api().SendPlatformMessage =
404  MOCK_ENGINE_PROC(SendPlatformMessage,
405  ([](auto engine, auto message) { return kSuccess; }));
406 
407  // Set the EGL manager to nullptr to test software fallback path.
408  modifier.SetEGLManager(nullptr);
409 
410  EXPECT_FALSE(engine->Run());
411 }
412 
413 TEST_F(FlutterWindowsEngineTest, RunWithDefaultEnablesImpeller) {
414  FlutterWindowsEngineBuilder builder{GetContext()};
415  builder.SetImpellerSwitch(DefaultImpeller);
416  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
417  EngineModifier modifier(engine.get());
418 
419  modifier.embedder_api().NotifyDisplayUpdate =
420  MOCK_ENGINE_PROC(NotifyDisplayUpdate,
421  ([engine_instance = engine.get()](
422  FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
423  const FlutterEngineDisplaysUpdateType update_type,
424  const FlutterEngineDisplay* embedder_displays,
425  size_t display_count) { return kSuccess; }));
426 
427  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
428  UpdateAccessibilityFeatures,
429  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
430  FlutterAccessibilityFeature flags) { return kSuccess; });
431 
432  modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
433  UpdateLocales, ([](auto engine, const FlutterLocale** locales,
434  size_t locales_count) { return kSuccess; }));
435 
436  modifier.embedder_api().SendPlatformMessage =
437  MOCK_ENGINE_PROC(SendPlatformMessage,
438  ([](auto engine, auto message) { return kSuccess; }));
439 
440  bool run_called = false;
441  modifier.embedder_api().Run = MOCK_ENGINE_PROC(
442  Run, ([&run_called, engine_instance = engine.get()](
443  size_t version, const FlutterRendererConfig* config,
444  const FlutterProjectArgs* args, void* user_data,
445  FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
446  run_called = true;
447  *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
448 
449  bool has_impeller_switch = false;
450  bool has_sdf_switch = false;
451  for (int i = 0; i < args->command_line_argc; ++i) {
452  if (strcmp(args->command_line_argv[i], "--enable-impeller") == 0) {
453  has_impeller_switch = true;
454  }
455  if (strcmp(args->command_line_argv[i], "--impeller-use-sdfs=true") ==
456  0) {
457  has_sdf_switch = true;
458  }
459  }
460  EXPECT_TRUE(has_impeller_switch);
461  EXPECT_TRUE(has_sdf_switch);
462  return kSuccess;
463  }));
464 
465  // Set the EGL manager to !nullptr to test ANGLE rendering.
466  modifier.SetEGLManager(std::make_unique<egl::MockManager>());
467 
468  engine->Run();
469 
470  EXPECT_TRUE(run_called);
471 
472  modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
473  modifier.ReleaseEGLManager();
474 }
475 
476 TEST_F(FlutterWindowsEngineTest, RunWithProjectFlagEnableImpeller) {
477  FlutterWindowsEngineBuilder builder{GetContext()};
478  builder.SetImpellerSwitch(EnabledImpeller);
479  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
480  EngineModifier modifier(engine.get());
481 
482  modifier.embedder_api().NotifyDisplayUpdate =
483  MOCK_ENGINE_PROC(NotifyDisplayUpdate,
484  ([](FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
485  const FlutterEngineDisplaysUpdateType update_type,
486  const FlutterEngineDisplay* embedder_displays,
487  size_t display_count) { return kSuccess; }));
488 
489  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
490  UpdateAccessibilityFeatures,
491  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
492  FlutterAccessibilityFeature flags) { return kSuccess; });
493 
494  modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
495  UpdateLocales, ([](auto engine, const FlutterLocale** locales,
496  size_t locales_count) { return kSuccess; }));
497 
498  modifier.embedder_api().SendPlatformMessage =
499  MOCK_ENGINE_PROC(SendPlatformMessage,
500  ([](auto engine, auto message) { return kSuccess; }));
501 
502  bool run_called = false;
503  modifier.embedder_api().Run = MOCK_ENGINE_PROC(
504  Run, ([&run_called](size_t version, const FlutterRendererConfig* config,
505  const FlutterProjectArgs* args, void* user_data,
506  FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
507  run_called = true;
508  *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
509 
510  bool has_impeller_switch = false;
511  bool has_sdf_switch = false;
512  for (int i = 0; i < args->command_line_argc; ++i) {
513  if (strcmp(args->command_line_argv[i], "--enable-impeller") == 0) {
514  has_impeller_switch = true;
515  }
516  if (strcmp(args->command_line_argv[i], "--impeller-use-sdfs=true") ==
517  0) {
518  has_sdf_switch = true;
519  }
520  }
521  EXPECT_TRUE(has_impeller_switch);
522  EXPECT_TRUE(has_sdf_switch);
523  return kSuccess;
524  }));
525 
526  // Set the EGL manager to !nullptr to test ANGLE rendering.
527  modifier.SetEGLManager(std::make_unique<egl::MockManager>());
528 
529  engine->Run();
530 
531  EXPECT_TRUE(run_called);
532 
533  modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
534  modifier.ReleaseEGLManager();
535 }
536 
537 TEST_F(FlutterWindowsEngineTest, RunWithProjectFlagEnableFlutterGpu) {
538  FlutterWindowsEngineBuilder builder{GetContext()};
539  builder.SetEnableFlutterGpu(true);
540  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
541  EngineModifier modifier(engine.get());
542 
543  modifier.embedder_api().NotifyDisplayUpdate =
544  MOCK_ENGINE_PROC(NotifyDisplayUpdate,
545  ([](FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
546  const FlutterEngineDisplaysUpdateType update_type,
547  const FlutterEngineDisplay* embedder_displays,
548  size_t display_count) { return kSuccess; }));
549 
550  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
551  UpdateAccessibilityFeatures,
552  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
553  FlutterAccessibilityFeature flags) { return kSuccess; });
554 
555  modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
556  UpdateLocales, ([](auto engine, const FlutterLocale** locales,
557  size_t locales_count) { return kSuccess; }));
558 
559  modifier.embedder_api().SendPlatformMessage =
560  MOCK_ENGINE_PROC(SendPlatformMessage,
561  ([](auto engine, auto message) { return kSuccess; }));
562 
563  bool run_called = false;
564  modifier.embedder_api().Run = MOCK_ENGINE_PROC(
565  Run, ([&run_called](size_t version, const FlutterRendererConfig* config,
566  const FlutterProjectArgs* args, void* user_data,
567  FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
568  run_called = true;
569  *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
570 
571  bool has_flutter_gpu_switch = false;
572  for (int i = 0; i < args->command_line_argc; ++i) {
573  if (strcmp(args->command_line_argv[i], "--enable-flutter-gpu") == 0) {
574  has_flutter_gpu_switch = true;
575  }
576  }
577  EXPECT_TRUE(has_flutter_gpu_switch);
578  return kSuccess;
579  }));
580 
581  // Set the EGL manager to !nullptr to test ANGLE rendering.
582  modifier.SetEGLManager(std::make_unique<egl::MockManager>());
583 
584  engine->Run();
585 
586  EXPECT_TRUE(run_called);
587 
588  modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
589  modifier.ReleaseEGLManager();
590 }
591 
592 TEST_F(FlutterWindowsEngineTest, RunWithoutProjectFlagEnableFlutterGpu) {
593  FlutterWindowsEngineBuilder builder{GetContext()};
594  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
595  EngineModifier modifier(engine.get());
596 
597  modifier.embedder_api().NotifyDisplayUpdate =
598  MOCK_ENGINE_PROC(NotifyDisplayUpdate,
599  ([](FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
600  const FlutterEngineDisplaysUpdateType update_type,
601  const FlutterEngineDisplay* embedder_displays,
602  size_t display_count) { return kSuccess; }));
603 
604  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
605  UpdateAccessibilityFeatures,
606  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
607  FlutterAccessibilityFeature flags) { return kSuccess; });
608 
609  modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
610  UpdateLocales, ([](auto engine, const FlutterLocale** locales,
611  size_t locales_count) { return kSuccess; }));
612 
613  modifier.embedder_api().SendPlatformMessage =
614  MOCK_ENGINE_PROC(SendPlatformMessage,
615  ([](auto engine, auto message) { return kSuccess; }));
616 
617  bool run_called = false;
618  modifier.embedder_api().Run = MOCK_ENGINE_PROC(
619  Run, ([&run_called](size_t version, const FlutterRendererConfig* config,
620  const FlutterProjectArgs* args, void* user_data,
621  FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
622  run_called = true;
623  *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
624 
625  bool has_flutter_gpu_switch = false;
626  for (int i = 0; i < args->command_line_argc; ++i) {
627  if (strcmp(args->command_line_argv[i], "--enable-flutter-gpu") == 0) {
628  has_flutter_gpu_switch = true;
629  }
630  }
631  EXPECT_FALSE(has_flutter_gpu_switch);
632  return kSuccess;
633  }));
634 
635  // Set the EGL manager to !nullptr to test ANGLE rendering.
636  modifier.SetEGLManager(std::make_unique<egl::MockManager>());
637 
638  engine->Run();
639 
640  EXPECT_TRUE(run_called);
641 
642  modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
643  modifier.ReleaseEGLManager();
644 }
645 
646 TEST_F(FlutterWindowsEngineTest, RunWithProjectFlagDisableImpeller) {
647  FlutterWindowsEngineBuilder builder{GetContext()};
648  builder.SetImpellerSwitch(DisabledImpeller);
649  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
650  EngineModifier modifier(engine.get());
651 
652  modifier.embedder_api().NotifyDisplayUpdate =
653  MOCK_ENGINE_PROC(NotifyDisplayUpdate,
654  ([](FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
655  const FlutterEngineDisplaysUpdateType update_type,
656  const FlutterEngineDisplay* embedder_displays,
657  size_t display_count) { return kSuccess; }));
658 
659  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
660  UpdateAccessibilityFeatures,
661  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
662  FlutterAccessibilityFeature flags) { return kSuccess; });
663 
664  modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
665  UpdateLocales, ([](auto engine, const FlutterLocale** locales,
666  size_t locales_count) { return kSuccess; }));
667 
668  modifier.embedder_api().SendPlatformMessage =
669  MOCK_ENGINE_PROC(SendPlatformMessage,
670  ([](auto engine, auto message) { return kSuccess; }));
671 
672  bool run_called = false;
673  modifier.embedder_api().Run = MOCK_ENGINE_PROC(
674  Run, ([&run_called](size_t version, const FlutterRendererConfig* config,
675  const FlutterProjectArgs* args, void* user_data,
676  FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
677  run_called = true;
678  *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
679 
680  bool has_disable_impeller_switch = false;
681  for (int i = 0; i < args->command_line_argc; ++i) {
682  if (strcmp(args->command_line_argv[i], "--enable-impeller=false") ==
683  0) {
684  has_disable_impeller_switch = true;
685  }
686  }
687  EXPECT_TRUE(has_disable_impeller_switch);
688  return kSuccess;
689  }));
690 
691  engine->Run();
692 
693  EXPECT_TRUE(run_called);
694 
695  modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
696 }
697 
698 TEST_F(FlutterWindowsEngineTest, RunWithCommandLineDisableImpeller) {
699  FlutterWindowsEngineBuilder builder{GetContext()};
700  builder.SetSwitches({"--enable-impeller=false"});
701  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
702  EngineModifier modifier(engine.get());
703 
704  modifier.embedder_api().NotifyDisplayUpdate =
705  MOCK_ENGINE_PROC(NotifyDisplayUpdate,
706  ([](FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
707  const FlutterEngineDisplaysUpdateType update_type,
708  const FlutterEngineDisplay* embedder_displays,
709  size_t display_count) { return kSuccess; }));
710 
711  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
712  UpdateAccessibilityFeatures,
713  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
714  FlutterAccessibilityFeature flags) { return kSuccess; });
715 
716  modifier.embedder_api().UpdateLocales = MOCK_ENGINE_PROC(
717  UpdateLocales, ([](auto engine, const FlutterLocale** locales,
718  size_t locales_count) { return kSuccess; }));
719 
720  modifier.embedder_api().SendPlatformMessage =
721  MOCK_ENGINE_PROC(SendPlatformMessage,
722  ([](auto engine, auto message) { return kSuccess; }));
723 
724  bool run_called = false;
725  modifier.embedder_api().Run = MOCK_ENGINE_PROC(
726  Run, ([&run_called](size_t version, const FlutterRendererConfig* config,
727  const FlutterProjectArgs* args, void* user_data,
728  FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
729  run_called = true;
730  *engine_out = reinterpret_cast<FLUTTER_API_SYMBOL(FlutterEngine)>(1);
731 
732  bool has_disable_impeller_switch = false;
733  for (int i = 0; i < args->command_line_argc; ++i) {
734  if (strcmp(args->command_line_argv[i], "--enable-impeller=false") ==
735  0) {
736  has_disable_impeller_switch = true;
737  }
738  }
739  EXPECT_TRUE(has_disable_impeller_switch);
740  return kSuccess;
741  }));
742 
743  engine->Run();
744 
745  EXPECT_TRUE(run_called);
746 
747  modifier.embedder_api().Shutdown = [](auto engine) { return kSuccess; };
748 }
749 
750 TEST_F(FlutterWindowsEngineTest, SendPlatformMessageWithoutResponse) {
751  FlutterWindowsEngineBuilder builder{GetContext()};
752  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
753  EngineModifier modifier(engine.get());
754 
755  const char* channel = "test";
756  const std::vector<uint8_t> test_message = {1, 2, 3, 4};
757 
758  // Without a response, SendPlatformMessage should be a simple pass-through.
759  bool called = false;
760  modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
761  SendPlatformMessage, ([&called, test_message](auto engine, auto message) {
762  called = true;
763  EXPECT_STREQ(message->channel, "test");
764  EXPECT_EQ(message->message_size, test_message.size());
765  EXPECT_EQ(memcmp(message->message, test_message.data(),
766  message->message_size),
767  0);
768  EXPECT_EQ(message->response_handle, nullptr);
769  return kSuccess;
770  }));
771 
772  engine->SendPlatformMessage(channel, test_message.data(), test_message.size(),
773  nullptr, nullptr);
774  EXPECT_TRUE(called);
775 }
776 
777 TEST_F(FlutterWindowsEngineTest, PlatformMessageRoundTrip) {
778  FlutterWindowsEngineBuilder builder{GetContext()};
779  builder.SetDartEntrypoint("hiPlatformChannels");
780 
781  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
782  EngineModifier modifier(engine.get());
783  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
784 
785  auto binary_messenger =
786  std::make_unique<BinaryMessengerImpl>(engine->messenger());
787 
788  engine->Run();
789  bool did_call_callback = false;
790  bool did_call_reply = false;
791  bool did_call_dart_reply = false;
792  std::string channel = "hi";
793  binary_messenger->SetMessageHandler(
794  channel,
795  [&did_call_callback, &did_call_dart_reply](
796  const uint8_t* message, size_t message_size, BinaryReply reply) {
797  if (message_size == 5) {
798  EXPECT_EQ(message[0], static_cast<uint8_t>('h'));
799  char response[] = {'b', 'y', 'e'};
800  reply(reinterpret_cast<uint8_t*>(response), 3);
801  did_call_callback = true;
802  } else {
803  EXPECT_EQ(message_size, 3);
804  EXPECT_EQ(message[0], static_cast<uint8_t>('b'));
805  did_call_dart_reply = true;
806  }
807  });
808  char payload[] = {'h', 'e', 'l', 'l', 'o'};
809  binary_messenger->Send(
810  channel, reinterpret_cast<uint8_t*>(payload), 5,
811  [&did_call_reply](const uint8_t* reply, size_t reply_size) {
812  EXPECT_EQ(reply_size, 5);
813  EXPECT_EQ(reply[0], static_cast<uint8_t>('h'));
814  did_call_reply = true;
815  });
816  // Rely on timeout mechanism in CI.
817  while (!did_call_callback || !did_call_reply || !did_call_dart_reply) {
818  engine->task_runner()->ProcessTasks();
819  }
820 }
821 
822 TEST_F(FlutterWindowsEngineTest, PlatformMessageRespondOnDifferentThread) {
823  FlutterWindowsEngineBuilder builder{GetContext()};
824  builder.SetDartEntrypoint("hiPlatformChannels");
825 
826  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
827 
828  EngineModifier modifier(engine.get());
829  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
830 
831  auto binary_messenger =
832  std::make_unique<BinaryMessengerImpl>(engine->messenger());
833 
834  engine->Run();
835  bool did_call_callback = false;
836  bool did_call_reply = false;
837  bool did_call_dart_reply = false;
838  std::string channel = "hi";
839  std::unique_ptr<std::thread> reply_thread;
840  binary_messenger->SetMessageHandler(
841  channel,
842  [&did_call_callback, &did_call_dart_reply, &reply_thread](
843  const uint8_t* message, size_t message_size, BinaryReply reply) {
844  if (message_size == 5) {
845  EXPECT_EQ(message[0], static_cast<uint8_t>('h'));
846  reply_thread.reset(new std::thread([reply = std::move(reply)]() {
847  char response[] = {'b', 'y', 'e'};
848  reply(reinterpret_cast<uint8_t*>(response), 3);
849  }));
850  did_call_callback = true;
851  } else {
852  EXPECT_EQ(message_size, 3);
853  EXPECT_EQ(message[0], static_cast<uint8_t>('b'));
854  did_call_dart_reply = true;
855  }
856  });
857  char payload[] = {'h', 'e', 'l', 'l', 'o'};
858  binary_messenger->Send(
859  channel, reinterpret_cast<uint8_t*>(payload), 5,
860  [&did_call_reply](const uint8_t* reply, size_t reply_size) {
861  EXPECT_EQ(reply_size, 5);
862  EXPECT_EQ(reply[0], static_cast<uint8_t>('h'));
863  did_call_reply = true;
864  });
865  // Rely on timeout mechanism in CI.
866  while (!did_call_callback || !did_call_reply || !did_call_dart_reply) {
867  engine->task_runner()->ProcessTasks();
868  }
869  ASSERT_TRUE(reply_thread);
870  reply_thread->join();
871 }
872 
873 TEST_F(FlutterWindowsEngineTest, SendPlatformMessageWithResponse) {
874  FlutterWindowsEngineBuilder builder{GetContext()};
875  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
876  EngineModifier modifier(engine.get());
877 
878  const char* channel = "test";
879  const std::vector<uint8_t> test_message = {1, 2, 3, 4};
880  auto* dummy_response_handle =
881  reinterpret_cast<FlutterPlatformMessageResponseHandle*>(5);
882  const FlutterDesktopBinaryReply reply_handler = [](auto... args) {};
883  void* reply_user_data = reinterpret_cast<void*>(6);
884 
885  // When a response is requested, a handle should be created, passed as part
886  // of the message, and then released.
887  bool create_response_handle_called = false;
888  modifier.embedder_api().PlatformMessageCreateResponseHandle =
889  MOCK_ENGINE_PROC(
890  PlatformMessageCreateResponseHandle,
891  ([&create_response_handle_called, &reply_handler, reply_user_data,
892  dummy_response_handle](auto engine, auto reply, auto user_data,
893  auto response_handle) {
894  create_response_handle_called = true;
895  EXPECT_EQ(reply, reply_handler);
896  EXPECT_EQ(user_data, reply_user_data);
897  EXPECT_NE(response_handle, nullptr);
898  *response_handle = dummy_response_handle;
899  return kSuccess;
900  }));
901  bool release_response_handle_called = false;
902  modifier.embedder_api().PlatformMessageReleaseResponseHandle =
903  MOCK_ENGINE_PROC(
904  PlatformMessageReleaseResponseHandle,
905  ([&release_response_handle_called, dummy_response_handle](
906  auto engine, auto response_handle) {
907  release_response_handle_called = true;
908  EXPECT_EQ(response_handle, dummy_response_handle);
909  return kSuccess;
910  }));
911  bool send_message_called = false;
912  modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
913  SendPlatformMessage, ([&send_message_called, test_message,
914  dummy_response_handle](auto engine, auto message) {
915  send_message_called = true;
916  EXPECT_STREQ(message->channel, "test");
917  EXPECT_EQ(message->message_size, test_message.size());
918  EXPECT_EQ(memcmp(message->message, test_message.data(),
919  message->message_size),
920  0);
921  EXPECT_EQ(message->response_handle, dummy_response_handle);
922  return kSuccess;
923  }));
924 
925  engine->SendPlatformMessage(channel, test_message.data(), test_message.size(),
926  reply_handler, reply_user_data);
927  EXPECT_TRUE(create_response_handle_called);
928  EXPECT_TRUE(release_response_handle_called);
929  EXPECT_TRUE(send_message_called);
930 }
931 
932 TEST_F(FlutterWindowsEngineTest, DispatchSemanticsAction) {
933  FlutterWindowsEngineBuilder builder{GetContext()};
934  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
935  EngineModifier modifier(engine.get());
936 
937  bool called = false;
938  std::string message = "Hello";
939  modifier.embedder_api().SendSemanticsAction = MOCK_ENGINE_PROC(
940  SendSemanticsAction, ([&called, &message](auto engine, auto info) {
941  called = true;
942  EXPECT_EQ(info->view_id, 456);
943  EXPECT_EQ(info->node_id, 42);
944  EXPECT_EQ(info->action, kFlutterSemanticsActionDismiss);
945  EXPECT_EQ(memcmp(info->data, message.c_str(), message.size()), 0);
946  EXPECT_EQ(info->data_length, message.size());
947  return kSuccess;
948  }));
949 
950  auto data = fml::MallocMapping::Copy(message.c_str(), message.size());
951  engine->DispatchSemanticsAction(456, 42, kFlutterSemanticsActionDismiss,
952  std::move(data));
953  EXPECT_TRUE(called);
954 }
955 
956 TEST_F(FlutterWindowsEngineTest, SetsThreadPriority) {
957  WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kBackground);
958  EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
959  THREAD_PRIORITY_BELOW_NORMAL);
960 
961  WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kDisplay);
962  EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
963  THREAD_PRIORITY_ABOVE_NORMAL);
964 
965  WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kRaster);
966  EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
967  THREAD_PRIORITY_ABOVE_NORMAL);
968 
969  // FlutterThreadPriority::kNormal does not change thread priority, reset to 0
970  // here.
971  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
972 
973  WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kNormal);
974  EXPECT_EQ(GetThreadPriority(GetCurrentThread()), THREAD_PRIORITY_NORMAL);
975 }
976 
977 TEST_F(FlutterWindowsEngineTest, AddPluginRegistrarDestructionCallback) {
978  FlutterWindowsEngineBuilder builder{GetContext()};
979  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
980  EngineModifier modifier(engine.get());
981 
982  MockEmbedderApiForKeyboard(modifier,
983  std::make_shared<MockKeyResponseController>());
984 
985  engine->Run();
986 
987  // Verify that destruction handlers don't overwrite each other.
988  int result1 = 0;
989  int result2 = 0;
990  engine->AddPluginRegistrarDestructionCallback(
992  auto result = reinterpret_cast<int*>(ref);
993  *result = 1;
994  },
995  reinterpret_cast<FlutterDesktopPluginRegistrarRef>(&result1));
996  engine->AddPluginRegistrarDestructionCallback(
998  auto result = reinterpret_cast<int*>(ref);
999  *result = 2;
1000  },
1001  reinterpret_cast<FlutterDesktopPluginRegistrarRef>(&result2));
1002 
1003  engine->Stop();
1004  EXPECT_EQ(result1, 1);
1005  EXPECT_EQ(result2, 2);
1006 }
1007 
1009  FlutterWindowsEngineBuilder builder{GetContext()};
1010  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
1011  EngineModifier modifier(engine.get());
1012 
1013  bool called = false;
1014  modifier.embedder_api().ScheduleFrame =
1015  MOCK_ENGINE_PROC(ScheduleFrame, ([&called](auto engine) {
1016  called = true;
1017  return kSuccess;
1018  }));
1019 
1020  engine->ScheduleFrame();
1021  EXPECT_TRUE(called);
1022 }
1023 
1024 TEST_F(FlutterWindowsEngineTest, SetNextFrameCallback) {
1025  FlutterWindowsEngineBuilder builder{GetContext()};
1026  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
1027  EngineModifier modifier(engine.get());
1028 
1029  bool called = false;
1030  modifier.embedder_api().SetNextFrameCallback = MOCK_ENGINE_PROC(
1031  SetNextFrameCallback, ([&called](auto engine, auto callback, auto data) {
1032  called = true;
1033  return kSuccess;
1034  }));
1035 
1036  engine->SetNextFrameCallback([]() {});
1037  EXPECT_TRUE(called);
1038 }
1039 
1040 TEST_F(FlutterWindowsEngineTest, GetExecutableName) {
1041  FlutterWindowsEngineBuilder builder{GetContext()};
1042  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
1043  EXPECT_EQ(engine->GetExecutableName(), "flutter_windows_unittests.exe");
1044 }
1045 
1046 // Ensure that after setting or resetting the high contrast feature,
1047 // the corresponding status flag can be retrieved from the engine.
1048 TEST_F(FlutterWindowsEngineTest, UpdateHighContrastFeature) {
1049  auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
1050  EXPECT_CALL(*windows_proc_table, GetHighContrastEnabled)
1051  .WillOnce(Return(true))
1052  .WillOnce(Return(false));
1053 
1054  FlutterWindowsEngineBuilder builder{GetContext()};
1055  builder.SetWindowsProcTable(windows_proc_table);
1056  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
1057  EngineModifier modifier(engine.get());
1058 
1059  std::optional<FlutterAccessibilityFeature> engine_flags;
1060  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
1061  UpdateAccessibilityFeatures, ([&engine_flags](auto engine, auto flags) {
1062  engine_flags = flags;
1063  return kSuccess;
1064  }));
1065  modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
1066  SendPlatformMessage,
1067  [](auto engine, const auto message) { return kSuccess; });
1068 
1069  // 1: High contrast is enabled.
1070  engine->UpdateHighContrastMode();
1071 
1072  EXPECT_TRUE(engine->high_contrast_enabled());
1073  EXPECT_TRUE(engine_flags.has_value());
1074  EXPECT_TRUE(
1075  engine_flags.value() &
1076  FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast);
1077 
1078  // 2: High contrast is disabled.
1079  engine_flags.reset();
1080  engine->UpdateHighContrastMode();
1081 
1082  EXPECT_FALSE(engine->high_contrast_enabled());
1083  EXPECT_TRUE(engine_flags.has_value());
1084  EXPECT_FALSE(
1085  engine_flags.value() &
1086  FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast);
1087 }
1088 
1089 TEST_F(FlutterWindowsEngineTest, PostRasterThreadTask) {
1090  FlutterWindowsEngineBuilder builder{GetContext()};
1091  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
1092  EngineModifier modifier(engine.get());
1093 
1094  modifier.embedder_api().PostRenderThreadTask = MOCK_ENGINE_PROC(
1095  PostRenderThreadTask, ([](auto engine, auto callback, auto context) {
1096  callback(context);
1097  return kSuccess;
1098  }));
1099 
1100  bool called = false;
1101  engine->PostRasterThreadTask([&called]() { called = true; });
1102 
1103  EXPECT_TRUE(called);
1104 }
1105 
1106 class MockFlutterWindowsView : public FlutterWindowsView {
1107  public:
1109  std::unique_ptr<WindowBindingHandler> wbh)
1111  engine,
1112  std::move(wbh),
1113  false,
1114  BoxConstraints()) {}
1116 
1118  NotifyWinEventWrapper,
1119  (ui::AXPlatformNodeWin*, ax::mojom::Event),
1120  (override));
1121  MOCK_METHOD(HWND, GetWindowHandle, (), (const, override));
1122  MOCK_METHOD(bool, Focus, (), (override));
1123 
1124  private:
1125  FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
1126 };
1127 
1128 // Verify the view is notified of accessibility announcements.
1129 TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncement) {
1130  auto& context = GetContext();
1131  WindowsConfigBuilder builder{context};
1132  builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
1133 
1134  bool done = false;
1135  auto native_entry =
1136  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
1137  context.AddNativeFunction("Signal", native_entry);
1138 
1139  EnginePtr engine{builder.RunHeadless()};
1140  ASSERT_NE(engine, nullptr);
1141 
1142  ui::AXPlatformNodeDelegateBase parent_delegate;
1143  AlertPlatformNodeDelegate delegate{parent_delegate};
1144 
1145  auto window_binding_handler =
1146  std::make_unique<NiceMock<MockWindowBindingHandler>>();
1147  EXPECT_CALL(*window_binding_handler, GetAlertDelegate)
1148  .WillOnce(Return(&delegate));
1149 
1150  auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
1151  MockFlutterWindowsView view{windows_engine,
1152  std::move(window_binding_handler)};
1153  EngineModifier modifier{windows_engine};
1154  modifier.SetImplicitView(&view);
1155 
1156  windows_engine->UpdateSemanticsEnabled(true);
1157 
1158  EXPECT_CALL(view, NotifyWinEventWrapper).Times(1);
1159 
1160  // Rely on timeout mechanism in CI.
1161  while (!done) {
1162  windows_engine->task_runner()->ProcessTasks();
1163  }
1164 }
1165 
1166 // Verify the app can send accessibility announcements while in headless mode.
1167 TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncementHeadless) {
1168  auto& context = GetContext();
1169  WindowsConfigBuilder builder{context};
1170  builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
1171 
1172  bool done = false;
1173  auto native_entry =
1174  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
1175  context.AddNativeFunction("Signal", native_entry);
1176 
1177  EnginePtr engine{builder.RunHeadless()};
1178  ASSERT_NE(engine, nullptr);
1179 
1180  auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
1181  windows_engine->UpdateSemanticsEnabled(true);
1182 
1183  // Rely on timeout mechanism in CI.
1184  while (!done) {
1185  windows_engine->task_runner()->ProcessTasks();
1186  }
1187 }
1188 
1189 // Verify the engine does not crash if it receives an accessibility event
1190 // it does not support yet.
1191 TEST_F(FlutterWindowsEngineTest, AccessibilityTooltip) {
1192  fml::testing::LogCapture log_capture;
1193 
1194  auto& context = GetContext();
1195  WindowsConfigBuilder builder{context};
1196  builder.SetDartEntrypoint("sendAccessibilityTooltipEvent");
1197 
1198  bool done = false;
1199  auto native_entry =
1200  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
1201  context.AddNativeFunction("Signal", native_entry);
1202 
1203  ViewControllerPtr controller{builder.Run()};
1204  ASSERT_NE(controller, nullptr);
1205 
1206  auto engine = FlutterDesktopViewControllerGetEngine(controller.get());
1207  auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine);
1208  windows_engine->UpdateSemanticsEnabled(true);
1209 
1210  // Rely on timeout mechanism in CI.
1211  while (!done) {
1212  windows_engine->task_runner()->ProcessTasks();
1213  }
1214 
1215  // Verify no error was logged.
1216  // Regression test for:
1217  // https://github.com/flutter/flutter/issues/144274
1218  EXPECT_EQ(log_capture.str().find("tooltip"), std::string::npos);
1219 }
1220 
1222  public:
1224  : WindowsLifecycleManager(engine) {}
1226 
1228  void,
1229  Quit,
1230  (std::optional<HWND>, std::optional<WPARAM>, std::optional<LPARAM>, UINT),
1231  (override));
1232  MOCK_METHOD(void, DispatchMessage, (HWND, UINT, WPARAM, LPARAM), (override));
1233  MOCK_METHOD(bool, IsLastWindowOfProcess, (), (override));
1235 
1236  void BeginProcessingLifecycle() override {
1240  }
1241  }
1242 
1243  std::function<void()> begin_processing_callback = nullptr;
1244 };
1245 
1247  FlutterWindowsEngineBuilder builder{GetContext()};
1248  builder.SetDartEntrypoint("exitTestExit");
1249  bool finished = false;
1250 
1251  auto engine = builder.Build();
1252  auto window_binding_handler =
1253  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1254  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1255 
1256  EngineModifier modifier(engine.get());
1257  modifier.SetImplicitView(&view);
1258  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1259  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1260  EXPECT_CALL(*handler, Quit)
1261  .WillOnce([&finished](std::optional<HWND> hwnd,
1262  std::optional<WPARAM> wparam,
1263  std::optional<LPARAM> lparam,
1264  UINT exit_code) { finished = exit_code == 0; });
1265  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
1266  modifier.SetLifecycleManager(std::move(handler));
1267 
1268  engine->lifecycle_manager()->BeginProcessingExit();
1269 
1270  engine->Run();
1271 
1272  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1273  0);
1274 
1275  // The test will only succeed when this while loop exits. Otherwise it will
1276  // timeout.
1277  while (!finished) {
1278  engine->task_runner()->ProcessTasks();
1279  }
1280 }
1281 
1283  FlutterWindowsEngineBuilder builder{GetContext()};
1284  builder.SetDartEntrypoint("exitTestCancel");
1285  bool did_call = false;
1286 
1287  auto engine = builder.Build();
1288  auto window_binding_handler =
1289  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1290  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1291 
1292  EngineModifier modifier(engine.get());
1293  modifier.SetImplicitView(&view);
1294  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1295  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1296  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
1297  EXPECT_CALL(*handler, Quit).Times(0);
1298  modifier.SetLifecycleManager(std::move(handler));
1299  engine->lifecycle_manager()->BeginProcessingExit();
1300 
1301  auto binary_messenger =
1302  std::make_unique<BinaryMessengerImpl>(engine->messenger());
1303  binary_messenger->SetMessageHandler(
1304  "flutter/platform", [&did_call](const uint8_t* message,
1305  size_t message_size, BinaryReply reply) {
1306  std::string contents(message, message + message_size);
1307  EXPECT_NE(contents.find("\"method\":\"System.exitApplication\""),
1308  std::string::npos);
1309  EXPECT_NE(contents.find("\"type\":\"required\""), std::string::npos);
1310  EXPECT_NE(contents.find("\"exitCode\":0"), std::string::npos);
1311  did_call = true;
1312  char response[] = "";
1313  reply(reinterpret_cast<uint8_t*>(response), 0);
1314  });
1315 
1316  engine->Run();
1317 
1318  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1319  0);
1320 
1321  while (!did_call) {
1322  engine->task_runner()->ProcessTasks();
1323  }
1324 }
1325 
1326 // Flutter consumes the first WM_CLOSE message to allow the app to cancel the
1327 // exit. If the app does not cancel the exit, Flutter synthesizes a second
1328 // WM_CLOSE message.
1329 TEST_F(FlutterWindowsEngineTest, TestExitSecondCloseMessage) {
1330  FlutterWindowsEngineBuilder builder{GetContext()};
1331  builder.SetDartEntrypoint("exitTestExit");
1332  bool second_close = false;
1333 
1334  auto engine = builder.Build();
1335  auto window_binding_handler =
1336  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1337  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1338 
1339  EngineModifier modifier(engine.get());
1340  modifier.SetImplicitView(&view);
1341  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1342  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1343  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(true));
1344  EXPECT_CALL(*handler, Quit)
1345  .WillOnce([handler_ptr = handler.get()](
1346  std::optional<HWND> hwnd, std::optional<WPARAM> wparam,
1347  std::optional<LPARAM> lparam, UINT exit_code) {
1348  handler_ptr->WindowsLifecycleManager::Quit(hwnd, wparam, lparam,
1349  exit_code);
1350  });
1351  EXPECT_CALL(*handler, DispatchMessage)
1352  .WillRepeatedly(
1353  [&engine](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
1354  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1355  hwnd, msg, wparam, lparam);
1356  });
1357  modifier.SetLifecycleManager(std::move(handler));
1358  engine->lifecycle_manager()->BeginProcessingExit();
1359 
1360  engine->Run();
1361 
1362  // This delegate will be registered after the lifecycle manager, so it will be
1363  // called only when a message is not consumed by the lifecycle manager. This
1364  // should be called on the second, synthesized WM_CLOSE message that the
1365  // lifecycle manager posts.
1366  engine->window_proc_delegate_manager()->RegisterTopLevelWindowProcDelegate(
1367  [](HWND hwnd, UINT message, WPARAM wpar, LPARAM lpar, void* user_data,
1368  LRESULT* result) {
1369  switch (message) {
1370  case WM_CLOSE: {
1371  bool* called = reinterpret_cast<bool*>(user_data);
1372  *called = true;
1373  return true;
1374  }
1375  }
1376  return false;
1377  },
1378  reinterpret_cast<void*>(&second_close));
1379 
1380  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1381  0);
1382 
1383  while (!second_close) {
1384  engine->task_runner()->ProcessTasks();
1385  }
1386 }
1387 
1388 TEST_F(FlutterWindowsEngineTest, TestExitCloseMultiWindow) {
1389  FlutterWindowsEngineBuilder builder{GetContext()};
1390  builder.SetDartEntrypoint("exitTestExit");
1391  bool finished = false;
1392 
1393  auto engine = builder.Build();
1394  auto window_binding_handler =
1395  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1396  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1397 
1398  EngineModifier modifier(engine.get());
1399  modifier.SetImplicitView(&view);
1400  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1401  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1402  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce([&finished]() {
1403  finished = true;
1404  return false;
1405  });
1406  // Quit should not be called when there is more than one window.
1407  EXPECT_CALL(*handler, Quit).Times(0);
1408  modifier.SetLifecycleManager(std::move(handler));
1409  engine->lifecycle_manager()->BeginProcessingExit();
1410 
1411  engine->Run();
1412 
1413  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1414  0);
1415 
1416  while (!finished) {
1417  engine->task_runner()->ProcessTasks();
1418  }
1419 }
1420 
1421 TEST_F(FlutterWindowsEngineTest, LifecycleManagerDisabledByDefault) {
1422  FlutterWindowsEngineBuilder builder{GetContext()};
1423 
1424  auto engine = builder.Build();
1425  auto window_binding_handler =
1426  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1427  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1428 
1429  EngineModifier modifier(engine.get());
1430  modifier.SetImplicitView(&view);
1431  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1432  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1433  EXPECT_CALL(*handler, IsLastWindowOfProcess).Times(0);
1434  modifier.SetLifecycleManager(std::move(handler));
1435 
1436  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1437  0);
1438 }
1439 
1440 TEST_F(FlutterWindowsEngineTest, EnableApplicationLifecycle) {
1441  FlutterWindowsEngineBuilder builder{GetContext()};
1442 
1443  auto engine = builder.Build();
1444  auto window_binding_handler =
1445  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1446  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1447 
1448  EngineModifier modifier(engine.get());
1449  modifier.SetImplicitView(&view);
1450  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1451  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1452  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
1453  modifier.SetLifecycleManager(std::move(handler));
1454  engine->lifecycle_manager()->BeginProcessingExit();
1455 
1456  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1457  0);
1458 }
1459 
1460 TEST_F(FlutterWindowsEngineTest, ApplicationLifecycleExternalWindow) {
1461  FlutterWindowsEngineBuilder builder{GetContext()};
1462 
1463  auto engine = builder.Build();
1464  auto window_binding_handler =
1465  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1466  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1467 
1468  EngineModifier modifier(engine.get());
1469  modifier.SetImplicitView(&view);
1470  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1471  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1472  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
1473  modifier.SetLifecycleManager(std::move(handler));
1474  engine->lifecycle_manager()->BeginProcessingExit();
1475 
1476  engine->lifecycle_manager()->ExternalWindowMessage(0, WM_CLOSE, 0, 0);
1477 }
1478 
1479 TEST_F(FlutterWindowsEngineTest, LifecycleStateTransition) {
1480  FlutterWindowsEngineBuilder builder{GetContext()};
1481 
1482  auto 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  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1490  engine->Run();
1491 
1492  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1493  (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1494 
1495  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1496  PumpMessage();
1497  }
1498 
1499  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1501 
1502  engine->lifecycle_manager()->OnWindowStateEvent((HWND)1,
1504 
1505  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1506  PumpMessage();
1507  }
1508 
1509  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1511 
1512  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1513  (HWND)1, WM_SIZE, SIZE_MINIMIZED, 0);
1514 
1515  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1516  PumpMessage();
1517  }
1518 
1519  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1521 
1522  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1523  (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1524 
1525  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1526  PumpMessage();
1527  }
1528 
1529  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1531 }
1532 
1533 TEST_F(FlutterWindowsEngineTest, ExternalWindowMessage) {
1534  FlutterWindowsEngineBuilder builder{GetContext()};
1535 
1536  auto engine = builder.Build();
1537  auto window_binding_handler =
1538  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1539  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1540 
1541  EngineModifier modifier(engine.get());
1542  modifier.SetImplicitView(&view);
1543  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1544  // Sets lifecycle state to resumed.
1545  engine->Run();
1546 
1547  // Ensure HWND(1) is in the set of visible windows before hiding it.
1548  engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1549  TRUE, NULL);
1550  engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1551  FALSE, NULL);
1552 
1553  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1554  PumpMessage();
1555  }
1556 
1557  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1559 }
1560 
1561 TEST_F(FlutterWindowsEngineTest, InnerWindowHidden) {
1562  FlutterWindowsEngineBuilder builder{GetContext()};
1563  HWND outer = reinterpret_cast<HWND>(1);
1564  HWND inner = reinterpret_cast<HWND>(2);
1565 
1566  auto engine = builder.Build();
1567  auto window_binding_handler =
1568  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1569  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1570  ON_CALL(view, GetWindowHandle).WillByDefault([=]() { return inner; });
1571 
1572  EngineModifier modifier(engine.get());
1573  modifier.SetImplicitView(&view);
1574  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1575  // Sets lifecycle state to resumed.
1576  engine->Run();
1577 
1578  // Show both top-level and Flutter window.
1579  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1580  outer, WM_SHOWWINDOW, TRUE, NULL);
1581  view.OnWindowStateEvent(inner, WindowStateEvent::kShow);
1582  view.OnWindowStateEvent(inner, WindowStateEvent::kFocus);
1583 
1584  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1585  PumpMessage();
1586  }
1587 
1588  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1590 
1591  // Hide Flutter window, but not top level window.
1592  view.OnWindowStateEvent(inner, WindowStateEvent::kHide);
1593 
1594  while (engine->lifecycle_manager()->IsUpdateStateScheduled()) {
1595  PumpMessage();
1596  }
1597 
1598  // The top-level window is still visible, so we ought not enter hidden state.
1599  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1601 }
1602 
1603 TEST_F(FlutterWindowsEngineTest, EnableLifecycleState) {
1604  FlutterWindowsEngineBuilder builder{GetContext()};
1605  builder.SetDartEntrypoint("enableLifecycleTest");
1606  bool finished = false;
1607 
1608  auto engine = builder.Build();
1609  auto window_binding_handler =
1610  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1611  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1612 
1613  EngineModifier modifier(engine.get());
1614  modifier.SetImplicitView(&view);
1615  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1616  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1617  EXPECT_CALL(*handler, SetLifecycleState)
1618  .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1619  handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1620  });
1621  modifier.SetLifecycleManager(std::move(handler));
1622 
1623  auto binary_messenger =
1624  std::make_unique<BinaryMessengerImpl>(engine->messenger());
1625  // Mark the test only as completed on receiving an inactive state message.
1626  binary_messenger->SetMessageHandler(
1627  "flutter/unittest", [&finished](const uint8_t* message,
1628  size_t message_size, BinaryReply reply) {
1629  std::string contents(message, message + message_size);
1630  EXPECT_NE(contents.find("AppLifecycleState.inactive"),
1631  std::string::npos);
1632  finished = true;
1633  char response[] = "";
1634  reply(reinterpret_cast<uint8_t*>(response), 0);
1635  });
1636 
1637  engine->Run();
1638 
1639  // Test that setting the state before enabling lifecycle does nothing.
1640  HWND hwnd = reinterpret_cast<HWND>(1);
1641  view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1642  view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1643  EXPECT_FALSE(finished);
1644 
1645  // Test that we can set the state afterwards.
1646 
1647  engine->lifecycle_manager()->BeginProcessingLifecycle();
1648  view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1649 
1650  while (!finished) {
1651  engine->task_runner()->ProcessTasks();
1652  }
1653 }
1654 
1655 TEST_F(FlutterWindowsEngineTest, LifecycleStateToFrom) {
1656  FlutterWindowsEngineBuilder builder{GetContext()};
1657  builder.SetDartEntrypoint("enableLifecycleToFrom");
1658  bool enabled_lifecycle = false;
1659  bool dart_responded = false;
1660 
1661  auto engine = builder.Build();
1662  auto window_binding_handler =
1663  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1664  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1665 
1666  EngineModifier modifier(engine.get());
1667  modifier.SetImplicitView(&view);
1668  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1669  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1670  EXPECT_CALL(*handler, SetLifecycleState)
1671  .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1672  handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1673  });
1674  handler->begin_processing_callback = [&]() { enabled_lifecycle = true; };
1675  modifier.SetLifecycleManager(std::move(handler));
1676 
1677  auto binary_messenger =
1678  std::make_unique<BinaryMessengerImpl>(engine->messenger());
1679  binary_messenger->SetMessageHandler(
1680  "flutter/unittest",
1681  [&](const uint8_t* message, size_t message_size, BinaryReply reply) {
1682  std::string contents(message, message + message_size);
1683  EXPECT_NE(contents.find("AppLifecycleState."), std::string::npos);
1684  dart_responded = true;
1685  char response[] = "";
1686  reply(reinterpret_cast<uint8_t*>(response), 0);
1687  });
1688 
1689  engine->Run();
1690 
1691  while (!enabled_lifecycle) {
1692  engine->task_runner()->ProcessTasks();
1693  }
1694 
1695  HWND hwnd = reinterpret_cast<HWND>(1);
1696  view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1697  view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1698 
1699  while (!dart_responded) {
1700  engine->task_runner()->ProcessTasks();
1701  }
1702 }
1703 
1704 TEST_F(FlutterWindowsEngineTest, ChannelListenedTo) {
1705  FlutterWindowsEngineBuilder builder{GetContext()};
1706  builder.SetDartEntrypoint("enableLifecycleToFrom");
1707 
1708  auto engine = builder.Build();
1709  auto window_binding_handler =
1710  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1711  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1712 
1713  EngineModifier modifier(engine.get());
1714  modifier.SetImplicitView(&view);
1715  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1716 
1717  bool lifecycle_began = false;
1718  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1719  handler->begin_processing_callback = [&]() { lifecycle_began = true; };
1720  modifier.SetLifecycleManager(std::move(handler));
1721 
1722  engine->Run();
1723 
1724  while (!lifecycle_began) {
1725  engine->task_runner()->ProcessTasks();
1726  }
1727 }
1728 
1729 TEST_F(FlutterWindowsEngineTest, ReceivePlatformViewMessage) {
1730  FlutterWindowsEngineBuilder builder{GetContext()};
1731  builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1732  auto engine = builder.Build();
1733 
1734  EngineModifier modifier{engine.get()};
1735  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1736 
1737  bool received_call = false;
1738 
1739  auto manager = std::make_unique<MockPlatformViewManager>(engine.get());
1740  EXPECT_CALL(*manager, AddPlatformView)
1741  .WillOnce([&](PlatformViewId id, std::string_view type_name) {
1742  received_call = true;
1743  return true;
1744  });
1745  modifier.SetPlatformViewPlugin(std::move(manager));
1746 
1747  engine->Run();
1748 
1749  while (!received_call) {
1750  engine->task_runner()->ProcessTasks();
1751  }
1752 }
1753 
1754 TEST_F(FlutterWindowsEngineTest, AddViewFailureDoesNotHang) {
1755  FlutterWindowsEngineBuilder builder{GetContext()};
1756  auto engine = builder.Build();
1757 
1758  EngineModifier modifier{engine.get()};
1759 
1760  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1761  modifier.embedder_api().AddView = MOCK_ENGINE_PROC(
1762  AddView,
1763  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
1764  const FlutterAddViewInfo* info) { return kInternalInconsistency; });
1765 
1766  ASSERT_TRUE(engine->Run());
1767 
1768  // Create the first view. This is the implicit view and isn't added to the
1769  // engine.
1770  auto implicit_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1771 
1772  std::unique_ptr<FlutterWindowsView> implicit_view =
1773  engine->CreateView(std::move(implicit_window),
1774  /*is_sized_to_content=*/false, BoxConstraints());
1775 
1776  EXPECT_TRUE(implicit_view);
1777 
1778  // Create a second view. The embedder attempts to add it to the engine.
1779  auto second_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1780 
1781  EXPECT_DEBUG_DEATH(
1782  engine->CreateView(std::move(second_window),
1783  /*is_sized_to_content=*/false, BoxConstraints()),
1784  "FlutterEngineAddView returned an unexpected result");
1785 }
1786 
1787 TEST_F(FlutterWindowsEngineTest, RemoveViewFailureDoesNotHang) {
1788  FlutterWindowsEngineBuilder builder{GetContext()};
1789  builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1790  auto engine = builder.Build();
1791 
1792  EngineModifier modifier{engine.get()};
1793 
1794  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1795  modifier.embedder_api().RemoveView = MOCK_ENGINE_PROC(
1796  RemoveView,
1797  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
1798  const FlutterRemoveViewInfo* info) { return kInternalInconsistency; });
1799 
1800  ASSERT_TRUE(engine->Run());
1801  EXPECT_DEBUG_DEATH(engine->RemoveView(123),
1802  "FlutterEngineRemoveView returned an unexpected result");
1803 }
1804 
1806  auto& context = GetContext();
1807  WindowsConfigBuilder builder{context};
1808  builder.SetDartEntrypoint("mergedUIThread");
1809  builder.SetUIThreadPolicy(FlutterDesktopUIThreadPolicy::RunOnPlatformThread);
1810 
1811  std::optional<std::thread::id> ui_thread_id;
1812 
1813  auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1814  ui_thread_id = std::this_thread::get_id();
1815  });
1816  context.AddNativeFunction("Signal", native_entry);
1817 
1818  EnginePtr engine{builder.RunHeadless()};
1819  while (!ui_thread_id) {
1820  PumpMessage();
1821  }
1822  ASSERT_EQ(*ui_thread_id, std::this_thread::get_id());
1823 }
1824 
1825 TEST_F(FlutterWindowsEngineTest, OnViewFocusChangeRequest) {
1826  FlutterWindowsEngineBuilder builder{GetContext()};
1827  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
1828  auto window_binding_handler =
1829  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1830  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1831 
1832  EngineModifier modifier(engine.get());
1833  modifier.SetImplicitView(&view);
1834 
1835  FlutterViewFocusChangeRequest request;
1836  request.view_id = kImplicitViewId;
1837 
1838  EXPECT_CALL(view, Focus()).WillOnce(Return(true));
1839  modifier.OnViewFocusChangeRequest(&request);
1840 }
1841 
1842 TEST_F(FlutterWindowsEngineTest, UpdateSemanticsMultiView) {
1843  auto& context = GetContext();
1844  WindowsConfigBuilder builder{context};
1845  builder.SetDartEntrypoint("sendSemanticsTreeInfo");
1846 
1847  // Setup: a signal for when we have send out all of our semantics updates
1848  bool done = false;
1849  auto native_entry =
1850  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
1851  context.AddNativeFunction("Signal", native_entry);
1852 
1853  // Setup: Create the engine and two views + enable semantics
1854  EnginePtr engine{builder.RunHeadless()};
1855  ASSERT_NE(engine, nullptr);
1856 
1857  auto window_binding_handler1 =
1858  std::make_unique<NiceMock<MockWindowBindingHandler>>();
1859  auto window_binding_handler2 =
1860  std::make_unique<NiceMock<MockWindowBindingHandler>>();
1861 
1862  // The following mocks are required by
1863  // FlutterWindowsView::CreateWindowMetricsEvent so that we create a valid
1864  // view.
1865  EXPECT_CALL(*window_binding_handler1, GetPhysicalWindowBounds)
1866  .WillRepeatedly(testing::Return(PhysicalWindowBounds{100, 100}));
1867  EXPECT_CALL(*window_binding_handler1, GetDpiScale)
1868  .WillRepeatedly(testing::Return(96.0));
1869  EXPECT_CALL(*window_binding_handler2, GetPhysicalWindowBounds)
1870  .WillRepeatedly(testing::Return(PhysicalWindowBounds{200, 200}));
1871  EXPECT_CALL(*window_binding_handler2, GetDpiScale)
1872  .WillRepeatedly(testing::Return(96.0));
1873 
1874  auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
1875  EngineModifier modifier{windows_engine};
1876  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1877 
1878  // We want to avoid adding an implicit view as the first view
1879  modifier.SetNextViewId(kImplicitViewId + 1);
1880 
1881  auto view1 = windows_engine->CreateView(std::move(window_binding_handler1),
1882  /*is_sized_to_content=*/false,
1883  BoxConstraints());
1884  auto view2 = windows_engine->CreateView(std::move(window_binding_handler2),
1885  /*is_sized_to_content=*/false,
1886  BoxConstraints());
1887 
1888  // Act: UpdateSemanticsEnabled will trigger the semantics updates
1889  // to get sent.
1890  windows_engine->UpdateSemanticsEnabled(true);
1891 
1892  while (!done) {
1893  windows_engine->task_runner()->ProcessTasks();
1894  }
1895 
1896  auto accessibility_bridge1 = view1->accessibility_bridge().lock();
1897  auto accessibility_bridge2 = view2->accessibility_bridge().lock();
1898 
1899  // Expect: that the semantics trees are updated with their
1900  // respective nodes.
1901  while (
1902  !accessibility_bridge1->GetPlatformNodeFromTree(view1->view_id() + 1)) {
1903  windows_engine->task_runner()->ProcessTasks();
1904  }
1905 
1906  while (
1907  !accessibility_bridge2->GetPlatformNodeFromTree(view2->view_id() + 1)) {
1908  windows_engine->task_runner()->ProcessTasks();
1909  }
1910 
1911  // Rely on timeout mechanism in CI.
1912  auto tree1 = accessibility_bridge1->GetTree();
1913  auto tree2 = accessibility_bridge2->GetTree();
1914  EXPECT_NE(tree1->GetFromId(view1->view_id() + 1), nullptr);
1915  EXPECT_NE(tree2->GetFromId(view2->view_id() + 1), nullptr);
1916 }
1917 
1918 } // namespace testing
1919 } // namespace flutter
std::unique_ptr< FlutterWindowsView > CreateView(std::unique_ptr< WindowBindingHandler > window, bool is_sized_to_content, const BoxConstraints &box_constraints, FlutterWindowsViewSizingDelegate *sizing_delegate=nullptr)
virtual void Quit(std::optional< HWND > window, std::optional< WPARAM > wparam, std::optional< LPARAM > lparam, UINT exit_code)
virtual void DispatchMessage(HWND window, UINT msg, WPARAM wparam, LPARAM lparam)
virtual void SetLifecycleState(AppLifecycleState state)
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(* FlutterDesktopBinaryReply)(const uint8_t *data, size_t data_size, void *user_data)
FlutterDesktopEngineRef FlutterDesktopViewControllerGetEngine(FlutterDesktopViewControllerRef ref)
@ RunOnPlatformThread
@ DefaultImpeller
@ EnabledImpeller
@ DisabledImpeller
int64_t PlatformViewId
FlutterDesktopBinaryReply callback
Win32Message message
TEST_F(AccessibilityPluginTest, DirectAnnounceCall)
UINT GetDpiForMonitor(HMONITOR monitor)
Definition: dpi_utils.cc:132
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
static void WindowsPlatformThreadPrioritySetter(FlutterThreadPriority priority)
constexpr FlutterViewId kImplicitViewId