| 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/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);
35 FlutterDesktopEngineDestroy(engine);
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
258 FlutterDesktopEngineSetNextFrameCallback(
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