Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit d91e208

Browse files
authored
Port touch-based tests from embedder integration test (#38234)
* Port touch-based tests from embedder integration test * Remove RegisterTouchScreen and related variables * Update embedded child view size
1 parent 81b4535 commit d91e208

File tree

7 files changed

+229
-171
lines changed

7 files changed

+229
-171
lines changed

shell/platform/fuchsia/flutter/tests/integration/embedder/flutter-embedder-test.cc

Lines changed: 0 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,8 @@ constexpr auto kTestUiStackRef = ChildRef{kTestUiStack};
8686

8787
constexpr fuchsia_test_utils::Color kParentBackgroundColor = {0x00, 0x00, 0xFF,
8888
0xFF}; // Blue
89-
constexpr fuchsia_test_utils::Color kParentTappedColor = {0x00, 0x00, 0x00,
90-
0xFF}; // Black
9189
constexpr fuchsia_test_utils::Color kChildBackgroundColor = {0xFF, 0x00, 0xFF,
9290
0xFF}; // Pink
93-
constexpr fuchsia_test_utils::Color kChildTappedColor = {0xFF, 0xFF, 0x00,
94-
0xFF}; // Yellow
9591

9692
// TODO(fxb/64201): Remove forced opacity colors when Flatland is enabled.
9793
constexpr fuchsia_test_utils::Color kOverlayBackgroundColor1 = {
@@ -160,60 +156,18 @@ class FlutterEmbedderTest : public ::loop_fixture::RealLoop,
160156
callback = nullptr,
161157
zx::duration timeout = kTestTimeout);
162158

163-
// Simulates a tap at location (x, y).
164-
void InjectTap(int32_t x, int32_t y);
165-
166-
// Injects an input event, and posts a task to retry after
167-
// `kTapRetryInterval`.
168-
//
169-
// We post the retry task because the first input event we send to Flutter may
170-
// be lost. The reason the first event may be lost is that there is a race
171-
// condition as the scene owner starts up.
172-
//
173-
// More specifically: in order for our app
174-
// to receive the injected input, two things must be true before we inject
175-
// touch input:
176-
// * The Scenic root view must have been installed, and
177-
// * The Input Pipeline must have received a viewport to inject touch into.
178-
//
179-
// The problem we have is that the `is_rendering` signal that we monitor only
180-
// guarantees us the view is ready. If the viewport is not ready in Input
181-
// Pipeline at that time, it will drop the touch event.
182-
//
183-
// TODO(fxbug.dev/96986): Improve synchronization and remove retry logic.
184-
void TryInject(int32_t x, int32_t y);
185-
186159
private:
187160
fuchsia::ui::scenic::Scenic* scenic() { return scenic_.get(); }
188161

189162
void SetUpRealmBase();
190163

191-
// Registers a fake touch screen device with an injection coordinate space
192-
// spanning [-1000, 1000] on both axes.
193-
void RegisterTouchScreen();
194-
195164
fuchsia::ui::scenic::ScenicPtr scenic_;
196-
fuchsia::ui::test::input::RegistryPtr input_registry_;
197-
fuchsia::ui::test::input::TouchScreenPtr fake_touchscreen_;
198165
fuchsia::ui::test::scene::ControllerPtr scene_provider_;
199166
fuchsia::ui::observation::geometry::ViewTreeWatcherPtr view_tree_watcher_;
200167

201168
// Wrapped in optional since the view is not created until the middle of SetUp
202169
component_testing::RealmBuilder realm_builder_;
203170
std::unique_ptr<component_testing::RealmRoot> realm_;
204-
205-
// The typical latency on devices we've tested is ~60 msec. The retry interval
206-
// is chosen to be a) Long enough that it's unlikely that we send a new tap
207-
// while a previous tap is still being
208-
// processed. That is, it should be far more likely that a new tap is sent
209-
// because the first tap was lost, than because the system is just running
210-
// slowly.
211-
// b) Short enough that we don't slow down tryjobs.
212-
//
213-
// The first property is important to avoid skewing the latency metrics that
214-
// we collect. For an explanation of why a tap might be lost, see the
215-
// documentation for TryInject().
216-
static constexpr auto kTapRetryInterval = zx::sec(1);
217171
};
218172

219173
void FlutterEmbedderTest::SetUpRealmBase() {
@@ -374,9 +328,6 @@ void FlutterEmbedderTest::LaunchParentViewInRealm(
374328
}
375329
realm_ = std::make_unique<RealmRoot>(realm_builder_.Build());
376330

377-
// Register fake touch screen device.
378-
RegisterTouchScreen();
379-
380331
// Instruct Test UI Stack to present parent-view's View.
381332
std::optional<zx_koid_t> view_ref_koid;
382333
scene_provider_ = realm_->Connect<fuchsia::ui::test::scene::Controller>();
@@ -443,36 +394,6 @@ bool FlutterEmbedderTest::TakeScreenshotUntil(
443394
timeout);
444395
}
445396

