Commit 4adefbad authored by gdk@chromium.org's avatar gdk@chromium.org

Adding tests for USB extension API

Includes test coverage for the recently-fixed zero-length transfer bug.

BUG=140390
TEST=self


Review URL: https://chromiumcodereview.appspot.com/10824298

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@151777 0039d316-1c4b-4281-b951-d872f2087c98
parent 701acb30
......@@ -25,6 +25,12 @@ namespace IsochronousTransfer =
using extensions::api::experimental_usb::Device;
using std::vector;
namespace {
static UsbDevice* device_for_test_ = NULL;
} // namespace
namespace extensions {
UsbAsyncApiFunction::UsbAsyncApiFunction()
......@@ -43,6 +49,10 @@ UsbFindDeviceFunction::UsbFindDeviceFunction() : event_notifier_(NULL) {}
UsbFindDeviceFunction::~UsbFindDeviceFunction() {}
void UsbFindDeviceFunction::SetDeviceForTest(UsbDevice* device) {
device_for_test_ = device;
}
bool UsbFindDeviceFunction::Prepare() {
parameters_ = FindDevice::Params::Create(*args_);
EXTENSION_FUNCTION_VALIDATE(parameters_.get());
......@@ -51,12 +61,16 @@ bool UsbFindDeviceFunction::Prepare() {
}
void UsbFindDeviceFunction::Work() {
UsbService* const service =
UsbServiceFactory::GetInstance()->GetForProfile(profile());
DCHECK(service) << "No UsbService associated with profile.";
UsbDevice* device = NULL;
if (device_for_test_) {
device = device_for_test_;
} else {
UsbService* const service = UsbServiceFactory::GetInstance()->GetForProfile(
profile());
device = service->FindDevice(parameters_->vendor_id,
parameters_->product_id);
}
UsbDevice* const device = service->FindDevice(parameters_->vendor_id,
parameters_->product_id);
if (!device) {
SetResult(base::Value::CreateNullValue());
return;
......@@ -87,6 +101,10 @@ bool UsbCloseDeviceFunction::Prepare() {
}
void UsbCloseDeviceFunction::Work() {
UsbDeviceResource* const device = manager_->Get(parameters_->device.handle);
if (device)
device->Close();
manager_->Remove(parameters_->device.handle);
}
......
......@@ -11,6 +11,8 @@
#include "chrome/browser/extensions/api/api_resource_manager.h"
#include "chrome/common/extensions/api/experimental_usb.h"
class UsbDevice;
namespace extensions {
class ApiResourceEventNotifier;
......@@ -34,6 +36,8 @@ class UsbFindDeviceFunction : public UsbAsyncApiFunction {
UsbFindDeviceFunction();
static void SetDeviceForTest(UsbDevice* device);
protected:
virtual ~UsbFindDeviceFunction();
virtual bool Prepare() OVERRIDE;
......
// Copyright (c) 2012 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/api/usb/usb_api.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/usb/usb_service.h"
#include "chrome/browser/usb/usb_service_factory.h"
#include "chrome/common/chrome_switches.h"
#include "net/base/io_buffer.h"
#include "testing/gmock/include/gmock/gmock.h"
using testing::AnyNumber;
using testing::_;
namespace {
ACTION_TEMPLATE(InvokeUsbTransferCallback,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(p1)) {
::std::tr1::get<k>(args).Run(p1);
}
// MSVC erroneously thinks that at least one of the arguments for the transfer
// methods differ by const or volatility and emits a warning about the old
// standards-noncompliant behaviour of their compiler.
#if defined(OS_WIN)
#pragma warning(push)
#pragma warning(disable:4373)
#endif
class MockUsbDevice : public UsbDevice {
public:
MockUsbDevice() : UsbDevice() {}
MOCK_METHOD0(Close, void(void));
MOCK_METHOD10(ControlTransfer, void(const TransferDirection direction,
const TransferRequestType request_type, const TransferRecipient recipient,
const uint8 request, const uint16 value, const uint16 index,
net::IOBuffer* buffer, const size_t length, const unsigned int timeout,
const UsbTransferCallback& callback));
MOCK_METHOD6(BulkTransfer, void(const TransferDirection direction,
const uint8 endpoint, net::IOBuffer* buffer, const size_t length,
const unsigned int timeout, const UsbTransferCallback& callback));
MOCK_METHOD6(InterruptTransfer, void(const TransferDirection direction,
const uint8 endpoint, net::IOBuffer* buffer, const size_t length,
const unsigned int timeout, const UsbTransferCallback& callback));
MOCK_METHOD8(IsochronousTransfer, void(const TransferDirection direction,
const uint8 endpoint, net::IOBuffer* buffer, const size_t length,
const unsigned int packets, const unsigned int packet_length,
const unsigned int timeout, const UsbTransferCallback& callback));
protected:
virtual ~MockUsbDevice() {}
};
#if defined(OS_WIN)
#pragma warning(pop)
#endif
class UsbApiTest : public ExtensionApiTest {
public:
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
ExtensionApiTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
}
void SetUpOnMainThread() OVERRIDE {
mock_device_ = new MockUsbDevice();
extensions::UsbFindDeviceFunction::SetDeviceForTest(mock_device_.get());
}
protected:
scoped_refptr<MockUsbDevice> mock_device_;
};
} // namespace
IN_PROC_BROWSER_TEST_F(UsbApiTest, DeviceHandling) {
EXPECT_CALL(*mock_device_, Close()).Times(3);
ASSERT_TRUE(RunExtensionTest("usb/device_handling"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferEvent) {
EXPECT_CALL(*mock_device_,
ControlTransfer(UsbDevice::OUTBOUND, UsbDevice::STANDARD,
UsbDevice::DEVICE, 1, 2, 3, _, 1, _, _))
.WillOnce(InvokeUsbTransferCallback<9>(USB_TRANSFER_COMPLETED));
EXPECT_CALL(*mock_device_,
BulkTransfer(UsbDevice::OUTBOUND, 1, _, 1, _, _))
.WillOnce(InvokeUsbTransferCallback<5>(USB_TRANSFER_COMPLETED));
EXPECT_CALL(*mock_device_,
InterruptTransfer(UsbDevice::OUTBOUND, 2, _, 1, _, _))
.WillOnce(InvokeUsbTransferCallback<5>(USB_TRANSFER_COMPLETED));
EXPECT_CALL(*mock_device_,
IsochronousTransfer(UsbDevice::OUTBOUND, 3, _, 1, 1, 1, _, _))
.WillOnce(InvokeUsbTransferCallback<7>(USB_TRANSFER_COMPLETED));
EXPECT_CALL(*mock_device_, Close()).Times(AnyNumber());
ASSERT_TRUE(RunExtensionTest("usb/transfer_event"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, ZeroLengthTransfer) {
EXPECT_CALL(*mock_device_, BulkTransfer(_, _, _, 0, _, _))
.WillOnce(InvokeUsbTransferCallback<5>(USB_TRANSFER_COMPLETED));
EXPECT_CALL(*mock_device_, Close());
ASSERT_TRUE(RunExtensionTest("usb/zero_length_transfer"));
}
IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferFailure) {
EXPECT_CALL(*mock_device_, BulkTransfer(_, _, _, _, _, _))
.WillOnce(InvokeUsbTransferCallback<5>(USB_TRANSFER_COMPLETED))
.WillOnce(InvokeUsbTransferCallback<5>(USB_TRANSFER_ERROR))
.WillOnce(InvokeUsbTransferCallback<5>(USB_TRANSFER_TIMEOUT));
EXPECT_CALL(*mock_device_, Close()).Times(AnyNumber());
ASSERT_TRUE(RunExtensionTest("usb/transfer_failure"));
}
......@@ -157,7 +157,13 @@ UsbDeviceResource::UsbDeviceResource(ApiResourceEventNotifier* notifier,
UsbDevice* device)
: ApiResource(notifier), device_(device) {}
UsbDeviceResource::~UsbDeviceResource() {}
UsbDeviceResource::~UsbDeviceResource() {
Close();
}
void UsbDeviceResource::Close() {
device_->Close();
}
void UsbDeviceResource::ControlTransfer(const ControlTransferInfo& transfer) {
UsbDevice::TransferDirection direction;
......
......@@ -32,6 +32,8 @@ class UsbDeviceResource : public ApiResource {
UsbDeviceResource(ApiResourceEventNotifier* notifier, UsbDevice* device);
virtual ~UsbDeviceResource();
void Close();
// All of the *Transfer variants that are exposed here adapt their arguments
// for the underlying UsbDevice's interface and invoke the corresponding
// methods with completion callbacks that call OnTransferComplete on the event
......
......@@ -99,6 +99,8 @@ UsbDevice::UsbDevice(UsbService* service, PlatformUsbDeviceHandle handle)
DCHECK(handle) << "Cannot create device with NULL handle.";
}
UsbDevice::UsbDevice() : service_(NULL), handle_(NULL) {}
UsbDevice::~UsbDevice() {}
void UsbDevice::Close() {
......
......@@ -55,41 +55,41 @@ class UsbDevice : public base::RefCounted<UsbDevice> {
PlatformUsbDeviceHandle handle() { return handle_; }
// Close the USB device and release the underlying platform device.
void Close();
void ControlTransfer(const TransferDirection direction,
const TransferRequestType request_type,
const TransferRecipient recipient,
const uint8 request,
const uint16 value,
const uint16 index,
net::IOBuffer* buffer,
const size_t length,
const unsigned int timeout,
const UsbTransferCallback& callback);
void BulkTransfer(const TransferDirection direction,
const uint8 endpoint,
net::IOBuffer* buffer,
const size_t length,
const unsigned int timeout,
const UsbTransferCallback& callback);
void InterruptTransfer(const TransferDirection direction,
const uint8 endpoint,
net::IOBuffer* buffer,
const size_t length,
const unsigned int timeout,
const UsbTransferCallback& callback);
void IsochronousTransfer(const TransferDirection direction,
const uint8 endpoint,
net::IOBuffer* buffer,
const size_t length,
const unsigned int packets,
const unsigned int packet_length,
const unsigned int timeout,
const UsbTransferCallback& callback);
virtual void Close();
virtual void ControlTransfer(const TransferDirection direction,
const TransferRequestType request_type,
const TransferRecipient recipient,
const uint8 request,
const uint16 value,
const uint16 index,
net::IOBuffer* buffer,
const size_t length,
const unsigned int timeout,
const UsbTransferCallback& callback);
virtual void BulkTransfer(const TransferDirection direction,
const uint8 endpoint,
net::IOBuffer* buffer,
const size_t length,
const unsigned int timeout,
const UsbTransferCallback& callback);
virtual void InterruptTransfer(const TransferDirection direction,
const uint8 endpoint,
net::IOBuffer* buffer,
const size_t length,
const unsigned int timeout,
const UsbTransferCallback& callback);
virtual void IsochronousTransfer(const TransferDirection direction,
const uint8 endpoint,
net::IOBuffer* buffer,
const size_t length,
const unsigned int packets,
const unsigned int packet_length,
const unsigned int timeout,
const UsbTransferCallback& callback);
// Normal code should not call this function. It is called by the platform's
// callback mechanism in such a way that it cannot be made private. Invokes
......@@ -97,6 +97,13 @@ class UsbDevice : public base::RefCounted<UsbDevice> {
// in-flight transfer set.
void TransferComplete(PlatformUsbTransferHandle transfer);
protected:
// This constructor variant is for use in testing only.
UsbDevice();
friend class base::RefCounted<UsbDevice>;
virtual ~UsbDevice();
private:
struct Transfer {
Transfer();
......@@ -106,9 +113,6 @@ class UsbDevice : public base::RefCounted<UsbDevice> {
UsbTransferCallback callback;
};
friend class base::RefCounted<UsbDevice>;
virtual ~UsbDevice();
// Checks that the device has not yet been closed.
void CheckDevice();
......
......@@ -2776,6 +2776,7 @@
'browser/extensions/api/tabs/tabs_test.cc',
'browser/extensions/api/terminal/terminal_private_apitest.cc',
'browser/extensions/api/test/apitest_apitest.cc',
'browser/extensions/api/usb/usb_apitest.cc',
'browser/extensions/api/web_navigation/web_navigation_apitest.cc',
'browser/extensions/api/web_request/web_request_apitest.cc',
'browser/extensions/api/web_socket_proxy_private/web_socket_proxy_private_apitest.cc',
......
{
"name": "USB Device Handling",
"version": "1.0",
"manifest_version": 2,
"background": {
"scripts": ["test.js"]
},
"permissions": [
"experimental"
]
}
// Copyright (c) 2012 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.
var usb = chrome.experimental.usb;
var tests = [
function implicitCloseDevice() {
usb.findDevice(0, 0, {}, function(device) {
chrome.test.succeed();
});
},
function explicitCloseDevice() {
usb.findDevice(0, 0, {}, function(device) {
usb.closeDevice(device);
chrome.test.succeed();
});
},
];
chrome.test.runTests(tests);
{
"name": "USB Transfer Events",
"version": "1.0",
"manifest_version": 2,
"background": {
"scripts": ["test.js"]
},
"permissions": [
"experimental"
]
}
// Copyright (c) 2012 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.
var usb = chrome.experimental.usb;
function handler() {
var handlerObject = new Object();
handlerObject.onEvent = chrome.test.callbackAdded();
return handlerObject;
}
var tests = [
function controlTransfer() {
usb.findDevice(0, 0, handler(), function (device) {
var transfer = new Object();
transfer.direction = "out";
transfer.recipient = "device";
transfer.requestType = "standard";
transfer.request = 1;
transfer.value = 2;
transfer.index = 3;
transfer.data = new ArrayBuffer(1);
usb.controlTransfer(device, transfer);
});
},
function bulkTransfer() {
usb.findDevice(0, 0, handler(), function (device) {
var transfer = new Object();
transfer.direction = "out";
transfer.endpoint = 1;
transfer.data = new ArrayBuffer(1);
usb.bulkTransfer(device, transfer);
});
},
function interruptTransfer() {
usb.findDevice(0, 0, handler(), function (device) {
var transfer = new Object();
transfer.direction = "out";
transfer.endpoint = 2;
transfer.data = new ArrayBuffer(1);
usb.interruptTransfer(device, transfer);
});
},
function isochronousTransfer() {
usb.findDevice(0, 0, handler(), function (device) {
var transfer = new Object();
transfer.direction = "out";
transfer.endpoint = 3;
transfer.data = new ArrayBuffer(1);
var isoTransfer = new Object();
isoTransfer.transferInfo = transfer;
isoTransfer.packets = 1;
isoTransfer.packetLength = 1;
usb.isochronousTransfer(device, isoTransfer);
});
},
];
chrome.test.runTests(tests);
{
"name": "USB Transfer Failure",
"version": "1.0",
"manifest_version": 2,
"background": {
"scripts": ["test.js"]
},
"permissions": [
"experimental"
]
}
// Copyright (c) 2012 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.
var usb = chrome.experimental.usb;
function createErrorTest(resultCode, errorMessage) {
return function() {
var handler = new Object();
handler.onEvent = (function (callback) {
return function (usbEvent) {
chrome.test.assertTrue(resultCode == usbEvent.resultCode);
chrome.test.assertTrue(errorMessage == usbEvent.error);
callback();
}
})(chrome.test.callbackAdded());
usb.findDevice(0, 0, handler, function(device) {
var transfer = new Object();
transfer.direction = "out";
transfer.endpoint = 1;
transfer.data = new ArrayBuffer(0);
usb.bulkTransfer(device, transfer);
});
};
}
var tests = [
createErrorTest(0, undefined),
createErrorTest(1, "Transfer failed"),
createErrorTest(2, "Transfer timed out"),
];
chrome.test.runTests(tests);
{
"name": "USB Zero-Length Transfer",
"version": "1.0",
"manifest_version": 2,
"background": {
"scripts": ["test.js"]
},
"permissions": [
"experimental"
]
}
// Copyright (c) 2012 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.
var usb = chrome.experimental.usb;
function handler() {
var handlerObject = new Object();
handlerObject.onEvent = chrome.test.callbackAdded();
return handlerObject;
}
var tests = [
function zeroLengthTransfer() {
usb.findDevice(0, 0, handler(), function(device) {
var transfer = new Object();
transfer.direction = "out";
transfer.endpoint = 1;
transfer.data = new ArrayBuffer(0);
usb.bulkTransfer(device, transfer);
});
},
];
chrome.test.runTests(tests);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment