Files
tubestation/testing/web-platform/tests/webusb/usbDevice.https.any.js
Matt Reynolds 01a858eeb7 Bug 1859418 [wpt PR 42575] - usb: Validate isochronous transfer packet lengths, a=testonly
Automatic update from web-platform-tests
usb: Validate isochronous transfer packet lengths

USBDevice.isochronousTransferIn and
USBDevice.isochronousTransferOut take a parameter containing
a list of packet lengths. This CL adds validation that the
total packet length does not exceed the maximum buffer size.
For isochronousTransferOut, it also checks that the total
length of all packets in bytes is equal to the size of the
data buffer.

Passing invalid packet lengths causes the promise to be
rejected with a DataError.

Bug: 1492381, 1492384
Change-Id: Id9ae16c7e6f1c417e0fc4f21d53e9de11560b2b7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4944690
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Commit-Queue: Matt Reynolds <mattreynolds@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1212916}

--

wpt-commits: af9aadf4a48eef9313d8b9e30e942505986b3579
wpt-pr: 42575
2023-10-31 13:59:42 +00:00

1307 lines
45 KiB
JavaScript

// 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');