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