From f80e5283b89316666b18175c1a827e55a67bc3dc Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 30 Nov 2022 18:18:32 +0000 Subject: [PATCH 1/6] Implement touch-input-test --- .../tests/integration/text-input/BUILD.gn | 17 +- .../text-input/meta/text-input-test.cml | 32 +- .../integration/text-input/text-input-test.cc | 309 +++++++----------- .../text-input/text-input-view/BUILD.gn | 15 +- .../text-input-view/lib/text_input_view.dart | 93 +++++- .../text-input-view/meta/text-input-view.cml | 17 +- .../integration/utils/portable_ui_test.cc | 35 +- .../integration/utils/portable_ui_test.h | 7 + .../fuchsia/devshell/run_integration_test.sh | 2 - 9 files changed, 294 insertions(+), 233 deletions(-) diff --git a/shell/platform/fuchsia/flutter/tests/integration/text-input/BUILD.gn b/shell/platform/fuchsia/flutter/tests/integration/text-input/BUILD.gn index 9fb5acaebaecd..b45ea72458b6b 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/text-input/BUILD.gn +++ b/shell/platform/fuchsia/flutter/tests/integration/text-input/BUILD.gn @@ -15,22 +15,22 @@ group("tests") { executable("text-input-test-bin") { testonly = true - output_name = "text-input-test" - sources = [ "text-input-test.cc" ] # This is needed for //third_party/googletest for linking zircon symbols. libs = [ "$fuchsia_sdk_path/arch/$target_cpu/sysroot/lib/libzircon.so" ] deps = [ - "$fuchsia_sdk_root/fidl:fuchsia.feedback", - "$fuchsia_sdk_root/fidl:fuchsia.logger", - "$fuchsia_sdk_root/fidl:fuchsia.sys", + "$fuchsia_sdk_root/fidl:fuchsia.accessibility.semantics", + "$fuchsia_sdk_root/fidl:fuchsia.component", + "$fuchsia_sdk_root/fidl:fuchsia.intl", + "$fuchsia_sdk_root/fidl:fuchsia.kernel", "$fuchsia_sdk_root/fidl:fuchsia.tracing.provider", "$fuchsia_sdk_root/fidl:fuchsia.ui.app", - "$fuchsia_sdk_root/fidl:fuchsia.ui.composition", "$fuchsia_sdk_root/fidl:fuchsia.ui.input", + "$fuchsia_sdk_root/fidl:fuchsia.ui.pointerinjector", + "$fuchsia_sdk_root/fidl:fuchsia.ui.policy", "$fuchsia_sdk_root/fidl:fuchsia.ui.scenic", "$fuchsia_sdk_root/fidl:fuchsia.ui.test.input", "$fuchsia_sdk_root/fidl:fuchsia.ui.test.scene", @@ -40,9 +40,12 @@ executable("text-input-test-bin") { "$fuchsia_sdk_root/pkg:scenic_cpp", "$fuchsia_sdk_root/pkg:sys_component_cpp_testing", "$fuchsia_sdk_root/pkg:zx", + "text-input-view:package", + "//build/fuchsia/fidl:fuchsia.ui.gfx", "//flutter/fml", "//flutter/shell/platform/fuchsia/flutter/tests/integration/utils:check_view", "//flutter/shell/platform/fuchsia/flutter/tests/integration/utils:color", + "//flutter/shell/platform/fuchsia/flutter/tests/integration/utils:portable_ui_test", "//flutter/shell/platform/fuchsia/flutter/tests/integration/utils:screenshot", "//third_party/googletest:gtest", "//third_party/googletest:gtest_main", @@ -52,7 +55,6 @@ executable("text-input-test-bin") { fuchsia_test_archive("text-input-test") { deps = [ ":text-input-test-bin", - "text-input-view:package", # "OOT" copies of the runners used by tests, to avoid conflicting with the # runners in the base fuchsia image. @@ -61,6 +63,5 @@ fuchsia_test_archive("text-input-test") { ] binary = "$target_name" - cml_file = rebase_path("meta/$target_name.cml") } diff --git a/shell/platform/fuchsia/flutter/tests/integration/text-input/meta/text-input-test.cml b/shell/platform/fuchsia/flutter/tests/integration/text-input/meta/text-input-test.cml index 6b97f55f426f3..b86b0f54772b6 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/text-input/meta/text-input-test.cml +++ b/shell/platform/fuchsia/flutter/tests/integration/text-input/meta/text-input-test.cml @@ -6,9 +6,6 @@ "gtest_runner.shard.cml", "sys/component/realm_builder_absolute.shard.cml", - "syslog/client.shard.cml", - "vulkan/client.shard.cml", - // This test needs both the vulkan facet and the hermetic-tier-2 facet, // so we are forced to make it a system test. "sys/testing/system-test.shard.cml", @@ -17,6 +14,13 @@ program: { binary: "bin/app", }, + use: [ + { + protocol: [ + "fuchsia.ui.test.input.KeyboardInputListener", + ] + } + ], offer: [ { protocol: [ @@ -26,21 +30,43 @@ "fuchsia.scheduler.ProfileProvider", "fuchsia.sysmem.Allocator", "fuchsia.tracing.provider.Registry", + "fuchsia.ui.input.ImeService", "fuchsia.vulkan.loader.Loader", + "fuchsia.ui.scenic.Scenic", + "fuchsia.ui.test.input.KeyboardInputListener", + "fuchsia.ui.input3.Keyboard", + "fuchsia.intl.PropertyProvider", + "fuchsia.posix.socket.Provider", + "fuchsia.ui.pointerinjector.Registry", + "fuchsia.fonts.Provider", + "fuchsia.feedback.CrashReportingProductRegister", + "fuchsia.settings.Keyboard", + "fuchsia.accessibility.semantics.SemanticsManager", ], from: "parent", to: "#realm_builder", }, + { + directory: "pkg", + subdir: "config", + as: "config-data", + from: "framework", + to: "#realm_builder", + }, ], // TODO(https://fxbug.dev/114584): Figure out how to bring these in as deps (if possible oot). facets: { "fuchsia.test": { "deprecated-allowed-packages": [ + "cursor", + "flatland-scene-manager-test-ui-stack", + "test-ui-stack", "oot_flutter_aot_runner", "oot_flutter_jit_runner", "oot_flutter_jit_product_runner", "oot_flutter_aot_product_runner", "test_manager", + "text-input-view", ], }, }, diff --git a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-test.cc b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-test.cc index 0fe3a47d5e78f..ba232d53e16f0 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-test.cc +++ b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-test.cc @@ -35,6 +35,7 @@ #include "flutter/fml/logging.h" #include "flutter/shell/platform/fuchsia/flutter/tests/integration/utils/check_view.h" +#include "flutter/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.h" namespace { @@ -49,216 +50,98 @@ using component_testing::RealmRoot; using component_testing::Route; using fuchsia_test_utils::CheckViewExistsInUpdates; +using fuchsia_test_utils::PortableUITest; /// Max timeout in failure cases. /// Set this as low as you can that still works across all test platforms. constexpr zx::duration kTimeout = zx::min(5); -// The FIDL bindings for these services are not exposed in the Fuchsia SDK so we -// must encode the names manually here. -constexpr auto kVulkanLoaderServiceName = "fuchsia.vulkan.loader.Loader"; -constexpr auto kProfileProviderServiceName = "fuchsia.sheduler.ProfileProvider"; +constexpr auto kKeyboardInputListener = "keyboard_input_listener"; +constexpr auto kKeyboardInputListenerRef = ChildRef{kKeyboardInputListener}; -constexpr auto kResponseListener = "test_text_response_listener"; constexpr auto kTextInputView = "text-input-view"; +constexpr auto kTextInputViewRef = ChildRef{kTextInputView}; static constexpr auto kTextInputViewUrl = - "fuchsia-pkg://fuchsia.com/view#meta/view-realm.cm"; + "fuchsia-pkg://fuchsia.com/text-input-view#meta/text-input-view.cm"; -constexpr auto kTestUiStack = "ui"; -constexpr auto kTestUiStackUrl = - "fuchsia-pkg://fuchsia.com/test-ui-stack#meta/test-ui-stack.cm"; +constexpr auto kTestUIStackUrl = + "fuchsia-pkg://fuchsia.com/flatland-scene-manager-test-ui-stack#meta/" + "test-ui-stack.cm"; -/// |ResponseListener| is a local test protocol that our test Flutter app uses -/// to let us know what text is being entered into its only text field. +/// |KeyboardInputListener| is a local test protocol that our test Flutter app +/// uses to let us know what text is being entered into its only text field. /// /// The text field contents are reported on almost every change, so if you are /// entering a long text, you will see calls corresponding to successive /// additions of characters, not just the end result. -class TestResponseListenerServer +class KeyboardInputListenerServer : public fuchsia::ui::test::input::KeyboardInputListener, public LocalComponent { public: - explicit TestResponseListenerServer(async_dispatcher_t* dispatcher) + explicit KeyboardInputListenerServer(async_dispatcher_t* dispatcher) : dispatcher_(dispatcher) {} - TestResponseListenerServer(const TestResponseListenerServer&) = delete; - TestResponseListenerServer& operator=(const TestResponseListenerServer&) = - delete; + // KeyboardInputListenerServer(const KeyboardInputListenerServer&) = delete; + // KeyboardInputListenerServer& operator=(const KeyboardInputListenerServer&) + // = + // delete; // |fuchsia::ui::test::input::KeyboardInputListener| void ReportTextInput( fuchsia::ui::test::input::KeyboardInputListenerReportTextInputRequest request) override { FML_LOG(INFO) << "Flutter app sent: '" << request.text() << "'"; - response_ = request.text(); + response_list_.push_back(request.text()); } /// Starts this server. - void Start(std::unique_ptr handles) override { - handles_ = std::move(handles); - - ASSERT_EQ(ZX_OK, handles_->outgoing()->AddPublicService( + void Start(std::unique_ptr local_handles) override { + FML_LOG(INFO) << "Starting KeyboardInputListenerServer"; + local_handles_ = std::move(local_handles); + ASSERT_EQ(ZX_OK, local_handles_->outgoing()->AddPublicService( bindings_.GetHandler(this, dispatcher_))); } - /// Returns true if the last response received matches `expected`. If a match - /// is found, the match is consumed, so a next call to HasResponse starts from - /// scratch. - bool HasResponse(const std::string& expected) { - bool match = response_.has_value() && response_.value() == expected; - if (match) { - response_ = std::nullopt; + /// Returns true if the response vector values matches `expected` + bool HasResponse(const std::vector& expected) { + if (response_list_.size() != expected.size()) { + return false; + } + + // Iterate through the expected vector + // Corresponding indices for response_list and expected should contain the + // same values + for (size_t i = 0; i < expected.size(); ++i) { + if (response_list_[i] != expected[i]) { + return false; + } } - return match; + + return true; } // KeyboardInputListener override - void ReportReady(ReportReadyCallback) override {} + void ReportReady(ReportReadyCallback callback) override { + FML_LOG(INFO) << "ReportReady callback ready"; + ready_ = true; + callback(); + } private: - // Not owned. async_dispatcher_t* dispatcher_ = nullptr; + std::unique_ptr local_handles_; fidl::BindingSet bindings_; - std::unique_ptr handles_; - std::optional response_; + std::vector response_list_; + bool ready_ = false; }; -class TextInputTest : public ::loop_fixture::RealLoop, public ::testing::Test { +class TextInputTest : public PortableUITest, + public ::testing::Test, + public ::testing::WithParamInterface { protected: - TextInputTest() - : test_response_listener_( - std::make_unique(dispatcher())) {} - - bool HasViewConnected( - const fuchsia::ui::observation::geometry::ViewTreeWatcherPtr& - view_tree_watcher, - std::optional& - watch_response, - zx_koid_t view_ref_koid) { - std::optional - watch_result; - view_tree_watcher->Watch( - [&watch_result](auto response) { watch_result = std::move(response); }); - FML_LOG(INFO) << "Waiting for view tree watch result"; - RunLoopUntil([&watch_result] { return watch_result.has_value(); }); - FML_LOG(INFO) << "Received for view tree watch result"; - if (CheckViewExistsInUpdates(watch_result->updates(), view_ref_koid)) { - watch_response = std::move(watch_result); - }; - return watch_response.has_value(); - } - - void RegisterKeyboard() { - FML_LOG(INFO) << "Registering fake keyboard"; - input_registry_ = - realm_root_->Connect(); - input_registry_.set_error_handler( - [](auto) { FML_LOG(ERROR) << "Error from input helper"; }); - bool keyboard_registered = false; - fuchsia::ui::test::input::RegistryRegisterKeyboardRequest request; - request.set_device(fake_keyboard_.NewRequest()); - input_registry_->RegisterKeyboard( - std::move(request), - [&keyboard_registered]() { keyboard_registered = true; }); - RunLoopUntil([&keyboard_registered] { return keyboard_registered; }); - FML_LOG(INFO) << "Keyboard registered"; - } - - void InitializeScene() { - // Instruct Scene Manager to present test's View. - std::optional view_ref_koid; - scene_provider_ = - realm_root_->Connect(); - scene_provider_.set_error_handler( - [](auto) { FML_LOG(ERROR) << "Error from test scene provider"; }); - fuchsia::ui::test::scene::ControllerAttachClientViewRequest request; - request.set_view_provider( - realm_root_->Connect()); - scene_provider_->RegisterViewTreeWatcher(view_tree_watcher_.NewRequest(), - []() {}); - scene_provider_->AttachClientView( - std::move(request), [&view_ref_koid](auto client_view_ref_koid) { - view_ref_koid = client_view_ref_koid; - }); - - FML_LOG(INFO) << "Waiting for client view ref koid"; - RunLoopUntil([&view_ref_koid] { return view_ref_koid.has_value(); }); - - // Wait for the client view to get attached to the view tree. - std::optional - watch_response; - FML_LOG(INFO) << "Waiting for client view to render"; - RunLoopUntil([this, &watch_response, &view_ref_koid] { - return HasViewConnected(view_tree_watcher_, watch_response, - *view_ref_koid); - }); - FML_LOG(INFO) << "Client view has rendered"; - } - - void BuildRealm() { - FML_LOG(INFO) << "Building realm"; - realm_builder_.AddChild(kTestUiStack, kTestUiStackUrl); - realm_builder_.AddLocalChild(kResponseListener, - test_response_listener_.get()); - realm_builder_.AddChild(kTextInputView, kTextInputViewUrl); - - // Route base system services to the view and the test UI stack. - realm_builder_.AddRoute(Route{ - .capabilities = {Protocol{fuchsia::logger::LogSink::Name_}, - Protocol{fuchsia::sys::Environment::Name_}, - Protocol{fuchsia::sysmem::Allocator::Name_}, - Protocol{fuchsia::tracing::provider::Registry::Name_}, - Protocol{kVulkanLoaderServiceName}, - Protocol{kProfileProviderServiceName}}, - .source = ParentRef{}, - .targets = {ChildRef{kTestUiStack}, ChildRef{kTextInputView}}}); - - // Expose fuchsia.ui.app.ViewProvider from the flutter app. - realm_builder_.AddRoute( - Route{.capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}}, - .source = ChildRef{kTextInputView}, - .targets = {ParentRef()}}); - - // Route UI capabilities from test-ui-stack to the flutter app. - realm_builder_.AddRoute(Route{ - .capabilities = {Protocol{fuchsia::ui::composition::Flatland::Name_}, - Protocol{fuchsia::ui::composition::Allocator::Name_}, - Protocol{fuchsia::ui::input::ImeService::Name_}, - Protocol{fuchsia::ui::input3::Keyboard::Name_}, - Protocol{fuchsia::ui::scenic::Scenic::Name_}}, - .source = ChildRef{kTestUiStack}, - .targets = {ChildRef{kTextInputView}}}); - - // Route UI helpers to test driver. - realm_builder_.AddRoute(Route{ - .capabilities = {Protocol{fuchsia::ui::test::input::Registry::Name_}, - Protocol{fuchsia::ui::test::scene::Controller::Name_}}, - .source = ChildRef{kTestUiStack}, - .targets = {ParentRef{}}}); - - // Route crash reporter service to flutter app. - realm_builder_.AddRoute( - {.capabilities = - { - Protocol{fuchsia::feedback::CrashReporter::Name_}, - }, - .source = ParentRef(), - .targets = {ChildRef{kTextInputView}}}); - - // Route text listener from the flutter app to the response listener. - realm_builder_.AddRoute(Route{ - .capabilities = - { - Protocol{ - fuchsia::ui::test::input::KeyboardInputListener::Name_}, - }, - .source = ChildRef{kResponseListener}, - .targets = {ChildRef{kTextInputView}}}); - - realm_root_ = std::make_unique(realm_builder_.Build()); - } - void SetUp() override { + PortableUITest::SetUp(); + // Post a "just in case" quit task, if the test hangs. async::PostDelayedTask( dispatcher(), @@ -268,39 +151,87 @@ class TextInputTest : public ::loop_fixture::RealLoop, public ::testing::Test { }, kTimeout); - BuildRealm(); + // Get the display dimensions. + FML_LOG(INFO) << "Waiting for scenic display info"; + scenic_ = realm_root()->template Connect(); + scenic_->GetDisplayInfo([this](fuchsia::ui::gfx::DisplayInfo display_info) { + display_width_ = display_info.width_in_px; + display_height_ = display_info.height_in_px; + FML_LOG(INFO) << "Got display_width = " << display_width_ + << " and display_height = " << display_height_; + }); + RunLoopUntil( + [this] { return display_width_ != 0 && display_height_ != 0; }); + // Register input injection device. + FML_LOG(INFO) << "Registering input injection device"; RegisterKeyboard(); - - InitializeScene(); } - std::unique_ptr test_response_listener_; + // Guaranteed to be initialized after SetUp(). + uint32_t display_width() const { return display_width_; } + uint32_t display_height() const { return display_height_; } + + std::string GetTestUIStackUrl() override { return GetParam(); }; + + std::unique_ptr keyboard_input_listener_server_; - fuchsia::ui::test::input::RegistryPtr input_registry_; - fuchsia::ui::test::input::KeyboardPtr fake_keyboard_; - fuchsia::ui::test::scene::ControllerPtr scene_provider_; - fuchsia::ui::observation::geometry::ViewTreeWatcherPtr view_tree_watcher_; + private: + void ExtendRealm() override { + FML_LOG(INFO) << "Extending realm"; + // Key part of service setup: have this test component vend the + // |TouchInputListener| service in the constructed realm. + keyboard_input_listener_server_ = + std::make_unique(dispatcher()); + + realm_builder()->AddLocalChild(kKeyboardInputListener, + keyboard_input_listener_server_.get()); + + // Add text-input-view to the Realm + realm_builder()->AddChild(kTextInputView, kTextInputViewUrl, + component_testing::ChildOptions{ + .environment = kFlutterRunnerEnvironment, + }); + + // Route KeyboardInputListener to the runner and Flutter app + realm_builder()->AddRoute( + Route{.capabilities = {Protocol{ + fuchsia::ui::test::input::KeyboardInputListener::Name_}}, + .source = kKeyboardInputListenerRef, + .targets = {kFlutterJitRunnerRef, kTextInputViewRef}}); + + // Expose fuchsia.ui.app.ViewProvider from the flutter app. + realm_builder()->AddRoute( + Route{.capabilities = {Protocol{fuchsia::ui::app::ViewProvider::Name_}}, + .source = kTextInputViewRef, + .targets = {ParentRef()}}); - RealmBuilder realm_builder_ = RealmBuilder::Create(); - std::unique_ptr realm_root_; + realm_builder()->AddRoute(Route{ + .capabilities = + {Protocol{fuchsia::ui::input3::Keyboard::Name_}, + Protocol{"fuchsia.accessibility.semantics.SemanticsManager"}}, + .source = kTestUIStackRef, + .targets = {ParentRef(), kFlutterJitRunnerRef}}); + } }; -TEST_F(TextInputTest, FlutterTextFieldEntry) { - FML_LOG(INFO) << "Wait for the initial text response"; - RunLoopUntil([&] { return test_response_listener_->HasResponse(""); }); +INSTANTIATE_TEST_SUITE_P(TextInputTestParameterized, + TextInputTest, + ::testing::Values(kTestUIStackUrl)); + +TEST_P(TextInputTest, TextInput) { + // Launch view + FML_LOG(INFO) << "Initializing scene"; + LaunchClient(); + FML_LOG(INFO) << "Client launched"; - FML_LOG(INFO) << "Sending a text message"; - bool done = false; - fuchsia::ui::test::input::KeyboardSimulateUsAsciiTextEntryRequest request; - request.set_text("Hello\nworld!"); - fake_keyboard_->SimulateUsAsciiTextEntry(std::move(request), - [&done]() { done = true; }); - RunLoopUntil([&] { return done; }); - FML_LOG(INFO) << "Message was sent"; + SimulateTextEntry("Hello\nworld!"); + std::vector expected = {"LEFT_SHIFT", "H", "E", "L", "L", "O", + "ENTER", "W", "O", "R", "L", "D", + "LEFT_SHIFT", "KEY_1"}; RunLoopUntil( - [&] { return test_response_listener_->HasResponse("Hello\nworld!"); }); + [&] { return keyboard_input_listener_server_->HasResponse(expected); }); } } // namespace diff --git a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/BUILD.gn b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/BUILD.gn index 24219ea200dfd..c1a857a01e1ac 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/BUILD.gn +++ b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/BUILD.gn @@ -9,13 +9,13 @@ import("//flutter/tools/fuchsia/gn-sdk/component.gni") import("//flutter/tools/fuchsia/gn-sdk/package.gni") dart_library("lib") { - testonly = true package_name = "text-input-view" sources = [ "text_input_view.dart" ] deps = [ "//flutter/shell/platform/fuchsia/dart:args", "//flutter/tools/fuchsia/dart:fuchsia_services", "//flutter/tools/fuchsia/dart:zircon", + "//flutter/tools/fuchsia/fidl:fuchsia.input", "//flutter/tools/fuchsia/fidl:fuchsia.ui.test.input", ] } @@ -26,21 +26,12 @@ flutter_component("component") { manifest = rebase_path("meta/text-input-view.cml") main_package = "text-input-view" main_dart = "text_input_view.dart" - deps = [ ":lib" ] -} -fuchsia_component("realm") { - testonly = true - manifest = "meta/text-input-view-realm.cml" - manifest_output_name = "text-input-view-realm.cm" - deps = [ ":component" ] + deps = [ ":lib" ] } fuchsia_package("package") { testonly = true package_name = "text-input-view" - deps = [ - ":component", - ":realm", - ] + deps = [ ":component" ] } diff --git a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart index 83e70afac3631..2ae1fe923f16f 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart +++ b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart @@ -8,14 +8,99 @@ import 'dart:convert'; import 'dart:typed_data'; -import 'dart:io'; import 'dart:ui'; import 'package:fidl_fuchsia_ui_test_input/fidl_async.dart' as test_text; import 'package:fuchsia_services/services.dart'; +import 'package:zircon/zircon.dart'; + +// Corresponds to the USB HID values provided in fidl.fuchsia.input +// https://fuchsia.dev/reference/fidl/fuchsia.input +final Map hidToKey = { + 458977: 'LEFT_SHIFT', // Keyboard Left Shift + 458792: 'ENTER', // Keyboard Enter (Return) + 458782: 'KEY_1', // Keyboard 1 and ! + 458763: 'H', // Keyboard h and H + 458760: 'E', // Keyboard e and E + 458767: 'L', // Keyboard l and L + 458770: 'O', // Keyboard o and O + 458778: 'W', // Keyboard w and W + 458773: 'R', // Keyboard r and R + 458759: 'D', // Keyboard d and D +}; int main() { - // TODO(https://fxbug.dev/107917): Port https://cs.opensource.google/fuchsia/fuchsia/+/main:src/ui/tests/integration_input_tests/text-input/text-input-flutter/lib/text-input-flutter.dart - // to dart:ui. - print('text-input-view: starting'); + print('Launching text-input-view'); + TestApp app = TestApp(); + app.run(); +} + +class TestApp { + static const _yellow = Color.fromARGB(255, 255, 255, 0); + // static const _pink = Color.fromARGB(255, 255, 0, 255); + Color _backgroundColor = _yellow; + + final _responseListener = test_text.KeyboardInputListenerProxy(); + + void run() { + // Connect to keyboard input response listener + Incoming.fromSvcPath().connectToService(_responseListener); + // Set up window callbacks + window.onPlatformMessage = (String name, ByteData data, PlatformMessageResponseCallback callback) { + this.platformMessage(name, data); + }; + window.onMetricsChanged = () { + window.scheduleFrame(); + }; + window.onBeginFrame = (Duration duration) { + this.beginFrame(duration); + }; + + window.scheduleFrame(); + } + + void beginFrame(Duration duration) { + // Convert physical screen size of device to values + final pixelRatio = window.devicePixelRatio; + final size = window.physicalSize / pixelRatio; + final physicalBounds = Offset.zero & size * pixelRatio; + final windowBounds = Offset.zero & size; + // Set up a Canvas that uses the screen size + final recorder = PictureRecorder(); + final canvas = Canvas(recorder, physicalBounds); + canvas.scale(pixelRatio, pixelRatio); + // Draw something + final paint = Paint()..color = this._backgroundColor; + canvas.drawRect(windowBounds, paint); + // Build the scene + final picture = recorder.endRecording(); + final sceneBuilder = SceneBuilder() + ..pushClipRect(physicalBounds) + ..addPicture(Offset.zero, picture) + ..pop(); + window.render(sceneBuilder.build()); + } + + void platformMessage(String name, ByteData data) async { + final buffer = data.buffer; + var list = buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + var decoded = utf8.decode(list); + var decodedJson = json.decode(decoded); + print('received ${name} platform message: ${decodedJson}'); + + if (name == "flutter/keyevent" && decodedJson["type"] == "keydown") { + if (hidToKey[decodedJson["hidUsage"]] != null) { + await _respond(test_text.KeyboardInputListenerReportTextInputRequest( + text: hidToKey[decodedJson["hidUsage"]], + )); + } + } + + window.scheduleFrame(); + } + + void _respond(test_text.KeyboardInputListenerReportTextInputRequest request) async { + print('text-input-view reporting keyboard input to KeyboardInputListener'); + await _responseListener.reportTextInput(request); + } } diff --git a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/meta/text-input-view.cml b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/meta/text-input-view.cml index 471f1c1c569b5..901c136829f40 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/meta/text-input-view.cml +++ b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/meta/text-input-view.cml @@ -4,7 +4,7 @@ { include: [ "syslog/client.shard.cml" ], program: { - data: "data/text_input_view", + data: "data/text-input-view", // Always use the jit runner for now. // TODO(fxbug.dev/106577): Implement manifest merging build rules for V2 components. @@ -12,26 +12,17 @@ }, capabilities: [ { - protocol: [ "fuchsia.ui.app.ViewProvider" ], + protocol: [ "fuchsia.ui.app.ViewProvider", "fuchsia.settings.Keyboard" ], }, ], use: [ { - protocol: [ - "fuchsia.logger.LogSink", - "fuchsia.sysmem.Allocator", - "fuchsia.tracing.provider.Registry", - "fuchsia.ui.input.ImeService", - "fuchsia.ui.input3.Keyboard", - "fuchsia.ui.scenic.Scenic", - "fuchsia.ui.test.input.KeyboardInputListener", - "fuchsia.vulkan.loader.Loader", - ], + protocol: [ "fuchsia.ui.test.input.KeyboardInputListener", "fuchsia.settings.Keyboard" ], }, ], expose: [ { - protocol: [ "fuchsia.ui.app.ViewProvider" ], + protocol: [ "fuchsia.ui.app.ViewProvider", "fuchsia.settings.Keyboard" ], from: "self", }, ], diff --git a/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.cc b/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.cc index 74085ba945248..bbbf2e2c36e76 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.cc +++ b/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.cc @@ -74,7 +74,6 @@ void PortableUITest::SetUpRealmBase() { Protocol{fuchsia::sysmem::Allocator::Name_}, Protocol{fuchsia::tracing::provider::Registry::Name_}, Protocol{fuchsia::ui::input::ImeService::Name_}, - Protocol{kPointerInjectorRegistryName}, Protocol{kPosixSocketProviderName}, Protocol{kVulkanLoaderServiceName}, component_testing::Directory{"config-data"}}, @@ -87,7 +86,8 @@ void PortableUITest::SetUpRealmBase() { Protocol{fuchsia::ui::composition::Flatland::Name_}, Protocol{fuchsia::ui::scenic::Scenic::Name_}, Protocol{fuchsia::ui::test::input::Registry::Name_}, - Protocol{fuchsia::ui::test::scene::Controller::Name_}}, + Protocol{fuchsia::ui::test::scene::Controller::Name_}, + Protocol{kPointerInjectorRegistryName}}, .source = kTestUIStackRef, .targets = {ParentRef(), kFlutterJitRunnerRef}}); } @@ -240,6 +240,24 @@ void PortableUITest::RegisterMouse() { FML_LOG(INFO) << "Mouse registered"; } +void PortableUITest::RegisterKeyboard() { + FML_LOG(INFO) << "Registering fake keyboard"; + input_registry_ = realm_->Connect(); + input_registry_.set_error_handler([](auto) { + FML_LOG(ERROR) << "Error from input helper: " << &zx_status_get_string; + }); + + bool keyboard_registered = false; + fuchsia::ui::test::input::RegistryRegisterKeyboardRequest request; + request.set_device(fake_keyboard_.NewRequest()); + input_registry_->RegisterKeyboard( + std::move(request), + [&keyboard_registered]() { keyboard_registered = true; }); + + RunLoopUntil([&keyboard_registered] { return keyboard_registered; }); + FML_LOG(INFO) << "Keyboard registered"; +} + void PortableUITest::InjectTap(int32_t x, int32_t y) { fuchsia::ui::test::input::TouchScreenSimulateTapRequest tap_request; tap_request.mutable_tap_location()->x = x; @@ -290,4 +308,17 @@ void PortableUITest::SimulateMouseScroll( }); } +void PortableUITest::SimulateTextEntry(std::string text) { + FML_LOG(INFO) << "Sending text request"; + bool done = false; + + fuchsia::ui::test::input::KeyboardSimulateUsAsciiTextEntryRequest request; + request.set_text(text); + fake_keyboard_->SimulateUsAsciiTextEntry(std::move(request), + [&done]() { done = true; }); + + RunLoopUntil([&] { return done; }); + FML_LOG(INFO) << "Text request sent"; +} + } // namespace fuchsia_test_utils diff --git a/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.h b/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.h index 42178a2ccfb54..f6a457d3bb8ce 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.h +++ b/shell/platform/fuchsia/flutter/tests/integration/utils/portable_ui_test.h @@ -65,6 +65,9 @@ class PortableUITest : public ::loop_fixture::RealLoop { // on both axes. void RegisterMouse(); + // Register a fake keyboard + void RegisterKeyboard(); + // Simulates a tap at location (x, y). void InjectTap(int32_t x, int32_t y); @@ -85,6 +88,9 @@ class PortableUITest : public ::loop_fixture::RealLoop { int scroll_y, bool use_physical_units = false); + // Helper method to simluate text input + void SimulateTextEntry(std::string text); + protected: component_testing::RealmBuilder* realm_builder() { return &realm_builder_; } component_testing::RealmRoot* realm_root() { return realm_.get(); } @@ -117,6 +123,7 @@ class PortableUITest : public ::loop_fixture::RealLoop { fuchsia::ui::test::input::RegistryPtr input_registry_; fuchsia::ui::test::input::TouchScreenPtr fake_touchscreen_; fuchsia::ui::test::input::MousePtr fake_mouse_; + fuchsia::ui::test::input::KeyboardPtr fake_keyboard_; fuchsia::ui::test::scene::ControllerPtr scene_provider_; fuchsia::ui::observation::geometry::ViewTreeWatcherPtr view_tree_watcher_; diff --git a/tools/fuchsia/devshell/run_integration_test.sh b/tools/fuchsia/devshell/run_integration_test.sh index e8fad8eb0c7b2..831f077a9ae80 100755 --- a/tools/fuchsia/devshell/run_integration_test.sh +++ b/tools/fuchsia/devshell/run_integration_test.sh @@ -55,8 +55,6 @@ case $test_name in test_packages=("flutter-embedder-test-0.far" "parent-view.far" "child-view.far") ;; text-input) - # TODO(https://fxbug.dev/107917): Finish implementing and remove this warning. - engine-warning "This test currently hangs because the Dart view hasn't been implemented yet. https://fxbug.dev/107917" test_packages=("text-input-test-0.far" "text-input-view.far") ;; touch-input) From ce92b9f43bdca829afc98ec34e72961b969203cc Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 30 Nov 2022 18:21:33 +0000 Subject: [PATCH 2/6] Cleanup build file --- .../text-input/text-input-view/BUILD.gn | 2 +- .../meta/text-input-view-realm.cml | 62 ------------------- 2 files changed, 1 insertion(+), 63 deletions(-) delete mode 100644 shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/meta/text-input-view-realm.cml diff --git a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/BUILD.gn b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/BUILD.gn index c1a857a01e1ac..885874dc0c771 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/BUILD.gn +++ b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/BUILD.gn @@ -9,13 +9,13 @@ import("//flutter/tools/fuchsia/gn-sdk/component.gni") import("//flutter/tools/fuchsia/gn-sdk/package.gni") dart_library("lib") { + testonly = true package_name = "text-input-view" sources = [ "text_input_view.dart" ] deps = [ "//flutter/shell/platform/fuchsia/dart:args", "//flutter/tools/fuchsia/dart:fuchsia_services", "//flutter/tools/fuchsia/dart:zircon", - "//flutter/tools/fuchsia/fidl:fuchsia.input", "//flutter/tools/fuchsia/fidl:fuchsia.ui.test.input", ] } diff --git a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/meta/text-input-view-realm.cml b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/meta/text-input-view-realm.cml deleted file mode 100644 index d95699769dcd0..0000000000000 --- a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/meta/text-input-view-realm.cml +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -{ - children: [ - { - name: "flutter_jit_runner", - url: "fuchsia-pkg://fuchsia.com/flutter_jit_runner#meta/flutter_jit_runner.cm", - }, - { - name: "text_input_view", - url: "#meta/text-input-view.cm", - environment: "#text_input_view_env", - }, - ], - offer: [ - { - protocol: [ - "fuchsia.logger.LogSink", - "fuchsia.memorypressure.Provider", - "fuchsia.posix.socket.Provider", - "fuchsia.sysmem.Allocator", - "fuchsia.tracing.provider.Registry", - "fuchsia.ui.composition.Allocator", - "fuchsia.ui.composition.Flatland", - "fuchsia.ui.input.ImeService", - "fuchsia.ui.input3.Keyboard", - "fuchsia.ui.scenic.Scenic", - "fuchsia.vulkan.loader.Loader", - ], - from: "parent", - to: [ - "#flutter_jit_runner", - "#text_input_view", - ], - }, - { - protocol: [ "fuchsia.ui.test.input.KeyboardInputListener" ], - from: "parent", - to: "#text_input_view", - }, - ], - expose: [ - { - protocol: [ "fuchsia.ui.app.ViewProvider" ], - from: "#text_input_view", - to: "parent", - }, - ], - environments: [ - { - name: "text_input_view_env", - extends: "realm", - runners: [ - { - runner: "flutter_jit_runner", - from: "#flutter_jit_runner", - }, - ], - }, - ], -} From 532a7c972d8b0bb48afcdaab5ed33068766cf9d4 Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 30 Nov 2022 18:22:43 +0000 Subject: [PATCH 3/6] Add text-input-test to test_suites --- testing/fuchsia/test_suites.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/testing/fuchsia/test_suites.yaml b/testing/fuchsia/test_suites.yaml index d417444918902..bfebfa2451c19 100644 --- a/testing/fuchsia/test_suites.yaml +++ b/testing/fuchsia/test_suites.yaml @@ -65,3 +65,8 @@ - mouse-input-test-0.far - oot_flutter_jit_runner-0.far - gen/flutter/shell/platform/fuchsia/flutter/tests/integration/mouse-input/mouse-input-view/mouse-input-view/mouse-input-view.far +- test_command: test run fuchsia-pkg://fuchsia.com/text-input-test#meta/text-input-test.cm + packages: + - text-input-test-0.far + - oot_flutter_jit_runner-0.far + - gen/flutter/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/text-input-view/text-input-view.far From 1cb33c484ecb837443a0b2787f4f066272e1fb63 Mon Sep 17 00:00:00 2001 From: Erik Date: Wed, 30 Nov 2022 18:28:25 +0000 Subject: [PATCH 4/6] Remove commented variable --- .../text-input/text-input-view/lib/text_input_view.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart index 2ae1fe923f16f..1eb42a26763d2 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart +++ b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart @@ -37,7 +37,6 @@ int main() { class TestApp { static const _yellow = Color.fromARGB(255, 255, 255, 0); - // static const _pink = Color.fromARGB(255, 255, 0, 255); Color _backgroundColor = _yellow; final _responseListener = test_text.KeyboardInputListenerProxy(); From a51961c69bc0956e05df038f86ed9552a22941df Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 5 Dec 2022 19:52:08 +0000 Subject: [PATCH 5/6] Cleanup variable names --- .../tests/integration/text-input/text-input-test.cc | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-test.cc b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-test.cc index ba232d53e16f0..88f94a28a3144 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-test.cc +++ b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-test.cc @@ -81,11 +81,6 @@ class KeyboardInputListenerServer explicit KeyboardInputListenerServer(async_dispatcher_t* dispatcher) : dispatcher_(dispatcher) {} - // KeyboardInputListenerServer(const KeyboardInputListenerServer&) = delete; - // KeyboardInputListenerServer& operator=(const KeyboardInputListenerServer&) - // = - // delete; - // |fuchsia::ui::test::input::KeyboardInputListener| void ReportTextInput( fuchsia::ui::test::input::KeyboardInputListenerReportTextInputRequest @@ -180,7 +175,7 @@ class TextInputTest : public PortableUITest, void ExtendRealm() override { FML_LOG(INFO) << "Extending realm"; // Key part of service setup: have this test component vend the - // |TouchInputListener| service in the constructed realm. + // |KeyboardInputListener| service in the constructed realm. keyboard_input_listener_server_ = std::make_unique(dispatcher()); From 42b87410d88d6681283ced1566804771fc4b06ed Mon Sep 17 00:00:00 2001 From: Erik Date: Tue, 6 Dec 2022 18:27:02 +0000 Subject: [PATCH 6/6] Rename platformMessage callback --- .../text-input/text-input-view/lib/text_input_view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart index 1eb42a26763d2..c460a309b6ae6 100644 --- a/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart +++ b/shell/platform/fuchsia/flutter/tests/integration/text-input/text-input-view/lib/text_input_view.dart @@ -46,7 +46,7 @@ class TestApp { Incoming.fromSvcPath().connectToService(_responseListener); // Set up window callbacks window.onPlatformMessage = (String name, ByteData data, PlatformMessageResponseCallback callback) { - this.platformMessage(name, data); + this.decodeAndReportPlatformMessage(name, data); }; window.onMetricsChanged = () { window.scheduleFrame(); @@ -80,7 +80,7 @@ class TestApp { window.render(sceneBuilder.build()); } - void platformMessage(String name, ByteData data) async { + void decodeAndReportPlatformMessage(String name, ByteData data) async { final buffer = data.buffer; var list = buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); var decoded = utf8.decode(list);