blob: 0b3b6848f59d72994c8bef74f3165147f68a6773 [file] [log] [blame]
// Copyright 2022 The Fuchsia 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/display/singleton/cpp/fidl.h>
#include <fuchsia/ui/test/conformance/cpp/fidl.h>
#include <fuchsia/ui/test/input/cpp/fidl.h>
#include <fuchsia/ui/test/scene/cpp/fidl.h>
#include <fuchsia/ui/views/cpp/fidl.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/ui/scenic/cpp/view_creation_tokens.h>
#include <zircon/errors.h>
#include <memory>
#include <gtest/gtest.h>
#include "src/ui/tests/conformance_input_tests/conformance-test-base.h"
namespace ui_conformance_testing {
namespace futi = fuchsia::ui::test::input;
namespace futc = fuchsia::ui::test::conformance;
const std::string PUPPET_UNDER_TEST_FACTORY_SERVICE = "puppet-under-test-factory-service";
// Two physical pixel coordinates are considered equivalent if their distance is less than 1 unit.
constexpr double kPixelEpsilon = 0.5f;
// Epsilon for floating error.
constexpr double kEpsilon = 0.0001f;
class MouseListener : public futi::MouseInputListener {
public:
MouseListener() : binding_(this) {}
// |fuchsia::ui::test::input::MouseInputListener|
void ReportMouseInput(futi::MouseInputListenerReportMouseInputRequest request) override {
events_received_.push_back(std::move(request));
}
// Returns a client end bound to this object.
fidl::InterfaceHandle<futi::MouseInputListener> NewBinding() { return binding_.NewBinding(); }
const std::vector<futi::MouseInputListenerReportMouseInputRequest>& events_received() const {
return events_received_;
}
void clear_events() { events_received_.clear(); }
private:
fidl::Binding<futi::MouseInputListener> binding_;
std::vector<futi::MouseInputListenerReportMouseInputRequest> events_received_;
};
// Holds resources associated with a single puppet instance.
struct MousePuppet {
futc::PuppetSyncPtr puppet_ptr;
MouseListener mouse_listener;
};
using device_pixel_ratio = float;
class MouseConformanceTest : public ui_conformance_test_base::ConformanceTest,
public ::testing::WithParamInterface<device_pixel_ratio> {
public:
~MouseConformanceTest() override = default;
float DevicePixelRatio() const override { return GetParam(); }
void SetUp() override {
ui_conformance_test_base::ConformanceTest::SetUp();
// Register fake mouse.
{
FX_LOGS(INFO) << "Connecting to input registry";
auto input_registry = ConnectSyncIntoRealm<futi::Registry>();
FX_LOGS(INFO) << "Registering fake mouse";
futi::RegistryRegisterMouseRequest request;
request.set_device(fake_mouse_.NewRequest());
ASSERT_EQ(input_registry->RegisterMouse(std::move(request)), ZX_OK);
}
// Get display dimensions.
{
FX_LOGS(INFO) << "Reading display dimensions";
auto display_info = ConnectSyncIntoRealm<fuchsia::ui::display::singleton::Info>();
fuchsia::ui::display::singleton::Metrics metrics;
ASSERT_EQ(display_info->GetMetrics(&metrics), ZX_OK);
display_width_ = metrics.extent_in_px().width;
display_height_ = metrics.extent_in_px().height;
FX_LOGS(INFO) << "Received display dimensions (" << display_width_ << ", " << display_height_
<< ")";
}
fuchsia::ui::views::ViewCreationToken root_view_token;
// Get root view token.
{
FX_LOGS(INFO) << "Creating root view token";
auto controller = ConnectSyncIntoRealm<fuchsia::ui::test::scene::Controller>();
fuchsia::ui::test::scene::ControllerPresentClientViewRequest req;
auto [view_token, viewport_token] = scenic::ViewCreationTokenPair::New();
req.set_viewport_creation_token(std::move(viewport_token));
ASSERT_EQ(controller->PresentClientView(std::move(req)), ZX_OK);
root_view_token = std::move(view_token);
}
{
FX_LOGS(INFO) << "Create puppet under test";
futc::PuppetFactorySyncPtr puppet_factory;
ASSERT_EQ(LocalServiceDirectory()->Connect(puppet_factory.NewRequest(),
PUPPET_UNDER_TEST_FACTORY_SERVICE),
ZX_OK);
futc::PuppetFactoryCreateResponse resp;
auto flatland = ConnectSyncIntoRealm<fuchsia::ui::composition::Flatland>();
auto keyboard = ConnectSyncIntoRealm<fuchsia::ui::input3::Keyboard>();
futc::PuppetCreationArgs creation_args;
creation_args.set_server_end(puppet_.puppet_ptr.NewRequest());
creation_args.set_view_token(std::move(root_view_token));
creation_args.set_mouse_listener(puppet_.mouse_listener.NewBinding());
creation_args.set_flatland_client(std::move(flatland));
creation_args.set_keyboard_client(std::move(keyboard));
creation_args.set_device_pixel_ratio(DevicePixelRatio());
ASSERT_EQ(puppet_factory->Create(std::move(creation_args), &resp), ZX_OK);
ASSERT_EQ(resp.result(), futc::Result::SUCCESS);
}
}
void SimulateMouseEvent(std::vector<futi::MouseButton> pressed_buttons, int movement_x,
int movement_y) {
FX_LOGS(INFO) << "Requesting mouse event";
futi::MouseSimulateMouseEventRequest request;
request.set_pressed_buttons(std::move(pressed_buttons));
request.set_movement_x(movement_x);
request.set_movement_y(movement_y);
fake_mouse_->SimulateMouseEvent(std::move(request));
FX_LOGS(INFO) << "Mouse event injected";
}
void ExpectEvent(const std::string& scoped_message,
const futi::MouseInputListenerReportMouseInputRequest& e, double expected_x,
double expected_y,
const std::vector<futi::MouseButton>& expected_buttons) const {
SCOPED_TRACE(scoped_message);
auto pixel_scale = e.has_device_pixel_ratio() ? e.device_pixel_ratio() : 1;
EXPECT_NEAR(static_cast<double>(DevicePixelRatio()), pixel_scale, kEpsilon);
auto actual_x = pixel_scale * e.local_x();
auto actual_y = pixel_scale * e.local_y();
EXPECT_NEAR(expected_x, actual_x, kPixelEpsilon);
EXPECT_NEAR(expected_y, actual_y, kPixelEpsilon);
if (expected_buttons.empty()) {
EXPECT_FALSE(e.has_buttons());
} else {
ASSERT_TRUE(e.has_buttons());
EXPECT_EQ(expected_buttons, e.buttons());
}
}
protected:
int32_t display_width_as_int() const { return static_cast<int32_t>(display_width_); }
int32_t display_height_as_int() const { return static_cast<int32_t>(display_height_); }
futi::MouseSyncPtr fake_mouse_;
MousePuppet puppet_;
uint32_t display_width_ = 0;
uint32_t display_height_ = 0;
};
INSTANTIATE_TEST_SUITE_P(/*no prefix*/, MouseConformanceTest, ::testing::Values(1.0, 2.0));
TEST_P(MouseConformanceTest, SimpleClick) {
// Inject click with no mouse movement.
// Left button down.
SimulateMouseEvent(/* pressed_buttons = */ {futi::MouseButton::FIRST},
/* movement_x = */ 0, /* movement_y = */ 0);
FX_LOGS(INFO) << "Waiting for puppet to report DOWN event";
RunLoopUntil([this]() { return this->puppet_.mouse_listener.events_received().size() >= 1; });
ASSERT_EQ(puppet_.mouse_listener.events_received().size(), 1u);
ExpectEvent("Down", puppet_.mouse_listener.events_received()[0],
/* expected_x = */ static_cast<double>(display_width_) / 2.f,
/* expected_y = */ static_cast<double>(display_height_) / 2.f,
/* expected_buttons = */ {futi::MouseButton::FIRST});
puppet_.mouse_listener.clear_events();
// Left button up.
SimulateMouseEvent(/* pressed_buttons = */ {}, /* movement_x = */ 0, /* movement_y = */ 0);
FX_LOGS(INFO) << "Waiting for puppet to report UP";
RunLoopUntil([this]() { return this->puppet_.mouse_listener.events_received().size() >= 1; });
ASSERT_EQ(puppet_.mouse_listener.events_received().size(), 1u);
ExpectEvent("Up", puppet_.mouse_listener.events_received()[0],
/* expected_x = */ static_cast<double>(display_width_) / 2.f,
/* expected_y = */ static_cast<double>(display_height_) / 2.f,
/* expected_buttons = */ {});
}
} // namespace ui_conformance_testing