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