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

Unified 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, 9 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 side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/LayoutTests/usb/resources/webusb-test.js
diff --git a/third_party/WebKit/LayoutTests/usb/resources/webusb-test.js b/third_party/WebKit/LayoutTests/usb/resources/webusb-test.js
new file mode 100644
index 0000000000000000000000000000000000000000..94184fe92ad0a55b77658100339bba2b6661df89
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/usb/resources/webusb-test.js
@@ -0,0 +1,565 @@
+'use strict';
+
+// This polyfil library implements the following WebIDL:
+//
+// partial interface USB {
+// [SameObject] readonly attribute USBTest test;
+// }
+//
+// interface USBTest {
+// attribute EventHandler ondeviceclose;
+// attribute DOMString? chosenDevice;
+// attribute FrozenArray<USBDeviceFilter>? lastFilters;
+//
+// Promise<void> initialize();
+// Promise<void> attachToWindow(Window window);
+// 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
+// void removeFakeDevice(DOMString);
+// void reset();
+// };
+//
+// dictionary FakeUSBDeviceInit {
+// octet usbVersionMajor;
+// octet usbVersionMinor;
+// octet usbVersionSubminor;
+// octet deviceClass;
+// octet deviceSubclass;
+// octet deviceProtocol;
+// unsigned short vendorId;
+// unsigned short productId;
+// octet deviceVersionMajor;
+// octet deviceVersionMinor;
+// octet deviceVersionSubminor;
+// DOMString? manufacturerName;
+// DOMString? productName;
+// DOMString? serialNumber;
+// octet activeConfigurationValue = 0;
+// sequence<FakeUSBConfigurationInit> configurations;
+// };
+//
+// dictionary FakeUSBConfigurationInit {
+// octet configurationValue;
+// DOMString? configurationName;
+// sequence<FakeUSBInterfaceInit> interfaces;
+// };
+//
+// dictionary FakeUSBInterfaceInit {
+// octet interfaceNumber;
+// sequence<FakeUSBAlternateInterfaceInit> alternates;
+// };
+//
+// dictionary FakeUSBAlternateInterfaceInit {
+// octet alternateSetting;
+// octet interfaceClass;
+// octet interfaceSubclass;
+// octet interfaceProtocol;
+// DOMString? interfaceName;
+// sequence<FakeUSBEndpointInit> endpoints;
+// };
+//
+// dictionary FakeUSBEndpointInit {
+// octet endpointNumber;
+// USBDirection direction;
+// USBEndpointType type;
+// unsigned long packetSize;
+// };
+
+(() => {
+
+// The global mojo object contains the Mojo JS binding modules loaded during
+// initialization.
+let mojo = null;
+
+// These variables are logically members of the USBTest class but are defined
+// here to hide them from being visible as fields of navigator.usb.test.
+let g_initializePromise = null;
+let g_chooserService = null;
+let g_deviceManager = null;
+let g_closeListener = null;
+
+function fakeDeviceInitToDeviceInfo(guid, init) {
+ let deviceInfo = {
+ guid: guid + "",
+ usb_version_major: init.usbVersionMajor,
+ usb_version_minor: init.usbVersionMinor,
+ usb_version_subminor: init.usbVersionSubminor,
+ class_code: init.deviceClass,
+ subclass_code: init.deviceSubclass,
+ protocol_code: init.deviceProtocol,
+ vendor_id: init.vendorId,
+ product_id: init.productId,
+ device_version_major: init.deviceVersionMajor,
+ device_version_minor: init.deviceVersionMinor,
+ device_version_subminor: init.deviceVersionSubminor,
+ manufacturer_name: init.manufacturerName,
+ product_name: init.productName,
+ serial_number: init.serialNumber,
+ active_configuration: init.activeConfigurationValue,
+ configurations: []
+ };
+ init.configurations.forEach(config => {
+ var configInfo = {
+ configuration_value: config.configurationValue,
+ configuration_name: config.configurationName,
+ interfaces: []
+ };
+ config.interfaces.forEach(iface => {
+ var interfaceInfo = {
+ interface_number: iface.interfaceNumber,
+ alternates: []
+ };
+ iface.alternates.forEach(alternate => {
+ var alternateInfo = {
+ alternate_setting: alternate.alternateSetting,
+ class_code: alternate.interfaceClass,
+ subclass_code: alternate.interfaceSubclass,
+ protocol_code: alternate.interfaceProtocol,
+ interface_name: alternate.interfaceName,
+ endpoints: []
+ };
+ alternate.endpoints.forEach(endpoint => {
+ var endpointInfo = {
+ endpoint_number: endpoint.endpointNumber,
+ packet_size: endpoint.packetSize,
+ };
+ switch (endpoint.direction) {
+ case "in":
+ endpointInfo.direction = mojo.device.TransferDirection.INBOUND;
+ break;
+ case "out":
+ endpointInfo.direction = mojo.device.TransferDirection.OUTBOUND;
+ break;
+ }
+ switch (endpoint.type) {
+ case "bulk":
+ endpointInfo.type = mojo.device.EndpointType.BULK;
+ break;
+ case "interrupt":
+ endpointInfo.type = mojo.device.EndpointType.INTERRUPT;
+ break;
+ case "isochronous":
+ endpointInfo.type = mojo.device.EndpointType.ISOCHRONOUS;
+ break;
+ }
+ alternateInfo.endpoints.push(endpointInfo);
+ });
+ interfaceInfo.alternates.push(alternateInfo);
+ });
+ configInfo.interfaces.push(interfaceInfo);
+ });
+ deviceInfo.configurations.push(configInfo);
+ });
+ return deviceInfo;
+}
+
+function convertMojoDeviceFilters(input) {
+ let output = [];
+ input.forEach(filter => {
+ output.push(convertMojoDeviceFilter(filter));
+ });
+ return output;
+}
+
+function convertMojoDeviceFilter(input) {
+ let output = {};
+ if (input.has_vendor_id)
+ output.vendorId = input.vendor_id;
+ if (input.has_product_id)
+ output.productId = input.product_id;
+ if (input.has_class_code)
+ output.classCode = input.class_code;
+ if (input.has_subclass_code)
+ output.subclassCode = input.subclass_code;
+ if (input.has_protocol_code)
+ output.protocolCode = input.protocol_code;
+ if (input.serial_number)
+ output.serialNumber = input.serial_number;
+ return output;
+}
+
+class FakeDevice {
+ constructor(deviceInit) {
+ this.info_ = deviceInit;
+ this.opened_ = false;
+ this.currentConfiguration_ = null;
+ this.claimedInterfaces_ = new Map();
+ }
+
+ getDeviceInfo() {
+ return Promise.resolve({ info: this.info_ });
+ }
+
+ getConfiguration() {
+ if (this.currentConfiguration_) {
+ return Promise.resolve({
+ value: this.currentConfiguration_.configuration_value });
+ } else {
+ return Promise.resolve({ value: 0 });
+ }
+ }
+
+ open() {
+ assert_false(this.opened_);
+ this.opened_ = true;
+ return Promise.resolve({ error: mojo.device.OpenDeviceError.OK });
+ }
+
+ close() {
+ assert_true(this.opened_);
+ this.opened_ = false;
+ return Promise.resolve();
+ }
+
+ setConfiguration(value) {
+ assert_true(this.opened_);
+
+ let selected_configuration = this.info_.configurations.find(
+ configuration => configuration.configurationValue == value);
+ // Blink should never request an invalid configuration.
+ assert_false(selected_configuration == undefined);
+ this.currentConfiguration_ = selected_configuration;
+ return Promise.resolve({ success: true });
+ }
+
+ claimInterface(interfaceNumber) {
+ assert_true(this.opened_);
+ assert_false(this.currentConfiguration_ == null, 'device configured');
+ assert_false(this.claimedInterfaces_.has(interfaceNumber),
+ 'interface already claimed');
+
+ // Blink should never request an invalid interface.
+ assert_true(this.currentConfiguration_.interfaces.some(
+ iface => iface.interfaceNumber == interfaceNumber));
+ this.claimedInterfaces_.set(interfaceNumber, 0);
+ return Promise.resolve({ success: true });
+ }
+
+ releaseInterface(interfaceNumber) {
+ assert_true(this.opened_);
+ assert_false(this.currentConfiguration_ == null, 'device configured');
+ assert_true(this.claimedInterfaces_.has(interfaceNumber));
+ this.claimedInterfaces_.delete(interfaceNumber);
+ return Promise.resolve({ success: true });
+ }
+
+ setInterfaceAlternateSetting(interfaceNumber, alternateSetting) {
+ assert_true(this.opened_);
+ assert_false(this.currentConfiguration_ == null, 'device configured');
+ assert_true(this.claimedInterfaces_.has(interfaceNumber));
+
+ let iface = this.currentConfiguration_.interfaces.find(
+ iface => iface.interfaceNumber == interfaceNumber);
+ // Blink should never request an invalid interface or alternate.
+ assert_false(iface == undefined);
+ assert_true(iface.alternates.some(
+ x => x.alternateSetting == alternateSetting));
+ this.claimedInterfaces_.set(interfaceNumber, alternateSetting);
+ return Promise.resolve({ success: true });
+ }
+
+ reset() {
+ assert_true(this.opened_);
+ return Promise.resolve({ success: true });
+ }
+
+ clearHalt(endpoint) {
+ assert_true(this.opened_);
+ assert_false(this.currentConfiguration_ == null, 'device configured');
+ // TODO(reillyg): Assert that endpoint is valid.
+ return Promise.resolve({ success: true });
+ }
+
+ controlTransferIn(params, length, timeout) {
+ assert_true(this.opened_);
+ assert_false(this.currentConfiguration_ == null, 'device configured');
+ return Promise.resolve({
+ status: mojo.device.TransferStatus.OK,
+ data: [length >> 8, length & 0xff, params.request, params.value >> 8,
+ params.value & 0xff, params.index >> 8, params.index & 0xff]
+ });
+ }
+
+ controlTransferOut(params, data, timeout) {
+ assert_true(this.opened_);
+ assert_false(this.currentConfiguration_ == null, 'device configured');
+ return Promise.resolve({
+ status: mojo.device.TransferStatus.OK,
+ bytesWritten: data.byteLength
+ });
+ }
+
+ genericTransferIn(endpointNumber, length, timeout) {
+ assert_true(this.opened_);
+ assert_false(this.currentConfiguration_ == null, 'device configured');
+ // TODO(reillyg): Assert that endpoint is valid.
+ let data = new Array(length);
+ for (let i = 0; i < length; ++i)
+ data[i] = i & 0xff;
+ return Promise.resolve({
+ status: mojo.device.TransferStatus.OK,
+ data: data
+ });
+ }
+
+ genericTransferOut(endpointNumber, data, timeout) {
+ assert_true(this.opened_);
+ assert_false(this.currentConfiguration_ == null, 'device configured');
+ // TODO(reillyg): Assert that endpoint is valid.
+ return Promise.resolve({
+ status: mojo.device.TransferStatus.OK,
+ bytesWritten: data.byteLength
+ });
+ }
+
+ isochronousTransferIn(endpointNumber, packetLengths, timeout) {
+ assert_true(this.opened_);
+ assert_false(this.currentConfiguration_ == null, 'device configured');
+ // TODO(reillyg): Assert that endpoint is valid.
+ let data = new Array(packetLengths.reduce((a, b) => a + b, 0));
+ let dataOffset = 0;
+ let packets = new Array(packetLengths.length);
+ for (let i = 0; i < packetLengths.length; ++i) {
+ for (let j = 0; j < packetLengths[i]; ++j)
+ data[dataOffset++] = j & 0xff;
+ packets[i] = {
+ length: packetLengths[i],
+ transferred_length: packetLengths[i],
+ status: mojo.device.TransferStatus.OK
+ };
+ }
+ return Promise.resolve({ data: data, packets: packets });
+ }
+
+ isochronousTransferOut(endpointNumber, data, packetLengths, timeout) {
+ assert_true(this.opened_);
+ assert_false(this.currentConfiguration_ == null, 'device configured');
+ // TODO(reillyg): Assert that endpoint is valid.
+ let packets = new Array(packetLengths.length);
+ for (let i = 0; i < packetLengths.length; ++i) {
+ packets[i] = {
+ length: packetLengths[i],
+ transferred_length: packetLengths[i],
+ status: mojo.device.TransferStatus.OK
+ };
+ }
+ return Promise.resolve({ packets: packets });
+ }
+}
+
+class FakeDeviceManager {
+ constructor() {
+ this.bindingSet_ =
+ new mojo.bindings.BindingSet(mojo.deviceManager.DeviceManager);
+ this.devices_ = new Map();
+ this.client_ = null;
+ }
+
+ addBinding(handle) {
+ this.bindingSet_.addBinding(this, handle);
+ }
+
+ addDevice(info) {
+ let device = {
+ guid: this.nextGuid_++ + "",
+ info: info,
+ bindingArray: []
+ };
+ this.devices_.set(device.guid, device);
+ if (this.client_)
+ this.client_.onDeviceAdded(fakeDeviceInitToDeviceInfo(device.guid, info));
+ return device.guid;
+ }
+
+ removeDevice(guid) {
+ let device = this.devices_.get(guid);
+ for (var binding of device.bindingArray)
+ binding.close();
+ this.devices_.delete(guid);
+ if (this.client_) {
+ this.client_.onDeviceRemoved(
+ fakeDeviceInitToDeviceInfo(guid, device.info));
+ }
+ }
+
+ removeAllDevices() {
+ this.devices_.forEach(device => {
+ for (var binding of device.bindingArray)
+ binding.close();
+ this.client_.onDeviceRemoved(
+ fakeDeviceInitToDeviceInfo(device.guid, device.info));
+ });
+ this.devices_.clear();
+ }
+
+ getDevices(options) {
+ let devices = [];
+ this.devices_.forEach(device => {
+ devices.push(fakeDeviceInitToDeviceInfo(device.guid, device.info));
+ });
+ return Promise.resolve({ results: devices });
+ }
+
+ getDevice(guid, request) {
+ let device = this.devices_.get(guid);
+ if (device) {
+ let binding = new mojo.bindings.Binding(
+ mojo.device.Device, new FakeDevice(device.info), request);
+ binding.setConnectionErrorHandler(() => {
+ if (g_closeListener)
+ g_closeListener(guid);
+ });
+ device.bindingArray.push(binding);
+ } else {
+ request.close();
+ }
+ }
+
+ setClient(client) {
+ this.client_ = client;
+ }
+}
+
+class FakeChooserService {
+ constructor() {
+ this.bindingSet_ = new mojo.bindings.BindingSet(
+ mojo.chooserService.ChooserService);
+ this.chosenDevice_ = null;
+ this.lastFilters_ = null;
+ }
+
+ addBinding(handle) {
+ this.bindingSet_.addBinding(this, handle);
+ }
+
+ setChosenDevice(guid) {
+ this.chosenDeviceGuid_ = guid;
+ }
+
+ getPermission(deviceFilters) {
+ this.lastFilters_ = convertMojoDeviceFilters(deviceFilters);
+ let device = g_deviceManager.devices_.get(this.chosenDeviceGuid_);
+ if (device) {
+ return Promise.resolve({
+ result: fakeDeviceInitToDeviceInfo(device.guid, device.info)
+ });
+ } else {
+ return Promise.resolve({ result: null });
+ }
+ }
+}
+
+class USBTest {
+ constructor() {}
+
+ initialize() {
+ if (!g_initializePromise) {
+ g_initializePromise = new Promise(resolve => {
+ window.define = gin.define; // Mojo modules expect this.
+
+ 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
+ 'content/public/renderer/frame_interfaces',
+ 'device/usb/public/interfaces/chooser_service.mojom',
+ 'device/usb/public/interfaces/device_manager.mojom',
+ 'device/usb/public/interfaces/device.mojom',
+ 'mojo/public/js/bindings',
+ 'mojo/public/js/core',
+ 'mojo/public/js/router',
+ 'mojo/public/js/support',
+ ], (frameInterfaces, chooserService, deviceManager, device,
+ bindings, core, router, support) => {
+ delete window.define; // Clean up.
+
+ mojo = {
+ frameInterfaces: frameInterfaces,
+ chooserService: chooserService,
+ deviceManager: deviceManager,
+ device: device,
+ bindings: bindings,
+ core: core,
+ router: router,
+ support: support
+ };
+
+ g_deviceManager = new FakeDeviceManager();
+ mojo.frameInterfaces.addInterfaceOverrideForTesting(
+ mojo.deviceManager.DeviceManager.name,
+ handle => g_deviceManager.addBinding(handle));
+
+ g_chooserService = new FakeChooserService();
+ mojo.frameInterfaces.addInterfaceOverrideForTesting(
+ mojo.chooserService.ChooserService.name,
+ handle => g_chooserService.addBinding(handle));
+
+ resolve();
+ });
+ });
+ }
+
+ return g_initializePromise;
+ }
+
+ attachToWindow(otherWindow) {
+ if (!g_deviceManager || !g_chooserService)
+ throw new Error('Call initialize() before attachToWindow().');
+
+ return new Promise(resolve => {
+ otherWindow.gin.define(
+ 'WebUSB Test Frame Attach', [
+ 'content/public/renderer/frame_interfaces'
+ ], frameInterfaces => {
+ frameInterfaces.addInterfaceOverrideForTesting(
+ mojo.deviceManager.DeviceManager.name,
+ handle => g_deviceManager.addBinding(handle));
+ frameInterfaces.addInterfaceOverrideForTesting(
+ mojo.chooserService.ChooserService.name,
+ handle => g_chooserService.addBinding(handle));
+ resolve();
+ });
+ });
+ }
+
+ addFakeDevice(deviceInit) {
+ if (!g_deviceManager)
+ throw new Error('Call initialize() before addFakeDevice().');
+
+ return g_deviceManager.addDevice(deviceInit);
+ }
+
+ removeFakeDevice(guid) {
+ if (!g_deviceManager)
+ throw new Error('Call initialize() before removeFakeDevice().');
+
+ return g_deviceManager.removeDevice(guid);
+ }
+
+ set ondeviceclose(func) {
+ g_closeListener = func;
+ }
+
+ set chosenDevice(guid) {
+ if (!g_chooserService)
+ throw new Error('Call initialize() before setting chosenDevice.');
+
+ g_chooserService.setChosenDevice(guid);
+ }
+
+ get lastFilters() {
+ if (!g_chooserService)
+ throw new Error('Call initialize() before getting lastFilters.');
+
+ return g_chooserService.lastFilters_;
+ }
+
+ reset() {
+ if (!g_deviceManager || !g_chooserService)
+ throw new Error('Call initialize() before reset().');
+
+ g_deviceManager.removeAllDevices();
+ g_chooserService.setChosenDevice(null);
+ g_closeListener = null;
+ }
+}
+
+navigator.usb.test = new USBTest();
+
+})();
« 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