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