Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
flutter-embedder-test.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 <fuchsia/inspect/cpp/fidl.h>
6#include <fuchsia/logger/cpp/fidl.h>
7#include <fuchsia/tracing/provider/cpp/fidl.h>
8#include <fuchsia/ui/app/cpp/fidl.h>
9#include <fuchsia/ui/composition/cpp/fidl.h>
10#include <fuchsia/ui/display/singleton/cpp/fidl.h>
11#include <fuchsia/ui/observation/geometry/cpp/fidl.h>
12#include <fuchsia/ui/test/input/cpp/fidl.h>
13#include <fuchsia/ui/test/scene/cpp/fidl.h>
14#include <lib/async-loop/testing/cpp/real_loop.h>
15#include <lib/async/cpp/task.h>
16#include <lib/fidl/cpp/binding_set.h>
17#include <lib/sys/component/cpp/testing/realm_builder.h>
18#include <lib/sys/component/cpp/testing/realm_builder_types.h>
19#include <lib/zx/clock.h>
20#include <zircon/status.h>
21#include <zircon/time.h>
22
23#include <optional>
24#include <vector>
25
26#include "flutter/fml/logging.h"
27#include "gtest/gtest.h"
28
29#include "flutter/shell/platform/fuchsia/flutter/tests/integration/utils/check_view.h"
30#include "flutter/shell/platform/fuchsia/flutter/tests/integration/utils/screenshot.h"
31
33namespace {
34
35// Types imported for the realm_builder library.
36using component_testing::ChildOptions;
37using component_testing::ChildRef;
38using component_testing::DirectoryContents;
39using component_testing::ParentRef;
40using component_testing::Protocol;
41using component_testing::RealmRoot;
42using component_testing::Route;
43using component_testing::StartupMode;
44
46
47// The FIDL bindings for this service are not exposed in the Fuchsia SDK, so we
48// must encode the name manually here.
49constexpr auto kVulkanLoaderServiceName = "fuchsia.vulkan.loader.Loader";
50
51constexpr auto kFlutterJitRunnerUrl =
52 "fuchsia-pkg://fuchsia.com/oot_flutter_jit_runner#meta/"
53 "flutter_jit_runner.cm";
54constexpr auto kFlutterJitProductRunnerUrl =
55 "fuchsia-pkg://fuchsia.com/oot_flutter_jit_product_runner#meta/"
56 "flutter_jit_product_runner.cm";
57constexpr auto kFlutterAotRunnerUrl =
58 "fuchsia-pkg://fuchsia.com/oot_flutter_aot_runner#meta/"
59 "flutter_aot_runner.cm";
60constexpr auto kFlutterAotProductRunnerUrl =
61 "fuchsia-pkg://fuchsia.com/oot_flutter_aot_product_runner#meta/"
62 "flutter_aot_product_runner.cm";
63constexpr char kChildViewUrl[] =
64 "fuchsia-pkg://fuchsia.com/child-view#meta/child-view.cm";
65constexpr char kParentViewUrl[] =
66 "fuchsia-pkg://fuchsia.com/parent-view#meta/parent-view.cm";
67static constexpr auto kTestUiStackUrl =
68 "fuchsia-pkg://fuchsia.com/flatland-scene-manager-test-ui-stack#meta/"
69 "test-ui-stack.cm";
70
71constexpr auto kFlutterRunnerEnvironment = "flutter_runner_env";
72constexpr auto kFlutterJitRunner = "flutter_jit_runner";
73constexpr auto kFlutterJitRunnerRef = ChildRef{kFlutterJitRunner};
74constexpr auto kFlutterJitProductRunner = "flutter_jit_product_runner";
75constexpr auto kFlutterJitProductRunnerRef = ChildRef{kFlutterJitProductRunner};
76constexpr auto kFlutterAotRunner = "flutter_aot_runner";
77constexpr auto kFlutterAotRunnerRef = ChildRef{kFlutterAotRunner};
78constexpr auto kFlutterAotProductRunner = "flutter_aot_product_runner";
79constexpr auto kFlutterAotProductRunnerRef = ChildRef{kFlutterAotProductRunner};
80constexpr auto kChildView = "child_view";
81constexpr auto kChildViewRef = ChildRef{kChildView};
82constexpr auto kParentView = "parent_view";
83constexpr auto kParentViewRef = ChildRef{kParentView};
84constexpr auto kTestUiStack = "ui";
85constexpr auto kTestUiStackRef = ChildRef{kTestUiStack};
86
87// Background and foreground color values.
88const fuchsia_test_utils::Pixel kParentBackgroundColor(0xFF,
89 0x00,
90 0x00,
91 0xFF); // Blue
92const fuchsia_test_utils::Pixel kChildBackgroundColor(0xFF,
93 0x00,
94 0xFF,
95 0xFF); // Pink
96const fuchsia_test_utils::Pixel kFlatlandOverlayColor(0x00,
97 0xFF,
98 0x00,
99 0xFF); // Green
100
101static uint32_t OverlayPixelCount(
102 std::map<fuchsia_test_utils::Pixel, uint32_t>& histogram) {
103 return histogram[kFlatlandOverlayColor];
104}
105
106// Timeout for |TakeScreenshot| FIDL call.
107constexpr zx::duration kScreenshotTimeout = zx::sec(10);
108// Timeout to fail the test if it goes beyond this duration.
109constexpr zx::duration kTestTimeout = zx::min(1);
110
111} // namespace
112
113class FlutterEmbedderTest : public ::loop_fixture::RealLoop,
114 public ::testing::Test {
115 public:
117 : realm_builder_(component_testing::RealmBuilder::Create()) {
118 FML_VLOG(1) << "Setting up base realm";
119 SetUpRealmBase();
120
121 // Post a "just in case" quit task, if the test hangs.
122 async::PostDelayedTask(
123 dispatcher(),
124 [] {
126 << "\n\n>> Test did not complete in time, terminating. <<\n\n";
127 },
128 kTestTimeout);
129 }
130
131 bool HasViewConnected(
132 const fuchsia::ui::observation::geometry::ViewTreeWatcherPtr&
133 view_tree_watcher,
134 std::optional<fuchsia::ui::observation::geometry::WatchResponse>&
135 watch_response,
136 zx_koid_t view_ref_koid);
137
139 const std::vector<std::string>& component_args = {});
140
142
145 fit::function<void(std::map<fuchsia_test_utils::Pixel, uint32_t>)>
146 callback = nullptr,
147 zx::duration timeout = kTestTimeout);
148
149 private:
150 void SetUpRealmBase();
151
152 fuchsia::ui::test::scene::ControllerPtr scene_provider_;
153 fuchsia::ui::observation::geometry::ViewTreeWatcherPtr view_tree_watcher_;
154 fuchsia::ui::composition::ScreenshotPtr screenshot_;
155
156 // Wrapped in optional since the view is not created until the middle of SetUp
157 component_testing::RealmBuilder realm_builder_;
158 std::unique_ptr<component_testing::RealmRoot> realm_;
159
160 uint64_t display_width_ = 0;
161 uint64_t display_height_ = 0;
162};
163
164void FlutterEmbedderTest::SetUpRealmBase() {
165 FML_LOG(INFO) << "Setting up realm base.";
166
167 // First, add the flutter runner(s) as children.
168 realm_builder_.AddChild(kFlutterJitRunner, kFlutterJitRunnerUrl);
169 realm_builder_.AddChild(kFlutterJitProductRunner,
170 kFlutterJitProductRunnerUrl);
171 realm_builder_.AddChild(kFlutterAotRunner, kFlutterAotRunnerUrl);
172 realm_builder_.AddChild(kFlutterAotProductRunner,
173 kFlutterAotProductRunnerUrl);
174
175 // Then, add an environment providing them.
176 fuchsia::component::decl::Environment flutter_runner_environment;
177 flutter_runner_environment.set_name(kFlutterRunnerEnvironment);
178 flutter_runner_environment.set_extends(
179 fuchsia::component::decl::EnvironmentExtends::REALM);
180 flutter_runner_environment.set_runners({});
181 auto environment_runners = flutter_runner_environment.mutable_runners();
182 fuchsia::component::decl::RunnerRegistration flutter_jit_runner_reg;
183 flutter_jit_runner_reg.set_source(fuchsia::component::decl::Ref::WithChild(
184 fuchsia::component::decl::ChildRef{.name = kFlutterJitRunner}));
185 flutter_jit_runner_reg.set_source_name(kFlutterJitRunner);
186 flutter_jit_runner_reg.set_target_name(kFlutterJitRunner);
187 environment_runners->push_back(std::move(flutter_jit_runner_reg));
188 fuchsia::component::decl::RunnerRegistration flutter_jit_product_runner_reg;
189 flutter_jit_product_runner_reg.set_source(
190 fuchsia::component::decl::Ref::WithChild(
191 fuchsia::component::decl::ChildRef{.name =
192 kFlutterJitProductRunner}));
193 flutter_jit_product_runner_reg.set_source_name(kFlutterJitProductRunner);
194 flutter_jit_product_runner_reg.set_target_name(kFlutterJitProductRunner);
195 environment_runners->push_back(std::move(flutter_jit_product_runner_reg));
196 fuchsia::component::decl::RunnerRegistration flutter_aot_runner_reg;
197 flutter_aot_runner_reg.set_source(fuchsia::component::decl::Ref::WithChild(
198 fuchsia::component::decl::ChildRef{.name = kFlutterAotRunner}));
199 flutter_aot_runner_reg.set_source_name(kFlutterAotRunner);
200 flutter_aot_runner_reg.set_target_name(kFlutterAotRunner);
201 environment_runners->push_back(std::move(flutter_aot_runner_reg));
202 fuchsia::component::decl::RunnerRegistration flutter_aot_product_runner_reg;
203 flutter_aot_product_runner_reg.set_source(
204 fuchsia::component::decl::Ref::WithChild(
205 fuchsia::component::decl::ChildRef{.name =
206 kFlutterAotProductRunner}));
207 flutter_aot_product_runner_reg.set_source_name(kFlutterAotProductRunner);
208 flutter_aot_product_runner_reg.set_target_name(kFlutterAotProductRunner);
209 environment_runners->push_back(std::move(flutter_aot_product_runner_reg));
210 auto realm_decl = realm_builder_.GetRealmDecl();
211 if (!realm_decl.has_environments()) {
212 realm_decl.set_environments({});
213 }
214 auto realm_environments = realm_decl.mutable_environments();
215 realm_environments->push_back(std::move(flutter_runner_environment));
216 realm_builder_.ReplaceRealmDecl(std::move(realm_decl));
217
218 // Add test UI stack component.
219 realm_builder_.AddChild(kTestUiStack, kTestUiStackUrl);
220
221 // Add embedded parent and child components.
222 realm_builder_.AddChild(kChildView, kChildViewUrl,
223 ChildOptions{
224 .environment = kFlutterRunnerEnvironment,
225 });
226 realm_builder_.AddChild(kParentView, kParentViewUrl,
227 ChildOptions{
228 .environment = kFlutterRunnerEnvironment,
229 });
230
231 // Route base system services to flutter runners.
232 realm_builder_.AddRoute(
233 Route{.capabilities =
234 {
235 Protocol{fuchsia::logger::LogSink::Name_},
236 Protocol{fuchsia::inspect::InspectSink::Name_},
237 Protocol{fuchsia::sysmem::Allocator::Name_},
238 Protocol{fuchsia::tracing::provider::Registry::Name_},
239 Protocol{kVulkanLoaderServiceName},
240 },
241 .source = ParentRef{},
242 .targets = {kFlutterJitRunnerRef, kFlutterJitProductRunnerRef,
243 kFlutterAotRunnerRef, kFlutterAotProductRunnerRef}});
244
245 // Route base system services to the test UI stack.
246 realm_builder_.AddRoute(Route{
247 .capabilities = {Protocol{fuchsia::logger::LogSink::Name_},
248 Protocol{fuchsia::inspect::InspectSink::Name_},
249 Protocol{fuchsia::sysmem::Allocator::Name_},
250 Protocol{fuchsia::tracing::provider::Registry::Name_},
251 Protocol{kVulkanLoaderServiceName}},
252 .source = ParentRef{},
253 .targets = {kTestUiStackRef}});
254
255 // Route UI capabilities from test UI stack to flutter runners.
256 realm_builder_.AddRoute(Route{
257 .capabilities = {Protocol{fuchsia::ui::composition::Allocator::Name_},
258 Protocol{fuchsia::ui::composition::Flatland::Name_}},
259 .source = kTestUiStackRef,
260 .targets = {kFlutterJitRunnerRef, kFlutterJitProductRunnerRef,
261 kFlutterAotRunnerRef, kFlutterAotProductRunnerRef}});
262
263 // Route test capabilities from test UI stack to test driver.
264 realm_builder_.AddRoute(Route{
265 .capabilities = {Protocol{fuchsia::ui::composition::Screenshot::Name_},
266 Protocol{fuchsia::ui::test::input::Registry::Name_},
267 Protocol{fuchsia::ui::test::scene::Controller::Name_},
268 Protocol{fuchsia::ui::display::singleton::Info::Name_}},
269 .source = kTestUiStackRef,
270 .targets = {ParentRef()}});
271
272 // Route ViewProvider from child to parent, and parent to test.
273 realm_builder_.AddRoute(
274 Route{.capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}},
275 .source = kParentViewRef,
276 .targets = {ParentRef()}});
277 realm_builder_.AddRoute(
278 Route{.capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}},
279 .source = kChildViewRef,
280 .targets = {kParentViewRef}});
281}
282
283// Checks whether the view with |view_ref_koid| has connected to the view tree.
284// The response of a f.u.o.g.Provider.Watch call is stored in |watch_response|
285// if it contains |view_ref_koid|.
287 const fuchsia::ui::observation::geometry::ViewTreeWatcherPtr&
288 view_tree_watcher,
289 std::optional<fuchsia::ui::observation::geometry::WatchResponse>&
290 watch_response,
291 zx_koid_t view_ref_koid) {
292 std::optional<fuchsia::ui::observation::geometry::WatchResponse> watch_result;
293 view_tree_watcher->Watch(
294 [&watch_result](auto response) { watch_result = std::move(response); });
295 FML_LOG(INFO) << "Waiting for view tree watch result";
296 RunLoopUntil([&watch_result] { return watch_result.has_value(); });
297 FML_LOG(INFO) << "Received for view tree watch result";
298 if (CheckViewExistsInUpdates(watch_result->updates(), view_ref_koid)) {
299 watch_response = std::move(watch_result);
300 };
301 return watch_response.has_value();
302}
303
305 const std::vector<std::string>& component_args) {
306 FML_LOG(INFO) << "Launching parent-view";
307
308 if (!component_args.empty()) {
309 // Construct a args.csv file containing the specified comma-separated
310 // component args.
311 std::string csv;
312 for (const auto& arg : component_args) {
313 csv += arg + ',';
314 }
315 // Remove last comma.
316 csv.pop_back();
317
318 auto config_directory_contents = DirectoryContents();
319 config_directory_contents.AddFile("args.csv", csv);
320 realm_builder_.RouteReadOnlyDirectory("config-data", {kParentViewRef},
321 std::move(config_directory_contents));
322 }
323 realm_ = std::make_unique<RealmRoot>(realm_builder_.Build());
324
325 // Get the display information using the |fuchsia.ui.display.singleton.Info|.
326 std::optional<bool> display_metrics_obtained;
327 fuchsia::ui::display::singleton::InfoPtr display_info =
328 realm_->component().Connect<fuchsia::ui::display::singleton::Info>();
329 display_info->GetMetrics([this, &display_metrics_obtained](auto info) {
330 display_width_ = info.extent_in_px().width;
331 display_height_ = info.extent_in_px().height;
332 display_metrics_obtained = true;
333 });
334 RunLoopUntil([&display_metrics_obtained] {
335 return display_metrics_obtained.has_value();
336 });
337 FML_LOG(INFO) << "Got display_width " << display_width_ << " display_height "
338 << display_height_;
339
340 // Instruct Test UI Stack to present parent-view's View.
341 std::optional<zx_koid_t> view_ref_koid;
342 scene_provider_ =
343 realm_->component().Connect<fuchsia::ui::test::scene::Controller>();
344 scene_provider_.set_error_handler(
345 [](auto) { FML_LOG(ERROR) << "Error from test scene provider"; });
346 fuchsia::ui::test::scene::ControllerAttachClientViewRequest request;
347 request.set_view_provider(
348 realm_->component().Connect<fuchsia::ui::app::ViewProvider>());
349 scene_provider_->RegisterViewTreeWatcher(view_tree_watcher_.NewRequest(),
350 []() {});
351 scene_provider_->AttachClientView(
352 std::move(request), [&view_ref_koid](auto client_view_ref_koid) {
353 view_ref_koid = client_view_ref_koid;
354 });
355
356 FML_LOG(INFO) << "Waiting for client view ref koid";
357 RunLoopUntil([&view_ref_koid] { return view_ref_koid.has_value(); });
358
359 // Wait for the client view to get attached to the view tree.
360 std::optional<fuchsia::ui::observation::geometry::WatchResponse>
361 watch_response;
362 FML_LOG(INFO) << "Waiting for client view to render; koid is "
363 << (view_ref_koid.has_value() ? view_ref_koid.value() : 0);
364 RunLoopUntil([this, &watch_response, &view_ref_koid] {
365 return HasViewConnected(view_tree_watcher_, watch_response, *view_ref_koid);
366 });
367 FML_LOG(INFO) << "Client view has rendered";
368
369 screenshot_ =
370 realm_->component().Connect<fuchsia::ui::composition::Screenshot>();
371 FML_LOG(INFO) << "Launched parent-view";
372}
373
375 FML_LOG(INFO) << "Taking screenshot... ";
376
377 fuchsia::ui::composition::ScreenshotTakeRequest request;
378 request.set_format(fuchsia::ui::composition::ScreenshotFormat::BGRA_RAW);
379
380 std::optional<fuchsia::ui::composition::ScreenshotTakeResponse> response;
381 screenshot_->Take(std::move(request), [this, &response](auto screenshot) {
382 response = std::move(screenshot);
383 QuitLoop();
384 });
385
386 EXPECT_FALSE(RunLoopWithTimeout(kScreenshotTimeout))
387 << "Timed out waiting for screenshot.";
388 FML_LOG(INFO) << "Screenshot captured.";
389
391 response->vmo(), display_width_, display_height_, /*display_rotation*/ 0);
392}
393
396 fit::function<void(std::map<fuchsia_test_utils::Pixel, uint32_t>)> callback,
397 zx::duration timeout) {
398 return RunLoopWithTimeoutOrUntil(
399 [this, &callback, &color] {
400 auto screenshot = TakeScreenshot();
401 auto histogram = screenshot.Histogram();
402
403 bool color_found = histogram[color] > 0;
404 if (color_found && callback != nullptr) {
405 callback(std::move(histogram));
406 }
407 return color_found;
408 },
409 timeout);
410}
411
413 LaunchParentViewInRealm();
414
415 // Take screenshot until we see the child-view's embedded color.
416 ASSERT_TRUE(TakeScreenshotUntil(
417 kChildBackgroundColor,
418 [](std::map<fuchsia_test_utils::Pixel, uint32_t> histogram) {
419 // Expect parent and child background colors, with parent color > child
420 // color.
421 EXPECT_GT(histogram[kParentBackgroundColor], 0u);
422 EXPECT_GT(histogram[kChildBackgroundColor], 0u);
423 EXPECT_GT(histogram[kParentBackgroundColor],
424 histogram[kChildBackgroundColor]);
425 }));
426}
427
428TEST_F(FlutterEmbedderTest, EmbeddingWithOverlay) {
429 LaunchParentViewInRealm({"--showOverlay"});
430
431 // Take screenshot until we see the child-view's embedded color.
432 ASSERT_TRUE(TakeScreenshotUntil(
433 kChildBackgroundColor,
434 [](std::map<fuchsia_test_utils::Pixel, uint32_t> histogram) {
435 // Expect parent, overlay and child background colors.
436 // With parent color > child color and overlay color > child color.
437 const uint32_t overlay_pixel_count = OverlayPixelCount(histogram);
438 EXPECT_GT(histogram[kParentBackgroundColor], 0u);
439 EXPECT_GT(overlay_pixel_count, 0u);
440 EXPECT_GT(histogram[kChildBackgroundColor], 0u);
441 EXPECT_GT(histogram[kParentBackgroundColor],
442 histogram[kChildBackgroundColor]);
443 EXPECT_GT(overlay_pixel_count, histogram[kChildBackgroundColor]);
444 }));
445}
446
447} // namespace flutter_embedder_test
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
SkColor4f color
static sk_sp< Effect > Create()
fuchsia_test_utils::Screenshot TakeScreenshot()
bool HasViewConnected(const fuchsia::ui::observation::geometry::ViewTreeWatcherPtr &view_tree_watcher, std::optional< fuchsia::ui::observation::geometry::WatchResponse > &watch_response, zx_koid_t view_ref_koid)
bool TakeScreenshotUntil(fuchsia_test_utils::Pixel color, fit::function< void(std::map< fuchsia_test_utils::Pixel, uint32_t >)> callback=nullptr, zx::duration timeout=kTestTimeout)
void LaunchParentViewInRealm(const std::vector< std::string > &component_args={})
#define FATAL(error)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
#define FML_VLOG(verbose_level)
Definition logging.h:98
#define FML_LOG(severity)
Definition logging.h:82
TEST_F(FlutterEmbedderTest, Embedding)
bool CheckViewExistsInUpdates(const std::vector< fuchsia::ui::observation::geometry::ViewTreeSnapshot > &updates, zx_koid_t view_ref_koid)
Definition check_view.cc:27
#define ERROR(message)