446-
void FlutterEmbedderTest::RegisterTouchScreen() {
447-
FML_LOG(INFO) << "Registering fake touch screen";
448-
input_registry_ = realm_->Connect<fuchsia::ui::test::input::Registry>();
449-
input_registry_.set_error_handler(
450-
[](auto) { FML_LOG(ERROR) << "Error from input helper"; });
451-
bool touchscreen_registered = false;
452-
fuchsia::ui::test::input::RegistryRegisterTouchScreenRequest request;
453-
request.set_device(fake_touchscreen_.NewRequest());
454-
input_registry_->RegisterTouchScreen(
455-
std::move(request),
456-
[&touchscreen_registered]() { touchscreen_registered = true; });
457-
RunLoopUntil([&touchscreen_registered] { return touchscreen_registered; });
458-
FML_LOG(INFO) << "Touchscreen registered";
459-
}
460-
461-
void FlutterEmbedderTest::InjectTap(int32_t x, int32_t y) {
462-
fuchsia::ui::test::input::TouchScreenSimulateTapRequest tap_request;
463-
tap_request.mutable_tap_location()->x = x;
464-
tap_request.mutable_tap_location()->y = y;
465-
fake_touchscreen_->SimulateTap(std::move(tap_request), [x, y]() {
466-
FML_LOG(INFO) << "Tap injected at (" << x << ", " << y << ")";
467-
});
468-
}
469-
470-
void FlutterEmbedderTest::TryInject(int32_t x, int32_t y) {
471-
InjectTap(x, y);
472-
async::PostDelayedTask(
473-
dispatcher(), [this, x, y] { TryInject(x, y); }, kTapRetryInterval);
474-
}
475-
476397
TEST_F(FlutterEmbedderTest, Embedding) {
477398
LaunchParentViewInRealm();
478399

@@ -489,53 +410,6 @@ TEST_F(FlutterEmbedderTest, Embedding) {
489410
}));
490411
}
491412

492-
TEST_F(FlutterEmbedderTest, HittestEmbedding) {
493-
LaunchParentViewInRealm();
494-
495-
// Take screenshot until we see the child-view's embedded color.
496-
ASSERT_TRUE(TakeScreenshotUntil(kChildBackgroundColor));
497-
498-
// Simulate a tap at the center of the child view.
499-
TryInject(/* x = */ 0, /* y = */ 0);
500-
501-
// Take screenshot until we see the child-view's tapped color.
502-
ASSERT_TRUE(TakeScreenshotUntil(
503-
kChildTappedColor,
504-
[](std::map<fuchsia_test_utils::Color, size_t> histogram) {
505-
// Expect parent and child background colors, with parent color > child
506-
// color.
507-
EXPECT_GT(histogram[kParentBackgroundColor], 0u);
508-
EXPECT_EQ(histogram[kChildBackgroundColor], 0u);
509-
EXPECT_GT(histogram[kChildTappedColor], 0u);
510-
EXPECT_GT(histogram[kParentBackgroundColor],
511-
histogram[kChildTappedColor]);
512-
}));
513-
}
514-
515-
TEST_F(FlutterEmbedderTest, HittestDisabledEmbedding) {
516-
LaunchParentViewInRealm({"--no-hitTestable"});
517-
518-
// Take screenshots until we see the child-view's embedded color.
519-
ASSERT_TRUE(TakeScreenshotUntil(kChildBackgroundColor));
520-
521-
// Simulate a tap at the center of the child view.
522-
TryInject(/* x = */ 0, /* y = */ 0);
523-
524-
// The parent-view should change color.
525-
ASSERT_TRUE(TakeScreenshotUntil(
526-
kParentTappedColor,
527-
[](std::map<fuchsia_test_utils::Color, size_t> histogram) {
528-
// Expect parent and child background colors, with parent color > child
529-
// color.
530-
EXPECT_EQ(histogram[kParentBackgroundColor], 0u);
531-
EXPECT_GT(histogram[kParentTappedColor], 0u);
532-
EXPECT_GT(histogram[kChildBackgroundColor], 0u);
533-
EXPECT_EQ(histogram[kChildTappedColor], 0u);
534-
EXPECT_GT(histogram[kParentTappedColor],
535-
histogram[kChildBackgroundColor]);
536-
}));
537-
}
538-
539413
TEST_F(FlutterEmbedderTest, EmbeddingWithOverlay) {
540414
LaunchParentViewInRealm({"--showOverlay"});
541415

@@ -555,33 +429,4 @@ TEST_F(FlutterEmbedderTest, EmbeddingWithOverlay) {
555429
}));
556430
}
557431

