Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / extensions / browser / api / hid / hid_apitest.cc
blob1e35ecd91d1f6e6f4392b2fa20bf9948bd5814f1
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/core/device_client.h"
9 #include "device/hid/hid_collection_info.h"
10 #include "device/hid/hid_connection.h"
11 #include "device/hid/hid_device_info.h"
12 #include "device/hid/hid_service.h"
13 #include "device/hid/hid_usage_and_page.h"
14 #include "extensions/browser/api/device_permissions_prompt.h"
15 #include "extensions/shell/browser/shell_extensions_api_client.h"
16 #include "extensions/shell/test/shell_apitest.h"
17 #include "extensions/test/extension_test_message_listener.h"
18 #include "net/base/io_buffer.h"
20 using base::ThreadTaskRunnerHandle;
21 using device::HidCollectionInfo;
22 using device::HidConnection;
23 using device::HidDeviceId;
24 using device::HidDeviceInfo;
25 using device::HidService;
26 using device::HidUsageAndPage;
27 using net::IOBuffer;
29 #if defined(OS_MACOSX)
30 const uint64_t kTestDeviceIds[] = {1, 2, 3, 4, 5};
31 #else
32 const char* kTestDeviceIds[] = {"A", "B", "C", "D", "E"};
33 #endif
35 namespace device {
37 // These report descriptors define two devices with 8-byte input, output and
38 // feature reports. The first implements usage page 0xFF00 and has a single
39 // report without and ID. The second implements usage page 0xFF01 and has a
40 // single report with ID 1.
41 const uint8 kReportDescriptor[] = {0x06, 0x00, 0xFF, 0x08, 0xA1, 0x01, 0x15,
42 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95,
43 0x08, 0x08, 0x81, 0x02, 0x08, 0x91, 0x02,
44 0x08, 0xB1, 0x02, 0xC0};
45 const uint8 kReportDescriptorWithIDs[] = {
46 0x06, 0x01, 0xFF, 0x08, 0xA1, 0x01, 0x15, 0x00, 0x26,
47 0xFF, 0x00, 0x85, 0x01, 0x75, 0x08, 0x95, 0x08, 0x08,
48 0x81, 0x02, 0x08, 0x91, 0x02, 0x08, 0xB1, 0x02, 0xC0};
50 class MockHidConnection : public HidConnection {
51 public:
52 MockHidConnection(scoped_refptr<HidDeviceInfo> device_info)
53 : HidConnection(device_info) {}
55 void PlatformClose() override {}
57 void PlatformRead(const ReadCallback& callback) override {
58 const char kResult[] = "This is a HID input report.";
59 uint8_t report_id = device_info()->has_report_id() ? 1 : 0;
60 scoped_refptr<IOBuffer> buffer(new IOBuffer(sizeof(kResult)));
61 buffer->data()[0] = report_id;
62 memcpy(buffer->data() + 1, kResult, sizeof(kResult) - 1);
63 ThreadTaskRunnerHandle::Get()->PostTask(
64 FROM_HERE, base::Bind(callback, true, buffer, sizeof(kResult)));
67 void PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
68 size_t size,
69 const WriteCallback& callback) override {
70 const char kExpected[] = "o-report"; // 8 bytes
71 bool result = false;
72 if (size == sizeof(kExpected)) {
73 uint8_t report_id = buffer->data()[0];
74 uint8_t expected_report_id = device_info()->has_report_id() ? 1 : 0;
75 if (report_id == expected_report_id) {
76 if (memcmp(buffer->data() + 1, kExpected, sizeof(kExpected) - 1) == 0) {
77 result = true;
81 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
82 base::Bind(callback, result));
85 void PlatformGetFeatureReport(uint8_t report_id,
86 const ReadCallback& callback) override {
87 const char kResult[] = "This is a HID feature report.";
88 scoped_refptr<IOBuffer> buffer(new IOBuffer(sizeof(kResult)));
89 size_t offset = 0;
90 if (device_info()->has_report_id()) {
91 buffer->data()[offset++] = report_id;
93 memcpy(buffer->data() + offset, kResult, sizeof(kResult) - 1);
94 ThreadTaskRunnerHandle::Get()->PostTask(
95 FROM_HERE,
96 base::Bind(callback, true, buffer, sizeof(kResult) - 1 + offset));
99 void PlatformSendFeatureReport(scoped_refptr<net::IOBuffer> buffer,
100 size_t size,
101 const WriteCallback& callback) override {
102 const char kExpected[] = "The app is setting this HID feature report.";
103 bool result = false;
104 if (size == sizeof(kExpected)) {
105 uint8_t report_id = buffer->data()[0];
106 uint8_t expected_report_id = device_info()->has_report_id() ? 1 : 0;
107 if (report_id == expected_report_id &&
108 memcmp(buffer->data() + 1, kExpected, sizeof(kExpected) - 1) == 0) {
109 result = true;
112 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
113 base::Bind(callback, result));
116 private:
117 ~MockHidConnection() override {}
120 class MockHidService : public HidService {
121 public:
122 MockHidService() : HidService() {
123 // Verify that devices are enumerated properly even when the first
124 // enumeration happens asynchronously.
125 ThreadTaskRunnerHandle::Get()->PostTask(
126 FROM_HERE, base::Bind(&MockHidService::LazyFirstEnumeration,
127 base::Unretained(this)));
130 void Connect(const HidDeviceId& device_id,
131 const ConnectCallback& callback) override {
132 const auto& device_entry = devices().find(device_id);
133 scoped_refptr<HidConnection> connection;
134 if (device_entry != devices().end()) {
135 connection = new MockHidConnection(device_entry->second);
138 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
139 base::Bind(callback, connection));
142 void LazyFirstEnumeration() {
143 AddDevice(kTestDeviceIds[0], 0x18D1, 0x58F0, false);
144 AddDevice(kTestDeviceIds[1], 0x18D1, 0x58F0, true);
145 AddDevice(kTestDeviceIds[2], 0x18D1, 0x58F1, false);
146 FirstEnumerationComplete();
149 void AddDevice(const HidDeviceId& device_id,
150 int vendor_id,
151 int product_id,
152 bool report_id) {
153 std::vector<uint8> report_descriptor;
154 if (report_id) {
155 report_descriptor.insert(
156 report_descriptor.begin(), kReportDescriptorWithIDs,
157 kReportDescriptorWithIDs + sizeof(kReportDescriptorWithIDs));
158 } else {
159 report_descriptor.insert(report_descriptor.begin(), kReportDescriptor,
160 kReportDescriptor + sizeof(kReportDescriptor));
162 HidService::AddDevice(new HidDeviceInfo(device_id, vendor_id, product_id,
163 "Test Device", "A", kHIDBusTypeUSB,
164 report_descriptor));
167 void RemoveDevice(const HidDeviceId& device_id) {
168 HidService::RemoveDevice(device_id);
172 class TestDeviceClient : public DeviceClient {
173 public:
174 TestDeviceClient() : DeviceClient() {}
175 ~TestDeviceClient() override {}
177 MockHidService& mock_service() { return hid_service_; }
179 private:
180 HidService* GetHidService() override { return &hid_service_; }
182 MockHidService hid_service_;
185 } // namespace device
187 namespace extensions {
189 class TestDevicePermissionsPrompt
190 : public DevicePermissionsPrompt,
191 public DevicePermissionsPrompt::Prompt::Observer {
192 public:
193 TestDevicePermissionsPrompt(content::WebContents* web_contents)
194 : DevicePermissionsPrompt(web_contents) {}
196 ~TestDevicePermissionsPrompt() override { prompt()->SetObserver(nullptr); }
198 void ShowDialog() override { prompt()->SetObserver(this); }
200 void OnDevicesChanged() override {
201 for (size_t i = 0; i < prompt()->GetDeviceCount(); ++i) {
202 prompt()->GrantDevicePermission(i);
203 if (!prompt()->multiple()) {
204 break;
207 prompt()->Dismissed();
211 class TestExtensionsAPIClient : public ShellExtensionsAPIClient {
212 public:
213 TestExtensionsAPIClient() : ShellExtensionsAPIClient() {}
215 scoped_ptr<DevicePermissionsPrompt> CreateDevicePermissionsPrompt(
216 content::WebContents* web_contents) const override {
217 return make_scoped_ptr(new TestDevicePermissionsPrompt(web_contents));
221 class HidApiTest : public ShellApiTest {
222 public:
223 void SetUpOnMainThread() override {
224 ShellApiTest::SetUpOnMainThread();
225 device_client_.reset(new device::TestDeviceClient());
228 protected:
229 scoped_ptr<device::TestDeviceClient> device_client_;
232 IN_PROC_BROWSER_TEST_F(HidApiTest, HidApp) {
233 ASSERT_TRUE(RunAppTest("api_test/hid/api")) << message_;
236 IN_PROC_BROWSER_TEST_F(HidApiTest, OnDeviceAdded) {
237 ExtensionTestMessageListener load_listener("loaded", false);
238 ExtensionTestMessageListener result_listener("success", false);
239 result_listener.set_failure_message("failure");
241 ASSERT_TRUE(LoadApp("api_test/hid/add_event"));
242 ASSERT_TRUE(load_listener.WaitUntilSatisfied());
244 // Add a blocked device first so that the test will fail if a notification is
245 // received.
246 device_client_->mock_service().AddDevice(kTestDeviceIds[3], 0x18D1, 0x58F1,
247 false);
248 device_client_->mock_service().AddDevice(kTestDeviceIds[4], 0x18D1, 0x58F0,
249 false);
250 ASSERT_TRUE(result_listener.WaitUntilSatisfied());
251 EXPECT_EQ("success", result_listener.message());
254 IN_PROC_BROWSER_TEST_F(HidApiTest, OnDeviceRemoved) {
255 ExtensionTestMessageListener load_listener("loaded", false);
256 ExtensionTestMessageListener result_listener("success", false);
257 result_listener.set_failure_message("failure");
259 ASSERT_TRUE(LoadApp("api_test/hid/remove_event"));
260 ASSERT_TRUE(load_listener.WaitUntilSatisfied());
262 // Device C was not returned by chrome.hid.getDevices, the app will not get
263 // a notification.
264 device_client_->mock_service().RemoveDevice(kTestDeviceIds[2]);
265 // Device A was returned, the app will get a notification.
266 device_client_->mock_service().RemoveDevice(kTestDeviceIds[0]);
267 ASSERT_TRUE(result_listener.WaitUntilSatisfied());
268 EXPECT_EQ("success", result_listener.message());
271 IN_PROC_BROWSER_TEST_F(HidApiTest, GetUserSelectedDevices) {
272 ExtensionTestMessageListener open_listener("opened_device", false);
274 TestExtensionsAPIClient test_api_client;
275 ASSERT_TRUE(LoadApp("api_test/hid/get_user_selected_devices"));
276 ASSERT_TRUE(open_listener.WaitUntilSatisfied());
278 ExtensionTestMessageListener remove_listener("removed", false);
279 device_client_->mock_service().RemoveDevice(kTestDeviceIds[0]);
280 ASSERT_TRUE(remove_listener.WaitUntilSatisfied());
282 ExtensionTestMessageListener add_listener("added", false);
283 device_client_->mock_service().AddDevice(kTestDeviceIds[0], 0x18D1, 0x58F0,
284 true);
285 ASSERT_TRUE(add_listener.WaitUntilSatisfied());
288 } // namespace extensions