blob: 22821456e2b3de99bb3d0a7c5b83ef6d42a41d27 [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 <lib/async-loop/cpp/loop.h>
#include <lib/fidl/llcpp/server.h>
#include <lib/input_report_reader/reader.h>
#include <zxtest/zxtest.h>
struct MouseReport {
int64_t movement_x;
int64_t movement_y;
void ToFidlInputReport(
fidl::WireTableBuilder<::fuchsia_input_report::wire::InputReport>& input_report,
fidl::AnyArena& allocator) {
auto mouse = fuchsia_input_report::wire::MouseInputReport::Builder(allocator);
mouse.movement_x(this->movement_x);
mouse.movement_y(this->movement_y);
input_report.mouse(mouse.Build());
}
};
class MouseDevice : public fidl::WireServer<fuchsia_input_report::InputDevice> {
public:
zx_status_t Start();
void SendReport(const MouseReport& report);
// Function for testing that blocks until a new reader is connected.
zx_status_t WaitForNextReader(zx::duration timeout) {
zx_status_t status = sync_completion_wait(&next_reader_wait_, timeout.get());
if (status == ZX_OK) {
sync_completion_reset(&next_reader_wait_);
}
return ZX_OK;
}
// The FIDL methods for InputDevice.
void GetInputReportsReader(GetInputReportsReaderRequestView request,
GetInputReportsReaderCompleter::Sync& completer) override;
void GetDescriptor(GetDescriptorRequestView request,
GetDescriptorCompleter::Sync& completer) override;
void SendOutputReport(SendOutputReportRequestView request,
SendOutputReportCompleter::Sync& completer) override;
void GetFeatureReport(GetFeatureReportRequestView request,
GetFeatureReportCompleter::Sync& completer) override;
void SetFeatureReport(SetFeatureReportRequestView request,
SetFeatureReportCompleter::Sync& completer) override;
void GetInputReport(GetInputReportRequestView request,
GetInputReportCompleter::Sync& completer) override;
private:
sync_completion_t next_reader_wait_;
input_report_reader::InputReportReaderManager<MouseReport> input_report_readers_;
async::Loop loop_ = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
};
zx_status_t MouseDevice::Start() {
zx_status_t status = ZX_OK;
if ((status = loop_.StartThread("MouseDeviceReaderThread")) != ZX_OK) {
return status;
}
return ZX_OK;
}
void MouseDevice::SendReport(const MouseReport& report) {
input_report_readers_.SendReportToAllReaders(report);
}
void MouseDevice::GetInputReportsReader(GetInputReportsReaderRequestView request,
GetInputReportsReaderCompleter::Sync& completer) {
zx_status_t status =
input_report_readers_.CreateReader(loop_.dispatcher(), std::move(request->reader));
if (status == ZX_OK) {
// Signal to a test framework (if it exists) that we are connected to a reader.
sync_completion_signal(&next_reader_wait_);
}
}
void MouseDevice::GetDescriptor(GetDescriptorRequestView request,
GetDescriptorCompleter::Sync& completer) {
fidl::Arena allocator;
completer.Reply(fuchsia_input_report::wire::DeviceDescriptor::Builder(allocator).Build());
}
void MouseDevice::SendOutputReport(SendOutputReportRequestView request,
SendOutputReportCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void MouseDevice::GetFeatureReport(GetFeatureReportRequestView request,
GetFeatureReportCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void MouseDevice::SetFeatureReport(SetFeatureReportRequestView request,
SetFeatureReportCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void MouseDevice::GetInputReport(GetInputReportRequestView request,
GetInputReportCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
class InputReportReaderTests : public zxtest::Test {
void SetUp() override {
ASSERT_EQ(mouse_.Start(), ZX_OK);
auto endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputDevice>();
ASSERT_TRUE(endpoints.is_ok());
auto [client, server] = std::move(endpoints.value());
auto result = fidl::BindServer(loop_.dispatcher(), std::move(server), &mouse_);
input_device_ = fidl::WireSyncClient<fuchsia_input_report::InputDevice>(std::move(client));
ASSERT_EQ(loop_.StartThread("MouseDeviceThread"), ZX_OK);
}
void TearDown() override {}
protected:
MouseDevice mouse_;
async::Loop loop_ = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
fidl::WireSyncClient<fuchsia_input_report::InputDevice> input_device_;
};
TEST_F(InputReportReaderTests, LifeTimeTest) {
// Get an InputReportsReader.
fidl::WireSyncClient<fuchsia_input_report::InputReportsReader> reader;
{
auto endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>();
ASSERT_TRUE(endpoints.is_ok());
auto [client, server] = std::move(endpoints.value());
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)input_device_->GetInputReportsReader(std::move(server));
reader = fidl::WireSyncClient<fuchsia_input_report::InputReportsReader>(std::move(client));
mouse_.WaitForNextReader(zx::duration::infinite());
}
}
TEST_F(InputReportReaderTests, ReadInputReportsTest) {
// Get an InputReportsReader.
fidl::WireSyncClient<fuchsia_input_report::InputReportsReader> reader;
{
auto endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>();
ASSERT_TRUE(endpoints.is_ok());
auto [client, server] = std::move(endpoints.value());
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)input_device_->GetInputReportsReader(std::move(server));
reader = fidl::WireSyncClient<fuchsia_input_report::InputReportsReader>(std::move(client));
mouse_.WaitForNextReader(zx::duration::infinite());
}
// Send a report.
MouseReport report;
report.movement_x = 0x100;
report.movement_y = 0x200;
mouse_.SendReport(report);
// Get the report.
auto result = reader->ReadInputReports();
ASSERT_OK(result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(1, reports.count());
ASSERT_TRUE(reports[0].has_event_time());
ASSERT_TRUE(reports[0].has_mouse());
auto& mouse_report = reports[0].mouse();
ASSERT_TRUE(mouse_report.has_movement_x());
ASSERT_EQ(0x100, mouse_report.movement_x());
ASSERT_TRUE(mouse_report.has_movement_y());
ASSERT_EQ(0x200, mouse_report.movement_y());
ASSERT_FALSE(mouse_report.has_pressed_buttons());
}
TEST_F(InputReportReaderTests, ReaderAddsRequiredFields) {
// Get an InputReportsReader.
fidl::WireSyncClient<fuchsia_input_report::InputReportsReader> reader;
{
auto endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>();
ASSERT_TRUE(endpoints.is_ok());
auto [client, server] = std::move(endpoints.value());
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)input_device_->GetInputReportsReader(std::move(server));
reader = fidl::WireSyncClient<fuchsia_input_report::InputReportsReader>(std::move(client));
mouse_.WaitForNextReader(zx::duration::infinite());
}
// Send a report.
MouseReport report;
report.movement_x = 0x100;
report.movement_y = 0x200;
mouse_.SendReport(report);
// Get the report.
auto result = reader->ReadInputReports();
ASSERT_OK(result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(1, reports.count());
ASSERT_TRUE(reports[0].has_event_time());
ASSERT_TRUE(reports[0].has_trace_id());
}
TEST_F(InputReportReaderTests, TwoReaders) {
// Get the first reader.
fidl::WireSyncClient<fuchsia_input_report::InputReportsReader> reader_one;
{
auto endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>();
ASSERT_TRUE(endpoints.is_ok());
auto [client, server] = std::move(endpoints.value());
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)input_device_->GetInputReportsReader(std::move(server));
reader_one = fidl::WireSyncClient<fuchsia_input_report::InputReportsReader>(std::move(client));
mouse_.WaitForNextReader(zx::duration::infinite());
}
// Get the second reader.
fidl::WireSyncClient<fuchsia_input_report::InputReportsReader> reader_two;
{
auto endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>();
ASSERT_TRUE(endpoints.is_ok());
auto [client, server] = std::move(endpoints.value());
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)input_device_->GetInputReportsReader(std::move(server));
reader_two = fidl::WireSyncClient<fuchsia_input_report::InputReportsReader>(std::move(client));
mouse_.WaitForNextReader(zx::duration::infinite());
}
// Send a report.
MouseReport report;
report.movement_x = 0x100;
report.movement_y = 0x200;
mouse_.SendReport(report);
// Get the first report.
{
auto result = reader_one->ReadInputReports();
ASSERT_OK(result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(1, reports.count());
ASSERT_TRUE(reports[0].has_event_time());
ASSERT_TRUE(reports[0].has_mouse());
auto& mouse_report = reports[0].mouse();
ASSERT_TRUE(mouse_report.has_movement_x());
ASSERT_EQ(0x100, mouse_report.movement_x());
ASSERT_TRUE(mouse_report.has_movement_y());
ASSERT_EQ(0x200, mouse_report.movement_y());
ASSERT_FALSE(mouse_report.has_pressed_buttons());
}
// Get the second report.
{
auto result = reader_two->ReadInputReports();
ASSERT_OK(result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(1, reports.count());
ASSERT_TRUE(reports[0].has_event_time());
ASSERT_TRUE(reports[0].has_mouse());
auto& mouse_report = reports[0].mouse();
ASSERT_TRUE(mouse_report.has_movement_x());
ASSERT_EQ(0x100, mouse_report.movement_x());
ASSERT_TRUE(mouse_report.has_movement_y());
ASSERT_EQ(0x200, mouse_report.movement_y());
ASSERT_FALSE(mouse_report.has_pressed_buttons());
}
}
TEST_F(InputReportReaderTests, ReadInputReportsHangingGetTest) {
async::Loop loop = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
// Get an async InputReportsReader.
fidl::WireClient<fuchsia_input_report::InputReportsReader> reader;
{
auto endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>();
ASSERT_TRUE(endpoints.is_ok());
auto [client, server] = std::move(endpoints.value());
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)input_device_->GetInputReportsReader(std::move(server));
reader.Bind(std::move(client), loop.dispatcher());
mouse_.WaitForNextReader(zx::duration::infinite());
}
// Read the report. This will hang until a report is sent.
reader->ReadInputReports().ThenExactlyOnce(
[&](fidl::WireUnownedResult<fuchsia_input_report::InputReportsReader::ReadInputReports>&
result) {
ASSERT_OK(result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(1, reports.count());
auto& report = reports[0];
ASSERT_TRUE(report.has_event_time());
ASSERT_TRUE(report.has_mouse());
auto& mouse = report.mouse();
ASSERT_TRUE(mouse.has_movement_x());
ASSERT_EQ(0x50, mouse.movement_x());
ASSERT_TRUE(mouse.has_movement_y());
ASSERT_EQ(0x70, mouse.movement_y());
loop.Quit();
});
loop.RunUntilIdle();
// Send the report.
MouseReport report;
report.movement_x = 0x50;
report.movement_y = 0x70;
mouse_.SendReport(report);
loop.Run();
}
TEST_F(InputReportReaderTests, CloseReaderWithOutstandingRead) {
async::Loop loop = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
// Get an async InputReportsReader.
fidl::WireClient<fuchsia_input_report::InputReportsReader> reader;
{
auto endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>();
ASSERT_TRUE(endpoints.is_ok());
auto [client, server] = std::move(endpoints.value());
// TODO(fxbug.dev/97955) Consider handling the error instead of ignoring it.
(void)input_device_->GetInputReportsReader(std::move(server));
reader.Bind(std::move(client), loop.dispatcher());
mouse_.WaitForNextReader(zx::duration::infinite());
}
// Queue a read.
reader->ReadInputReports().ThenExactlyOnce(
[&](fidl::WireUnownedResult<fuchsia_input_report::InputReportsReader::ReadInputReports>&
result) { ASSERT_TRUE(result.is_canceled()); });
loop.RunUntilIdle();
// Unbind the reader now that the report is waiting.
reader = {};
}