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().DispatchSemanticsAction = MOCK_ENGINE_PROC(
486  DispatchSemanticsAction,
487  ([&called, &message](auto engine, auto target, auto action, auto data,
488  auto data_length) {
489  called = true;
490  EXPECT_EQ(target, 42);
491  EXPECT_EQ(action, kFlutterSemanticsActionDismiss);
492  EXPECT_EQ(memcmp(data, message.c_str(), message.size()), 0);
493  EXPECT_EQ(data_length, message.size());
494  return kSuccess;
495  }));
496 
497  auto data = fml::MallocMapping::Copy(message.c_str(), message.size());
498  engine->DispatchSemanticsAction(42, kFlutterSemanticsActionDismiss,
499  std::move(data));
500  EXPECT_TRUE(called);
501 }
502 
503 TEST_F(FlutterWindowsEngineTest, SetsThreadPriority) {
504  WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kBackground);
505  EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
506  THREAD_PRIORITY_BELOW_NORMAL);
507 
508  WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kDisplay);
509  EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
510  THREAD_PRIORITY_ABOVE_NORMAL);
511 
512  WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kRaster);
513  EXPECT_EQ(GetThreadPriority(GetCurrentThread()),
514  THREAD_PRIORITY_ABOVE_NORMAL);
515 
516  // FlutterThreadPriority::kNormal does not change thread priority, reset to 0
517  // here.
518  SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
519 
520  WindowsPlatformThreadPrioritySetter(FlutterThreadPriority::kNormal);
521  EXPECT_EQ(GetThreadPriority(GetCurrentThread()), THREAD_PRIORITY_NORMAL);
522 }
523 
524 TEST_F(FlutterWindowsEngineTest, AddPluginRegistrarDestructionCallback) {
525  FlutterWindowsEngineBuilder builder{GetContext()};
526  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
527  EngineModifier modifier(engine.get());
528 
529  MockEmbedderApiForKeyboard(modifier,
530  std::make_shared<MockKeyResponseController>());
531 
532  engine->Run();
533 
534  // Verify that destruction handlers don't overwrite each other.
535  int result1 = 0;
536  int result2 = 0;
537  engine->AddPluginRegistrarDestructionCallback(
539  auto result = reinterpret_cast<int*>(ref);
540  *result = 1;
541  },
542  reinterpret_cast<FlutterDesktopPluginRegistrarRef>(&result1));
543  engine->AddPluginRegistrarDestructionCallback(
545  auto result = reinterpret_cast<int*>(ref);
546  *result = 2;
547  },
548  reinterpret_cast<FlutterDesktopPluginRegistrarRef>(&result2));
549 
550  engine->Stop();
551  EXPECT_EQ(result1, 1);
552  EXPECT_EQ(result2, 2);
553 }
554 
556  FlutterWindowsEngineBuilder builder{GetContext()};
557  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
558  EngineModifier modifier(engine.get());
559 
560  bool called = false;
561  modifier.embedder_api().ScheduleFrame =
562  MOCK_ENGINE_PROC(ScheduleFrame, ([&called](auto engine) {
563  called = true;
564  return kSuccess;
565  }));
566 
567  engine->ScheduleFrame();
568  EXPECT_TRUE(called);
569 }
570 
571 TEST_F(FlutterWindowsEngineTest, SetNextFrameCallback) {
572  FlutterWindowsEngineBuilder builder{GetContext()};
573  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
574  EngineModifier modifier(engine.get());
575 
576  bool called = false;
577  modifier.embedder_api().SetNextFrameCallback = MOCK_ENGINE_PROC(
578  SetNextFrameCallback, ([&called](auto engine, auto callback, auto data) {
579  called = true;
580  return kSuccess;
581  }));
582 
583  engine->SetNextFrameCallback([]() {});
584  EXPECT_TRUE(called);
585 }
586 
587 TEST_F(FlutterWindowsEngineTest, GetExecutableName) {
588  FlutterWindowsEngineBuilder builder{GetContext()};
589  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
590  EXPECT_EQ(engine->GetExecutableName(), "flutter_windows_unittests.exe");
591 }
592 
593 // Ensure that after setting or resetting the high contrast feature,
594 // the corresponding status flag can be retrieved from the engine.
595 TEST_F(FlutterWindowsEngineTest, UpdateHighContrastFeature) {
596  auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
597  EXPECT_CALL(*windows_proc_table, GetHighContrastEnabled)
598  .WillOnce(Return(true))
599  .WillOnce(Return(false));
600 
601  FlutterWindowsEngineBuilder builder{GetContext()};
602  builder.SetWindowsProcTable(windows_proc_table);
603  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
604  EngineModifier modifier(engine.get());
605 
606  std::optional<FlutterAccessibilityFeature> engine_flags;
607  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
608  UpdateAccessibilityFeatures, ([&engine_flags](auto engine, auto flags) {
609  engine_flags = flags;
610  return kSuccess;
611  }));
612  modifier.embedder_api().SendPlatformMessage = MOCK_ENGINE_PROC(
613  SendPlatformMessage,
614  [](auto engine, const auto message) { return kSuccess; });
615 
616  // 1: High contrast is enabled.
617  engine->UpdateHighContrastMode();
618 
619  EXPECT_TRUE(engine->high_contrast_enabled());
620  EXPECT_TRUE(engine_flags.has_value());
621  EXPECT_TRUE(
622  engine_flags.value() &
623  FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast);
624 
625  // 2: High contrast is disabled.
626  engine_flags.reset();
627  engine->UpdateHighContrastMode();
628 
629  EXPECT_FALSE(engine->high_contrast_enabled());
630  EXPECT_TRUE(engine_flags.has_value());
631  EXPECT_FALSE(
632  engine_flags.value() &
633  FlutterAccessibilityFeature::kFlutterAccessibilityFeatureHighContrast);
634 }
635 
636 TEST_F(FlutterWindowsEngineTest, PostRasterThreadTask) {
637  FlutterWindowsEngineBuilder builder{GetContext()};
638  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
639  EngineModifier modifier(engine.get());
640 
641  modifier.embedder_api().PostRenderThreadTask = MOCK_ENGINE_PROC(
642  PostRenderThreadTask, ([](auto engine, auto callback, auto context) {
643  callback(context);
644  return kSuccess;
645  }));
646 
647  bool called = false;
648  engine->PostRasterThreadTask([&called]() { called = true; });
649 
650  EXPECT_TRUE(called);
651 }
652 
653 class MockFlutterWindowsView : public FlutterWindowsView {
654  public:
656  std::unique_ptr<WindowBindingHandler> wbh)
657  : FlutterWindowsView(kImplicitViewId, engine, std::move(wbh)) {}
659 
660  MOCK_METHOD(void,
661  NotifyWinEventWrapper,
662  (ui::AXPlatformNodeWin*, ax::mojom::Event),
663  (override));
664  MOCK_METHOD(HWND, GetWindowHandle, (), (const, override));
665  MOCK_METHOD(bool, Focus, (), (override));
666 
667  private:
668  FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
669 };
670 
671 // Verify the view is notified of accessibility announcements.
672 TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncement) {
673  auto& context = GetContext();
674  WindowsConfigBuilder builder{context};
675  builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
676 
677  bool done = false;
678  auto native_entry =
679  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
680  context.AddNativeFunction("Signal", native_entry);
681 
682  EnginePtr engine{builder.RunHeadless()};
683  ASSERT_NE(engine, nullptr);
684 
685  ui::AXPlatformNodeDelegateBase parent_delegate;
686  AlertPlatformNodeDelegate delegate{parent_delegate};
687 
688  auto window_binding_handler =
689  std::make_unique<NiceMock<MockWindowBindingHandler>>();
690  EXPECT_CALL(*window_binding_handler, GetAlertDelegate)
691  .WillOnce(Return(&delegate));
692 
693  auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
694  MockFlutterWindowsView view{windows_engine,
695  std::move(window_binding_handler)};
696  EngineModifier modifier{windows_engine};
697  modifier.SetImplicitView(&view);
698 
699  windows_engine->UpdateSemanticsEnabled(true);
700 
701  EXPECT_CALL(view, NotifyWinEventWrapper).Times(1);
702 
703  // Rely on timeout mechanism in CI.
704  while (!done) {
705  windows_engine->task_runner()->ProcessTasks();
706  }
707 }
708 
709 // Verify the app can send accessibility announcements while in headless mode.
710 TEST_F(FlutterWindowsEngineTest, AccessibilityAnnouncementHeadless) {
711  auto& context = GetContext();
712  WindowsConfigBuilder builder{context};
713  builder.SetDartEntrypoint("sendAccessibilityAnnouncement");
714 
715  bool done = false;
716  auto native_entry =
717  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
718  context.AddNativeFunction("Signal", native_entry);
719 
720  EnginePtr engine{builder.RunHeadless()};
721  ASSERT_NE(engine, nullptr);
722 
723  auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
724  windows_engine->UpdateSemanticsEnabled(true);
725 
726  // Rely on timeout mechanism in CI.
727  while (!done) {
728  windows_engine->task_runner()->ProcessTasks();
729  }
730 }
731 
732 // Verify the engine does not crash if it receives an accessibility event
733 // it does not support yet.
734 TEST_F(FlutterWindowsEngineTest, AccessibilityTooltip) {
735  fml::testing::LogCapture log_capture;
736 
737  auto& context = GetContext();
738  WindowsConfigBuilder builder{context};
739  builder.SetDartEntrypoint("sendAccessibilityTooltipEvent");
740 
741  bool done = false;
742  auto native_entry =
743  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { done = true; });
744  context.AddNativeFunction("Signal", native_entry);
745 
746  ViewControllerPtr controller{builder.Run()};
747  ASSERT_NE(controller, nullptr);
748 
749  auto engine = FlutterDesktopViewControllerGetEngine(controller.get());
750  auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine);
751  windows_engine->UpdateSemanticsEnabled(true);
752 
753  // Rely on timeout mechanism in CI.
754  while (!done) {
755  windows_engine->task_runner()->ProcessTasks();
756  }
757 
758  // Verify no error was logged.
759  // Regression test for:
760  // https://github.com/flutter/flutter/issues/144274
761  EXPECT_EQ(log_capture.str().find("tooltip"), std::string::npos);
762 }
763 
765  public:
767  : WindowsLifecycleManager(engine) {}
769 
770  MOCK_METHOD(
771  void,
772  Quit,
773  (std::optional<HWND>, std::optional<WPARAM>, std::optional<LPARAM>, UINT),
774  (override));
775  MOCK_METHOD(void, DispatchMessage, (HWND, UINT, WPARAM, LPARAM), (override));
776  MOCK_METHOD(bool, IsLastWindowOfProcess, (), (override));
777  MOCK_METHOD(void, SetLifecycleState, (AppLifecycleState), (override));
778 
779  void BeginProcessingLifecycle() override {
783  }
784  }
785 
786  std::function<void()> begin_processing_callback = nullptr;
787 };
788 
790  FlutterWindowsEngineBuilder builder{GetContext()};
791  builder.SetDartEntrypoint("exitTestExit");
792  bool finished = false;
793 
794  auto engine = builder.Build();
795  auto window_binding_handler =
796  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
797  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
798 
799  EngineModifier modifier(engine.get());
800  modifier.SetImplicitView(&view);
801  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
802  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
803  EXPECT_CALL(*handler, SetLifecycleState(AppLifecycleState::kResumed));
804  EXPECT_CALL(*handler, Quit)
805  .WillOnce([&finished](std::optional<HWND> hwnd,
806  std::optional<WPARAM> wparam,
807  std::optional<LPARAM> lparam,
808  UINT exit_code) { finished = exit_code == 0; });
809  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
810  modifier.SetLifecycleManager(std::move(handler));
811 
812  engine->lifecycle_manager()->BeginProcessingExit();
813 
814  engine->Run();
815 
816  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
817  0);
818 
819  // The test will only succeed when this while loop exits. Otherwise it will
820  // timeout.
821  while (!finished) {
822  engine->task_runner()->ProcessTasks();
823  }
824 }
825 
827  FlutterWindowsEngineBuilder builder{GetContext()};
828  builder.SetDartEntrypoint("exitTestCancel");
829  bool did_call = false;
830 
831  auto engine = builder.Build();
832  auto window_binding_handler =
833  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
834  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
835 
836  EngineModifier modifier(engine.get());
837  modifier.SetImplicitView(&view);
838  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
839  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
840  EXPECT_CALL(*handler, SetLifecycleState(AppLifecycleState::kResumed));
841  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillRepeatedly(Return(true));
842  EXPECT_CALL(*handler, Quit).Times(0);
843  modifier.SetLifecycleManager(std::move(handler));
844  engine->lifecycle_manager()->BeginProcessingExit();
845 
846  auto binary_messenger =
847  std::make_unique<BinaryMessengerImpl>(engine->messenger());
848  binary_messenger->SetMessageHandler(
849  "flutter/platform", [&did_call](const uint8_t* message,
850  size_t message_size, BinaryReply reply) {
851  std::string contents(message, message + message_size);
852  EXPECT_NE(contents.find("\"method\":\"System.exitApplication\""),
853  std::string::npos);
854  EXPECT_NE(contents.find("\"type\":\"required\""), std::string::npos);
855  EXPECT_NE(contents.find("\"exitCode\":0"), std::string::npos);
856  did_call = true;
857  char response[] = "";
858  reply(reinterpret_cast<uint8_t*>(response), 0);
859  });
860 
861  engine->Run();
862 
863  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
864  0);
865 
866  while (!did_call) {
867  engine->task_runner()->ProcessTasks();
868  }
869 }
870 
871 // Flutter consumes the first WM_CLOSE message to allow the app to cancel the
872 // exit. If the app does not cancel the exit, Flutter synthesizes a second
873 // WM_CLOSE message.
874 TEST_F(FlutterWindowsEngineTest, TestExitSecondCloseMessage) {
875  FlutterWindowsEngineBuilder builder{GetContext()};
876  builder.SetDartEntrypoint("exitTestExit");
877  bool second_close = false;
878 
879  auto engine = builder.Build();
880  auto window_binding_handler =
881  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
882  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
883 
884  EngineModifier modifier(engine.get());
885  modifier.SetImplicitView(&view);
886  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
887  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
888  EXPECT_CALL(*handler, SetLifecycleState(AppLifecycleState::kResumed));
889  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(true));
890  EXPECT_CALL(*handler, Quit)
891  .WillOnce([handler_ptr = handler.get()](
892  std::optional<HWND> hwnd, std::optional<WPARAM> wparam,
893  std::optional<LPARAM> lparam, UINT exit_code) {
894  handler_ptr->WindowsLifecycleManager::Quit(hwnd, wparam, lparam,
895  exit_code);
896  });
897  EXPECT_CALL(*handler, DispatchMessage)
898  .WillRepeatedly(
899  [&engine](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
900  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
901  hwnd, msg, wparam, lparam);
902  });
903  modifier.SetLifecycleManager(std::move(handler));
904  engine->lifecycle_manager()->BeginProcessingExit();
905 
906  engine->Run();
907 
908  // This delegate will be registered after the lifecycle manager, so it will be
909  // called only when a message is not consumed by the lifecycle manager. This
910  // should be called on the second, synthesized WM_CLOSE message that the
911  // lifecycle manager posts.
912  engine->window_proc_delegate_manager()->RegisterTopLevelWindowProcDelegate(
913  [](HWND hwnd, UINT message, WPARAM wpar, LPARAM lpar, void* user_data,
914  LRESULT* result) {
915  switch (message) {
916  case WM_CLOSE: {
917  bool* called = reinterpret_cast<bool*>(user_data);
918  *called = true;
919  return true;
920  }
921  }
922  return false;
923  },
924  reinterpret_cast<void*>(&second_close));
925 
926  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
927  0);
928 
929  while (!second_close) {
930  engine->task_runner()->ProcessTasks();
931  }
932 }
933 
934 TEST_F(FlutterWindowsEngineTest, TestExitCloseMultiWindow) {
935  FlutterWindowsEngineBuilder builder{GetContext()};
936  builder.SetDartEntrypoint("exitTestExit");
937  bool finished = false;
938 
939  auto engine = builder.Build();
940  auto window_binding_handler =
941  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
942  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
943 
944  EngineModifier modifier(engine.get());
945  modifier.SetImplicitView(&view);
946  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
947  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
948  EXPECT_CALL(*handler, SetLifecycleState(AppLifecycleState::kResumed));
949  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce([&finished]() {
950  finished = true;
951  return false;
952  });
953  // Quit should not be called when there is more than one window.
954  EXPECT_CALL(*handler, Quit).Times(0);
955  modifier.SetLifecycleManager(std::move(handler));
956  engine->lifecycle_manager()->BeginProcessingExit();
957 
958  engine->Run();
959 
960  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
961  0);
962 
963  while (!finished) {
964  engine->task_runner()->ProcessTasks();
965  }
966 }
967 
968 TEST_F(FlutterWindowsEngineTest, LifecycleManagerDisabledByDefault) {
969  FlutterWindowsEngineBuilder builder{GetContext()};
970 
971  auto engine = builder.Build();
972  auto window_binding_handler =
973  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
974  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
975 
976  EngineModifier modifier(engine.get());
977  modifier.SetImplicitView(&view);
978  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
979  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
980  EXPECT_CALL(*handler, IsLastWindowOfProcess).Times(0);
981  modifier.SetLifecycleManager(std::move(handler));
982 
983  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
984  0);
985 }
986 
987 TEST_F(FlutterWindowsEngineTest, EnableApplicationLifecycle) {
988  FlutterWindowsEngineBuilder builder{GetContext()};
989 
990  auto engine = builder.Build();
991  auto window_binding_handler =
992  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
993  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
994 
995  EngineModifier modifier(engine.get());
996  modifier.SetImplicitView(&view);
997  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
998  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
999  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
1000  modifier.SetLifecycleManager(std::move(handler));
1001  engine->lifecycle_manager()->BeginProcessingExit();
1002 
1003  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(0, WM_CLOSE, 0,
1004  0);
1005 }
1006 
1007 TEST_F(FlutterWindowsEngineTest, ApplicationLifecycleExternalWindow) {
1008  FlutterWindowsEngineBuilder builder{GetContext()};
1009 
1010  auto engine = builder.Build();
1011  auto window_binding_handler =
1012  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1013  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1014 
1015  EngineModifier modifier(engine.get());
1016  modifier.SetImplicitView(&view);
1017  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1018  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1019  EXPECT_CALL(*handler, IsLastWindowOfProcess).WillOnce(Return(false));
1020  modifier.SetLifecycleManager(std::move(handler));
1021  engine->lifecycle_manager()->BeginProcessingExit();
1022 
1023  engine->lifecycle_manager()->ExternalWindowMessage(0, WM_CLOSE, 0, 0);
1024 }
1025 
1026 TEST_F(FlutterWindowsEngineTest, AppStartsInResumedState) {
1027  FlutterWindowsEngineBuilder builder{GetContext()};
1028 
1029  auto engine = builder.Build();
1030  auto window_binding_handler =
1031  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1032  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1033 
1034  EngineModifier modifier(engine.get());
1035  modifier.SetImplicitView(&view);
1036  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1037  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1038  EXPECT_CALL(*handler, SetLifecycleState(AppLifecycleState::kResumed))
1039  .Times(1);
1040  modifier.SetLifecycleManager(std::move(handler));
1041  engine->Run();
1042 }
1043 
1044 TEST_F(FlutterWindowsEngineTest, LifecycleStateTransition) {
1045  FlutterWindowsEngineBuilder builder{GetContext()};
1046 
1047  auto engine = builder.Build();
1048  auto window_binding_handler =
1049  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1050  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1051 
1052  EngineModifier modifier(engine.get());
1053  modifier.SetImplicitView(&view);
1054  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1055  engine->Run();
1056 
1057  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1058  (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1059  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1061 
1062  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1063  (HWND)1, WM_SIZE, SIZE_MINIMIZED, 0);
1064  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1066 
1067  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1068  (HWND)1, WM_SIZE, SIZE_RESTORED, 0);
1069  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1071 }
1072 
1073 TEST_F(FlutterWindowsEngineTest, ExternalWindowMessage) {
1074  FlutterWindowsEngineBuilder builder{GetContext()};
1075 
1076  auto engine = builder.Build();
1077  auto window_binding_handler =
1078  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1079  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1080 
1081  EngineModifier modifier(engine.get());
1082  modifier.SetImplicitView(&view);
1083  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1084  // Sets lifecycle state to resumed.
1085  engine->Run();
1086 
1087  // Ensure HWND(1) is in the set of visible windows before hiding it.
1088  engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1089  TRUE, NULL);
1090  engine->ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), WM_SHOWWINDOW,
1091  FALSE, NULL);
1092 
1093  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1095 }
1096 
1097 TEST_F(FlutterWindowsEngineTest, InnerWindowHidden) {
1098  FlutterWindowsEngineBuilder builder{GetContext()};
1099  HWND outer = reinterpret_cast<HWND>(1);
1100  HWND inner = reinterpret_cast<HWND>(2);
1101 
1102  auto engine = builder.Build();
1103  auto window_binding_handler =
1104  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1105  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1106  ON_CALL(view, GetWindowHandle).WillByDefault([=]() { return inner; });
1107 
1108  EngineModifier modifier(engine.get());
1109  modifier.SetImplicitView(&view);
1110  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1111  // Sets lifecycle state to resumed.
1112  engine->Run();
1113 
1114  // Show both top-level and Flutter window.
1115  engine->window_proc_delegate_manager()->OnTopLevelWindowProc(
1116  outer, WM_SHOWWINDOW, TRUE, NULL);
1117  view.OnWindowStateEvent(inner, WindowStateEvent::kShow);
1118  view.OnWindowStateEvent(inner, WindowStateEvent::kFocus);
1119 
1120  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1122 
1123  // Hide Flutter window, but not top level window.
1124  view.OnWindowStateEvent(inner, WindowStateEvent::kHide);
1125 
1126  // The top-level window is still visible, so we ought not enter hidden state.
1127  EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(),
1129 }
1130 
1131 TEST_F(FlutterWindowsEngineTest, EnableLifecycleState) {
1132  FlutterWindowsEngineBuilder builder{GetContext()};
1133  builder.SetDartEntrypoint("enableLifecycleTest");
1134  bool finished = false;
1135 
1136  auto engine = builder.Build();
1137  auto window_binding_handler =
1138  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1139  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1140 
1141  EngineModifier modifier(engine.get());
1142  modifier.SetImplicitView(&view);
1143  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1144  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1145  EXPECT_CALL(*handler, SetLifecycleState)
1146  .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1147  handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1148  });
1149  modifier.SetLifecycleManager(std::move(handler));
1150 
1151  auto binary_messenger =
1152  std::make_unique<BinaryMessengerImpl>(engine->messenger());
1153  // Mark the test only as completed on receiving an inactive state message.
1154  binary_messenger->SetMessageHandler(
1155  "flutter/unittest", [&finished](const uint8_t* message,
1156  size_t message_size, BinaryReply reply) {
1157  std::string contents(message, message + message_size);
1158  EXPECT_NE(contents.find("AppLifecycleState.inactive"),
1159  std::string::npos);
1160  finished = true;
1161  char response[] = "";
1162  reply(reinterpret_cast<uint8_t*>(response), 0);
1163  });
1164 
1165  engine->Run();
1166 
1167  // Test that setting the state before enabling lifecycle does nothing.
1168  HWND hwnd = reinterpret_cast<HWND>(1);
1169  view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1170  view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1171  EXPECT_FALSE(finished);
1172 
1173  // Test that we can set the state afterwards.
1174 
1175  engine->lifecycle_manager()->BeginProcessingLifecycle();
1176  view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1177 
1178  while (!finished) {
1179  engine->task_runner()->ProcessTasks();
1180  }
1181 }
1182 
1183 TEST_F(FlutterWindowsEngineTest, LifecycleStateToFrom) {
1184  FlutterWindowsEngineBuilder builder{GetContext()};
1185  builder.SetDartEntrypoint("enableLifecycleToFrom");
1186  bool enabled_lifecycle = false;
1187  bool dart_responded = false;
1188 
1189  auto engine = builder.Build();
1190  auto window_binding_handler =
1191  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1192  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1193 
1194  EngineModifier modifier(engine.get());
1195  modifier.SetImplicitView(&view);
1196  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1197  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1198  EXPECT_CALL(*handler, SetLifecycleState)
1199  .WillRepeatedly([handler_ptr = handler.get()](AppLifecycleState state) {
1200  handler_ptr->WindowsLifecycleManager::SetLifecycleState(state);
1201  });
1202  handler->begin_processing_callback = [&]() { enabled_lifecycle = true; };
1203  modifier.SetLifecycleManager(std::move(handler));
1204 
1205  auto binary_messenger =
1206  std::make_unique<BinaryMessengerImpl>(engine->messenger());
1207  binary_messenger->SetMessageHandler(
1208  "flutter/unittest",
1209  [&](const uint8_t* message, size_t message_size, BinaryReply reply) {
1210  std::string contents(message, message + message_size);
1211  EXPECT_NE(contents.find("AppLifecycleState."), std::string::npos);
1212  dart_responded = true;
1213  char response[] = "";
1214  reply(reinterpret_cast<uint8_t*>(response), 0);
1215  });
1216 
1217  engine->Run();
1218 
1219  while (!enabled_lifecycle) {
1220  engine->task_runner()->ProcessTasks();
1221  }
1222 
1223  HWND hwnd = reinterpret_cast<HWND>(1);
1224  view.OnWindowStateEvent(hwnd, WindowStateEvent::kShow);
1225  view.OnWindowStateEvent(hwnd, WindowStateEvent::kHide);
1226 
1227  while (!dart_responded) {
1228  engine->task_runner()->ProcessTasks();
1229  }
1230 }
1231 
1232 TEST_F(FlutterWindowsEngineTest, ChannelListenedTo) {
1233  FlutterWindowsEngineBuilder builder{GetContext()};
1234  builder.SetDartEntrypoint("enableLifecycleToFrom");
1235 
1236  auto engine = builder.Build();
1237  auto window_binding_handler =
1238  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1239  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1240 
1241  EngineModifier modifier(engine.get());
1242  modifier.SetImplicitView(&view);
1243  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1244 
1245  bool lifecycle_began = false;
1246  auto handler = std::make_unique<MockWindowsLifecycleManager>(engine.get());
1247  EXPECT_CALL(*handler, SetLifecycleState).Times(1);
1248  handler->begin_processing_callback = [&]() { lifecycle_began = true; };
1249  modifier.SetLifecycleManager(std::move(handler));
1250 
1251  engine->Run();
1252 
1253  while (!lifecycle_began) {
1254  engine->task_runner()->ProcessTasks();
1255  }
1256 }
1257 
1258 TEST_F(FlutterWindowsEngineTest, ReceivePlatformViewMessage) {
1259  FlutterWindowsEngineBuilder builder{GetContext()};
1260  builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1261  auto engine = builder.Build();
1262 
1263  EngineModifier modifier{engine.get()};
1264  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1265 
1266  bool received_call = false;
1267 
1268  auto manager = std::make_unique<MockPlatformViewManager>(engine.get());
1269  EXPECT_CALL(*manager, AddPlatformView)
1270  .WillOnce([&](PlatformViewId id, std::string_view type_name) {
1271  received_call = true;
1272  return true;
1273  });
1274  modifier.SetPlatformViewPlugin(std::move(manager));
1275 
1276  engine->Run();
1277 
1278  while (!received_call) {
1279  engine->task_runner()->ProcessTasks();
1280  }
1281 }
1282 
1283 TEST_F(FlutterWindowsEngineTest, AddViewFailureDoesNotHang) {
1284  FlutterWindowsEngineBuilder builder{GetContext()};
1285  auto engine = builder.Build();
1286 
1287  EngineModifier modifier{engine.get()};
1288 
1289  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1290  modifier.embedder_api().AddView = MOCK_ENGINE_PROC(
1291  AddView,
1292  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
1293  const FlutterAddViewInfo* info) { return kInternalInconsistency; });
1294 
1295  ASSERT_TRUE(engine->Run());
1296 
1297  // Create the first view. This is the implicit view and isn't added to the
1298  // engine.
1299  auto implicit_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1300 
1301  std::unique_ptr<FlutterWindowsView> implicit_view =
1302  engine->CreateView(std::move(implicit_window));
1303 
1304  EXPECT_TRUE(implicit_view);
1305 
1306  // Create a second view. The embedder attempts to add it to the engine.
1307  auto second_window = std::make_unique<NiceMock<MockWindowBindingHandler>>();
1308 
1309  EXPECT_DEBUG_DEATH(engine->CreateView(std::move(second_window)),
1310  "FlutterEngineAddView returned an unexpected result");
1311 }
1312 
1313 TEST_F(FlutterWindowsEngineTest, RemoveViewFailureDoesNotHang) {
1314  FlutterWindowsEngineBuilder builder{GetContext()};
1315  builder.SetDartEntrypoint("sendCreatePlatformViewMethod");
1316  auto engine = builder.Build();
1317 
1318  EngineModifier modifier{engine.get()};
1319 
1320  modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; };
1321  modifier.embedder_api().RemoveView = MOCK_ENGINE_PROC(
1322  RemoveView,
1323  [](FLUTTER_API_SYMBOL(FlutterEngine) engine,
1324  const FlutterRemoveViewInfo* info) { return kInternalInconsistency; });
1325 
1326  ASSERT_TRUE(engine->Run());
1327  EXPECT_DEBUG_DEATH(engine->RemoveView(123),
1328  "FlutterEngineRemoveView returned an unexpected result");
1329 }
1330 
1332  auto& context = GetContext();
1333  WindowsConfigBuilder builder{context};
1334  builder.SetDartEntrypoint("mergedUIThread");
1335  builder.SetUIThreadPolicy(FlutterDesktopUIThreadPolicy::RunOnPlatformThread);
1336 
1337  std::optional<std::thread::id> ui_thread_id;
1338 
1339  auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
1340  ui_thread_id = std::this_thread::get_id();
1341  });
1342  context.AddNativeFunction("Signal", native_entry);
1343 
1344  EnginePtr engine{builder.RunHeadless()};
1345  while (!ui_thread_id) {
1346  PumpMessage();
1347  }
1348  ASSERT_EQ(*ui_thread_id, std::this_thread::get_id());
1349 }
1350 
1351 TEST_F(FlutterWindowsEngineTest, OnViewFocusChangeRequest) {
1352  FlutterWindowsEngineBuilder builder{GetContext()};
1353  std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
1354  auto window_binding_handler =
1355  std::make_unique<::testing::NiceMock<MockWindowBindingHandler>>();
1356  MockFlutterWindowsView view(engine.get(), std::move(window_binding_handler));
1357 
1358  EngineModifier modifier(engine.get());
1359  modifier.SetImplicitView(&view);
1360 
1361  FlutterViewFocusChangeRequest request;
1362  request.view_id = kImplicitViewId;
1363 
1364  EXPECT_CALL(view, Focus()).WillOnce(Return(true));
1365  modifier.OnViewFocusChangeRequest(&request);
1366 }
1367 
1368 } // namespace testing
1369 } // namespace flutter
flutter::AlertPlatformNodeDelegate
Definition: alert_platform_node_delegate.h:18
flutter::kImplicitViewId
constexpr FlutterViewId kImplicitViewId
Definition: flutter_windows_engine.h:55
flutter::AppLifecycleState::kHidden
@ kHidden
flutter::WindowStateEvent::kHide
@ kHide
flutter::FlutterWindowsView
Definition: flutter_windows_view.h:34
flutter::FlutterEngine
Definition: flutter_engine.h:28
FlutterDesktopBinaryReply
void(* FlutterDesktopBinaryReply)(const uint8_t *data, size_t data_size, void *user_data)
Definition: flutter_messenger.h:26
flutter::testing::MockFlutterWindowsView::~MockFlutterWindowsView
~MockFlutterWindowsView()
Definition: flutter_windows_engine_unittests.cc:658
user_data
void * user_data
Definition: flutter_windows_view_unittests.cc:53
flutter::FlutterWindowsEngine
Definition: flutter_windows_engine.h:90
flutter::testing::MockFlutterWindowsView::MockFlutterWindowsView
MockFlutterWindowsView(FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > wbh)
Definition: flutter_windows_engine_unittests.cc:655
PlatformViewId
int64_t PlatformViewId
Definition: flutter_windows_internal.h:42
flutter::WindowsLifecycleManager::BeginProcessingLifecycle
virtual void BeginProcessingLifecycle()
Definition: windows_lifecycle_manager.cc:187
flutter::testing::MockWindowsLifecycleManager::begin_processing_callback
std::function< void()> begin_processing_callback
Definition: flutter_windows_engine_unittests.cc:786
flutter::testing::MockWindowsLifecycleManager
Definition: flutter_windows_engine_unittests.cc:764
flutter::FlutterEngine::Run
bool Run()
Definition: flutter_engine.cc:49
flutter::WindowsLifecycleManager::DispatchMessage
virtual void DispatchMessage(HWND window, UINT msg, WPARAM wparam, LPARAM lparam)
Definition: windows_lifecycle_manager.cc:34
flutter::testing::MockWindowsLifecycleManager::MockWindowsLifecycleManager
MockWindowsLifecycleManager(FlutterWindowsEngine *engine)
Definition: flutter_windows_engine_unittests.cc:766
flutter_windows_view.h
flutter::WindowsLifecycleManager::SetLifecycleState
virtual void SetLifecycleState(AppLifecycleState state)
Definition: windows_lifecycle_manager.cc:195
flutter::WindowStateEvent::kFocus
@ kFocus
flutter::AppLifecycleState::kInactive
@ kInactive
flutter::WindowStateEvent::kShow
@ kShow
flutter::testing::FlutterWindowsEngineTest
Definition: flutter_windows_engine_unittests.cc:49
flutter::BinaryReply
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
Definition: binary_messenger.h:17
flutter::testing::MockWindowsLifecycleManager::~MockWindowsLifecycleManager
virtual ~MockWindowsLifecycleManager()
Definition: flutter_windows_engine_unittests.cc:768
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::WindowsLifecycleManager::IsLastWindowOfProcess
virtual bool IsLastWindowOfProcess()
Definition: windows_lifecycle_manager.cc:164
flutter_windows_engine.h
flutter::WindowsLifecycleManager::Quit
virtual void Quit(std::optional< HWND > window, std::optional< WPARAM > wparam, std::optional< LPARAM > lparam, UINT exit_code)
Definition: windows_lifecycle_manager.cc:21
FlutterDesktopViewControllerGetEngine
FlutterDesktopEngineRef FlutterDesktopViewControllerGetEngine(FlutterDesktopViewControllerRef ref)
Definition: flutter_windows.cc:147
flutter::AppLifecycleState::kResumed
@ kResumed
RunOnPlatformThread
@ RunOnPlatformThread
Definition: flutter_windows.h:54
flutter_windows.h
message
Win32Message message
Definition: keyboard_unittests.cc:137
action
int action
Definition: keyboard_key_handler_unittests.cc:116
flutter::AppLifecycleState
AppLifecycleState
Definition: app_lifecycle_state.h:32
flutter::FlutterWindowsEngine::UpdateSemanticsEnabled
void UpdateSemanticsEnabled(bool enabled)
Definition: flutter_windows_engine.cc:912
flutter::testing::MockWindowsLifecycleManager::MOCK_METHOD
MOCK_METHOD(void, Quit,(std::optional< HWND >, std::optional< WPARAM >, std::optional< LPARAM >, UINT),(override))
flutter::testing::TEST_F
TEST_F(CompositorOpenGLTest, CreateBackingStore)
Definition: compositor_opengl_unittests.cc:125
flutter::WindowsLifecycleManager
Definition: windows_lifecycle_manager.h:37
flutter::testing::MockWindowsLifecycleManager::BeginProcessingLifecycle
void BeginProcessingLifecycle() override
Definition: flutter_windows_engine_unittests.cc:779
FlutterDesktopPluginRegistrar
Definition: window_state.h:23
flutter::WindowsPlatformThreadPrioritySetter
static void WindowsPlatformThreadPrioritySetter(FlutterThreadPriority priority)
Definition: flutter_windows_engine.h:60
callback
FlutterDesktopBinaryReply callback
Definition: flutter_windows_view_unittests.cc:52