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