| // Copyright 2017 The Chromium 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 "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/run_loop.h" |
| #include "build/build_config.h" |
| #include "mojo/public/cpp/bindings/associated_binding.h" |
| #include "services/device/device_service_test_base.h" |
| #include "services/device/hid/hid_manager_impl.h" |
| #include "services/device/hid/mock_hid_connection.h" |
| #include "services/device/hid/mock_hid_service.h" |
| #include "services/device/public/mojom/constants.mojom.h" |
| #include "services/device/public/mojom/hid.mojom.h" |
| |
| namespace device { |
| |
| namespace { |
| |
| #if defined(OS_MACOSX) |
| const uint64_t kTestDeviceIds[] = {1, 2}; |
| #else |
| const char* kTestDeviceIds[] = {"A", "B"}; |
| #endif |
| |
| class MockHidManagerClient : public mojom::HidManagerClient { |
| public: |
| MockHidManagerClient() : binding_(this) {} |
| |
| void Bind(mojom::HidManagerClientAssociatedRequest request) { |
| binding_.Bind(std::move(request)); |
| } |
| |
| void DeviceAdded(mojom::HidDeviceInfoPtr device_info) override { |
| EXPECT_EQ(expect_guid_, device_info->guid); |
| std::move(quit_closure_).Run(); |
| } |
| |
| void DeviceRemoved(mojom::HidDeviceInfoPtr device_info) override { |
| EXPECT_EQ(expect_guid_, device_info->guid); |
| std::move(quit_closure_).Run(); |
| } |
| |
| void SetConnection(mojom::HidConnectionPtr hid_connection) { |
| hid_connection_ = std::move(hid_connection); |
| } |
| |
| mojom::HidConnection* GetConnection() { return hid_connection_.get(); } |
| |
| void SetQuitClosure(base::OnceClosure quit_closure) { |
| quit_closure_ = std::move(quit_closure); |
| } |
| |
| void SetExpectGUID(std::string guid) { expect_guid_ = guid; } |
| |
| private: |
| mojo::AssociatedBinding<mojom::HidManagerClient> binding_; |
| mojom::HidConnectionPtr hid_connection_; |
| base::OnceClosure quit_closure_; |
| std::string expect_guid_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockHidManagerClient); |
| }; |
| |
| void OnGetDevices(base::OnceClosure quit_closure, |
| size_t expect, |
| std::vector<mojom::HidDeviceInfoPtr> devices) { |
| EXPECT_EQ(expect, devices.size()); |
| |
| std::move(quit_closure).Run(); |
| } |
| |
| void OnConnect(base::OnceClosure quit_closure, |
| MockHidManagerClient* client, |
| mojom::HidConnectionPtr connection) { |
| DCHECK(client); |
| DCHECK(connection); |
| client->SetConnection(std::move(connection)); |
| |
| std::move(quit_closure).Run(); |
| } |
| |
| void OnRead(base::OnceClosure quit_closure, |
| bool success, |
| uint8_t report_id, |
| const base::Optional<std::vector<uint8_t>>& buffer) { |
| EXPECT_TRUE(success); |
| |
| DCHECK(buffer); |
| const char expected[] = "TestRead"; |
| EXPECT_EQ(report_id, 1); |
| EXPECT_EQ(memcmp(buffer->data(), expected, sizeof(expected) - 1), 0); |
| EXPECT_EQ(buffer->size(), sizeof(expected) - 1); |
| |
| std::move(quit_closure).Run(); |
| } |
| |
| void OnWrite(base::OnceClosure quit_closure, bool success) { |
| EXPECT_TRUE(success); |
| |
| std::move(quit_closure).Run(); |
| } |
| |
| void OnGetFeatureReport(base::OnceClosure quit_closure, |
| bool success, |
| const base::Optional<std::vector<uint8_t>>& buffer) { |
| EXPECT_TRUE(success); |
| |
| DCHECK(buffer); |
| const char expected[] = "TestGetFeatureReport"; |
| EXPECT_EQ(memcmp(buffer->data(), expected, sizeof(expected) - 1), 0); |
| EXPECT_EQ(buffer->size(), sizeof(expected) - 1); |
| |
| std::move(quit_closure).Run(); |
| } |
| |
| class HidManagerTest : public DeviceServiceTestBase { |
| public: |
| HidManagerTest() {} |
| |
| void SetUp() override { |
| DeviceServiceTestBase::SetUp(); |
| auto mock_hid_service = std::make_unique<MockHidService>(); |
| mock_hid_service_ = mock_hid_service.get(); |
| // Transfer the ownership of the |mock_hid_service| to HidManagerImpl. |
| // It is safe to use the |mock_hid_service_| in this test. |
| HidManagerImpl::SetHidServiceForTesting(std::move(mock_hid_service)); |
| connector()->BindInterface(mojom::kServiceName, &hid_manager_); |
| } |
| |
| void TearDown() override { HidManagerImpl::SetHidServiceForTesting(nullptr); } |
| |
| void AddDevice(scoped_refptr<HidDeviceInfo> device_info) { |
| mock_hid_service_->AddDevice(device_info); |
| } |
| |
| void RemoveDevice(const HidPlatformDeviceId& platform_device_id) { |
| mock_hid_service_->RemoveDevice(platform_device_id); |
| } |
| |
| mojom::HidManagerPtr hid_manager_; |
| MockHidService* mock_hid_service_; |
| |
| DISALLOW_COPY_AND_ASSIGN(HidManagerTest); |
| }; |
| |
| // Test the GetDevices. |
| TEST_F(HidManagerTest, GetDevicesOnly) { |
| // Add two hid devices |
| auto device0 = base::MakeRefCounted<HidDeviceInfo>( |
| kTestDeviceIds[0], 0, 0, "Hid Service Unit Test", "HidDevice-0", |
| mojom::HidBusType::kHIDBusTypeUSB, std::vector<uint8_t>()); |
| |
| auto device1 = base::MakeRefCounted<HidDeviceInfo>( |
| kTestDeviceIds[1], 0, 0, "Hid Service Unit Test", "HidDevice-1", |
| mojom::HidBusType::kHIDBusTypeUSB, std::vector<uint8_t>()); |
| mock_hid_service_->AddDevice(device0); |
| mock_hid_service_->AddDevice(device1); |
| mock_hid_service_->FirstEnumerationComplete(); |
| |
| // Expect two devices will be received in OnGetDevices(). |
| base::RunLoop run_loop; |
| hid_manager_->GetDevices( |
| base::BindOnce(&OnGetDevices, run_loop.QuitClosure(), 2)); |
| run_loop.Run(); |
| } |
| |
| // Test the GetDevicesAndSetClient and the mojom::HidManagerClient |
| // interface. |
| TEST_F(HidManagerTest, GetDevicesAndSetClient) { |
| // Add one hid device. |
| auto device0 = base::MakeRefCounted<HidDeviceInfo>( |
| kTestDeviceIds[0], 0, 0, "Hid Service Unit Test", "HidDevice-0", |
| mojom::HidBusType::kHIDBusTypeUSB, std::vector<uint8_t>()); |
| mock_hid_service_->AddDevice(device0); |
| mock_hid_service_->FirstEnumerationComplete(); |
| |
| auto client = std::make_unique<MockHidManagerClient>(); |
| mojom::HidManagerClientAssociatedPtrInfo hid_manager_client; |
| client->Bind(mojo::MakeRequest(&hid_manager_client)); |
| |
| // Call GetDevicesAndSetClient, expect 1 device will be received in |
| // OnGetDevices(). |
| { |
| base::RunLoop run_loop; |
| hid_manager_->GetDevicesAndSetClient( |
| std::move(hid_manager_client), |
| base::BindOnce(&OnGetDevices, run_loop.QuitClosure(), 1)); |
| run_loop.Run(); |
| } |
| |
| // Add another hid device, expect MockHidManagerClient::DeviceAdded() will be |
| // called, and the guid should be same as expected. |
| auto device1 = base::MakeRefCounted<HidDeviceInfo>( |
| kTestDeviceIds[1], 0, 0, "Hid Service Unit Test", "HidDevice-1", |
| mojom::HidBusType::kHIDBusTypeUSB, std::vector<uint8_t>()); |
| mock_hid_service_->AddDevice(device1); |
| { |
| base::RunLoop run_loop; |
| client->SetQuitClosure(run_loop.QuitClosure()); |
| client->SetExpectGUID(device1->device_guid()); |
| run_loop.Run(); |
| } |
| |
| // Remove one hid device, expect MockHidManagerClient::DeviceRemoved() will be |
| // called, and the guid should be same as expected. |
| mock_hid_service_->RemoveDevice(kTestDeviceIds[0]); |
| { |
| base::RunLoop run_loop; |
| client->SetQuitClosure(run_loop.QuitClosure()); |
| client->SetExpectGUID(device0->device_guid()); |
| run_loop.Run(); |
| } |
| } |
| |
| // Test the Connect and the mojom::HidConnection interface. |
| TEST_F(HidManagerTest, TestHidConnectionInterface) { |
| // Add one hid device. |
| auto c_info = mojom::HidCollectionInfo::New(); |
| c_info->usage = mojom::HidUsageAndPage::New(1, 0xf1d0); |
| auto device0 = base::MakeRefCounted<HidDeviceInfo>( |
| kTestDeviceIds[0], 0, 0, "Hid Service Unit Test", "HidDevice-0", |
| mojom::HidBusType::kHIDBusTypeUSB, std::move(c_info), 64, 64, 64); |
| mock_hid_service_->AddDevice(device0); |
| mock_hid_service_->FirstEnumerationComplete(); |
| |
| auto client = std::make_unique<MockHidManagerClient>(); |
| mojom::HidManagerClientAssociatedPtrInfo hid_manager_client; |
| client->Bind(mojo::MakeRequest(&hid_manager_client)); |
| |
| // Call GetDevicesAndSetClient, expect 1 device will be received in |
| // OnGetDevices(). |
| { |
| base::RunLoop run_loop; |
| hid_manager_->GetDevicesAndSetClient( |
| std::move(hid_manager_client), |
| base::BindOnce(&OnGetDevices, run_loop.QuitClosure(), 1)); |
| run_loop.Run(); |
| } |
| |
| // Connect and save the HidConnection InterfacePtr into MockHidManagerClient. |
| { |
| base::RunLoop run_loop; |
| hid_manager_->Connect( |
| device0->device_guid(), /*connection_client=*/nullptr, |
| base::BindOnce(&OnConnect, run_loop.QuitClosure(), client.get())); |
| run_loop.Run(); |
| } |
| |
| // Test mojom::HidConnection::Read(). |
| { |
| base::RunLoop run_loop; |
| client->GetConnection()->Read( |
| base::BindOnce(&OnRead, run_loop.QuitClosure())); |
| run_loop.Run(); |
| } |
| |
| // Test mojom::HidConnection::Write(). |
| { |
| base::RunLoop run_loop; |
| client->GetConnection()->Write( |
| 0, /* report_id */ |
| {}, base::BindOnce(&OnWrite, run_loop.QuitClosure())); |
| run_loop.Run(); |
| } |
| |
| // Test mojom::HidConnection::GetFeatureReport(). |
| { |
| base::RunLoop run_loop; |
| client->GetConnection()->GetFeatureReport( |
| 0, /* report_id*/ |
| base::BindOnce(&OnGetFeatureReport, run_loop.QuitClosure())); |
| run_loop.Run(); |
| } |
| |
| // Test mojom::HidConnection::SendFeatureReport(). |
| { |
| base::RunLoop run_loop; |
| // The callback of SendFeatureReport() is same as Write(). |
| client->GetConnection()->SendFeatureReport( |
| 0, /*report_id*/ |
| {}, base::BindOnce(&OnWrite, run_loop.QuitClosure())); |
| run_loop.Run(); |
| } |
| } |
| |
| } // namespace |
| |
| } // namespace device |