5#include <fuchsia/accessibility/semantics/cpp/fidl.h>
6#include <fuchsia/buildinfo/cpp/fidl.h>
7#include <fuchsia/component/cpp/fidl.h>
8#include <fuchsia/fonts/cpp/fidl.h>
9#include <fuchsia/intl/cpp/fidl.h>
10#include <fuchsia/kernel/cpp/fidl.h>
11#include <fuchsia/memorypressure/cpp/fidl.h>
12#include <fuchsia/metrics/cpp/fidl.h>
13#include <fuchsia/net/interfaces/cpp/fidl.h>
14#include <fuchsia/sysmem/cpp/fidl.h>
15#include <fuchsia/tracing/provider/cpp/fidl.h>
16#include <fuchsia/ui/app/cpp/fidl.h>
17#include <fuchsia/ui/display/singleton/cpp/fidl.h>
18#include <fuchsia/ui/input/cpp/fidl.h>
19#include <fuchsia/ui/test/input/cpp/fidl.h>
20#include <fuchsia/ui/test/scene/cpp/fidl.h>
21#include <fuchsia/web/cpp/fidl.h>
22#include <lib/async-loop/testing/cpp/real_loop.h>
23#include <lib/async/cpp/task.h>
24#include <lib/fidl/cpp/binding_set.h>
25#include <lib/sys/component/cpp/testing/realm_builder.h>
26#include <lib/sys/component/cpp/testing/realm_builder_types.h>
27#include <lib/sys/cpp/component_context.h>
28#include <lib/zx/clock.h>
29#include <lib/zx/time.h>
30#include <zircon/status.h>
31#include <zircon/types.h>
32#include <zircon/utc.h>
42#include <gtest/gtest.h>
44#include "flutter/fml/logging.h"
45#include "flutter/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.h"
108using component_testing::ChildRef;
109using component_testing::ConfigValue;
110using component_testing::DirectoryContents;
111using component_testing::LocalComponentImpl;
112using component_testing::ParentRef;
113using component_testing::Protocol;
114using component_testing::Realm;
115using component_testing::RealmRoot;
116using component_testing::Route;
120using RealmBuilder = component_testing::RealmBuilder;
126constexpr auto kTestUIStackUrl =
127 "fuchsia-pkg://fuchsia.com/flatland-scene-manager-test-ui-stack#meta/"
130constexpr auto kMockTouchInputListener =
"touch_input_listener";
131constexpr auto kMockTouchInputListenerRef = ChildRef{kMockTouchInputListener};
133constexpr auto kTouchInputView =
"touch-input-view";
134constexpr auto kTouchInputViewRef = ChildRef{kTouchInputView};
135constexpr auto kTouchInputViewUrl =
136 "fuchsia-pkg://fuchsia.com/touch-input-view#meta/touch-input-view.cm";
137constexpr auto kEmbeddingFlutterView =
"embedding-flutter-view";
138constexpr auto kEmbeddingFlutterViewRef = ChildRef{kEmbeddingFlutterView};
139constexpr auto kEmbeddingFlutterViewUrl =
140 "fuchsia-pkg://fuchsia.com/embedding-flutter-view#meta/"
141 "embedding-flutter-view.cm";
143bool CompareDouble(
double f0,
double f1,
double epsilon) {
144 return std::abs(f0 - f1) <= epsilon;
156class TouchInputListenerServer
157 :
public fuchsia::ui::test::input::TouchInputListener,
158 public LocalComponentImpl {
160 explicit TouchInputListenerServer(async_dispatcher_t* dispatcher)
161 : dispatcher_(dispatcher) {}
164 void ReportTouchInput(
165 fuchsia::ui::test::input::TouchInputListenerReportTouchInputRequest
167 FML_LOG(INFO) <<
"Received ReportTouchInput event";
168 events_received_.push_back(std::move(request));
174 void OnStart()
override {
175 FML_LOG(INFO) <<
"Starting TouchInputListenerServer";
178 ASSERT_EQ(ZX_OK, outgoing()->AddPublicService(
179 fidl::InterfaceRequestHandler<
180 fuchsia::ui::test::input::TouchInputListener>(
181 [
this](
auto request) {
182 bindings_.AddBinding(
this, std::move(request),
188 fuchsia::ui::test::input::TouchInputListenerReportTouchInputRequest>&
190 return events_received_;
194 async_dispatcher_t* dispatcher_ =
nullptr;
195 fidl::BindingSet<fuchsia::ui::test::input::TouchInputListener> bindings_;
197 fuchsia::ui::test::input::TouchInputListenerReportTouchInputRequest>
201class FlutterTapTestBase :
public PortableUITest,
public ::testing::Test {
203 ~FlutterTapTestBase()
override {
204 FML_CHECK(touch_injection_request_count() > 0)
205 <<
"Injection expected but didn't happen.";
208 void SetUp()
override {
209 PortableUITest::SetUp();
212 async::PostDelayedTask(
216 <<
"\n\n>> Test did not complete in time, terminating. <<\n\n";
223 <<
"Waiting for display info from fuchsia.ui.display.singleton.Info";
224 std::optional<bool> display_metrics_obtained;
225 fuchsia::ui::display::singleton::InfoPtr display_info =
228 .Connect<fuchsia::ui::display::singleton::Info>();
229 display_info->GetMetrics([
this, &display_metrics_obtained](
auto info) {
230 display_width_ =
info.extent_in_px().width;
231 display_height_ =
info.extent_in_px().height;
232 display_metrics_obtained =
true;
234 RunLoopUntil([&display_metrics_obtained] {
235 return display_metrics_obtained.has_value();
239 FML_LOG(INFO) <<
"Registering input injection device";
240 RegisterTouchScreen();
243 bool LastEventReceivedMatches(
float expected_x,
245 std::string component_name) {
246 const auto& events_received =
249 if (events_received.empty()) {
253 const auto& last_event = events_received.back();
255 auto pixel_scale = last_event.has_device_pixel_ratio()
256 ? last_event.device_pixel_ratio()
259 auto actual_x = pixel_scale * last_event.local_x();
260 auto actual_y = pixel_scale * last_event.local_y();
261 auto actual_component = last_event.component_name();
263 bool last_event_matches =
264 CompareDouble(actual_x, expected_x, pixel_scale) &&
265 CompareDouble(actual_y, expected_y, pixel_scale) &&
266 last_event.component_name() == component_name;
268 if (last_event_matches) {
269 FML_LOG(INFO) <<
"Received event for component " << component_name
270 <<
" at (" << expected_x <<
", " << expected_y <<
")";
272 FML_LOG(WARNING) <<
"Expecting event for component " << component_name
273 <<
" at (" << expected_x <<
", " << expected_y <<
"). "
274 <<
"Instead received event for component "
275 << actual_component <<
" at (" << actual_x <<
", "
276 << actual_y <<
"), accounting for pixel scale of "
280 return last_event_matches;
284 uint32_t display_width()
const {
return display_width_; }
285 uint32_t display_height()
const {
return display_height_; }
287 std::string GetTestUIStackUrl()
override {
return kTestUIStackUrl; };
292class FlutterTapTest :
public FlutterTapTestBase {
294 void ExtendRealm()
override {
295 FML_LOG(INFO) <<
"Extending realm";
298 auto touch_input_listener_server =
299 std::make_unique<TouchInputListenerServer>(dispatcher());
301 realm_builder()->AddLocalChild(
302 kMockTouchInputListener, [touch_input_listener_server = std::move(
303 touch_input_listener_server)]()
mutable {
304 return std::move(touch_input_listener_server);
308 realm_builder()->AddChild(kTouchInputView, kTouchInputViewUrl,
309 component_testing::ChildOptions{
310 .environment = kFlutterRunnerEnvironment,
314 realm_builder()->AddRoute(
315 Route{.capabilities = {Protocol{
316 fuchsia::ui::test::input::TouchInputListener::Name_}},
317 .source = kMockTouchInputListenerRef,
318 .targets = {kFlutterJitRunnerRef, kTouchInputViewRef}});
320 realm_builder()->AddRoute(
321 Route{.capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}},
322 .source = kTouchInputViewRef,
323 .targets = {ParentRef()}});
327class FlutterEmbedTapTest :
public FlutterTapTestBase {
329 void SetUp()
override {
330 PortableUITest::SetUp(
false);
333 async::PostDelayedTask(
337 <<
"\n\n>> Test did not complete in time, terminating. <<\n\n";
342 void LaunchClientWithEmbeddedView() {
348 <<
"Waiting for display info from fuchsia.ui.display.singleton.Info";
349 std::optional<bool> display_metrics_obtained;
350 fuchsia::ui::display::singleton::InfoPtr display_info =
353 .Connect<fuchsia::ui::display::singleton::Info>();
354 display_info->GetMetrics([
this, &display_metrics_obtained](
auto info) {
355 display_width_ =
info.extent_in_px().width;
356 display_height_ =
info.extent_in_px().height;
357 display_metrics_obtained =
true;
359 RunLoopUntil([&display_metrics_obtained] {
360 return display_metrics_obtained.has_value();
364 FML_LOG(INFO) <<
"Registering input injection device";
365 RegisterTouchScreen();
367 PortableUITest::LaunchClientWithEmbeddedView();
377 void AddComponentArgument(std::string component_arg) {
378 auto config_directory_contents = DirectoryContents();
379 config_directory_contents.AddFile(
"args.csv", component_arg);
380 realm_builder()->RouteReadOnlyDirectory(
381 "config-data", {kEmbeddingFlutterViewRef},
382 std::move(config_directory_contents));
386 void ExtendRealm()
override {
387 FML_LOG(INFO) <<
"Extending realm";
390 auto touch_input_listener_server =
391 std::make_unique<TouchInputListenerServer>(dispatcher());
393 realm_builder()->AddLocalChild(
394 kMockTouchInputListener, [touch_input_listener_server = std::move(
395 touch_input_listener_server)]()
mutable {
396 return std::move(touch_input_listener_server);
400 realm_builder()->AddChild(kTouchInputView, kTouchInputViewUrl,
401 component_testing::ChildOptions{
402 .environment = kFlutterRunnerEnvironment,
406 realm_builder()->AddChild(kEmbeddingFlutterView, kEmbeddingFlutterViewUrl,
407 component_testing::ChildOptions{
408 .environment = kFlutterRunnerEnvironment,
412 realm_builder()->AddRoute(
413 Route{.capabilities = {Protocol{
414 fuchsia::ui::test::input::TouchInputListener::Name_}},
415 .source = kMockTouchInputListenerRef,
416 .targets = {kFlutterJitRunnerRef, kTouchInputViewRef,
417 kEmbeddingFlutterViewRef}});
419 realm_builder()->AddRoute(
420 Route{.capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}},
421 .source = kEmbeddingFlutterViewRef,
422 .targets = {ParentRef()}});
423 realm_builder()->AddRoute(
424 Route{.capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}},
425 .source = kTouchInputViewRef,
426 .targets = {kEmbeddingFlutterViewRef}});
430TEST_F(FlutterTapTest, FlutterTap) {
432 FML_LOG(INFO) <<
"Initializing scene";
434 FML_LOG(INFO) <<
"Client launched";
440 InjectTap(-500, -500);
443 RunLoopUntil([
this] {
444 return LastEventReceivedMatches(
445 static_cast<float>(display_width() / 4.0f),
446 static_cast<float>(display_height() / 4.0f),
451 ASSERT_EQ(touch_injection_request_count(), 1);
454TEST_F(FlutterEmbedTapTest, FlutterEmbedTap) {
456 FML_LOG(INFO) <<
"Initializing scene";
457 LaunchClientWithEmbeddedView();
458 FML_LOG(INFO) <<
"Client launched";
464 RunLoopUntil([
this] {
465 return LastEventReceivedMatches(
466 static_cast<float>(display_width() / 8.0f),
467 static_cast<float>(display_height() / 8.0f),
476 RunLoopUntil([
this] {
477 return LastEventReceivedMatches(
478 static_cast<float>(display_width() / (4.0f / 3.0f)),
479 static_cast<float>(display_height() / (4.0f / 3.0f)),
480 "embedding-flutter-view");
485 ASSERT_EQ(touch_injection_request_count(), 2);
488TEST_F(FlutterEmbedTapTest, FlutterEmbedOverlayEnabled) {
489 FML_LOG(INFO) <<
"Initializing scene";
490 AddComponentArgument(
"--showOverlay");
491 LaunchClientWithEmbeddedView();
492 FML_LOG(INFO) <<
"Client launched";
499 RunLoopUntil([
this] {
500 return LastEventReceivedMatches(
501 static_cast<float>(display_width() / 2.0f),
502 static_cast<float>(display_height() / 2.0f),
503 "embedding-flutter-view");
512 RunLoopUntil([
this] {
513 return LastEventReceivedMatches(
514 static_cast<float>(display_width() / 8.0f),
515 static_cast<float>(display_height() / 8.0f),
521 ASSERT_EQ(touch_injection_request_count(), 2);
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
#define FML_LOG(severity)
#define FML_CHECK(condition)
static float min(float r, float g, float b)
TEST_F(DisplayListTest, Defaults)
SIN Vec< N, float > abs(const Vec< N, float > &x)