558-
TEST_F(FlutterEmbedderTest, HittestEmbeddingWithOverlay) {
559-
LaunchParentViewInRealm({"--showOverlay"});
560-
561-
// Take screenshot until we see the child-view's embedded color.
562-
ASSERT_TRUE(TakeScreenshotUntil(kChildBackgroundColor));
563-
564-
// The bottom-left corner of the overlay is at the center of the screen,
565-
// which is at (0, 0) in the injection coordinate space. Inject a pointer
566-
// event just outside the overlay's bounds, and ensure that it goes to the
567-
// embedded view.
568-
TryInject(/* x = */ -1, /* y = */ 1);
569-
570-
// Take screenshot until we see the child-view's tapped color.
571-
ASSERT_TRUE(TakeScreenshotUntil(
572-
kChildTappedColor,
573-
[](std::map<fuchsia_test_utils::Color, size_t> histogram) {
574-
// Expect parent, overlay and child background colors.
575-
// With parent color > child color and overlay color > child color.
576-
const size_t overlay_pixel_count = OverlayPixelCount(histogram);
577-
EXPECT_GT(histogram[kParentBackgroundColor], 0u);
578-
EXPECT_GT(overlay_pixel_count, 0u);
579-
EXPECT_EQ(histogram[kChildBackgroundColor], 0u);
580-
EXPECT_GT(histogram[kChildTappedColor], 0u);
581-
EXPECT_GT(histogram[kParentBackgroundColor],
582-
histogram[kChildTappedColor]);
583-
EXPECT_GT(overlay_pixel_count, histogram[kChildTappedColor]);
584-
}));
585-
}
586-
587432
} // namespace flutter_embedder_test

shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ dart_library("lib") {
1313
sources = [ "embedding-flutter-view.dart" ]
1414

1515
deps = [
16+
"//flutter/shell/platform/fuchsia/dart:args",
17+
"//flutter/shell/platform/fuchsia/dart:vector_math",
1618
"//flutter/tools/fuchsia/dart:fuchsia_services",
1719
"//flutter/tools/fuchsia/dart:zircon",
1820
"//flutter/tools/fuchsia/fidl:fuchsia.ui.app",

shell/platform/fuchsia/flutter/tests/integration/touch-input/embedding-flutter-view/lib/embedding-flutter-view.dart

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,37 @@ import 'dart:typed_data';
77
import 'dart:io';
88
import 'dart:ui';
99

10+
import 'package:args/args.dart';
1011
import 'package:fidl_fuchsia_ui_app/fidl_async.dart';
1112
import 'package:fidl_fuchsia_ui_views/fidl_async.dart';
1213
import 'package:fidl_fuchsia_ui_test_input/fidl_async.dart' as test_touch;
1314
import 'package:fuchsia_services/services.dart';
15+
import 'package:vector_math/vector_math_64.dart' as vector_math_64;
1416
import 'package:zircon/zircon.dart';
1517

18+
final _argsCsvFilePath = '/config/data/args.csv';
19+
1620
void main(List<String> args) {
1721
print('Launching embedding-flutter-view');
18-
TestApp app = TestApp(ChildView.gfx(_launchGfxChildView()));
22+
23+
args = args + _GetArgsFromConfigFile();
24+
final parser = ArgParser()
25+
..addFlag('showOverlay', defaultsTo: false)
26+
..addFlag('hitTestable', defaultsTo: true)
27+
..addFlag('focusable', defaultsTo: true);
28+
29+
final arguments = parser.parse(args);
30+
for (final option in arguments.options) {
31+
print('embedding-flutter-view args: $option: ${arguments[option]}');
32+
}
33+
34+
TestApp app = TestApp(
35+
ChildView.gfx(_launchGfxChildView()),
36+
showOverlay: arguments['showOverlay'],
37+
hitTestable: arguments['hitTestable'],
38+
focusable: arguments['focusable'],
39+
);
40+
1941
app.run();
2042
}
2143

@@ -24,14 +46,22 @@ class TestApp {
2446
static const _blue = Color.fromARGB(255, 0, 0, 255);
2547

2648
final ChildView childView;
49+
final bool showOverlay;
50+
final bool hitTestable;
51+
final bool focusable;
2752
final _responseListener = test_touch.TouchInputListenerProxy();
2853

2954
Color _backgroundColor = _blue;
3055

31-
TestApp(this.childView) {}
56+
TestApp(
57+
this.childView,
58+
{this.showOverlay = false,
59+
this.hitTestable = true,
60+
this.focusable = true}) {
61+
}
3262

3363
void run() {
34-
childView.create((ByteData reply) {
64+
childView.create(hitTestable, focusable, (ByteData reply) {
3565
// Set up window callbacks.
3666
window.onPointerDataPacket = (PointerDataPacket packet) {
3767
this.pointerDataPacket(packet);
@@ -67,13 +97,52 @@ class TestApp {
6797
final sceneBuilder = SceneBuilder()
6898
..pushClipRect(physicalBounds)
6999
..addPicture(Offset.zero, picture);
70-
// Child view should take up half the screen
71-
final childPhysicalSize = window.physicalSize * 0.5;
100+
101+
final childPhysicalSize = window.physicalSize * 0.25;
102+
// Alignment.center
103+
final windowCenter = size.center(Offset.zero);
104+
final windowPhysicalCenter = window.physicalSize.center(Offset.zero);
105+
final childPhysicalOffset = windowPhysicalCenter - childPhysicalSize.center(Offset.zero);
106+
72107
sceneBuilder
108+
..pushTransform(
109+
vector_math_64.Matrix4.translationValues(childPhysicalOffset.dx,
110+
childPhysicalOffset.dy,
111+
0.0).storage)
73112
..addPlatformView(childView.viewId,
74113
width: childPhysicalSize.width,
75-
height: size.height)
114+
height: childPhysicalSize.height)
76115
..pop();
116+
117+
if (showOverlay) {
118+
final containerSize = size * 0.5;
119+
// Alignment.center
120+
final containerOffset = windowCenter - containerSize.center(Offset.zero);
121+
122+
final overlaySize = containerSize * 0.5;
123+
// Alignment.topRight
124+
final overlayOffset = Offset(
125+
containerOffset.dx + containerSize.width - overlaySize.width,
126+
containerOffset.dy);
127+
final overlayPhysicalSize = overlaySize * pixelRatio;
128+
final overlayPhysicalOffset = overlayOffset * pixelRatio;
129+
final overlayPhysicalBounds = overlayPhysicalOffset & overlayPhysicalSize;
130+
131+
final recorder = PictureRecorder();
132+
final overlayCullRect = Offset.zero & overlayPhysicalSize; // in canvas physical coordinates
133+
final canvas = Canvas(recorder, overlayCullRect);
134+
canvas.scale(pixelRatio);
135+
136+
final paint = Paint()..color = Color.fromARGB(255, 0, 255, 0);
137+
canvas.drawRect(Offset.zero & overlaySize, paint);
138+
139+
final overlayPicture = recorder.endRecording();
140+
sceneBuilder
141+
..pushClipRect(overlayPhysicalBounds) // in window physical coordinates
142+
..addPicture(overlayPhysicalOffset, overlayPicture)
143+
..pop();
144+
}
145+
77146
sceneBuilder.pop();
78147
window.render(sceneBuilder.build());
79148
}
@@ -124,15 +193,18 @@ class ChildView {
124193
assert(viewId != null);
125194
}
126195

127-
void create(PlatformMessageResponseCallback callback) {
196+
void create(
197+
bool hitTestable,
198+
bool focusable,
199+
PlatformMessageResponseCallback callback) {
128200
// Construct the dart:ui platform message to create the view, and when the
129201
// return callback is invoked, build the scene. At that point, it is safe
130202
// to embed the child view in the scene.
131203
final viewOcclusionHint = Rect.zero;
132204
final Map<String, dynamic> args = <String, dynamic>{
133205
'viewId': viewId,
134-
'hitTestable': true,
135-
'focusable': true,
206+
'hitTestable': hitTestable,
207+
'focusable': focusable,
136208
'viewOcclusionHintLTRB': <double>[
137209
viewOcclusionHint.left,
138210
viewOcclusionHint.top,
@@ -173,3 +245,14 @@ ViewHolderToken _launchGfxChildView() {
173245

174246
return viewHolderToken;
175247
}
248+
249+
List<String> _GetArgsFromConfigFile() {
250+
List<String> args;
251+
final f = File(_argsCsvFilePath);
252+
if (!f.existsSync()) {
253+
return List.empty();
254+
}
255+
final fileContentCsv = f.readAsStringSync();
256+
args = fileContentCsv.split('\n');
257+
return args;
258+
}

0 commit comments

Comments
 (0)