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 
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/embedder/test_utils/proc_table_replacement.h"
14 #include "flutter/shell/platform/windows/testing/engine_modifier.h"
15 #include "flutter/shell/platform/windows/testing/windows_test.h"
16 #include "flutter/shell/platform/windows/testing/windows_test_config_builder.h"
17 #include "flutter/shell/platform/windows/testing/windows_test_context.h"
18 #include "flutter/testing/stream_capture.h"
19 #include "gtest/gtest.h"
20 #include "third_party/tonic/converter/dart_converter.h"
21 
22 namespace flutter {
23 namespace testing {
24 
25 // Verify that we can fetch a texture registrar.
26 // Prevent regression: https://github.com/flutter/flutter/issues/86617
27 TEST(WindowsNoFixtureTest, GetTextureRegistrar) {
28  FlutterDesktopEngineProperties properties = {};
29  properties.assets_path = L"";
30  properties.icu_data_path = L"icudtl.dat";
31  auto engine = FlutterDesktopEngineCreate(&properties);
32  ASSERT_NE(engine, nullptr);
33  auto texture_registrar = FlutterDesktopEngineGetTextureRegistrar(engine);
34  EXPECT_NE(texture_registrar, nullptr);
36 }
37 
38 // Verify we can successfully launch main().
39 TEST_F(WindowsTest, LaunchMain) {
40  auto& context = GetContext();
41  WindowsConfigBuilder builder(context);
42  ViewControllerPtr controller{builder.Run()};
43  ASSERT_NE(controller, nullptr);
44 }
45 
46 // Verify there is no unexpected output from launching main.
47 TEST_F(WindowsTest, LaunchMainHasNoOutput) {
48  // Replace stdout & stderr stream buffers with our own.
49  StreamCapture stdout_capture(&std::cout);
50  StreamCapture stderr_capture(&std::cerr);
51 
52  auto& context = GetContext();
53  WindowsConfigBuilder builder(context);
54  ViewControllerPtr controller{builder.Run()};
55  ASSERT_NE(controller, nullptr);
56 
57  stdout_capture.Stop();
58  stderr_capture.Stop();
59 
60  // Verify stdout & stderr have no output.
61  EXPECT_TRUE(stdout_capture.GetOutput().empty());
62  EXPECT_TRUE(stderr_capture.GetOutput().empty());
63 }
64 
65 // Verify we can successfully launch a custom entry point.
66 TEST_F(WindowsTest, LaunchCustomEntrypoint) {
67  auto& context = GetContext();
68  WindowsConfigBuilder builder(context);
69  builder.SetDartEntrypoint("customEntrypoint");
70  ViewControllerPtr controller{builder.Run()};
71  ASSERT_NE(controller, nullptr);
72 }
73 
74 // Verify that engine launches with the custom entrypoint specified in the
75 // FlutterDesktopEngineRun parameter when no entrypoint is specified in
76 // FlutterDesktopEngineProperties.dart_entrypoint.
77 //
78 // TODO(cbracken): https://github.com/flutter/flutter/issues/109285
79 TEST_F(WindowsTest, LaunchCustomEntrypointInEngineRunInvocation) {
80  auto& context = GetContext();
81  WindowsConfigBuilder builder(context);
82  EnginePtr engine{builder.InitializeEngine()};
83  ASSERT_NE(engine, nullptr);
84 
85  ASSERT_TRUE(FlutterDesktopEngineRun(engine.get(), "customEntrypoint"));
86 }
87 
88 // Verify that the engine can launch in headless mode.
89 TEST_F(WindowsTest, LaunchHeadlessEngine) {
90  auto& context = GetContext();
91  WindowsConfigBuilder builder(context);
92  EnginePtr engine{builder.InitializeEngine()};
93  ASSERT_NE(engine, nullptr);
94 
95  ASSERT_TRUE(FlutterDesktopEngineRun(engine.get(), nullptr));
96 }
97 
98 // Verify that accessibility features are initialized when a view is created.
99 TEST_F(WindowsTest, LaunchRefreshesAccessibility) {
100  auto& context = GetContext();
101  WindowsConfigBuilder builder(context);
102  EnginePtr engine{builder.InitializeEngine()};
103  EngineModifier modifier{
104  reinterpret_cast<FlutterWindowsEngine*>(engine.get())};
105 
106  auto called = false;
107  modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
108  UpdateAccessibilityFeatures, ([&called](auto engine, auto flags) {
109  called = true;
110  return kSuccess;
111  }));
112 
113  ViewControllerPtr controller{
114  FlutterDesktopViewControllerCreate(0, 0, engine.release())};
115 
116  ASSERT_TRUE(called);
117 }
118 
119 // Verify that engine fails to launch when a conflicting entrypoint in
120 // FlutterDesktopEngineProperties.dart_entrypoint and the
121 // FlutterDesktopEngineRun parameter.
122 //
123 // TODO(cbracken): https://github.com/flutter/flutter/issues/109285
124 TEST_F(WindowsTest, LaunchConflictingCustomEntrypoints) {
125  auto& context = GetContext();
126  WindowsConfigBuilder builder(context);
127  builder.SetDartEntrypoint("customEntrypoint");
128  EnginePtr engine{builder.InitializeEngine()};
129  ASSERT_NE(engine, nullptr);
130 
131  ASSERT_FALSE(FlutterDesktopEngineRun(engine.get(), "conflictingEntrypoint"));
132 }
133 
134 // Verify that native functions can be registered and resolved.
135 TEST_F(WindowsTest, VerifyNativeFunction) {
136  auto& context = GetContext();
137  WindowsConfigBuilder builder(context);
138  builder.SetDartEntrypoint("verifyNativeFunction");
139 
140  fml::AutoResetWaitableEvent latch;
141  auto native_entry =
142  CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { latch.Signal(); });
143  context.AddNativeFunction("Signal", native_entry);
144 
145  ViewControllerPtr controller{builder.Run()};
146  ASSERT_NE(controller, nullptr);
147 
148  // Wait until signal has been called.
149  latch.Wait();
150 }
151 
152 // Verify that native functions that pass parameters can be registered and
153 // resolved.
154 TEST_F(WindowsTest, VerifyNativeFunctionWithParameters) {
155  auto& context = GetContext();
156  WindowsConfigBuilder builder(context);
157  builder.SetDartEntrypoint("verifyNativeFunctionWithParameters");
158 
159  bool bool_value = false;
160  fml::AutoResetWaitableEvent latch;
161  auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
162  auto handle = Dart_GetNativeBooleanArgument(args, 0, &bool_value);
163  ASSERT_FALSE(Dart_IsError(handle));
164  latch.Signal();
165  });
166  context.AddNativeFunction("SignalBoolValue", native_entry);
167 
168  ViewControllerPtr controller{builder.Run()};
169  ASSERT_NE(controller, nullptr);
170 
171  // Wait until signalBoolValue has been called.
172  latch.Wait();
173  EXPECT_TRUE(bool_value);
174 }
175 
176 // Verify that Platform.executable returns the executable name.
177 TEST_F(WindowsTest, PlatformExecutable) {
178  auto& context = GetContext();
179  WindowsConfigBuilder builder(context);
180  builder.SetDartEntrypoint("readPlatformExecutable");
181 
182  std::string executable_name;
183  fml::AutoResetWaitableEvent latch;
184  auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
185  auto handle = Dart_GetNativeArgument(args, 0);
186  ASSERT_FALSE(Dart_IsError(handle));
187  executable_name = tonic::DartConverter<std::string>::FromDart(handle);
188  latch.Signal();
189  });
190  context.AddNativeFunction("SignalStringValue", native_entry);
191 
192  ViewControllerPtr controller{builder.Run()};
193  ASSERT_NE(controller, nullptr);
194 
195  // Wait until signalStringValue has been called.
196  latch.Wait();
197  EXPECT_EQ(executable_name, "flutter_windows_unittests.exe");
198 }
199 
200 // Verify that native functions that return values can be registered and
201 // resolved.
202 TEST_F(WindowsTest, VerifyNativeFunctionWithReturn) {
203  auto& context = GetContext();
204  WindowsConfigBuilder builder(context);
205  builder.SetDartEntrypoint("verifyNativeFunctionWithReturn");
206 
207  bool bool_value_to_return = true;
208  fml::CountDownLatch latch(2);
209  auto bool_return_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
210  Dart_SetBooleanReturnValue(args, bool_value_to_return);
211  latch.CountDown();
212  });
213  context.AddNativeFunction("SignalBoolReturn", bool_return_entry);
214 
215  bool bool_value_passed = false;
216  auto bool_pass_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
217  auto handle = Dart_GetNativeBooleanArgument(args, 0, &bool_value_passed);
218  ASSERT_FALSE(Dart_IsError(handle));
219  latch.CountDown();
220  });
221  context.AddNativeFunction("SignalBoolValue", bool_pass_entry);
222 
223  ViewControllerPtr controller{builder.Run()};
224  ASSERT_NE(controller, nullptr);
225 
226  // Wait until signalBoolReturn and signalBoolValue have been called.
227  latch.Wait();
228  EXPECT_TRUE(bool_value_passed);
229 }
230 
231 // Verify the next frame callback is executed.
232 TEST_F(WindowsTest, NextFrameCallback) {
233  struct Captures {
234  fml::AutoResetWaitableEvent frame_scheduled_latch;
235  fml::AutoResetWaitableEvent frame_drawn_latch;
236  std::thread::id thread_id;
237  };
238  Captures captures;
239 
240  CreateNewThread("test_platform_thread")->PostTask([&]() {
241  captures.thread_id = std::this_thread::get_id();
242 
243  auto& context = GetContext();
244  WindowsConfigBuilder builder(context);
245  builder.SetDartEntrypoint("drawHelloWorld");
246 
247  auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
248  ASSERT_FALSE(captures.frame_drawn_latch.IsSignaledForTest());
249  captures.frame_scheduled_latch.Signal();
250  });
251  context.AddNativeFunction("NotifyFirstFrameScheduled", native_entry);
252 
253  ViewControllerPtr controller{builder.Run()};
254  ASSERT_NE(controller, nullptr);
255 
256  auto engine = FlutterDesktopViewControllerGetEngine(controller.get());
257 
259  engine,
260  [](void* user_data) {
261  auto captures = static_cast<Captures*>(user_data);
262 
263  ASSERT_TRUE(captures->frame_scheduled_latch.IsSignaledForTest());
264 
265  // Callback should execute on platform thread.
266  ASSERT_EQ(std::this_thread::get_id(), captures->thread_id);
267 
268  // Signal the test passed and end the Windows message loop.
269  captures->frame_drawn_latch.Signal();
270  ::PostQuitMessage(0);
271  },
272  &captures);
273 
274  // Pump messages for the Windows platform task runner.
275  ::MSG msg;
276  while (::GetMessage(&msg, nullptr, 0, 0)) {
277  ::TranslateMessage(&msg);
278  ::DispatchMessage(&msg);
279  }
280  });
281 
282  captures.frame_drawn_latch.Wait();
283 }
284 
285 TEST_F(WindowsTest, GetGraphicsAdapter) {
286  auto& context = GetContext();
287  WindowsConfigBuilder builder(context);
288  ViewControllerPtr controller{builder.Run()};
289  ASSERT_NE(controller, nullptr);
290  auto view = FlutterDesktopViewControllerGetView(controller.get());
291 
292  Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
293  dxgi_adapter = FlutterDesktopViewGetGraphicsAdapter(view);
294  ASSERT_NE(dxgi_adapter, nullptr);
295  DXGI_ADAPTER_DESC desc{};
296  ASSERT_TRUE(SUCCEEDED(dxgi_adapter->GetDesc(&desc)));
297 }
298 
299 } // namespace testing
300 } // namespace flutter
FlutterDesktopViewControllerGetView
FlutterDesktopViewRef FlutterDesktopViewControllerGetView(FlutterDesktopViewControllerRef ref)
Definition: flutter_windows.cc:120
FlutterDesktopEngineGetTextureRegistrar
FlutterDesktopTextureRegistrarRef FlutterDesktopEngineGetTextureRegistrar(FlutterDesktopEngineRef engine)
Definition: flutter_windows.cc:200
FlutterDesktopEngineProperties
Definition: flutter_windows.h:36
user_data
void * user_data
Definition: flutter_windows_view_unittests.cc:49
flutter::FlutterWindowsEngine
Definition: flutter_windows_engine.h:78
FlutterDesktopEngineProperties::icu_data_path
const wchar_t * icu_data_path
Definition: flutter_windows.h:45
flutter::FlutterWindowsEngine::UpdateAccessibilityFeatures
void UpdateAccessibilityFeatures()
Definition: flutter_windows_engine.cc:747
flutter::testing::TEST_F
TEST_F(CursorHandlerTest, ActivateSystemCursor)
Definition: cursor_handler_unittests.cc:94
flutter
Definition: accessibility_bridge_windows.cc:11
FlutterDesktopEngineDestroy
bool FlutterDesktopEngineDestroy(FlutterDesktopEngineRef engine_ref)
Definition: flutter_windows.cc:157
FlutterDesktopEngineCreate
FlutterDesktopEngineRef FlutterDesktopEngineCreate(const FlutterDesktopEngineProperties *engine_properties)
Definition: flutter_windows.cc:150
FlutterDesktopViewControllerGetEngine
FlutterDesktopEngineRef FlutterDesktopViewControllerGetEngine(FlutterDesktopViewControllerRef ref)
Definition: flutter_windows.cc:114
flutter_windows.h
flutter::testing::TEST
TEST(AccessibilityBridgeWindows, GetParent)
Definition: accessibility_bridge_windows_unittests.cc:235
FlutterDesktopEngineSetNextFrameCallback
void FlutterDesktopEngineSetNextFrameCallback(FlutterDesktopEngineRef engine, VoidCallback callback, void *user_data)
Definition: flutter_windows.cc:206
FlutterDesktopViewGetGraphicsAdapter
IDXGIAdapter * FlutterDesktopViewGetGraphicsAdapter(FlutterDesktopViewRef view)
Definition: flutter_windows.cc:217
FlutterDesktopEngineProperties::assets_path
const wchar_t * assets_path
Definition: flutter_windows.h:40
FlutterDesktopEngineRun
bool FlutterDesktopEngineRun(FlutterDesktopEngineRef engine, const char *entry_point)
Definition: flutter_windows.cc:167
FlutterDesktopViewControllerCreate
FlutterDesktopViewControllerRef FlutterDesktopViewControllerCreate(int width, int height, FlutterDesktopEngineRef engine_ref)
Definition: flutter_windows.cc:75