// META: timeout=long // META: script=/resources/test-only-api.js // META: script=/webusb/resources/fake-devices.js // META: script=/webusb/resources/usb-helpers.js 'use strict'; function detachBuffer(buffer) { if (self.GLOBAL.isWindow()) window.postMessage('', '*', [buffer]); else self.postMessage('', [buffer]); } usb_test((t) => { return getFakeDevice().then(({device, fakeDevice}) => { return waitForDisconnect(fakeDevice) .then(() => promise_rejects_dom(t, 'NotFoundError', device.open())); }); }, 'open rejects when called on a disconnected device'); usb_test(() => { return getFakeDevice().then(({ device, fakeDevice }) => { return device.open() .then(() => waitForDisconnect(fakeDevice)) .then(() => { assert_false(device.opened); }); }); }, 'disconnection closes the device'); usb_test(() => { return getFakeDevice().then(({ device }) => { assert_false(device.opened); return device.open().then(() => { assert_true(device.opened); return device.close().then(() => { assert_false(device.opened); }); }); }); }, 'a device can be opened and closed'); usb_test(() => { return getFakeDevice().then(({ device }) => { return device.open() .then(() => device.open()) .then(() => device.open()) .then(() => device.open()) .then(() => device.close()) .then(() => device.close()) .then(() => device.close()) .then(() => device.close()); }); }, 'open and close can be called multiple times'); usb_test(async (t) => { let { device } = await getFakeDevice(); await Promise.all([ device.open(), promise_rejects_dom(t, 'InvalidStateError', device.open()), promise_rejects_dom(t, 'InvalidStateError', device.close()), ]); await Promise.all([ device.close(), promise_rejects_dom(t, 'InvalidStateError', device.open()), promise_rejects_dom(t, 'InvalidStateError', device.close()), ]); }, 'open and close cannot be called again while open or close are in progress'); usb_test(async (t) => { let { device } = await getFakeDevice(); await device.open(); return Promise.all([ device.selectConfiguration(1), promise_rejects_dom(t, 'InvalidStateError', device.claimInterface(0)), promise_rejects_dom(t, 'InvalidStateError', device.releaseInterface(0)), promise_rejects_dom(t, 'InvalidStateError', device.open()), promise_rejects_dom(t, 'InvalidStateError', device.selectConfiguration(1)), promise_rejects_dom(t, 'InvalidStateError', device.reset()), promise_rejects_dom( t, 'InvalidStateError', device.selectAlternateInterface(0, 0)), promise_rejects_dom(t, 'InvalidStateError', device.controlTransferOut({ requestType: 'standard', recipient: 'interface', request: 0x42, value: 0x1234, index: 0x0000, })), promise_rejects_dom( t, 'InvalidStateError', device.controlTransferOut( { requestType: 'standard', recipient: 'interface', request: 0x42, value: 0x1234, index: 0x0000, }, new Uint8Array([1, 2, 3]))), promise_rejects_dom( t, 'InvalidStateError', device.controlTransferIn( { requestType: 'standard', recipient: 'interface', request: 0x42, value: 0x1234, index: 0x0000 }, 0)), promise_rejects_dom(t, 'InvalidStateError', device.close()), ]); }, 'device operations reject if an device state change is in progress'); usb_test((t) => { return getFakeDevice().then(({device, fakeDevice}) => { return device.open() .then(() => waitForDisconnect(fakeDevice)) .then(() => promise_rejects_dom(t, 'NotFoundError', device.close())); }); }, 'close rejects when called on a disconnected device'); usb_test((t) => { return getFakeDevice().then(({device, fakeDevice}) => { return device.open() .then(() => waitForDisconnect(fakeDevice)) .then( () => promise_rejects_dom( t, 'NotFoundError', device.selectConfiguration(1))); }); }, 'selectConfiguration rejects when called on a disconnected device'); usb_test((t) => { return getFakeDevice().then(({device}) => Promise.all([ promise_rejects_dom(t, 'InvalidStateError', device.selectConfiguration(1)), promise_rejects_dom(t, 'InvalidStateError', device.claimInterface(0)), promise_rejects_dom(t, 'InvalidStateError', device.releaseInterface(0)), promise_rejects_dom( t, 'InvalidStateError', device.selectAlternateInterface(0, 1)), promise_rejects_dom( t, 'InvalidStateError', device.controlTransferIn( { requestType: 'vendor', recipient: 'device', request: 0x42, value: 0x1234, index: 0x5678 }, 7)), promise_rejects_dom( t, 'InvalidStateError', device.controlTransferOut( { requestType: 'vendor', recipient: 'device', request: 0x42, value: 0x1234, index: 0x5678 }, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]))), promise_rejects_dom(t, 'InvalidStateError', device.clearHalt('in', 1)), promise_rejects_dom(t, 'InvalidStateError', device.transferIn(1, 8)), promise_rejects_dom( t, 'InvalidStateError', device.transferOut(1, new ArrayBuffer(8))), promise_rejects_dom( t, 'InvalidStateError', device.isochronousTransferIn(1, [8])), promise_rejects_dom( t, 'InvalidStateError', device.isochronousTransferOut(1, new ArrayBuffer(8), [8])), promise_rejects_dom(t, 'InvalidStateError', device.reset()) ])); }, 'methods requiring it reject when the device is not open'); usb_test(() => { return getFakeDevice().then(({ device }) => { assert_equals(device.configuration, null); return device.open() .then(() => { assert_equals(device.configuration, null); return device.selectConfiguration(1); }) .then(() => { assertDeviceInfoEquals( device.configuration, fakeDeviceInit.configurations[0]); }) .then(() => device.close()); }); }, 'device configuration can be set and queried'); usb_test(async () => { let { device } = await getFakeDevice(); assert_equals(device.configuration, null); await device.open(); assert_equals(device.configuration, null); await device.selectConfiguration(1); await device.selectConfiguration(1); assertDeviceInfoEquals( device.configuration, fakeDeviceInit.configurations[0]); await device.selectConfiguration(2); assertDeviceInfoEquals( device.configuration, fakeDeviceInit.configurations[1]); await device.close(); }, 'a device configuration value can be set again'); usb_test((t) => { return getFakeDevice().then(({ device }) => { assert_equals(device.configuration, null); return device.open() .then( () => promise_rejects_dom( t, 'NotFoundError', device.selectConfiguration(10))) .then(() => device.close()); }); }, 'selectConfiguration rejects on invalid configurations'); usb_test((t) => { return getFakeDevice().then(({ device }) => { assert_equals(device.configuration, null); return device.open() .then(() => Promise.all([ promise_rejects_dom(t, 'InvalidStateError', device.claimInterface(0)), promise_rejects_dom( t, 'InvalidStateError', device.releaseInterface(0)), promise_rejects_dom( t, 'InvalidStateError', device.selectAlternateInterface(0, 1)), promise_rejects_dom( t, 'InvalidStateError', device.clearHalt('in', 1)), promise_rejects_dom(t, 'InvalidStateError', device.transferIn(1, 8)), promise_rejects_dom( t, 'InvalidStateError', device.transferOut(1, new ArrayBuffer(8))), promise_rejects_dom( t, 'InvalidStateError', device.isochronousTransferIn(1, [8])), promise_rejects_dom( t, 'InvalidStateError', device.isochronousTransferOut(1, new ArrayBuffer(8), [8])), ])) .then(() => device.close()); }); }, 'methods requiring it reject when the device is unconfigured'); usb_test(async () => { let { device } = await getFakeDevice(); await device.open(); await device.selectConfiguration(1); assert_false(device.configuration.interfaces[0].claimed); assert_false(device.configuration.interfaces[1].claimed); await device.claimInterface(0); assert_true(device.configuration.interfaces[0].claimed); assert_false(device.configuration.interfaces[1].claimed); await device.claimInterface(1); assert_true(device.configuration.interfaces[0].claimed); assert_true(device.configuration.interfaces[1].claimed); await device.releaseInterface(0); assert_false(device.configuration.interfaces[0].claimed); assert_true(device.configuration.interfaces[1].claimed); await device.releaseInterface(1); assert_false(device.configuration.interfaces[0].claimed); assert_false(device.configuration.interfaces[1].claimed); await device.close(); }, 'interfaces can be claimed and released'); usb_test(async () => { let { device } = await getFakeDevice(); await device.open(); await device.selectConfiguration(1); assert_false(device.configuration.interfaces[0].claimed); assert_false(device.configuration.interfaces[1].claimed); await Promise.all([device.claimInterface(0), device.claimInterface(1)]); assert_true(device.configuration.interfaces[0].claimed); assert_true(device.configuration.interfaces[1].claimed); await Promise.all([device.releaseInterface(0), device.releaseInterface(1)]); assert_false(device.configuration.interfaces[0].claimed); assert_false(device.configuration.interfaces[1].claimed); await device.close(); }, 'interfaces can be claimed and released in parallel'); usb_test(async () => { let { device } = await getFakeDevice() await device.open(); await device.selectConfiguration(1); await device.claimInterface(0); assert_true(device.configuration.interfaces[0].claimed); await device.claimInterface(0); assert_true(device.configuration.interfaces[0].claimed); await device.close(); }, 'an interface can be claimed multiple times'); usb_test(async () => { let { device } = await getFakeDevice(); await device.open(); await device.selectConfiguration(1); await device.claimInterface(0); assert_true(device.configuration.interfaces[0].claimed); await device.releaseInterface(0); assert_false(device.configuration.interfaces[0].claimed); await device.releaseInterface(0); assert_false(device.configuration.interfaces[0].claimed); await device.close(); }, 'an interface can be released multiple times'); usb_test(async (t) => { let { device } = await getFakeDevice(); await device.open(); await device.selectConfiguration(1); return Promise.all([ device.claimInterface(0), promise_rejects_dom(t, 'InvalidStateError', device.claimInterface(0)), promise_rejects_dom(t, 'InvalidStateError', device.releaseInterface(0)), promise_rejects_dom(t, 'InvalidStateError', device.open()), promise_rejects_dom(t, 'InvalidStateError', device.selectConfiguration(1)), promise_rejects_dom(t, 'InvalidStateError', device.reset()), promise_rejects_dom( t, 'InvalidStateError', device.selectAlternateInterface(0, 0)), promise_rejects_dom(t, 'InvalidStateError', device.controlTransferOut({ requestType: 'standard', recipient: 'interface', request: 0x42, value: 0x1234, index: 0x0000, })), promise_rejects_dom( t, 'InvalidStateError', device.controlTransferOut( { requestType: 'standard', recipient: 'interface', request: 0x42, value: 0x1234, index: 0x0000, }, new Uint8Array([1, 2, 3]))), promise_rejects_dom( t, 'InvalidStateError', device.controlTransferIn( { requestType: 'standard', recipient: 'interface', request: 0x42, value: 0x1234, index: 0x0000 }, 0)), promise_rejects_dom(t, 'InvalidStateError', device.close()), ]); }, 'device operations reject if an interface state change is in progress'); usb_test(async () => { let { device } = await getFakeDevice(); await device.open(); await device.selectConfiguration(1); await device.claimInterface(0); assert_true(device.configuration.interfaces[0].claimed); await device.close(0); assert_false(device.configuration.interfaces[0].claimed); }, 'interfaces are released on close'); usb_test((t) => { return getFakeDevice().then(({device}) => { return device.open() .then(() => device.selectConfiguration(1)) .then(() => Promise.all([ promise_rejects_dom(t, 'NotFoundError', device.claimInterface(2)), promise_rejects_dom(t, 'NotFoundError', device.releaseInterface(2)), ])) .then(() => device.close()); }); }, 'a non-existent interface cannot be claimed or released'); usb_test((t) => { return getFakeDevice().then(({device, fakeDevice}) => { return device.open() .then(() => device.selectConfiguration(1)) .then(() => waitForDisconnect(fakeDevice)) .then( () => promise_rejects_dom( t, 'NotFoundError', device.claimInterface(0))); }); }, 'claimInterface rejects when called on a disconnected device'); usb_test((t) => { return getFakeDevice().then(({device, fakeDevice}) => { return device.open() .then(() => device.selectConfiguration(1)) .then(() => device.claimInterface(0)) .then(() => waitForDisconnect(fakeDevice)) .then( () => promise_rejects_dom( t, 'NotFoundError', device.releaseInterface(0))); }); }, 'releaseInterface rejects when called on a disconnected device'); usb_test(() => { return getFakeDevice().then(({ device }) => { return device.open() .then(() => device.selectConfiguration(2)) .then(() => device.claimInterface(0)) .then(() => device.selectAlternateInterface(0, 1)) .then(() => device.close()); }); }, 'can select an alternate interface'); usb_test( async () => { const {device} = await getFakeDevice(); await device.open(); await device.selectConfiguration(3); await device.claimInterface(2); await device.selectAlternateInterface(2, 0); await device.close(); }, 'can select an alternate interface on a setting with non-sequential ' + 'interface number'); usb_test( async () => { const {device} = await getFakeDevice(); await device.open(); await device.selectConfiguration(3); await device.claimInterface(0); await device.selectAlternateInterface(0, 2); await device.close(); }, 'can select an alternate interface on a setting with non-sequential ' + 'alternative setting value'); usb_test((t) => { return getFakeDevice().then(({device}) => { return device.open() .then(() => device.selectConfiguration(2)) .then(() => device.claimInterface(0)) .then( () => promise_rejects_dom( t, 'NotFoundError', device.selectAlternateInterface(0, 2))) .then(() => device.close()); }); }, 'cannot select a non-existent alternate interface'); usb_test((t) => { return getFakeDevice().then(({device, fakeDevice}) => { return device.open() .then(() => device.selectConfiguration(2)) .then(() => device.claimInterface(0)) .then(() => waitForDisconnect(fakeDevice)) .then( () => promise_rejects_dom( t, 'NotFoundError', device.selectAlternateInterface(0, 1))); }); }, 'selectAlternateInterface rejects when called on a disconnected device'); usb_test(async () => { let { device } = await getFakeDevice(); let usbRequestTypes = ['standard', 'class', 'vendor']; let usbRecipients = ['device', 'interface', 'endpoint', 'other']; await device.open(); await device.selectConfiguration(1); await device.claimInterface(0); await device.selectAlternateInterface(0, 0); for (const requestType of usbRequestTypes) { for (const recipient of usbRecipients) { let index = recipient === 'interface' ? 0x5600 : 0x5681; let result = await device.controlTransferIn({ requestType: requestType, recipient: recipient, request: 0x42, value: 0x1234, index: index }, 7); assert_true(result instanceof USBInTransferResult); assert_equals(result.status, 'ok'); assert_equals(result.data.byteLength, 7); assert_equals(result.data.getUint16(0), 0x07); assert_equals(result.data.getUint8(2), 0x42); assert_equals(result.data.getUint16(3), 0x1234); assert_equals(result.data.getUint16(5), index); } } await device.close(); }, 'can issue all types of IN control transfers'); usb_test(async () => { let { device } = await getFakeDevice(); let usbRequestTypes = ['standard', 'class', 'vendor']; let usbRecipients = ['device', 'other']; await device.open(); await Promise.all(usbRequestTypes.flatMap(requestType => { return usbRecipients.map(async recipient => { let result = await device.controlTransferIn({ requestType: requestType, recipient: recipient, request: 0x42, value: 0x1234, index: 0x5678 }, 7); assert_true(result instanceof USBInTransferResult); assert_equals(result.status, 'ok'); assert_equals(result.data.byteLength, 7); assert_equals(result.data.getUint16(0), 0x07); assert_equals(result.data.getUint8(2), 0x42); assert_equals(result.data.getUint16(3), 0x1234); assert_equals(result.data.getUint16(5), 0x5678); }); })); await device.close(); }, 'device-scope IN control transfers don\'t require configuration'); usb_test(async (t) => { let { device } = await getFakeDevice(); let usbRequestTypes = ['standard', 'class', 'vendor']; let usbRecipients = ['interface', 'endpoint']; await device.open(); await Promise.all(usbRequestTypes.flatMap(requestType => { return usbRecipients.map(recipient => { let index = recipient === 'interface' ? 0x5600 : 0x5681; return promise_rejects_dom( t, 'InvalidStateError', device.controlTransferIn( { requestType: requestType, recipient: recipient, request: 0x42, value: 0x1234, index: index }, 7)); }); })); await device.close(); }, 'interface-scope IN control transfers require configuration'); usb_test(async (t) => { let { device } = await getFakeDevice(); let usbRequestTypes = ['standard', 'class', 'vendor']; let usbRecipients = ['interface', 'endpoint']; await device.open(); await device.selectConfiguration(1); await Promise.all(usbRequestTypes.flatMap(requestType => { return [ promise_rejects_dom( t, 'InvalidStateError', device.controlTransferIn( { requestType: requestType, recipient: 'interface', request: 0x42, value: 0x1234, index: 0x5600 }, 7)), promise_rejects_dom( t, 'NotFoundError', device.controlTransferIn( { requestType: requestType, recipient: 'endpoint', request: 0x42, value: 0x1234, index: 0x5681 }, 7)) ]; })); await device.close(); }, 'interface-scope IN control transfers require claiming the interface'); usb_test((t) => { return getFakeDevice().then(({device, fakeDevice}) => { return device.open() .then(() => device.selectConfiguration(1)) .then(() => waitForDisconnect(fakeDevice)) .then( () => promise_rejects_dom( t, 'NotFoundError', device.controlTransferIn( { requestType: 'vendor', recipient: 'device', request: 0x42, value: 0x1234, index: 0x5678 }, 7))); }); }, 'controlTransferIn rejects when called on a disconnected device'); usb_test(async () => { let { device } = await getFakeDevice(); let usbRequestTypes = ['standard', 'class', 'vendor']; let usbRecipients = ['device', 'interface', 'endpoint', 'other']; let dataArray = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); let dataTypes = [dataArray, dataArray.buffer]; await device.open(); await device.selectConfiguration(1); await device.claimInterface(0); await device.selectAlternateInterface(0, 0); for (const requestType of usbRequestTypes) { for (const recipient of usbRecipients) { let index = recipient === 'interface' ? 0x5600 : 0x5681; let transferParams = { requestType: requestType, recipient: recipient, request: 0x42, value: 0x1234, index: index }; for (const data of dataTypes) { let result = await device.controlTransferOut(transferParams, data); assert_true(result instanceof USBOutTransferResult); assert_equals(result.status, 'ok'); assert_equals(result.bytesWritten, 8); } let result = await device.controlTransferOut(transferParams); assert_true(result instanceof USBOutTransferResult); assert_equals(result.status, 'ok'); } } await device.close(); }, 'can issue all types of OUT control transfers'); usb_test(async () => { let { device } = await getFakeDevice(); let usbRequestTypes = ['standard', 'class', 'vendor']; let usbRecipients = ['device', 'other']; let dataArray = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); let dataTypes = [dataArray, dataArray.buffer]; await device.open(); await Promise.all(usbRequestTypes.flatMap(requestType => { return usbRecipients.flatMap(recipient => { let transferParams = { requestType: requestType, recipient: recipient, request: 0x42, value: 0x1234, index: 0x5678 }; return dataTypes.map(async data => { let result = await device.controlTransferOut(transferParams, data); assert_true(result instanceof USBOutTransferResult); assert_equals(result.status, 'ok'); assert_equals(result.bytesWritten, 8); }).push((async () => { let result = await device.controlTransferOut(transferParams); assert_true(result instanceof USBOutTransferResult); assert_equals(result.status, 'ok'); })()); }); })); await device.close(); }, 'device-scope OUT control transfers don\'t require configuration'); usb_test(async (t) => { let { device } = await getFakeDevice(); let usbRequestTypes = ['standard', 'class', 'vendor']; let usbRecipients = ['interface', 'endpoint']; let dataArray = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); let dataTypes = [dataArray, dataArray.buffer]; await device.open(); await Promise.all(usbRequestTypes.flatMap(requestType => { return usbRecipients.flatMap(recipient => { let index = recipient === 'interface' ? 0x5600 : 0x5681; let transferParams = { requestType: requestType, recipient: recipient, request: 0x42, value: 0x1234, index: index }; return dataTypes .map(data => { return promise_rejects_dom( t, 'InvalidStateError', device.controlTransferOut(transferParams, data)); }) .push(promise_rejects_dom( t, 'InvalidStateError', device.controlTransferOut(transferParams))); }); })); await device.close(); }, 'interface-scope OUT control transfers require configuration'); usb_test(async (t) => { let { device } = await getFakeDevice(); let usbRequestTypes = ['standard', 'class', 'vendor']; let usbRecipients = ['interface', 'endpoint']; let dataArray = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); let dataTypes = [dataArray, dataArray.buffer]; await device.open(); await device.selectConfiguration(1); await Promise.all(usbRequestTypes.flatMap(requestType => { return usbRecipients.flatMap(recipient => { let index = recipient === 'interface' ? 0x5600 : 0x5681; let error = recipient === 'interface' ? 'InvalidStateError' : 'NotFoundError'; let transferParams = { requestType: requestType, recipient: recipient, request: 0x42, value: 0x1234, index: index }; return dataTypes .map(data => { return promise_rejects_dom( t, error, device.controlTransferOut(transferParams, data)); }) .push(promise_rejects_dom( t, error, device.controlTransferOut(transferParams))); }); })); await device.close(); }, 'interface-scope OUT control transfers an interface claim'); usb_test((t) => { return getFakeDevice().then(({device, fakeDevice}) => { return device.open() .then(() => device.selectConfiguration(1)) .then(() => waitForDisconnect(fakeDevice)) .then( () => promise_rejects_dom( t, 'NotFoundError', device.controlTransferOut( { requestType: 'vendor', recipient: 'device', request: 0x42, value: 0x1234, index: 0x5678 }, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])))); }); }, 'controlTransferOut rejects when called on a disconnected device'); usb_test(async (t) => { let { device } = await getFakeDevice(); await device.open(); await device.selectConfiguration(1); await device.claimInterface(0); await Promise.all([ promise_rejects_js( t, TypeError, device.controlTransferOut( { requestType: 'invalid', recipient: 'device', request: 0x42, value: 0x1234, index: 0x5678 }, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]))), promise_rejects_js( t, TypeError, device.controlTransferIn( { requestType: 'invalid', recipient: 'device', request: 0x42, value: 0x1234, index: 0x5678 }, 0)), ]); await device.close(); }, 'control transfers with a invalid request type reject'); usb_test(async (t) => { let { device } = await getFakeDevice(); await device.open(); await device.selectConfiguration(1); await device.claimInterface(0); await Promise.all([ promise_rejects_js( t, TypeError, device.controlTransferOut( { requestType: 'vendor', recipient: 'invalid', request: 0x42, value: 0x1234, index: 0x5678 }, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]))), promise_rejects_js( t, TypeError, device.controlTransferIn( { requestType: 'vendor', recipient: 'invalid', request: 0x42, value: 0x1234, index: 0x5678 }, 0)), ]); }, 'control transfers with a invalid recipient type reject'); usb_test(async (t) => { let { device } = await getFakeDevice(); await device.open(); await device.selectConfiguration(1); await device.claimInterface(0); await Promise.all([ promise_rejects_dom( t, 'NotFoundError', device.controlTransferOut( { requestType: 'vendor', recipient: 'interface', request: 0x42, value: 0x1234, index: 0x0002 // Last byte of index is interface number. }, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]))), promise_rejects_dom( t, 'NotFoundError', device.controlTransferIn( { requestType: 'vendor', recipient: 'interface', request: 0x42, value: 0x1234, index: 0x0002 // Last byte of index is interface number. }, 0)), ]); }, 'control transfers to a non-existant interface reject'); usb_test((t) => { return getFakeDevice().then(({ device }) => { let interfaceRequest = { requestType: 'vendor', recipient: 'interface', request: 0x42, value: 0x1234, index: 0x5600 // Last byte of index is interface number. }; let endpointRequest = { requestType: 'vendor', recipient: 'endpoint', request: 0x42, value: 0x1234, index: 0x5681 // Last byte of index is endpoint address. }; let data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); return device.open() .then(() => device.selectConfiguration(1)) .then(() => Promise.all([ promise_rejects_dom( t, 'InvalidStateError', device.controlTransferIn(interfaceRequest, 7)), promise_rejects_dom( t, 'NotFoundError', device.controlTransferIn(endpointRequest, 7)), promise_rejects_dom( t, 'InvalidStateError', device.controlTransferOut(interfaceRequest, data)), promise_rejects_dom( t, 'NotFoundError', device.controlTransferOut(endpointRequest, data)), ])) .then(() => device.claimInterface(0)) .then(() => Promise.all([ device.controlTransferIn(interfaceRequest, 7).then(result => { assert_true(result instanceof USBInTransferResult); assert_equals(result.status, 'ok'); assert_equals(result.data.byteLength, 7); assert_equals(result.data.getUint16(0), 0x07); assert_equals(result.data.getUint8(2), 0x42); assert_equals(result.data.getUint16(3), 0x1234); assert_equals(result.data.getUint16(5), 0x5600); }), device.controlTransferIn(endpointRequest, 7).then(result => { assert_true(result instanceof USBInTransferResult); assert_equals(result.status, 'ok'); assert_equals(result.data.byteLength, 7); assert_equals(result.data.getUint16(0), 0x07); assert_equals(result.data.getUint8(2), 0x42); assert_equals(result.data.getUint16(3), 0x1234); assert_equals(result.data.getUint16(5), 0x5681); }), device.controlTransferOut(interfaceRequest, data), device.controlTransferOut(endpointRequest, data), ])) .then(() => device.close()); }); }, 'requests to interfaces and endpoint require an interface claim'); usb_test(async () => { const { device } = await getFakeDevice(); await device.open(); await device.selectConfiguration(1); await device.claimInterface(0); const transfer_params = { requestType: 'vendor', recipient: 'device', request: 0, value: 0, index: 0 }; try { const array_buffer = new ArrayBuffer(64 * 8); const result = await device.controlTransferOut(transfer_params, array_buffer); assert_equals(result.status, 'ok'); detachBuffer(array_buffer); await device.controlTransferOut(transfer_params, array_buffer); assert_unreached(); } catch (e) { assert_equals(e.code, DOMException.INVALID_STATE_ERR); } try { const typed_array = new Uint8Array(64 * 8); const result = await device.controlTransferOut(transfer_params, typed_array); assert_equals(result.status, 'ok'); detachBuffer(typed_array.buffer); await device.controlTransferOut(transfer_params, typed_array); assert_unreached(); } catch (e) { assert_equals(e.code, DOMException.INVALID_STATE_ERR); } }, 'controlTransferOut rejects if called with a detached buffer'); usb_test(() => { return getFakeDevice().then(({ device }) => { return device.open() .then(() => device.selectConfiguration(1)) .then(() => device.claimInterface(0)) .then(() => device.clearHalt('in', 1)) .then(() => device.close()); }); }, 'can clear a halt condition'); usb_test((t) => { return getFakeDevice(t).then(({device, fakeDevice}) => { return device.open() .then(() => device.selectConfiguration(1)) .then(() => device.claimInterface(0)) .then(() => waitForDisconnect(fakeDevice)) .then( () => promise_rejects_dom( t, 'NotFoundError', device.clearHalt('in', 1))); }); }, 'clearHalt rejects when called on a disconnected device'); usb_test((t) => { return getFakeDevice().then(({ device }) => { let data = new DataView(new ArrayBuffer(1024)); for (let i = 0; i < 1024; ++i) data.setUint8(i, i & 0xff); return device.open() .then(() => device.selectConfiguration(1)) .then(() => device.claimInterface(0)) .then(() => Promise.all([ promise_rejects_dom( t, 'NotFoundError', device.transferIn(2, 8)), // Unclaimed promise_rejects_dom( t, 'NotFoundError', device.transferIn(3, 8)), // Non-existent promise_rejects_dom(t, 'IndexSizeError', device.transferIn(16, 8)), promise_rejects_dom( t, 'NotFoundError', device.transferOut(2, data)), // Unclaimed promise_rejects_dom( t, 'NotFoundError', device.transferOut(3, data)), // Non-existent promise_rejects_dom( t, 'IndexSizeError', device.transferOut(16, data)), ])); }); }, 'transfers to unavailable endpoints are rejected'); usb_test(() => { return getFakeDevice().then(({ device }) => { return device.open() .then(() => device.selectConfiguration(1)) .then(() => device.claimInterface(0)) .then(() => device.transferIn(1, 8)) .then(result => { assert_true(result instanceof USBInTransferResult); assert_equals(result.status, 'ok'); assert_equals(result.data.byteLength, 8); for (let i = 0; i < 8; ++i) assert_equals(result.data.getUint8(i), i, 'mismatch at byte ' + i); return device.close(); }); }); }, 'can issue IN interrupt transfer'); usb_test(() => { return getFakeDevice().then(({ device }) => { return device.open() .then(() => device.selectConfiguration(1)) .then(() => device.claimInterface(1)) .then(() => device.transferIn(2, 1024)) .then(result => { assert_true(result instanceof USBInTransferResult); assert_equals(result.status, 'ok'); assert_equals(result.data.byteLength, 1024); for (let i = 0; i < 1024; ++i) assert_equals(result.data.getUint8(i), i & 0xff, 'mismatch at byte ' + i); return device.close(); }); }); }, 'can issue IN bulk transfer'); usb_test((t) => { return getFakeDevice().then(({device, fakeDevice}) => { return device.open() .then(() => device.selectConfiguration(1)) .then(() => device.claimInterface(1)) .then(() => waitForDisconnect(fakeDevice)) .then( () => promise_rejects_dom( t, 'NotFoundError', device.transferIn(2, 1024))); }); }, 'transferIn rejects if called on a disconnected device'); usb_test(() => { return getFakeDevice().then(({ device }) => { return device.open() .then(() => device.selectConfiguration(1)) .then(() => device.claimInterface(1)) .then(() => { let data = new DataView(new ArrayBuffer(1024)); for (let i = 0; i < 1024; ++i) data.setUint8(i, i & 0xff); return device.transferOut(2, data); }) .then(result => { assert_true(result instanceof USBOutTransferResult); assert_equals(result.status, 'ok'); assert_equals(result.bytesWritten, 1024); return device.close(); }); }); }, 'can issue OUT bulk transfer'); usb_test((t) => { return getFakeDevice().then(({ device, fakeDevice }) => { return device.open() .then(() => device.selectConfiguration(1)) .then(() => device.claimInterface(1)) .then(() => { let data = new DataView(new ArrayBuffer(1024)); for (let i = 0; i < 1024; ++i) data.setUint8(i, i & 0xff); return waitForDisconnect(fakeDevice) .then( () => promise_rejects_dom( t, 'NotFoundError', device.transferOut(2, data))); }); }); }, 'transferOut rejects if called on a disconnected device'); usb_test(async () => { const { device } = await getFakeDevice(); await device.open(); await device.selectConfiguration(1); await device.claimInterface(1); try { const array_buffer = new ArrayBuffer(64 * 8); const result = await device.transferOut(2, array_buffer); assert_equals(result.status, 'ok'); detachBuffer(array_buffer); await device.transferOut(2, array_buffer); assert_unreached(); } catch (e) { assert_equals(e.code, DOMException.INVALID_STATE_ERR); } try { const typed_array = new Uint8Array(64 * 8); const result = await device.transferOut(2, typed_array); assert_equals(result.status, 'ok'); detachBuffer(typed_array.buffer); await device.transferOut(2, typed_array); assert_unreached(); } catch (e) { assert_equals(e.code, DOMException.INVALID_STATE_ERR); } }, 'transferOut rejects if called with a detached buffer'); usb_test(() => { return getFakeDevice().then(({ device }) => { return device.open() .then(() => device.selectConfiguration(2)) .then(() => device.claimInterface(0)) .then(() => device.selectAlternateInterface(0, 1)) .then(() => device.isochronousTransferIn( 1, [64, 64, 64, 64, 64, 64, 64, 64])) .then(result => { assert_true(result instanceof USBIsochronousInTransferResult); assert_equals(result.data.byteLength, 64 * 8, 'buffer size'); assert_equals(result.packets.length, 8, 'number of packets'); let byteOffset = 0; for (let i = 0; i < result.packets.length; ++i) { assert_true( result.packets[i] instanceof USBIsochronousInTransferPacket); assert_equals(result.packets[i].status, 'ok'); assert_equals(result.packets[i].data.byteLength, 64); assert_equals(result.packets[i].data.buffer, result.data.buffer); assert_equals(result.packets[i].data.byteOffset, byteOffset); for (let j = 0; j < 64; ++j) assert_equals(result.packets[i].data.getUint8(j), j & 0xff, 'mismatch at byte ' + j + ' of packet ' + i); byteOffset += result.packets[i].data.byteLength; } return device.close(); }); }); }, 'can issue IN isochronous transfer'); usb_test((t) => { return getFakeDevice().then(({device, fakeDevice}) => { return device.open() .then(() => device.selectConfiguration(2)) .then(() => device.claimInterface(0)) .then(() => device.selectAlternateInterface(0, 1)) .then(() => waitForDisconnect(fakeDevice)) .then( () => promise_rejects_dom( t, 'NotFoundError', device.isochronousTransferIn( 1, [64, 64, 64, 64, 64, 64, 64, 64]))); }); }, 'isochronousTransferIn rejects when called on a disconnected device'); usb_test(() => { return getFakeDevice().then(({ device }) => { return device.open() .then(() => device.selectConfiguration(2)) .then(() => device.claimInterface(0)) .then(() => device.selectAlternateInterface(0, 1)) .then(() => { let data = new DataView(new ArrayBuffer(64 * 8)); for (let i = 0; i < 8; ++i) { for (let j = 0; j < 64; ++j) data.setUint8(i * j, j & 0xff); } return device.isochronousTransferOut( 1, data, [64, 64, 64, 64, 64, 64, 64, 64]); }) .then(result => { assert_true(result instanceof USBIsochronousOutTransferResult); assert_equals(result.packets.length, 8, 'number of packets'); let byteOffset = 0; for (let i = 0; i < result.packets.length; ++i) { assert_true( result.packets[i] instanceof USBIsochronousOutTransferPacket); assert_equals(result.packets[i].status, 'ok'); assert_equals(result.packets[i].bytesWritten, 64); } return device.close(); }); }); }, 'can issue OUT isochronous transfer'); usb_test((t) => { return getFakeDevice().then(({ device, fakeDevice }) => { return device.open() .then(() => device.selectConfiguration(2)) .then(() => device.claimInterface(0)) .then(() => device.selectAlternateInterface(0, 1)) .then(() => { let data = new DataView(new ArrayBuffer(64 * 8)); for (let i = 0; i < 8; ++i) { for (let j = 0; j < 64; ++j) data.setUint8(i * j, j & 0xff); } return waitForDisconnect(fakeDevice) .then( () => promise_rejects_dom( t, 'NotFoundError', device.isochronousTransferOut( 1, data, [64, 64, 64, 64, 64, 64, 64, 64]))); }); }); }, 'isochronousTransferOut rejects when called on a disconnected device'); usb_test(async () => { const { device } = await getFakeDevice(); await device.open(); await device.selectConfiguration(2); await device.claimInterface(0); await device.selectAlternateInterface(0, 1); try { const array_buffer = new ArrayBuffer(64 * 8); const result = await device.isochronousTransferOut( 1, array_buffer, [64, 64, 64, 64, 64, 64, 64, 64]); for (let i = 0; i < result.packets.length; ++i) assert_equals(result.packets[i].status, 'ok'); detachBuffer(array_buffer); await device.isochronousTransferOut( 1, array_buffer, [64, 64, 64, 64, 64, 64, 64, 64]); assert_unreached(); } catch (e) { assert_equals(e.code, DOMException.INVALID_STATE_ERR); } try { const typed_array = new Uint8Array(64 * 8); const result = await device.isochronousTransferOut( 1, typed_array, [64, 64, 64, 64, 64, 64, 64, 64]); for (let i = 0; i < result.packets.length; ++i) assert_equals(result.packets[i].status, 'ok'); detachBuffer(typed_array.buffer); await device.isochronousTransferOut( 1, typed_array, [64, 64, 64, 64, 64, 64, 64, 64]); assert_unreached(); } catch (e) { assert_equals(e.code, DOMException.INVALID_STATE_ERR); } }, 'isochronousTransferOut rejects when called with a detached buffer'); usb_test(() => { return getFakeDevice().then(({ device }) => { return device.open().then(() => device.reset()).then(() => device.close()); }); }, 'can reset the device'); usb_test((t) => { return getFakeDevice().then(({device, fakeDevice}) => { return device.open() .then(() => waitForDisconnect(fakeDevice)) .then(() => promise_rejects_dom(t, 'NotFoundError', device.reset())); }); }, 'resetDevice rejects when called on a disconnected device'); usb_test(async (t) => { const PACKET_COUNT = 4; const PACKET_LENGTH = 8; const {device, fakeDevice} = await getFakeDevice(); await device.open(); await device.selectConfiguration(2); await device.claimInterface(0); await device.selectAlternateInterface(0, 1); const buffer = new Uint8Array(PACKET_COUNT * PACKET_LENGTH); const packetLengths = new Array(PACKET_COUNT).fill(PACKET_LENGTH); packetLengths[0] = PACKET_LENGTH - 1; await promise_rejects_dom( t, 'DataError', device.isochronousTransferOut(1, buffer, packetLengths)); }, 'isochronousTransferOut rejects when buffer size exceeds packet lengths'); usb_test(async (t) => { const PACKET_COUNT = 4; const PACKET_LENGTH = 8; const {device, fakeDevice} = await getFakeDevice(); await device.open(); await device.selectConfiguration(2); await device.claimInterface(0); await device.selectAlternateInterface(0, 1); const buffer = new Uint8Array(PACKET_COUNT * PACKET_LENGTH); const packetLengths = new Array(PACKET_COUNT).fill(PACKET_LENGTH); packetLengths[0] = PACKET_LENGTH + 1; await promise_rejects_dom( t, 'DataError', device.isochronousTransferOut(1, buffer, packetLengths)); }, 'isochronousTransferOut rejects when packet lengths exceed buffer size'); usb_test(async (t) => { const PACKET_COUNT = 2; const PACKET_LENGTH = 8; const {device, fakeDevice} = await getFakeDevice(); await device.open(); await device.selectConfiguration(2); await device.claimInterface(0); await device.selectAlternateInterface(0, 1); const packetLengths = [0xffffffff, 1]; await promise_rejects_dom( t, 'DataError', device.isochronousTransferIn(1, packetLengths)); }, 'isochronousTransferIn rejects when packet lengths exceed maximum size'); usb_test(async (t) => { const PACKET_COUNT = 2; const PACKET_LENGTH = 8; const {device, fakeDevice} = await getFakeDevice(); await device.open(); await device.selectConfiguration(2); await device.claimInterface(0); await device.selectAlternateInterface(0, 1); const buffer = new Uint8Array(PACKET_LENGTH * PACKET_COUNT); const packetLengths = [0xffffffff, 1]; await promise_rejects_dom( t, 'DataError', device.isochronousTransferOut(1, buffer, packetLengths)); }, 'isochronousTransferOut rejects when packet lengths exceed maximum size');