blob: 3694720d923b6ae7a94082a15712fd2edd261c06 [file] [log] [blame]
// This file can run with from the dawn folder, after building dawn node, with
// node src/dawn/node/test.mjs
// This assumes you have a symbolic link from out/active to your build folder.
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
import { describe, it, before, after } from 'node:test';
const require = createRequire(import.meta.url);
const __dirname = dirname(fileURLToPath(import.meta.url));
const dawnNodePath = join(__dirname, '..', '..', '..', 'out', 'active', 'dawn.node');
const { create, globals } = require(dawnNodePath);
// Save these before they might get replaced by the following assignment
const globalsBeforeGlobalAssignment = {
DOMException,
EventTarget,
Error,
Event,
};
Object.assign(globalThis, globals);
const assert = {
ok(cond, msg) {
if (typeof cond === 'function') {
assert.ok(cond(), msg ?? cond.toString());
} else if (!cond) {
throw new Error(msg ?? '');
}
}
};
describe('tests', async () => {
let device;
let navigator;
let adapter
before(async () => {
navigator = { gpu: create([]) };
adapter = await navigator.gpu.requestAdapter();
if (!adapter) {
return;
}
device = await adapter.requestDevice({label: "test-device"});
});
after(async () => {
device.destroy();
device = undefined;
navigator = undefined;
adapter = undefined;
await new Promise(r => setTimeout(r, 1000));
})
await describe('global tests', async () => {
// Check that these globals were not replaced when we added dawn.node's globals
assert.ok(() => globalsBeforeGlobalAssignment.EventTarget === EventTarget);
assert.ok(() => globalsBeforeGlobalAssignment.Event === Event);
assert.ok(() => globalsBeforeGlobalAssignment.Error === Error);
assert.ok(() => globalsBeforeGlobalAssignment.DOMException === DOMException);
});
await describe('device tests', async () => {
await it('basic device tests', () => {
assert.ok(device.adapterInfo.description !== undefined);
assert.ok(device.label === 'test-device');
assert.ok(() => device instanceof EventTarget);
});
await it('can not construct device', () => {
// check we can't make things we're not supposed to be able to.
let err;
try {
const gd = new GPUDevice();
assert(gd || !gd, 'should never get here'); // use gd
} catch (e) {
err = e;
}
assert.ok(() => err instanceof TypeError);
});
});
await describe('popErrorScope ', async () => {
await it('popErrorScope returns error', async () => {
device.pushErrorScope('validation');
device.createTexture({
format: 'rgba8unorm',
usage: GPUTextureUsage.TEXTURE_BINDING,
size: [device.limits.maxTextureDimension2D + 1],
});
const err = await device.popErrorScope();
assert.ok(() => err instanceof GPUValidationError);
});
await it('popErrorScope twice returns error', async () => {
device.pushErrorScope('out-of-memory');
device.pushErrorScope('internal');
device.pushErrorScope('validation');
device.pushErrorScope('validation');
device.createTexture({
format: 'rgba8unorm',
usage: GPUTextureUsage.TEXTURE_BINDING,
size: [device.limits.maxTextureDimension2D + 1],
});
const err1 = await device.popErrorScope();
assert.ok(() => err1 instanceof GPUValidationError);
const err2 = await device.popErrorScope();
assert.ok(() => !err2);
const err3 = await device.popErrorScope();
assert.ok(() => !err3);
const err4 = await device.popErrorScope();
assert.ok(() => !err4);
});
await it('works when popErrorScope on empty stack', async () => {
try {
await device.popErrorScope();
assert.ok(false, 'should not get here');
} catch (e) {
assert.ok(e.name === 'OperationError');
}
})
});
await describe('error tests', { skip: false }, async () => {
await it('basic error tests', () => {
const e = new GPUValidationError('test1');
assert.ok(() => e instanceof GPUError);
assert.ok(() => e.message === 'test1');
});
});
await describe('GPUPipelineError tests', async () => {
await it('can create a GPUPipelineError', () => {
const e = new GPUPipelineError("msg1", { reason: 'validation' });
assert.ok(() => e instanceof GPUPipelineError);
assert.ok(() => e instanceof DOMException);
assert.ok(() => e.name === 'GPUPipelineError');
assert.ok(() => e.message === 'msg1')
assert.ok(() => e.reason === 'validation')
});
await it('generates GPUPipelineError', { skip: false }, async () => {
const vertexModule = device.createShaderModule({
code: `
@group(0) @binding(0) var<uniform> myUniform : vec4f;
@vertex fn vs() -> @builtin(position) vec4f {
return myUniform;
}
`,
});
const fragmentModule = device.createShaderModule({
code: `
@group(0) @binding(0) var tex: texture_2d<f32>;
@fragment fn fs() -> @location(0) vec4f {
return textureLoad(tex, vec2u(0), 0);
}
`,
});
const promise = device.createRenderPipelineAsync({
layout: 'auto',
vertex: { module: vertexModule },
fragment: { module: fragmentModule, targets: [ { format: 'rgba8unorm' } ]},
});
try {
const pipeline = await promise;
assert(false, 'should not get here');
} catch (e) {
//console.log(e);
//console.log('name:', e.name);
//console.log('message:', e.message);
//console.log('reason:', e.reason);
assert.ok(() => e instanceof GPUPipelineError);
assert.ok(() => e instanceof DOMException);
assert.ok(() => e.reason === 'validation');
}
});
});
await describe('event tests', { skip: false }, async () => {
await it('can pass Event through device', async () => {
const e = await new Promise(resolve => {
device.addEventListener('uncapturedFoo', resolve);
device.dispatchEvent(new Event('uncapturedFoo', { error: { message: 'yo (1)!'} } ));
});
assert.ok(!!e, 'Event worked');
});
await it('can pass CustomEvent through device', async () => {
const e = await new Promise(resolve => {
device.addEventListener('uncapturedEvent', resolve, { once: true });
device.dispatchEvent(new CustomEvent('uncapturedEvent', { detail: {error: { message: 'yo!'} } }));
});
assert.ok(e?.detail.error.message === 'yo!', 'CustomEvent worked');
});
await it('can pass extended Event through device', async () => {
class MyEvent extends Event {
constructor(type, msg) {
super(type);
this.message = msg;
}
}
const target = new EventTarget();
const e = await new Promise(resolve => {
target.addEventListener('custom', resolve, { once: true });
target.dispatchEvent(new MyEvent('custom', 'hello'));
});
assert.ok(() => e.message === 'hello');
});
await it('can pass dispatch user create GPUUncapturedErrorEvent', async () => {
const gEvent = new GPUUncapturedErrorEvent(
'uncapturederror',
{ error: new GPUValidationError('test2') }
);
assert.ok(() => gEvent instanceof Event);
assert.ok(() => gEvent.error.message === 'test2');
const e = await new Promise(resolve => {
device.addEventListener('uncapturederror', resolve, { once: true } );
device.dispatchEvent(gEvent);
});
assert.ok(e?.error.message === 'test2');
});
await it('receives uncaptured events', async () => {
const e = await new Promise(resolve => {
device.addEventListener('uncapturederror', (e) => {
e.preventDefault();
resolve(e);
}, { once: true, passive: false });
const texture = device.createTexture({
format: 'rgba8unorm',
usage: GPUTextureUsage.TEXTURE_BINDING,
size: [device.limits.maxTextureDimension2D + 1, 1],
});
texture.destroy();
});
assert.ok(() => e?.error.message.includes('maxTextureDimension2D'));
});
});
});