blob: 1e5185bb1581cb7e47568f1a573e4db691adf3c1 [file] [log] [blame]
// 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