blob: f6d80af85a89328a149a9276dfd3b3a0846201b6 [file] [log] [blame]
export const description = `
Tests for GPUDevice.onuncapturederror / addEventListener('uncapturederror')
`;
import { makeTestGroup } from '../../../common/framework/test_group.js';
import { raceWithRejectOnTimeout } from '../../../common/util/util.js';
import { kGeneratableErrorScopeFilters } from '../../capability_info.js';
import { ErrorTest } from '../../error_test.js';
export const g = makeTestGroup(ErrorTest);
g.test('iff_uncaptured')
.desc(
`{validation, out-of-memory} error should fire uncapturederror iff not captured by a scope.`
)
.params(u =>
u
.combine('useOnuncapturederror', [false, true])
.combine('errorType', kGeneratableErrorScopeFilters)
)
.fn(async t => {
const { useOnuncapturederror, errorType } = t.params;
const uncapturedErrorEvent = await t.expectUncapturedError(() => {
t.generateError(errorType);
}, useOnuncapturederror);
t.expect(t.isInstanceOfError(errorType, uncapturedErrorEvent.error));
});
g.test('only_original_device_is_event_target')
.desc(
`Original GPUDevice objects are EventTargets and have onuncapturederror, but
deserialized GPUDevices do not.`
)
.unimplemented();
g.test('uncapturederror_from_non_originating_thread')
.desc(
`Uncaptured errors on any thread should always propagate to the original GPUDevice object
(since deserialized ones don't have EventTarget/onuncapturederror).`
)
.unimplemented();
g.test('onuncapturederror_order_wrt_addEventListener')
.desc(
`
Test that onuncapturederror and addEventListener work in the correct order.
The spec says setting onuncapturederror adds a listener via addEventListener that
calls the callback. Changing onuncapturederror changes the callback to the existing
listener. Setting onuncapturederror to null removes the listener.
`
)
.fn(async t => {
const callOrder: string[] = [];
const makeListener = (id: string) => {
let resolve: () => void;
return {
getPromise() {
return new Promise<void>(r => {
resolve = r;
});
},
listener: (e: Event) => {
e.preventDefault();
t.debug(`listener ${id} called`);
callOrder.push(id);
resolve();
},
};
};
const listenerA = makeListener('a');
const listenerB = makeListener('b');
const callbackC = makeListener('c');
const callbackD = makeListener('d');
const callbackE = makeListener('e');
try {
t.debug('test they are called in the order added');
{
const promises = [listenerA.getPromise(), listenerB.getPromise(), callbackC.getPromise()];
t.device.addEventListener('uncapturederror', listenerA.listener);
t.device.onuncapturederror = callbackC.listener;
t.device.addEventListener('uncapturederror', listenerB.listener);
t.generateError('validation');
await raceWithRejectOnTimeout(Promise.all(promises), 500, 'timeout1');
const order = callOrder.join(',');
t.expect(order === 'a,c,b', `'${order}' === 'a,c,b'`);
callOrder.length = 0;
}
t.debug('test changing onuncapturederror does not change the order');
{
const promises = [listenerA.getPromise(), listenerB.getPromise(), callbackD.getPromise()];
t.device.onuncapturederror = callbackD.listener;
t.generateError('validation');
await raceWithRejectOnTimeout(Promise.all(promises), 500, 'timeout2');
const order = callOrder.join(',');
t.expect(order === 'a,d,b', `'${order}' === 'a,d,b'`);
callOrder.length = 0;
}
t.debug('test clearing onuncapturederror then setting it does change the order');
{
const promises = [listenerA.getPromise(), listenerB.getPromise(), callbackE.getPromise()];
t.device.onuncapturederror = null;
t.device.onuncapturederror = callbackE.listener;
t.generateError('validation');
await raceWithRejectOnTimeout(Promise.all(promises), 500, 'timeout3');
const order = callOrder.join(',');
t.expect(order === 'a,b,e', `'${order}' === 'a,b,e'`);
callOrder.length = 0;
}
} finally {
t.device.onuncapturederror = null;
t.device.removeEventListener('uncapturederror', listenerA.listener);
t.device.removeEventListener('uncapturederror', listenerB.listener);
}
});