summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/buffers/map_ArrayBuffer.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/buffers/map_ArrayBuffer.spec.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/operation/buffers/map_ArrayBuffer.spec.ts89
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]));
+ });