export const description = `
Tests submit validation.

Note: destroyed buffer/texture/querySet are tested in destroyed/. (unless it gets moved here)
Note: buffer map state is tested in ./buffer_mapped.spec.ts.
`;

import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../../gpu_test.js';

interface CommandBufferOptions {
  device?: GPUDevice;
  valid?: boolean;
}

class F extends AllFeaturesMaxLimitsGPUTest {
  createCommandBuffer(options: CommandBufferOptions = {}): GPUCommandBuffer {
    const device = options.device ?? this.device;

    let cb!: GPUCommandBuffer;

    this.expectValidationError(() => {
      const encoder = device.createCommandEncoder();
      if (options.valid === false) {
        // Popping a debug group when none are pushed results in an invalid command buffer.
        encoder.popDebugGroup();
      }
      cb = encoder.finish();
    }, options.valid === false);

    return cb;
  }
}

export const g = makeTestGroup(F);

g.test('command_buffer,device_mismatch')
  .desc(
    `
    Tests submit cannot be called with command buffers created from another device
    Test with two command buffers to make sure all command buffers can be validated:
    - cb0 and cb1 from same device
    - cb0 and cb1 from different device
    `
  )
  .paramsSubcasesOnly([
    { cb0Mismatched: false, cb1Mismatched: false }, // control case
    { cb0Mismatched: true, cb1Mismatched: false },
    { cb0Mismatched: false, cb1Mismatched: true },
  ])
  .beforeAllSubcases(t => t.usesMismatchedDevice())
  .fn(t => {
    const { cb0Mismatched, cb1Mismatched } = t.params;
    const mismatched = cb0Mismatched || cb1Mismatched;

    const cb0 = t.createCommandBuffer({ device: cb0Mismatched ? t.mismatchedDevice : t.device });
    const cb1 = t.createCommandBuffer({ device: cb1Mismatched ? t.mismatchedDevice : t.device });

    t.expectValidationError(() => {
      t.device.queue.submit([cb0, cb1]);
    }, mismatched);
  });

g.test('command_buffer,duplicate_buffers')
  .desc(
    `
    Tests submit cannot be called with the same command buffer listed multiple times:
    `
  )
  .fn(t => {
    const cb = t.createCommandBuffer();

    t.expectValidationError(() => {
      t.device.queue.submit([cb, cb]);
    }, true);
  });

g.test('command_buffer,submit_invalidates')
  .desc(
    `
    Tests that calling submit invalidates the command buffers passed to it:
    `
  )
  .fn(t => {
    const cb = t.createCommandBuffer();

    // Initial submit of a valid command buffer should pass.
    t.device.queue.submit([cb]);

    // Subsequent submits of the same command buffer should fail.
    t.expectValidationError(() => {
      t.device.queue.submit([cb]);
    });
  });

g.test('command_buffer,invalid_submit_invalidates')
  .desc(
    `
    Tests that calling submit invalidates all command buffers passed to it, even
    if they're part of an invalid submit.
    `
  )
  .fn(t => {
    const cb1 = t.createCommandBuffer();
    const cb1_invalid = t.createCommandBuffer({ valid: false });

    // Submit should fail because on of the command buffers is invalid
    t.expectValidationError(() => {
      t.device.queue.submit([cb1, cb1_invalid]);
    });

    // Subsequent submits of the previously valid command buffer should fail.
    t.expectValidationError(() => {
      t.device.queue.submit([cb1]);
    });

    // The order of the invalid and valid command buffers in the submit array should not matter.
    const cb2 = t.createCommandBuffer();
    const cb2_invalid = t.createCommandBuffer({ valid: false });

    t.expectValidationError(() => {
      t.device.queue.submit([cb2_invalid, cb2]);
    });
    t.expectValidationError(() => {
      t.device.queue.submit([cb2]);
    });
  });
