// META: timeout=long
// META: title=validation tests for WebNN API MLContext::destroy()
// META: global=window,worker
// META: variant=?cpu
// META: variant=?gpu
// META: variant=?npu

'use strict';

let contextOptions;

promise_setup(async () => {
  if (navigator.ml === undefined) {
    return;
  }
  contextOptions = {deviceType: location.search.substring(1)};
}, {explicit_timeout: true});

promise_test(async t => {
  const context = await navigator.ml.createContext(contextOptions);
  context.destroy();
  await context.lost;
}, 'Context will be lost by destroyed.');

promise_test(async t => {
  const context = await navigator.ml.createContext(contextOptions);
  context.destroy();
  context.destroy();
  await context.lost;
}, 'Context can be destroyed twice.');

promise_test(async t => {
  const context = await navigator.ml.createContext(contextOptions);
  const builder = new MLGraphBuilder(context);
  context.destroy();
  assert_throws_dom('InvalidStateError', () => {
    const operandType = {dataType: 'float32', shape: [1]};
    builder.input('input', operandType);
  });
}, 'Destroyed context can not build operator.');

promise_test(async t => {
  const context = await navigator.ml.createContext(contextOptions);
  context.destroy();
  assert_throws_dom('InvalidStateError', () => {
    new MLGraphBuilder(context);
  });
}, 'Destroyed context can not create graph builder.');

promise_test(async t => {
  const context = await navigator.ml.createContext(contextOptions);
  const builder = new MLGraphBuilder(context);
  const operandType = {dataType: 'float32', shape: [1]};
  const input_operand = builder.input('input', operandType);
  const const_operand = builder.constant(operandType, Float32Array.from([2]));
  const output_operand = builder.mul(input_operand, const_operand);

  context.destroy();
  promise_rejects_dom(
      t, 'InvalidStateError', builder.build({'output': output_operand}));
}, 'Destroyed context can not build graph.');

promise_test(async t => {
  const context = await navigator.ml.createContext(contextOptions);
  const builder = new MLGraphBuilder(context);
  const operandType = {dataType: 'float32', shape: [1]};
  const lhsOperand = builder.input('lhs', operandType);
  const rhsOperand = builder.input('rhs', operandType);
  const graph =
      await builder.build({'output': builder.sub(lhsOperand, rhsOperand)});

  const lhsTensor = await context.createTensor(operandType);
  const rhsTensor = await context.createTensor(operandType);

  const dispatchOutputs = {'output': await context.createTensor(operandType)};
  context.destroy();
  assert_throws_dom('InvalidStateError', () => {
    context.dispatch(
        graph, {
          'lhs': lhsTensor,
          'rhs': rhsTensor,
        },
        dispatchOutputs);
  });
}, 'Destroyed context can not dispatch.');

promise_test(async t => {
  const context = await navigator.ml.createContext(contextOptions);
  const builder = new MLGraphBuilder(context);
  const operandType = {dataType: 'float32', shape: [1]};
  const lhsOperand = builder.input('lhs', operandType);
  const rhsOperand = builder.input('rhs', operandType);
  const graph =
      await builder.build({'output': builder.sub(lhsOperand, rhsOperand)});

  const lhsTensor = await context.createTensor(operandType);
  const rhsTensor = await context.createTensor(operandType);

  const dispatchOutputs = {'output': await context.createTensor(operandType)};
  context.dispatch(
      graph, {
        'lhs': lhsTensor,
        'rhs': rhsTensor,
      },
      dispatchOutputs);
  context.destroy();
}, 'Executing dispatch() before context destroyed is OK.');

promise_test(async t => {
  const context = await navigator.ml.createContext(contextOptions);
  context.destroy();
  promise_rejects_dom(
      t, 'InvalidStateError',
      context.createTensor({dataType: 'float32', shape: [1]}));
}, 'Destroyed context can not create tensor.');

promise_test(async t => {
  const context = await navigator.ml.createContext(contextOptions);
  const tensor = await context.createTensor({
    dataType: 'float32',
    shape: [1],
    readable: true,
  });
  context.destroy();
  promise_rejects_dom(t, 'InvalidStateError', context.readTensor(tensor));
}, 'Destroyed context can not read tensor.');

promise_test(async t => {
  const context = await navigator.ml.createContext(contextOptions);
  const tensor = await context.createTensor({
    dataType: 'float32',
    shape: [1],
    readable: true,
  });
  let promise = context.readTensor(tensor);
  context.destroy();
  promise_rejects_dom(t, 'InvalidStateError', promise);
}, 'Pending promise of readtensor() will be rejected immediately when context is destroyed.');

promise_test(async t => {
  const context = await navigator.ml.createContext(contextOptions);
  // Destroying another context doesn't impact the first context.
  const another_context = await navigator.ml.createContext(contextOptions);
  another_context.destroy();
  const tensor = await context.createTensor({
    dataType: 'float32',
    shape: [1],
    writable: true,
  });
  let arrayBuffer = new ArrayBuffer(4);
  context.destroy();
  assert_throws_dom('InvalidStateError', () => {
    context.writeTensor(tensor, new Uint8Array(arrayBuffer));
  });
}, 'Destroyed context can not write tensor.');
