chromium/third_party/blink/web_tests/external/wpt/webnn/conformance_tests/byob_readbuffer.https.any.js

// META: title=test WebNN API buffer operations
// META: global=window,dedicatedworker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
// META: script=../resources/utils_validation.js
// META: script=../resources/utils.js
// META: timeout=long

'use strict';

// Skip tests if WebNN is unimplemented.
promise_setup(async () => {
  assert_implements(navigator.ml, 'missing navigator.ml');
});

// https://www.w3.org/TR/webnn/#api-mlbuffer

const testContents = Uint32Array.from([0, 1, 2, 3, 4, 5, 6, 7]);

let mlContext;
let mlBuffer;
promise_setup(async () => {
  try {
    mlContext = await navigator.ml.createContext(contextOptions);
  } catch (e) {
    throw new AssertionError(
        `Unable to create context for ${variant} variant. ${e}`);
  }

  try {
    mlBuffer = await mlContext.createBuffer({
      dataType: 'int32',
      dimensions: [2, 4],
      usage: MLBufferUsage.WRITE_TO | MLBufferUsage.READ_FROM,
    });
  } catch (e) {
    throw new AssertionError(
        `Unable to create buffer for ${variant} variant. ${e}`);
  }

  mlContext.writeBuffer(mlBuffer, testContents);
});

promise_test(async (t) => {
  const arrayBuffer = new ArrayBuffer(testContents.byteLength - 4);

  await promise_rejects_js(
      t, TypeError, mlContext.readBuffer(mlBuffer, arrayBuffer));
}, `readBuffer() with an ArrayBuffer that is too small should reject`);

promise_test(async (t) => {
  const typedArray = new Uint32Array(testContents.length - 1);

  await promise_rejects_js(
      t, TypeError, mlContext.readBuffer(mlBuffer, typedArray));
}, `readBuffer() with a TypedArray that is too small should reject`);

promise_test(async (t) => {
  const arrayBuffer = new ArrayBuffer(testContents.byteLength);
  const typedArray = new Uint32Array(arrayBuffer);

  arrayBuffer.transfer();

  await promise_rejects_js(
      t, TypeError, mlContext.readBuffer(mlBuffer, arrayBuffer));

  await promise_rejects_js(
      t, TypeError, mlContext.readBuffer(mlBuffer, typedArray));
}, `readBuffer() with a detached ArrayBuffer should reject`);

promise_test(async (t) => {
  const arrayBuffer = new ArrayBuffer(testContents.byteLength);
  const typedArray = new Uint32Array(arrayBuffer);

  const checks = Promise.all([
    promise_rejects_js(
        t, TypeError, mlContext.readBuffer(mlBuffer, arrayBuffer)),
    promise_rejects_js(
        t, TypeError, mlContext.readBuffer(mlBuffer, typedArray)),
  ]);

  arrayBuffer.transfer();

  await checks;
}, `Detaching an ArrayBuffer while readBuffer() is in progress should reject`);

promise_test(async () => {
  const arrayBuffer = new ArrayBuffer(testContents.byteLength);

  await mlContext.readBuffer(mlBuffer, arrayBuffer);

  assert_array_equals(new Uint32Array(arrayBuffer), testContents);
}, `readBuffer() with an ArrayBuffer`);

promise_test(async () => {
  // Create a slightly larger ArrayBuffer and set up the TypedArray at an
  // offset to make sure the MLBuffer contents are written to the correct
  // offset.
  const arrayBuffer = new ArrayBuffer(testContents.byteLength + 4);
  const typedArray = new Uint32Array(arrayBuffer, 4);

  await mlContext.readBuffer(mlBuffer, typedArray);

  assert_array_equals(typedArray, testContents);
}, `readBuffer() with a TypedArray`);

promise_test(async () => {
  const arrayBuffer = new ArrayBuffer(testContents.byteLength * 2);

  await mlContext.readBuffer(mlBuffer, arrayBuffer);

  assert_array_equals(
      new Uint32Array(arrayBuffer).subarray(0, testContents.length),
      testContents);
  // The rest of the array should remain uninitialized.
  assert_array_equals(
      new Uint32Array(arrayBuffer)
          .subarray(testContents.length, testContents.length * 2),
      new Uint32Array(testContents.length));
}, `readBuffer() with a larger ArrayBuffer`);

promise_test(async () => {
  // Create a slightly larger ArrayBuffer and set up the TypedArray at an
  // offset to make sure the MLBuffer contents are written to the correct
  // offset.
  const arrayBuffer = new ArrayBuffer(testContents.byteLength * 2 + 4);
  const typedArray = new Uint32Array(arrayBuffer, 4);

  await mlContext.readBuffer(mlBuffer, typedArray);

  assert_array_equals(
      typedArray.subarray(0, testContents.length), testContents);
  // The rest of the array should remain uninitialized.
  assert_array_equals(
      typedArray.subarray(testContents.length, testContents.length * 2),
      new Uint32Array(testContents.length));
}, `readBuffer() with a larger TypedArray`);

promise_test(async (t) => {
  const buffer = await mlContext.createBuffer({
    dataType: 'int32',
    dimensions: [2, 2],
    usage: MLBufferUsage.READ_FROM,
  });
  const arrayBufferView = new Int32Array(2 * 2);
  const arrayBuffer = arrayBufferView.buffer;

  // Reading a destroyed MLBuffer should reject.
  buffer.destroy();

  await promise_rejects_dom(
      t, 'InvalidStateError', mlContext.readBuffer(buffer, arrayBuffer));
  await promise_rejects_dom(
      t, 'InvalidStateError', mlContext.readBuffer(buffer, arrayBufferView));
}, `readBuffer() rejects on a destroyed MLBuffer`);

promise_test(async (t) => {
  const buffer = await mlContext.createBuffer({
    dataType: 'int32',
    dimensions: [2, 2],
    usage: MLBufferUsage.READ_FROM,
  });
  const arrayBufferView = new Int32Array(2 * 2);
  const arrayBuffer = arrayBufferView.buffer;

  const checks = Promise.all([
    promise_rejects_dom(
        t, 'InvalidStateError', mlContext.readBuffer(buffer, arrayBuffer)),
    promise_rejects_dom(
        t, 'InvalidStateError', mlContext.readBuffer(buffer, arrayBufferView)),
  ]);

  buffer.destroy();

  await checks;
}, `readBuffer() rejects when the MLBuffer is destroyed`);