| 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); |
| } |
| }); |