Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(19)

Side by Side Diff: third_party/WebKit/LayoutTests/usb/resources/webusb-test.js

Issue 2775323003: Refactor WebUSB LayoutTests to separate out the Mojo service mocks (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 'use strict';
2
3 // This polyfil library implements the following WebIDL:
4 //
5 // partial interface USB {
6 // [SameObject] readonly attribute USBTest test;
7 // }
8 //
9 // interface USBTest {
10 // attribute EventHandler ondeviceclose;
11 // attribute DOMString? chosenDevice;
12 // attribute FrozenArray<USBDeviceFilter>? lastFilters;
13 //
14 // Promise<void> initialize();
15 // Promise<void> attachToWindow(Window window);
16 // DOMString addFakeDevice(FakeUSBDeviceInit deviceInit);
ortuno 2017/03/29 00:12:30 We made our whole API promised based so that we co
Reilly Grant (use Gerrit) 2017/03/29 00:29:53 I looked over the Web Bluetooth Test API and the g
17 // void removeFakeDevice(DOMString);
18 // void reset();
19 // };
20 //
21 // dictionary FakeUSBDeviceInit {
22 // octet usbVersionMajor;
23 // octet usbVersionMinor;
24 // octet usbVersionSubminor;
25 // octet deviceClass;
26 // octet deviceSubclass;
27 // octet deviceProtocol;
28 // unsigned short vendorId;
29 // unsigned short productId;
30 // octet deviceVersionMajor;
31 // octet deviceVersionMinor;
32 // octet deviceVersionSubminor;
33 // DOMString? manufacturerName;
34 // DOMString? productName;
35 // DOMString? serialNumber;
36 // octet activeConfigurationValue = 0;
37 // sequence<FakeUSBConfigurationInit> configurations;
38 // };
39 //
40 // dictionary FakeUSBConfigurationInit {
41 // octet configurationValue;
42 // DOMString? configurationName;
43 // sequence<FakeUSBInterfaceInit> interfaces;
44 // };
45 //
46 // dictionary FakeUSBInterfaceInit {
47 // octet interfaceNumber;
48 // sequence<FakeUSBAlternateInterfaceInit> alternates;
49 // };
50 //
51 // dictionary FakeUSBAlternateInterfaceInit {
52 // octet alternateSetting;
53 // octet interfaceClass;
54 // octet interfaceSubclass;
55 // octet interfaceProtocol;
56 // DOMString? interfaceName;
57 // sequence<FakeUSBEndpointInit> endpoints;
58 // };
59 //
60 // dictionary FakeUSBEndpointInit {
61 // octet endpointNumber;
62 // USBDirection direction;
63 // USBEndpointType type;
64 // unsigned long packetSize;
65 // };
66
67 (() => {
68
69 // The global mojo object contains the Mojo JS binding modules loaded during
70 // initialization.
71 let mojo = null;
72
73 // These variables are logically members of the USBTest class but are defined
74 // here to hide them from being visible as fields of navigator.usb.test.
75 let g_initializePromise = null;
76 let g_chooserService = null;
77 let g_deviceManager = null;
78 let g_closeListener = null;
79
80 function fakeDeviceInitToDeviceInfo(guid, init) {
81 let deviceInfo = {
82 guid: guid + "",
83 usb_version_major: init.usbVersionMajor,
84 usb_version_minor: init.usbVersionMinor,
85 usb_version_subminor: init.usbVersionSubminor,
86 class_code: init.deviceClass,
87 subclass_code: init.deviceSubclass,
88 protocol_code: init.deviceProtocol,
89 vendor_id: init.vendorId,
90 product_id: init.productId,
91 device_version_major: init.deviceVersionMajor,
92 device_version_minor: init.deviceVersionMinor,
93 device_version_subminor: init.deviceVersionSubminor,
94 manufacturer_name: init.manufacturerName,
95 product_name: init.productName,
96 serial_number: init.serialNumber,
97 active_configuration: init.activeConfigurationValue,
98 configurations: []
99 };
100 init.configurations.forEach(config => {
101 var configInfo = {
102 configuration_value: config.configurationValue,
103 configuration_name: config.configurationName,
104 interfaces: []
105 };
106 config.interfaces.forEach(iface => {
107 var interfaceInfo = {
108 interface_number: iface.interfaceNumber,
109 alternates: []
110 };
111 iface.alternates.forEach(alternate => {
112 var alternateInfo = {
113 alternate_setting: alternate.alternateSetting,
114 class_code: alternate.interfaceClass,
115 subclass_code: alternate.interfaceSubclass,
116 protocol_code: alternate.interfaceProtocol,
117 interface_name: alternate.interfaceName,
118 endpoints: []
119 };
120 alternate.endpoints.forEach(endpoint => {
121 var endpointInfo = {
122 endpoint_number: endpoint.endpointNumber,
123 packet_size: endpoint.packetSize,
124 };
125 switch (endpoint.direction) {
126 case "in":
127 endpointInfo.direction = mojo.device.TransferDirection.INBOUND;
128 break;
129 case "out":
130 endpointInfo.direction = mojo.device.TransferDirection.OUTBOUND;
131 break;
132 }
133 switch (endpoint.type) {
134 case "bulk":
135 endpointInfo.type = mojo.device.EndpointType.BULK;
136 break;
137 case "interrupt":
138 endpointInfo.type = mojo.device.EndpointType.INTERRUPT;
139 break;
140 case "isochronous":
141 endpointInfo.type = mojo.device.EndpointType.ISOCHRONOUS;
142 break;
143 }
144 alternateInfo.endpoints.push(endpointInfo);
145 });
146 interfaceInfo.alternates.push(alternateInfo);
147 });
148 configInfo.interfaces.push(interfaceInfo);
149 });
150 deviceInfo.configurations.push(configInfo);
151 });
152 return deviceInfo;
153 }
154
155 function convertMojoDeviceFilters(input) {
156 let output = [];
157 input.forEach(filter => {
158 output.push(convertMojoDeviceFilter(filter));
159 });
160 return output;
161 }
162
163 function convertMojoDeviceFilter(input) {
164 let output = {};
165 if (input.has_vendor_id)
166 output.vendorId = input.vendor_id;
167 if (input.has_product_id)
168 output.productId = input.product_id;
169 if (input.has_class_code)
170 output.classCode = input.class_code;
171 if (input.has_subclass_code)
172 output.subclassCode = input.subclass_code;
173 if (input.has_protocol_code)
174 output.protocolCode = input.protocol_code;
175 if (input.serial_number)
176 output.serialNumber = input.serial_number;
177 return output;
178 }
179
180 class FakeDevice {
181 constructor(deviceInit) {
182 this.info_ = deviceInit;
183 this.opened_ = false;
184 this.currentConfiguration_ = null;
185 this.claimedInterfaces_ = new Map();
186 }
187
188 getDeviceInfo() {
189 return Promise.resolve({ info: this.info_ });
190 }
191
192 getConfiguration() {
193 if (this.currentConfiguration_) {
194 return Promise.resolve({
195 value: this.currentConfiguration_.configuration_value });
196 } else {
197 return Promise.resolve({ value: 0 });
198 }
199 }
200
201 open() {
202 assert_false(this.opened_);
203 this.opened_ = true;
204 return Promise.resolve({ error: mojo.device.OpenDeviceError.OK });
205 }
206
207 close() {
208 assert_true(this.opened_);
209 this.opened_ = false;
210 return Promise.resolve();
211 }
212
213 setConfiguration(value) {
214 assert_true(this.opened_);
215
216 let selected_configuration = this.info_.configurations.find(
217 configuration => configuration.configurationValue == value);
218 // Blink should never request an invalid configuration.
219 assert_false(selected_configuration == undefined);
220 this.currentConfiguration_ = selected_configuration;
221 return Promise.resolve({ success: true });
222 }
223
224 claimInterface(interfaceNumber) {
225 assert_true(this.opened_);
226 assert_false(this.currentConfiguration_ == null, 'device configured');
227 assert_false(this.claimedInterfaces_.has(interfaceNumber),
228 'interface already claimed');
229
230 // Blink should never request an invalid interface.
231 assert_true(this.currentConfiguration_.interfaces.some(
232 iface => iface.interfaceNumber == interfaceNumber));
233 this.claimedInterfaces_.set(interfaceNumber, 0);
234 return Promise.resolve({ success: true });
235 }
236
237 releaseInterface(interfaceNumber) {
238 assert_true(this.opened_);
239 assert_false(this.currentConfiguration_ == null, 'device configured');
240 assert_true(this.claimedInterfaces_.has(interfaceNumber));
241 this.claimedInterfaces_.delete(interfaceNumber);
242 return Promise.resolve({ success: true });
243 }
244
245 setInterfaceAlternateSetting(interfaceNumber, alternateSetting) {
246 assert_true(this.opened_);
247 assert_false(this.currentConfiguration_ == null, 'device configured');
248 assert_true(this.claimedInterfaces_.has(interfaceNumber));
249
250 let iface = this.currentConfiguration_.interfaces.find(
251 iface => iface.interfaceNumber == interfaceNumber);
252 // Blink should never request an invalid interface or alternate.
253 assert_false(iface == undefined);
254 assert_true(iface.alternates.some(
255 x => x.alternateSetting == alternateSetting));
256 this.claimedInterfaces_.set(interfaceNumber, alternateSetting);
257 return Promise.resolve({ success: true });
258 }
259
260 reset() {
261 assert_true(this.opened_);
262 return Promise.resolve({ success: true });
263 }
264
265 clearHalt(endpoint) {
266 assert_true(this.opened_);
267 assert_false(this.currentConfiguration_ == null, 'device configured');
268 // TODO(reillyg): Assert that endpoint is valid.
269 return Promise.resolve({ success: true });
270 }
271
272 controlTransferIn(params, length, timeout) {
273 assert_true(this.opened_);
274 assert_false(this.currentConfiguration_ == null, 'device configured');
275 return Promise.resolve({
276 status: mojo.device.TransferStatus.OK,
277 data: [length >> 8, length & 0xff, params.request, params.value >> 8,
278 params.value & 0xff, params.index >> 8, params.index & 0xff]
279 });
280 }
281
282 controlTransferOut(params, data, timeout) {
283 assert_true(this.opened_);
284 assert_false(this.currentConfiguration_ == null, 'device configured');
285 return Promise.resolve({
286 status: mojo.device.TransferStatus.OK,
287 bytesWritten: data.byteLength
288 });
289 }
290
291 genericTransferIn(endpointNumber, length, timeout) {
292 assert_true(this.opened_);
293 assert_false(this.currentConfiguration_ == null, 'device configured');
294 // TODO(reillyg): Assert that endpoint is valid.
295 let data = new Array(length);
296 for (let i = 0; i < length; ++i)
297 data[i] = i & 0xff;
298 return Promise.resolve({
299 status: mojo.device.TransferStatus.OK,
300 data: data
301 });
302 }
303
304 genericTransferOut(endpointNumber, data, timeout) {
305 assert_true(this.opened_);
306 assert_false(this.currentConfiguration_ == null, 'device configured');
307 // TODO(reillyg): Assert that endpoint is valid.
308 return Promise.resolve({
309 status: mojo.device.TransferStatus.OK,
310 bytesWritten: data.byteLength
311 });
312 }
313
314 isochronousTransferIn(endpointNumber, packetLengths, timeout) {
315 assert_true(this.opened_);
316 assert_false(this.currentConfiguration_ == null, 'device configured');
317 // TODO(reillyg): Assert that endpoint is valid.
318 let data = new Array(packetLengths.reduce((a, b) => a + b, 0));
319 let dataOffset = 0;
320 let packets = new Array(packetLengths.length);
321 for (let i = 0; i < packetLengths.length; ++i) {
322 for (let j = 0; j < packetLengths[i]; ++j)
323 data[dataOffset++] = j & 0xff;
324 packets[i] = {
325 length: packetLengths[i],
326 transferred_length: packetLengths[i],
327 status: mojo.device.TransferStatus.OK
328 };
329 }
330 return Promise.resolve({ data: data, packets: packets });
331 }
332
333 isochronousTransferOut(endpointNumber, data, packetLengths, timeout) {
334 assert_true(this.opened_);
335 assert_false(this.currentConfiguration_ == null, 'device configured');
336 // TODO(reillyg): Assert that endpoint is valid.
337 let packets = new Array(packetLengths.length);
338 for (let i = 0; i < packetLengths.length; ++i) {
339 packets[i] = {
340 length: packetLengths[i],
341 transferred_length: packetLengths[i],
342 status: mojo.device.TransferStatus.OK
343 };
344 }
345 return Promise.resolve({ packets: packets });
346 }
347 }
348
349 class FakeDeviceManager {
350 constructor() {
351 this.bindingSet_ =
352 new mojo.bindings.BindingSet(mojo.deviceManager.DeviceManager);
353 this.devices_ = new Map();
354 this.client_ = null;
355 }
356
357 addBinding(handle) {
358 this.bindingSet_.addBinding(this, handle);
359 }
360
361 addDevice(info) {
362 let device = {
363 guid: this.nextGuid_++ + "",
364 info: info,
365 bindingArray: []
366 };
367 this.devices_.set(device.guid, device);
368 if (this.client_)
369 this.client_.onDeviceAdded(fakeDeviceInitToDeviceInfo(device.guid, info));
370 return device.guid;
371 }
372
373 removeDevice(guid) {
374 let device = this.devices_.get(guid);
375 for (var binding of device.bindingArray)
376 binding.close();
377 this.devices_.delete(guid);
378 if (this.client_) {
379 this.client_.onDeviceRemoved(
380 fakeDeviceInitToDeviceInfo(guid, device.info));
381 }
382 }
383
384 removeAllDevices() {
385 this.devices_.forEach(device => {
386 for (var binding of device.bindingArray)
387 binding.close();
388 this.client_.onDeviceRemoved(
389 fakeDeviceInitToDeviceInfo(device.guid, device.info));
390 });
391 this.devices_.clear();
392 }
393
394 getDevices(options) {
395 let devices = [];
396 this.devices_.forEach(device => {
397 devices.push(fakeDeviceInitToDeviceInfo(device.guid, device.info));
398 });
399 return Promise.resolve({ results: devices });
400 }
401
402 getDevice(guid, request) {
403 let device = this.devices_.get(guid);
404 if (device) {
405 let binding = new mojo.bindings.Binding(
406 mojo.device.Device, new FakeDevice(device.info), request);
407 binding.setConnectionErrorHandler(() => {
408 if (g_closeListener)
409 g_closeListener(guid);
410 });
411 device.bindingArray.push(binding);
412 } else {
413 request.close();
414 }
415 }
416
417 setClient(client) {
418 this.client_ = client;
419 }
420 }
421
422 class FakeChooserService {
423 constructor() {
424 this.bindingSet_ = new mojo.bindings.BindingSet(
425 mojo.chooserService.ChooserService);
426 this.chosenDevice_ = null;
427 this.lastFilters_ = null;
428 }
429
430 addBinding(handle) {
431 this.bindingSet_.addBinding(this, handle);
432 }
433
434 setChosenDevice(guid) {
435 this.chosenDeviceGuid_ = guid;
436 }
437
438 getPermission(deviceFilters) {
439 this.lastFilters_ = convertMojoDeviceFilters(deviceFilters);
440 let device = g_deviceManager.devices_.get(this.chosenDeviceGuid_);
441 if (device) {
442 return Promise.resolve({
443 result: fakeDeviceInitToDeviceInfo(device.guid, device.info)
444 });
445 } else {
446 return Promise.resolve({ result: null });
447 }
448 }
449 }
450
451 class USBTest {
452 constructor() {}
453
454 initialize() {
455 if (!g_initializePromise) {
456 g_initializePromise = new Promise(resolve => {
457 window.define = gin.define; // Mojo modules expect this.
458
459 gin.define('WebUSB Test Mocks', [
ortuno 2017/03/29 00:12:30 This seems similar to what mojo_helpers.js has. Wh
Reilly Grant (use Gerrit) 2017/03/29 00:29:53 I duplicated the logic in mojo-helpers.js to avoid
ortuno 2017/03/29 20:56:00 Makes sense. Another question, mostly to figure ou
Reilly Grant (use Gerrit) 2017/03/29 21:04:14 Last time I checked we were not able to use this i
ortuno 2017/03/29 21:12:56 I talked to rockot and he mentioned that we it'll
460 'content/public/renderer/frame_interfaces',
461 'device/usb/public/interfaces/chooser_service.mojom',
462 'device/usb/public/interfaces/device_manager.mojom',
463 'device/usb/public/interfaces/device.mojom',
464 'mojo/public/js/bindings',
465 'mojo/public/js/core',
466 'mojo/public/js/router',
467 'mojo/public/js/support',
468 ], (frameInterfaces, chooserService, deviceManager, device,
469 bindings, core, router, support) => {
470 delete window.define; // Clean up.
471
472 mojo = {
473 frameInterfaces: frameInterfaces,
474 chooserService: chooserService,
475 deviceManager: deviceManager,
476 device: device,
477 bindings: bindings,
478 core: core,
479 router: router,
480 support: support
481 };
482
483 g_deviceManager = new FakeDeviceManager();
484 mojo.frameInterfaces.addInterfaceOverrideForTesting(
485 mojo.deviceManager.DeviceManager.name,
486 handle => g_deviceManager.addBinding(handle));
487
488 g_chooserService = new FakeChooserService();
489 mojo.frameInterfaces.addInterfaceOverrideForTesting(
490 mojo.chooserService.ChooserService.name,
491 handle => g_chooserService.addBinding(handle));
492
493 resolve();
494 });
495 });
496 }
497
498 return g_initializePromise;
499 }
500
501 attachToWindow(otherWindow) {
502 if (!g_deviceManager || !g_chooserService)
503 throw new Error('Call initialize() before attachToWindow().');
504
505 return new Promise(resolve => {
506 otherWindow.gin.define(
507 'WebUSB Test Frame Attach', [
508 'content/public/renderer/frame_interfaces'
509 ], frameInterfaces => {
510 frameInterfaces.addInterfaceOverrideForTesting(
511 mojo.deviceManager.DeviceManager.name,
512 handle => g_deviceManager.addBinding(handle));
513 frameInterfaces.addInterfaceOverrideForTesting(
514 mojo.chooserService.ChooserService.name,
515 handle => g_chooserService.addBinding(handle));
516 resolve();
517 });
518 });
519 }
520
521 addFakeDevice(deviceInit) {
522 if (!g_deviceManager)
523 throw new Error('Call initialize() before addFakeDevice().');
524
525 return g_deviceManager.addDevice(deviceInit);
526 }
527
528 removeFakeDevice(guid) {
529 if (!g_deviceManager)
530 throw new Error('Call initialize() before removeFakeDevice().');
531
532 return g_deviceManager.removeDevice(guid);
533 }
534
535 set ondeviceclose(func) {
536 g_closeListener = func;
537 }
538
539 set chosenDevice(guid) {
540 if (!g_chooserService)
541 throw new Error('Call initialize() before setting chosenDevice.');
542
543 g_chooserService.setChosenDevice(guid);
544 }
545
546 get lastFilters() {
547 if (!g_chooserService)
548 throw new Error('Call initialize() before getting lastFilters.');
549
550 return g_chooserService.lastFilters_;
551 }
552
553 reset() {
554 if (!g_deviceManager || !g_chooserService)
555 throw new Error('Call initialize() before reset().');
556
557 g_deviceManager.removeAllDevices();
558 g_chooserService.setChosenDevice(null);
559 g_closeListener = null;
560 }
561 }
562
563 navigator.usb.test = new USBTest();
564
565 })();
OLDNEW
« no previous file with comments | « third_party/WebKit/LayoutTests/usb/resources/usb-helpers.js ('k') | third_party/WebKit/LayoutTests/usb/test-polyfil.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698