blob: e7f54fa11add0882dcc782df5d83f2f1f0afe4fb [file] [log] [blame]
// Copyright 2020 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/input/report/llcpp/fidl.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fidl-async/cpp/bind.h>
#include <set>
#include <ddk/device.h>
#include <fbl/auto_call.h>
#include <fbl/auto_lock.h>
#include <fbl/mutex.h>
#include <gtest/gtest.h>
#include "lib/gtest/test_loop_fixture.h"
#include "src/ui/input/lib/hid-input-report/fidl.h"
#include "src/ui/input/testing/fake_input_report_device/fake.h"
#include "src/ui/lib/input_report_reader/input_reader.h"
#include "src/ui/lib/input_report_reader/tests/mock_device_watcher.h"
#include "src/ui/testing/mock_input_device.h"
#include "src/ui/testing/mock_input_device_registry.h"
namespace ui_input {
namespace fuchsia_input_report = ::llcpp::fuchsia::input::report;
namespace {
using MockInputDevice = ui_input::test::MockInputDevice;
using MockInputDeviceRegistry = ui_input::test::MockInputDeviceRegistry;
// This fixture sets up a |MockDeviceWatcher| so that tests can add mock
// devices.
class ReaderInterpreterTest : public gtest::TestLoopFixture {
protected:
// Starts an |InputReader| with a |MockDeviceWatcher| and saves it locally so
// that |MockHidDecoder|s can be added to it.
void StartInputReader(InputReader* input_reader) {
auto device_watcher = std::make_unique<MockDeviceWatcher>();
device_watcher_ = device_watcher->GetWeakPtr();
input_reader->Start(std::move(device_watcher));
}
void AddDevice(zx::channel chan) { device_watcher_->AddDevice(std::move(chan)); }
private:
fxl::WeakPtr<MockDeviceWatcher> device_watcher_;
};
// This fixture sets up a |MockInputDeviceRegistry| and an |InputReader| in
// addition to the |MockDeviceWatcher| provided by |ReaderInterpreterTest| so
// that tests can additionally verify the reports seen by the registry.
class ReaderInterpreterInputTest : public ReaderInterpreterTest {
protected:
ReaderInterpreterInputTest()
: registry_([this](test::MockInputDevice* device) { last_device_ = device; },
[this](fuchsia::ui::input::InputReport report) {
++report_count_;
last_report_ = std::move(report);
}),
input_reader_(&registry_) {}
int report_count_ = 0;
fuchsia::ui::input::InputReport last_report_;
MockInputDevice* last_device_ = nullptr;
protected:
MockInputDeviceRegistry registry_;
InputReader input_reader_;
std::unique_ptr<async::Loop> loop_;
std::unique_ptr<fake_input_report_device::FakeInputDevice> fake_device_;
std::optional<fuchsia_input_report::InputDevice::SyncClient> client_;
void StartDevice() { AddDevice(std::move(token_client_)); }
private:
zx::channel token_client_;
void SetUp() override {
StartInputReader(&input_reader_);
// Make the channels and the fake device.
zx::channel token_server;
ASSERT_EQ(zx::channel::create(0, &token_server, &token_client_), ZX_OK);
fake_device_ = std::make_unique<fake_input_report_device::FakeInputDevice>();
// Make and run the thread for the fake device's FIDL interface.
loop_ = std::make_unique<async::Loop>(&kAsyncLoopConfigNoAttachToCurrentThread);
ASSERT_EQ(loop_->StartThread("test-print-input-report-loop"), ZX_OK);
fidl::Bind(loop_->dispatcher(), std::move(token_server), fake_device_.get());
}
void TearDown() override {
loop_->Quit();
loop_->JoinThreads();
}
};
TEST_F(ReaderInterpreterInputTest, TouchScreen) {
// Add a touchscreen descriptor.
{
hid_input_report::TouchDescriptor touch_desc = {};
touch_desc.input = hid_input_report::TouchInputDescriptor();
touch_desc.input->touch_type = fuchsia_input_report::TouchType::TOUCHSCREEN;
touch_desc.input->max_contacts = 100;
fuchsia_input_report::Axis axis;
axis.unit = fuchsia_input_report::Unit::NONE;
axis.range.min = 0;
axis.range.max = 300;
touch_desc.input->contacts[0].position_x = axis;
axis.range.max = 500;
touch_desc.input->contacts[0].position_y = axis;
touch_desc.input->num_contacts = 1;
hid_input_report::ReportDescriptor desc;
desc.descriptor = touch_desc;
fake_device_->SetDescriptor(desc);
}
// Add the device.
StartDevice();
RunLoopUntilIdle();
// Check the touchscreen descriptor.
{
ASSERT_NE(last_device_, nullptr);
fuchsia::ui::input::DeviceDescriptor* descriptor = last_device_->descriptor();
ASSERT_NE(descriptor, nullptr);
ASSERT_NE(descriptor->touchscreen, nullptr);
ASSERT_EQ(descriptor->touchscreen->x.range.min, 0);
ASSERT_EQ(descriptor->touchscreen->x.range.max, 300);
ASSERT_EQ(descriptor->touchscreen->y.range.min, 0);
ASSERT_EQ(descriptor->touchscreen->y.range.max, 500);
}
// Send a Touchscreen report.
{
hid_input_report::TouchInputReport touch = {};
touch.num_contacts = 1;
touch.contacts[0].contact_id = 10;
touch.contacts[0].is_pressed = true;
touch.contacts[0].position_x = 30;
touch.contacts[0].position_y = 50;
hid_input_report::InputReport report;
report.report = touch;
fake_device_->SetReport(report);
}
RunLoopUntilIdle();
// Check the touchscreen report.
{
ASSERT_EQ(report_count_, 1);
ASSERT_EQ(last_report_.touchscreen->touches.size(), 1U);
ASSERT_EQ(last_report_.touchscreen->touches[0].finger_id, 10U);
ASSERT_EQ(last_report_.touchscreen->touches[0].x, 30);
ASSERT_EQ(last_report_.touchscreen->touches[0].y, 50);
}
}
TEST_F(ReaderInterpreterInputTest, ConsumerControl) {
// Add a descriptor.
{
hid_input_report::ConsumerControlDescriptor consumer_desc = {};
consumer_desc.input = hid_input_report::ConsumerControlInputDescriptor();
consumer_desc.input->num_buttons = 5;
consumer_desc.input->buttons[0] = fuchsia_input_report::ConsumerControlButton::VOLUME_UP;
consumer_desc.input->buttons[1] = fuchsia_input_report::ConsumerControlButton::VOLUME_DOWN;
consumer_desc.input->buttons[2] = fuchsia_input_report::ConsumerControlButton::PAUSE;
consumer_desc.input->buttons[3] = fuchsia_input_report::ConsumerControlButton::MIC_MUTE;
consumer_desc.input->buttons[4] = fuchsia_input_report::ConsumerControlButton::REBOOT;
hid_input_report::ReportDescriptor desc;
desc.descriptor = consumer_desc;
fake_device_->SetDescriptor(desc);
}
// Add the device.
StartDevice();
RunLoopUntilIdle();
// Check the descriptor.
{
ASSERT_NE(last_device_, nullptr);
fuchsia::ui::input::DeviceDescriptor* descriptor = last_device_->descriptor();
ASSERT_NE(descriptor, nullptr);
ASSERT_NE(descriptor->media_buttons, nullptr);
ASSERT_EQ(descriptor->media_buttons->buttons,
fuchsia::ui::input::kVolumeUp | fuchsia::ui::input::kVolumeDown |
fuchsia::ui::input::kPause | fuchsia::ui::input::kMicMute |
fuchsia::ui::input::kReset);
}
// Send a report.
{
hid_input_report::ConsumerControlInputReport consumer = {};
consumer.num_pressed_buttons = 5;
consumer.pressed_buttons[0] = fuchsia_input_report::ConsumerControlButton::VOLUME_UP;
consumer.pressed_buttons[1] = fuchsia_input_report::ConsumerControlButton::VOLUME_DOWN;
consumer.pressed_buttons[2] = fuchsia_input_report::ConsumerControlButton::PAUSE;
consumer.pressed_buttons[3] = fuchsia_input_report::ConsumerControlButton::MIC_MUTE;
consumer.pressed_buttons[4] = fuchsia_input_report::ConsumerControlButton::REBOOT;
hid_input_report::InputReport report;
report.report = consumer;
fake_device_->SetReport(report);
}
RunLoopUntilIdle();
// Check the report.
{
ASSERT_EQ(report_count_, 1);
ASSERT_TRUE(last_report_.media_buttons->volume_up);
ASSERT_TRUE(last_report_.media_buttons->volume_down);
ASSERT_TRUE(last_report_.media_buttons->mic_mute);
ASSERT_TRUE(last_report_.media_buttons->reset);
ASSERT_TRUE(last_report_.media_buttons->pause);
}
}
TEST_F(ReaderInterpreterInputTest, Mouse) {
// Add a descriptor.
{
hid_input_report::MouseDescriptor mouse_desc = {};
mouse_desc.input = hid_input_report::MouseInputDescriptor();
fuchsia_input_report::Axis axis;
axis.unit = fuchsia_input_report::Unit::NONE;
axis.range.min = -100;
axis.range.max = 100;
mouse_desc.input->movement_x = axis;
axis.range.min = -200;
axis.range.max = 200;
mouse_desc.input->movement_y = axis;
mouse_desc.input->num_buttons = 2;
mouse_desc.input->buttons[0] = 1;
mouse_desc.input->buttons[1] = 3;
hid_input_report::ReportDescriptor desc;
desc.descriptor = mouse_desc;
fake_device_->SetDescriptor(desc);
}
// Add the device.
StartDevice();
RunLoopUntilIdle();
// Check the descriptor.
{
ASSERT_NE(last_device_, nullptr);
fuchsia::ui::input::DeviceDescriptor* descriptor = last_device_->descriptor();
ASSERT_NE(descriptor, nullptr);
ASSERT_NE(descriptor->mouse, nullptr);
ASSERT_EQ(descriptor->mouse->buttons, 0b101U);
ASSERT_EQ(descriptor->mouse->rel_x.range.min, -100);
ASSERT_EQ(descriptor->mouse->rel_x.range.max, 100);
ASSERT_EQ(descriptor->mouse->rel_y.range.min, -200);
ASSERT_EQ(descriptor->mouse->rel_y.range.max, 200);
}
// Send a report.
{
hid_input_report::MouseInputReport mouse = {};
mouse.num_buttons_pressed = 2;
mouse.buttons_pressed[0] = 1;
mouse.buttons_pressed[1] = 3;
mouse.movement_x = 100;
mouse.movement_y = 200;
hid_input_report::InputReport report;
report.report = mouse;
fake_device_->SetReport(report);
}
RunLoopUntilIdle();
// Check the report.
{
ASSERT_EQ(report_count_, 1);
ASSERT_EQ(last_report_.mouse->pressed_buttons, 0b101U);
ASSERT_EQ(last_report_.mouse->rel_x, 100);
ASSERT_EQ(last_report_.mouse->rel_y, 200);
}
}
} // namespace
} // namespace ui_input