| // 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 "ft_device.h" |
| |
| #include <lib/async-loop/default.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/metadata.h> |
| #include <lib/fake-i2c/fake-i2c.h> |
| #include <lib/fdf/cpp/dispatcher.h> |
| #include <lib/focaltech/focaltech.h> |
| #include <lib/zx/clock.h> |
| #include <zircon/assert.h> |
| #include <zircon/errors.h> |
| |
| #include <cstddef> |
| |
| #include <gtest/gtest.h> |
| |
| #include "ft_firmware.h" |
| #include "lib/zx/result.h" |
| #include "src/devices/gpio/testing/fake-gpio/fake-gpio.h" |
| #include "src/devices/testing/mock-ddk/mock-device.h" |
| |
| namespace { |
| |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wc99-designator" |
| // Firmware must be at least 0x120 bytes. Add some extra size to make them different. |
| constexpr uint8_t kFirmware0[0x120 + 0] = {0x00, 0xd2, 0xc8, 0x53, [0x10a] = 0xd5}; |
| constexpr uint8_t kFirmware1[0x120 + 1] = {0x10, 0x58, 0xb2, 0x12, [0x10a] = 0xc8}; |
| constexpr uint8_t kFirmware2[0x120 + 2] = {0xb7, 0xf9, 0xd1, 0x12, [0x10a] = 0xb0}; |
| constexpr uint8_t kFirmware3[0x120 + 3] = {0x02, 0x69, 0x96, 0x71, [0x10a] = 0x61}; |
| #pragma GCC diagnostic pop |
| |
| } // namespace |
| |
| namespace ft { |
| |
| const FirmwareEntry kFirmwareEntries[] = { |
| { |
| .display_vendor = 0, |
| .ddic_version = 0, |
| .firmware_data = kFirmware0, |
| .firmware_size = sizeof(kFirmware0), |
| }, |
| { |
| .display_vendor = 1, |
| .ddic_version = 0, |
| .firmware_data = kFirmware1, |
| .firmware_size = sizeof(kFirmware1), |
| }, |
| { |
| .display_vendor = 0, |
| .ddic_version = 1, |
| .firmware_data = kFirmware2, |
| .firmware_size = sizeof(kFirmware2), |
| }, |
| { |
| .display_vendor = 1, |
| .ddic_version = 1, |
| .firmware_data = kFirmware3, |
| .firmware_size = sizeof(kFirmware3), |
| }, |
| }; |
| |
| const size_t kNumFirmwareEntries = std::size(kFirmwareEntries); |
| |
| class FakeFtDevice : public fake_i2c::FakeI2c { |
| public: |
| ~FakeFtDevice() { ZX_ASSERT(expected_report_.empty()); } |
| |
| uint32_t firmware_write_size() const { return firmware_write_size_; } |
| |
| void ExpectReport(uint8_t addr, std::vector<uint8_t> report) { |
| expected_report_.emplace(addr, std::move(report)); |
| } |
| |
| protected: |
| zx_status_t Transact(const uint8_t* write_buffer, size_t write_buffer_size, uint8_t* read_buffer, |
| size_t* read_buffer_size) override { |
| if (write_buffer_size < 1) { |
| return ZX_ERR_IO; |
| } |
| |
| *read_buffer_size = 0; |
| |
| if (write_buffer[0] == 0xa3) { // Chip core register |
| read_buffer[0] = 0x58; // Firmware is valid |
| *read_buffer_size = 1; |
| } else if (write_buffer[0] == 0xa6) { // Chip firmware version |
| read_buffer[0] = kFirmware1[0x10a]; // Set to a known version to test the up-to-date case |
| *read_buffer_size = 1; |
| } else if (write_buffer[0] == 0xfc && write_buffer_size == 2) { // Chip work mode |
| if (write_buffer[1] != 0xaa && write_buffer[1] != 0x55) { // Soft reset |
| return ZX_ERR_IO; |
| } |
| } else if (write_buffer[0] == 0xeb && write_buffer_size == 3) { // HID to STD |
| if (write_buffer[1] != 0xaa || write_buffer[2] != 0x09) { |
| return ZX_ERR_IO; |
| } |
| } else if (write_buffer[0] == 0x55 && write_buffer_size == 1) { // Unlock boot |
| } else if (write_buffer[0] == 0x90 && write_buffer_size == 1) { // Boot ID |
| read_buffer[0] = 0x58; |
| read_buffer[1] = 0x2c; |
| *read_buffer_size = 2; |
| } else if (write_buffer[0] == 0x09 && write_buffer_size == 2) { // Flash erase |
| if (write_buffer[1] != 0x0b) { // Erase app area |
| return ZX_ERR_IO; |
| } |
| } else if (write_buffer[0] == 0xb0 && write_buffer_size == 4) { // Set erase size |
| } else if (write_buffer[0] == 0x61 && write_buffer_size == 1) { // Start erase |
| ecc_ = 0; |
| flash_status_ = 0xf0aa; |
| } else if (write_buffer[0] == 0x6a && write_buffer_size == 1) { // Read flash status |
| read_buffer[0] = flash_status_ >> 8; |
| read_buffer[1] = flash_status_ & 0xff; |
| *read_buffer_size = 2; |
| } else if (write_buffer[0] == 0xbf && write_buffer_size >= 6) { // Firmware packet |
| const uint32_t address = (write_buffer[1] << 16) | (write_buffer[2] << 8) | write_buffer[3]; |
| const auto packet_size = static_cast<uint8_t>((write_buffer[4] << 8) | write_buffer[5]); |
| |
| if ((packet_size + 6) != write_buffer_size) { |
| return ZX_ERR_IO; |
| } |
| |
| for (uint32_t i = 6; i < write_buffer_size; i++) { |
| ecc_ ^= write_buffer[i]; |
| } |
| |
| flash_status_ = (0x1000 + (address / packet_size)) & 0xffff; |
| firmware_write_size_ += packet_size; // Ignore overlapping addresses. |
| } else if (write_buffer[0] == 0x64 && write_buffer_size == 1) { // ECC initialization |
| } else if (write_buffer[0] == 0x65 && write_buffer_size == 6) { // Start ECC calculation |
| flash_status_ = 0xf055; // ECC calculation done |
| } else if (write_buffer[0] == 0x66 && write_buffer_size == 1) { // Read calculated ECC |
| read_buffer[0] = ecc_; |
| *read_buffer_size = 1; |
| } else if (write_buffer[0] == 0x07 && write_buffer_size == 1) { // Reset |
| } else if ((write_buffer[0] == FTS_REG_TYPE || write_buffer[0] == FTS_REG_FIRMID || |
| write_buffer[0] == FTS_REG_VENDOR_ID || write_buffer[0] == FTS_REG_PANEL_ID || |
| write_buffer[0] == FTS_REG_RELEASE_ID_HIGH || |
| write_buffer[0] == FTS_REG_RELEASE_ID_LOW || |
| write_buffer[0] == FTS_REG_IC_VERSION) && |
| write_buffer_size == 1) { // LogRegisterValue |
| read_buffer[0] = 0; |
| *read_buffer_size = 1; |
| } else if (write_buffer_size == 1) { // Read report |
| EXPECT_FALSE(expected_report_.empty()); |
| EXPECT_EQ(write_buffer[0], expected_report_.front().first); |
| memcpy(read_buffer, expected_report_.front().second.data(), |
| expected_report_.front().second.size()); |
| *read_buffer_size = expected_report_.front().second.size(); |
| expected_report_.pop(); |
| } |
| |
| return ZX_OK; |
| } |
| |
| private: |
| uint16_t flash_status_ = 0; |
| uint8_t ecc_ = 0; |
| uint32_t firmware_write_size_ = 0; |
| |
| std::queue<std::pair<uint8_t, std::vector<uint8_t>>> expected_report_; // address, data pair |
| }; |
| |
| struct IncomingNamespace { |
| FakeFtDevice i2c_; |
| fake_gpio::FakeGpio interrupt_gpio_; |
| fake_gpio::FakeGpio reset_gpio_; |
| component::OutgoingDirectory i2c_fragment_outgoing_{async_get_default_dispatcher()}; |
| component::OutgoingDirectory interrupt_gpio_fragment_outgoing_{async_get_default_dispatcher()}; |
| component::OutgoingDirectory reset_gpio_fragment_outgoing_{async_get_default_dispatcher()}; |
| }; |
| |
| class FocaltechTest : public testing::Test { |
| public: |
| void SetUp() override { |
| ASSERT_EQ(ZX_OK, incoming_loop_.StartThread("incoming-ns-thread")); |
| |
| // I2c fragment |
| { |
| auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>(); |
| ZX_ASSERT(endpoints.is_ok()); |
| incoming_.SyncCall([server = std::move(endpoints->server)](IncomingNamespace* infra) mutable { |
| zx::result service_result = |
| infra->i2c_fragment_outgoing_.AddService<fuchsia_hardware_i2c::Service>( |
| fuchsia_hardware_i2c::Service::InstanceHandler( |
| {.device = infra->i2c_.bind_handler(async_get_default_dispatcher())})); |
| ZX_ASSERT(service_result.is_ok()); |
| ZX_ASSERT(infra->i2c_fragment_outgoing_.Serve(std::move(server)).is_ok()); |
| }); |
| fake_parent_->AddFidlService(fuchsia_hardware_i2c::Service::Name, |
| std::move(endpoints->client), "i2c"); |
| } |
| |
| // Reset gpio fragment |
| { |
| auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>(); |
| ZX_ASSERT(endpoints.is_ok()); |
| incoming_.SyncCall([server = std::move(endpoints->server)](IncomingNamespace* infra) mutable { |
| auto service_result = |
| infra->reset_gpio_fragment_outgoing_.AddService<fuchsia_hardware_gpio::Service>( |
| infra->reset_gpio_.CreateInstanceHandler()); |
| ZX_ASSERT(service_result.is_ok()); |
| ZX_ASSERT(infra->reset_gpio_fragment_outgoing_.Serve(std::move(server)).is_ok()); |
| }); |
| fake_parent_->AddFidlService(fuchsia_hardware_gpio::Service::Name, |
| std::move(endpoints->client), "gpio-reset"); |
| } |
| |
| // Interrupt gpio fragment |
| { |
| auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>(); |
| ZX_ASSERT(endpoints.is_ok()); |
| incoming_.SyncCall([server = std::move(endpoints->server)](IncomingNamespace* infra) mutable { |
| auto service_result = |
| infra->interrupt_gpio_fragment_outgoing_.AddService<fuchsia_hardware_gpio::Service>( |
| infra->interrupt_gpio_.CreateInstanceHandler()); |
| ZX_ASSERT(service_result.is_ok()); |
| ZX_ASSERT(infra->interrupt_gpio_fragment_outgoing_.Serve(std::move(server)).is_ok()); |
| }); |
| fake_parent_->AddFidlService(fuchsia_hardware_gpio::Service::Name, |
| std::move(endpoints->client), "gpio-int"); |
| } |
| |
| zx::interrupt interrupt; |
| ASSERT_EQ(ZX_OK, zx::interrupt::create(zx::resource(), 0, ZX_INTERRUPT_VIRTUAL, &interrupt)); |
| ASSERT_EQ(ZX_OK, interrupt.duplicate(ZX_RIGHT_SAME_RIGHTS, &irq_)); |
| incoming_.SyncCall([interrupt = std::move(interrupt)](IncomingNamespace* infra) mutable { |
| infra->interrupt_gpio_.SetInterrupt(zx::ok(std::move(interrupt))); |
| }); |
| } |
| |
| fidl::ClientEnd<fuchsia_input_report::InputDevice> CreateDut() { |
| auto result = fdf::RunOnDispatcherSync(dispatcher_->async_dispatcher(), [&]() { |
| EXPECT_EQ(ZX_OK, FtDevice::Create(nullptr, fake_parent_.get())); |
| }); |
| EXPECT_EQ(ZX_OK, result.status_value()); |
| EXPECT_EQ(size_t(1), fake_parent_->child_count()); |
| child_ = fake_parent_->GetLatestChild(); |
| dut_ = child_->GetDeviceContext<FtDevice>(); |
| VerifyGpioInit(); |
| |
| auto endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputDevice>(); |
| EXPECT_EQ(ZX_OK, endpoints.status_value()); |
| fidl::BindServer(dispatcher_->async_dispatcher(), std::move(endpoints->server), dut_); |
| return std::move(std::move(endpoints->client)); |
| } |
| |
| void TearDown() override { |
| auto result = fdf::RunOnDispatcherSync(dispatcher_->async_dispatcher(), [&]() { |
| device_async_remove(child_); |
| mock_ddk::ReleaseFlaggedDevices(fake_parent_.get()); |
| ; |
| }); |
| EXPECT_EQ(ZX_OK, result.status_value()); |
| } |
| |
| private: |
| void VerifyGpioInit() { |
| incoming_.SyncCall([](IncomingNamespace* infra) mutable { |
| std::vector interrupt_states = infra->interrupt_gpio_.GetStateLog(); |
| ASSERT_GE(interrupt_states.size(), size_t(1)); |
| ASSERT_EQ(fake_gpio::ReadSubState{.flags = fuchsia_hardware_gpio::GpioFlags::kNoPull}, |
| interrupt_states[0].sub_state); |
| |
| std::vector reset_states = infra->reset_gpio_.GetStateLog(); |
| ASSERT_GE(reset_states.size(), size_t(2)); |
| ASSERT_EQ(fake_gpio::WriteSubState{.value = 0}, reset_states[0].sub_state); |
| ASSERT_EQ(fake_gpio::WriteSubState{.value = 1}, reset_states[1].sub_state); |
| }); |
| } |
| |
| async::Loop incoming_loop_{&kAsyncLoopConfigNoAttachToCurrentThread}; |
| |
| protected: |
| std::shared_ptr<MockDevice> fake_parent_ = MockDevice::FakeRootParent(); |
| fdf::UnownedSynchronizedDispatcher dispatcher_ = |
| fdf_testing::DriverRuntime::GetInstance()->StartBackgroundDispatcher(); |
| zx::interrupt irq_; |
| zx_device_t* child_; |
| FtDevice* dut_; |
| async_patterns::TestDispatcherBound<IncomingNamespace> incoming_{incoming_loop_.dispatcher(), |
| std::in_place}; |
| }; |
| |
| void VerifyDescriptor(const fuchsia_input_report::wire::DeviceDescriptor& descriptor, int64_t x_max, |
| int64_t y_max) { |
| EXPECT_TRUE(descriptor.has_device_info()); |
| EXPECT_EQ(descriptor.device_info().vendor_id, |
| static_cast<uint32_t>(fuchsia_input_report::wire::VendorId::kGoogle)); |
| EXPECT_EQ(descriptor.device_info().product_id, |
| static_cast<uint32_t>( |
| fuchsia_input_report::wire::VendorGoogleProductId::kFocaltechTouchscreen)); |
| |
| EXPECT_TRUE(descriptor.has_touch()); |
| EXPECT_FALSE(descriptor.has_consumer_control()); |
| EXPECT_FALSE(descriptor.has_keyboard()); |
| EXPECT_FALSE(descriptor.has_mouse()); |
| EXPECT_FALSE(descriptor.has_sensor()); |
| |
| EXPECT_TRUE(descriptor.touch().has_input()); |
| EXPECT_FALSE(descriptor.touch().has_feature()); |
| |
| EXPECT_TRUE(descriptor.touch().input().has_touch_type()); |
| EXPECT_EQ(descriptor.touch().input().touch_type(), |
| fuchsia_input_report::wire::TouchType::kTouchscreen); |
| |
| EXPECT_TRUE(descriptor.touch().input().has_max_contacts()); |
| EXPECT_EQ(descriptor.touch().input().max_contacts(), uint32_t(10)); |
| |
| EXPECT_FALSE(descriptor.touch().input().has_buttons()); |
| EXPECT_TRUE(descriptor.touch().input().has_contacts()); |
| EXPECT_EQ(descriptor.touch().input().contacts().count(), size_t(10)); |
| |
| for (const auto& c : descriptor.touch().input().contacts()) { |
| EXPECT_TRUE(c.has_position_x()); |
| EXPECT_TRUE(c.has_position_y()); |
| EXPECT_FALSE(c.has_contact_height()); |
| EXPECT_FALSE(c.has_contact_width()); |
| EXPECT_FALSE(c.has_pressure()); |
| |
| EXPECT_EQ(c.position_x().range.min, 0); |
| EXPECT_EQ(c.position_x().range.max, x_max); |
| EXPECT_EQ(c.position_x().unit.type, fuchsia_input_report::wire::UnitType::kOther); |
| EXPECT_EQ(c.position_x().unit.exponent, 0); |
| |
| EXPECT_EQ(c.position_y().range.min, 0); |
| EXPECT_EQ(c.position_y().range.max, y_max); |
| EXPECT_EQ(c.position_y().unit.type, fuchsia_input_report::wire::UnitType::kOther); |
| EXPECT_EQ(c.position_y().unit.exponent, 0); |
| } |
| } |
| |
| TEST_F(FocaltechTest, Metadata3x27) { |
| constexpr FocaltechMetadata kFt3x27Metadata = { |
| .device_id = FOCALTECH_DEVICE_FT3X27, |
| .needs_firmware = false, |
| }; |
| fake_parent_->SetMetadata(DEVICE_METADATA_PRIVATE, &kFt3x27Metadata, sizeof(kFt3x27Metadata)); |
| |
| fidl::WireSyncClient<fuchsia_input_report::InputDevice> client(CreateDut()); |
| |
| auto result = client->GetDescriptor(); |
| EXPECT_TRUE(result.ok()); |
| VerifyDescriptor(result->descriptor, 600, 1024); |
| } |
| |
| TEST_F(FocaltechTest, Metadata5726) { |
| constexpr FocaltechMetadata kFt5726Metadata = { |
| .device_id = FOCALTECH_DEVICE_FT5726, |
| .needs_firmware = false, |
| }; |
| fake_parent_->SetMetadata(DEVICE_METADATA_PRIVATE, &kFt5726Metadata, sizeof(kFt5726Metadata)); |
| |
| fidl::WireSyncClient<fuchsia_input_report::InputDevice> client(CreateDut()); |
| |
| auto result = client->GetDescriptor(); |
| EXPECT_TRUE(result.ok()); |
| VerifyDescriptor(result->descriptor, 800, 1280); |
| } |
| |
| TEST_F(FocaltechTest, Metadata6336) { |
| constexpr FocaltechMetadata kFt6336Metadata = { |
| .device_id = FOCALTECH_DEVICE_FT6336, |
| .needs_firmware = false, |
| }; |
| fake_parent_->SetMetadata(DEVICE_METADATA_PRIVATE, &kFt6336Metadata, sizeof(kFt6336Metadata)); |
| |
| fidl::WireSyncClient<fuchsia_input_report::InputDevice> client(CreateDut()); |
| |
| auto result = client->GetDescriptor(); |
| EXPECT_TRUE(result.ok()); |
| VerifyDescriptor(result->descriptor, 480, 800); |
| } |
| |
| TEST_F(FocaltechTest, Firmware5726) { |
| constexpr FocaltechMetadata kFt5726Metadata = { |
| .device_id = FOCALTECH_DEVICE_FT5726, |
| .needs_firmware = true, |
| .display_vendor = 1, |
| .ddic_version = 1, |
| }; |
| fake_parent_->SetMetadata(DEVICE_METADATA_PRIVATE, &kFt5726Metadata, sizeof(kFt5726Metadata)); |
| |
| CreateDut(); |
| |
| incoming_.SyncCall([](IncomingNamespace* infra) mutable { |
| EXPECT_EQ(infra->i2c_.firmware_write_size(), sizeof(kFirmware3)); |
| }); |
| } |
| |
| TEST_F(FocaltechTest, Firmware5726UpToDate) { |
| constexpr FocaltechMetadata kFt5726Metadata = { |
| .device_id = FOCALTECH_DEVICE_FT5726, |
| .needs_firmware = true, |
| .display_vendor = 1, |
| .ddic_version = 0, |
| }; |
| fake_parent_->SetMetadata(DEVICE_METADATA_PRIVATE, &kFt5726Metadata, sizeof(kFt5726Metadata)); |
| |
| CreateDut(); |
| |
| incoming_.SyncCall([](IncomingNamespace* infra) mutable { |
| EXPECT_EQ(infra->i2c_.firmware_write_size(), uint32_t(0)); |
| }); |
| } |
| |
| TEST_F(FocaltechTest, Touch) { |
| constexpr FocaltechMetadata kFt6336Metadata = { |
| .device_id = FOCALTECH_DEVICE_FT6336, |
| .needs_firmware = false, |
| }; |
| fake_parent_->SetMetadata(DEVICE_METADATA_PRIVATE, &kFt6336Metadata, sizeof(kFt6336Metadata)); |
| |
| fidl::WireSyncClient<fuchsia_input_report::InputDevice> client(CreateDut()); |
| |
| auto reader_endpoints = fidl::CreateEndpoints<fuchsia_input_report::InputReportsReader>(); |
| ASSERT_EQ(ZX_OK, reader_endpoints.status_value()); |
| auto result = client->GetInputReportsReader(std::move(reader_endpoints->server)); |
| ASSERT_EQ(ZX_OK, result.status()); |
| auto reader = fidl::WireSyncClient<fuchsia_input_report::InputReportsReader>( |
| std::move(reader_endpoints->client)); |
| |
| ASSERT_EQ(ZX_OK, dut_->WaitForNextReader(zx::duration::infinite())); |
| |
| // clang-format off |
| static const uint8_t expected_report[] = { |
| 0x02, // contact_count |
| |
| // Contact 0, finger_id = 0 |
| 0x80, 0x01, // x = 0x001 |
| 0x00, 0x13, // y = 0x013 |
| 0x00, 0x00, |
| |
| // Contact 1, finger_id = 1 |
| 0x80, 0x31, // x = 0x031 |
| 0x10, 0x00, // y = 0x000 |
| 0x00, 0x00, |
| |
| // Contact 2 |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| // Contact 3 |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| // Contact 4 |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| // Contact 5 |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| // Contact 6 |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| // Contact 7 |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| // Contact 8 |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| |
| // Contact 9 |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| }; |
| // clang-format on |
| incoming_.SyncCall([](IncomingNamespace* infra) mutable { |
| for (size_t i = 0; i < sizeof(expected_report); i += 8) { |
| infra->i2c_.ExpectReport( |
| static_cast<uint8_t>(i + FTS_REG_CURPOINT), |
| std::vector<uint8_t>(expected_report + i, |
| expected_report + std::min(i + 8, sizeof(expected_report)))); |
| } |
| }); |
| irq_.trigger(0, zx::clock::get_monotonic()); |
| |
| { |
| auto result = reader->ReadInputReports(); |
| ASSERT_EQ(ZX_OK, result.status()); |
| ASSERT_FALSE(result.value().is_error()); |
| auto& reports = result.value().value()->reports; |
| |
| ASSERT_EQ(size_t(1), reports.count()); |
| auto report = reports[0]; |
| |
| ASSERT_TRUE(report.has_event_time()); |
| ASSERT_TRUE(report.has_touch()); |
| auto& touch_report = report.touch(); |
| |
| ASSERT_TRUE(touch_report.has_contacts()); |
| ASSERT_EQ(touch_report.contacts().count(), size_t(2)); |
| EXPECT_EQ(touch_report.contacts()[0].contact_id(), uint32_t(0)); |
| EXPECT_EQ(touch_report.contacts()[0].position_x(), 0x001); |
| EXPECT_EQ(touch_report.contacts()[0].position_y(), 0x013); |
| |
| EXPECT_EQ(touch_report.contacts()[1].contact_id(), uint32_t(1)); |
| EXPECT_EQ(touch_report.contacts()[1].position_x(), 0x031); |
| EXPECT_EQ(touch_report.contacts()[1].position_y(), 0x000); |
| } |
| } |
| |
| } // namespace ft |
| |
| int main(int argc, char** argv) { |
| testing::InitGoogleTest(&argc, argv); |
| return RUN_ALL_TESTS(); |
| } |