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