7#include <fuchsia/inspect/cpp/fidl.h>
8#include <fuchsia/kernel/cpp/fidl.h>
9#include <fuchsia/logger/cpp/fidl.h>
10#include <fuchsia/sysmem/cpp/fidl.h>
11#include <fuchsia/sysmem2/cpp/fidl.h>
12#include <fuchsia/tracing/provider/cpp/fidl.h>
13#include <fuchsia/ui/app/cpp/fidl.h>
14#include <lib/async/cpp/task.h>
15#include <lib/sys/component/cpp/testing/realm_builder.h>
16#include <lib/sys/component/cpp/testing/realm_builder_types.h>
25using component_testing::ChildOptions;
26using component_testing::ChildRef;
27using component_testing::ParentRef;
28using component_testing::Protocol;
29using component_testing::RealmRoot;
30using component_testing::Route;
31using component_testing::Storage;
47 realm_ = std::make_unique<RealmRoot>(realm_builder_.Build());
50void PortableUITest::SetUpRealmBase() {
51 FML_LOG(INFO) <<
"Setting up realm base";
57 fuchsia::component::decl::Environment flutter_runner_environment;
59 flutter_runner_environment.set_extends(
60 fuchsia::component::decl::EnvironmentExtends::REALM);
61 flutter_runner_environment.set_runners({});
62 auto environment_runners = flutter_runner_environment.mutable_runners();
65 fuchsia::component::decl::RunnerRegistration flutter_jit_runner_reg;
66 flutter_jit_runner_reg.set_source(fuchsia::component::decl::Ref::WithChild(
70 environment_runners->push_back(std::move(flutter_jit_runner_reg));
71 auto realm_decl = realm_builder_.GetRealmDecl();
72 if (!realm_decl.has_environments()) {
73 realm_decl.set_environments({});
75 auto realm_environments = realm_decl.mutable_environments();
76 realm_environments->push_back(std::move(flutter_runner_environment));
77 realm_builder_.ReplaceRealmDecl(std::move(realm_decl));
80 realm_builder_.AddChild(
kTestUIStack, GetTestUIStackUrl());
83 realm_builder_.AddRoute(Route{
84 .capabilities = {Protocol{fuchsia::inspect::InspectSink::Name_},
85 Protocol{fuchsia::kernel::VmexResource::Name_},
86 Protocol{fuchsia::logger::LogSink::Name_},
87 Protocol{fuchsia::sysmem::Allocator::Name_},
88 Protocol{fuchsia::sysmem2::Allocator::Name_},
89 Protocol{fuchsia::tracing::provider::Registry::Name_},
90 Protocol{fuchsia::ui::input::ImeService::Name_},
93 component_testing::Directory{
"config-data"}},
94 .source = ParentRef(),
98 realm_builder_.AddRoute(Route{
99 .capabilities = {Protocol{fuchsia::ui::composition::Allocator::Name_},
100 Protocol{fuchsia::ui::composition::Flatland::Name_},
101 Protocol{fuchsia::ui::test::input::Registry::Name_},
102 Protocol{fuchsia::ui::test::scene::Controller::Name_},
103 Protocol{fuchsia::ui::display::singleton::Info::Name_},
109 realm_builder_.AddRoute(Route{.capabilities = {Storage{
"tmp"}},
110 .source = ParentRef(),
114void PortableUITest::ProcessViewGeometryResponse(
115 fuchsia::ui::observation::geometry::WatchResponse response) {
117 if (!response.has_error()) {
118 std::vector<fuchsia::ui::observation::geometry::ViewTreeSnapshot>* updates =
119 response.mutable_updates();
120 if (updates && !updates->empty()) {
121 last_view_tree_snapshot_ = std::move(updates->back());
125 const auto&
error = response.error();
126 if (
error | fuchsia::ui::observation::geometry::Error::CHANNEL_OVERFLOW) {
127 FML_LOG(INFO) <<
"View Tree watcher channel overflowed";
129 fuchsia::ui::observation::geometry::Error::BUFFER_OVERFLOW) {
130 FML_LOG(INFO) <<
"View Tree watcher buffer overflowed";
132 fuchsia::ui::observation::geometry::Error::VIEWS_OVERFLOW) {
136 <<
"View Tree watcher attempted to report too many views";
141void PortableUITest::WatchViewGeometry() {
143 <<
"View Tree watcher must be registered before calling Watch()";
145 view_tree_watcher_->Watch([
this](
auto response) {
146 ProcessViewGeometryResponse(std::move(response));
152 return last_view_tree_snapshot_.has_value() &&
158 realm_->component().Connect<fuchsia::ui::test::scene::Controller>();
159 scene_provider_.set_error_handler([](
auto) {
160 FML_LOG(ERROR) <<
"Error from test scene provider: "
161 << &zx_status_get_string;
164 fuchsia::ui::test::scene::ControllerAttachClientViewRequest request;
165 request.set_view_provider(
166 realm_->component().Connect<fuchsia::ui::app::ViewProvider>());
167 scene_provider_->RegisterViewTreeWatcher(view_tree_watcher_.NewRequest(),
169 scene_provider_->AttachClientView(
170 std::move(request), [
this](
auto client_view_ref_koid) {
171 client_root_view_ref_koid_ = client_view_ref_koid;
174 FML_LOG(INFO) <<
"Waiting for client view ref koid";
175 RunLoopUntil([
this] {
return client_root_view_ref_koid_.has_value(); });
179 FML_LOG(INFO) <<
"Waiting for client view to connect";
183 FML_LOG(INFO) <<
"Client view has rendered";
190 RunLoopUntil([
this] {
191 if (!last_view_tree_snapshot_.has_value() ||
192 !last_view_tree_snapshot_->has_views()) {
196 if (!client_root_view_ref_koid_.has_value()) {
200 for (
const auto&
view : last_view_tree_snapshot_->views()) {
201 if (!
view.has_view_ref_koid() ||
202 view.view_ref_koid() != *client_root_view_ref_koid_) {
206 if (
view.children().empty()) {
213 return std::count_if(
214 last_view_tree_snapshot_->views().begin(),
215 last_view_tree_snapshot_->views().end(),
217 view.children().back()](
const auto& view_to_check) {
218 return view_to_check.has_view_ref_koid() &&
219 view_to_check.view_ref_koid() == view_to_find;
226 FML_LOG(INFO) <<
"Embedded view has rendered";
230 FML_LOG(INFO) <<
"Registering fake touch screen";
232 realm_->component().Connect<fuchsia::ui::test::input::Registry>();
233 input_registry_.set_error_handler([](
auto) {
234 FML_LOG(ERROR) <<
"Error from input helper: " << &zx_status_get_string;
237 bool touchscreen_registered =
false;
238 fuchsia::ui::test::input::RegistryRegisterTouchScreenRequest request;
239 request.set_device(fake_touchscreen_.NewRequest());
240 input_registry_->RegisterTouchScreen(
242 [&touchscreen_registered]() { touchscreen_registered =
true; });
244 RunLoopUntil([&touchscreen_registered] {
return touchscreen_registered; });
245 FML_LOG(INFO) <<
"Touchscreen registered";
249 FML_LOG(INFO) <<
"Registering fake mouse";
251 realm_->component().Connect<fuchsia::ui::test::input::Registry>();
252 input_registry_.set_error_handler([](
auto) {
253 FML_LOG(ERROR) <<
"Error from input helper: " << &zx_status_get_string;
256 bool mouse_registered =
false;
257 fuchsia::ui::test::input::RegistryRegisterMouseRequest request;
258 request.set_device(fake_mouse_.NewRequest());
259 input_registry_->RegisterMouse(
260 std::move(request), [&mouse_registered]() { mouse_registered =
true; });
262 RunLoopUntil([&mouse_registered] {
return mouse_registered; });
263 FML_LOG(INFO) <<
"Mouse registered";
267 FML_LOG(INFO) <<
"Registering fake keyboard";
269 realm_->component().Connect<fuchsia::ui::test::input::Registry>();
270 input_registry_.set_error_handler([](
auto) {
271 FML_LOG(ERROR) <<
"Error from input helper: " << &zx_status_get_string;
274 bool keyboard_registered =
false;
275 fuchsia::ui::test::input::RegistryRegisterKeyboardRequest request;
276 request.set_device(fake_keyboard_.NewRequest());
277 input_registry_->RegisterKeyboard(
279 [&keyboard_registered]() { keyboard_registered =
true; });
281 RunLoopUntil([&keyboard_registered] {
return keyboard_registered; });
282 FML_LOG(INFO) <<
"Keyboard registered";
286 fuchsia::ui::test::input::TouchScreenSimulateTapRequest tap_request;
287 tap_request.mutable_tap_location()->x =
x;
288 tap_request.mutable_tap_location()->y =
y;
290 FML_LOG(INFO) <<
"Injecting tap at (" << tap_request.tap_location().x <<
", "
291 << tap_request.tap_location().y <<
")";
292 fake_touchscreen_->SimulateTap(std::move(tap_request), [
this]() {
293 ++touch_injection_request_count_;
294 FML_LOG(INFO) <<
"*** Tap injected, count: "
295 << touch_injection_request_count_;
300 std::vector<fuchsia::ui::test::input::MouseButton> pressed_buttons,
303 fuchsia::ui::test::input::MouseSimulateMouseEventRequest request;
304 request.set_pressed_buttons(std::move(pressed_buttons));
305 request.set_movement_x(movement_x);
306 request.set_movement_y(movement_y);
308 FML_LOG(INFO) <<
"Injecting mouse input";
310 fake_mouse_->SimulateMouseEvent(
311 std::move(request), [] {
FML_LOG(INFO) <<
"Mouse event injected"; });
315 std::vector<fuchsia::ui::test::input::MouseButton> pressed_buttons,
318 bool use_physical_units) {
319 FML_LOG(INFO) <<
"Requesting mouse scroll";
320 fuchsia::ui::test::input::MouseSimulateMouseEventRequest request;
321 request.set_pressed_buttons(std::move(pressed_buttons));
322 if (use_physical_units) {
323 request.set_scroll_h_physical_pixel(scroll_x);
324 request.set_scroll_v_physical_pixel(scroll_y);
326 request.set_scroll_h_detent(scroll_x);
327 request.set_scroll_v_detent(scroll_y);
330 fake_mouse_->SimulateMouseEvent(std::move(request), [] {
331 FML_LOG(INFO) <<
"Mouse scroll event injected";
336 FML_LOG(INFO) <<
"Sending text request";
339 fuchsia::ui::test::input::KeyboardSimulateUsAsciiTextEntryRequest request;
340 request.set_text(
text);
341 fake_keyboard_->SimulateUsAsciiTextEntry(std::move(request),
342 [&done]() { done =
true; });
344 RunLoopUntil([&] {
return done; });
345 FML_LOG(INFO) <<
"Text request sent";
static constexpr auto kTestUIStack
void RegisterTouchScreen()
static constexpr auto kFlutterRunnerEnvironment
void SimulateMouseEvent(std::vector< fuchsia::ui::test::input::MouseButton > pressed_buttons, int movement_x, int movement_y)
void SimulateMouseScroll(std::vector< fuchsia::ui::test::input::MouseButton > pressed_buttons, int scroll_x, int scroll_y, bool use_physical_units=false)
static constexpr auto kFlutterJitRunnerRef
static constexpr auto kPointerInjectorRegistryName
void InjectTap(int32_t x, int32_t y)
void LaunchClientWithEmbeddedView()
bool HasViewConnected(zx_koid_t view_ref_koid)
static constexpr auto kTestUIStackRef
void SimulateTextEntry(std::string text)
static constexpr auto kFlutterJitRunner
static constexpr auto kFlutterJitRunnerUrl
static constexpr auto kVulkanLoaderServiceName
static constexpr auto kPosixSocketProviderName
void SetUp(bool build_realm=true)
const uint8_t uint32_t uint32_t GError ** error
#define FML_LOG(severity)
#define FML_CHECK(condition)
bool CheckViewExistsInSnapshot(const fuchsia::ui::observation::geometry::ViewTreeSnapshot &snapshot, zx_koid_t view_ref_koid)