diff options
Diffstat (limited to '')
-rw-r--r-- | dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/buffers/map_ArrayBuffer.spec.ts | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/buffers/map_ArrayBuffer.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/buffers/map_ArrayBuffer.spec.ts new file mode 100644 index 0000000000..fc0bfac39d --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/buffers/map_ArrayBuffer.spec.ts @@ -0,0 +1,89 @@ +export const description = ` +Tests for the behavior of ArrayBuffers returned by getMappedRange. + +TODO: Add tests that transfer to another thread instead of just using MessageChannel. +TODO: Add tests for any other Web APIs that can detach ArrayBuffers. +`; + +import { makeTestGroup } from '../../../../common/framework/test_group.js'; +import { timeout } from '../../../../common/util/timeout.js'; +import { GPUTest } from '../../../gpu_test.js'; +import { checkElementsEqual } from '../../../util/check_contents.js'; + +export const g = makeTestGroup(GPUTest); + +g.test('postMessage') + .desc( + `Using postMessage to send a getMappedRange-returned ArrayBuffer throws a TypeError + if it was included in the transfer list. Otherwise, it makes a copy. + Test combinations of transfer={false, true}, mapMode={read,write}.` + ) + .params(u => + u // + .combine('transfer', [false, true]) + .combine('mapMode', ['READ', 'WRITE'] as const) + ) + .fn(async t => { + const { transfer, mapMode } = t.params; + const kSize = 1024; + + // Populate initial data. + const initialData = new Uint32Array(new ArrayBuffer(kSize)); + for (let i = 0; i < initialData.length; ++i) { + initialData[i] = i; + } + + const buf = t.makeBufferWithContents( + initialData, + mapMode === 'WRITE' ? GPUBufferUsage.MAP_WRITE : GPUBufferUsage.MAP_READ + ); + + await buf.mapAsync(GPUMapMode[mapMode]); + const ab1 = buf.getMappedRange(); + t.expect(ab1.byteLength === kSize, 'ab1 should have the size of the buffer'); + + const mc = new MessageChannel(); + const ab2Promise = new Promise<ArrayBuffer>(resolve => { + mc.port2.onmessage = ev => { + if (transfer) { + t.fail( + `postMessage with ab1 in transfer list should not be received. Unexpected message: ${ev.data}` + ); + } else { + resolve(ev.data); + } + }; + }); + + if (transfer) { + t.shouldThrow('TypeError', () => mc.port1.postMessage(ab1, [ab1])); + // Wait to make sure the postMessage isn't received. + await new Promise(resolve => timeout(resolve, 100)); + } else { + mc.port1.postMessage(ab1); + } + t.expect(ab1.byteLength === kSize, 'after postMessage, ab1 should not be detached'); + + if (!transfer) { + const ab2 = await ab2Promise; + t.expect(ab2.byteLength === kSize, 'ab2 should be the same size'); + const ab2Data = new Uint32Array(ab2, 0, initialData.length); + // ab2 should have the same initial contents. + t.expectOK(checkElementsEqual(ab2Data, initialData)); + + // Mutations to ab2 should not be visible in ab1. + const ab1Data = new Uint32Array(ab1, 0, initialData.length); + const abs2NewData = initialData.slice().reverse(); + for (let i = 0; i < ab2Data.length; ++i) { + ab2Data[i] = abs2NewData[i]; + } + t.expectOK(checkElementsEqual(ab1Data, initialData)); + t.expectOK(checkElementsEqual(ab2Data, abs2NewData)); + } + + buf.unmap(); + t.expect(ab1.byteLength === 0, 'after unmap, ab1 should be detached'); + + // Transferring an already-detached ArrayBuffer is a DataCloneError. + t.shouldThrow('DataCloneError', () => mc.port1.postMessage(ab1, [ab1])); + }); |