| // 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. |
| |
| #include <fuchsia/ui/pointerinjector/cpp/fidl.h> |
| #include <fuchsia/ui/views/cpp/fidl.h> |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/async/cpp/task.h> |
| |
| #include <lib/fidl/cpp/binding_set.h> |
| #include <lib/ui/scenic/cpp/view_ref_pair.h> |
| |
| #include "pointer_injector_delegate.h" |
| #include "tests/fakes/mock_injector_registry.h" |
| #include "tests/fakes/platform_message.h" |
| |
| namespace flutter_runner::testing { |
| |
| using fup_DeviceType = fuchsia::ui::pointerinjector::DeviceType; |
| using fup_DispatchPolicy = fuchsia::ui::pointerinjector::DispatchPolicy; |
| using fup_EventPhase = fuchsia::ui::pointerinjector::EventPhase; |
| using fup_RegistryHandle = fuchsia::ui::pointerinjector::RegistryHandle; |
| using fuv_ViewRef = fuchsia::ui::views::ViewRef; |
| |
| namespace { |
| |
| // clang-format off |
| static constexpr std::array<float, 9> kIdentityMatrix = { |
| 1, 0, 0, // column one |
| 0, 1, 0, // column two |
| 0, 0, 1, // column three |
| }; |
| // clang-format on |
| |
| rapidjson::Value ParsePlatformMessage(std::string json) { |
| rapidjson::Document document; |
| document.Parse(json); |
| if (document.HasParseError() || !document.IsObject()) { |
| FML_LOG(ERROR) << "Could not parse document"; |
| return rapidjson::Value(); |
| } |
| return document.GetObject(); |
| } |
| |
| zx_koid_t ExtractKoid(const zx::object_base& object) { |
| zx_info_handle_basic_t info{}; |
| if (object.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, |
| nullptr) != ZX_OK) { |
| return ZX_KOID_INVALID; // no info |
| } |
| |
| return info.koid; |
| } |
| |
| zx_koid_t ExtractKoid(const fuv_ViewRef& view_ref) { |
| return ExtractKoid(view_ref.reference); |
| } |
| |
| class PlatformMessageBuilder { |
| public: |
| PlatformMessageBuilder& SetViewId(uint64_t view_id) { |
| view_id_ = view_id; |
| return *this; |
| } |
| |
| PlatformMessageBuilder& SetPointerX(float x) { |
| pointer_x_ = x; |
| return *this; |
| } |
| |
| PlatformMessageBuilder& SetPointerY(float y) { |
| pointer_y_ = y; |
| return *this; |
| } |
| |
| PlatformMessageBuilder& SetPhase(int phase) { |
| phase_ = phase; |
| return *this; |
| } |
| |
| PlatformMessageBuilder& SetPointerId(int pointer_id) { |
| pointer_id_ = pointer_id; |
| return *this; |
| } |
| |
| PlatformMessageBuilder& SetTraceFlowId(int trace_flow_id) { |
| trace_flow_id_ = trace_flow_id; |
| return *this; |
| } |
| |
| PlatformMessageBuilder& SetViewRefMaybe(std::optional<fuv_ViewRef> view_ref) { |
| if (view_ref.has_value()) { |
| view_ref_ = std::move(*view_ref); |
| } |
| return *this; |
| } |
| |
| PlatformMessageBuilder& SetLogicalWidth(float width) { |
| width_ = width; |
| return *this; |
| } |
| |
| PlatformMessageBuilder& SetLogicalHeight(float height) { |
| height_ = height; |
| return *this; |
| } |
| |
| PlatformMessageBuilder& SetTimestamp(int timestamp) { |
| timestamp_ = timestamp; |
| return *this; |
| } |
| |
| rapidjson::Value Build() { |
| std::ostringstream message; |
| message << "{" |
| << " \"method\":\"" |
| << PointerInjectorDelegate::kPointerInjectorMethodPrefix << "\"," |
| << " \"args\": {" |
| << " \"viewId\":" << view_id_ << "," |
| << " \"x\":" << pointer_x_ << "," |
| << " \"y\":" << pointer_y_ << "," |
| << " \"phase\":" << phase_ << "," |
| << " \"pointerId\":" << pointer_id_ << "," |
| << " \"traceFlowId\":" << trace_flow_id_ << "," |
| << " \"viewRef\":" << view_ref_.reference.get() << "," |
| << " \"logicalWidth\":" << width_ << "," |
| << " \"logicalHeight\":" << height_ << "," |
| << " \"timestamp\":" << timestamp_ << " }" |
| << "}"; |
| return ParsePlatformMessage(message.str()); |
| } |
| |
| private: |
| uint64_t view_id_ = 0; |
| float pointer_x_ = 0.f, pointer_y_ = 0.f; |
| int phase_ = 1, pointer_id_ = 0, trace_flow_id_ = 0; |
| fuv_ViewRef view_ref_; |
| float width_ = 0.f, height_ = 0.f; |
| int timestamp_ = 0; |
| }; |
| |
| } // namespace |
| |
| class PointerInjectorDelegateTest : public ::testing::Test, |
| public ::testing::WithParamInterface<bool> { |
| protected: |
| PointerInjectorDelegateTest() |
| : loop_(&kAsyncLoopConfigAttachToCurrentThread) {} |
| |
| // TODO(fxbug.dev/104285): Replace the RunLoop methods with the one provided |
| // by the sdk. |
| void RunLoopUntilIdle() { loop_.RunUntilIdle(); } |
| |
| bool RunGivenLoopWithTimeout(async::Loop* loop, zx::duration timeout) { |
| // This cannot be a local variable because the delayed task below can |
| // execute after this function returns. |
| auto canceled = std::make_shared<bool>(false); |
| bool timed_out = false; |
| async::PostDelayedTask( |
| loop->dispatcher(), |
| [loop, canceled, &timed_out] { |
| if (*canceled) { |
| return; |
| } |
| timed_out = true; |
| loop->Quit(); |
| }, |
| timeout); |
| loop->Run(); |
| loop->ResetQuit(); |
| |
| if (!timed_out) { |
| *canceled = true; |
| } |
| return timed_out; |
| } |
| |
| bool RunLoopWithTimeoutOrUntil(fit::function<bool()> condition, |
| zx::duration timeout, |
| zx::duration step) { |
| const zx::time timeout_deadline = zx::deadline_after(timeout); |
| |
| while (zx::clock::get_monotonic() < timeout_deadline && |
| loop_.GetState() == ASYNC_LOOP_RUNNABLE) { |
| if (condition()) { |
| loop_.ResetQuit(); |
| return true; |
| } |
| |
| if (step == zx::duration::infinite()) { |
| // Performs a single unit of work, possibly blocking until there is work |
| // to do or the timeout deadline arrives. |
| loop_.Run(timeout_deadline, true); |
| } else { |
| // Performs work until the step deadline arrives. |
| RunGivenLoopWithTimeout(&loop_, step); |
| } |
| } |
| |
| loop_.ResetQuit(); |
| return condition(); |
| } |
| |
| void RunLoopUntil(fit::function<bool()> condition, |
| zx::duration step = zx::msec(10)) { |
| RunLoopWithTimeoutOrUntil(std::move(condition), zx::duration::infinite(), |
| step); |
| } |
| |
| void SetUp() override { |
| auto view_ref_pair = scenic::ViewRefPair::New(); |
| |
| host_view_ref_ = std::move(view_ref_pair.view_ref); |
| |
| fup_RegistryHandle registry; |
| registry_ = std::make_unique<MockInjectorRegistry>(registry.NewRequest()); |
| |
| fuv_ViewRef host_view_ref_clone; |
| fidl::Clone(host_view_ref_, &host_view_ref_clone); |
| |
| is_flatland_ = GetParam(); |
| pointer_injector_delegate_ = std::make_unique<PointerInjectorDelegate>( |
| std::move(registry), std::move(host_view_ref_clone), is_flatland_); |
| } |
| |
| void CreateView(uint64_t view_id, |
| std::optional<fuv_ViewRef> view_ref = std::nullopt) { |
| if (!is_flatland_) { |
| pointer_injector_delegate_->OnCreateView(view_id); |
| } else { |
| fuv_ViewRef ref; |
| if (view_ref.has_value()) { |
| ref = std::move(*view_ref); |
| } else { |
| auto view_ref_pair = scenic::ViewRefPair::New(); |
| ref = std::move(view_ref_pair.view_ref); |
| } |
| pointer_injector_delegate_->OnCreateView(view_id, std::move(ref)); |
| } |
| } |
| |
| std::unique_ptr<PointerInjectorDelegate> pointer_injector_delegate_; |
| std::unique_ptr<MockInjectorRegistry> registry_; |
| fuv_ViewRef host_view_ref_; |
| bool is_flatland_ = false; |
| |
| private: |
| async::Loop loop_; |
| }; |
| |
| TEST_P(PointerInjectorDelegateTest, IncorrectPlatformMessage_ShouldFail) { |
| const uint64_t view_id = 1; |
| |
| // Create a view. |
| CreateView(view_id); |
| |
| // A platform message in incorrect JSON format should fail. |
| { |
| auto response = FakePlatformMessageResponse::Create(); |
| |
| EXPECT_FALSE(pointer_injector_delegate_->HandlePlatformMessage( |
| ParsePlatformMessage("{Incorrect Json}"), response)); |
| } |
| |
| // |PointerInjectorDelegate| only handles "View.Pointerinjector.inject" |
| // platform messages. |
| { |
| auto response = FakePlatformMessageResponse::Create(); |
| |
| EXPECT_FALSE(pointer_injector_delegate_->HandlePlatformMessage( |
| ParsePlatformMessage("{\"method\":\"View.focus.getCurrent\"}"), |
| response)); |
| } |
| |
| // A platform message with no args should fail. |
| { |
| auto response = FakePlatformMessageResponse::Create(); |
| |
| EXPECT_FALSE(pointer_injector_delegate_->HandlePlatformMessage( |
| ParsePlatformMessage("{\"method\":\"View.Pointerinjector.inject\"}"), |
| response)); |
| } |
| } |
| |
| TEST_P(PointerInjectorDelegateTest, ViewsReceiveInjectedEvents) { |
| const uint64_t num_events = 150; |
| |
| // Inject |num_events| platform messages for view 1. |
| { |
| const uint64_t view_id = 1; |
| |
| CreateView(view_id); |
| |
| auto view_ref_pair = scenic::ViewRefPair::New(); |
| |
| for (size_t i = 0; i < num_events; i++) { |
| auto response = FakePlatformMessageResponse::Create(); |
| |
| // Flatland views do not rely on ViewRef to be passed in the platform |
| // message. |
| std::optional<fuv_ViewRef> view_ref_clone; |
| if (fuv_ViewRef temp_ref; !is_flatland_) { |
| fidl::Clone(view_ref_pair.view_ref, &temp_ref); |
| view_ref_clone = std::move(temp_ref); |
| } |
| |
| EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage( |
| PlatformMessageBuilder() |
| .SetViewId(view_id) |
| .SetViewRefMaybe(std::move(view_ref_clone)) |
| .Build(), |
| response)); |
| |
| response->ExpectCompleted("[0]"); |
| } |
| } |
| |
| // Inject |num_events| platform messages for view 2. |
| { |
| const uint64_t view_id = 2; |
| |
| CreateView(view_id); |
| |
| auto view_ref_pair = scenic::ViewRefPair::New(); |
| |
| for (size_t i = 0; i < num_events; i++) { |
| auto response = FakePlatformMessageResponse::Create(); |
| |
| // Flatland views do not rely on ViewRef to be passed in the platform |
| // message. |
| std::optional<fuv_ViewRef> view_ref_clone; |
| if (fuv_ViewRef temp_ref; !is_flatland_) { |
| fidl::Clone(view_ref_pair.view_ref, &temp_ref); |
| view_ref_clone = std::move(temp_ref); |
| } |
| |
| EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage( |
| PlatformMessageBuilder() |
| .SetViewId(view_id) |
| .SetViewRefMaybe(std::move(view_ref_clone)) |
| .Build(), |
| response)); |
| |
| response->ExpectCompleted("[0]"); |
| } |
| } |
| |
| // The mock Pointerinjector registry server receives |num_events| pointer |
| // events from |f.u.p.Device.Inject| calls for each view. |
| RunLoopUntil( |
| [this] { return registry_->num_events_received() == 2 * num_events; }); |
| |
| // The mock Pointerinjector registry server receives a |
| // |f.u.p.Registry.Register| call for each view. |
| EXPECT_TRUE(registry_->num_register_calls() == 2); |
| } |
| |
| TEST_P(PointerInjectorDelegateTest, |
| ViewsDontReceivePointerEventsBeforeCreation) { |
| const uint64_t num_events = 150; |
| const uint64_t view_id_1 = 1; |
| |
| // Inject |num_events| platform messages for |view_id_1|. |
| { |
| auto view_ref_pair = scenic::ViewRefPair::New(); |
| |
| for (size_t i = 0; i < num_events; i++) { |
| auto response = FakePlatformMessageResponse::Create(); |
| |
| // Flatland views do not rely on ViewRef to be passed in the platform |
| // message. |
| std::optional<fuv_ViewRef> view_ref_clone; |
| if (fuv_ViewRef temp_ref; !is_flatland_) { |
| fidl::Clone(view_ref_pair.view_ref, &temp_ref); |
| view_ref_clone = std::move(temp_ref); |
| } |
| |
| // The platform message is *silently* accepted for non-existent views, in |
| // order to cleanly handle the lifecycle case where the child view is |
| // forcibly killed. By doing so, products avoid "MissingPluginException" |
| // log spam. |
| EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage( |
| PlatformMessageBuilder() |
| .SetViewId(view_id_1) |
| .SetViewRefMaybe(std::move(view_ref_clone)) |
| .Build(), |
| response)); |
| } |
| } |
| |
| const uint64_t view_id_2 = 2; |
| |
| // Inject |num_events| platform messages for |view_id_2|. |
| { |
| auto view_ref_pair = scenic::ViewRefPair::New(); |
| |
| for (size_t i = 0; i < num_events; i++) { |
| auto response = FakePlatformMessageResponse::Create(); |
| |
| // Flatland views do not rely on ViewRef to be passed in the platform |
| // message. |
| std::optional<fuv_ViewRef> view_ref_clone; |
| if (fuv_ViewRef temp_ref; !is_flatland_) { |
| fidl::Clone(view_ref_pair.view_ref, &temp_ref); |
| view_ref_clone = std::move(temp_ref); |
| } |
| |
| // The platform message is *silently* accepted for non-existent views, in |
| // order to cleanly handle the lifecycle case where the child view is |
| // forcibly killed. By doing so, products avoid "MissingPluginException" |
| // log spam. |
| EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage( |
| PlatformMessageBuilder() |
| .SetViewId(view_id_2) |
| .SetViewRefMaybe(std::move(view_ref_clone)) |
| .Build(), |
| response)); |
| } |
| } |
| |
| RunLoopUntilIdle(); |
| |
| // The views do not receive any pointer events till they get created. |
| EXPECT_TRUE(registry_->num_events_received() == 0); |
| } |
| |
| // PointerInjectorDelegate should generate a correct |f.u.p.Config| from a |
| // platform message. |
| TEST_P(PointerInjectorDelegateTest, ValidRegistrationConfigTest) { |
| const uint64_t view_id = 1; |
| |
| const float x = 2.f, y = 2.f, width = 5.f, height = 5.f; |
| const int phase = 2, pointer_id = 5, trace_flow_id = 5, timestamp = 10; |
| |
| auto response = FakePlatformMessageResponse::Create(); |
| |
| auto view_ref_pair = scenic::ViewRefPair::New(); |
| std::optional<fuv_ViewRef> view_ref_clone; |
| |
| // Create the view. |
| if (!is_flatland_) { |
| CreateView(view_id); |
| fuv_ViewRef temp_ref; |
| fidl::Clone(view_ref_pair.view_ref, &temp_ref); |
| view_ref_clone = std::move(temp_ref); |
| } else { |
| fuv_ViewRef view_ref; |
| fidl::Clone(view_ref_pair.view_ref, &view_ref); |
| CreateView(view_id, std::move(view_ref)); |
| } |
| |
| // Inject a platform message. |
| EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage( |
| PlatformMessageBuilder() |
| .SetViewId(view_id) |
| .SetPointerX(x) |
| .SetPointerY(y) |
| .SetPhase(phase) |
| .SetPointerId(pointer_id) |
| .SetTraceFlowId(trace_flow_id) |
| .SetViewRefMaybe(std::move(view_ref_clone)) |
| .SetLogicalWidth(width) |
| .SetLogicalHeight(height) |
| .SetTimestamp(timestamp) |
| .Build(), |
| response)); |
| |
| response->ExpectCompleted("[0]"); |
| |
| // The mock Pointerinjector registry server receives a pointer event from |
| // |f.u.p.Device.Inject| call for the view. |
| RunLoopUntil([this] { return registry_->num_events_received() == 1; }); |
| |
| // The mock Pointerinjector registry server receives a |
| // |f.u.p.Registry.Register| call for the view. |
| ASSERT_TRUE(registry_->num_register_calls() == 1); |
| |
| const auto& config = registry_->config(); |
| |
| ASSERT_TRUE(config.has_device_id()); |
| EXPECT_EQ(config.device_id(), 1u); |
| |
| ASSERT_TRUE(config.has_device_type()); |
| EXPECT_EQ(config.device_type(), fup_DeviceType::TOUCH); |
| |
| ASSERT_TRUE(config.has_dispatch_policy()); |
| EXPECT_EQ(config.dispatch_policy(), fup_DispatchPolicy::EXCLUSIVE_TARGET); |
| |
| ASSERT_TRUE(config.has_context()); |
| ASSERT_TRUE(config.context().is_view()); |
| EXPECT_EQ(ExtractKoid(config.context().view()), ExtractKoid(host_view_ref_)); |
| |
| ASSERT_TRUE(config.has_target()); |
| ASSERT_TRUE(config.target().is_view()); |
| EXPECT_EQ(ExtractKoid(config.target().view()), |
| ExtractKoid(view_ref_pair.view_ref)); |
| |
| ASSERT_TRUE(config.has_viewport()); |
| ASSERT_TRUE(config.viewport().has_viewport_to_context_transform()); |
| EXPECT_EQ(config.viewport().viewport_to_context_transform(), kIdentityMatrix); |
| |
| std::array<std::array<float, 2>, 2> extents{{{0, 0}, {width, height}}}; |
| ASSERT_TRUE(config.viewport().has_extents()); |
| EXPECT_EQ(config.viewport().extents(), extents); |
| } |
| |
| // PointerInjectorDelegate generates a correct f.u.p.Event from the platform |
| // message. |
| TEST_P(PointerInjectorDelegateTest, ValidPointerEventTest) { |
| const uint64_t view_id = 1; |
| |
| const float x = 2.f, y = 2.f, width = 5.f, height = 5.f; |
| const int phase = 2, pointer_id = 5, trace_flow_id = 5, timestamp = 10; |
| |
| auto response = FakePlatformMessageResponse::Create(); |
| |
| auto view_ref_pair = scenic::ViewRefPair::New(); |
| |
| std::optional<fuv_ViewRef> view_ref_clone; |
| |
| // Create the view. |
| if (!is_flatland_) { |
| CreateView(view_id); |
| fuv_ViewRef temp_ref; |
| fidl::Clone(view_ref_pair.view_ref, &temp_ref); |
| view_ref_clone = std::move(temp_ref); |
| } else { |
| fuv_ViewRef view_ref; |
| fidl::Clone(view_ref_pair.view_ref, &view_ref); |
| CreateView(view_id, std::move(view_ref)); |
| } |
| |
| // Inject a platform message. |
| EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage( |
| PlatformMessageBuilder() |
| .SetViewId(view_id) |
| .SetPointerX(x) |
| .SetPointerY(y) |
| .SetPhase(phase) |
| .SetPointerId(pointer_id) |
| .SetTraceFlowId(trace_flow_id) |
| .SetViewRefMaybe(std::move(view_ref_clone)) |
| .SetLogicalWidth(width) |
| .SetLogicalHeight(height) |
| .SetTimestamp(timestamp) |
| .Build(), |
| response)); |
| |
| response->ExpectCompleted("[0]"); |
| |
| // The mock Pointerinjector registry server receives a pointer event from |
| // |f.u.p.Device.Inject| call for the view. |
| RunLoopUntil([this] { return registry_->num_events_received() == 1; }); |
| |
| // The mock Pointerinjector registry server receives a |
| // |f.u.p.Registry.Register| call for the view. |
| ASSERT_TRUE(registry_->num_register_calls() == 1); |
| |
| const auto& events = registry_->events(); |
| |
| ASSERT_EQ(events.size(), 1u); |
| |
| const auto& event = events[0]; |
| |
| ASSERT_TRUE(event.has_timestamp()); |
| EXPECT_EQ(event.timestamp(), timestamp); |
| |
| ASSERT_TRUE(event.has_trace_flow_id()); |
| EXPECT_EQ(event.trace_flow_id(), static_cast<uint64_t>(trace_flow_id)); |
| |
| ASSERT_TRUE(event.has_data()); |
| ASSERT_TRUE(event.data().is_pointer_sample()); |
| |
| const auto& pointer_sample = event.data().pointer_sample(); |
| |
| ASSERT_TRUE(pointer_sample.has_pointer_id()); |
| ASSERT_TRUE(pointer_sample.has_phase()); |
| ASSERT_TRUE(pointer_sample.has_position_in_viewport()); |
| EXPECT_EQ(pointer_sample.pointer_id(), static_cast<uint32_t>(pointer_id)); |
| EXPECT_EQ(pointer_sample.phase(), static_cast<fup_EventPhase>(phase)); |
| EXPECT_THAT(pointer_sample.position_in_viewport(), |
| ::testing::ElementsAre(x, y)); |
| } |
| |
| TEST_P(PointerInjectorDelegateTest, DestroyedViewsDontGetPointerEvents) { |
| const uint64_t view_id = 1, num_events = 150; |
| |
| auto view_ref_pair = scenic::ViewRefPair::New(); |
| |
| // Create the view. |
| CreateView(view_id); |
| |
| // Inject |num_events| platform messages. |
| for (size_t i = 0; i < num_events; i++) { |
| auto response = FakePlatformMessageResponse::Create(); |
| |
| // Flatland views do not rely on ViewRef to be passed in the platform |
| // message. |
| std::optional<fuv_ViewRef> view_ref_clone; |
| if (fuv_ViewRef temp_ref; !is_flatland_) { |
| fidl::Clone(view_ref_pair.view_ref, &temp_ref); |
| view_ref_clone = std::move(temp_ref); |
| } |
| |
| EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage( |
| PlatformMessageBuilder() |
| .SetViewId(view_id) |
| .SetViewRefMaybe(std::move(view_ref_clone)) |
| .Build(), |
| response)); |
| |
| response->ExpectCompleted("[0]"); |
| } |
| |
| // Destroy the view. |
| pointer_injector_delegate_->OnDestroyView(view_id); |
| |
| // The view does not receive |num_events| pointer events as it gets destroyed |
| // before all the pointer events could be dispatched. |
| const zx::duration timeout = zx::sec(1), step = zx::msec(10); |
| EXPECT_FALSE(RunLoopWithTimeoutOrUntil( |
| [this] { return registry_->num_events_received() == num_events; }, |
| timeout, step)); |
| |
| EXPECT_LT(registry_->num_events_received(), num_events); |
| } |
| |
| TEST_P(PointerInjectorDelegateTest, ViewsGetPointerEventsInFIFO) { |
| const uint64_t view_id = 1, num_events = 150; |
| |
| auto view_ref_pair = scenic::ViewRefPair::New(); |
| |
| // Create the view. |
| CreateView(view_id); |
| |
| // Inject |num_events| platform messages. |
| for (size_t i = 0; i < num_events; i++) { |
| auto response = FakePlatformMessageResponse::Create(); |
| |
| // Flatland views do not rely on ViewRef to be passed in the platform |
| // message. |
| std::optional<fuv_ViewRef> view_ref_clone; |
| if (fuv_ViewRef temp_ref; !is_flatland_) { |
| fidl::Clone(view_ref_pair.view_ref, &temp_ref); |
| view_ref_clone = std::move(temp_ref); |
| } |
| |
| EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage( |
| PlatformMessageBuilder() |
| .SetViewId(view_id) |
| .SetPointerId(static_cast<uint32_t>(i)) |
| .SetViewRefMaybe(std::move(view_ref_clone)) |
| .Build(), |
| response)); |
| |
| response->ExpectCompleted("[0]"); |
| } |
| |
| // The mock Pointerinjector registry server receives |num_events| pointer |
| // events from |f.u.p.Device.Inject| call for the view. |
| RunLoopUntil( |
| [this] { return registry_->num_events_received() == num_events; }); |
| |
| // The mock Pointerinjector registry server receives a |
| // |f.u.p.Registry.Register| call for the view. |
| ASSERT_TRUE(registry_->num_register_calls() == 1); |
| |
| auto& events = registry_->events(); |
| |
| // The view should receive the pointer events in a FIFO order. As we injected |
| // platform messages with an increasing |pointer_id|, the received pointer |
| // events should also have the |pointer_id| in an increasing order. |
| for (size_t i = 0; i < events.size() - 1; i++) { |
| ASSERT_TRUE(events[i].has_data()); |
| ASSERT_TRUE(events[i + 1].has_data()); |
| ASSERT_TRUE(events[i].data().is_pointer_sample()); |
| ASSERT_TRUE(events[i + 1].data().is_pointer_sample()); |
| |
| const auto& pointer_sample_1 = events[i].data().pointer_sample(); |
| const auto& pointer_sample_2 = events[i + 1].data().pointer_sample(); |
| |
| ASSERT_TRUE(pointer_sample_1.has_pointer_id()); |
| ASSERT_TRUE(pointer_sample_2.has_pointer_id()); |
| |
| EXPECT_TRUE(pointer_sample_1.pointer_id() < pointer_sample_2.pointer_id()); |
| } |
| } |
| |
| TEST_P(PointerInjectorDelegateTest, DeviceRetriesRegisterWhenClosed) { |
| const uint64_t view_id = 1; |
| const int pointer_id = 1; |
| auto view_ref_pair = scenic::ViewRefPair::New(); |
| |
| auto response = FakePlatformMessageResponse::Create(); |
| auto response_2 = FakePlatformMessageResponse::Create(); |
| |
| std::optional<fuv_ViewRef> view_ref_clone; |
| std::optional<fuv_ViewRef> view_ref_clone_2; |
| |
| // Create the view. |
| if (!is_flatland_) { |
| CreateView(view_id); |
| fuv_ViewRef temp_ref; |
| fuv_ViewRef temp_ref_2; |
| fidl::Clone(view_ref_pair.view_ref, &temp_ref); |
| fidl::Clone(view_ref_pair.view_ref, &temp_ref_2); |
| view_ref_clone = std::move(temp_ref); |
| view_ref_clone_2 = std::move(temp_ref_2); |
| } else { |
| fuv_ViewRef view_ref; |
| fidl::Clone(view_ref_pair.view_ref, &view_ref); |
| CreateView(view_id, std::move(view_ref)); |
| } |
| |
| EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage( |
| PlatformMessageBuilder() |
| .SetViewId(view_id) |
| .SetPointerId(pointer_id) |
| .SetViewRefMaybe(std::move(view_ref_clone)) |
| .Build(), |
| response)); |
| |
| response->ExpectCompleted("[0]"); |
| |
| // The mock Pointerinjector registry server receives a pointer event from |
| // |f.u.p.Device.Inject| call for the view. |
| RunLoopUntil([this] { return registry_->num_events_received() == 1; }); |
| |
| // The mock Pointerinjector registry server receives a |
| // |f.u.p.Registry.Register| call for the view. |
| ASSERT_TRUE(registry_->num_register_calls() == 1); |
| |
| // Close the device channel. |
| registry_->ClearBindings(); |
| RunLoopUntilIdle(); |
| |
| EXPECT_TRUE(pointer_injector_delegate_->HandlePlatformMessage( |
| PlatformMessageBuilder() |
| .SetViewId(view_id) |
| .SetPointerId(pointer_id) |
| .SetViewRefMaybe(std::move(view_ref_clone_2)) |
| .Build(), |
| response_2)); |
| |
| response_2->ExpectCompleted("[0]"); |
| |
| // The mock Pointerinjector registry server receives a pointer event from |
| // |f.u.p.Device.Inject| call for the view. |
| RunLoopUntil([this] { return registry_->num_events_received() == 2; }); |
| |
| // The device tries to register again as the channel got closed. |
| ASSERT_TRUE(registry_->num_register_calls() == 2); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(PointerInjectorDelegateParameterizedTest, |
| PointerInjectorDelegateTest, |
| ::testing::Bool()); |
| |
| } // namespace flutter_runner::testing |