blob: a25c70262fc38b430287c4af8ee9f6b3dc1aaffe [file] [log] [blame]
// Copyright 2019 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 <hid/boot.h>
#include <hid/buttons.h>
#include <hid/egalax.h>
#include <hid/paradise.h>
#include <hid/usages.h>
#include <set>
#include "garnet/bin/ui/input_reader/input_reader.h"
#include "garnet/bin/ui/input_reader/tests/mock_device_watcher.h"
#include "garnet/bin/ui/input_reader/tests/mock_hid_decoder.h"
#include "garnet/bin/ui/input_reader/tests/sensor_test_data.h"
#include "gtest/gtest.h"
#include "lib/gtest/test_loop_fixture.h"
#include "lib/ui/tests/mocks/mock_input_device.h"
#include "lib/ui/tests/mocks/mock_input_device_registry.h"
namespace ui_input {
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));
}
// Creates a |MockHidDecoder| with the supplied |args| and adds it to the
// |MockDeviceWatcher|, returning an |fxl::WeakPtr| to the new
// |MockHidDecoder|.
template <class... Args>
fxl::WeakPtr<MockHidDecoder> AddDevice(Args&&... args) {
auto device = std::make_unique<MockHidDecoder>(std::forward<Args>(args)...);
auto weak_device = device->GetWeakPtr();
device_watcher_->AddDevice(std::move(device));
return weak_device;
}
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_(nullptr,
[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_;
private:
void SetUp() override { StartInputReader(&input_reader_); }
MockInputDeviceRegistry registry_;
InputReader input_reader_;
};
} // namespace
TEST_F(ReaderInterpreterInputTest, BootMouse) {
// Create the MockHidDecoder as a BootMouse. Note that when a boot protocol is
// set, InputInterpreter never reads a report descriptor so it is not
// necessary to set one.
fxl::WeakPtr<MockHidDecoder> device = AddDevice(HidDecoder::BootMode::MOUSE);
RunLoopUntilIdle();
// Create a single boot mouse report.
hid_boot_mouse_report_t mouse_report = {};
mouse_report.rel_x = 50;
mouse_report.rel_y = 100;
uint8_t* mouse_report_bytes = reinterpret_cast<uint8_t*>(&mouse_report);
std::vector<uint8_t> report(mouse_report_bytes,
mouse_report_bytes + sizeof(mouse_report));
// Send the boot mouse report.
device->SetHidDecoderRead(report, sizeof(mouse_report));
RunLoopUntilIdle();
ASSERT_TRUE(last_report_.mouse);
EXPECT_EQ(last_report_.mouse->rel_x, 50);
EXPECT_EQ(last_report_.mouse->rel_y, 100);
}
TEST_F(ReaderInterpreterInputTest, BootKeyboard) {
fxl::WeakPtr<MockHidDecoder> device =
AddDevice(HidDecoder::BootMode::KEYBOARD);
RunLoopUntilIdle();
// A keyboard report is 8 bytes long, with bytes 3-8 containing HID usage
// codes.
device->SetHidDecoderRead({0, 0, HID_USAGE_KEY_A, 0, 0, 0, 0, 0}, 8);
RunLoopUntilIdle();
EXPECT_EQ(1, report_count_);
ASSERT_TRUE(last_report_.keyboard);
EXPECT_EQ(std::vector<uint32_t>{HID_USAGE_KEY_A},
last_report_.keyboard->pressed_keys);
device->SetHidDecoderRead(
{0, 0, HID_USAGE_KEY_A, HID_USAGE_KEY_Z, 0, 0, 0, 0}, 8);
RunLoopUntilIdle();
EXPECT_EQ(2, report_count_);
EXPECT_EQ(std::multiset<uint32_t>({HID_USAGE_KEY_A, HID_USAGE_KEY_Z}),
std::multiset<uint32_t>(last_report_.keyboard->pressed_keys.begin(),
last_report_.keyboard->pressed_keys.end()));
device->SetHidDecoderRead({0, 0, HID_USAGE_KEY_Z, 0, 0, 0, 0, 0}, 8);
RunLoopUntilIdle();
EXPECT_EQ(std::vector<uint32_t>{HID_USAGE_KEY_Z},
last_report_.keyboard->pressed_keys);
}
TEST_F(ReaderInterpreterInputTest, EgalaxTouchScreen) {
// Create the egalax report descriptor.
size_t desc_len;
const uint8_t* desc_data = get_egalax_touch_report_desc(&desc_len);
ASSERT_TRUE(desc_len > 0);
std::vector<uint8_t> report_descriptor(desc_data, desc_data + desc_len);
// Create the MockHidDecoder with our report descriptor.
fxl::WeakPtr<MockHidDecoder> device = AddDevice(report_descriptor);
RunLoopUntilIdle();
// Create a single touch report.
egalax_touch_t touch_report = {};
touch_report.report_id = EGALAX_RPT_ID_TOUCH;
touch_report.x = 100;
touch_report.y = 200;
touch_report.button_pad = 0xFF;
uint8_t* touch_report_bytes = reinterpret_cast<uint8_t*>(&touch_report);
std::vector<uint8_t> report(touch_report_bytes,
touch_report_bytes + sizeof(touch_report));
// Send the touch report.
device->SetHidDecoderRead(report, sizeof(touch_report));
RunLoopUntilIdle();
// Check that we saw one report, and that the data was sent out correctly.
ASSERT_EQ(1, report_count_);
ASSERT_TRUE(last_report_.touchscreen);
fuchsia::ui::input::Touch touch = last_report_.touchscreen->touches.at(0);
EXPECT_TRUE(touch.finger_id = 1);
EXPECT_TRUE(touch.x = 100);
EXPECT_TRUE(touch.y = 200);
}
TEST_F(ReaderInterpreterInputTest, ParadiseTouchscreen) {
// Create the paradise report descriptor.
size_t desc_len;
const uint8_t* desc_data = get_paradise_touch_report_desc(&desc_len);
ASSERT_TRUE(desc_len > 0);
std::vector<uint8_t> report_descriptor(desc_data, desc_data + desc_len);
// Create the MockHidDecoder with our report descriptor.
fxl::WeakPtr<MockHidDecoder> device = AddDevice(report_descriptor);
RunLoopUntilIdle();
std::vector<uint8_t> feature_report = device->GetLastOutputReport();
ASSERT_EQ(3U, feature_report.size());
uint8_t multitouch_enable_report[3] = {14, 2, 0};
for (int i = 0; i < 3; i++) {
EXPECT_EQ(multitouch_enable_report[i], feature_report[i]);
}
// Create a single touch report.
paradise_touch_t touch_report = {};
touch_report.rpt_id = PARADISE_RPT_ID_TOUCH;
touch_report.contact_count = 1;
touch_report.fingers[0].flags = 0xFF;
touch_report.fingers[0].finger_id = 1;
touch_report.fingers[0].x = 100;
touch_report.fingers[0].y = 200;
uint8_t* touch_report_bytes = reinterpret_cast<uint8_t*>(&touch_report);
std::vector<uint8_t> report(touch_report_bytes,
touch_report_bytes + sizeof(touch_report));
// Send the touch report.
device->SetHidDecoderRead(report, sizeof(touch_report));
RunLoopUntilIdle();
// Check that we saw one report, and that the data was sent out correctly.
ASSERT_EQ(1, report_count_);
ASSERT_TRUE(last_report_.touchscreen);
fuchsia::ui::input::Touch touch = last_report_.touchscreen->touches.at(0);
EXPECT_TRUE(touch.finger_id = 1);
EXPECT_TRUE(touch.x = 100);
EXPECT_TRUE(touch.y = 200);
}
TEST_F(ReaderInterpreterInputTest, ParadiseTouchpad) {
// Create the paradise report descriptor.
size_t desc_len;
const uint8_t* desc_data = get_paradise_touchpad_v1_report_desc(&desc_len);
ASSERT_TRUE(desc_len > 0);
std::vector<uint8_t> report_descriptor(desc_data, desc_data + desc_len);
// Create the MockHidDecoder with our report descriptor.
fxl::WeakPtr<MockHidDecoder> device = AddDevice(report_descriptor);
RunLoopUntilIdle();
// Create a single touch report.
paradise_touchpad_v1_t touch_report = {};
touch_report.report_id = 1;
touch_report.contact_count = 1;
touch_report.fingers[0].tip_switch = 1;
touch_report.fingers[0].id = 1;
touch_report.fingers[0].x = 100;
touch_report.fingers[0].y = 200;
uint8_t* touch_report_bytes = reinterpret_cast<uint8_t*>(&touch_report);
std::vector<uint8_t> report(touch_report_bytes,
touch_report_bytes + sizeof(touch_report));
// Send the touch report.
device->SetHidDecoderRead(report, sizeof(touch_report));
RunLoopUntilIdle();
// Check that we saw one report. Mice are relative so we shouldn't see
// any movement yet.
ASSERT_EQ(1, report_count_);
ASSERT_TRUE(last_report_.mouse);
EXPECT_EQ(0, last_report_.mouse->rel_x);
EXPECT_EQ(0, last_report_.mouse->rel_y);
// Send a second touch report.
touch_report.fingers[0].x = 150;
touch_report.fingers[0].y = 300;
report = std::vector<uint8_t>(touch_report_bytes,
touch_report_bytes + sizeof(touch_report));
// Send the touch report.
device->SetHidDecoderRead(report, sizeof(touch_report));
RunLoopUntilIdle();
ASSERT_EQ(2, report_count_);
ASSERT_TRUE(last_report_.mouse);
// These output values were hand calculated by converting the relative x
// and y to the units described by the paradise report.
EXPECT_EQ(39, last_report_.mouse->rel_x);
EXPECT_EQ(78, last_report_.mouse->rel_y);
}
TEST_F(ReaderInterpreterInputTest, SensorTest) {
// Create the paradise report descriptor.
size_t desc_len = sizeof(lightmeter_report_desc);
const uint8_t* desc_data = lightmeter_report_desc;
std::vector<uint8_t> report_descriptor(desc_data, desc_data + desc_len);
// Create the MockHidDecoder with our report descriptor.
fxl::WeakPtr<MockHidDecoder> device = AddDevice(report_descriptor);
RunLoopUntilIdle();
// Create a single light report.
uint8_t report_data[] = {
0x04, // Report ID
0x12, 0x24, // Illuminance
};
std::vector<uint8_t> report(report_data, report_data + sizeof(report_data));
// Send the touch report.
device->SetHidDecoderRead(report, sizeof(report_data));
RunLoopUntilIdle();
// Check that the report matches.
ASSERT_EQ(1, report_count_);
ASSERT_TRUE(last_report_.sensor);
EXPECT_EQ(0x2412, last_report_.sensor->scalar());
}
TEST_F(ReaderInterpreterInputTest, MediaButtonsTest) {
// Create the paradise report descriptor.
size_t desc_len;
const uint8_t* desc_data;
desc_len = get_buttons_report_desc(&desc_data);
std::vector<uint8_t> report_descriptor(desc_data, desc_data + desc_len);
// Create the MockHidDecoder with our report descriptor.
fxl::WeakPtr<MockHidDecoder> device = AddDevice(report_descriptor);
RunLoopUntilIdle();
// Create a single buttons report.
buttons_input_rpt_t report_data = {};
report_data.rpt_id = BUTTONS_RPT_ID_INPUT;
report_data.volume_up = true;
report_data.volume_down = false;
report_data.reset = true;
report_data.mute = true;
uint8_t* report_data_ptr = reinterpret_cast<uint8_t*>(&report_data);
std::vector<uint8_t> report(report_data_ptr,
report_data_ptr + sizeof(report_data));
// Send the touch report.
device->SetHidDecoderRead(report, sizeof(report_data));
RunLoopUntilIdle();
// Check that the report matches.
ASSERT_EQ(1, report_count_);
ASSERT_TRUE(last_report_.media_buttons);
EXPECT_EQ(true, last_report_.media_buttons->volume_up);
EXPECT_EQ(false, last_report_.media_buttons->volume_down);
EXPECT_EQ(true, last_report_.media_buttons->reset);
EXPECT_EQ(true, last_report_.media_buttons->mic_mute);
}
} // namespace ui_input