blob: cd166e740be9dc8c47ed14b65a890df003c78a15 [file]
// 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 "src/ui/input/lib/input-report-reader/reader.h"
#include <lib/async-loop/cpp/loop.h>
#include <lib/async/cpp/task.h>
#include <lib/fidl/cpp/wire/server.h>
#include <lib/sync/cpp/completion.h>
#include <zircon/time.h>
#include <algorithm>
#include <cinttypes>
#include <gtest/gtest.h>
#include "src/lib/testing/predicates/status.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) const {
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());
}
};
template <size_t kMaxBatchSize = 1, zx_duration_t kMaxBatchDelayNs = 0>
class MouseDevice : public fidl::WireServer<fuchsia_input_report::InputDevice> {
public:
zx_status_t Start();
size_t 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 = next_reader_wait_.Wait(timeout);
if (status == ZX_OK) {
next_reader_wait_.Reset();
}
return ZX_OK;
}
void SetInitialReport(MouseReport report) { initial_report_ = report; }
// The FIDL methods for InputDevice.
void GetInputReportsReader(GetInputReportsReaderRequestView request,
GetInputReportsReaderCompleter::Sync& completer) override;
void GetInputReportsReaderV2(GetInputReportsReaderV2RequestView request,
GetInputReportsReaderV2Completer::Sync& completer) override;
void GetDescriptor(GetDescriptorCompleter::Sync& completer) override;
void SendOutputReport(SendOutputReportRequestView request,
SendOutputReportCompleter::Sync& completer) override;
void GetFeatureReport(GetFeatureReportCompleter::Sync& completer) override;
void SetFeatureReport(SetFeatureReportRequestView request,
SetFeatureReportCompleter::Sync& completer) override;
void GetInputReport(GetInputReportRequestView request,
GetInputReportCompleter::Sync& completer) override;
void handle_unknown_method(
fidl::UnknownMethodMetadata<fuchsia_input_report::InputDevice> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override {
fprintf(stderr, "Unexpected fidl method invoked: %" PRIu64 "\n", metadata.method_ordinal);
}
private:
libsync::Completion next_reader_wait_;
input_report_reader::InputReportReaderManager<MouseReport, 10, kMaxBatchSize, kMaxBatchDelayNs>
input_report_readers_;
async::Loop loop_ = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
std::optional<MouseReport> initial_report_ = std::nullopt;
};
template <size_t kMaxBatchSize, zx_duration_t kMaxBatchDelayNs>
zx_status_t MouseDevice<kMaxBatchSize, kMaxBatchDelayNs>::Start() {
zx_status_t status = ZX_OK;
if ((status = loop_.StartThread("MouseDeviceReaderThread")) != ZX_OK) {
return status;
}
return ZX_OK;
}
template <size_t kMaxBatchSize, zx_duration_t kMaxBatchDelayNs>
size_t MouseDevice<kMaxBatchSize, kMaxBatchDelayNs>::SendReport(const MouseReport& report) {
return input_report_readers_.SendReportToAllReaders(report);
}
template <size_t kMaxBatchSize, zx_duration_t kMaxBatchDelayNs>
void MouseDevice<kMaxBatchSize, kMaxBatchDelayNs>::GetInputReportsReader(
GetInputReportsReaderRequestView request, GetInputReportsReaderCompleter::Sync& completer) {
libsync::Completion wait;
async::PostTask(loop_.dispatcher(), [&]() {
zx_status_t status = input_report_readers_.CreateReader(
loop_.dispatcher(), std::move(request->reader), initial_report_);
if (status == ZX_OK) {
// Signal to a test framework (if it exists) that we are connected to a reader.
next_reader_wait_.Signal();
}
wait.Signal();
});
wait.Wait();
}
template <size_t kMaxBatchSize, zx_duration_t kMaxBatchDelayNs>
void MouseDevice<kMaxBatchSize, kMaxBatchDelayNs>::GetInputReportsReaderV2(
GetInputReportsReaderV2RequestView request, GetInputReportsReaderV2Completer::Sync& completer) {
const uint16_t max_unacknowledged_reports =
std::max<uint16_t>(1, request->max_unacknowledged_reports_limit);
libsync::Completion wait;
zx_status_t status = ZX_OK;
async::PostTask(loop_.dispatcher(), [&]() {
status = input_report_readers_.CreateReaderV2(loop_.dispatcher(), std::move(request->reader),
max_unacknowledged_reports, initial_report_);
if (status == ZX_OK) {
next_reader_wait_.Signal();
}
wait.Signal();
});
wait.Wait();
if (status != ZX_OK) {
completer.Close(status);
return;
}
completer.Reply(max_unacknowledged_reports);
}
template <size_t kMaxBatchSize, zx_duration_t kMaxBatchDelayNs>
void MouseDevice<kMaxBatchSize, kMaxBatchDelayNs>::GetDescriptor(
GetDescriptorCompleter::Sync& completer) {
fidl::Arena allocator;
completer.Reply(fuchsia_input_report::wire::DeviceDescriptor::Builder(allocator).Build());
}
template <size_t kMaxBatchSize, zx_duration_t kMaxBatchDelayNs>
void MouseDevice<kMaxBatchSize, kMaxBatchDelayNs>::SendOutputReport(
SendOutputReportRequestView request, SendOutputReportCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
template <size_t kMaxBatchSize, zx_duration_t kMaxBatchDelayNs>
void MouseDevice<kMaxBatchSize, kMaxBatchDelayNs>::GetFeatureReport(
GetFeatureReportCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
template <size_t kMaxBatchSize, zx_duration_t kMaxBatchDelayNs>
void MouseDevice<kMaxBatchSize, kMaxBatchDelayNs>::SetFeatureReport(
SetFeatureReportRequestView request, SetFeatureReportCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
template <size_t kMaxBatchSize, zx_duration_t kMaxBatchDelayNs>
void MouseDevice<kMaxBatchSize, kMaxBatchDelayNs>::GetInputReport(
GetInputReportRequestView request, GetInputReportCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
class InputReportReaderTests : public testing::Test {
void SetUp() override {
ASSERT_EQ(mouse_.Start(), ZX_OK);
auto [client, server] = fidl::Endpoints<fuchsia_input_report::InputDevice>::Create();
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 [client, server] = fidl::Endpoints<fuchsia_input_report::InputReportsReader>::Create();
// TODO(https://fxbug.dev/42180237) 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 [client, server] = fidl::Endpoints<fuchsia_input_report::InputReportsReader>::Create();
// TODO(https://fxbug.dev/42180237) 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_EQ(ZX_OK, result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(1u, reports.size());
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 [client, server] = fidl::Endpoints<fuchsia_input_report::InputReportsReader>::Create();
// TODO(https://fxbug.dev/42180237) 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_EQ(ZX_OK, result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(1u, reports.size());
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 [client, server] = fidl::Endpoints<fuchsia_input_report::InputReportsReader>::Create();
// TODO(https://fxbug.dev/42180237) 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 [client, server] = fidl::Endpoints<fuchsia_input_report::InputReportsReader>::Create();
// TODO(https://fxbug.dev/42180237) 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_EQ(ZX_OK, result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(1u, reports.size());
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_EQ(ZX_OK, result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(1u, reports.size());
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 [client, server] = fidl::Endpoints<fuchsia_input_report::InputReportsReader>::Create();
// TODO(https://fxbug.dev/42180237) 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_EQ(ZX_OK, result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(1u, reports.size());
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 [client, server] = fidl::Endpoints<fuchsia_input_report::InputReportsReader>::Create();
// TODO(https://fxbug.dev/42180237) 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 = {};
}
TEST_F(InputReportReaderTests, MaxUnreadReports) {
fidl::WireSyncClient<fuchsia_input_report::InputReportsReader> reader;
{
auto [client, server] = fidl::Endpoints<fuchsia_input_report::InputReportsReader>::Create();
ASSERT_TRUE(input_device_->GetInputReportsReader(std::move(server)).ok());
reader = fidl::WireSyncClient<fuchsia_input_report::InputReportsReader>(std::move(client));
mouse_.WaitForNextReader(zx::duration::infinite());
}
// Send 15 reports, and store the counter value in movement_x. Our InputReportReaderManager has
// kMaxUnreadReports set to 10, so the first five reports should be dropped.
for (int64_t i = 1; i <= 10; i++) {
// The first 10 reports should be accepted without causing others to be dropped.
EXPECT_EQ(mouse_.SendReport({.movement_x = i}), 0u);
}
for (int64_t i = 11; i <= 15; i++) {
// With the report queue full, SendReport should now result in one report getting dropped.
EXPECT_EQ(mouse_.SendReport({.movement_x = i}), 1u);
}
auto result = reader->ReadInputReports();
ASSERT_EQ(ZX_OK, result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(10u, reports.size());
ASSERT_TRUE(reports[0].has_mouse());
ASSERT_TRUE(reports[0].mouse().has_movement_x());
EXPECT_EQ(reports[0].mouse().movement_x(), 6);
ASSERT_TRUE(reports[9].has_mouse());
ASSERT_TRUE(reports[9].mouse().has_movement_x());
EXPECT_EQ(reports[9].mouse().movement_x(), 15);
}
TEST_F(InputReportReaderTests, InitialReportTest) {
mouse_.SetInitialReport({
.movement_x = 0x50,
.movement_y = 0x100,
});
// Get an InputReportsReader.
fidl::WireSyncClient<fuchsia_input_report::InputReportsReader> reader;
{
auto [client, server] = fidl::Endpoints<fuchsia_input_report::InputReportsReader>::Create();
// TODO(https://fxbug.dev/42180237) 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());
}
// Get the report.
auto result = reader->ReadInputReports();
ASSERT_EQ(ZX_OK, result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(1u, reports.size());
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(0x50, mouse_report.movement_x());
ASSERT_TRUE(mouse_report.has_movement_y());
ASSERT_EQ(0x100, mouse_report.movement_y());
ASSERT_FALSE(mouse_report.has_pressed_buttons());
}
class BatchedInputReportReaderTests : public testing::Test {
void SetUp() override {
ASSERT_EQ(mouse_.Start(), ZX_OK);
auto [client, server] = fidl::Endpoints<fuchsia_input_report::InputDevice>::Create();
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:
static constexpr size_t kMaxBatchSize = 5;
MouseDevice<kMaxBatchSize, 1'000'000'000> mouse_;
async::Loop loop_ = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
fidl::WireSyncClient<fuchsia_input_report::InputDevice> input_device_;
};
TEST_F(BatchedInputReportReaderTests, ReadIsBatched) {
async::Loop loop = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
// Get an InputReportsReader.
fidl::WireClient<fuchsia_input_report::InputReportsReader> reader;
{
auto [client, server] = fidl::Endpoints<fuchsia_input_report::InputReportsReader>::Create();
// TODO(https://fxbug.dev/42180237) 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 reports. This will hang until a report is sent.
reader->ReadInputReports().ThenExactlyOnce(
[&](fidl::WireUnownedResult<fuchsia_input_report::InputReportsReader::ReadInputReports>&
result) {
ASSERT_EQ(ZX_OK, result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(kMaxBatchSize, reports.size());
for (auto& report : reports) {
ASSERT_TRUE(report.has_event_time());
ASSERT_TRUE(report.has_mouse());
auto& mouse = report.mouse();
ASSERT_TRUE(mouse.has_movement_x());
ASSERT_EQ(0x01, mouse.movement_x());
ASSERT_TRUE(mouse.has_movement_y());
ASSERT_EQ(0x23, mouse.movement_y());
}
loop.Quit();
});
loop.RunUntilIdle();
// Send the reports.
MouseReport report;
report.movement_x = 0x01;
report.movement_y = 0x23;
for (size_t i = 0; i < kMaxBatchSize; i++) {
mouse_.SendReport(report);
}
loop.Run();
}
TEST_F(BatchedInputReportReaderTests, ReadIsDelayed) {
constexpr size_t kSmallBatchSize = 3;
static_assert(kSmallBatchSize < kMaxBatchSize);
async::Loop loop = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
// Get an InputReportsReader.
fidl::WireClient<fuchsia_input_report::InputReportsReader> reader;
{
auto [client, server] = fidl::Endpoints<fuchsia_input_report::InputReportsReader>::Create();
// TODO(https://fxbug.dev/42180237) 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 reports. This will hang until a report is sent.
reader->ReadInputReports().ThenExactlyOnce(
[&](fidl::WireUnownedResult<fuchsia_input_report::InputReportsReader::ReadInputReports>&
result) {
ASSERT_EQ(ZX_OK, result.status());
ASSERT_FALSE(result->is_error());
auto& reports = result->value()->reports;
ASSERT_EQ(kSmallBatchSize, reports.size());
for (auto& report : reports) {
ASSERT_TRUE(report.has_event_time());
ASSERT_TRUE(report.has_mouse());
auto& mouse = report.mouse();
ASSERT_TRUE(mouse.has_movement_x());
ASSERT_EQ(0x19, mouse.movement_x());
ASSERT_TRUE(mouse.has_movement_y());
ASSERT_EQ(0x85, mouse.movement_y());
}
loop.Quit();
});
loop.RunUntilIdle();
// Send the reports.
MouseReport report;
report.movement_x = 0x19;
report.movement_y = 0x85;
for (size_t i = 0; i < kSmallBatchSize; i++) {
mouse_.SendReport(report);
}
loop.Run();
}
class TestReaderV2EventHandler
: public fidl::WireAsyncEventHandler<fuchsia_input_report::InputReportsReaderV2> {
public:
// `loop` must outlive the `TestReaderV2EventHandler` instance.
explicit TestReaderV2EventHandler(async::Loop& loop) : loop_(loop) {}
void OnInputReports(
fidl::WireEvent<fuchsia_input_report::InputReportsReaderV2::OnInputReports>* event) override {
reports_received_ += event->reports.size();
last_stamp_ = event->last_report_stamp;
events_received_++;
loop_.Quit();
}
void handle_unknown_event(
fidl::UnknownEventMetadata<fuchsia_input_report::InputReportsReaderV2> metadata) override {}
size_t events_received() const { return events_received_; }
size_t reports_received() const { return reports_received_; }
uint64_t last_stamp() const { return last_stamp_; }
private:
async::Loop& loop_;
size_t events_received_ = 0;
size_t reports_received_ = 0;
uint64_t last_stamp_ = 0;
};
TEST_F(InputReportReaderTests, V2Basic) {
async::Loop loop = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
auto [client_end, server_end] =
fidl::Endpoints<fuchsia_input_report::InputReportsReaderV2>::Create();
fidl::WireResult<fuchsia_input_report::InputDevice::GetInputReportsReaderV2> get_reader_result =
input_device_->GetInputReportsReaderV2(std::move(server_end),
/*max_unacknowledged_reports=*/5);
ASSERT_OK(get_reader_result.status());
ASSERT_EQ(5, get_reader_result.value().max_unacknowledged_reports);
mouse_.WaitForNextReader(zx::duration::infinite());
TestReaderV2EventHandler event_handler(loop);
fidl::WireClient<fuchsia_input_report::InputReportsReaderV2> client(
std::move(client_end), loop.dispatcher(), &event_handler);
MouseReport report;
report.movement_x = 100;
report.movement_y = 200;
mouse_.SendReport(report);
loop.Run();
ASSERT_EQ(1u, event_handler.events_received());
ASSERT_EQ(1u, event_handler.reports_received());
ASSERT_EQ(1u, event_handler.last_stamp());
// Acknowledge and send another.
ASSERT_EQ(ZX_OK, client->AcknowledgeReports(event_handler.last_stamp()).status());
loop.ResetQuit();
mouse_.SendReport(report);
loop.Run();
ASSERT_EQ(2u, event_handler.events_received());
ASSERT_EQ(2u, event_handler.reports_received());
ASSERT_EQ(2u, event_handler.last_stamp());
}
TEST_F(InputReportReaderTests, V2UnacknowledgedLimit) {
async::Loop loop = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
auto [client_end, server_end] =
fidl::Endpoints<fuchsia_input_report::InputReportsReaderV2>::Create();
fidl::WireResult<fuchsia_input_report::InputDevice::GetInputReportsReaderV2> get_reader_result =
input_device_->GetInputReportsReaderV2(std::move(server_end),
/*max_unacknowledged_reports=*/2);
ASSERT_OK(get_reader_result.status());
ASSERT_EQ(2, get_reader_result.value().max_unacknowledged_reports);
mouse_.WaitForNextReader(zx::duration::infinite());
TestReaderV2EventHandler event_handler(loop);
fidl::WireClient<fuchsia_input_report::InputReportsReaderV2> client(
std::move(client_end), loop.dispatcher(), &event_handler);
MouseReport report = {.movement_x = 10, .movement_y = 20};
// Send 3 reports while max_unacknowledged_reports is 2.
mouse_.SendReport(report);
mouse_.SendReport(report);
mouse_.SendReport(report);
// Run the loop twice to receive the first two events.
loop.Run();
loop.ResetQuit();
loop.Run();
ASSERT_EQ(2u, event_handler.events_received());
ASSERT_EQ(2u, event_handler.last_stamp());
// Running until idle should show no 3rd event yet because limit is reached.
loop.ResetQuit();
loop.RunUntilIdle();
ASSERT_EQ(2u, event_handler.events_received());
// Now acknowledge up to stamp 2.
ASSERT_EQ(ZX_OK, client->AcknowledgeReports(2).status());
// Now the 3rd event should be received.
loop.Run();
ASSERT_EQ(3u, event_handler.events_received());
ASSERT_EQ(3u, event_handler.last_stamp());
}
TEST(InputReportReaderTestsV2, V2BatchSizeGrouping) {
async::Loop client_loop = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
async::Loop device_loop = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
ASSERT_EQ(device_loop.StartThread("DeviceThread"), ZX_OK);
MouseDevice</*kMaxBatchSize=*/5, /*kMaxBatchDelayNs=*/1'000'000'000> mouse;
ASSERT_EQ(mouse.Start(), ZX_OK);
auto [client, server] = fidl::Endpoints<fuchsia_input_report::InputDevice>::Create();
fidl::BindServer(device_loop.dispatcher(), std::move(server), &mouse);
fidl::WireSyncClient<fuchsia_input_report::InputDevice> input_device(std::move(client));
// Get reader
auto [client_end, server_end] =
fidl::Endpoints<fuchsia_input_report::InputReportsReaderV2>::Create();
fidl::WireResult<fuchsia_input_report::InputDevice::GetInputReportsReaderV2> get_reader_result =
input_device->GetInputReportsReaderV2(std::move(server_end), 10);
ASSERT_OK(get_reader_result.status());
mouse.WaitForNextReader(zx::duration::infinite());
TestReaderV2EventHandler event_handler(client_loop);
fidl::WireClient<fuchsia_input_report::InputReportsReaderV2> client_v2(
std::move(client_end), client_loop.dispatcher(), &event_handler);
MouseReport report = {.movement_x = 1, .movement_y = 2};
// Send 5 reports (equal to max batch size).
for (size_t i = 0; i < 5; i++) {
mouse.SendReport(report);
}
client_loop.Run();
// They should be grouped into 1 event.
ASSERT_EQ(1u, event_handler.events_received());
ASSERT_EQ(5u, event_handler.reports_received());
}
TEST(InputReportReaderTestsV2, V2BatchSizeLimit) {
async::Loop client_loop = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
async::Loop device_loop = async::Loop(&kAsyncLoopConfigNeverAttachToThread);
ASSERT_EQ(device_loop.StartThread("DeviceThread"), ZX_OK);
MouseDevice</*kMaxBatchSize=*/3, /*kMaxBatchDelayNs=*/1'000'000'000> mouse;
ASSERT_EQ(mouse.Start(), ZX_OK);
auto [client, server] = fidl::Endpoints<fuchsia_input_report::InputDevice>::Create();
fidl::BindServer(device_loop.dispatcher(), std::move(server), &mouse);
fidl::WireSyncClient<fuchsia_input_report::InputDevice> input_device(std::move(client));
// Get reader with max_unacknowledged_events = 2.
auto [client_end, server_end] =
fidl::Endpoints<fuchsia_input_report::InputReportsReaderV2>::Create();
fidl::WireResult<fuchsia_input_report::InputDevice::GetInputReportsReaderV2> get_reader_result =
input_device->GetInputReportsReaderV2(std::move(server_end), 2);
ASSERT_OK(get_reader_result.status());
mouse.WaitForNextReader(zx::duration::infinite());
TestReaderV2EventHandler event_handler(client_loop);
fidl::WireClient<fuchsia_input_report::InputReportsReaderV2> client_v2(
std::move(client_end), client_loop.dispatcher(), &event_handler);
MouseReport report = {.movement_x = 10, .movement_y = 20};
// Send 9 reports (3 batches of 3).
for (size_t i = 0; i < 9; i++) {
mouse.SendReport(report);
}
// Run the loop twice to receive the first two batches.
client_loop.Run();
client_loop.ResetQuit();
client_loop.Run();
ASSERT_EQ(2u, event_handler.events_received());
ASSERT_EQ(6u, event_handler.reports_received());
ASSERT_EQ(2u, event_handler.last_stamp());
// Running until idle should show no 3rd batch yet because limit is reached.
client_loop.ResetQuit();
client_loop.RunUntilIdle();
ASSERT_EQ(2u, event_handler.events_received());
// Now acknowledge up to stamp 2.
ASSERT_EQ(ZX_OK, client_v2->AcknowledgeReports(2).status());
// Now the 3rd batch should be received.
client_loop.Run();
ASSERT_EQ(3u, event_handler.events_received());
ASSERT_EQ(9u, event_handler.reports_received());
ASSERT_EQ(3u, event_handler.last_stamp());
}