blob: a1bdc83d8cb331ed55c63f3d4f59738fd7efdd9d [file] [log] [blame]
// Copyright 2014 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/extensions/extension_apitest.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_utils.h"
#include "device/usb/usb_service.h"
#include "extensions/browser/api/device_permissions_prompt.h"
#include "extensions/browser/api/usb/usb_api.h"
#include "extensions/shell/browser/shell_extensions_api_client.h"
#include "extensions/shell/test/shell_apitest.h"
#include "extensions/test/extension_test_message_listener.h"
#include "net/base/io_buffer.h"
#include "testing/gmock/include/gmock/gmock.h"
using testing::_;
using testing::AnyNumber;
using testing::Invoke;
using testing::Return;
using content::BrowserThread;
using device::UsbConfigDescriptor;
using device::UsbDevice;
using device::UsbDeviceHandle;
using device::UsbEndpointDirection;
using device::UsbInterfaceDescriptor;
using device::UsbService;
namespace extensions {
namespace {
ACTION_TEMPLATE(InvokeCallback,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(p1)) {
::std::tr1::get<k>(args).Run(p1);
}
ACTION_TEMPLATE(InvokeUsbTransferCallback,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(p1)) {
net::IOBuffer* io_buffer = new net::IOBuffer(1);
memset(io_buffer->data(), 0, 1); // Avoid uninitialized reads.
::std::tr1::get<k>(args).Run(p1, io_buffer, 1);
}
class TestDevicePermissionsPrompt
: public DevicePermissionsPrompt,
public DevicePermissionsPrompt::Prompt::Observer {
public:
TestDevicePermissionsPrompt(content::WebContents* web_contents)
: DevicePermissionsPrompt(web_contents) {}
void ShowDialog() override { prompt()->SetObserver(this); }
void OnDevicesChanged() override {
for (size_t i = 0; i < prompt()->GetDeviceCount(); ++i) {
prompt()->GrantDevicePermission(i);
if (!prompt()->multiple()) {
break;
}
}
prompt()->Dismissed();
}
};
class TestExtensionsAPIClient : public ShellExtensionsAPIClient {
public:
TestExtensionsAPIClient() : ShellExtensionsAPIClient() {}
scoped_ptr<DevicePermissionsPrompt> CreateDevicePermissionsPrompt(
content::WebContents* web_contents) const override {
return make_scoped_ptr(new TestDevicePermissionsPrompt(web_contents));
}
};
class MockUsbDeviceHandle : public UsbDeviceHandle {
public:
MockUsbDeviceHandle() : UsbDeviceHandle() {}
MOCK_METHOD0(Close, void());
MOCK_METHOD10(ControlTransfer,
void(UsbEndpointDirection direction,
TransferRequestType request_type,
TransferRecipient recipient,
uint8 request,
uint16 value,
uint16 index,
scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int timeout,
const TransferCallback& callback));
MOCK_METHOD6(BulkTransfer,
void(UsbEndpointDirection direction,
uint8 endpoint,
scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int timeout,
const TransferCallback& callback));
MOCK_METHOD6(InterruptTransfer,
void(UsbEndpointDirection direction,
uint8 endpoint,
scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int timeout,
const TransferCallback& callback));
MOCK_METHOD8(IsochronousTransfer,
void(UsbEndpointDirection direction,
uint8 endpoint,
scoped_refptr<net::IOBuffer> buffer,
size_t length,
unsigned int packets,
unsigned int packet_length,
unsigned int timeout,
const TransferCallback& callback));
MOCK_METHOD1(ResetDevice, void(const ResultCallback& callback));
MOCK_METHOD2(GetStringDescriptor, bool(uint8_t, base::string16*));
MOCK_METHOD2(SetConfiguration,
void(int configuration_value, const ResultCallback& callback));
MOCK_METHOD2(ClaimInterface,
void(int interface_number, const ResultCallback& callback));
MOCK_METHOD1(ReleaseInterface, bool(int interface_number));
MOCK_METHOD3(SetInterfaceAlternateSetting,
void(int interface_number,
int alternate_setting,
const ResultCallback& callback));
scoped_refptr<UsbDevice> GetDevice() const override { return device_; }
void set_device(UsbDevice* device) { device_ = device; }
protected:
UsbDevice* device_;
virtual ~MockUsbDeviceHandle() {}
};
class MockUsbDevice : public UsbDevice {
public:
MockUsbDevice(uint16 vendor_id, uint16 product_id, uint32 unique_id)
: UsbDevice(vendor_id,
product_id,
unique_id,
base::string16(),
base::string16(),
base::string16()) {}
MOCK_METHOD1(Open, void(const OpenCallback&));
MOCK_METHOD1(Close, bool(scoped_refptr<UsbDeviceHandle>));
MOCK_METHOD0(GetConfiguration, const device::UsbConfigDescriptor*());
private:
virtual ~MockUsbDevice() {}
};
class MockUsbService : public UsbService {
public:
explicit MockUsbService(scoped_refptr<UsbDevice> device) : device_(device) {}
// Public wrapper around the protected base class method.
void NotifyDeviceAdded(scoped_refptr<UsbDevice> device) {
UsbService::NotifyDeviceAdded(device);
}
// Public wrapper around the protected base class method.
void NotifyDeviceRemoved(scoped_refptr<UsbDevice> device) {
UsbService::NotifyDeviceRemoved(device);
}
protected:
scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override {
EXPECT_EQ(unique_id, 0U);
return device_;
}
void GetDevices(const GetDevicesCallback& callback) override {
std::vector<scoped_refptr<UsbDevice>> devices;
devices.push_back(device_);
callback.Run(devices);
}
scoped_refptr<UsbDevice> device_;
};
class UsbApiTest : public ShellApiTest {
public:
void SetUpOnMainThread() override {
ShellApiTest::SetUpOnMainThread();
mock_device_ = new MockUsbDevice(0, 0, 0);
mock_device_handle_ = new MockUsbDeviceHandle();
mock_device_handle_->set_device(mock_device_.get());
EXPECT_CALL(*mock_device_.get(), Open(_))
.WillRepeatedly(InvokeCallback<0>(mock_device_handle_));
mock_service_.reset(new MockUsbService(mock_device_));
}
protected:
scoped_refptr<MockUsbDeviceHandle> mock_device_handle_;
scoped_refptr<MockUsbDevice> mock_device_;
scoped_ptr<MockUsbService> mock_service_;
};
} // namespace
IN_PROC_BROWSER_TEST_F(UsbApiTest, DeviceHandling) {
EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(2);
ASSERT_TRUE(RunAppTest("api_test/usb/device_handling"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, ResetDevice) {
EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(2);
EXPECT_CALL(*mock_device_handle_.get(), ResetDevice(_))
.WillOnce(InvokeCallback<0>(true))
.WillOnce(InvokeCallback<0>(false));
EXPECT_CALL(*mock_device_handle_.get(),
InterruptTransfer(device::USB_DIRECTION_OUTBOUND, 2, _, 1, _, _))
.WillOnce(InvokeUsbTransferCallback<5>(device::USB_TRANSFER_COMPLETED));
ASSERT_TRUE(RunAppTest("api_test/usb/reset_device"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, SetConfiguration) {
UsbConfigDescriptor config_descriptor;
EXPECT_CALL(*mock_device_handle_.get(), SetConfiguration(1, _))
.WillOnce(InvokeCallback<1>(true));
EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(1);
EXPECT_CALL(*mock_device_.get(), GetConfiguration())
.WillOnce(Return(nullptr))
.WillOnce(Return(&config_descriptor));
ASSERT_TRUE(RunAppTest("api_test/usb/set_configuration"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, ListInterfaces) {
UsbConfigDescriptor config_descriptor;
EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(1);
EXPECT_CALL(*mock_device_.get(), GetConfiguration())
.WillOnce(Return(&config_descriptor));
ASSERT_TRUE(RunAppTest("api_test/usb/list_interfaces"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferEvent) {
EXPECT_CALL(*mock_device_handle_.get(),
ControlTransfer(device::USB_DIRECTION_OUTBOUND,
UsbDeviceHandle::STANDARD,
UsbDeviceHandle::DEVICE,
1,
2,
3,
_,
1,
_,
_))
.WillOnce(InvokeUsbTransferCallback<9>(device::USB_TRANSFER_COMPLETED));
EXPECT_CALL(*mock_device_handle_.get(),
BulkTransfer(device::USB_DIRECTION_OUTBOUND, 1, _, 1, _, _))
.WillOnce(InvokeUsbTransferCallback<5>(device::USB_TRANSFER_COMPLETED));
EXPECT_CALL(*mock_device_handle_.get(),
InterruptTransfer(device::USB_DIRECTION_OUTBOUND, 2, _, 1, _, _))
.WillOnce(InvokeUsbTransferCallback<5>(device::USB_TRANSFER_COMPLETED));
EXPECT_CALL(
*mock_device_handle_.get(),
IsochronousTransfer(device::USB_DIRECTION_OUTBOUND, 3, _, 1, 1, 1, _, _))
.WillOnce(InvokeUsbTransferCallback<7>(device::USB_TRANSFER_COMPLETED));
EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber());
ASSERT_TRUE(RunAppTest("api_test/usb/transfer_event"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, ZeroLengthTransfer) {
EXPECT_CALL(*mock_device_handle_.get(), BulkTransfer(_, _, _, 0, _, _))
.WillOnce(InvokeUsbTransferCallback<5>(device::USB_TRANSFER_COMPLETED));
EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber());
ASSERT_TRUE(RunAppTest("api_test/usb/zero_length_transfer"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferFailure) {
EXPECT_CALL(*mock_device_handle_.get(), BulkTransfer(_, _, _, _, _, _))
.WillOnce(InvokeUsbTransferCallback<5>(device::USB_TRANSFER_COMPLETED))
.WillOnce(InvokeUsbTransferCallback<5>(device::USB_TRANSFER_ERROR))
.WillOnce(InvokeUsbTransferCallback<5>(device::USB_TRANSFER_TIMEOUT));
EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber());
ASSERT_TRUE(RunAppTest("api_test/usb/transfer_failure"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, InvalidLengthTransfer) {
EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber());
ASSERT_TRUE(RunAppTest("api_test/usb/invalid_length_transfer"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, InvalidTimeout) {
EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber());
ASSERT_TRUE(RunAppTest("api_test/usb/invalid_timeout"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, OnDeviceAdded) {
ExtensionTestMessageListener load_listener("loaded", false);
ExtensionTestMessageListener result_listener("success", false);
result_listener.set_failure_message("failure");
ASSERT_TRUE(LoadApp("api_test/usb/add_event"));
ASSERT_TRUE(load_listener.WaitUntilSatisfied());
scoped_refptr<MockUsbDevice> device(new MockUsbDevice(0x18D1, 0x58F0, 1));
mock_service_->NotifyDeviceAdded(device);
device = new MockUsbDevice(0x18D1, 0x58F1, 2);
mock_service_->NotifyDeviceAdded(device);
ASSERT_TRUE(result_listener.WaitUntilSatisfied());
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, OnDeviceRemoved) {
ExtensionTestMessageListener load_listener("loaded", false);
ExtensionTestMessageListener result_listener("success", false);
result_listener.set_failure_message("failure");
ASSERT_TRUE(LoadApp("api_test/usb/remove_event"));
ASSERT_TRUE(load_listener.WaitUntilSatisfied());
mock_service_->NotifyDeviceRemoved(mock_device_);
ASSERT_TRUE(result_listener.WaitUntilSatisfied());
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, GetUserSelectedDevices) {
ExtensionTestMessageListener ready_listener("opened_device", false);
ExtensionTestMessageListener result_listener("success", false);
result_listener.set_failure_message("failure");
EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(1);
TestExtensionsAPIClient test_api_client;
ASSERT_TRUE(LoadApp("api_test/usb/get_user_selected_devices"));
ASSERT_TRUE(ready_listener.WaitUntilSatisfied());
mock_service_->NotifyDeviceRemoved(mock_device_);
ASSERT_TRUE(result_listener.WaitUntilSatisfied());
}
} // namespace extensions