summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webusb/usbDevice.https.any.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /testing/web-platform/tests/webusb/usbDevice.https.any.js
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/webusb/usbDevice.https.any.js')
-rw-r--r--testing/web-platform/tests/webusb/usbDevice.https.any.js1249
1 files changed, 1249 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webusb/usbDevice.https.any.js b/testing/web-platform/tests/webusb/usbDevice.https.any.js
new file mode 100644
index 0000000000..b1b0c133ce
--- /dev/null
+++ b/testing/web-platform/tests/webusb/usbDevice.https.any.js
@@ -0,0 +1,1249 @@
+// 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');