blob: 536bea629bb04cfa4167b23513a944c948a70efd [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/root_device.h"
#include <fidl/fuchsia.hardware.goldfish.pipe/cpp/wire.h>
#include <fidl/fuchsia.input.report/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/async-loop/loop.h>
#include <lib/async/default.h>
#include <lib/async_patterns/testing/cpp/dispatcher_bound.h>
#include <lib/component/outgoing/cpp/outgoing_directory.h>
#include <lib/ddk/driver.h>
#include <string>
#include <variant>
#include <gtest/gtest.h>
#include "src/devices/testing/goldfish/fake_pipe/fake_pipe.h"
#include "src/devices/testing/mock-ddk/mock-device.h"
#include "src/ui/input/drivers/goldfish_sensor/input_device.h"
namespace goldfish::sensor {
namespace {
class FakeInputDevice : public InputDevice {
public:
static fpromise::result<InputDevice*, zx_status_t> Create(RootDevice* rootdevice,
async_dispatcher_t* dispatcher,
const std::string& name) {
auto val = new FakeInputDevice(rootdevice, dispatcher, name);
g_devices_[name] = val;
return fpromise::ok(val);
}
FakeInputDevice(RootDevice* rootdevice, async_dispatcher_t* dispatcher, const std::string& name)
: InputDevice(
rootdevice->zxdev(), dispatcher,
[rootdevice](InputDevice* dev) { rootdevice->input_devices()->RemoveDevice(dev); }),
name_(name) {}
~FakeInputDevice() override { g_devices_.erase(name_); }
zx_status_t OnReport(const SensorReport& rpt) override {
std::vector<double> new_report;
if (rpt.name != name_) {
return ZX_ERR_INVALID_ARGS;
}
for (const auto& val : rpt.data) {
if (std::holds_alternative<std::string>(val)) {
return ZX_ERR_INVALID_ARGS;
}
new_report.push_back(std::get<Numeric>(val).Double());
}
report_ = std::move(new_report);
report_id_++;
return ZX_OK;
}
void GetInputReportsReader(GetInputReportsReaderRequestView request,
GetInputReportsReaderCompleter::Sync& completer) override {
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
void GetDescriptor(GetDescriptorCompleter::Sync& completer) override {
completer.Close(ZX_ERR_NOT_SUPPORTED);
}
static std::map<std::string, FakeInputDevice*> GetAllDevices() { return g_devices_; }
static void EraseAllDevices() {
for (const auto& kv : GetAllDevices()) {
delete kv.second;
}
}
std::vector<double> report() const { return report_; }
uint32_t report_id() const { return report_id_.load(); }
private:
// For test purposes only.
static inline std::map<std::string, FakeInputDevice*> g_devices_;
std::atomic<uint32_t> report_id_ = 0;
std::vector<double> report_;
std::string name_;
};
fpromise::result<InputDevice*, zx_status_t> CreateFakeDevice1(RootDevice* rootdevice,
async_dispatcher_t* dispatcher) {
return FakeInputDevice::Create(rootdevice, dispatcher, "fake1");
}
fpromise::result<InputDevice*, zx_status_t> CreateFakeDevice2(RootDevice* rootdevice,
async_dispatcher_t* dispatcher) {
return FakeInputDevice::Create(rootdevice, dispatcher, "fake2");
}
const std::map<uint64_t, InputDeviceInfo> kFakeDevices = {
{0x0001, {"fake1", CreateFakeDevice1}},
{0x0002, {"fake2", CreateFakeDevice2}},
};
class TestRootDevice : public RootDevice {
public:
using RootDevice::OnReadSensor;
using RootDevice::RootDevice;
};
struct IncomingNamespace {
testing::FakePipe fake_pipe_;
component::OutgoingDirectory outgoing_{async_get_default_dispatcher()};
};
class RootDeviceTest : public ::testing::Test {
public:
void SetUp() override {
ASSERT_EQ(incoming_loop_.StartThread("incoming-ns-thread"), ZX_OK);
zx::result endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
ASSERT_EQ(endpoints.status_value(), ZX_OK);
incoming_.SyncCall([server = std::move(endpoints->server)](IncomingNamespace* infra) mutable {
zx::result service_result =
infra->outgoing_.AddService<fuchsia_hardware_goldfish_pipe::Service>(
fuchsia_hardware_goldfish_pipe::Service::InstanceHandler({
.device = infra->fake_pipe_.bind_handler(async_get_default_dispatcher()),
}));
ASSERT_EQ(service_result.status_value(), ZX_OK);
ASSERT_EQ(infra->outgoing_.Serve(std::move(server)).status_value(), ZX_OK);
});
fake_parent_->AddFidlService(fuchsia_hardware_goldfish_pipe::Service::Name,
std::move(endpoints->client));
auto result = fdf::RunOnDispatcherSync(dispatcher_->async_dispatcher(), [&]() {
auto device = std::make_unique<TestRootDevice>(fake_parent_.get());
ASSERT_EQ(device->Bind(), ZX_OK);
// dut_ will be deleted by MockDevice when test ends.
dut_ = device.release();
});
EXPECT_EQ(result.status_value(), ZX_OK);
ASSERT_EQ(fake_parent_->child_count(), 1u);
}
void TearDown() override {
FakeInputDevice::EraseAllDevices();
if (dut_) {
auto result = fdf::RunOnDispatcherSync(dispatcher_->async_dispatcher(), [&]() {
device_async_remove(dut_->zxdev());
mock_ddk::ReleaseFlaggedDevices(fake_parent_.get());
});
EXPECT_EQ(result.status_value(), ZX_OK);
}
}
protected:
std::shared_ptr<MockDevice> fake_parent_ = MockDevice::FakeRootParent();
fdf::UnownedSynchronizedDispatcher dispatcher_ =
fdf_testing::DriverRuntime::GetInstance()->StartBackgroundDispatcher();
TestRootDevice* dut_;
async::Loop incoming_loop_{&kAsyncLoopConfigNoAttachToCurrentThread};
async_patterns::TestDispatcherBound<IncomingNamespace> incoming_{incoming_loop_.dispatcher(),
std::in_place};
};
TEST_F(RootDeviceTest, SetupDevices) {
bool list_sensors_called = false;
incoming_.SyncCall([&list_sensors_called](IncomingNamespace* infra) mutable {
infra->fake_pipe_.SetOnCmdWriteCallback(
[pipe = &infra->fake_pipe_, &list_sensors_called](const std::vector<uint8_t>& cmd) {
const char* kCmdExpected = "000clist-sensors";
if (memcmp(cmd.data(), kCmdExpected, strlen(kCmdExpected)) == 0) {
list_sensors_called = true;
const char* kFrameLength = "0004";
const char* kFrameContents = "0001";
pipe->EnqueueBytesToRead(
std::vector<uint8_t>(kFrameLength, kFrameLength + strlen(kFrameLength)));
pipe->EnqueueBytesToRead(
std::vector<uint8_t>(kFrameContents, kFrameContents + strlen(kFrameContents)));
}
});
});
auto result = fdf::RunOnDispatcherSync(dispatcher_->async_dispatcher(),
[&]() { ASSERT_EQ(dut_->Setup(kFakeDevices), ZX_OK); });
EXPECT_EQ(result.status_value(), ZX_OK);
EXPECT_EQ(FakeInputDevice::GetAllDevices().size(), 1u);
EXPECT_TRUE(list_sensors_called);
// Only fake1 is set.
incoming_.SyncCall([](IncomingNamespace* infra) mutable {
const char* kSetFake1 = "000bset:fake1:1";
EXPECT_EQ(
memcmp(infra->fake_pipe_.io_buffer_contents().back().data(), kSetFake1, strlen(kSetFake1)),
0);
});
}
TEST_F(RootDeviceTest, SetupMultipleDevices) {
bool list_sensors_called = false;
incoming_.SyncCall([&list_sensors_called](IncomingNamespace* infra) mutable {
infra->fake_pipe_.SetOnCmdWriteCallback(
[pipe = &infra->fake_pipe_, &list_sensors_called](const std::vector<uint8_t>& cmd) {
const char* kCmdExpected = "000clist-sensors";
if (memcmp(cmd.data(), kCmdExpected, strlen(kCmdExpected)) == 0) {
list_sensors_called = true;
const char* kFrameLength = "0004";
const char* kFrameContents = "0003";
pipe->EnqueueBytesToRead(
std::vector<uint8_t>(kFrameLength, kFrameLength + strlen(kFrameLength)));
pipe->EnqueueBytesToRead(
std::vector<uint8_t>(kFrameContents, kFrameContents + strlen(kFrameContents)));
}
});
});
auto result = fdf::RunOnDispatcherSync(dispatcher_->async_dispatcher(),
[&]() { ASSERT_EQ(dut_->Setup(kFakeDevices), ZX_OK); });
EXPECT_EQ(result.status_value(), ZX_OK);
EXPECT_EQ(FakeInputDevice::GetAllDevices().size(), 2u);
EXPECT_TRUE(list_sensors_called);
// Both fake1 and fake2 are set.
incoming_.SyncCall([](IncomingNamespace* infra) mutable {
const char* kSetFake1 = "000bset:fake1:1";
const char* kSetFake2 = "000bset:fake2:1";
EXPECT_EQ(memcmp(infra->fake_pipe_.io_buffer_contents().rbegin()->data(), kSetFake2,
strlen(kSetFake2)),
0);
EXPECT_EQ(memcmp((++infra->fake_pipe_.io_buffer_contents().rbegin())->data(), kSetFake1,
strlen(kSetFake1)),
0);
});
}
TEST_F(RootDeviceTest, DispatchSensorReports) {
// Set list-sensors mask to 0x03, enabling both fake1 and fake2 devices.
bool list_sensors_called = false;
incoming_.SyncCall([&list_sensors_called](IncomingNamespace* infra) mutable {
infra->fake_pipe_.SetOnCmdWriteCallback(
[pipe = &infra->fake_pipe_, &list_sensors_called](const std::vector<uint8_t>& cmd) {
const char* kCmdExpected = "000clist-sensors";
if (memcmp(cmd.data(), kCmdExpected, strlen(kCmdExpected)) == 0) {
list_sensors_called = true;
const char* kFrameLength = "0004";
const char* kFrameContents = "0003";
pipe->EnqueueBytesToRead(
std::vector<uint8_t>(kFrameLength, kFrameLength + strlen(kFrameLength)));
pipe->EnqueueBytesToRead(
std::vector<uint8_t>(kFrameContents, kFrameContents + strlen(kFrameContents)));
}
});
});
auto result = fdf::RunOnDispatcherSync(dispatcher_->async_dispatcher(),
[&]() { ASSERT_EQ(dut_->Setup(kFakeDevices), ZX_OK); });
EXPECT_EQ(result.status_value(), ZX_OK);
EXPECT_EQ(FakeInputDevice::GetAllDevices().size(), 2u);
EXPECT_TRUE(list_sensors_called);
auto fake1 = FakeInputDevice::GetAllDevices().at("fake1");
auto fake2 = FakeInputDevice::GetAllDevices().at("fake2");
auto fake1_report_id = fake1->report_id();
auto fake2_report_id = fake2->report_id();
const char* kFake1Report = "fake1:0.1:0.2";
PipeIo::ReadResult<char> read_result =
zx::ok(std::string(kFake1Report, kFake1Report + strlen(kFake1Report)));
dut_->OnReadSensor(std::move(read_result));
EXPECT_EQ(fake1->report_id(), fake1_report_id + 1);
EXPECT_EQ(fake2->report_id(), fake2_report_id);
EXPECT_EQ(fake1->report().size(), 2u);
EXPECT_EQ(fake1->report()[0], 0.1);
EXPECT_EQ(fake1->report()[1], 0.2);
const char* kFake2Report = "fake2:0:0.2:0.3";
read_result = zx::ok(std::string(kFake2Report, kFake2Report + strlen(kFake2Report)));
dut_->OnReadSensor(std::move(read_result));
EXPECT_EQ(fake1->report_id(), fake1_report_id + 1);
EXPECT_EQ(fake2->report_id(), fake2_report_id + 1);
EXPECT_EQ(fake2->report().size(), 3u);
EXPECT_EQ(fake2->report()[0], 0);
EXPECT_EQ(fake2->report()[1], 0.2);
EXPECT_EQ(fake2->report()[2], 0.3);
const char* kFake3Report = "fake3:1:2:3:4";
read_result = zx::ok(std::string(kFake3Report, kFake3Report + strlen(kFake3Report)));
dut_->OnReadSensor(std::move(read_result));
EXPECT_EQ(fake1->report_id(), fake1_report_id + 1);
EXPECT_EQ(fake2->report_id(), fake2_report_id + 1);
}
} // namespace
} // namespace goldfish::sensor