/**
* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
**/import { Fixture } from '../common/framework/fixture.js';import { getGPU } from '../common/util/navigator_gpu.js';import { assert, raceWithRejectOnTimeout } from '../common/util/util.js';

/**
 * A test class to help test error scopes and uncapturederror events.
 */
export class ErrorTest extends Fixture {
  _device = undefined;

  get device() {
    assert(this._device !== undefined);
    return this._device;
  }

  async init() {
    await super.init();
    const gpu = getGPU(this.rec);
    const adapter = await gpu.requestAdapter();
    assert(adapter !== null);

    // We need to max out the adapter limits related to texture dimensions to more reliably cause an
    // OOM error when asked for it, so set that on the device now.
    const device = await this.requestDeviceTracked(adapter, {
      requiredLimits: {
        maxTextureDimension2D: adapter.limits.maxTextureDimension2D,
        maxTextureArrayLayers: adapter.limits.maxTextureArrayLayers
      }
    });
    assert(device !== null);
    this._device = device;
  }

  /**
   * Generates an error of the given filter type. For now, the errors are generated by calling a
   * known code-path to cause the error. This can be updated in the future should there be a more
   * direct way to inject errors.
   */
  generateError(filter) {
    switch (filter) {
      case 'out-of-memory':
        this.trackForCleanup(
          this.device.createTexture({
            // One of the largest formats. With the base limits, the texture will be 256 GiB.
            format: 'rgba32float',
            usage: GPUTextureUsage.COPY_DST,
            size: [
            this.device.limits.maxTextureDimension2D,
            this.device.limits.maxTextureDimension2D,
            this.device.limits.maxTextureArrayLayers]

          })
        );
        break;
      case 'validation':
        // Generating a validation error by passing in an invalid usage when creating a buffer.
        this.trackForCleanup(
          this.device.createBuffer({
            size: 1024,
            usage: 0xffff // Invalid GPUBufferUsage
          })
        );
        break;
    }
    // MAINTENANCE_TODO: This is a workaround for Chromium not flushing. Remove when not needed.
    this.device.queue.submit([]);
  }

  /**
   * Checks whether the error is of the type expected given the filter.
   */
  isInstanceOfError(filter, error) {
    switch (filter) {
      case 'out-of-memory':
        return error instanceof GPUOutOfMemoryError;
      case 'validation':
        return error instanceof GPUValidationError;
      case 'internal':
        return error instanceof GPUInternalError;
    }
  }

  /**
   * Pop `count` error scopes, and assert they all return `null`. Chunks the
   * awaits so we only `Promise.all` 200 scopes at a time, instead of stalling
   * on a huge `Promise.all` all at once. This helps Chromium's "heartbeat"
   * mechanism know that the test is still running (and not hung).
   */
  async chunkedPopManyErrorScopes(count) {
    const promises = [];
    for (let i = 0; i < count; i++) {
      promises.push(this.device.popErrorScope());
      if (promises.length >= 200) {
        this.expect((await Promise.all(promises)).every((e) => e === null));
        promises.length = 0;
      }
    }
    this.expect((await Promise.all(promises)).every((e) => e === null));
  }

  /**
   * Expect an uncapturederror event to occur. Note: this MUST be awaited, because
   * otherwise it could erroneously pass by capturing an error from later in the test.
   */
  async expectUncapturedError(fn) {
    return this.immediateAsyncExpectation(() => {
      // MAINTENANCE_TODO: Make arbitrary timeout value a test runner variable
      const TIMEOUT_IN_MS = 1000;

      const promise = new Promise((resolve) => {
        const eventListener = (event) => {
          this.debug(`Got uncaptured error event with ${event.error}`);
          resolve(event);
        };

        this.device.addEventListener('uncapturederror', eventListener, { once: true });
      });

      fn();

      return raceWithRejectOnTimeout(
        promise,
        TIMEOUT_IN_MS,
        'Timeout occurred waiting for uncaptured error'
      );
    });
  }
}