summaryrefslogtreecommitdiff
path: root/chromium/extensions/browser/api/hid/hid_apitest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/extensions/browser/api/hid/hid_apitest.cc')
-rw-r--r--chromium/extensions/browser/api/hid/hid_apitest.cc266
1 files changed, 266 insertions, 0 deletions
diff --git a/chromium/extensions/browser/api/hid/hid_apitest.cc b/chromium/extensions/browser/api/hid/hid_apitest.cc
new file mode 100644
index 00000000000..706419dd393
--- /dev/null
+++ b/chromium/extensions/browser/api/hid/hid_apitest.cc
@@ -0,0 +1,266 @@
+// 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 <stdint.h>
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/thread_task_runner_handle.h"
+#include "build/build_config.h"
+#include "device/core/mock_device_client.h"
+#include "device/hid/hid_collection_info.h"
+#include "device/hid/hid_connection.h"
+#include "device/hid/hid_device_info.h"
+#include "device/hid/hid_usage_and_page.h"
+#include "device/hid/mock_hid_service.h"
+#include "extensions/browser/api/device_permissions_prompt.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 base::ThreadTaskRunnerHandle;
+using device::HidCollectionInfo;
+using device::HidDeviceId;
+using device::HidDeviceInfo;
+using device::HidUsageAndPage;
+using device::MockDeviceClient;
+using net::IOBuffer;
+using testing::_;
+
+#if defined(OS_MACOSX)
+const uint64_t kTestDeviceIds[] = {1, 2, 3, 4, 5};
+#else
+const char* kTestDeviceIds[] = {"A", "B", "C", "D", "E"};
+#endif
+
+// These report descriptors define two devices with 8-byte input, output and
+// feature reports. The first implements usage page 0xFF00 and has a single
+// report without and ID. The second implements usage page 0xFF01 and has a
+// single report with ID 1.
+const uint8_t kReportDescriptor[] = {0x06, 0x00, 0xFF, 0x08, 0xA1, 0x01, 0x15,
+ 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95,
+ 0x08, 0x08, 0x81, 0x02, 0x08, 0x91, 0x02,
+ 0x08, 0xB1, 0x02, 0xC0};
+const uint8_t kReportDescriptorWithIDs[] = {
+ 0x06, 0x01, 0xFF, 0x08, 0xA1, 0x01, 0x15, 0x00, 0x26,
+ 0xFF, 0x00, 0x85, 0x01, 0x75, 0x08, 0x95, 0x08, 0x08,
+ 0x81, 0x02, 0x08, 0x91, 0x02, 0x08, 0xB1, 0x02, 0xC0};
+
+namespace extensions {
+
+class MockHidConnection : public device::HidConnection {
+ public:
+ explicit MockHidConnection(scoped_refptr<HidDeviceInfo> device_info)
+ : HidConnection(device_info) {}
+
+ void PlatformClose() override {}
+
+ void PlatformRead(const ReadCallback& callback) override {
+ const char kResult[] = "This is a HID input report.";
+ uint8_t report_id = device_info()->has_report_id() ? 1 : 0;
+ scoped_refptr<IOBuffer> buffer(new IOBuffer(sizeof(kResult)));
+ buffer->data()[0] = report_id;
+ memcpy(buffer->data() + 1, kResult, sizeof(kResult) - 1);
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::Bind(callback, true, buffer, sizeof(kResult)));
+ }
+
+ void PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const WriteCallback& callback) override {
+ const char kExpected[] = "o-report"; // 8 bytes
+ bool result = false;
+ if (size == sizeof(kExpected)) {
+ uint8_t report_id = buffer->data()[0];
+ uint8_t expected_report_id = device_info()->has_report_id() ? 1 : 0;
+ if (report_id == expected_report_id) {
+ if (memcmp(buffer->data() + 1, kExpected, sizeof(kExpected) - 1) == 0) {
+ result = true;
+ }
+ }
+ }
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ base::Bind(callback, result));
+ }
+
+ void PlatformGetFeatureReport(uint8_t report_id,
+ const ReadCallback& callback) override {
+ const char kResult[] = "This is a HID feature report.";
+ scoped_refptr<IOBuffer> buffer(new IOBuffer(sizeof(kResult)));
+ size_t offset = 0;
+ if (device_info()->has_report_id()) {
+ buffer->data()[offset++] = report_id;
+ }
+ memcpy(buffer->data() + offset, kResult, sizeof(kResult) - 1);
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(callback, true, buffer, sizeof(kResult) - 1 + offset));
+ }
+
+ void PlatformSendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
+ size_t size,
+ const WriteCallback& callback) override {
+ const char kExpected[] = "The app is setting this HID feature report.";
+ bool result = false;
+ if (size == sizeof(kExpected)) {
+ uint8_t report_id = buffer->data()[0];
+ uint8_t expected_report_id = device_info()->has_report_id() ? 1 : 0;
+ if (report_id == expected_report_id &&
+ memcmp(buffer->data() + 1, kExpected, sizeof(kExpected) - 1) == 0) {
+ result = true;
+ }
+ }
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ base::Bind(callback, result));
+ }
+
+ private:
+ ~MockHidConnection() override {}
+};
+
+class TestDevicePermissionsPrompt
+ : public DevicePermissionsPrompt,
+ public DevicePermissionsPrompt::Prompt::Observer {
+ public:
+ explicit TestDevicePermissionsPrompt(content::WebContents* web_contents)
+ : DevicePermissionsPrompt(web_contents) {}
+
+ ~TestDevicePermissionsPrompt() override { prompt()->SetObserver(nullptr); }
+
+ 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 HidApiTest : public ShellApiTest {
+ public:
+ void SetUpOnMainThread() override {
+ ShellApiTest::SetUpOnMainThread();
+
+ // MockDeviceClient replaces ShellDeviceClient.
+ device_client_.reset(new MockDeviceClient());
+
+ ON_CALL(*device_client_->hid_service(), Connect(_, _))
+ .WillByDefault(Invoke(this, &HidApiTest::Connect));
+ ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE,
+ base::Bind(&HidApiTest::LazyFirstEnumeration, base::Unretained(this)));
+ }
+
+ void Connect(const HidDeviceId& device_id,
+ const device::HidService::ConnectCallback& callback) {
+ const auto& devices = device_client_->hid_service()->devices();
+ const auto& device_entry = devices.find(device_id);
+ scoped_refptr<MockHidConnection> connection;
+ if (device_entry != devices.end()) {
+ connection = new MockHidConnection(device_entry->second);
+ }
+
+ ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ base::Bind(callback, connection));
+ }
+
+ void LazyFirstEnumeration() {
+ AddDevice(kTestDeviceIds[0], 0x18D1, 0x58F0, false);
+ AddDevice(kTestDeviceIds[1], 0x18D1, 0x58F0, true);
+ AddDevice(kTestDeviceIds[2], 0x18D1, 0x58F1, false);
+ device_client_->hid_service()->FirstEnumerationComplete();
+ }
+
+ void AddDevice(const HidDeviceId& device_id,
+ int vendor_id,
+ int product_id,
+ bool report_id) {
+ std::vector<uint8_t> report_descriptor;
+ if (report_id) {
+ report_descriptor.insert(
+ report_descriptor.begin(), kReportDescriptorWithIDs,
+ kReportDescriptorWithIDs + sizeof(kReportDescriptorWithIDs));
+ } else {
+ report_descriptor.insert(report_descriptor.begin(), kReportDescriptor,
+ kReportDescriptor + sizeof(kReportDescriptor));
+ }
+ device_client_->hid_service()->AddDevice(
+ new HidDeviceInfo(device_id, vendor_id, product_id, "Test Device", "A",
+ device::kHIDBusTypeUSB, report_descriptor));
+ }
+
+ protected:
+ scoped_ptr<MockDeviceClient> device_client_;
+};
+
+IN_PROC_BROWSER_TEST_F(HidApiTest, HidApp) {
+ ASSERT_TRUE(RunAppTest("api_test/hid/api")) << message_;
+}
+
+IN_PROC_BROWSER_TEST_F(HidApiTest, OnDeviceAdded) {
+ ExtensionTestMessageListener load_listener("loaded", false);
+ ExtensionTestMessageListener result_listener("success", false);
+ result_listener.set_failure_message("failure");
+
+ ASSERT_TRUE(LoadApp("api_test/hid/add_event"));
+ ASSERT_TRUE(load_listener.WaitUntilSatisfied());
+
+ // Add a blocked device first so that the test will fail if a notification is
+ // received.
+ AddDevice(kTestDeviceIds[3], 0x18D1, 0x58F1, false);
+ AddDevice(kTestDeviceIds[4], 0x18D1, 0x58F0, false);
+ ASSERT_TRUE(result_listener.WaitUntilSatisfied());
+ EXPECT_EQ("success", result_listener.message());
+}
+
+IN_PROC_BROWSER_TEST_F(HidApiTest, OnDeviceRemoved) {
+ ExtensionTestMessageListener load_listener("loaded", false);
+ ExtensionTestMessageListener result_listener("success", false);
+ result_listener.set_failure_message("failure");
+
+ ASSERT_TRUE(LoadApp("api_test/hid/remove_event"));
+ ASSERT_TRUE(load_listener.WaitUntilSatisfied());
+
+ // Device C was not returned by chrome.hid.getDevices, the app will not get
+ // a notification.
+ device_client_->hid_service()->RemoveDevice(kTestDeviceIds[2]);
+ // Device A was returned, the app will get a notification.
+ device_client_->hid_service()->RemoveDevice(kTestDeviceIds[0]);
+ ASSERT_TRUE(result_listener.WaitUntilSatisfied());
+ EXPECT_EQ("success", result_listener.message());
+}
+
+IN_PROC_BROWSER_TEST_F(HidApiTest, GetUserSelectedDevices) {
+ ExtensionTestMessageListener open_listener("opened_device", false);
+
+ TestExtensionsAPIClient test_api_client;
+ ASSERT_TRUE(LoadApp("api_test/hid/get_user_selected_devices"));
+ ASSERT_TRUE(open_listener.WaitUntilSatisfied());
+
+ ExtensionTestMessageListener remove_listener("removed", false);
+ device_client_->hid_service()->RemoveDevice(kTestDeviceIds[0]);
+ ASSERT_TRUE(remove_listener.WaitUntilSatisfied());
+
+ ExtensionTestMessageListener add_listener("added", false);
+ AddDevice(kTestDeviceIds[0], 0x18D1, 0x58F0, true);
+ ASSERT_TRUE(add_listener.WaitUntilSatisfied());
+}
+
+} // namespace extensions