| // 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 <stddef.h> |
| |
| #include <numeric> |
| |
| #include "base/memory/ptr_util.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/test/test_utils.h" |
| #include "device/base/mock_device_client.h" |
| #include "device/usb/mock_usb_device.h" |
| #include "device/usb/mock_usb_device_handle.h" |
| #include "device/usb/mock_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" |
| |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::Invoke; |
| using testing::Return; |
| using device::MockDeviceClient; |
| using device::MockUsbDevice; |
| using device::MockUsbDeviceHandle; |
| using device::UsbConfigDescriptor; |
| using device::UsbControlTransferRecipient; |
| using device::UsbControlTransferType; |
| using device::UsbDeviceHandle; |
| using device::UsbInterfaceDescriptor; |
| using device::UsbTransferDirection; |
| using device::UsbTransferStatus; |
| |
| 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 = nullptr; |
| size_t length = 0; |
| if (p1 != UsbTransferStatus::TRANSFER_ERROR) { |
| length = 1; |
| io_buffer = new net::IOBuffer(length); |
| memset(io_buffer->data(), 0, length); // Avoid uninitialized reads. |
| } |
| ::std::tr1::get<k>(args).Run(p1, io_buffer, 1); |
| } |
| |
| ACTION_P2(InvokeUsbIsochronousTransferOutCallback, |
| transferred_length, |
| success_packets) { |
| std::vector<UsbDeviceHandle::IsochronousPacket> packets(arg2.size()); |
| for (size_t i = 0; i < packets.size(); ++i) { |
| packets[i].length = arg2[i]; |
| if (i < success_packets) { |
| packets[i].transferred_length = transferred_length; |
| packets[i].status = UsbTransferStatus::COMPLETED; |
| } else { |
| packets[i].transferred_length = 0; |
| packets[i].status = UsbTransferStatus::TRANSFER_ERROR; |
| } |
| } |
| arg4.Run(arg1, packets); |
| } |
| |
| ACTION_P2(InvokeUsbIsochronousTransferInCallback, |
| transferred_length, |
| success_packets) { |
| size_t total_length = std::accumulate(arg1.begin(), arg1.end(), 0u); |
| net::IOBuffer* io_buffer = new net::IOBuffer(total_length); |
| memset(io_buffer->data(), 0, total_length); // Avoid uninitialized reads. |
| std::vector<UsbDeviceHandle::IsochronousPacket> packets(arg1.size()); |
| for (size_t i = 0; i < packets.size(); ++i) { |
| packets[i].length = arg1[i]; |
| packets[i].transferred_length = transferred_length; |
| if (i < success_packets) { |
| packets[i].transferred_length = transferred_length; |
| packets[i].status = UsbTransferStatus::COMPLETED; |
| } else { |
| packets[i].transferred_length = 0; |
| packets[i].status = UsbTransferStatus::TRANSFER_ERROR; |
| } |
| } |
| arg3.Run(io_buffer, packets); |
| } |
| |
| ACTION_P(SetConfiguration, mock_device) { |
| mock_device->ActiveConfigurationChanged(arg0); |
| arg1.Run(true); |
| } |
| |
| class TestDevicePermissionsPrompt |
| : public DevicePermissionsPrompt, |
| public DevicePermissionsPrompt::Prompt::Observer { |
| public: |
| explicit TestDevicePermissionsPrompt(content::WebContents* web_contents) |
| : DevicePermissionsPrompt(web_contents) {} |
| |
| void ShowDialog() override { prompt()->SetObserver(this); } |
| |
| void OnDeviceAdded(size_t index, const base::string16& device_name) override { |
| OnDevicesChanged(); |
| } |
| |
| void OnDeviceRemoved(size_t index, |
| const base::string16& device_name) override { |
| OnDevicesChanged(); |
| } |
| |
| private: |
| void OnDevicesChanged() { |
| 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() {} |
| |
| std::unique_ptr<DevicePermissionsPrompt> CreateDevicePermissionsPrompt( |
| content::WebContents* web_contents) const override { |
| return base::MakeUnique<TestDevicePermissionsPrompt>(web_contents); |
| } |
| }; |
| |
| class UsbApiTest : public ShellApiTest { |
| public: |
| void SetUpOnMainThread() override { |
| ShellApiTest::SetUpOnMainThread(); |
| |
| // MockDeviceClient replaces ShellDeviceClient. |
| device_client_.reset(new MockDeviceClient()); |
| |
| std::vector<UsbConfigDescriptor> configs; |
| configs.emplace_back(1, false, false, 0); |
| configs.emplace_back(2, false, false, 0); |
| |
| mock_device_ = new MockUsbDevice(0, 0, "Test Manufacturer", "Test Device", |
| "ABC123", configs); |
| mock_device_handle_ = new MockUsbDeviceHandle(mock_device_.get()); |
| EXPECT_CALL(*mock_device_, Open(_)) |
| .WillRepeatedly(InvokeCallback<0>(mock_device_handle_)); |
| device_client_->usb_service()->AddDevice(mock_device_); |
| } |
| |
| protected: |
| scoped_refptr<MockUsbDeviceHandle> mock_device_handle_; |
| scoped_refptr<MockUsbDevice> mock_device_; |
| std::unique_ptr<MockDeviceClient> device_client_; |
| }; |
| |
| } // namespace |
| |
| IN_PROC_BROWSER_TEST_F(UsbApiTest, DeviceHandling) { |
| mock_device_->ActiveConfigurationChanged(1); |
| EXPECT_CALL(*mock_device_handle_, Close()).Times(2); |
| ASSERT_TRUE(RunAppTest("api_test/usb/device_handling")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UsbApiTest, ResetDevice) { |
| EXPECT_CALL(*mock_device_handle_, Close()).Times(2); |
| EXPECT_CALL(*mock_device_handle_, ResetDevice(_)) |
| .WillOnce(InvokeCallback<0>(true)) |
| .WillOnce(InvokeCallback<0>(false)); |
| EXPECT_CALL(*mock_device_handle_, |
| GenericTransfer(UsbTransferDirection::OUTBOUND, 2, _, 1, _, _)) |
| .WillOnce(InvokeUsbTransferCallback<5>(UsbTransferStatus::COMPLETED)); |
| ASSERT_TRUE(RunAppTest("api_test/usb/reset_device")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UsbApiTest, SetConfiguration) { |
| EXPECT_CALL(*mock_device_handle_, SetConfiguration(1, _)) |
| .WillOnce(SetConfiguration(mock_device_.get())); |
| EXPECT_CALL(*mock_device_handle_, Close()).Times(1); |
| ASSERT_TRUE(RunAppTest("api_test/usb/set_configuration")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UsbApiTest, ListInterfaces) { |
| mock_device_->ActiveConfigurationChanged(1); |
| EXPECT_CALL(*mock_device_handle_, Close()).Times(1); |
| ASSERT_TRUE(RunAppTest("api_test/usb/list_interfaces")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferEvent) { |
| EXPECT_CALL( |
| *mock_device_handle_, |
| ControlTransfer(UsbTransferDirection::OUTBOUND, |
| UsbControlTransferType::STANDARD, |
| UsbControlTransferRecipient::DEVICE, 1, 2, 3, _, 1, _, _)) |
| .WillOnce(InvokeUsbTransferCallback<9>(UsbTransferStatus::COMPLETED)); |
| EXPECT_CALL(*mock_device_handle_, |
| GenericTransfer(UsbTransferDirection::OUTBOUND, 1, _, 1, _, _)) |
| .WillOnce(InvokeUsbTransferCallback<5>(UsbTransferStatus::COMPLETED)); |
| EXPECT_CALL(*mock_device_handle_, |
| GenericTransfer(UsbTransferDirection::OUTBOUND, 2, _, 1, _, _)) |
| .WillOnce(InvokeUsbTransferCallback<5>(UsbTransferStatus::COMPLETED)); |
| EXPECT_CALL(*mock_device_handle_, IsochronousTransferOut(3, _, _, _, _)) |
| .WillOnce(InvokeUsbIsochronousTransferOutCallback(1, 1u)); |
| EXPECT_CALL(*mock_device_handle_, Close()).Times(AnyNumber()); |
| ASSERT_TRUE(RunAppTest("api_test/usb/transfer_event")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UsbApiTest, ZeroLengthTransfer) { |
| EXPECT_CALL(*mock_device_handle_, GenericTransfer(_, _, _, 0, _, _)) |
| .WillOnce(InvokeUsbTransferCallback<5>(UsbTransferStatus::COMPLETED)); |
| EXPECT_CALL(*mock_device_handle_, Close()).Times(AnyNumber()); |
| ASSERT_TRUE(RunAppTest("api_test/usb/zero_length_transfer")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferFailure) { |
| EXPECT_CALL(*mock_device_handle_, |
| GenericTransfer(UsbTransferDirection::OUTBOUND, 1, _, _, _, _)) |
| .WillOnce(InvokeUsbTransferCallback<5>(UsbTransferStatus::COMPLETED)) |
| .WillOnce(InvokeUsbTransferCallback<5>(UsbTransferStatus::TRANSFER_ERROR)) |
| .WillOnce(InvokeUsbTransferCallback<5>(UsbTransferStatus::TIMEOUT)); |
| EXPECT_CALL(*mock_device_handle_, IsochronousTransferIn(2, _, _, _)) |
| .WillOnce(InvokeUsbIsochronousTransferInCallback(8, 10u)) |
| .WillOnce(InvokeUsbIsochronousTransferInCallback(8, 5u)); |
| EXPECT_CALL(*mock_device_handle_, Close()).Times(AnyNumber()); |
| ASSERT_TRUE(RunAppTest("api_test/usb/transfer_failure")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UsbApiTest, InvalidLengthTransfer) { |
| EXPECT_CALL(*mock_device_handle_, Close()).Times(AnyNumber()); |
| ASSERT_TRUE(RunAppTest("api_test/usb/invalid_length_transfer")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UsbApiTest, InvalidTimeout) { |
| EXPECT_CALL(*mock_device_handle_, 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)); |
| device_client_->usb_service()->AddDevice(device); |
| |
| device = new MockUsbDevice(0x18D1, 0x58F1); |
| device_client_->usb_service()->AddDevice(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()); |
| |
| device_client_->usb_service()->RemoveDevice(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_, Close()).Times(1); |
| |
| TestExtensionsAPIClient test_api_client; |
| ASSERT_TRUE(LoadApp("api_test/usb/get_user_selected_devices")); |
| ASSERT_TRUE(ready_listener.WaitUntilSatisfied()); |
| |
| device_client_->usb_service()->RemoveDevice(mock_device_); |
| ASSERT_TRUE(result_listener.WaitUntilSatisfied()); |
| } |
| |
| } // namespace extensions |