Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / extensions / browser / api / hid / hid_apitest.cc
blobac114f2cd1421e275a8f8a79248dba595c671cad
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/browser/api/device_permissions_prompt.h"
14 #include "extensions/shell/browser/shell_extensions_api_client.h"
15 #include "extensions/shell/test/shell_apitest.h"
16 #include "extensions/test/extension_test_message_listener.h"
17 #include "net/base/io_buffer.h"
19 using base::ThreadTaskRunnerHandle;
20 using device::HidCollectionInfo;
21 using device::HidConnection;
22 using device::HidDeviceId;
23 using device::HidDeviceInfo;
24 using device::HidService;
25 using device::HidUsageAndPage;
26 using net::IOBuffer;
28 #if defined(OS_MACOSX)
29 const uint64_t kTestDeviceIds[] = {1, 2, 3, 4, 5};
30 #else
31 const char* kTestDeviceIds[] = {"A", "B", "C", "D", "E"};
32 #endif
34 namespace device {
36 // These report descriptors define two devices with 8-byte input, output and
37 // feature reports. The first implements usage page 0xFF00 and has a single
38 // report without and ID. The second implements usage page 0xFF01 and has a
39 // single report with ID 1.
40 const uint8 kReportDescriptor[] = {0x06, 0x00, 0xFF, 0x08, 0xA1, 0x01, 0x15,
41 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95,
42 0x08, 0x08, 0x81, 0x02, 0x08, 0x91, 0x02,
43 0x08, 0xB1, 0x02, 0xC0};
44 const uint8 kReportDescriptorWithIDs[] = {
45 0x06, 0x01, 0xFF, 0x08, 0xA1, 0x01, 0x15, 0x00, 0x26,
46 0xFF, 0x00, 0x85, 0x01, 0x75, 0x08, 0x95, 0x08, 0x08,
47 0x81, 0x02, 0x08, 0x91, 0x02, 0x08, 0xB1, 0x02, 0xC0};
49 class MockHidConnection : public HidConnection {
50 public:
51 MockHidConnection(scoped_refptr<HidDeviceInfo> device_info)
52 : HidConnection(device_info) {}
54 void PlatformClose() override {}
56 void PlatformRead(const ReadCallback& callback) override {
57 const char kResult[] = "This is a HID input report.";
58 uint8_t report_id = device_info()->has_report_id() ? 1 : 0;
59 scoped_refptr<IOBuffer> buffer(new IOBuffer(sizeof(kResult)));
60 buffer->data()[0] = report_id;
61 memcpy(buffer->data() + 1, kResult, sizeof(kResult) - 1);
62 ThreadTaskRunnerHandle::Get()->PostTask(
63 FROM_HERE, base::Bind(callback, true, buffer, sizeof(kResult)));
66 void PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
67 size_t size,
68 const WriteCallback& callback) override {
69 const char kExpected[] = "o-report"; // 8 bytes
70 bool result = false;
71 if (size == sizeof(kExpected)) {
72 uint8_t report_id = buffer->data()[0];
73 uint8_t expected_report_id = device_info()->has_report_id() ? 1 : 0;
74 if (report_id == expected_report_id) {
75 if (memcmp(buffer->data() + 1, kExpected, sizeof(kExpected) - 1) == 0) {
76 result = true;
80 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
81 base::Bind(callback, result));
84 void PlatformGetFeatureReport(uint8_t report_id,
85 const ReadCallback& callback) override {
86 const char kResult[] = "This is a HID feature report.";
87 scoped_refptr<IOBuffer> buffer(new IOBuffer(sizeof(kResult)));
88 size_t offset = 0;
89 if (device_info()->has_report_id()) {
90 buffer->data()[offset++] = report_id;
92 memcpy(buffer->data() + offset, kResult, sizeof(kResult) - 1);
93 ThreadTaskRunnerHandle::Get()->PostTask(
94 FROM_HERE,
95 base::Bind(callback, true, buffer, sizeof(kResult) - 1 + offset));
98 void PlatformSendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
99 size_t size,
100 const WriteCallback& callback) override {
101 const char kExpected[] = "The app is setting this HID feature report.";
102 bool result = false;
103 if (size == sizeof(kExpected)) {
104 uint8_t report_id = buffer->data()[0];
105 uint8_t expected_report_id = device_info()->has_report_id() ? 1 : 0;
106 if (report_id == expected_report_id &&
107 memcmp(buffer->data() + 1, kExpected, sizeof(kExpected) - 1) == 0) {
108 result = true;
111 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
112 base::Bind(callback, result));
115 private:
116 ~MockHidConnection() override {}
119 class MockHidService : public HidService {
120 public:
121 MockHidService() : HidService() {
122 // Verify that devices are enumerated properly even when the first
123 // enumeration happens asynchronously.
124 ThreadTaskRunnerHandle::Get()->PostTask(
125 FROM_HERE, base::Bind(&MockHidService::LazyFirstEnumeration,
126 base::Unretained(this)));
129 void Connect(const HidDeviceId& device_id,
130 const ConnectCallback& callback) override {
131 const auto& device_entry = devices().find(device_id);
132 scoped_refptr<HidConnection> connection;
133 if (device_entry != devices().end()) {
134 connection = new MockHidConnection(device_entry->second);
137 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
138 base::Bind(callback, connection));
141 void LazyFirstEnumeration() {
142 AddDevice(kTestDeviceIds[0], 0x18D1, 0x58F0, false);
143 AddDevice(kTestDeviceIds[1], 0x18D1, 0x58F0, true);
144 AddDevice(kTestDeviceIds[2], 0x18D1, 0x58F1, false);
145 FirstEnumerationComplete();
148 void AddDevice(const HidDeviceId& device_id,
149 int vendor_id,
150 int product_id,
151 bool report_id) {
152 std::vector<uint8> report_descriptor;
153 if (report_id) {
154 report_descriptor.insert(
155 report_descriptor.begin(), kReportDescriptorWithIDs,
156 kReportDescriptorWithIDs + sizeof(kReportDescriptorWithIDs));
157 } else {
158 report_descriptor.insert(report_descriptor.begin(), kReportDescriptor,
159 kReportDescriptor + sizeof(kReportDescriptor));
161 HidService::AddDevice(new HidDeviceInfo(device_id, vendor_id, product_id,
162 "Test Device", "A", kHIDBusTypeUSB,
163 report_descriptor));
166 void RemoveDevice(const HidDeviceId& device_id) {
167 HidService::RemoveDevice(device_id);
171 } // namespace device
173 namespace extensions {
175 class TestDevicePermissionsPrompt
176 : public DevicePermissionsPrompt,
177 public DevicePermissionsPrompt::Prompt::Observer {
178 public:
179 TestDevicePermissionsPrompt(content::WebContents* web_contents)
180 : DevicePermissionsPrompt(web_contents) {}
182 ~TestDevicePermissionsPrompt() override { prompt()->SetObserver(nullptr); }
184 void ShowDialog() override { prompt()->SetObserver(this); }
186 void OnDevicesChanged() override {
187 for (size_t i = 0; i < prompt()->GetDeviceCount(); ++i) {
188 prompt()->GrantDevicePermission(i);
189 if (!prompt()->multiple()) {
190 break;
193 prompt()->Dismissed();
197 class TestExtensionsAPIClient : public ShellExtensionsAPIClient {
198 public:
199 TestExtensionsAPIClient() : ShellExtensionsAPIClient() {}
201 scoped_ptr<DevicePermissionsPrompt> CreateDevicePermissionsPrompt(
202 content::WebContents* web_contents) const override {
203 return make_scoped_ptr(new TestDevicePermissionsPrompt(web_contents));
207 class HidApiTest : public ShellApiTest {
208 public:
209 void SetUpOnMainThread() override {
210 ShellApiTest::SetUpOnMainThread();
211 hid_service_ = new device::MockHidService();
212 HidService::SetInstanceForTest(hid_service_);
215 protected:
216 device::MockHidService* hid_service_;
219 IN_PROC_BROWSER_TEST_F(HidApiTest, HidApp) {
220 ASSERT_TRUE(RunAppTest("api_test/hid/api")) << message_;
223 IN_PROC_BROWSER_TEST_F(HidApiTest, OnDeviceAdded) {
224 ExtensionTestMessageListener load_listener("loaded", false);
225 ExtensionTestMessageListener result_listener("success", false);
226 result_listener.set_failure_message("failure");
228 ASSERT_TRUE(LoadApp("api_test/hid/add_event"));
229 ASSERT_TRUE(load_listener.WaitUntilSatisfied());
231 // Add a blocked device first so that the test will fail if a notification is
232 // received.
233 hid_service_->AddDevice(kTestDeviceIds[3], 0x18D1, 0x58F1, false);
234 hid_service_->AddDevice(kTestDeviceIds[4], 0x18D1, 0x58F0, false);
235 ASSERT_TRUE(result_listener.WaitUntilSatisfied());
236 EXPECT_EQ("success", result_listener.message());
239 IN_PROC_BROWSER_TEST_F(HidApiTest, OnDeviceRemoved) {
240 ExtensionTestMessageListener load_listener("loaded", false);
241 ExtensionTestMessageListener result_listener("success", false);
242 result_listener.set_failure_message("failure");
244 ASSERT_TRUE(LoadApp("api_test/hid/remove_event"));
245 ASSERT_TRUE(load_listener.WaitUntilSatisfied());
247 // Device C was not returned by chrome.hid.getDevices, the app will not get
248 // a notification.
249 hid_service_->RemoveDevice(kTestDeviceIds[2]);
250 // Device A was returned, the app will get a notification.
251 hid_service_->RemoveDevice(kTestDeviceIds[0]);
252 ASSERT_TRUE(result_listener.WaitUntilSatisfied());
253 EXPECT_EQ("success", result_listener.message());
256 IN_PROC_BROWSER_TEST_F(HidApiTest, GetUserSelectedDevices) {
257 ExtensionTestMessageListener open_listener("opened_device", false);
259 TestExtensionsAPIClient test_api_client;
260 ASSERT_TRUE(LoadApp("api_test/hid/get_user_selected_devices"));
261 ASSERT_TRUE(open_listener.WaitUntilSatisfied());
263 ExtensionTestMessageListener remove_listener("removed", false);
264 hid_service_->RemoveDevice(kTestDeviceIds[0]);
265 ASSERT_TRUE(remove_listener.WaitUntilSatisfied());
267 ExtensionTestMessageListener add_listener("added", false);
268 hid_service_->AddDevice(kTestDeviceIds[0], 0x18D1, 0x58F0, true);
269 ASSERT_TRUE(add_listener.WaitUntilSatisfied());
272 } // namespace extensions