8 #include <wrl/client.h>
11 #include "flutter/fml/synchronization/count_down_latch.h"
12 #include "flutter/fml/synchronization/waitable_event.h"
14 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.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"
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"
32 class HalfBrokenEGLManager :
public egl::Manager {
36 std::unique_ptr<egl::WindowSurface>
37 CreateWindowSurface(HWND hwnd,
size_t width,
size_t height)
override {
42 class MockWindowsLifecycleManager :
public WindowsLifecycleManager {
54 if (::GetMessage(&msg,
nullptr, 0, 0)) {
55 ::TranslateMessage(&msg);
56 ::DispatchMessage(&msg);
64 TEST(WindowsNoFixtureTest, GetTextureRegistrar) {
69 ASSERT_NE(engine,
nullptr);
71 EXPECT_NE(texture_registrar,
nullptr);
77 auto& context = GetContext();
78 WindowsConfigBuilder builder(context);
79 ViewControllerPtr controller{builder.Run()};
80 ASSERT_NE(controller,
nullptr);
84 TEST_F(WindowsTest, LaunchMainHasNoOutput) {
86 StreamCapture stdout_capture(&std::cout);
87 StreamCapture stderr_capture(&std::cerr);
89 auto& context = GetContext();
90 WindowsConfigBuilder builder(context);
91 ViewControllerPtr controller{builder.Run()};
92 ASSERT_NE(controller,
nullptr);
94 stdout_capture.Stop();
95 stderr_capture.Stop();
98 EXPECT_TRUE(stdout_capture.GetOutput().empty());
99 EXPECT_TRUE(stderr_capture.GetOutput().empty());
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);
116 TEST_F(WindowsTest, LaunchCustomEntrypointInEngineRunInvocation) {
117 auto& context = GetContext();
118 WindowsConfigBuilder builder(context);
119 EnginePtr engine{builder.InitializeEngine()};
120 ASSERT_NE(engine,
nullptr);
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);
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);
143 ViewControllerPtr controller{builder.Run()};
144 ASSERT_NE(controller,
nullptr);
148 EXPECT_EQ(view_ids,
"View IDs: [0]");
152 TEST_F(WindowsTest, EngineCanTransitionToHeadless) {
153 auto& context = GetContext();
154 WindowsConfigBuilder builder(context);
155 EnginePtr engine{builder.RunHeadless()};
156 ASSERT_NE(engine,
nullptr);
162 ViewControllerPtr controller{
165 ASSERT_NE(controller,
nullptr);
169 ASSERT_NE(engine,
nullptr);
172 ASSERT_TRUE(engine_ptr->running());
176 TEST_F(WindowsTest, LaunchRefreshesAccessibility) {
177 auto& context = GetContext();
178 WindowsConfigBuilder builder(context);
179 EnginePtr engine{builder.InitializeEngine()};
180 EngineModifier modifier{
185 UpdateAccessibilityFeatures, ([&called](
auto engine,
auto flags) {
190 ViewControllerPtr controller{
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);
212 TEST_F(WindowsTest, VerifyNativeFunction) {
213 auto& context = GetContext();
214 WindowsConfigBuilder builder(context);
215 builder.SetDartEntrypoint(
"verifyNativeFunction");
217 fml::AutoResetWaitableEvent latch;
219 CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); });
220 context.AddNativeFunction(
"Signal", native_entry);
222 ViewControllerPtr controller{builder.Run()};
223 ASSERT_NE(controller,
nullptr);
231 TEST_F(WindowsTest, VerifyNativeFunctionWithParameters) {
232 auto& context = GetContext();
233 WindowsConfigBuilder builder(context);
234 builder.SetDartEntrypoint(
"verifyNativeFunctionWithParameters");
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));
243 context.AddNativeFunction(
"SignalBoolValue", native_entry);
245 ViewControllerPtr controller{builder.Run()};
246 ASSERT_NE(controller,
nullptr);
250 EXPECT_TRUE(bool_value);
254 TEST_F(WindowsTest, PlatformExecutable) {
255 auto& context = GetContext();
256 WindowsConfigBuilder builder(context);
257 builder.SetDartEntrypoint(
"readPlatformExecutable");
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);
267 context.AddNativeFunction(
"SignalStringValue", native_entry);
269 ViewControllerPtr controller{builder.Run()};
270 ASSERT_NE(controller,
nullptr);
274 EXPECT_EQ(executable_name,
"flutter_windows_unittests.exe");
279 TEST_F(WindowsTest, VerifyNativeFunctionWithReturn) {
280 auto& context = GetContext();
281 WindowsConfigBuilder builder(context);
282 builder.SetDartEntrypoint(
"verifyNativeFunctionWithReturn");
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);
290 context.AddNativeFunction(
"SignalBoolReturn", bool_return_entry);
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));
298 context.AddNativeFunction(
"SignalBoolValue", bool_pass_entry);
300 ViewControllerPtr controller{builder.Run()};
301 ASSERT_NE(controller,
nullptr);
305 EXPECT_TRUE(bool_value_passed);
311 fml::AutoResetWaitableEvent frame_scheduled_latch;
312 fml::AutoResetWaitableEvent frame_drawn_latch;
313 std::thread::id thread_id;
318 CreateNewThread(
"test_platform_thread")->PostTask([&]() {
319 captures.thread_id = std::this_thread::get_id();
321 auto& context = GetContext();
322 WindowsConfigBuilder builder(context);
323 builder.SetDartEntrypoint(
"drawHelloWorld");
325 auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
326 ASSERT_FALSE(captures.frame_drawn_latch.IsSignaledForTest());
327 captures.frame_scheduled_latch.Signal();
329 context.AddNativeFunction(
"NotifyFirstFrameScheduled", native_entry);
331 ViewControllerPtr controller{builder.Run()};
332 ASSERT_NE(controller,
nullptr);
339 auto captures =
static_cast<Captures*
>(
user_data);
341 ASSERT_TRUE(captures->frame_scheduled_latch.IsSignaledForTest());
344 ASSERT_EQ(std::this_thread::get_id(), captures->thread_id);
347 captures->done =
true;
348 captures->frame_drawn_latch.Signal();
353 while (!captures.done) {
358 captures.frame_drawn_latch.Wait();
364 auto& context = GetContext();
365 WindowsConfigBuilder builder(context);
366 builder.SetDartEntrypoint(
"renderImplicitView");
368 EnginePtr engine{builder.RunHeadless()};
369 ASSERT_NE(engine,
nullptr);
376 auto done = reinterpret_cast<std::atomic<bool>*>(user_data);
385 FlutterWindowMetricsEvent metrics = {};
386 metrics.struct_size =
sizeof(FlutterWindowMetricsEvent);
388 metrics.height = 100;
389 metrics.pixel_ratio = 1.0;
391 engine_ptr->SendWindowMetricsEvent(metrics);
401 auto& context = GetContext();
402 WindowsConfigBuilder builder(context);
403 ViewControllerPtr controller{builder.Run()};
404 ASSERT_NE(controller,
nullptr);
411 TEST_F(WindowsTest, GetGraphicsAdapter) {
412 auto& context = GetContext();
413 WindowsConfigBuilder builder(context);
414 ViewControllerPtr controller{builder.Run()};
415 ASSERT_NE(controller,
nullptr);
418 Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
420 ASSERT_NE(dxgi_adapter,
nullptr);
421 DXGI_ADAPTER_DESC desc{};
422 ASSERT_TRUE(SUCCEEDED(dxgi_adapter->GetDesc(&desc)));
425 TEST_F(WindowsTest, GetGraphicsAdapterWithLowPowerPreference) {
428 GTEST_SKIP() <<
"Not able to find low power GPU, nothing to check.";
431 auto& context = GetContext();
432 WindowsConfigBuilder builder(context);
434 ViewControllerPtr controller{builder.Run()};
435 ASSERT_NE(controller,
nullptr);
438 Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
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);
448 TEST_F(WindowsTest, PluginRegistrarGetImplicitView) {
449 auto& context = GetContext();
450 WindowsConfigBuilder builder(context);
451 ViewControllerPtr controller{builder.Run()};
452 ASSERT_NE(controller,
nullptr);
461 ASSERT_NE(implicit_view,
nullptr);
464 TEST_F(WindowsTest, PluginRegistrarGetView) {
465 auto& context = GetContext();
466 WindowsConfigBuilder builder(context);
467 ViewControllerPtr controller{builder.Run()};
468 ASSERT_NE(controller,
nullptr);
483 ASSERT_NE(view,
nullptr);
484 ASSERT_EQ(view_123,
nullptr);
487 TEST_F(WindowsTest, PluginRegistrarGetViewHeadless) {
488 auto& context = GetContext();
489 WindowsConfigBuilder builder(context);
490 EnginePtr engine{builder.RunHeadless()};
491 ASSERT_NE(engine,
nullptr);
501 ASSERT_EQ(implicit_view,
nullptr);
502 ASSERT_EQ(view_123,
nullptr);
508 auto& context = GetContext();
509 WindowsConfigBuilder builder(context);
510 EnginePtr engine{builder.InitializeEngine()};
511 EngineModifier modifier{
514 auto egl_manager = std::make_unique<HalfBrokenEGLManager>();
515 ASSERT_TRUE(egl_manager->IsValid());
516 modifier.SetEGLManager(std::move(egl_manager));
518 ViewControllerPtr controller{
521 ASSERT_NE(controller,
nullptr);
526 auto& context = GetContext();
527 WindowsConfigBuilder builder(context);
528 EnginePtr engine{builder.InitializeEngine()};
530 EngineModifier modifier{windows_engine};
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));
537 EXPECT_CALL(*lifecycle_manager_ptr,
540 lifecycle_manager_ptr->WindowsLifecycleManager::SetLifecycleState(
544 EXPECT_CALL(*lifecycle_manager_ptr,
547 lifecycle_manager_ptr->WindowsLifecycleManager::SetLifecycleState(
553 ViewControllerPtr controller{
558 ASSERT_NE(view,
nullptr);
561 ASSERT_NE(hwnd,
nullptr);
566 ::MoveWindow(hwnd, 0, 0, 100, 100,
570 TEST_F(WindowsTest, GetKeyboardStateHeadless) {
571 auto& context = GetContext();
572 WindowsConfigBuilder builder(context);
573 builder.SetDartEntrypoint(
"sendGetKeyboardState");
575 std::atomic<bool> done =
false;
576 context.AddNativeFunction(
577 "SignalStringValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
578 auto handle = Dart_GetNativeArgument(args, 0);
579 ASSERT_FALSE(Dart_IsError(handle));
580 auto value = tonic::DartConverter<std::string>::FromDart(handle);
581 EXPECT_EQ(value,
"Success");
585 ViewControllerPtr controller{builder.Run()};
586 ASSERT_NE(controller,
nullptr);
598 std::string view_ids;
600 auto& context = GetContext();
601 WindowsConfigBuilder builder(context);
602 builder.SetDartEntrypoint(
"onMetricsChangedSignalViewIds");
604 fml::AutoResetWaitableEvent ready_latch;
605 context.AddNativeFunction(
606 "Signal", CREATE_NATIVE_ENTRY(
607 [&](Dart_NativeArguments args) { ready_latch.Signal(); }));
609 context.AddNativeFunction(
610 "SignalStringValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
611 auto handle = Dart_GetNativeArgument(args, 0);
612 ASSERT_FALSE(Dart_IsError(handle));
614 std::scoped_lock lock{mutex};
615 view_ids = tonic::DartConverter<std::string>::FromDart(handle);
619 ViewControllerPtr first_controller{builder.Run()};
620 ASSERT_NE(first_controller,
nullptr);
628 properties.
width = 100;
630 ViewControllerPtr second_controller{
632 ASSERT_NE(second_controller,
nullptr);
637 std::scoped_lock lock{mutex};
638 if (view_ids ==
"View IDs: [0, 1]") {
645 second_controller.reset();
648 std::scoped_lock lock{mutex};
649 if (view_ids ==
"View IDs: [0]") {
656 auto& context = GetContext();
657 WindowsConfigBuilder builder(context);
658 builder.SetDartEntrypoint(
"testEngineId");
660 fml::AutoResetWaitableEvent latch;
661 std::optional<int64_t> engineId;
662 context.AddNativeFunction(
663 "NotifyEngineId", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
664 const auto argument = Dart_GetNativeArgument(args, 0);
665 if (!Dart_IsNull(argument)) {
666 const auto handle = tonic::DartConverter<int64_t>::FromDart(argument);
672 ViewControllerPtr first_controller{builder.Run()};
673 ASSERT_NE(first_controller,
nullptr);
676 EXPECT_TRUE(engineId.has_value());
677 if (!engineId.has_value()) {