blob: d8aa45e10d13ac156bd3183273b2ba8c691f5bcb [file] [log] [blame]
// Copyright 2021 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/drivers/goldfish_sensor/input_device.h"
#include <fuchsia/hardware/goldfish/llcpp/fidl.h>
#include <fuchsia/hardware/goldfish/pipe/cpp/banjo.h>
#include <fuchsia/input/report/llcpp/fidl.h>
#include <lib/async-loop/default.h>
#include <lib/async/dispatcher.h>
#include <lib/fake_ddk/fake_ddk.h>
#include <lib/fidl/llcpp/client.h>
#include <lib/zx/time.h>
#include <cmath>
#include <gtest/gtest.h>
#include "src/lib/testing/loop_fixture/test_loop_fixture.h"
#include "src/ui/input/lib/input-report-reader/reader.h"
namespace goldfish::sensor {
class InputDeviceTest : public ::testing::Test {
public:
void SetUp() override {}
void TearDown() override {}
protected:
fake_ddk::Bind ddk_;
std::unique_ptr<async::TestLoop> loop_;
std::unique_ptr<InputDevice> dut_;
fidl::WireSyncClient<fuchsia_input_report::InputDevice> fidl_client_;
};
class TestAccelerationInputDevice : public AccelerationInputDevice {
public:
explicit TestAccelerationInputDevice(async_dispatcher_t* dispatcher)
: AccelerationInputDevice(fake_ddk::kFakeParent, dispatcher, nullptr) {}
void GetInputReportsReader(GetInputReportsReaderRequestView request,
GetInputReportsReaderCompleter::Sync& completer) override {
AccelerationInputDevice::GetInputReportsReader(request, completer);
++readers_created_;
}
size_t readers_created() const { return readers_created_.load(); }
private:
// For test purpose only.
std::atomic<size_t> readers_created_ = 0;
};
class AccelerationInputDeviceTest : public InputDeviceTest {
public:
void SetUp() override {
loop_ = std::make_unique<async::TestLoop>();
dut_ = std::make_unique<TestAccelerationInputDevice>(loop_->dispatcher());
ASSERT_EQ(dut_->DdkAdd("goldfish-sensor-accel"), ZX_OK);
auto client_end =
fidl::ClientEnd<fuchsia_input_report::InputDevice>(std::move(ddk_.FidlClient()));
fidl_client_ = fidl::WireSyncClient<fuchsia_input_report::InputDevice>(std::move(client_end));
}
void TearDown() override {
// After tests finishes, there might still be some pending events on the
// loop that uses the InputReportsReader which could cause a use-after-free
// error if we destroy the loop after destroying the device. Thus we destroy
// the loop first. This is safe as long as we don't call
// GetInputReportsReader() after resetting |loop_|.
loop_.reset();
dut_.reset();
}
TestAccelerationInputDevice* dut() const {
return static_cast<TestAccelerationInputDevice*>(dut_.get());
}
};
TEST_F(AccelerationInputDeviceTest, ReadInputReports) {
auto endpoint_result = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>();
ASSERT_TRUE(endpoint_result.is_ok());
auto [client_end, server_end] = std::move(endpoint_result.value());
auto reader_result = fidl_client_.GetInputReportsReader(std::move(server_end));
ASSERT_TRUE(reader_result.ok());
auto client = fidl::Client<fuchsia_input_report::InputReportsReader>(std::move(client_end),
loop_->dispatcher());
// The FIDL callback runs on another thread by fake_ddk::FidlMessenger.
// We will need to wait for the FIDL callback to finish before using |client|.
while (!dut()->readers_created()) {
}
SensorReport rpt = {.name = "acceleration", .data = {Numeric(1.0), Numeric(2.0), Numeric(3.0)}};
EXPECT_EQ(dut_->OnReport(rpt), ZX_OK);
auto result = client->ReadInputReports(
[](fidl::WireResponse<fuchsia_input_report::InputReportsReader::ReadInputReports>* response) {
ASSERT_TRUE(response->result.is_response());
auto& reports = response->result.response().reports;
ASSERT_EQ(reports.count(), 1u);
auto& report = response->result.response().reports[0];
ASSERT_TRUE(report.has_sensor());
auto& sensor = response->result.response().reports[0].sensor();
ASSERT_TRUE(sensor.has_values() && sensor.values().count() == 3);
EXPECT_EQ(sensor.values().at(0), 100);
EXPECT_EQ(sensor.values().at(1), 200);
EXPECT_EQ(sensor.values().at(2), 300);
});
ASSERT_TRUE(result.ok());
ASSERT_TRUE(loop_->RunUntilIdle());
dut_->DdkAsyncRemove();
EXPECT_TRUE(ddk_.Ok());
}
TEST_F(AccelerationInputDeviceTest, Descriptor) {
auto endpoint_result = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>();
ASSERT_TRUE(endpoint_result.is_ok());
auto [client_end, server_end] = std::move(endpoint_result.value());
auto descriptor_result = fidl_client_.GetDescriptor();
ASSERT_TRUE(descriptor_result.ok());
const auto& descriptor = descriptor_result->descriptor;
ASSERT_TRUE(descriptor.has_sensor());
EXPECT_FALSE(descriptor.has_keyboard());
EXPECT_FALSE(descriptor.has_mouse());
EXPECT_FALSE(descriptor.has_touch());
EXPECT_FALSE(descriptor.has_consumer_control());
ASSERT_TRUE(descriptor.sensor().has_input());
ASSERT_TRUE(descriptor.sensor().input().has_values());
const auto& values = descriptor.sensor().input().values();
ASSERT_EQ(values.count(), 3u);
EXPECT_EQ(values[0].type, fuchsia_input_report::wire::SensorType::kAccelerometerX);
EXPECT_EQ(values[1].type, fuchsia_input_report::wire::SensorType::kAccelerometerY);
EXPECT_EQ(values[2].type, fuchsia_input_report::wire::SensorType::kAccelerometerZ);
EXPECT_EQ(values[0].axis.unit.type, fuchsia_input_report::wire::UnitType::kSiLinearAcceleration);
EXPECT_EQ(values[1].axis.unit.type, fuchsia_input_report::wire::UnitType::kSiLinearAcceleration);
EXPECT_EQ(values[2].axis.unit.type, fuchsia_input_report::wire::UnitType::kSiLinearAcceleration);
EXPECT_EQ(values[0].axis.unit.exponent, -2);
EXPECT_EQ(values[1].axis.unit.exponent, -2);
EXPECT_EQ(values[2].axis.unit.exponent, -2);
dut_->DdkAsyncRemove();
EXPECT_TRUE(ddk_.Ok());
}
TEST_F(AccelerationInputDeviceTest, InvalidInputReports) {
// Invalid number of elements.
SensorReport invalid_report1 = {.name = "acceleration", .data = {Numeric(1.0), Numeric(2.0)}};
EXPECT_EQ(dut_->OnReport(invalid_report1), ZX_ERR_INVALID_ARGS);
// Invalid x.
SensorReport invalid_report2 = {.name = "acceleration",
.data = {"string", Numeric(2.0), Numeric(3.0)}};
EXPECT_EQ(dut_->OnReport(invalid_report2), ZX_ERR_INVALID_ARGS);
// Invalid y.
SensorReport invalid_report3 = {.name = "acceleration",
.data = {Numeric(2.0), "string", Numeric(3.0)}};
EXPECT_EQ(dut_->OnReport(invalid_report3), ZX_ERR_INVALID_ARGS);
// Invalid z.
SensorReport invalid_report4 = {.name = "acceleration",
.data = {Numeric(2.0), Numeric(3.0), "string"}};
EXPECT_EQ(dut_->OnReport(invalid_report4), ZX_ERR_INVALID_ARGS);
}
class TestGyroscopeInputDevice : public GyroscopeInputDevice {
public:
explicit TestGyroscopeInputDevice(async_dispatcher_t* dispatcher)
: GyroscopeInputDevice(fake_ddk::kFakeParent, dispatcher, nullptr) {}
void GetInputReportsReader(GetInputReportsReaderRequestView request,
GetInputReportsReaderCompleter::Sync& completer) override {
GyroscopeInputDevice::GetInputReportsReader(request, completer);
++readers_created_;
}
size_t readers_created() const { return readers_created_.load(); }
private:
// For test purpose only.
std::atomic<size_t> readers_created_ = 0;
};
class GyroscopeInputDeviceTest : public InputDeviceTest {
public:
void SetUp() override {
loop_ = std::make_unique<async::TestLoop>();
dut_ = std::make_unique<TestGyroscopeInputDevice>(loop_->dispatcher());
ASSERT_EQ(dut_->DdkAdd("goldfish-sensor-gyroscope"), ZX_OK);
auto client_end =
fidl::ClientEnd<fuchsia_input_report::InputDevice>(std::move(ddk_.FidlClient()));
fidl_client_ = fidl::WireSyncClient<fuchsia_input_report::InputDevice>(std::move(client_end));
}
void TearDown() override {
// After tests finishes, there might still be some pending events on the
// loop that uses the InputReportsReader which could cause a use-after-free
// error if we destroy the loop after destroying the device. Thus we destroy
// the loop first. This is safe as long as we don't call
// GetInputReportsReader() after resetting |loop_|.
loop_.reset();
dut_.reset();
}
TestGyroscopeInputDevice* dut() const {
return static_cast<TestGyroscopeInputDevice*>(dut_.get());
}
};
TEST_F(GyroscopeInputDeviceTest, ReadInputReports) {
auto endpoint_result = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>();
ASSERT_TRUE(endpoint_result.is_ok());
auto [client_end, server_end] = std::move(endpoint_result.value());
auto reader_result = fidl_client_.GetInputReportsReader(std::move(server_end));
ASSERT_TRUE(reader_result.ok());
auto client = fidl::Client<fuchsia_input_report::InputReportsReader>(std::move(client_end),
loop_->dispatcher());
// The FIDL callback runs on another thread by fake_ddk::FidlMessenger.
// We will need to wait for the FIDL callback to finish before using |client|.
while (!dut()->readers_created()) {
}
SensorReport rpt = {.name = "gyroscope",
.data = {Numeric(M_PI), Numeric(2.0 * M_PI), Numeric(3.0 * M_PI)}};
EXPECT_EQ(dut_->OnReport(rpt), ZX_OK);
auto result = client->ReadInputReports(
[](fidl::WireResponse<fuchsia_input_report::InputReportsReader::ReadInputReports>* response) {
ASSERT_TRUE(response->result.is_response());
auto& reports = response->result.response().reports;
ASSERT_EQ(reports.count(), 1u);
auto& report = response->result.response().reports[0];
ASSERT_TRUE(report.has_sensor());
auto& sensor = response->result.response().reports[0].sensor();
ASSERT_TRUE(sensor.has_values() && sensor.values().count() == 3);
EXPECT_EQ(sensor.values().at(0), 18000);
EXPECT_EQ(sensor.values().at(1), 36000);
EXPECT_EQ(sensor.values().at(2), 54000);
});
ASSERT_TRUE(result.ok());
ASSERT_TRUE(loop_->RunUntilIdle());
dut_->DdkAsyncRemove();
EXPECT_TRUE(ddk_.Ok());
}
TEST_F(GyroscopeInputDeviceTest, Descriptor) {
auto endpoint_result = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>();
ASSERT_TRUE(endpoint_result.is_ok());
auto [client_end, server_end] = std::move(endpoint_result.value());
auto descriptor_result = fidl_client_.GetDescriptor();
ASSERT_TRUE(descriptor_result.ok());
const auto& descriptor = descriptor_result->descriptor;
ASSERT_TRUE(descriptor.has_sensor());
EXPECT_FALSE(descriptor.has_keyboard());
EXPECT_FALSE(descriptor.has_mouse());
EXPECT_FALSE(descriptor.has_touch());
EXPECT_FALSE(descriptor.has_consumer_control());
ASSERT_TRUE(descriptor.sensor().has_input());
ASSERT_TRUE(descriptor.sensor().input().has_values());
const auto& values = descriptor.sensor().input().values();
ASSERT_EQ(values.count(), 3u);
EXPECT_EQ(values[0].type, fuchsia_input_report::wire::SensorType::kGyroscopeX);
EXPECT_EQ(values[1].type, fuchsia_input_report::wire::SensorType::kGyroscopeY);
EXPECT_EQ(values[2].type, fuchsia_input_report::wire::SensorType::kGyroscopeZ);
EXPECT_EQ(values[0].axis.unit.type,
fuchsia_input_report::wire::UnitType::kEnglishAngularVelocity);
EXPECT_EQ(values[1].axis.unit.type,
fuchsia_input_report::wire::UnitType::kEnglishAngularVelocity);
EXPECT_EQ(values[2].axis.unit.type,
fuchsia_input_report::wire::UnitType::kEnglishAngularVelocity);
EXPECT_EQ(values[0].axis.unit.exponent, -2);
EXPECT_EQ(values[1].axis.unit.exponent, -2);
EXPECT_EQ(values[2].axis.unit.exponent, -2);
dut_->DdkAsyncRemove();
EXPECT_TRUE(ddk_.Ok());
}
TEST_F(GyroscopeInputDeviceTest, InvalidInputReports) {
// Invalid number of elements.
SensorReport invalid_report1 = {.name = "gyroscope", .data = {Numeric(1.0), Numeric(2.0)}};
EXPECT_EQ(dut_->OnReport(invalid_report1), ZX_ERR_INVALID_ARGS);
// Invalid x.
SensorReport invalid_report2 = {.name = "gyroscope",
.data = {"string", Numeric(2.0), Numeric(3.0)}};
EXPECT_EQ(dut_->OnReport(invalid_report2), ZX_ERR_INVALID_ARGS);
// Invalid y.
SensorReport invalid_report3 = {.name = "gyroscope",
.data = {Numeric(2.0), "string", Numeric(3.0)}};
EXPECT_EQ(dut_->OnReport(invalid_report3), ZX_ERR_INVALID_ARGS);
// Invalid z.
SensorReport invalid_report4 = {.name = "gyroscope",
.data = {Numeric(2.0), Numeric(3.0), "string"}};
EXPECT_EQ(dut_->OnReport(invalid_report4), ZX_ERR_INVALID_ARGS);
}
} // namespace goldfish::sensor