Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / extensions / browser / api / hid / hid_apitest.cc
blobc53bc336ce8c3bfc2314951a8a4d3170e2b2193a
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/bind.h"
6 #include "base/run_loop.h"
7 #include "base/thread_task_runner_handle.h"
8 #include "device/hid/hid_collection_info.h"
9 #include "device/hid/hid_connection.h"
10 #include "device/hid/hid_device_info.h"
11 #include "device/hid/hid_service.h"
12 #include "device/hid/hid_usage_and_page.h"
13 #include "extensions/shell/test/shell_apitest.h"
14 #include "extensions/test/extension_test_message_listener.h"
15 #include "net/base/io_buffer.h"
17 using base::ThreadTaskRunnerHandle;
18 using device::HidCollectionInfo;
19 using device::HidConnection;
20 using device::HidDeviceId;
21 using device::HidDeviceInfo;
22 using device::HidService;
23 using device::HidUsageAndPage;
24 using net::IOBuffer;
26 #if defined(OS_MACOSX)
27 const uint64_t kTestDeviceIds[] = {1, 2, 3, 4, 5};
28 #else
29 const char* kTestDeviceIds[] = {"A", "B", "C", "D", "E"};
30 #endif
32 namespace device {
34 // These report descriptors define two devices with 8-byte input, output and
35 // feature reports. The first implements usage page 0xFF00 and has a single
36 // report without and ID. The second implements usage page 0xFF01 and has a
37 // single report with ID 1.
38 const uint8 kReportDescriptor[] = {0x06, 0x00, 0xFF, 0x08, 0xA1, 0x01, 0x15,
39 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95,
40 0x08, 0x08, 0x81, 0x02, 0x08, 0x91, 0x02,
41 0x08, 0xB1, 0x02, 0xC0};
42 const uint8 kReportDescriptorWithIDs[] = {
43 0x06, 0x01, 0xFF, 0x08, 0xA1, 0x01, 0x15, 0x00, 0x26,
44 0xFF, 0x00, 0x85, 0x01, 0x75, 0x08, 0x95, 0x08, 0x08,
45 0x81, 0x02, 0x08, 0x91, 0x02, 0x08, 0xB1, 0x02, 0xC0};
47 class MockHidConnection : public HidConnection {
48 public:
49 MockHidConnection(scoped_refptr<HidDeviceInfo> device_info)
50 : HidConnection(device_info) {}
52 void PlatformClose() override {}
54 void PlatformRead(const ReadCallback& callback) override {
55 const char kResult[] = "This is a HID input report.";
56 uint8_t report_id = device_info()->has_report_id() ? 1 : 0;
57 scoped_refptr<IOBuffer> buffer(new IOBuffer(sizeof(kResult)));
58 buffer->data()[0] = report_id;
59 memcpy(buffer->data() + 1, kResult, sizeof(kResult) - 1);
60 ThreadTaskRunnerHandle::Get()->PostTask(
61 FROM_HERE, base::Bind(callback, true, buffer, sizeof(kResult)));
64 void PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
65 size_t size,
66 const WriteCallback& callback) override {
67 const char kExpected[] = "o-report"; // 8 bytes
68 bool result = false;
69 if (size == sizeof(kExpected)) {
70 uint8_t report_id = buffer->data()[0];
71 uint8_t expected_report_id = device_info()->has_report_id() ? 1 : 0;
72 if (report_id == expected_report_id) {
73 if (memcmp(buffer->data() + 1, kExpected, sizeof(kExpected) - 1) == 0) {
74 result = true;
78 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
79 base::Bind(callback, result));
82 void PlatformGetFeatureReport(uint8_t report_id,
83 const ReadCallback& callback) override {
84 const char kResult[] = "This is a HID feature report.";
85 scoped_refptr<IOBuffer> buffer(new IOBuffer(sizeof(kResult)));
86 size_t offset = 0;
87 if (device_info()->has_report_id()) {
88 buffer->data()[offset++] = report_id;
90 memcpy(buffer->data() + offset, kResult, sizeof(kResult) - 1);
91 ThreadTaskRunnerHandle::Get()->PostTask(
92 FROM_HERE,
93 base::Bind(callback, true, buffer, sizeof(kResult) - 1 + offset));
96 void PlatformSendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
97 size_t size,
98 const WriteCallback& callback) override {
99 const char kExpected[] = "The app is setting this HID feature report.";
100 bool result = false;
101 if (size == sizeof(kExpected)) {
102 uint8_t report_id = buffer->data()[0];
103 uint8_t expected_report_id = device_info()->has_report_id() ? 1 : 0;
104 if (report_id == expected_report_id &&
105 memcmp(buffer->data() + 1, kExpected, sizeof(kExpected) - 1) == 0) {
106 result = true;
109 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
110 base::Bind(callback, result));
113 private:
114 ~MockHidConnection() override {}
117 class MockHidService : public HidService {
118 public:
119 MockHidService() : HidService() {
120 // Verify that devices are enumerated properly even when the first
121 // enumeration happens asynchronously.
122 ThreadTaskRunnerHandle::Get()->PostTask(
123 FROM_HERE, base::Bind(&MockHidService::LazyFirstEnumeration,
124 base::Unretained(this)));
127 void Connect(const HidDeviceId& device_id,
128 const ConnectCallback& callback) override {
129 const auto& device_entry = devices().find(device_id);
130 scoped_refptr<HidConnection> connection;
131 if (device_entry != devices().end()) {
132 connection = new MockHidConnection(device_entry->second);
135 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
136 base::Bind(callback, connection));
139 void LazyFirstEnumeration() {
140 AddDevice(kTestDeviceIds[0], 0x18D1, 0x58F0, false);
141 AddDevice(kTestDeviceIds[1], 0x18D1, 0x58F0, true);
142 AddDevice(kTestDeviceIds[2], 0x18D1, 0x58F1, false);
143 FirstEnumerationComplete();
146 void AddDevice(const HidDeviceId& device_id,
147 int vendor_id,
148 int product_id,
149 bool report_id) {
150 std::vector<uint8> report_descriptor;
151 if (report_id) {
152 report_descriptor.insert(
153 report_descriptor.begin(), kReportDescriptorWithIDs,
154 kReportDescriptorWithIDs + sizeof(kReportDescriptorWithIDs));
155 } else {
156 report_descriptor.insert(report_descriptor.begin(), kReportDescriptor,
157 kReportDescriptor + sizeof(kReportDescriptor));
159 HidService::AddDevice(new HidDeviceInfo(device_id, vendor_id, product_id,
160 "", "", kHIDBusTypeUSB,
161 report_descriptor));
164 void RemoveDevice(const HidDeviceId& device_id) {
165 HidService::RemoveDevice(device_id);
169 } // namespace device
171 namespace extensions {
173 class HidApiTest : public ShellApiTest {
174 public:
175 void SetUpOnMainThread() override {
176 ShellApiTest::SetUpOnMainThread();
177 hid_service_ = new device::MockHidService();
178 HidService::SetInstanceForTest(hid_service_);
181 protected:
182 device::MockHidService* hid_service_;
185 IN_PROC_BROWSER_TEST_F(HidApiTest, HidApp) {
186 ASSERT_TRUE(RunAppTest("api_test/hid/api")) << message_;
189 IN_PROC_BROWSER_TEST_F(HidApiTest, OnDeviceAdded) {
190 ExtensionTestMessageListener load_listener("loaded", false);
191 ExtensionTestMessageListener result_listener("success", false);
192 result_listener.set_failure_message("failure");
194 ASSERT_TRUE(LoadApp("api_test/hid/add_event"));
195 ASSERT_TRUE(load_listener.WaitUntilSatisfied());
197 // Add a blocked device first so that the test will fail if a notification is
198 // received.
199 hid_service_->AddDevice(kTestDeviceIds[3], 0x18D1, 0x58F1, false);
200 hid_service_->AddDevice(kTestDeviceIds[4], 0x18D1, 0x58F0, false);
201 ASSERT_TRUE(result_listener.WaitUntilSatisfied());
202 EXPECT_EQ("success", result_listener.message());
205 IN_PROC_BROWSER_TEST_F(HidApiTest, OnDeviceRemoved) {
206 ExtensionTestMessageListener load_listener("loaded", false);
207 ExtensionTestMessageListener result_listener("success", false);
208 result_listener.set_failure_message("failure");
210 ASSERT_TRUE(LoadApp("api_test/hid/remove_event"));
211 ASSERT_TRUE(load_listener.WaitUntilSatisfied());
213 // Device C was not returned by chrome.hid.getDevices, the app will not get
214 // a notification.
215 hid_service_->RemoveDevice(kTestDeviceIds[2]);
216 // Device A was returned, the app will get a notification.
217 hid_service_->RemoveDevice(kTestDeviceIds[0]);
218 ASSERT_TRUE(result_listener.WaitUntilSatisfied());
219 EXPECT_EQ("success", result_listener.message());
222 } // namespace extensions