| Flutter Windows Embedder |

flutter_windows_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 "flutter/shell/platform/windows/public/flutter_windows.h"
6
7 #include <dxgi.h>
8 #include <wrl/client.h>
9 #include <thread>
10
11 #include "flutter/fml/synchronization/count_down_latch.h"
12 #include "flutter/fml/synchronization/waitable_event.h"
13 #include "flutter/shell/platform/common/app_lifecycle_state.h"
14 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
15 #include "flutter/shell/platform/windows/egl/manager.h"
16 #include "flutter/shell/platform/windows/testing/engine_modifier.h"
17 #include "flutter/shell/platform/windows/testing/windows_test.h"
18 #include "flutter/shell/platform/windows/testing/windows_test_config_builder.h"
19 #include "flutter/shell/platform/windows/testing/windows_test_context.h"
20 #include "flutter/shell/platform/windows/windows_lifecycle_manager.h"
21 #include "flutter/testing/stream_capture.h"
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
24 #include "third_party/tonic/converter/dart_converter.h"
25
26 namespace flutter {
27 namespace testing {
28
29 namespace {
30
31 // An EGL manager that initializes EGL but fails to create surfaces.
32 class HalfBrokenEGLManager : public egl::Manager {
33 public:
34 HalfBrokenEGLManager() : egl::Manager(egl::GpuPreference::NoPreference) {}
35
36 std::unique_ptr<egl::WindowSurface>
37 CreateWindowSurface(HWND hwnd, size_t width, size_t height) override {
38 return nullptr;
39 }
40 };
41
42 class MockWindowsLifecycleManager : public WindowsLifecycleManager {
43 public:
44 MockWindowsLifecycleManager(FlutterWindowsEngine* engine)
45 : WindowsLifecycleManager(engine) {}
46
47 MOCK_METHOD(void, SetLifecycleState, (AppLifecycleState), (override));
48 };
49
50 // Process the next win32 message if there is one. This can be used to
51 // pump the Windows platform thread task runner.
52 void PumpMessage() {
53 ::MSG msg;
54 if (::GetMessage(&msg, nullptr, 0, 0)) {
55 ::TranslateMessage(&msg);
56 ::DispatchMessage(&msg);
57 }
58 }
59
60 } // namespace
61
62 // Verify that we can fetch a texture registrar.
63 // Prevent regression: https://github.com/flutter/flutter/issues/86617
64 TEST(WindowsNoFixtureTest, GetTextureRegistrar) {
65 FlutterDesktopEngineProperties properties = {};
66 properties.assets_path = L"";
67 properties.icu_data_path = L"icudtl.dat";
68 auto engine = FlutterDesktopEngineCreate(&properties);
69 ASSERT_NE(engine, nullptr);
70 auto texture_registrar = FlutterDesktopEngineGetTextureRegistrar(engine);
71 EXPECT_NE(texture_registrar, nullptr);
72 FlutterDesktopEngineDestroy(engine);
73 }
74
75 // Verify we can successfully launch main().
76 TEST_F(WindowsTest, LaunchMain) {
77 auto& context = GetContext();
78 WindowsConfigBuilder builder(context);
79 ViewControllerPtr controller{builder.Run()};
80 ASSERT_NE(controller, nullptr);
81 }
82
83 // Verify there is no unexpected output from launching main.
84 TEST_F(WindowsTest, LaunchMainHasNoOutput) {
85 // Replace stdout & stderr stream buffers with our own.
86 StreamCapture stdout_capture(&std::cout);
87 StreamCapture stderr_capture(&std::cerr);
88
89 auto& context = GetContext();
90 WindowsConfigBuilder builder(context);
91 ViewControllerPtr controller{builder.Run()};
92 ASSERT_NE(controller, nullptr);
93
94 stdout_capture.Stop();
95 stderr_capture.Stop();
96
97 // Verify stdout & stderr have no output.
98 EXPECT_TRUE(stdout_capture.GetOutput().empty());
99 EXPECT_TRUE(stderr_capture.GetOutput().empty());
100 }
101
102 // Verify we can successfully launch a custom entry point.
103 TEST_F(WindowsTest, LaunchCustomEntrypoint) {
104 auto& context = GetContext();
105 WindowsConfigBuilder builder(context);
106 builder.SetDartEntrypoint("customEntrypoint");
107 ViewControllerPtr controller{builder.Run()};
108 ASSERT_NE(controller, nullptr);
109 }
110
111 // Verify that engine launches with the custom entrypoint specified in the
112 // FlutterDesktopEngineRun parameter when no entrypoint is specified in
113 // FlutterDesktopEngineProperties.dart_entrypoint.
114 //
115 // TODO(cbracken): https://github.com/flutter/flutter/issues/109285
116 TEST_F(WindowsTest, LaunchCustomEntrypointInEngineRunInvocation) {
117 auto& context = GetContext();
118 WindowsConfigBuilder builder(context);
119 EnginePtr engine{builder.InitializeEngine()};
120 ASSERT_NE(engine, nullptr);
121
122 ASSERT_TRUE(FlutterDesktopEngineRun(engine.get(), "customEntrypoint"));
123 }
124
125 // Verify that the engine can launch in headless mode.
126 TEST_F(WindowsTest, LaunchHeadlessEngine) {
127 auto& context = GetContext();
128 WindowsConfigBuilder builder(context);
129 builder.SetDartEntrypoint("signalViewIds");
130 EnginePtr engine{builder.RunHeadless()};
131 ASSERT_NE(engine, nullptr);
132
133 std::string view_ids;
134 fml::AutoResetWaitableEvent latch;
135 context.AddNativeFunction(
136 "SignalStringValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
137 auto handle = Dart_GetNativeArgument(args, 0);
138 ASSERT_FALSE(Dart_IsError(handle));
139 view_ids = tonic::DartConverter<std::string>::FromDart(handle);
140 latch.Signal();
141 }));
142
143 ViewControllerPtr controller{builder.Run()};
144 ASSERT_NE(controller, nullptr);
145
146 // Verify a headless app has the implicit view.
147 latch.Wait();
148 EXPECT_EQ(view_ids, "View IDs: [0]");
149 }
150
151 // Verify that the engine can return to headless mode.
152 TEST_F(WindowsTest, EngineCanTransitionToHeadless) {
153 auto& context = GetContext();
154 WindowsConfigBuilder builder(context);
155 EnginePtr engine{builder.RunHeadless()};
156 ASSERT_NE(engine, nullptr);
157
158 // Create and then destroy a view controller that does not own its engine.
159 // This causes the engine to transition back to headless mode.
160 {
161 FlutterDesktopViewControllerProperties properties = {};
162 ViewControllerPtr controller{
163 FlutterDesktopEngineCreateViewController(engine.get(), &properties)};
164
165 ASSERT_NE(controller, nullptr);
166 }
167
168 // The engine is back in headless mode now.
169 ASSERT_NE(engine, nullptr);
170
171 auto engine_ptr = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
172 ASSERT_TRUE(engine_ptr->running());
173 }
174
175 // Verify that accessibility features are initialized when a view is created.
176 TEST_F(WindowsTest, LaunchRefreshesAccessibility) {
177 auto& context = GetContext();
178 WindowsConfigBuilder builder(context);
179 EnginePtr engine{builder.InitializeEngine()};
180 EngineModifier modifier{
181 reinterpret_cast<FlutterWindowsEngine*>(engine.get())};
182
183 auto called = false;
184 modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
185 UpdateAccessibilityFeatures, ([&called](auto engine, auto flags) {
186 called = true;
187 return kSuccess;
188 }));
189
190 ViewControllerPtr controller{
191 FlutterDesktopViewControllerCreate(0, 0, engine.release())};
192
193 ASSERT_TRUE(called);
194 }
195
196 // Verify that engine fails to launch when a conflicting entrypoint in
197 // FlutterDesktopEngineProperties.dart_entrypoint and the
198 // FlutterDesktopEngineRun parameter.
199 //
200 // TODO(cbracken): https://github.com/flutter/flutter/issues/109285
201 TEST_F(WindowsTest, LaunchConflictingCustomEntrypoints) {
202 auto& context = GetContext();
203 WindowsConfigBuilder builder(context);
204 builder.SetDartEntrypoint("customEntrypoint");
205 EnginePtr engine{builder.InitializeEngine()};
206 ASSERT_NE(engine, nullptr);
207
208 ASSERT_FALSE(FlutterDesktopEngineRun(engine.get(), "conflictingEntrypoint"));
209 }
210
211 // Verify that native functions can be registered and resolved.
212 TEST_F(WindowsTest, VerifyNativeFunction) {
213 auto& context = GetContext();
214 WindowsConfigBuilder builder(context);
215 builder.SetDartEntrypoint("verifyNativeFunction");
216
217 fml::AutoResetWaitableEvent latch;
218 auto native_entry =
219 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); });
220 context.AddNativeFunction("Signal", native_entry);
221
222 ViewControllerPtr controller{builder.Run()};
223 ASSERT_NE(controller, nullptr);
224
225 // Wait until signal has been called.
226 latch.Wait();
227 }
228
229 // Verify that native functions that pass parameters can be registered and
230 // resolved.
231 TEST_F(WindowsTest, VerifyNativeFunctionWithParameters) {
232 auto& context = GetContext();
233 WindowsConfigBuilder builder(context);
234 builder.SetDartEntrypoint("verifyNativeFunctionWithParameters");
235
236 bool bool_value = false;
237 fml::AutoResetWaitableEvent latch;
238 auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
239 auto handle = Dart_GetNativeBooleanArgument(args, 0, &bool_value);
240 ASSERT_FALSE(Dart_IsError(handle));
241 latch.Signal();
242 });
243 context.AddNativeFunction("SignalBoolValue", native_entry);
244
245 ViewControllerPtr controller{builder.Run()};
246 ASSERT_NE(controller, nullptr);
247
248 // Wait until signalBoolValue has been called.
249 latch.Wait();
250 EXPECT_TRUE(bool_value);
251 }
252
253 // Verify that Platform.executable returns the executable name.
254 TEST_F(WindowsTest, PlatformExecutable) {
255 auto& context = GetContext();
256 WindowsConfigBuilder builder(context);
257 builder.SetDartEntrypoint("readPlatformExecutable");
258
259 std::string executable_name;
260 fml::AutoResetWaitableEvent latch;
261 auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
262 auto handle = Dart_GetNativeArgument(args, 0);
263 ASSERT_FALSE(Dart_IsError(handle));
264 executable_name = tonic::DartConverter<std::string>::FromDart(handle);
265 latch.Signal();
266 });
267 context.AddNativeFunction("SignalStringValue", native_entry);
268
269 ViewControllerPtr controller{builder.Run()};
270 ASSERT_NE(controller, nullptr);
271
272 // Wait until signalStringValue has been called.
273 latch.Wait();
274 EXPECT_EQ(executable_name, "flutter_windows_unittests.exe");
275 }
276
277 // Verify that native functions that return values can be registered and
278 // resolved.
279 TEST_F(WindowsTest, VerifyNativeFunctionWithReturn) {
280 auto& context = GetContext();
281 WindowsConfigBuilder builder(context);
282 builder.SetDartEntrypoint("verifyNativeFunctionWithReturn");
283
284 bool bool_value_to_return = true;
285 fml::CountDownLatch latch(2);
286 auto bool_return_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
287 Dart_SetBooleanReturnValue(args, bool_value_to_return);
288 latch.CountDown();
289 });
290 context.AddNativeFunction("SignalBoolReturn", bool_return_entry);
291
292 bool bool_value_passed = false;
293 auto bool_pass_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
294 auto handle = Dart_GetNativeBooleanArgument(args, 0, &bool_value_passed);
295 ASSERT_FALSE(Dart_IsError(handle));
296 latch.CountDown();
297 });
298 context.AddNativeFunction("SignalBoolValue", bool_pass_entry);
299
300 ViewControllerPtr controller{builder.Run()};
301 ASSERT_NE(controller, nullptr);
302
303 // Wait until signalBoolReturn and signalBoolValue have been called.
304 latch.Wait();
305 EXPECT_TRUE(bool_value_passed);
306 }
307
308 // Verify the next frame callback is executed.
309 TEST_F(WindowsTest, NextFrameCallback) {
310 struct Captures {
311 fml::AutoResetWaitableEvent frame_scheduled_latch;
312 fml::AutoResetWaitableEvent frame_drawn_latch;
313 std::thread::id thread_id;
314 bool done = false;
315 };
316 Captures captures;
317
318 CreateNewThread("test_platform_thread")->PostTask([&]() {
319 captures.thread_id = std::this_thread::get_id();
320
321 auto& context = GetContext();
322 WindowsConfigBuilder builder(context);
323 builder.SetDartEntrypoint("drawHelloWorld");
324
325 auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
326 ASSERT_FALSE(captures.frame_drawn_latch.IsSignaledForTest());
327 captures.frame_scheduled_latch.Signal();
328 });
329 context.AddNativeFunction("NotifyFirstFrameScheduled", native_entry);
330
331 ViewControllerPtr controller{builder.Run()};
332 ASSERT_NE(controller, nullptr);
333
334 auto engine = FlutterDesktopViewControllerGetEngine(controller.get());
335
336 FlutterDesktopEngineSetNextFrameCallback(
337 engine,
338 [](void* user_data) {
339 auto captures = static_cast<Captures*>(user_data);
340
341 ASSERT_TRUE(captures->frame_scheduled_latch.IsSignaledForTest());
342
343 // Callback should execute on platform thread.
344 ASSERT_EQ(std::this_thread::get_id(), captures->thread_id);
345
346 // Signal the test passed and end the Windows message loop.
347 captures->done = true;
348 captures->frame_drawn_latch.Signal();
349 },
350 &captures);
351
352 // Pump messages for the Windows platform task runner.
353 while (!captures.done) {
354 PumpMessage();
355 }
356 });
357
358 captures.frame_drawn_latch.Wait();
359 }
360
361 // Verify the embedder ignores presents to the implicit view when there is no
362 // implicit view.
363 TEST_F(WindowsTest, PresentHeadless) {
364 auto& context = GetContext();
365 WindowsConfigBuilder builder(context);
366 builder.SetDartEntrypoint("renderImplicitView");
367
368 EnginePtr engine{builder.RunHeadless()};
369 ASSERT_NE(engine, nullptr);
370
371 bool done = false;
372 FlutterDesktopEngineSetNextFrameCallback(
373 engine.get(),
374 [](void* user_data) {
375 // This executes on the platform thread.
376 auto done = reinterpret_cast<std::atomic<bool>*>(user_data);
377 *done = true;
378 },
379 &done);
380
381 // This app is in headless mode, however, the engine assumes the implicit
382 // view always exists. Send window metrics for the implicit view, causing
383 // the engine to present to the implicit view. The embedder must not crash.
384 auto engine_ptr = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
385 FlutterWindowMetricsEvent metrics = {};
386 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
387 metrics.width = 100;
388 metrics.height = 100;
389 metrics.pixel_ratio = 1.0;
390 metrics.view_id = kImplicitViewId;
391 engine_ptr->SendWindowMetricsEvent(metrics);
392
393 // Pump messages for the Windows platform task runner.
394 while (!done) {
395 PumpMessage();
396 }
397 }
398
399 // Implicit view has the implicit view ID.
400 TEST_F(WindowsTest, GetViewId) {
401 auto& context = GetContext();
402 WindowsConfigBuilder builder(context);
403 ViewControllerPtr controller{builder.Run()};
404 ASSERT_NE(controller, nullptr);
405 FlutterDesktopViewId view_id =
406 FlutterDesktopViewControllerGetViewId(controller.get());
407
408 ASSERT_EQ(view_id, static_cast<FlutterDesktopViewId>(kImplicitViewId));
409 }
410
411 TEST_F(WindowsTest, GetGraphicsAdapter) {
412 auto& context = GetContext();
413 WindowsConfigBuilder builder(context);
414 ViewControllerPtr controller{builder.Run()};
415 ASSERT_NE(controller, nullptr);
416 auto view = FlutterDesktopViewControllerGetView(controller.get());
417
418 Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
419 dxgi_adapter = FlutterDesktopViewGetGraphicsAdapter(view);
420 ASSERT_NE(dxgi_adapter, nullptr);
421 DXGI_ADAPTER_DESC desc{};
422 ASSERT_TRUE(SUCCEEDED(dxgi_adapter->GetDesc(&desc)));
423 }
424
425 TEST_F(WindowsTest, GetGraphicsAdapterWithLowPowerPreference) {
426 std::optional<LUID> luid = egl::Manager::GetLowPowerGpuLuid();
427 if (!luid) {
428 GTEST_SKIP() << "Not able to find low power GPU, nothing to check.";
429 }
430
431 auto& context = GetContext();
432 WindowsConfigBuilder builder(context);
433 builder.SetGpuPreference(FlutterDesktopGpuPreference::LowPowerPreference);
434 ViewControllerPtr controller{builder.Run()};
435 ASSERT_NE(controller, nullptr);
436 auto view = FlutterDesktopViewControllerGetView(controller.get());
437
438 Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
439 dxgi_adapter = FlutterDesktopViewGetGraphicsAdapter(view);
440 ASSERT_NE(dxgi_adapter, nullptr);
441 DXGI_ADAPTER_DESC desc{};
442 ASSERT_TRUE(SUCCEEDED(dxgi_adapter->GetDesc(&desc)));
443 ASSERT_EQ(desc.AdapterLuid.HighPart, luid->HighPart);
444 ASSERT_EQ(desc.AdapterLuid.LowPart, luid->LowPart);
445 }
446
447 // Implicit view has the implicit view ID.
448 TEST_F(WindowsTest, PluginRegistrarGetImplicitView) {
449 auto& context = GetContext();
450 WindowsConfigBuilder builder(context);
451 ViewControllerPtr controller{builder.Run()};
452 ASSERT_NE(controller, nullptr);
453
454 FlutterDesktopEngineRef engine =
455 FlutterDesktopViewControllerGetEngine(controller.get());
456 FlutterDesktopPluginRegistrarRef registrar =
457 FlutterDesktopEngineGetPluginRegistrar(engine, "foo_bar");
458 FlutterDesktopViewRef implicit_view =
459 FlutterDesktopPluginRegistrarGetView(registrar);
460
461 ASSERT_NE(implicit_view, nullptr);
462 }
463
464 TEST_F(WindowsTest, PluginRegistrarGetView) {
465 auto& context = GetContext();
466 WindowsConfigBuilder builder(context);
467 ViewControllerPtr controller{builder.Run()};
468 ASSERT_NE(controller, nullptr);
469
470 FlutterDesktopEngineRef engine =
471 FlutterDesktopViewControllerGetEngine(controller.get());
472 FlutterDesktopPluginRegistrarRef registrar =
473 FlutterDesktopEngineGetPluginRegistrar(engine, "foo_bar");
474
475 FlutterDesktopViewId view_id =
476 FlutterDesktopViewControllerGetViewId(controller.get());
477 FlutterDesktopViewRef view =
478 FlutterDesktopPluginRegistrarGetViewById(registrar, view_id);
479
480 FlutterDesktopViewRef view_123 = FlutterDesktopPluginRegistrarGetViewById(
481 registrar, static_cast<FlutterDesktopViewId>(123));
482
483 ASSERT_NE(view, nullptr);
484 ASSERT_EQ(view_123, nullptr);
485 }
486
487 TEST_F(WindowsTest, PluginRegistrarGetViewHeadless) {
488 auto& context = GetContext();
489 WindowsConfigBuilder builder(context);
490 EnginePtr engine{builder.RunHeadless()};
491 ASSERT_NE(engine, nullptr);
492
493 FlutterDesktopPluginRegistrarRef registrar =
494 FlutterDesktopEngineGetPluginRegistrar(engine.get(), "foo_bar");
495
496 FlutterDesktopViewRef implicit_view =
497 FlutterDesktopPluginRegistrarGetView(registrar);
498 FlutterDesktopViewRef view_123 = FlutterDesktopPluginRegistrarGetViewById(
499 registrar, static_cast<FlutterDesktopViewId>(123));
500
501 ASSERT_EQ(implicit_view, nullptr);
502 ASSERT_EQ(view_123, nullptr);
503 }
504
505 // Verify the app does not crash if EGL initializes successfully but
506 // the rendering surface cannot be created.
507 TEST_F(WindowsTest, SurfaceOptional) {
508 auto& context = GetContext();
509 WindowsConfigBuilder builder(context);
510 EnginePtr engine{builder.InitializeEngine()};
511 EngineModifier modifier{
512 reinterpret_cast<FlutterWindowsEngine*>(engine.get())};
513
514 auto egl_manager = std::make_unique<HalfBrokenEGLManager>();
515 ASSERT_TRUE(egl_manager->IsValid());
516 modifier.SetEGLManager(std::move(egl_manager));
517
518 ViewControllerPtr controller{
519 FlutterDesktopViewControllerCreate(0, 0, engine.release())};
520
521 ASSERT_NE(controller, nullptr);
522 }
523
524 // Verify the app produces the expected lifecycle events.
525 TEST_F(WindowsTest, Lifecycle) {
526 auto& context = GetContext();
527 WindowsConfigBuilder builder(context);
528 EnginePtr engine{builder.InitializeEngine()};
529 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
530 EngineModifier modifier{windows_engine};
531
532 auto lifecycle_manager =
533 std::make_unique<MockWindowsLifecycleManager>(windows_engine);
534 auto lifecycle_manager_ptr = lifecycle_manager.get();
535 modifier.SetLifecycleManager(std::move(lifecycle_manager));
536
537 EXPECT_CALL(*lifecycle_manager_ptr,
538 SetLifecycleState(AppLifecycleState::kInactive))
539 .WillOnce([lifecycle_manager_ptr](AppLifecycleState state) {
540 lifecycle_manager_ptr->WindowsLifecycleManager::SetLifecycleState(
541 state);
542 });
543
544 EXPECT_CALL(*lifecycle_manager_ptr,
545 SetLifecycleState(AppLifecycleState::kHidden))
546 .WillOnce([lifecycle_manager_ptr](AppLifecycleState state) {
547 lifecycle_manager_ptr->WindowsLifecycleManager::SetLifecycleState(
548 state);
549 });
550
551 FlutterDesktopViewControllerProperties properties = {0, 0};
552
553 // Create a controller. This launches the engine and sets the app lifecycle
554 // to the "resumed" state.
555 ViewControllerPtr controller{
556 FlutterDesktopEngineCreateViewController(engine.get(), &properties)};
557
558 FlutterDesktopViewRef view =
559 FlutterDesktopViewControllerGetView(controller.get());
560 ASSERT_NE(view, nullptr);
561
562 HWND hwnd = FlutterDesktopViewGetHWND(view);
563 ASSERT_NE(hwnd, nullptr);
564
565 // Give the window a non-zero size to show it. This does not change the app
566 // lifecycle directly. However, destroying the view will now result in a
567 // "hidden" app lifecycle event.
568 ::MoveWindow(hwnd, /* X */ 0, /* Y */ 0, /* nWidth*/ 100, /* nHeight*/ 100,
569 /* bRepaint*/ false);
570
571 while (lifecycle_manager_ptr->IsUpdateStateScheduled()) {
572 PumpMessage();
573 }
574
575 // Resets the view, simulating the window being hidden.
576 controller.reset();
577
578 while (lifecycle_manager_ptr->IsUpdateStateScheduled()) {
579 PumpMessage();
580 }
581 }
582
583 TEST_F(WindowsTest, GetKeyboardStateHeadless) {
584 auto& context = GetContext();
585 WindowsConfigBuilder builder(context);
586 builder.SetDartEntrypoint("sendGetKeyboardState");
587
588 std::atomic<bool> done = false;
589 context.AddNativeFunction(
590 "SignalStringValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
591 auto handle = Dart_GetNativeArgument(args, 0);
592 ASSERT_FALSE(Dart_IsError(handle));
593 auto value = tonic::DartConverter<std::string>::FromDart(handle);
594 EXPECT_EQ(value, "Success");
595 done = true;
596 }));
597
598 ViewControllerPtr controller{builder.Run()};
599 ASSERT_NE(controller, nullptr);
600
601 // Pump messages for the Windows platform task runner.
602 ::MSG msg;
603 while (!done) {
604 PumpMessage();
605 }
606 }
607
608 // Verify the embedder can add and remove views.
609 TEST_F(WindowsTest, AddRemoveView) {
610 std::mutex mutex;
611 std::string view_ids;
612
613 auto& context = GetContext();
614 WindowsConfigBuilder builder(context);
615 builder.SetDartEntrypoint("onMetricsChangedSignalViewIds");
616
617 fml::AutoResetWaitableEvent ready_latch;
618 context.AddNativeFunction(
619 "Signal", CREATE_NATIVE_ENTRY(
620 [&](Dart_NativeArguments args) { ready_latch.Signal(); }));
621
622 context.AddNativeFunction(
623 "SignalStringValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
624 auto handle = Dart_GetNativeArgument(args, 0);
625 ASSERT_FALSE(Dart_IsError(handle));
626
627 std::scoped_lock lock{mutex};
628 view_ids = tonic::DartConverter<std::string>::FromDart(handle);
629 }));
630
631 // Create the implicit view.
632 ViewControllerPtr first_controller{builder.Run()};
633 ASSERT_NE(first_controller, nullptr);
634
635 ready_latch.Wait();
636
637 // Create a second view.
638 FlutterDesktopEngineRef engine =
639 FlutterDesktopViewControllerGetEngine(first_controller.get());
640 FlutterDesktopViewControllerProperties properties = {};
641 properties.width = 100;
642 properties.height = 100;
643 ViewControllerPtr second_controller{
644 FlutterDesktopEngineCreateViewController(engine, &properties)};
645 ASSERT_NE(second_controller, nullptr);
646
647 // Pump messages for the Windows platform task runner until the view is added.
648 while (true) {
649 PumpMessage();
650 std::scoped_lock lock{mutex};
651 if (view_ids == "View IDs: [0, 1]") {
652 break;
653 }
654 }
655
656 // Delete the second view and pump messages for the Windows platform task
657 // runner until the view is removed.
658 second_controller.reset();
659 while (true) {
660 PumpMessage();
661 std::scoped_lock lock{mutex};
662 if (view_ids == "View IDs: [0]") {
663 break;
664 }
665 }
666 }
667
668 TEST_F(WindowsTest, EngineId) {
669 auto& context = GetContext();
670 WindowsConfigBuilder builder(context);
671 builder.SetDartEntrypoint("testEngineId");
672
673 fml::AutoResetWaitableEvent latch;
674 std::optional<int64_t> engineId;
675 context.AddNativeFunction(
676 "NotifyEngineId", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
677 const auto argument = Dart_GetNativeArgument(args, 0);
678 if (!Dart_IsNull(argument)) {
679 const auto handle = tonic::DartConverter<int64_t>::FromDart(argument);
680 engineId = handle;
681 }
682 latch.Signal();
683 }));
684 // Create the implicit view.
685 ViewControllerPtr first_controller{builder.Run()};
686 ASSERT_NE(first_controller, nullptr);
687
688 latch.Wait();
689 EXPECT_TRUE(engineId.has_value());
690 if (!engineId.has_value()) {
691 return;
692 }
693 auto engine = FlutterDesktopViewControllerGetEngine(first_controller.get());
694 EXPECT_EQ(engine, FlutterDesktopEngineForId(*engineId));
695 }
696
697 } // namespace testing
698 } // namespace flutter
app_lifecycle_state.h
flutter::FlutterWindowsEngine
Definition: flutter_windows_engine.h:90
flutter::FlutterWindowsEngine::UpdateAccessibilityFeatures
void UpdateAccessibilityFeatures()
Definition: flutter_windows_engine.cc:980
flutter::WindowsLifecycleManager::WindowsLifecycleManager
WindowsLifecycleManager(FlutterWindowsEngine *engine)
Definition: windows_lifecycle_manager.cc:16
flutter::WindowsLifecycleManager::SetLifecycleState
virtual void SetLifecycleState(AppLifecycleState state)
Definition: windows_lifecycle_manager.cc:195
flutter::egl::Manager::GetLowPowerGpuLuid
static std::optional< LUID > GetLowPowerGpuLuid()
Definition: manager.cc:336
flutter::testing::MockWindowsLifecycleManager::MOCK_METHOD
MOCK_METHOD(void, Quit,(std::optional< HWND >, std::optional< WPARAM >, std::optional< LPARAM >, UINT),(override))
flutter::testing::MockWindowsLifecycleManager::MockWindowsLifecycleManager
MockWindowsLifecycleManager(FlutterWindowsEngine *engine)
Definition: flutter_windows_engine_unittests.cc:765
FlutterDesktopEngineCreate
FlutterDesktopEngineRef FlutterDesktopEngineCreate(const FlutterDesktopEngineProperties *engine_properties)
Definition: flutter_windows.cc:183
FlutterDesktopEngineForId
FLUTTER_EXPORT FlutterDesktopEngineRef FlutterDesktopEngineForId(int64_t engine_id)
Definition: flutter_windows.cc:200
FlutterDesktopPluginRegistrarGetViewById
FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetViewById(FlutterDesktopPluginRegistrarRef registrar, FlutterDesktopViewId view_id)
Definition: flutter_windows.cc:301
FlutterDesktopEngineGetPluginRegistrar
FlutterDesktopPluginRegistrarRef FlutterDesktopEngineGetPluginRegistrar(FlutterDesktopEngineRef engine, const char *plugin_name)
Definition: flutter_windows.cc:224
FlutterDesktopViewControllerCreate
FlutterDesktopViewControllerRef FlutterDesktopViewControllerCreate(int width, int height, FlutterDesktopEngineRef engine)
Definition: flutter_windows.cc:121
FlutterDesktopEngineDestroy
bool FlutterDesktopEngineDestroy(FlutterDesktopEngineRef engine_ref)
Definition: flutter_windows.cc:190
FlutterDesktopEngineGetTextureRegistrar
FlutterDesktopTextureRegistrarRef FlutterDesktopEngineGetTextureRegistrar(FlutterDesktopEngineRef engine)
Definition: flutter_windows.cc:239
FlutterDesktopViewGetGraphicsAdapter
IDXGIAdapter * FlutterDesktopViewGetGraphicsAdapter(FlutterDesktopViewRef view)
Definition: flutter_windows.cc:256
FlutterDesktopViewControllerGetViewId
FlutterDesktopViewId FlutterDesktopViewControllerGetViewId(FlutterDesktopViewControllerRef ref)
Definition: flutter_windows.cc:141
FlutterDesktopViewControllerGetEngine
FlutterDesktopEngineRef FlutterDesktopViewControllerGetEngine(FlutterDesktopViewControllerRef ref)
Definition: flutter_windows.cc:147
FlutterDesktopEngineSetNextFrameCallback
void FlutterDesktopEngineSetNextFrameCallback(FlutterDesktopEngineRef engine, VoidCallback callback, void *user_data)
Definition: flutter_windows.cc:245
FlutterDesktopViewGetHWND
HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef view)
Definition: flutter_windows.cc:252
FlutterDesktopViewControllerGetView
FlutterDesktopViewRef FlutterDesktopViewControllerGetView(FlutterDesktopViewControllerRef ref)
Definition: flutter_windows.cc:153
FlutterDesktopPluginRegistrarGetView
FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView(FlutterDesktopPluginRegistrarRef registrar)
Definition: flutter_windows.cc:296
FlutterDesktopEngineCreateViewController
FlutterDesktopViewControllerRef FlutterDesktopEngineCreateViewController(FlutterDesktopEngineRef engine, const FlutterDesktopViewControllerProperties *properties)
Definition: flutter_windows.cc:128
FlutterDesktopEngineRun
bool FlutterDesktopEngineRun(FlutterDesktopEngineRef engine, const char *entry_point)
Definition: flutter_windows.cc:206
flutter_windows.h
FlutterDesktopEngineRef
struct FlutterDesktopEngine * FlutterDesktopEngineRef
Definition: flutter_windows.h:33
FlutterDesktopViewId
int64_t FlutterDesktopViewId
Definition: flutter_windows.h:36
FlutterDesktopViewRef
struct FlutterDesktopView * FlutterDesktopViewRef
Definition: flutter_windows.h:29
LowPowerPreference
@ LowPowerPreference
Definition: flutter_windows.h:45
NoPreference
@ NoPreference
Definition: flutter_windows.h:41
user_data
void * user_data
Definition: flutter_windows_view_unittests.cc:53
manager.h
flutter::testing::TEST
TEST(AccessibilityBridgeWindows, GetParent)
Definition: accessibility_bridge_windows_unittests.cc:237
flutter::testing::TEST_F
TEST_F(CompositorOpenGLTest, CreateBackingStore)
Definition: compositor_opengl_unittests.cc:125
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::GpuPreference
GpuPreference
Definition: dart_project.h:14
flutter::AppLifecycleState
AppLifecycleState
Definition: app_lifecycle_state.h:32
flutter::AppLifecycleState::kHidden
@ kHidden
flutter::AppLifecycleState::kInactive
@ kInactive
flutter::kImplicitViewId
constexpr FlutterViewId kImplicitViewId
Definition: flutter_windows_engine.h:55
FlutterDesktopEngineProperties
Definition: flutter_windows.h:60
FlutterDesktopEngineProperties::icu_data_path
const wchar_t * icu_data_path
Definition: flutter_windows.h:69
FlutterDesktopEngineProperties::assets_path
const wchar_t * assets_path
Definition: flutter_windows.h:64
FlutterDesktopPluginRegistrar
Definition: window_state.h:23
FlutterDesktopViewControllerProperties
Definition: flutter_windows_internal.h:18
FlutterDesktopViewControllerProperties::width
int width
Definition: flutter_windows_internal.h:20
FlutterDesktopViewControllerProperties::height
int height
Definition: flutter_windows_internal.h:23
windows_lifecycle_manager.h