blob: 5bfd841d5248e91a5cabf10d00d78db23d11f0e5 [file] [log] [blame]
// 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 assertRejectsWithNotFoundError(promise) {
return assertRejectsWithError(promise, 'NotFoundError');
}
function assertRejectsWithTypeError(promise) {
return assertRejectsWithError(promise, 'TypeError');
}
function assertRejectsWithNotOpenError(promise) {
return assertRejectsWithError(
promise, 'InvalidStateError', 'The device must be opened first.');
}
function assertRejectsWithNotConfiguredError(promise) {
return assertRejectsWithError(
promise, 'InvalidStateError',
'The device must have a configuration selected.');
}
function assertRejectsWithNotClaimedError(promise) {
return assertRejectsWithError(
promise, 'InvalidStateError',
'The specified interface has not been claimed.');
}
function assertRejectsWithEndpointNotFoundError(promise) {
return assertRejectsWithError(
promise, 'NotFoundError',
'The specified endpoint is not part of a claimed and selected ' +
'alternate interface.');
}
function assertRejectsWithDeviceStateChangeInProgressError(promise) {
return assertRejectsWithError(
promise, 'InvalidStateError',
'An operation that changes the device state is in progress.');
}
function assertRejectsWithInterfaceStateChangeInProgressError(promise) {
return assertRejectsWithError(
promise, 'InvalidStateError',
'An operation that changes interface state is in progress.');
}
function detachBuffer(buffer) {
if (self.GLOBAL.isWindow())
window.postMessage('', '*', [buffer]);
else
self.postMessage('', [buffer]);
}
usb_test(() => {
return getFakeDevice().then(({ device, fakeDevice }) => {
return waitForDisconnect(fakeDevice)
.then(() => assertRejectsWithNotFoundError(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 () => {
let { device } = await getFakeDevice();
await Promise.all([
device.open(),
assertRejectsWithDeviceStateChangeInProgressError(device.open()),
assertRejectsWithDeviceStateChangeInProgressError(device.close()),
]);
await Promise.all([
device.close(),
assertRejectsWithDeviceStateChangeInProgressError(device.open()),
assertRejectsWithDeviceStateChangeInProgressError(device.close()),
]);
}, 'open and close cannot be called again while open or close are in progress');
usb_test(async () => {
let { device } = await getFakeDevice();
await device.open();
return Promise.all([
device.selectConfiguration(1),
assertRejectsWithDeviceStateChangeInProgressError(
device.claimInterface(0)),
assertRejectsWithDeviceStateChangeInProgressError(
device.releaseInterface(0)),
assertRejectsWithDeviceStateChangeInProgressError(device.open()),
assertRejectsWithDeviceStateChangeInProgressError(
device.selectConfiguration(1)),
assertRejectsWithDeviceStateChangeInProgressError(device.reset()),
assertRejectsWithDeviceStateChangeInProgressError(
device.selectAlternateInterface(0, 0)),
assertRejectsWithDeviceStateChangeInProgressError(
device.controlTransferOut({
requestType: 'standard',
recipient: 'interface',
request: 0x42,
value: 0x1234,
index: 0x0000,
})),
assertRejectsWithDeviceStateChangeInProgressError(
device.controlTransferOut({
requestType: 'standard',
recipient: 'interface',
request: 0x42,
value: 0x1234,
index: 0x0000,
}, new Uint8Array([1, 2, 3]))),
assertRejectsWithDeviceStateChangeInProgressError(
device.controlTransferIn({
requestType: 'standard',
recipient: 'interface',
request: 0x42,
value: 0x1234,
index: 0x0000
}, 0)),
assertRejectsWithDeviceStateChangeInProgressError(device.close()),
]);
}, 'device operations reject if an device state change is in progress');
usb_test(() => {
return getFakeDevice().then(({ device, fakeDevice }) => {
return device.open()
.then(() => waitForDisconnect(fakeDevice))
.then(() => assertRejectsWithNotFoundError(device.close()));
});
}, 'close rejects when called on a disconnected device');
usb_test(() => {
return getFakeDevice().then(({ device, fakeDevice }) => {
return device.open()
.then(() => waitForDisconnect(fakeDevice))
.then(() => assertRejectsWithNotFoundError(device.selectConfiguration(1)));
});
}, 'selectConfiguration rejects when called on a disconnected device');
usb_test(() => {
return getFakeDevice().then(({ device }) => Promise.all([
assertRejectsWithNotOpenError(device.selectConfiguration(1)),
assertRejectsWithNotOpenError(device.claimInterface(0)),
assertRejectsWithNotOpenError(device.releaseInterface(0)),
assertRejectsWithNotOpenError(device.selectAlternateInterface(0, 1)),
assertRejectsWithNotOpenError(device.controlTransferIn({
requestType: 'vendor',
recipient: 'device',
request: 0x42,
value: 0x1234,
index: 0x5678
}, 7)),
assertRejectsWithNotOpenError(device.controlTransferOut({
requestType: 'vendor',
recipient: 'device',
request: 0x42,
value: 0x1234,
index: 0x5678
}, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]))),
assertRejectsWithNotOpenError(device.clearHalt('in', 1)),
assertRejectsWithNotOpenError(device.transferIn(1, 8)),
assertRejectsWithNotOpenError(
device.transferOut(1, new ArrayBuffer(8))),
assertRejectsWithNotOpenError(device.isochronousTransferIn(1, [8])),
assertRejectsWithNotOpenError(
device.isochronousTransferOut(1, new ArrayBuffer(8), [8])),
assertRejectsWithNotOpenError(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(() => {
return getFakeDevice().then(({ device }) => {
assert_equals(device.configuration, null);
return device.open()
.then(() => assertRejectsWithError(
device.selectConfiguration(3), 'NotFoundError',
'The configuration value provided is not supported by the device.'))
.then(() => device.close());
});
}, 'selectConfiguration rejects on invalid configurations');
usb_test(() => {
return getFakeDevice().then(({ device }) => {
assert_equals(device.configuration, null);
return device.open().then(() => Promise.all([
assertRejectsWithNotConfiguredError(device.claimInterface(0)),
assertRejectsWithNotConfiguredError(device.releaseInterface(0)),
assertRejectsWithNotConfiguredError(device.selectAlternateInterface(0, 1)),
assertRejectsWithNotConfiguredError(device.clearHalt('in', 1)),
assertRejectsWithNotConfiguredError(device.transferIn(1, 8)),
assertRejectsWithNotConfiguredError(
device.transferOut(1, new ArrayBuffer(8))),
assertRejectsWithNotConfiguredError(
device.isochronousTransferIn(1, [8])),
assertRejectsWithNotConfiguredError(
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 () => {
let { device } = await getFakeDevice();
await device.open();
await device.selectConfiguration(1);
return Promise.all([
device.claimInterface(0),
assertRejectsWithInterfaceStateChangeInProgressError(
device.claimInterface(0)),
assertRejectsWithInterfaceStateChangeInProgressError(
device.releaseInterface(0)),
assertRejectsWithInterfaceStateChangeInProgressError(device.open()),
assertRejectsWithInterfaceStateChangeInProgressError(
device.selectConfiguration(1)),
assertRejectsWithInterfaceStateChangeInProgressError(device.reset()),
assertRejectsWithInterfaceStateChangeInProgressError(
device.selectAlternateInterface(0, 0)),
assertRejectsWithInterfaceStateChangeInProgressError(
device.controlTransferOut({
requestType: 'standard',
recipient: 'interface',
request: 0x42,
value: 0x1234,
index: 0x0000,
})),
assertRejectsWithInterfaceStateChangeInProgressError(
device.controlTransferOut({
requestType: 'standard',
recipient: 'interface',
request: 0x42,
value: 0x1234,
index: 0x0000,
}, new Uint8Array([1, 2, 3]))),
assertRejectsWithInterfaceStateChangeInProgressError(
device.controlTransferIn({
requestType: 'standard',
recipient: 'interface',
request: 0x42,
value: 0x1234,
index: 0x0000
}, 0)),
assertRejectsWithInterfaceStateChangeInProgressError(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(() => {
return getFakeDevice().then(({ device }) => {
const message = 'The interface number provided is not supported by the ' +
'device in its current configuration.';
return device.open()
.then(() => device.selectConfiguration(1))
.then(() => Promise.all([
assertRejectsWithError(
device.claimInterface(2), 'NotFoundError', message),
assertRejectsWithError(
device.releaseInterface(2), 'NotFoundError', message),
]))
.then(() => device.close());
});
}, 'a non-existent interface cannot be claimed or released');
usb_test(() => {
return getFakeDevice().then(({ device, fakeDevice }) => {
return device.open()
.then(() => device.selectConfiguration(1))
.then(() => waitForDisconnect(fakeDevice))
.then(() => assertRejectsWithNotFoundError(device.claimInterface(0)));
});
}, 'claimInterface rejects when called on a disconnected device');
usb_test(() => {
return getFakeDevice().then(({ device, fakeDevice }) => {
return device.open()
.then(() => device.selectConfiguration(1))
.then(() => device.claimInterface(0))
.then(() => waitForDisconnect(fakeDevice))
.then(() => assertRejectsWithNotFoundError(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(() => {
return getFakeDevice().then(({ device }) => {
return device.open()
.then(() => device.selectConfiguration(2))
.then(() => device.claimInterface(0))
.then(() => assertRejectsWithError(
device.selectAlternateInterface(0, 2), 'NotFoundError',
'The alternate setting provided is not supported by the device in ' +
'its current configuration.'))
.then(() => device.close());
});
}, 'cannot select a non-existent alternate interface');
usb_test(() => {
return getFakeDevice().then(({ device, fakeDevice }) => {
return device.open()
.then(() => device.selectConfiguration(2))
.then(() => device.claimInterface(0))
.then(() => waitForDisconnect(fakeDevice))
.then(() => assertRejectsWithNotFoundError(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 () => {
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 assertRejectsWithNotConfiguredError(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 () => {
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 [
assertRejectsWithNotClaimedError(device.controlTransferIn({
requestType: requestType,
recipient: 'interface',
request: 0x42,
value: 0x1234,
index: 0x5600
}, 7)),
assertRejectsWithNotFoundError(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(() => {
return getFakeDevice().then(({ device, fakeDevice }) => {
return device.open()
.then(() => device.selectConfiguration(1))
.then(() => waitForDisconnect(fakeDevice))
.then(() => assertRejectsWithNotFoundError(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 () => {
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 assertRejectsWithNotConfiguredError(
device.controlTransferOut(transferParams, data));
}).push(assertRejectsWithNotConfiguredError(
device.controlTransferOut(transferParams)));
});
}));
await device.close();
}, 'interface-scope OUT control transfers require configuration');
usb_test(async () => {
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 assertion = recipient === 'interface'
? assertRejectsWithNotClaimedError
: assertRejectsWithEndpointNotFoundError;
let transferParams = {
requestType: requestType,
recipient: recipient,
request: 0x42,
value: 0x1234,
index: index
};
return dataTypes.map(data => {
return assertion(device.controlTransferOut(transferParams, data));
}).push(assertion(device.controlTransferOut(transferParams)));
});
}));
await device.close();
}, 'interface-scope OUT control transfers an interface claim');
usb_test(() => {
return getFakeDevice().then(({ device, fakeDevice }) => {
return device.open()
.then(() => device.selectConfiguration(1))
.then(() => waitForDisconnect(fakeDevice))
.then(() => assertRejectsWithNotFoundError(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 () => {
let { device } = await getFakeDevice();
await device.open();
await device.selectConfiguration(1);
await device.claimInterface(0);
assertRejectsWithTypeError(device.controlTransferOut({
requestType: 'invalid',
recipient: 'device',
request: 0x42,
value: 0x1234,
index: 0x5678
}, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])));
assertRejectsWithTypeError(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 () => {
let { device } = await getFakeDevice();
await device.open();
await device.selectConfiguration(1);
await device.claimInterface(0);
assertRejectsWithTypeError(device.controlTransferOut({
requestType: 'vendor',
recipient: 'invalid',
request: 0x42,
value: 0x1234,
index: 0x5678
}, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])));
assertRejectsWithTypeError(device.controlTransferIn({
requestType: 'vendor',
recipient: 'invalid',
request: 0x42,
value: 0x1234,
index: 0x5678
}, 0));
}, 'control transfers with a invalid recipient type reject');
usb_test(async () => {
let { device } = await getFakeDevice();
await device.open();
await device.selectConfiguration(1);
await device.claimInterface(0);
assertRejectsWithNotFoundError(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])));
assertRejectsWithNotFoundError(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(() => {
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([
assertRejectsWithError(
device.controlTransferIn(interfaceRequest, 7),
'InvalidStateError'),
assertRejectsWithError(
device.controlTransferIn(endpointRequest, 7),
'NotFoundError'),
assertRejectsWithError(
device.controlTransferOut(interfaceRequest, data),
'InvalidStateError'),
assertRejectsWithError(
device.controlTransferOut(endpointRequest, data),
'NotFoundError'),
]))
.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(() => {
return getFakeDevice().then(({ device, fakeDevice }) => {
return device.open()
.then(() => device.selectConfiguration(1))
.then(() => device.claimInterface(0))
.then(() => waitForDisconnect(fakeDevice))
.then(() => assertRejectsWithNotFoundError(device.clearHalt('in', 1)));
});
}, 'clearHalt rejects when called on a disconnected device');
usb_test(() => {
return getFakeDevice().then(({ device }) => {
let data = new DataView(new ArrayBuffer(1024));
for (let i = 0; i < 1024; ++i)
data.setUint8(i, i & 0xff);
const rangeError = 'The specified endpoint number is out of range.';
return device.open()
.then(() => device.selectConfiguration(1))
.then(() => device.claimInterface(0))
.then(() => Promise.all([
assertRejectsWithEndpointNotFoundError(device.transferIn(2, 8)), // Unclaimed
assertRejectsWithEndpointNotFoundError(device.transferIn(3, 8)), // Non-existent
assertRejectsWithError(
device.transferIn(16, 8), 'IndexSizeError', rangeError),
assertRejectsWithEndpointNotFoundError(device.transferOut(2, data)), // Unclaimed
assertRejectsWithEndpointNotFoundError(device.transferOut(3, data)), // Non-existent
assertRejectsWithError(
device.transferOut(16, data), 'IndexSizeError', rangeError),
]));
});
}, '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(() => {
return getFakeDevice().then(({ device, fakeDevice }) => {
return device.open()
.then(() => device.selectConfiguration(1))
.then(() => device.claimInterface(1))
.then(() => waitForDisconnect(fakeDevice))
.then(() => assertRejectsWithNotFoundError(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(() => {
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(() => assertRejectsWithNotFoundError(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(() => {
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(() => assertRejectsWithNotFoundError(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(() => {
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(() => assertRejectsWithNotFoundError(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(() => {
return getFakeDevice().then(({ device, fakeDevice }) => {
return device.open()
.then(() => waitForDisconnect(fakeDevice))
.then(() => assertRejectsWithNotFoundError(device.reset()));
});
}, 'resetDevice rejects when called on a disconnected device');