// META: title=test that input tensors are not modified during a call to dispatch()
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu
// META: script=../resources/utils.js
// META: timeout=long

'use strict';

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

let mlContext;

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

promise_test(async () => {
  const builder = new MLGraphBuilder(mlContext);
  const inputOperand =
      builder.input('input', {dataType: 'float32', shape: [4]});
  const hardSwishOperand = builder.hardSwish(inputOperand);
  // Add some other operator for the output tensor to bind to; otherwise there
  // is no reason to implement hardSwish "in-place".
  const outputOperand = builder.identity(hardSwishOperand);

  const [inputTensor, outputTensor, mlGraph] = await Promise.all([
    mlContext.createTensor({
      dataType: 'float32',
      shape: [4],
      readable: true,
      writable: true,
    }),
    mlContext.createTensor({dataType: 'float32', shape: [4], readable: true}),
    builder.build({'output': outputOperand})
  ]);

  const inputData = Float32Array.from([-4, -1, 1, 4]);
  mlContext.writeTensor(inputTensor, inputData);

  mlContext.dispatch(mlGraph, {'input': inputTensor}, {'output': outputTensor});

  // Wait for graph execution to complete.
  await mlContext.readTensor(outputTensor);

  // The input tensor should not be modified.
  assert_array_equals(
      new Float32Array(await mlContext.readTensor(inputTensor)), inputData);
}, 'input tensor is not modified: hardSwish');

promise_test(async () => {
  const builder = new MLGraphBuilder(mlContext);
  const inputOperand =
      builder.input('input', {dataType: 'float32', shape: [4]});
  const constantOperand = builder.constant(
      {dataType: 'float32', shape: [4]}, Float32Array.from([-2, 0, 3, 4]));
  const mulOperand = builder.mul(inputOperand, constantOperand);
  // Add some other operator for the output tensor to bind to; otherwise there
  // is no reason to implement mul "in-place".
  const outputOperand = builder.add(mulOperand, constantOperand);

  const [inputTensor, outputTensor, mlGraph] = await Promise.all([
    mlContext.createTensor({
      dataType: 'float32',
      shape: [4],
      readable: true,
      writable: true,
    }),
    mlContext.createTensor({dataType: 'float32', shape: [4], readable: true}),
    builder.build({'output': outputOperand})
  ]);

  const inputData = Float32Array.from([1, 2, 3, 4]);
  mlContext.writeTensor(inputTensor, inputData);
  mlContext.dispatch(mlGraph, {'input': inputTensor}, {'output': outputTensor});

  // Wait for graph execution to complete.
  await mlContext.readTensor(outputTensor);

  // The input tensor should not be modified.
  assert_array_equals(
      new Float32Array(await mlContext.readTensor(inputTensor)), inputData);
}, 'input tensor is not modified: mul');
