blob: f4620eeb60ca3d4f3da2eeaf4e70f631be7951fa [file] [log] [blame]
// Copyright 2018 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 "chrome/browser/usb/web_usb_service_impl.h"
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "device/base/mock_device_client.h"
#include "device/usb/mock_usb_device.h"
#include "device/usb/mock_usb_service.h"
#include "device/usb/mojo/device_impl.h"
#include "device/usb/mojo/mock_permission_provider.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::AtMost;
using blink::mojom::WebUsbServicePtr;
using device::mojom::UsbDeviceInfo;
using device::mojom::UsbDeviceInfoPtr;
using device::mojom::UsbDeviceManagerClient;
using device::mojom::UsbDeviceManagerClientPtr;
using device::MockUsbDevice;
using device::usb::MockPermissionProvider;
namespace {
ACTION_P2(ExpectGuidAndThen, expected_guid, callback) {
ASSERT_TRUE(arg0);
EXPECT_EQ(expected_guid, arg0->guid);
if (!callback.is_null())
callback.Run();
};
class WebUsbServiceImplTest : public testing::Test {
public:
WebUsbServiceImplTest() = default;
~WebUsbServiceImplTest() override = default;
protected:
WebUsbServicePtr ConnectToService() {
WebUsbServicePtr service;
WebUsbServiceImpl::Create(permission_provider_.GetWeakPtr(),
mojo::MakeRequest(&service));
return service;
}
device::MockDeviceClient device_client_;
private:
MockPermissionProvider permission_provider_;
base::test::ScopedTaskEnvironment task_environment_;
};
class MockDeviceManagerClient : public UsbDeviceManagerClient {
public:
MockDeviceManagerClient() : binding_(this) {}
~MockDeviceManagerClient() override = default;
UsbDeviceManagerClientPtr CreateInterfacePtrAndBind() {
UsbDeviceManagerClientPtr client;
binding_.Bind(mojo::MakeRequest(&client));
return client;
}
MOCK_METHOD1(DoOnDeviceAdded, void(UsbDeviceInfo*));
void OnDeviceAdded(UsbDeviceInfoPtr device_info) override {
DoOnDeviceAdded(device_info.get());
}
MOCK_METHOD1(DoOnDeviceRemoved, void(UsbDeviceInfo*));
void OnDeviceRemoved(UsbDeviceInfoPtr device_info) override {
DoOnDeviceRemoved(device_info.get());
}
private:
mojo::Binding<UsbDeviceManagerClient> binding_;
};
void ExpectDevicesAndThen(const std::set<std::string>& expected_guids,
base::OnceClosure continuation,
std::vector<UsbDeviceInfoPtr> results) {
EXPECT_EQ(expected_guids.size(), results.size());
std::set<std::string> actual_guids;
for (size_t i = 0; i < results.size(); ++i)
actual_guids.insert(results[i]->guid);
EXPECT_EQ(expected_guids, actual_guids);
std::move(continuation).Run();
}
} // namespace
// Test requesting device enumeration updates with GetDeviceChanges.
TEST_F(WebUsbServiceImplTest, NoPermissionDevice) {
scoped_refptr<MockUsbDevice> device0 =
new MockUsbDevice(0x1234, 0x5678, "ACME", "Frobinator", "ABCDEF");
scoped_refptr<MockUsbDevice> device1 =
new MockUsbDevice(0x1234, 0x5679, "ACME", "Frobinator+", "GHIJKL");
scoped_refptr<MockUsbDevice> no_permission_device1 =
new MockUsbDevice(0xffff, 0x567b, "ACME", "Frobinator II",
MockPermissionProvider::kRestrictedSerialNumber);
scoped_refptr<MockUsbDevice> no_permission_device2 =
new MockUsbDevice(0xffff, 0x567c, "ACME", "Frobinator Xtreme",
MockPermissionProvider::kRestrictedSerialNumber);
device_client_.usb_service()->AddDevice(device0);
device_client_.usb_service()->AddDevice(no_permission_device1);
WebUsbServicePtr web_usb_service = ConnectToService();
MockDeviceManagerClient mock_client;
web_usb_service->SetClient(mock_client.CreateInterfacePtrAndBind());
{
// Call GetDevices once to make sure the WebUsbService is up and running
// and the client is set or else we could block forever waiting for calls.
// The site has no permission to access |no_permission_device1| and
// |no_permission_device2|, so result of GetDevices() should only contain
// the |guid| of |device0|.
std::set<std::string> guids;
guids.insert(device0->guid());
base::RunLoop loop;
web_usb_service->GetDevices(
base::BindOnce(&ExpectDevicesAndThen, guids, loop.QuitClosure()));
loop.Run();
}
device_client_.usb_service()->AddDevice(device1);
device_client_.usb_service()->AddDevice(no_permission_device2);
device_client_.usb_service()->RemoveDevice(device0);
device_client_.usb_service()->RemoveDevice(device1);
device_client_.usb_service()->RemoveDevice(no_permission_device1);
device_client_.usb_service()->RemoveDevice(no_permission_device2);
{
base::RunLoop loop;
base::RepeatingClosure barrier =
base::BarrierClosure(3, loop.QuitClosure());
testing::InSequence s;
EXPECT_CALL(mock_client, DoOnDeviceAdded(_))
.Times(1)
.WillOnce(ExpectGuidAndThen(device1->guid(), barrier));
EXPECT_CALL(mock_client, DoOnDeviceRemoved(_))
.Times(2)
.WillOnce(ExpectGuidAndThen(device0->guid(), barrier))
.WillOnce(ExpectGuidAndThen(device1->guid(), barrier));
loop.Run();
}
}