summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/mozilla/tests/webgpu/common
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /testing/web-platform/mozilla/tests/webgpu/common
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/mozilla/tests/webgpu/common')
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/framework/data_cache.js89
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/framework/fixture.js310
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/framework/params_builder.js213
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/framework/resources.js111
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/framework/test_config.js10
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/framework/test_group.js3
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/file_loader.js96
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/file_loader.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/logging/log_message.js45
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/logging/log_message.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/logging/logger.js31
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/logging/logger.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/logging/result.js4
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/logging/result.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/logging/test_case_recorder.js159
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/logging/test_case_recorder.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/params_utils.js125
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/params_utils.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/compare.js95
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/compare.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/encode_selectively.js24
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/encode_selectively.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/json_param_value.js84
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/json_param_value.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/parseQuery.js156
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/parseQuery.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/query.js263
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/query.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/separators.js15
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/separators.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/stringify_params.js45
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/stringify_params.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/validQueryPart.js4
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/query/validQueryPart.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/stack.js83
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/stack.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/test_group.js647
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/test_group.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/test_suite_listing.js4
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/test_suite_listing.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/tree.js576
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/tree.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/util.js11
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/util.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/internal/version.js3
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/options.js18
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/sys.js37
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/test_worker-worker.js32
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/test_worker.js37
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/runtime/wpt.js73
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/collect_garbage.js59
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/collect_garbage.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/colors.js128
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/colors.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/data_tables.js40
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/data_tables.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/navigator_gpu.js75
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/navigator_gpu.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/preprocessor.js150
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/preprocessor.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/timeout.js8
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/timeout.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/types.js60
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/types.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/util.js304
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/util.js.map1
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/wpt_reftest_wait.js25
-rw-r--r--testing/web-platform/mozilla/tests/webgpu/common/util/wpt_reftest_wait.js.map1
68 files changed, 4280 insertions, 0 deletions
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/framework/data_cache.js b/testing/web-platform/mozilla/tests/webgpu/common/framework/data_cache.js
new file mode 100644
index 0000000000..e426ffbbc9
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/framework/data_cache.js
@@ -0,0 +1,89 @@
+/**
+ * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+ **/
+
+/** DataCache is an interface to a data store used to hold cached data */
+export class DataCache {
+ /** setDataStore() sets the backing data store used by the data cache */
+ setStore(dataStore) {
+ this.dataStore = dataStore;
+ }
+
+ /** setDebugLogger() sets the verbose logger */
+ setDebugLogger(logger) {
+ this.debugLogger = logger;
+ }
+
+ /**
+ * fetch() retrieves cacheable data from the data cache, first checking the
+ * in-memory cache, then the data store (if specified), then resorting to
+ * building the data and storing it in the cache.
+ */
+ async fetch(cacheable) {
+ // First check the in-memory cache
+ let data = this.cache.get(cacheable.path);
+ if (data !== undefined) {
+ this.log('in-memory cache hit');
+ return Promise.resolve(data);
+ }
+ this.log('in-memory cache miss');
+ // In in-memory cache miss.
+ // Next, try the data store.
+ if (this.dataStore !== null && !this.unavailableFiles.has(cacheable.path)) {
+ let serialized;
+ try {
+ serialized = await this.dataStore.load(cacheable.path);
+ this.log('loaded serialized');
+ } catch (err) {
+ // not found in data store
+ this.log(`failed to load (${cacheable.path}): ${err}`);
+ this.unavailableFiles.add(cacheable.path);
+ }
+ if (serialized !== undefined) {
+ this.log(`deserializing`);
+ data = cacheable.deserialize(serialized);
+ this.cache.set(cacheable.path, data);
+ return data;
+ }
+ }
+ // Not found anywhere. Build the data, and cache for future lookup.
+ this.log(`cache: building (${cacheable.path})`);
+ data = await cacheable.build();
+ this.cache.set(cacheable.path, data);
+ return data;
+ }
+
+ log(msg) {
+ if (this.debugLogger !== null) {
+ this.debugLogger(`DataCache: ${msg}`);
+ }
+ }
+
+ cache = new Map();
+ unavailableFiles = new Set();
+ dataStore = null;
+ debugLogger = null;
+}
+
+/** The data cache */
+export const dataCache = new DataCache();
+
+/** true if the current process is building the cache */
+let isBuildingDataCache = false;
+
+/** @returns true if the data cache is currently being built */
+export function getIsBuildingDataCache() {
+ return isBuildingDataCache;
+}
+
+/** Sets whether the data cache is currently being built */
+export function setIsBuildingDataCache(value = true) {
+ isBuildingDataCache = value;
+}
+
+/**
+ * Cacheable is the interface to something that can be stored into the
+ * DataCache.
+ * The 'npm run gen_cache' tool will look for module-scope variables of this
+ * interface, with the name `d`.
+ */
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/framework/fixture.js b/testing/web-platform/mozilla/tests/webgpu/common/framework/fixture.js
new file mode 100644
index 0000000000..1611e9a742
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/framework/fixture.js
@@ -0,0 +1,310 @@
+/**
+ * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+ **/ import { assert, unreachable } from '../util/util.js';
+
+export class SkipTestCase extends Error {}
+export class UnexpectedPassError extends Error {}
+
+export { TestCaseRecorder } from '../internal/logging/test_case_recorder.js';
+
+/** The fully-general type for params passed to a test function invocation. */
+
+export class SubcaseBatchState {
+ constructor(params) {
+ this._params = params;
+ }
+
+ /**
+ * Returns the case parameters for this test fixture shared state. Subcase params
+ * are not included.
+ */
+ get params() {
+ return this._params;
+ }
+
+ /**
+ * Runs before the `.before()` function.
+ * @internal MAINTENANCE_TODO: Make this not visible to test code?
+ */
+ async init() {}
+ /**
+ * Runs between the `.before()` function and the subcases.
+ * @internal MAINTENANCE_TODO: Make this not visible to test code?
+ */
+ async postInit() {}
+ /**
+ * Runs after all subcases finish.
+ * @internal MAINTENANCE_TODO: Make this not visible to test code?
+ */
+ async finalize() {}
+}
+
+/**
+ * A Fixture is a class used to instantiate each test sub/case at run time.
+ * A new instance of the Fixture is created for every single test subcase
+ * (i.e. every time the test function is run).
+ */
+export class Fixture {
+ /**
+ * Interface for recording logs and test status.
+ *
+ * @internal
+ */
+
+ eventualExpectations = [];
+ numOutstandingAsyncExpectations = 0;
+ objectsToCleanUp = [];
+
+ static MakeSharedState(params) {
+ return new SubcaseBatchState(params);
+ }
+
+ /** @internal */
+ constructor(sharedState, rec, params) {
+ this._sharedState = sharedState;
+ this.rec = rec;
+ this._params = params;
+ }
+
+ /**
+ * Returns the (case+subcase) parameters for this test function invocation.
+ */
+ get params() {
+ return this._params;
+ }
+
+ /**
+ * Gets the test fixture's shared state. This object is shared between subcases
+ * within the same testcase.
+ */
+ get sharedState() {
+ return this._sharedState;
+ }
+
+ /**
+ * Override this to do additional pre-test-function work in a derived fixture.
+ * This has to be a member function instead of an async `createFixture` function, because
+ * we need to be able to ergonomically override it in subclasses.
+ *
+ * @internal MAINTENANCE_TODO: Make this not visible to test code?
+ */
+ async init() {}
+
+ /**
+ * Override this to do additional post-test-function work in a derived fixture.
+ *
+ * Called even if init was unsuccessful.
+ *
+ * @internal MAINTENANCE_TODO: Make this not visible to test code?
+ */
+ async finalize() {
+ assert(
+ this.numOutstandingAsyncExpectations === 0,
+ 'there were outstanding immediateAsyncExpectations (e.g. expectUncapturedError) at the end of the test'
+ );
+
+ // Loop to exhaust the eventualExpectations in case they chain off each other.
+ while (this.eventualExpectations.length) {
+ const p = this.eventualExpectations.shift();
+ try {
+ await p;
+ } catch (ex) {
+ this.rec.threw(ex);
+ }
+ }
+
+ // And clean up any objects now that they're done being used.
+ for (const o of this.objectsToCleanUp) {
+ if ('getExtension' in o) {
+ const WEBGL_lose_context = o.getExtension('WEBGL_lose_context');
+ if (WEBGL_lose_context) WEBGL_lose_context.loseContext();
+ } else if ('destroy' in o) {
+ o.destroy();
+ } else {
+ o.close();
+ }
+ }
+ }
+
+ /**
+ * Tracks an object to be cleaned up after the test finishes.
+ *
+ * MAINTENANCE_TODO: Use this in more places. (Will be easier once .destroy() is allowed on
+ * invalid objects.)
+ */
+ trackForCleanup(o) {
+ this.objectsToCleanUp.push(o);
+ return o;
+ }
+
+ /** Tracks an object, if it's destroyable, to be cleaned up after the test finishes. */
+ tryTrackForCleanup(o) {
+ if (typeof o === 'object' && o !== null) {
+ if (
+ 'destroy' in o ||
+ 'close' in o ||
+ o instanceof WebGLRenderingContext ||
+ o instanceof WebGL2RenderingContext
+ ) {
+ this.objectsToCleanUp.push(o);
+ }
+ }
+ return o;
+ }
+
+ /** Log a debug message. */
+ debug(msg) {
+ this.rec.debug(new Error(msg));
+ }
+
+ /** Throws an exception marking the subcase as skipped. */
+ skip(msg) {
+ throw new SkipTestCase(msg);
+ }
+
+ /** Log a warning and increase the result status to "Warn". */
+ warn(msg) {
+ this.rec.warn(new Error(msg));
+ }
+
+ /** Log an error and increase the result status to "ExpectFailed". */
+ fail(msg) {
+ this.rec.expectationFailed(new Error(msg));
+ }
+
+ /**
+ * Wraps an async function. Tracks its status to fail if the test tries to report a test status
+ * before the async work has finished.
+ */
+ async immediateAsyncExpectation(fn) {
+ this.numOutstandingAsyncExpectations++;
+ const ret = await fn();
+ this.numOutstandingAsyncExpectations--;
+ return ret;
+ }
+
+ /**
+ * Wraps an async function, passing it an `Error` object recording the original stack trace.
+ * The async work will be implicitly waited upon before reporting a test status.
+ */
+ eventualAsyncExpectation(fn) {
+ const promise = fn(new Error());
+ this.eventualExpectations.push(promise);
+ }
+
+ expectErrorValue(expectedError, ex, niceStack) {
+ if (!(ex instanceof Error)) {
+ niceStack.message = `THREW non-error value, of type ${typeof ex}: ${ex}`;
+ this.rec.expectationFailed(niceStack);
+ return;
+ }
+ const actualName = ex.name;
+ if (expectedError !== true && actualName !== expectedError) {
+ niceStack.message = `THREW ${actualName}, instead of ${expectedError}: ${ex}`;
+ this.rec.expectationFailed(niceStack);
+ } else {
+ niceStack.message = `OK: threw ${actualName}: ${ex.message}`;
+ this.rec.debug(niceStack);
+ }
+ }
+
+ /** Expect that the provided promise resolves (fulfills). */
+ shouldResolve(p, msg) {
+ this.eventualAsyncExpectation(async niceStack => {
+ const m = msg ? ': ' + msg : '';
+ try {
+ await p;
+ niceStack.message = 'resolved as expected' + m;
+ } catch (ex) {
+ niceStack.message = `REJECTED${m}`;
+ if (ex instanceof Error) {
+ niceStack.message += '\n' + ex.message;
+ }
+ this.rec.expectationFailed(niceStack);
+ }
+ });
+ }
+
+ /** Expect that the provided promise rejects, with the provided exception name. */
+ shouldReject(expectedName, p, msg) {
+ this.eventualAsyncExpectation(async niceStack => {
+ const m = msg ? ': ' + msg : '';
+ try {
+ await p;
+ niceStack.message = 'DID NOT REJECT' + m;
+ this.rec.expectationFailed(niceStack);
+ } catch (ex) {
+ niceStack.message = 'rejected as expected' + m;
+ this.expectErrorValue(expectedName, ex, niceStack);
+ }
+ });
+ }
+
+ /**
+ * Expect that the provided function throws.
+ * If an `expectedName` is provided, expect that the throw exception has that name.
+ */
+ shouldThrow(expectedError, fn, msg) {
+ const m = msg ? ': ' + msg : '';
+ try {
+ fn();
+ if (expectedError === false) {
+ this.rec.debug(new Error('did not throw, as expected' + m));
+ } else {
+ this.rec.expectationFailed(new Error('unexpectedly did not throw' + m));
+ }
+ } catch (ex) {
+ if (expectedError === false) {
+ this.rec.expectationFailed(new Error('threw unexpectedly' + m));
+ } else {
+ this.expectErrorValue(expectedError, ex, new Error(m));
+ }
+ }
+ }
+
+ /** Expect that a condition is true. */
+ expect(cond, msg) {
+ if (cond) {
+ const m = msg ? ': ' + msg : '';
+ this.rec.debug(new Error('expect OK' + m));
+ } else {
+ this.rec.expectationFailed(new Error(msg));
+ }
+ return cond;
+ }
+
+ /**
+ * If the argument is an `Error`, fail (or warn). If it's `undefined`, no-op.
+ * If the argument is an array, apply the above behavior on each of elements.
+ */
+ expectOK(error, { mode = 'fail', niceStack } = {}) {
+ const handleError = error => {
+ if (error instanceof Error) {
+ if (niceStack) {
+ error.stack = niceStack.stack;
+ }
+ if (mode === 'fail') {
+ this.rec.expectationFailed(error);
+ } else if (mode === 'warn') {
+ this.rec.warn(error);
+ } else {
+ unreachable();
+ }
+ }
+ };
+
+ if (Array.isArray(error)) {
+ for (const e of error) {
+ handleError(e);
+ }
+ } else {
+ handleError(error);
+ }
+ }
+
+ eventualExpectOK(error, { mode = 'fail' } = {}) {
+ this.eventualAsyncExpectation(async niceStack => {
+ this.expectOK(await error, { mode, niceStack });
+ });
+ }
+}
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/framework/params_builder.js b/testing/web-platform/mozilla/tests/webgpu/common/framework/params_builder.js
new file mode 100644
index 0000000000..787911f964
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/framework/params_builder.js
@@ -0,0 +1,213 @@
+/**
+ * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+ **/ import { mergeParams } from '../internal/params_utils.js';
+import { stringifyPublicParams } from '../internal/query/stringify_params.js';
+import { assert, mapLazy } from '../util/util.js';
+
+// ================================================================
+// "Public" ParamsBuilder API / Documentation
+// ================================================================
+
+/**
+ * Provides doc comments for the methods of CaseParamsBuilder and SubcaseParamsBuilder.
+ * (Also enforces rough interface match between them.)
+ */
+
+/**
+ * Base class for `CaseParamsBuilder` and `SubcaseParamsBuilder`.
+ */
+export class ParamsBuilderBase {
+ constructor(cases) {
+ this.cases = cases;
+ }
+
+ /**
+ * Hidden from test files. Use `builderIterateCasesWithSubcases` to access this.
+ */
+}
+
+/**
+ * Calls the (normally hidden) `iterateCasesWithSubcases()` method.
+ */
+export function builderIterateCasesWithSubcases(builder) {
+ return builder.iterateCasesWithSubcases();
+}
+
+/**
+ * Builder for combinatorial test **case** parameters.
+ *
+ * CaseParamsBuilder is immutable. Each method call returns a new, immutable object,
+ * modifying the list of cases according to the method called.
+ *
+ * This means, for example, that the `unit` passed into `TestBuilder.params()` can be reused.
+ */
+export class CaseParamsBuilder extends ParamsBuilderBase {
+ *iterateCasesWithSubcases() {
+ for (const a of this.cases()) {
+ yield [a, undefined];
+ }
+ }
+
+ [Symbol.iterator]() {
+ return this.cases();
+ }
+
+ /** @inheritDoc */
+ expandWithParams(expander) {
+ const newGenerator = expanderGenerator(this.cases, expander);
+ return new CaseParamsBuilder(() => newGenerator({}));
+ }
+
+ /** @inheritDoc */
+ expand(key, expander) {
+ return this.expandWithParams(function* (p) {
+ for (const value of expander(p)) {
+ yield { [key]: value };
+ }
+ });
+ }
+
+ /** @inheritDoc */
+ combineWithParams(newParams) {
+ assertNotGenerator(newParams);
+ const seenValues = new Set();
+ for (const params of newParams) {
+ const paramsStr = stringifyPublicParams(params);
+ assert(!seenValues.has(paramsStr), `Duplicate entry in combine[WithParams]: ${paramsStr}`);
+ seenValues.add(paramsStr);
+ }
+
+ return this.expandWithParams(() => newParams);
+ }
+
+ /** @inheritDoc */
+ combine(key, values) {
+ assertNotGenerator(values);
+ const mapped = mapLazy(values, v => ({ [key]: v }));
+ return this.combineWithParams(mapped);
+ }
+
+ /** @inheritDoc */
+ filter(pred) {
+ const newGenerator = filterGenerator(this.cases, pred);
+ return new CaseParamsBuilder(() => newGenerator({}));
+ }
+
+ /** @inheritDoc */
+ unless(pred) {
+ return this.filter(x => !pred(x));
+ }
+
+ /**
+ * "Finalize" the list of cases and begin defining subcases.
+ * Returns a new SubcaseParamsBuilder. Methods called on SubcaseParamsBuilder
+ * generate new subcases instead of new cases.
+ */
+ beginSubcases() {
+ return new SubcaseParamsBuilder(
+ () => this.cases(),
+ function* () {
+ yield {};
+ }
+ );
+ }
+}
+
+/**
+ * The unit CaseParamsBuilder, representing a single case with no params: `[ {} ]`.
+ *
+ * `punit` is passed to every `.params()`/`.paramsSubcasesOnly()` call, so `kUnitCaseParamsBuilder`
+ * is only explicitly needed if constructing a ParamsBuilder outside of a test builder.
+ */
+export const kUnitCaseParamsBuilder = new CaseParamsBuilder(function* () {
+ yield {};
+});
+
+/**
+ * Builder for combinatorial test _subcase_ parameters.
+ *
+ * SubcaseParamsBuilder is immutable. Each method call returns a new, immutable object,
+ * modifying the list of subcases according to the method called.
+ */
+export class SubcaseParamsBuilder extends ParamsBuilderBase {
+ constructor(cases, generator) {
+ super(cases);
+ this.subcases = generator;
+ }
+
+ *iterateCasesWithSubcases() {
+ for (const caseP of this.cases()) {
+ const subcases = Array.from(this.subcases(caseP));
+ if (subcases.length) {
+ yield [caseP, subcases];
+ }
+ }
+ }
+
+ /** @inheritDoc */
+ expandWithParams(expander) {
+ return new SubcaseParamsBuilder(this.cases, expanderGenerator(this.subcases, expander));
+ }
+
+ /** @inheritDoc */
+ expand(key, expander) {
+ return this.expandWithParams(function* (p) {
+ for (const value of expander(p)) {
+ // TypeScript doesn't know here that NewPKey is always a single literal string type.
+ yield { [key]: value };
+ }
+ });
+ }
+
+ /** @inheritDoc */
+ combineWithParams(newParams) {
+ assertNotGenerator(newParams);
+ return this.expandWithParams(() => newParams);
+ }
+
+ /** @inheritDoc */
+ combine(key, values) {
+ assertNotGenerator(values);
+ return this.expand(key, () => values);
+ }
+
+ /** @inheritDoc */
+ filter(pred) {
+ return new SubcaseParamsBuilder(this.cases, filterGenerator(this.subcases, pred));
+ }
+
+ /** @inheritDoc */
+ unless(pred) {
+ return this.filter(x => !pred(x));
+ }
+}
+
+function expanderGenerator(baseGenerator, expander) {
+ return function* (base) {
+ for (const a of baseGenerator(base)) {
+ for (const b of expander(mergeParams(base, a))) {
+ yield mergeParams(a, b);
+ }
+ }
+ };
+}
+
+function filterGenerator(baseGenerator, pred) {
+ return function* (base) {
+ for (const a of baseGenerator(base)) {
+ if (pred(mergeParams(base, a))) {
+ yield a;
+ }
+ }
+ };
+}
+
+/** Assert an object is not a Generator (a thing returned from a generator function). */
+function assertNotGenerator(x) {
+ if ('constructor' in x) {
+ assert(
+ x.constructor !== (function* () {})().constructor,
+ 'Argument must not be a generator, as generators are not reusable'
+ );
+ }
+}
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/framework/resources.js b/testing/web-platform/mozilla/tests/webgpu/common/framework/resources.js
new file mode 100644
index 0000000000..72eabeda4e
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/framework/resources.js
@@ -0,0 +1,111 @@
+/**
+ * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+ **/ /**
+ * Base path for resources. The default value is correct for non-worker WPT, but standalone and
+ * workers must access resources using a different base path, so this is overridden in
+ * `test_worker-worker.ts` and `standalone.ts`.
+ */ let baseResourcePath = './resources';
+let crossOriginHost = '';
+
+function getAbsoluteBaseResourcePath(path) {
+ // Path is already an absolute one.
+ if (path[0] === '/') {
+ return path;
+ }
+
+ // Path is relative
+ const relparts = window.location.pathname.split('/');
+ relparts.pop();
+ const pathparts = path.split('/');
+
+ let i;
+ for (i = 0; i < pathparts.length; ++i) {
+ switch (pathparts[i]) {
+ case '':
+ break;
+ case '.':
+ break;
+ case '..':
+ relparts.pop();
+ break;
+ default:
+ relparts.push(pathparts[i]);
+ break;
+ }
+ }
+
+ return relparts.join('/');
+}
+
+function runningOnLocalHost() {
+ const hostname = window.location.hostname;
+ return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1';
+}
+
+/**
+ * Get a path to a resource in the `resources` directory relative to the current execution context
+ * (html file or worker .js file), for `fetch()`, `<img>`, `<video>`, etc but from cross origin host.
+ * Provide onlineUrl if the case running online.
+ * @internal MAINTENANCE_TODO: Cases may run in the LAN environment (not localhost but no internet
+ * access). We temporarily use `crossOriginHost` to configure the cross origin host name in that situation.
+ * But opening to auto-detect mechanism or other solutions.
+ */
+export function getCrossOriginResourcePath(pathRelativeToResourcesDir, onlineUrl = '') {
+ // A cross origin host has been configured. Use this to load resource.
+ if (crossOriginHost !== '') {
+ return (
+ crossOriginHost +
+ getAbsoluteBaseResourcePath(baseResourcePath) +
+ '/' +
+ pathRelativeToResourcesDir
+ );
+ }
+
+ // Using 'localhost' and '127.0.0.1' trick to load cross origin resource. Set cross origin host name
+ // to 'localhost' if case is not running in 'localhost' domain. Otherwise, use '127.0.0.1'.
+ // host name to locahost unless the server running in
+ if (runningOnLocalHost()) {
+ let crossOriginHostName = '';
+ if (location.hostname === 'localhost') {
+ crossOriginHostName = 'http://127.0.0.1';
+ } else {
+ crossOriginHostName = 'http://localhost';
+ }
+
+ return (
+ crossOriginHostName +
+ ':' +
+ location.port +
+ getAbsoluteBaseResourcePath(baseResourcePath) +
+ '/' +
+ pathRelativeToResourcesDir
+ );
+ }
+
+ return onlineUrl;
+}
+
+/**
+ * Get a path to a resource in the `resources` directory, relative to the current execution context
+ * (html file or worker .js file), for `fetch()`, `<img>`, `<video>`, etc. Pass the cross origin host
+ * name if wants to load resoruce from cross origin host.
+ */
+export function getResourcePath(pathRelativeToResourcesDir) {
+ return baseResourcePath + '/' + pathRelativeToResourcesDir;
+}
+
+/**
+ * Set the base resource path (path to the `resources` directory relative to the current
+ * execution context).
+ */
+export function setBaseResourcePath(path) {
+ baseResourcePath = path;
+}
+
+/**
+ * Set the cross origin host and cases related to cross origin
+ * will load resource from the given host.
+ */
+export function setCrossOriginHost(host) {
+ crossOriginHost = host;
+}
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/framework/test_config.js b/testing/web-platform/mozilla/tests/webgpu/common/framework/test_config.js
new file mode 100644
index 0000000000..de016471cd
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/framework/test_config.js
@@ -0,0 +1,10 @@
+/**
+ * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+ **/
+
+export const globalTestConfig = {
+ maxSubcasesInFlight: 500,
+ testHeartbeatCallback: () => {},
+ noRaceWithRejectOnTimeout: false,
+ unrollConstEvalLoops: false,
+};
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/framework/test_group.js b/testing/web-platform/mozilla/tests/webgpu/common/framework/test_group.js
new file mode 100644
index 0000000000..2d1d3c1c07
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/framework/test_group.js
@@ -0,0 +1,3 @@
+/**
+ * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+ **/ export { makeTestGroup } from '../internal/test_group.js';
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/file_loader.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/file_loader.js
new file mode 100644
index 0000000000..e9aa0f66fc
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/file_loader.js
@@ -0,0 +1,96 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { assert } from '../util/util.js';
+import { parseQuery } from './query/parseQuery.js';
+
+
+import { loadTreeForQuery } from './tree.js';
+
+// A listing file, e.g. either of:
+// - `src/webgpu/listing.ts` (which is dynamically computed, has a Promise<TestSuiteListing>)
+// - `out/webgpu/listing.js` (which is pre-baked, has a TestSuiteListing)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// Base class for DefaultTestFileLoader and FakeTestFileLoader.
+export class TestFileLoader extends EventTarget {
+
+
+
+ importSpecFile(suite, path) {
+ const url = `${suite}/${path.join('/')}.spec.js`;
+ this.dispatchEvent(
+ new MessageEvent('import', { data: { url } }));
+
+ return this.import(url);
+ }
+
+ async loadTree(query, subqueriesToExpand = []) {
+ const tree = await loadTreeForQuery(
+ this,
+ query,
+ subqueriesToExpand.map((s) => {
+ const q = parseQuery(s);
+ assert(q.level >= 2, () => `subqueriesToExpand entries should not be multi-file:\n ${q}`);
+ return q;
+ }));
+
+ this.dispatchEvent(new MessageEvent('finish'));
+ return tree;
+ }
+
+ async loadCases(query) {
+ const tree = await this.loadTree(query);
+ return tree.iterateLeaves();
+ }
+}
+
+export class DefaultTestFileLoader extends TestFileLoader {
+ async listing(suite) {
+ return (await import(`../../${suite}/listing.js`)).listing;
+ }
+
+ import(path) {
+ return import(`../../${path}`);
+ }
+}
+//# sourceMappingURL=file_loader.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/file_loader.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/file_loader.js.map
new file mode 100644
index 0000000000..3a5d5b17f4
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/file_loader.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"file_loader.js","names":["assert","parseQuery","loadTreeForQuery","TestFileLoader","EventTarget","importSpecFile","suite","path","url","join","dispatchEvent","MessageEvent","data","import","loadTree","query","subqueriesToExpand","tree","map","s","q","level","loadCases","iterateLeaves","DefaultTestFileLoader","listing"],"sources":["../../../src/common/internal/file_loader.ts"],"sourcesContent":["import { IterableTestGroup } from '../internal/test_group.js';\nimport { assert } from '../util/util.js';\n\nimport { parseQuery } from './query/parseQuery.js';\nimport { TestQuery } from './query/query.js';\nimport { TestSuiteListing } from './test_suite_listing.js';\nimport { loadTreeForQuery, TestTree, TestTreeLeaf } from './tree.js';\n\n// A listing file, e.g. either of:\n// - `src/webgpu/listing.ts` (which is dynamically computed, has a Promise<TestSuiteListing>)\n// - `out/webgpu/listing.js` (which is pre-baked, has a TestSuiteListing)\ninterface ListingFile {\n listing: Promise<TestSuiteListing> | TestSuiteListing;\n}\n\n// A .spec.ts file, as imported.\nexport interface SpecFile {\n readonly description: string;\n readonly g: IterableTestGroup;\n}\n\nexport interface ImportInfo {\n url: string;\n}\n\ninterface TestFileLoaderEventMap {\n import: MessageEvent<ImportInfo>;\n finish: MessageEvent<void>;\n}\n\nexport interface TestFileLoader extends EventTarget {\n addEventListener<K extends keyof TestFileLoaderEventMap>(\n type: K,\n listener: (this: TestFileLoader, ev: TestFileLoaderEventMap[K]) => void,\n options?: boolean | AddEventListenerOptions\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions\n ): void;\n removeEventListener<K extends keyof TestFileLoaderEventMap>(\n type: K,\n listener: (this: TestFileLoader, ev: TestFileLoaderEventMap[K]) => void,\n options?: boolean | EventListenerOptions\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions\n ): void;\n}\n\n// Base class for DefaultTestFileLoader and FakeTestFileLoader.\nexport abstract class TestFileLoader extends EventTarget {\n abstract listing(suite: string): Promise<TestSuiteListing>;\n protected abstract import(path: string): Promise<SpecFile>;\n\n importSpecFile(suite: string, path: string[]): Promise<SpecFile> {\n const url = `${suite}/${path.join('/')}.spec.js`;\n this.dispatchEvent(\n new MessageEvent<ImportInfo>('import', { data: { url } })\n );\n return this.import(url);\n }\n\n async loadTree(query: TestQuery, subqueriesToExpand: string[] = []): Promise<TestTree> {\n const tree = await loadTreeForQuery(\n this,\n query,\n subqueriesToExpand.map(s => {\n const q = parseQuery(s);\n assert(q.level >= 2, () => `subqueriesToExpand entries should not be multi-file:\\n ${q}`);\n return q;\n })\n );\n this.dispatchEvent(new MessageEvent<void>('finish'));\n return tree;\n }\n\n async loadCases(query: TestQuery): Promise<IterableIterator<TestTreeLeaf>> {\n const tree = await this.loadTree(query);\n return tree.iterateLeaves();\n }\n}\n\nexport class DefaultTestFileLoader extends TestFileLoader {\n async listing(suite: string): Promise<TestSuiteListing> {\n return ((await import(`../../${suite}/listing.js`)) as ListingFile).listing;\n }\n\n import(path: string): Promise<SpecFile> {\n return import(`../../${path}`);\n }\n}\n"],"mappings":";AAAA;AAAA,GACA,SAASA,MAAM,QAAQ,iBAAiB;AAExC,SAASC,UAAU,QAAQ,uBAAuB;;;AAGlD,SAASC,gBAAgB,QAAgC,WAAW;;AAEpE;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA;AACA,OAAO,MAAeC,cAAc,SAASC,WAAW,CAAC;;;;EAIvDC,cAAc,CAACC,KAAa,EAAEC,IAAc,EAAqB;IAC/D,MAAMC,GAAG,GAAI,GAAEF,KAAM,IAAGC,IAAI,CAACE,IAAI,CAAC,GAAG,CAAE,UAAS;IAChD,IAAI,CAACC,aAAa;IAChB,IAAIC,YAAY,CAAa,QAAQ,EAAE,EAAEC,IAAI,EAAE,EAAEJ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAC1D;;IACD,OAAO,IAAI,CAACK,MAAM,CAACL,GAAG,CAAC;EACzB;;EAEA,MAAMM,QAAQ,CAACC,KAAgB,EAAEC,kBAA4B,GAAG,EAAE,EAAqB;IACrF,MAAMC,IAAI,GAAG,MAAMf,gBAAgB;IACjC,IAAI;IACJa,KAAK;IACLC,kBAAkB,CAACE,GAAG,CAAC,CAAAC,CAAC,KAAI;MAC1B,MAAMC,CAAC,GAAGnB,UAAU,CAACkB,CAAC,CAAC;MACvBnB,MAAM,CAACoB,CAAC,CAACC,KAAK,IAAI,CAAC,EAAE,MAAO,2DAA0DD,CAAE,EAAC,CAAC;MAC1F,OAAOA,CAAC;IACV,CAAC,CAAC,CACH;;IACD,IAAI,CAACV,aAAa,CAAC,IAAIC,YAAY,CAAO,QAAQ,CAAC,CAAC;IACpD,OAAOM,IAAI;EACb;;EAEA,MAAMK,SAAS,CAACP,KAAgB,EAA2C;IACzE,MAAME,IAAI,GAAG,MAAM,IAAI,CAACH,QAAQ,CAACC,KAAK,CAAC;IACvC,OAAOE,IAAI,CAACM,aAAa,EAAE;EAC7B;AACF;;AAEA,OAAO,MAAMC,qBAAqB,SAASrB,cAAc,CAAC;EACxD,MAAMsB,OAAO,CAACnB,KAAa,EAA6B;IACtD,OAAO,CAAE,MAAM,MAAM,CAAE,SAAQA,KAAM,aAAY,CAAC,EAAkBmB,OAAO;EAC7E;;EAEAZ,MAAM,CAACN,IAAY,EAAqB;IACtC,OAAO,MAAM,CAAE,SAAQA,IAAK,EAAC,CAAC;EAChC;AACF"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/log_message.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/log_message.js
new file mode 100644
index 0000000000..0414e45def
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/log_message.js
@@ -0,0 +1,45 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { extractImportantStackTrace } from '../stack.js';
+export class LogMessageWithStack extends Error {
+
+
+ stackHiddenMessage = undefined;
+
+ constructor(name, ex) {
+ super(ex.message);
+
+ this.name = name;
+ this.stack = ex.stack;
+ if ('extra' in ex) {
+ this.extra = ex.extra;
+ }
+ }
+
+ /** Set a flag so the stack is not printed in toJSON(). */
+ setStackHidden(stackHiddenMessage) {
+ this.stackHiddenMessage ??= stackHiddenMessage;
+ }
+
+ toJSON() {
+ let m = this.name;
+ if (this.message) m += ': ' + this.message;
+ if (this.stack) {
+ if (this.stackHiddenMessage === undefined) {
+ m += '\n' + extractImportantStackTrace(this);
+ } else if (this.stackHiddenMessage) {
+ m += `\n at (elided: ${this.stackHiddenMessage})`;
+ }
+ }
+ return m;
+ }
+}
+
+/**
+ * Returns a string, nicely indented, for debug logs.
+ * This is used in the cmdline and wpt runtimes. In WPT, it shows up in the `*-actual.txt` file.
+ */
+export function prettyPrintLog(log) {
+ return ' - ' + log.toJSON().replace(/\n/g, '\n ');
+}
+//# sourceMappingURL=log_message.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/log_message.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/log_message.js.map
new file mode 100644
index 0000000000..2af81c7452
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/log_message.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"log_message.js","names":["extractImportantStackTrace","LogMessageWithStack","Error","stackHiddenMessage","undefined","constructor","name","ex","message","stack","extra","setStackHidden","toJSON","m","prettyPrintLog","log","replace"],"sources":["../../../../src/common/internal/logging/log_message.ts"],"sourcesContent":["import { ErrorWithExtra } from '../../util/util.js';\nimport { extractImportantStackTrace } from '../stack.js';\n\nexport class LogMessageWithStack extends Error {\n readonly extra: unknown;\n\n private stackHiddenMessage: string | undefined = undefined;\n\n constructor(name: string, ex: Error | ErrorWithExtra) {\n super(ex.message);\n\n this.name = name;\n this.stack = ex.stack;\n if ('extra' in ex) {\n this.extra = ex.extra;\n }\n }\n\n /** Set a flag so the stack is not printed in toJSON(). */\n setStackHidden(stackHiddenMessage: string) {\n this.stackHiddenMessage ??= stackHiddenMessage;\n }\n\n toJSON(): string {\n let m = this.name;\n if (this.message) m += ': ' + this.message;\n if (this.stack) {\n if (this.stackHiddenMessage === undefined) {\n m += '\\n' + extractImportantStackTrace(this);\n } else if (this.stackHiddenMessage) {\n m += `\\n at (elided: ${this.stackHiddenMessage})`;\n }\n }\n return m;\n }\n}\n\n/**\n * Returns a string, nicely indented, for debug logs.\n * This is used in the cmdline and wpt runtimes. In WPT, it shows up in the `*-actual.txt` file.\n */\nexport function prettyPrintLog(log: LogMessageWithStack): string {\n return ' - ' + log.toJSON().replace(/\\n/g, '\\n ');\n}\n"],"mappings":";AAAA;AAAA,GACA,SAASA,0BAA0B,QAAQ,aAAa;AAExD,OAAO,MAAMC,mBAAmB,SAASC,KAAK,CAAC;;;EAGrCC,kBAAkB,GAAuBC,SAAS;;EAE1DC,WAAW,CAACC,IAAY,EAAEC,EAA0B,EAAE;IACpD,KAAK,CAACA,EAAE,CAACC,OAAO,CAAC;;IAEjB,IAAI,CAACF,IAAI,GAAGA,IAAI;IAChB,IAAI,CAACG,KAAK,GAAGF,EAAE,CAACE,KAAK;IACrB,IAAI,OAAO,IAAIF,EAAE,EAAE;MACjB,IAAI,CAACG,KAAK,GAAGH,EAAE,CAACG,KAAK;IACvB;EACF;;EAEA;EACAC,cAAc,CAACR,kBAA0B,EAAE;IACzC,IAAI,CAACA,kBAAkB,KAAKA,kBAAkB;EAChD;;EAEAS,MAAM,GAAW;IACf,IAAIC,CAAC,GAAG,IAAI,CAACP,IAAI;IACjB,IAAI,IAAI,CAACE,OAAO,EAAEK,CAAC,IAAI,IAAI,GAAG,IAAI,CAACL,OAAO;IAC1C,IAAI,IAAI,CAACC,KAAK,EAAE;MACd,IAAI,IAAI,CAACN,kBAAkB,KAAKC,SAAS,EAAE;QACzCS,CAAC,IAAI,IAAI,GAAGb,0BAA0B,CAAC,IAAI,CAAC;MAC9C,CAAC,MAAM,IAAI,IAAI,CAACG,kBAAkB,EAAE;QAClCU,CAAC,IAAK,mBAAkB,IAAI,CAACV,kBAAmB,GAAE;MACpD;IACF;IACA,OAAOU,CAAC;EACV;AACF;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASC,cAAc,CAACC,GAAwB,EAAU;EAC/D,OAAO,MAAM,GAAGA,GAAG,CAACH,MAAM,EAAE,CAACI,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC;AACvD"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/logger.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/logger.js
new file mode 100644
index 0000000000..c197579d4a
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/logger.js
@@ -0,0 +1,31 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { version } from '../version.js';
+import { TestCaseRecorder } from './test_case_recorder.js';
+
+
+
+export class Logger {
+ static globalDebugMode = false;
+
+
+ results = new Map();
+
+ constructor({ overrideDebugMode } = {}) {
+ this.overriddenDebugMode = overrideDebugMode;
+ }
+
+ record(name) {
+ const result = { status: 'running', timems: -1 };
+ this.results.set(name, result);
+ return [
+ new TestCaseRecorder(result, this.overriddenDebugMode ?? Logger.globalDebugMode),
+ result];
+
+ }
+
+ asJSON(space) {
+ return JSON.stringify({ version, results: Array.from(this.results) }, undefined, space);
+ }
+}
+//# sourceMappingURL=logger.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/logger.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/logger.js.map
new file mode 100644
index 0000000000..185f17edef
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/logger.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"logger.js","names":["version","TestCaseRecorder","Logger","globalDebugMode","results","Map","constructor","overrideDebugMode","overriddenDebugMode","record","name","result","status","timems","set","asJSON","space","JSON","stringify","Array","from","undefined"],"sources":["../../../../src/common/internal/logging/logger.ts"],"sourcesContent":["import { version } from '../version.js';\n\nimport { LiveTestCaseResult } from './result.js';\nimport { TestCaseRecorder } from './test_case_recorder.js';\n\nexport type LogResults = Map<string, LiveTestCaseResult>;\n\nexport class Logger {\n static globalDebugMode: boolean = false;\n\n readonly overriddenDebugMode: boolean | undefined;\n readonly results: LogResults = new Map();\n\n constructor({ overrideDebugMode }: { overrideDebugMode?: boolean } = {}) {\n this.overriddenDebugMode = overrideDebugMode;\n }\n\n record(name: string): [TestCaseRecorder, LiveTestCaseResult] {\n const result: LiveTestCaseResult = { status: 'running', timems: -1 };\n this.results.set(name, result);\n return [\n new TestCaseRecorder(result, this.overriddenDebugMode ?? Logger.globalDebugMode),\n result,\n ];\n }\n\n asJSON(space?: number): string {\n return JSON.stringify({ version, results: Array.from(this.results) }, undefined, space);\n }\n}\n"],"mappings":";AAAA;AAAA,GAAA,SAASA,OAAO,QAAQ,eAAe;AAGvC,SAASC,gBAAgB,QAAQ,yBAAyB;;;;AAI1D,OAAO,MAAMC,MAAM,CAAC;EAClB,OAAOC,eAAe,GAAY,KAAK;;;EAG9BC,OAAO,GAAe,IAAIC,GAAG,EAAE;;EAExCC,WAAW,CAAC,EAAEC,iBAAiB,CAAkC,CAAC,GAAG,CAAC,CAAC,EAAE;IACvE,IAAI,CAACC,mBAAmB,GAAGD,iBAAiB;EAC9C;;EAEAE,MAAM,CAACC,IAAY,EAA0C;IAC3D,MAAMC,MAA0B,GAAG,EAAEC,MAAM,EAAE,SAAS,EAAEC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACpE,IAAI,CAACT,OAAO,CAACU,GAAG,CAACJ,IAAI,EAAEC,MAAM,CAAC;IAC9B,OAAO;IACL,IAAIV,gBAAgB,CAACU,MAAM,EAAE,IAAI,CAACH,mBAAmB,IAAIN,MAAM,CAACC,eAAe,CAAC;IAChFQ,MAAM,CACP;;EACH;;EAEAI,MAAM,CAACC,KAAc,EAAU;IAC7B,OAAOC,IAAI,CAACC,SAAS,CAAC,EAAElB,OAAO,EAAEI,OAAO,EAAEe,KAAK,CAACC,IAAI,CAAC,IAAI,CAAChB,OAAO,CAAC,CAAC,CAAC,EAAEiB,SAAS,EAAEL,KAAK,CAAC;EACzF;AACF"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/result.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/result.js
new file mode 100644
index 0000000000..bbf217be6f
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/result.js
@@ -0,0 +1,4 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/export {};
+//# sourceMappingURL=result.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/result.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/result.js.map
new file mode 100644
index 0000000000..091c7cba8c
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/result.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"result.js","names":[],"sources":["../../../../src/common/internal/logging/result.ts"],"sourcesContent":["import { LogMessageWithStack } from './log_message.js';\n\n// MAINTENANCE_TODO: Add warn expectations\nexport type Expectation = 'pass' | 'skip' | 'fail';\n\nexport type Status = 'running' | 'warn' | Expectation;\n\nexport interface TestCaseResult {\n status: Status;\n timems: number;\n}\n\nexport interface LiveTestCaseResult extends TestCaseResult {\n logs?: LogMessageWithStack[];\n}\n\nexport interface TransferredTestCaseResult extends TestCaseResult {\n // When transferred from a worker, a LogMessageWithStack turns into a generic Error\n // (its prototype gets lost and replaced with Error).\n logs?: Error[];\n}\n"],"mappings":";AAAA;AAAA,G"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/test_case_recorder.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/test_case_recorder.js
new file mode 100644
index 0000000000..85ea61d862
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/test_case_recorder.js
@@ -0,0 +1,159 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { SkipTestCase, UnexpectedPassError } from '../../framework/fixture.js';import { globalTestConfig } from '../../framework/test_config.js';import { now, assert } from '../../util/util.js';
+
+import { LogMessageWithStack } from './log_message.js';var
+
+
+LogSeverity;(function (LogSeverity) {LogSeverity[LogSeverity["Pass"] = 0] = "Pass";LogSeverity[LogSeverity["Skip"] = 1] = "Skip";LogSeverity[LogSeverity["Warn"] = 2] = "Warn";LogSeverity[LogSeverity["ExpectFailed"] = 3] = "ExpectFailed";LogSeverity[LogSeverity["ValidationFailed"] = 4] = "ValidationFailed";LogSeverity[LogSeverity["ThrewException"] = 5] = "ThrewException";})(LogSeverity || (LogSeverity = {}));
+
+
+
+
+
+
+
+
+const kMaxLogStacks = 2;
+const kMinSeverityForStack = LogSeverity.Warn;
+
+/** Holds onto a LiveTestCaseResult owned by the Logger, and writes the results into it. */
+export class TestCaseRecorder {
+
+ inSubCase = false;
+ subCaseStatus = LogSeverity.Pass;
+ finalCaseStatus = LogSeverity.Pass;
+ hideStacksBelowSeverity = kMinSeverityForStack;
+ startTime = -1;
+ logs = [];
+ logLinesAtCurrentSeverity = 0;
+ debugging = false;
+ /** Used to dedup log messages which have identical stacks. */
+ messagesForPreviouslySeenStacks = new Map();
+
+ constructor(result, debugging) {
+ this.result = result;
+ this.debugging = debugging;
+ }
+
+ start() {
+ assert(this.startTime < 0, 'TestCaseRecorder cannot be reused');
+ this.startTime = now();
+ }
+
+ finish() {
+ assert(this.startTime >= 0, 'finish() before start()');
+
+ const timeMilliseconds = now() - this.startTime;
+ // Round to next microsecond to avoid storing useless .xxxx00000000000002 in results.
+ this.result.timems = Math.ceil(timeMilliseconds * 1000) / 1000;
+
+ // Convert numeric enum back to string (but expose 'exception' as 'fail')
+ this.result.status =
+ this.finalCaseStatus === LogSeverity.Pass ?
+ 'pass' :
+ this.finalCaseStatus === LogSeverity.Skip ?
+ 'skip' :
+ this.finalCaseStatus === LogSeverity.Warn ?
+ 'warn' :
+ 'fail'; // Everything else is an error
+
+ this.result.logs = this.logs;
+ }
+
+ beginSubCase() {
+ this.subCaseStatus = LogSeverity.Pass;
+ this.inSubCase = true;
+ }
+
+ endSubCase(expectedStatus) {
+ try {
+ if (expectedStatus === 'fail') {
+ if (this.subCaseStatus <= LogSeverity.Warn) {
+ throw new UnexpectedPassError();
+ } else {
+ this.subCaseStatus = LogSeverity.Pass;
+ }
+ }
+ } finally {
+ this.inSubCase = false;
+ if (this.subCaseStatus > this.finalCaseStatus) {
+ this.finalCaseStatus = this.subCaseStatus;
+ }
+ }
+ }
+
+ injectResult(injectedResult) {
+ Object.assign(this.result, injectedResult);
+ }
+
+ debug(ex) {
+ if (!this.debugging) return;
+ this.logImpl(LogSeverity.Pass, 'DEBUG', ex);
+ }
+
+ info(ex) {
+ this.logImpl(LogSeverity.Pass, 'INFO', ex);
+ }
+
+ skipped(ex) {
+ this.logImpl(LogSeverity.Skip, 'SKIP', ex);
+ }
+
+ warn(ex) {
+ this.logImpl(LogSeverity.Warn, 'WARN', ex);
+ }
+
+ expectationFailed(ex) {
+ this.logImpl(LogSeverity.ExpectFailed, 'EXPECTATION FAILED', ex);
+ }
+
+ validationFailed(ex) {
+ this.logImpl(LogSeverity.ValidationFailed, 'VALIDATION FAILED', ex);
+ }
+
+ threw(ex) {
+ if (ex instanceof SkipTestCase) {
+ this.skipped(ex);
+ return;
+ }
+ this.logImpl(LogSeverity.ThrewException, 'EXCEPTION', ex);
+ }
+
+ logImpl(level, name, baseException) {
+ assert(baseException instanceof Error, 'test threw a non-Error object');
+ globalTestConfig.testHeartbeatCallback();
+ const logMessage = new LogMessageWithStack(name, baseException);
+
+ // Final case status should be the "worst" of all log entries.
+ if (this.inSubCase) {
+ if (level > this.subCaseStatus) this.subCaseStatus = level;
+ } else {
+ if (level > this.finalCaseStatus) this.finalCaseStatus = level;
+ }
+
+ // setFirstLineOnly for all logs except `kMaxLogStacks` stacks at the highest severity
+ if (level > this.hideStacksBelowSeverity) {
+ this.logLinesAtCurrentSeverity = 0;
+ this.hideStacksBelowSeverity = level;
+
+ // Go back and setFirstLineOnly for everything of a lower log level
+ for (const log of this.logs) {
+ log.setStackHidden('below max severity');
+ }
+ }
+ if (level === this.hideStacksBelowSeverity) {
+ this.logLinesAtCurrentSeverity++;
+ } else if (level < kMinSeverityForStack) {
+ logMessage.setStackHidden('');
+ } else if (level < this.hideStacksBelowSeverity) {
+ logMessage.setStackHidden('below max severity');
+ }
+ if (this.logLinesAtCurrentSeverity > kMaxLogStacks) {
+ logMessage.setStackHidden(`only ${kMaxLogStacks} shown`);
+ }
+
+ this.logs.push(logMessage);
+ }
+}
+//# sourceMappingURL=test_case_recorder.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/test_case_recorder.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/test_case_recorder.js.map
new file mode 100644
index 0000000000..06c2e79bcd
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/logging/test_case_recorder.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"test_case_recorder.js","names":["SkipTestCase","UnexpectedPassError","globalTestConfig","now","assert","LogMessageWithStack","LogSeverity","kMaxLogStacks","kMinSeverityForStack","Warn","TestCaseRecorder","inSubCase","subCaseStatus","Pass","finalCaseStatus","hideStacksBelowSeverity","startTime","logs","logLinesAtCurrentSeverity","debugging","messagesForPreviouslySeenStacks","Map","constructor","result","start","finish","timeMilliseconds","timems","Math","ceil","status","Skip","beginSubCase","endSubCase","expectedStatus","injectResult","injectedResult","Object","assign","debug","ex","logImpl","info","skipped","warn","expectationFailed","ExpectFailed","validationFailed","ValidationFailed","threw","ThrewException","level","name","baseException","Error","testHeartbeatCallback","logMessage","log","setStackHidden","push"],"sources":["../../../../src/common/internal/logging/test_case_recorder.ts"],"sourcesContent":["import { SkipTestCase, UnexpectedPassError } from '../../framework/fixture.js';\nimport { globalTestConfig } from '../../framework/test_config.js';\nimport { now, assert } from '../../util/util.js';\n\nimport { LogMessageWithStack } from './log_message.js';\nimport { Expectation, LiveTestCaseResult } from './result.js';\n\nenum LogSeverity {\n Pass = 0,\n Skip = 1,\n Warn = 2,\n ExpectFailed = 3,\n ValidationFailed = 4,\n ThrewException = 5,\n}\n\nconst kMaxLogStacks = 2;\nconst kMinSeverityForStack = LogSeverity.Warn;\n\n/** Holds onto a LiveTestCaseResult owned by the Logger, and writes the results into it. */\nexport class TestCaseRecorder {\n private result: LiveTestCaseResult;\n private inSubCase: boolean = false;\n private subCaseStatus = LogSeverity.Pass;\n private finalCaseStatus = LogSeverity.Pass;\n private hideStacksBelowSeverity = kMinSeverityForStack;\n private startTime = -1;\n private logs: LogMessageWithStack[] = [];\n private logLinesAtCurrentSeverity = 0;\n private debugging = false;\n /** Used to dedup log messages which have identical stacks. */\n private messagesForPreviouslySeenStacks = new Map<string, LogMessageWithStack>();\n\n constructor(result: LiveTestCaseResult, debugging: boolean) {\n this.result = result;\n this.debugging = debugging;\n }\n\n start(): void {\n assert(this.startTime < 0, 'TestCaseRecorder cannot be reused');\n this.startTime = now();\n }\n\n finish(): void {\n assert(this.startTime >= 0, 'finish() before start()');\n\n const timeMilliseconds = now() - this.startTime;\n // Round to next microsecond to avoid storing useless .xxxx00000000000002 in results.\n this.result.timems = Math.ceil(timeMilliseconds * 1000) / 1000;\n\n // Convert numeric enum back to string (but expose 'exception' as 'fail')\n this.result.status =\n this.finalCaseStatus === LogSeverity.Pass\n ? 'pass'\n : this.finalCaseStatus === LogSeverity.Skip\n ? 'skip'\n : this.finalCaseStatus === LogSeverity.Warn\n ? 'warn'\n : 'fail'; // Everything else is an error\n\n this.result.logs = this.logs;\n }\n\n beginSubCase() {\n this.subCaseStatus = LogSeverity.Pass;\n this.inSubCase = true;\n }\n\n endSubCase(expectedStatus: Expectation) {\n try {\n if (expectedStatus === 'fail') {\n if (this.subCaseStatus <= LogSeverity.Warn) {\n throw new UnexpectedPassError();\n } else {\n this.subCaseStatus = LogSeverity.Pass;\n }\n }\n } finally {\n this.inSubCase = false;\n if (this.subCaseStatus > this.finalCaseStatus) {\n this.finalCaseStatus = this.subCaseStatus;\n }\n }\n }\n\n injectResult(injectedResult: LiveTestCaseResult): void {\n Object.assign(this.result, injectedResult);\n }\n\n debug(ex: Error): void {\n if (!this.debugging) return;\n this.logImpl(LogSeverity.Pass, 'DEBUG', ex);\n }\n\n info(ex: Error): void {\n this.logImpl(LogSeverity.Pass, 'INFO', ex);\n }\n\n skipped(ex: SkipTestCase): void {\n this.logImpl(LogSeverity.Skip, 'SKIP', ex);\n }\n\n warn(ex: Error): void {\n this.logImpl(LogSeverity.Warn, 'WARN', ex);\n }\n\n expectationFailed(ex: Error): void {\n this.logImpl(LogSeverity.ExpectFailed, 'EXPECTATION FAILED', ex);\n }\n\n validationFailed(ex: Error): void {\n this.logImpl(LogSeverity.ValidationFailed, 'VALIDATION FAILED', ex);\n }\n\n threw(ex: unknown): void {\n if (ex instanceof SkipTestCase) {\n this.skipped(ex);\n return;\n }\n this.logImpl(LogSeverity.ThrewException, 'EXCEPTION', ex);\n }\n\n private logImpl(level: LogSeverity, name: string, baseException: unknown): void {\n assert(baseException instanceof Error, 'test threw a non-Error object');\n globalTestConfig.testHeartbeatCallback();\n const logMessage = new LogMessageWithStack(name, baseException);\n\n // Final case status should be the \"worst\" of all log entries.\n if (this.inSubCase) {\n if (level > this.subCaseStatus) this.subCaseStatus = level;\n } else {\n if (level > this.finalCaseStatus) this.finalCaseStatus = level;\n }\n\n // setFirstLineOnly for all logs except `kMaxLogStacks` stacks at the highest severity\n if (level > this.hideStacksBelowSeverity) {\n this.logLinesAtCurrentSeverity = 0;\n this.hideStacksBelowSeverity = level;\n\n // Go back and setFirstLineOnly for everything of a lower log level\n for (const log of this.logs) {\n log.setStackHidden('below max severity');\n }\n }\n if (level === this.hideStacksBelowSeverity) {\n this.logLinesAtCurrentSeverity++;\n } else if (level < kMinSeverityForStack) {\n logMessage.setStackHidden('');\n } else if (level < this.hideStacksBelowSeverity) {\n logMessage.setStackHidden('below max severity');\n }\n if (this.logLinesAtCurrentSeverity > kMaxLogStacks) {\n logMessage.setStackHidden(`only ${kMaxLogStacks} shown`);\n }\n\n this.logs.push(logMessage);\n }\n}\n"],"mappings":";AAAA;AAAA,GAAA,SAASA,YAAY,EAAEC,mBAAmB,QAAQ,4BAA4B,CAC9E,SAASC,gBAAgB,QAAQ,gCAAgC,CACjE,SAASC,GAAG,EAAEC,MAAM,QAAQ,oBAAoB;;AAEhD,SAASC,mBAAmB,QAAQ,kBAAkB,CAAC;;;AAGlDC,WAAW,YAAXA,WAAW,GAAXA,WAAW,CAAXA,WAAW,uBAAXA,WAAW,CAAXA,WAAW,uBAAXA,WAAW,CAAXA,WAAW,uBAAXA,WAAW,CAAXA,WAAW,uCAAXA,WAAW,CAAXA,WAAW,+CAAXA,WAAW,CAAXA,WAAW,8CAAXA,WAAW,KAAXA,WAAW;;;;;;;;;AAShB,MAAMC,aAAa,GAAG,CAAC;AACvB,MAAMC,oBAAoB,GAAGF,WAAW,CAACG,IAAI;;AAE7C;AACA,OAAO,MAAMC,gBAAgB,CAAC;;EAEpBC,SAAS,GAAY,KAAK;EAC1BC,aAAa,GAAGN,WAAW,CAACO,IAAI;EAChCC,eAAe,GAAGR,WAAW,CAACO,IAAI;EAClCE,uBAAuB,GAAGP,oBAAoB;EAC9CQ,SAAS,GAAG,CAAC,CAAC;EACdC,IAAI,GAA0B,EAAE;EAChCC,yBAAyB,GAAG,CAAC;EAC7BC,SAAS,GAAG,KAAK;EACzB;EACQC,+BAA+B,GAAG,IAAIC,GAAG,EAA+B;;EAEhFC,WAAW,CAACC,MAA0B,EAAEJ,SAAkB,EAAE;IAC1D,IAAI,CAACI,MAAM,GAAGA,MAAM;IACpB,IAAI,CAACJ,SAAS,GAAGA,SAAS;EAC5B;;EAEAK,KAAK,GAAS;IACZpB,MAAM,CAAC,IAAI,CAACY,SAAS,GAAG,CAAC,EAAE,mCAAmC,CAAC;IAC/D,IAAI,CAACA,SAAS,GAAGb,GAAG,EAAE;EACxB;;EAEAsB,MAAM,GAAS;IACbrB,MAAM,CAAC,IAAI,CAACY,SAAS,IAAI,CAAC,EAAE,yBAAyB,CAAC;;IAEtD,MAAMU,gBAAgB,GAAGvB,GAAG,EAAE,GAAG,IAAI,CAACa,SAAS;IAC/C;IACA,IAAI,CAACO,MAAM,CAACI,MAAM,GAAGC,IAAI,CAACC,IAAI,CAACH,gBAAgB,GAAG,IAAI,CAAC,GAAG,IAAI;;IAE9D;IACA,IAAI,CAACH,MAAM,CAACO,MAAM;IAChB,IAAI,CAAChB,eAAe,KAAKR,WAAW,CAACO,IAAI;IACrC,MAAM;IACN,IAAI,CAACC,eAAe,KAAKR,WAAW,CAACyB,IAAI;IACzC,MAAM;IACN,IAAI,CAACjB,eAAe,KAAKR,WAAW,CAACG,IAAI;IACzC,MAAM;IACN,MAAM,CAAC,CAAC;;IAEd,IAAI,CAACc,MAAM,CAACN,IAAI,GAAG,IAAI,CAACA,IAAI;EAC9B;;EAEAe,YAAY,GAAG;IACb,IAAI,CAACpB,aAAa,GAAGN,WAAW,CAACO,IAAI;IACrC,IAAI,CAACF,SAAS,GAAG,IAAI;EACvB;;EAEAsB,UAAU,CAACC,cAA2B,EAAE;IACtC,IAAI;MACF,IAAIA,cAAc,KAAK,MAAM,EAAE;QAC7B,IAAI,IAAI,CAACtB,aAAa,IAAIN,WAAW,CAACG,IAAI,EAAE;UAC1C,MAAM,IAAIR,mBAAmB,EAAE;QACjC,CAAC,MAAM;UACL,IAAI,CAACW,aAAa,GAAGN,WAAW,CAACO,IAAI;QACvC;MACF;IACF,CAAC,SAAS;MACR,IAAI,CAACF,SAAS,GAAG,KAAK;MACtB,IAAI,IAAI,CAACC,aAAa,GAAG,IAAI,CAACE,eAAe,EAAE;QAC7C,IAAI,CAACA,eAAe,GAAG,IAAI,CAACF,aAAa;MAC3C;IACF;EACF;;EAEAuB,YAAY,CAACC,cAAkC,EAAQ;IACrDC,MAAM,CAACC,MAAM,CAAC,IAAI,CAACf,MAAM,EAAEa,cAAc,CAAC;EAC5C;;EAEAG,KAAK,CAACC,EAAS,EAAQ;IACrB,IAAI,CAAC,IAAI,CAACrB,SAAS,EAAE;IACrB,IAAI,CAACsB,OAAO,CAACnC,WAAW,CAACO,IAAI,EAAE,OAAO,EAAE2B,EAAE,CAAC;EAC7C;;EAEAE,IAAI,CAACF,EAAS,EAAQ;IACpB,IAAI,CAACC,OAAO,CAACnC,WAAW,CAACO,IAAI,EAAE,MAAM,EAAE2B,EAAE,CAAC;EAC5C;;EAEAG,OAAO,CAACH,EAAgB,EAAQ;IAC9B,IAAI,CAACC,OAAO,CAACnC,WAAW,CAACyB,IAAI,EAAE,MAAM,EAAES,EAAE,CAAC;EAC5C;;EAEAI,IAAI,CAACJ,EAAS,EAAQ;IACpB,IAAI,CAACC,OAAO,CAACnC,WAAW,CAACG,IAAI,EAAE,MAAM,EAAE+B,EAAE,CAAC;EAC5C;;EAEAK,iBAAiB,CAACL,EAAS,EAAQ;IACjC,IAAI,CAACC,OAAO,CAACnC,WAAW,CAACwC,YAAY,EAAE,oBAAoB,EAAEN,EAAE,CAAC;EAClE;;EAEAO,gBAAgB,CAACP,EAAS,EAAQ;IAChC,IAAI,CAACC,OAAO,CAACnC,WAAW,CAAC0C,gBAAgB,EAAE,mBAAmB,EAAER,EAAE,CAAC;EACrE;;EAEAS,KAAK,CAACT,EAAW,EAAQ;IACvB,IAAIA,EAAE,YAAYxC,YAAY,EAAE;MAC9B,IAAI,CAAC2C,OAAO,CAACH,EAAE,CAAC;MAChB;IACF;IACA,IAAI,CAACC,OAAO,CAACnC,WAAW,CAAC4C,cAAc,EAAE,WAAW,EAAEV,EAAE,CAAC;EAC3D;;EAEQC,OAAO,CAACU,KAAkB,EAAEC,IAAY,EAAEC,aAAsB,EAAQ;IAC9EjD,MAAM,CAACiD,aAAa,YAAYC,KAAK,EAAE,+BAA+B,CAAC;IACvEpD,gBAAgB,CAACqD,qBAAqB,EAAE;IACxC,MAAMC,UAAU,GAAG,IAAInD,mBAAmB,CAAC+C,IAAI,EAAEC,aAAa,CAAC;;IAE/D;IACA,IAAI,IAAI,CAAC1C,SAAS,EAAE;MAClB,IAAIwC,KAAK,GAAG,IAAI,CAACvC,aAAa,EAAE,IAAI,CAACA,aAAa,GAAGuC,KAAK;IAC5D,CAAC,MAAM;MACL,IAAIA,KAAK,GAAG,IAAI,CAACrC,eAAe,EAAE,IAAI,CAACA,eAAe,GAAGqC,KAAK;IAChE;;IAEA;IACA,IAAIA,KAAK,GAAG,IAAI,CAACpC,uBAAuB,EAAE;MACxC,IAAI,CAACG,yBAAyB,GAAG,CAAC;MAClC,IAAI,CAACH,uBAAuB,GAAGoC,KAAK;;MAEpC;MACA,KAAK,MAAMM,GAAG,IAAI,IAAI,CAACxC,IAAI,EAAE;QAC3BwC,GAAG,CAACC,cAAc,CAAC,oBAAoB,CAAC;MAC1C;IACF;IACA,IAAIP,KAAK,KAAK,IAAI,CAACpC,uBAAuB,EAAE;MAC1C,IAAI,CAACG,yBAAyB,EAAE;IAClC,CAAC,MAAM,IAAIiC,KAAK,GAAG3C,oBAAoB,EAAE;MACvCgD,UAAU,CAACE,cAAc,CAAC,EAAE,CAAC;IAC/B,CAAC,MAAM,IAAIP,KAAK,GAAG,IAAI,CAACpC,uBAAuB,EAAE;MAC/CyC,UAAU,CAACE,cAAc,CAAC,oBAAoB,CAAC;IACjD;IACA,IAAI,IAAI,CAACxC,yBAAyB,GAAGX,aAAa,EAAE;MAClDiD,UAAU,CAACE,cAAc,CAAE,QAAOnD,aAAc,QAAO,CAAC;IAC1D;;IAEA,IAAI,CAACU,IAAI,CAAC0C,IAAI,CAACH,UAAU,CAAC;EAC5B;AACF"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/params_utils.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/params_utils.js
new file mode 100644
index 0000000000..d1ba896d90
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/params_utils.js
@@ -0,0 +1,125 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { assert } from '../util/util.js';
+
+import { comparePublicParamsPaths, Ordering } from './query/compare.js';
+import { kWildcard, kParamSeparator, kParamKVSeparator } from './query/separators.js';
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+export function paramKeyIsPublic(key) {
+ return !key.startsWith('_');
+}
+
+export function extractPublicParams(params) {
+ const publicParams = {};
+ for (const k of Object.keys(params)) {
+ if (paramKeyIsPublic(k)) {
+ publicParams[k] = params[k];
+ }
+ }
+ return publicParams;
+}
+
+export const badParamValueChars = new RegExp(
+'[' + kParamKVSeparator + kParamSeparator + kWildcard + ']');
+
+
+export function publicParamsEquals(x, y) {
+ return comparePublicParamsPaths(x, y) === Ordering.Equal;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+function typeAssert() {}
+{
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ typeAssert();
+ typeAssert();
+ typeAssert();
+ typeAssert();
+ typeAssert();
+
+ typeAssert();
+
+ typeAssert();
+ typeAssert();
+ typeAssert();
+ typeAssert();
+ typeAssert();
+
+ // Unexpected test results - hopefully okay to ignore these
+ typeAssert();
+ typeAssert();
+ }
+}
+
+
+
+
+
+
+export function mergeParams(a, b) {
+ for (const key of Object.keys(a)) {
+ assert(!(key in b), 'Duplicate key: ' + key);
+ }
+ return { ...a, ...b };
+}
+//# sourceMappingURL=params_utils.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/params_utils.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/params_utils.js.map
new file mode 100644
index 0000000000..eb03517c23
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/params_utils.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"params_utils.js","names":["assert","comparePublicParamsPaths","Ordering","kWildcard","kParamSeparator","kParamKVSeparator","paramKeyIsPublic","key","startsWith","extractPublicParams","params","publicParams","k","Object","keys","badParamValueChars","RegExp","publicParamsEquals","x","y","Equal","typeAssert","mergeParams","a","b"],"sources":["../../../src/common/internal/params_utils.ts"],"sourcesContent":["import { TestParams } from '../framework/fixture.js';\nimport { ResolveType, UnionToIntersection } from '../util/types.js';\nimport { assert } from '../util/util.js';\n\nimport { comparePublicParamsPaths, Ordering } from './query/compare.js';\nimport { kWildcard, kParamSeparator, kParamKVSeparator } from './query/separators.js';\n\nexport type JSONWithUndefined =\n | undefined\n | null\n | number\n | string\n | boolean\n | readonly JSONWithUndefined[]\n // Ideally this would recurse into JSONWithUndefined, but it breaks code.\n | { readonly [k: string]: unknown };\nexport interface TestParamsRW {\n [k: string]: JSONWithUndefined;\n}\nexport type TestParamsIterable = Iterable<TestParams>;\n\nexport function paramKeyIsPublic(key: string): boolean {\n return !key.startsWith('_');\n}\n\nexport function extractPublicParams(params: TestParams): TestParams {\n const publicParams: TestParamsRW = {};\n for (const k of Object.keys(params)) {\n if (paramKeyIsPublic(k)) {\n publicParams[k] = params[k];\n }\n }\n return publicParams;\n}\n\nexport const badParamValueChars = new RegExp(\n '[' + kParamKVSeparator + kParamSeparator + kWildcard + ']'\n);\n\nexport function publicParamsEquals(x: TestParams, y: TestParams): boolean {\n return comparePublicParamsPaths(x, y) === Ordering.Equal;\n}\n\nexport type KeyOfNeverable<T> = T extends never ? never : keyof T;\nexport type AllKeysFromUnion<T> = keyof T | KeyOfNeverable<UnionToIntersection<T>>;\nexport type KeyOfOr<T, K, Default> = K extends keyof T ? T[K] : Default;\n\n/**\n * Flatten a union of interfaces into a single interface encoding the same type.\n *\n * Flattens a union in such a way that:\n * `{ a: number, b?: undefined } | { b: string, a?: undefined }`\n * (which is the value type of `[{ a: 1 }, { b: 1 }]`)\n * becomes `{ a: number | undefined, b: string | undefined }`.\n *\n * And also works for `{ a: number } | { b: string }` which maps to the same.\n */\nexport type FlattenUnionOfInterfaces<T> = {\n [K in AllKeysFromUnion<T>]: KeyOfOr<\n T,\n // If T always has K, just take T[K] (union of C[K] for each component C of T):\n K,\n // Otherwise, take the union of C[K] for each component C of T, PLUS undefined:\n undefined | KeyOfOr<UnionToIntersection<T>, K, void>\n >;\n};\n\n/* eslint-disable-next-line @typescript-eslint/no-unused-vars */\nfunction typeAssert<T extends 'pass'>() {}\n{\n type Test<T, U> = [T] extends [U]\n ? [U] extends [T]\n ? 'pass'\n : { actual: ResolveType<T>; expected: U }\n : { actual: ResolveType<T>; expected: U };\n\n type T01 = { a: number } | { b: string };\n type T02 = { a: number } | { b?: string };\n type T03 = { a: number } | { a?: number };\n type T04 = { a: number } | { a: string };\n type T05 = { a: number } | { a?: string };\n\n type T11 = { a: number; b?: undefined } | { a?: undefined; b: string };\n\n type T21 = { a: number; b?: undefined } | { b: string };\n type T22 = { a: number; b?: undefined } | { b?: string };\n type T23 = { a: number; b?: undefined } | { a?: number };\n type T24 = { a: number; b?: undefined } | { a: string };\n type T25 = { a: number; b?: undefined } | { a?: string };\n type T26 = { a: number; b?: undefined } | { a: undefined };\n type T27 = { a: number; b?: undefined } | { a: undefined; b: undefined };\n\n /* prettier-ignore */ {\n typeAssert<Test<FlattenUnionOfInterfaces<T01>, { a: number | undefined; b: string | undefined }>>();\n typeAssert<Test<FlattenUnionOfInterfaces<T02>, { a: number | undefined; b: string | undefined }>>();\n typeAssert<Test<FlattenUnionOfInterfaces<T03>, { a: number | undefined }>>();\n typeAssert<Test<FlattenUnionOfInterfaces<T04>, { a: number | string }>>();\n typeAssert<Test<FlattenUnionOfInterfaces<T05>, { a: number | string | undefined }>>();\n\n typeAssert<Test<FlattenUnionOfInterfaces<T11>, { a: number | undefined; b: string | undefined }>>();\n\n typeAssert<Test<FlattenUnionOfInterfaces<T22>, { a: number | undefined; b: string | undefined }>>();\n typeAssert<Test<FlattenUnionOfInterfaces<T23>, { a: number | undefined; b: undefined }>>();\n typeAssert<Test<FlattenUnionOfInterfaces<T24>, { a: number | string; b: undefined }>>();\n typeAssert<Test<FlattenUnionOfInterfaces<T25>, { a: number | string | undefined; b: undefined }>>();\n typeAssert<Test<FlattenUnionOfInterfaces<T27>, { a: number | undefined; b: undefined }>>();\n\n // Unexpected test results - hopefully okay to ignore these\n typeAssert<Test<FlattenUnionOfInterfaces<T21>, { b: string | undefined }>>();\n typeAssert<Test<FlattenUnionOfInterfaces<T26>, { a: number | undefined }>>();\n }\n}\n\nexport type Merged<A, B> = MergedFromFlat<A, FlattenUnionOfInterfaces<B>>;\nexport type MergedFromFlat<A, B> = {\n [K in keyof A | keyof B]: K extends keyof B ? B[K] : K extends keyof A ? A[K] : never;\n};\n\nexport function mergeParams<A extends {}, B extends {}>(a: A, b: B): Merged<A, B> {\n for (const key of Object.keys(a)) {\n assert(!(key in b), 'Duplicate key: ' + key);\n }\n return { ...a, ...b } as Merged<A, B>;\n}\n"],"mappings":";AAAA;AAAA,GAEA,SAASA,MAAM,QAAQ,iBAAiB;;AAExC,SAASC,wBAAwB,EAAEC,QAAQ,QAAQ,oBAAoB;AACvE,SAASC,SAAS,EAAEC,eAAe,EAAEC,iBAAiB,QAAQ,uBAAuB;;;;;;;;;;;;;;;;AAgBrF,OAAO,SAASC,gBAAgB,CAACC,GAAW,EAAW;EACrD,OAAO,CAACA,GAAG,CAACC,UAAU,CAAC,GAAG,CAAC;AAC7B;;AAEA,OAAO,SAASC,mBAAmB,CAACC,MAAkB,EAAc;EAClE,MAAMC,YAA0B,GAAG,CAAC,CAAC;EACrC,KAAK,MAAMC,CAAC,IAAIC,MAAM,CAACC,IAAI,CAACJ,MAAM,CAAC,EAAE;IACnC,IAAIJ,gBAAgB,CAACM,CAAC,CAAC,EAAE;MACvBD,YAAY,CAACC,CAAC,CAAC,GAAGF,MAAM,CAACE,CAAC,CAAC;IAC7B;EACF;EACA,OAAOD,YAAY;AACrB;;AAEA,OAAO,MAAMI,kBAAkB,GAAG,IAAIC,MAAM;AAC1C,GAAG,GAAGX,iBAAiB,GAAGD,eAAe,GAAGD,SAAS,GAAG,GAAG,CAC5D;;;AAED,OAAO,SAASc,kBAAkB,CAACC,CAAa,EAAEC,CAAa,EAAW;EACxE,OAAOlB,wBAAwB,CAACiB,CAAC,EAAEC,CAAC,CAAC,KAAKjB,QAAQ,CAACkB,KAAK;AAC1D;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,SAASC,UAAU,GAAqB,CAAC;AACzC;;;;;;;;;;;;;;;;;;;;;;;EAuBwB;IACpBA,UAAU,EAAyF;IACnGA,UAAU,EAAyF;IACnGA,UAAU,EAAkE;IAC5EA,UAAU,EAA+D;IACzEA,UAAU,EAA2E;;IAErFA,UAAU,EAAyF;;IAEnGA,UAAU,EAAyF;IACnGA,UAAU,EAAgF;IAC1FA,UAAU,EAA6E;IACvFA,UAAU,EAAyF;IACnGA,UAAU,EAAgF;;IAE1F;IACAA,UAAU,EAAkE;IAC5EA,UAAU,EAAkE;EAC9E;AACF;;;;;;;AAOA,OAAO,SAASC,WAAW,CAA6BC,CAAI,EAAEC,CAAI,EAAgB;EAChF,KAAK,MAAMjB,GAAG,IAAIM,MAAM,CAACC,IAAI,CAACS,CAAC,CAAC,EAAE;IAChCvB,MAAM,CAAC,EAAEO,GAAG,IAAIiB,CAAC,CAAC,EAAE,iBAAiB,GAAGjB,GAAG,CAAC;EAC9C;EACA,OAAO,EAAE,GAAGgB,CAAC,EAAE,GAAGC,CAAC,CAAC,CAAC;AACvB"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/compare.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/compare.js
new file mode 100644
index 0000000000..57975be912
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/compare.js
@@ -0,0 +1,95 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { assert, objectEquals } from '../../util/util.js';import { paramKeyIsPublic } from '../params_utils.js';
+
+
+
+export let Ordering;
+
+
+
+
+
+
+/**
+ * Compares two queries for their ordering (which is used to build the tree).
+ *
+ * See src/unittests/query_compare.spec.ts for examples.
+ */(function (Ordering) {Ordering[Ordering["Unordered"] = 0] = "Unordered";Ordering[Ordering["StrictSuperset"] = 1] = "StrictSuperset";Ordering[Ordering["Equal"] = 2] = "Equal";Ordering[Ordering["StrictSubset"] = 3] = "StrictSubset";})(Ordering || (Ordering = {}));
+export function compareQueries(a, b) {
+ if (a.suite !== b.suite) {
+ return Ordering.Unordered;
+ }
+
+ const filePathOrdering = comparePaths(a.filePathParts, b.filePathParts);
+ if (filePathOrdering !== Ordering.Equal || a.isMultiFile || b.isMultiFile) {
+ return compareOneLevel(filePathOrdering, a.isMultiFile, b.isMultiFile);
+ }
+ assert('testPathParts' in a && 'testPathParts' in b);
+
+ const testPathOrdering = comparePaths(a.testPathParts, b.testPathParts);
+ if (testPathOrdering !== Ordering.Equal || a.isMultiTest || b.isMultiTest) {
+ return compareOneLevel(testPathOrdering, a.isMultiTest, b.isMultiTest);
+ }
+ assert('params' in a && 'params' in b);
+
+ const paramsPathOrdering = comparePublicParamsPaths(a.params, b.params);
+ if (paramsPathOrdering !== Ordering.Equal || a.isMultiCase || b.isMultiCase) {
+ return compareOneLevel(paramsPathOrdering, a.isMultiCase, b.isMultiCase);
+ }
+ return Ordering.Equal;
+}
+
+/**
+ * Compares a single level of a query.
+ *
+ * "IsBig" means the query is big relative to the level, e.g. for test-level:
+ * - Anything >= `suite:a,*` is big
+ * - Anything <= `suite:a:*` is small
+ */
+function compareOneLevel(ordering, aIsBig, bIsBig) {
+ assert(ordering !== Ordering.Equal || aIsBig || bIsBig);
+ if (ordering === Ordering.Unordered) return Ordering.Unordered;
+ if (aIsBig && bIsBig) return ordering;
+ if (!aIsBig && !bIsBig) return Ordering.Unordered; // Equal case is already handled
+ // Exactly one of (a, b) is big.
+ if (aIsBig && ordering !== Ordering.StrictSubset) return Ordering.StrictSuperset;
+ if (bIsBig && ordering !== Ordering.StrictSuperset) return Ordering.StrictSubset;
+ return Ordering.Unordered;
+}
+
+function comparePaths(a, b) {
+ const shorter = Math.min(a.length, b.length);
+
+ for (let i = 0; i < shorter; ++i) {
+ if (a[i] !== b[i]) {
+ return Ordering.Unordered;
+ }
+ }
+ if (a.length === b.length) {
+ return Ordering.Equal;
+ } else if (a.length < b.length) {
+ return Ordering.StrictSuperset;
+ } else {
+ return Ordering.StrictSubset;
+ }
+}
+
+export function comparePublicParamsPaths(a, b) {
+ const aKeys = Object.keys(a).filter((k) => paramKeyIsPublic(k));
+ const commonKeys = new Set(aKeys.filter((k) => k in b));
+
+ for (const k of commonKeys) {
+ if (!objectEquals(a[k], b[k])) {
+ return Ordering.Unordered;
+ }
+ }
+ const bKeys = Object.keys(b).filter((k) => paramKeyIsPublic(k));
+ const aRemainingKeys = aKeys.length - commonKeys.size;
+ const bRemainingKeys = bKeys.length - commonKeys.size;
+ if (aRemainingKeys === 0 && bRemainingKeys === 0) return Ordering.Equal;
+ if (aRemainingKeys === 0) return Ordering.StrictSuperset;
+ if (bRemainingKeys === 0) return Ordering.StrictSubset;
+ return Ordering.Unordered;
+}
+//# sourceMappingURL=compare.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/compare.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/compare.js.map
new file mode 100644
index 0000000000..d4e90794ff
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/compare.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"compare.js","names":["assert","objectEquals","paramKeyIsPublic","Ordering","compareQueries","a","b","suite","Unordered","filePathOrdering","comparePaths","filePathParts","Equal","isMultiFile","compareOneLevel","testPathOrdering","testPathParts","isMultiTest","paramsPathOrdering","comparePublicParamsPaths","params","isMultiCase","ordering","aIsBig","bIsBig","StrictSubset","StrictSuperset","shorter","Math","min","length","i","aKeys","Object","keys","filter","k","commonKeys","Set","bKeys","aRemainingKeys","size","bRemainingKeys"],"sources":["../../../../src/common/internal/query/compare.ts"],"sourcesContent":["import { TestParams } from '../../framework/fixture.js';\nimport { assert, objectEquals } from '../../util/util.js';\nimport { paramKeyIsPublic } from '../params_utils.js';\n\nimport { TestQuery } from './query.js';\n\nexport const enum Ordering {\n Unordered,\n StrictSuperset,\n Equal,\n StrictSubset,\n}\n\n/**\n * Compares two queries for their ordering (which is used to build the tree).\n *\n * See src/unittests/query_compare.spec.ts for examples.\n */\nexport function compareQueries(a: TestQuery, b: TestQuery): Ordering {\n if (a.suite !== b.suite) {\n return Ordering.Unordered;\n }\n\n const filePathOrdering = comparePaths(a.filePathParts, b.filePathParts);\n if (filePathOrdering !== Ordering.Equal || a.isMultiFile || b.isMultiFile) {\n return compareOneLevel(filePathOrdering, a.isMultiFile, b.isMultiFile);\n }\n assert('testPathParts' in a && 'testPathParts' in b);\n\n const testPathOrdering = comparePaths(a.testPathParts, b.testPathParts);\n if (testPathOrdering !== Ordering.Equal || a.isMultiTest || b.isMultiTest) {\n return compareOneLevel(testPathOrdering, a.isMultiTest, b.isMultiTest);\n }\n assert('params' in a && 'params' in b);\n\n const paramsPathOrdering = comparePublicParamsPaths(a.params, b.params);\n if (paramsPathOrdering !== Ordering.Equal || a.isMultiCase || b.isMultiCase) {\n return compareOneLevel(paramsPathOrdering, a.isMultiCase, b.isMultiCase);\n }\n return Ordering.Equal;\n}\n\n/**\n * Compares a single level of a query.\n *\n * \"IsBig\" means the query is big relative to the level, e.g. for test-level:\n * - Anything >= `suite:a,*` is big\n * - Anything <= `suite:a:*` is small\n */\nfunction compareOneLevel(ordering: Ordering, aIsBig: boolean, bIsBig: boolean): Ordering {\n assert(ordering !== Ordering.Equal || aIsBig || bIsBig);\n if (ordering === Ordering.Unordered) return Ordering.Unordered;\n if (aIsBig && bIsBig) return ordering;\n if (!aIsBig && !bIsBig) return Ordering.Unordered; // Equal case is already handled\n // Exactly one of (a, b) is big.\n if (aIsBig && ordering !== Ordering.StrictSubset) return Ordering.StrictSuperset;\n if (bIsBig && ordering !== Ordering.StrictSuperset) return Ordering.StrictSubset;\n return Ordering.Unordered;\n}\n\nfunction comparePaths(a: readonly string[], b: readonly string[]): Ordering {\n const shorter = Math.min(a.length, b.length);\n\n for (let i = 0; i < shorter; ++i) {\n if (a[i] !== b[i]) {\n return Ordering.Unordered;\n }\n }\n if (a.length === b.length) {\n return Ordering.Equal;\n } else if (a.length < b.length) {\n return Ordering.StrictSuperset;\n } else {\n return Ordering.StrictSubset;\n }\n}\n\nexport function comparePublicParamsPaths(a: TestParams, b: TestParams): Ordering {\n const aKeys = Object.keys(a).filter(k => paramKeyIsPublic(k));\n const commonKeys = new Set(aKeys.filter(k => k in b));\n\n for (const k of commonKeys) {\n if (!objectEquals(a[k], b[k])) {\n return Ordering.Unordered;\n }\n }\n const bKeys = Object.keys(b).filter(k => paramKeyIsPublic(k));\n const aRemainingKeys = aKeys.length - commonKeys.size;\n const bRemainingKeys = bKeys.length - commonKeys.size;\n if (aRemainingKeys === 0 && bRemainingKeys === 0) return Ordering.Equal;\n if (aRemainingKeys === 0) return Ordering.StrictSuperset;\n if (bRemainingKeys === 0) return Ordering.StrictSubset;\n return Ordering.Unordered;\n}\n"],"mappings":"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"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/encode_selectively.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/encode_selectively.js
new file mode 100644
index 0000000000..518ff5ea34
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/encode_selectively.js
@@ -0,0 +1,24 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/ /**
+ * Encodes a stringified TestQuery so that it can be placed in a `?q=` parameter in a URL.
+ *
+ * `encodeURIComponent` encodes in accordance with `application/x-www-form-urlencoded`,
+ * but URLs don't actually have to be as strict as HTML form encoding
+ * (we interpret this purely from JavaScript).
+ * So we encode the component, then selectively convert some %-encoded escape codes
+ * back to their original form for readability/copyability.
+ */export function encodeURIComponentSelectively(s) {let ret = encodeURIComponent(s);
+ ret = ret.replace(/%22/g, '"'); // for JSON strings
+ ret = ret.replace(/%2C/g, ','); // for path separator, and JSON arrays
+ ret = ret.replace(/%3A/g, ':'); // for big separator
+ ret = ret.replace(/%3B/g, ';'); // for param separator
+ ret = ret.replace(/%3D/g, '='); // for params (k=v)
+ ret = ret.replace(/%5B/g, '['); // for JSON arrays
+ ret = ret.replace(/%5D/g, ']'); // for JSON arrays
+ ret = ret.replace(/%7B/g, '{'); // for JSON objects
+ ret = ret.replace(/%7D/g, '}'); // for JSON objects
+ ret = ret.replace(/%E2%9C%97/g, '✗'); // for jsUndefinedMagicValue
+ return ret;
+}
+//# sourceMappingURL=encode_selectively.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/encode_selectively.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/encode_selectively.js.map
new file mode 100644
index 0000000000..d70d890a32
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/encode_selectively.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"encode_selectively.js","names":["encodeURIComponentSelectively","s","ret","encodeURIComponent","replace"],"sources":["../../../../src/common/internal/query/encode_selectively.ts"],"sourcesContent":["/**\n * Encodes a stringified TestQuery so that it can be placed in a `?q=` parameter in a URL.\n *\n * `encodeURIComponent` encodes in accordance with `application/x-www-form-urlencoded`,\n * but URLs don't actually have to be as strict as HTML form encoding\n * (we interpret this purely from JavaScript).\n * So we encode the component, then selectively convert some %-encoded escape codes\n * back to their original form for readability/copyability.\n */\nexport function encodeURIComponentSelectively(s: string): string {\n let ret = encodeURIComponent(s);\n ret = ret.replace(/%22/g, '\"'); // for JSON strings\n ret = ret.replace(/%2C/g, ','); // for path separator, and JSON arrays\n ret = ret.replace(/%3A/g, ':'); // for big separator\n ret = ret.replace(/%3B/g, ';'); // for param separator\n ret = ret.replace(/%3D/g, '='); // for params (k=v)\n ret = ret.replace(/%5B/g, '['); // for JSON arrays\n ret = ret.replace(/%5D/g, ']'); // for JSON arrays\n ret = ret.replace(/%7B/g, '{'); // for JSON objects\n ret = ret.replace(/%7D/g, '}'); // for JSON objects\n ret = ret.replace(/%E2%9C%97/g, '✗'); // for jsUndefinedMagicValue\n return ret;\n}\n"],"mappings":";AAAA;AAAA,G,CAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,GACA,OAAO,SAASA,6BAA6B,CAACC,CAAS,EAAU,CAC/D,IAAIC,GAAG,GAAGC,kBAAkB,CAACF,CAAC,CAAC;EAC/BC,GAAG,GAAGA,GAAG,CAACE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;EAChCF,GAAG,GAAGA,GAAG,CAACE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;EAChCF,GAAG,GAAGA,GAAG,CAACE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;EAChCF,GAAG,GAAGA,GAAG,CAACE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;EAChCF,GAAG,GAAGA,GAAG,CAACE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;EAChCF,GAAG,GAAGA,GAAG,CAACE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;EAChCF,GAAG,GAAGA,GAAG,CAACE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;EAChCF,GAAG,GAAGA,GAAG,CAACE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;EAChCF,GAAG,GAAGA,GAAG,CAACE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;EAChCF,GAAG,GAAGA,GAAG,CAACE,OAAO,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC;EACtC,OAAOF,GAAG;AACZ"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/json_param_value.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/json_param_value.js
new file mode 100644
index 0000000000..dc5e20e439
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/json_param_value.js
@@ -0,0 +1,84 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { assert, sortObjectByKey } from '../../util/util.js';
+// JSON can't represent various values and by default stores them as `null`.
+// Instead, storing them as a magic string values in JSON.
+const jsUndefinedMagicValue = '_undef_';
+const jsNaNMagicValue = '_nan_';
+const jsPositiveInfinityMagicValue = '_posinfinity_';
+const jsNegativeInfinityMagicValue = '_neginfinity_';
+
+// -0 needs to be handled separately, because -0 === +0 returns true. Not
+// special casing +0/0, since it behaves intuitively. Assuming that if -0 is
+// being used, the differentiation from +0 is desired.
+const jsNegativeZeroMagicValue = '_negzero_';
+
+const toStringMagicValue = new Map([
+[undefined, jsUndefinedMagicValue],
+[NaN, jsNaNMagicValue],
+[Number.POSITIVE_INFINITY, jsPositiveInfinityMagicValue],
+[Number.NEGATIVE_INFINITY, jsNegativeInfinityMagicValue]
+// No -0 handling because it is special cased.
+]);
+
+const fromStringMagicValue = new Map([
+[jsUndefinedMagicValue, undefined],
+[jsNaNMagicValue, NaN],
+[jsPositiveInfinityMagicValue, Number.POSITIVE_INFINITY],
+[jsNegativeInfinityMagicValue, Number.NEGATIVE_INFINITY],
+// -0 is handled in this direction because there is no comparison issue.
+[jsNegativeZeroMagicValue, -0]]);
+
+
+function stringifyFilter(k, v) {
+ // Make sure no one actually uses a magic value as a parameter.
+ if (typeof v === 'string') {
+ assert(
+ !fromStringMagicValue.has(v),
+ `${v} is a magic value for stringification, so cannot be used`);
+
+
+ assert(
+ v !== jsNegativeZeroMagicValue,
+ `${v} is a magic value for stringification, so cannot be used`);
+
+ }
+
+ if (Object.is(v, -0)) {
+ return jsNegativeZeroMagicValue;
+ }
+
+ return toStringMagicValue.has(v) ? toStringMagicValue.get(v) : v;
+}
+
+export function stringifyParamValue(value) {
+ return JSON.stringify(value, stringifyFilter);
+}
+
+/**
+ * Like stringifyParamValue but sorts dictionaries by key, for hashing.
+ */
+export function stringifyParamValueUniquely(value) {
+ return JSON.stringify(value, (k, v) => {
+ if (typeof v === 'object' && v !== null) {
+ return sortObjectByKey(v);
+ }
+
+ return stringifyFilter(k, v);
+ });
+}
+
+// 'any' is part of the JSON.parse reviver interface, so cannot be avoided.
+
+function parseParamValueReviver(k, v) {
+ if (fromStringMagicValue.has(v)) {
+ return fromStringMagicValue.get(v);
+ }
+
+ return v;
+}
+
+export function parseParamValue(s) {
+ return JSON.parse(s, parseParamValueReviver);
+}
+//# sourceMappingURL=json_param_value.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/json_param_value.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/json_param_value.js.map
new file mode 100644
index 0000000000..d97066ae17
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/json_param_value.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"json_param_value.js","names":["assert","sortObjectByKey","jsUndefinedMagicValue","jsNaNMagicValue","jsPositiveInfinityMagicValue","jsNegativeInfinityMagicValue","jsNegativeZeroMagicValue","toStringMagicValue","Map","undefined","NaN","Number","POSITIVE_INFINITY","NEGATIVE_INFINITY","fromStringMagicValue","stringifyFilter","k","v","has","Object","is","get","stringifyParamValue","value","JSON","stringify","stringifyParamValueUniquely","parseParamValueReviver","parseParamValue","s","parse"],"sources":["../../../../src/common/internal/query/json_param_value.ts"],"sourcesContent":["import { assert, sortObjectByKey } from '../../util/util.js';\nimport { JSONWithUndefined } from '../params_utils.js';\n\n// JSON can't represent various values and by default stores them as `null`.\n// Instead, storing them as a magic string values in JSON.\nconst jsUndefinedMagicValue = '_undef_';\nconst jsNaNMagicValue = '_nan_';\nconst jsPositiveInfinityMagicValue = '_posinfinity_';\nconst jsNegativeInfinityMagicValue = '_neginfinity_';\n\n// -0 needs to be handled separately, because -0 === +0 returns true. Not\n// special casing +0/0, since it behaves intuitively. Assuming that if -0 is\n// being used, the differentiation from +0 is desired.\nconst jsNegativeZeroMagicValue = '_negzero_';\n\nconst toStringMagicValue = new Map<unknown, string>([\n [undefined, jsUndefinedMagicValue],\n [NaN, jsNaNMagicValue],\n [Number.POSITIVE_INFINITY, jsPositiveInfinityMagicValue],\n [Number.NEGATIVE_INFINITY, jsNegativeInfinityMagicValue],\n // No -0 handling because it is special cased.\n]);\n\nconst fromStringMagicValue = new Map<string, unknown>([\n [jsUndefinedMagicValue, undefined],\n [jsNaNMagicValue, NaN],\n [jsPositiveInfinityMagicValue, Number.POSITIVE_INFINITY],\n [jsNegativeInfinityMagicValue, Number.NEGATIVE_INFINITY],\n // -0 is handled in this direction because there is no comparison issue.\n [jsNegativeZeroMagicValue, -0],\n]);\n\nfunction stringifyFilter(k: string, v: unknown): unknown {\n // Make sure no one actually uses a magic value as a parameter.\n if (typeof v === 'string') {\n assert(\n !fromStringMagicValue.has(v),\n `${v} is a magic value for stringification, so cannot be used`\n );\n\n assert(\n v !== jsNegativeZeroMagicValue,\n `${v} is a magic value for stringification, so cannot be used`\n );\n }\n\n if (Object.is(v, -0)) {\n return jsNegativeZeroMagicValue;\n }\n\n return toStringMagicValue.has(v) ? toStringMagicValue.get(v) : v;\n}\n\nexport function stringifyParamValue(value: JSONWithUndefined): string {\n return JSON.stringify(value, stringifyFilter);\n}\n\n/**\n * Like stringifyParamValue but sorts dictionaries by key, for hashing.\n */\nexport function stringifyParamValueUniquely(value: JSONWithUndefined): string {\n return JSON.stringify(value, (k, v) => {\n if (typeof v === 'object' && v !== null) {\n return sortObjectByKey(v);\n }\n\n return stringifyFilter(k, v);\n });\n}\n\n// 'any' is part of the JSON.parse reviver interface, so cannot be avoided.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction parseParamValueReviver(k: string, v: any): any {\n if (fromStringMagicValue.has(v)) {\n return fromStringMagicValue.get(v);\n }\n\n return v;\n}\n\nexport function parseParamValue(s: string): JSONWithUndefined {\n return JSON.parse(s, parseParamValueReviver);\n}\n"],"mappings":";AAAA;AAAA,GAAA,SAASA,MAAM,EAAEC,eAAe,QAAQ,oBAAoB;AAG5D;AACA;AACA,MAAMC,qBAAqB,GAAG,SAAS;AACvC,MAAMC,eAAe,GAAG,OAAO;AAC/B,MAAMC,4BAA4B,GAAG,eAAe;AACpD,MAAMC,4BAA4B,GAAG,eAAe;;AAEpD;AACA;AACA;AACA,MAAMC,wBAAwB,GAAG,WAAW;;AAE5C,MAAMC,kBAAkB,GAAG,IAAIC,GAAG,CAAkB;AAClD,CAACC,SAAS,EAAEP,qBAAqB,CAAC;AAClC,CAACQ,GAAG,EAAEP,eAAe,CAAC;AACtB,CAACQ,MAAM,CAACC,iBAAiB,EAAER,4BAA4B,CAAC;AACxD,CAACO,MAAM,CAACE,iBAAiB,EAAER,4BAA4B;AACvD;AAAA,CACD,CAAC;;AAEF,MAAMS,oBAAoB,GAAG,IAAIN,GAAG,CAAkB;AACpD,CAACN,qBAAqB,EAAEO,SAAS,CAAC;AAClC,CAACN,eAAe,EAAEO,GAAG,CAAC;AACtB,CAACN,4BAA4B,EAAEO,MAAM,CAACC,iBAAiB,CAAC;AACxD,CAACP,4BAA4B,EAAEM,MAAM,CAACE,iBAAiB,CAAC;AACxD;AACA,CAACP,wBAAwB,EAAE,CAAC,CAAC,CAAC,CAC/B,CAAC;;;AAEF,SAASS,eAAe,CAACC,CAAS,EAAEC,CAAU,EAAW;EACvD;EACA,IAAI,OAAOA,CAAC,KAAK,QAAQ,EAAE;IACzBjB,MAAM;IACJ,CAACc,oBAAoB,CAACI,GAAG,CAACD,CAAC,CAAC;IAC3B,GAAEA,CAAE,0DAAyD,CAC/D;;;IAEDjB,MAAM;IACJiB,CAAC,KAAKX,wBAAwB;IAC7B,GAAEW,CAAE,0DAAyD,CAC/D;;EACH;;EAEA,IAAIE,MAAM,CAACC,EAAE,CAACH,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE;IACpB,OAAOX,wBAAwB;EACjC;;EAEA,OAAOC,kBAAkB,CAACW,GAAG,CAACD,CAAC,CAAC,GAAGV,kBAAkB,CAACc,GAAG,CAACJ,CAAC,CAAC,GAAGA,CAAC;AAClE;;AAEA,OAAO,SAASK,mBAAmB,CAACC,KAAwB,EAAU;EACpE,OAAOC,IAAI,CAACC,SAAS,CAACF,KAAK,EAAER,eAAe,CAAC;AAC/C;;AAEA;AACA;AACA;AACA,OAAO,SAASW,2BAA2B,CAACH,KAAwB,EAAU;EAC5E,OAAOC,IAAI,CAACC,SAAS,CAACF,KAAK,EAAE,CAACP,CAAC,EAAEC,CAAC,KAAK;IACrC,IAAI,OAAOA,CAAC,KAAK,QAAQ,IAAIA,CAAC,KAAK,IAAI,EAAE;MACvC,OAAOhB,eAAe,CAACgB,CAAC,CAAC;IAC3B;;IAEA,OAAOF,eAAe,CAACC,CAAC,EAAEC,CAAC,CAAC;EAC9B,CAAC,CAAC;AACJ;;AAEA;;AAEA,SAASU,sBAAsB,CAACX,CAAS,EAAEC,CAAM,EAAO;EACtD,IAAIH,oBAAoB,CAACI,GAAG,CAACD,CAAC,CAAC,EAAE;IAC/B,OAAOH,oBAAoB,CAACO,GAAG,CAACJ,CAAC,CAAC;EACpC;;EAEA,OAAOA,CAAC;AACV;;AAEA,OAAO,SAASW,eAAe,CAACC,CAAS,EAAqB;EAC5D,OAAOL,IAAI,CAACM,KAAK,CAACD,CAAC,EAAEF,sBAAsB,CAAC;AAC9C"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/parseQuery.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/parseQuery.js
new file mode 100644
index 0000000000..4cfa6faf58
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/parseQuery.js
@@ -0,0 +1,156 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { assert } from '../../util/util.js';import {
+
+badParamValueChars,
+paramKeyIsPublic } from
+'../params_utils.js';
+
+import { parseParamValue } from './json_param_value.js';
+import {
+
+TestQueryMultiFile,
+TestQueryMultiTest,
+TestQueryMultiCase,
+TestQuerySingleCase } from
+'./query.js';
+import { kBigSeparator, kWildcard, kPathSeparator, kParamSeparator } from './separators.js';
+import { validQueryPart } from './validQueryPart.js';
+
+export function parseQuery(s) {
+ try {
+ return parseQueryImpl(s);
+ } catch (ex) {
+ if (ex instanceof Error) {
+ ex.message += '\n on: ' + s;
+ }
+ throw ex;
+ }
+}
+
+function parseQueryImpl(s) {
+ // Undo encodeURIComponentSelectively
+ s = decodeURIComponent(s);
+
+ // bigParts are: suite, file, test, params (note kBigSeparator could appear in params)
+ let suite;
+ let fileString;
+ let testString;
+ let paramsString;
+ {
+ const i1 = s.indexOf(kBigSeparator);
+ assert(i1 !== -1, `query string must have at least one ${kBigSeparator}`);
+ suite = s.substring(0, i1);
+ const i2 = s.indexOf(kBigSeparator, i1 + 1);
+ if (i2 === -1) {
+ fileString = s.substring(i1 + 1);
+ } else {
+ fileString = s.substring(i1 + 1, i2);
+ const i3 = s.indexOf(kBigSeparator, i2 + 1);
+ if (i3 === -1) {
+ testString = s.substring(i2 + 1);
+ } else {
+ testString = s.substring(i2 + 1, i3);
+ paramsString = s.substring(i3 + 1);
+ }
+ }
+ }
+
+ const { parts: file, wildcard: filePathHasWildcard } = parseBigPart(fileString, kPathSeparator);
+
+ if (testString === undefined) {
+ // Query is file-level
+ assert(
+ filePathHasWildcard,
+ `File-level query without wildcard ${kWildcard}. Did you want a file-level query \
+(append ${kPathSeparator}${kWildcard}) or test-level query (append ${kBigSeparator}${kWildcard})?`);
+
+ return new TestQueryMultiFile(suite, file);
+ }
+ assert(!filePathHasWildcard, `Wildcard ${kWildcard} must be at the end of the query string`);
+
+ const { parts: test, wildcard: testPathHasWildcard } = parseBigPart(testString, kPathSeparator);
+
+ if (paramsString === undefined) {
+ // Query is test-level
+ assert(
+ testPathHasWildcard,
+ `Test-level query without wildcard ${kWildcard}; did you want a test-level query \
+(append ${kPathSeparator}${kWildcard}) or case-level query (append ${kBigSeparator}${kWildcard})?`);
+
+ assert(file.length > 0, 'File part of test-level query was empty (::)');
+ return new TestQueryMultiTest(suite, file, test);
+ }
+
+ // Query is case-level
+ assert(!testPathHasWildcard, `Wildcard ${kWildcard} must be at the end of the query string`);
+
+ const { parts: paramsParts, wildcard: paramsHasWildcard } = parseBigPart(
+ paramsString,
+ kParamSeparator);
+
+
+ assert(test.length > 0, 'Test part of case-level query was empty (::)');
+
+ const params = {};
+ for (const paramPart of paramsParts) {
+ const [k, v] = parseSingleParam(paramPart);
+ assert(validQueryPart.test(k), `param key names must match ${validQueryPart}`);
+ params[k] = v;
+ }
+ if (paramsHasWildcard) {
+ return new TestQueryMultiCase(suite, file, test, params);
+ } else {
+ return new TestQuerySingleCase(suite, file, test, params);
+ }
+}
+
+// webgpu:a,b,* or webgpu:a,b,c:*
+const kExampleQueries = `\
+webgpu${kBigSeparator}a${kPathSeparator}b${kPathSeparator}${kWildcard} or \
+webgpu${kBigSeparator}a${kPathSeparator}b${kPathSeparator}c${kBigSeparator}${kWildcard}`;
+
+function parseBigPart(
+s,
+separator)
+{
+ if (s === '') {
+ return { parts: [], wildcard: false };
+ }
+ const parts = s.split(separator);
+
+ let endsWithWildcard = false;
+ for (const [i, part] of parts.entries()) {
+ if (i === parts.length - 1) {
+ endsWithWildcard = part === kWildcard;
+ }
+ assert(
+ part.indexOf(kWildcard) === -1 || endsWithWildcard,
+ `Wildcard ${kWildcard} must be complete last part of a path (e.g. ${kExampleQueries})`);
+
+ }
+ if (endsWithWildcard) {
+ // Remove the last element of the array (which is just the wildcard).
+ parts.length = parts.length - 1;
+ }
+ return { parts, wildcard: endsWithWildcard };
+}
+
+function parseSingleParam(paramSubstring) {
+ assert(paramSubstring !== '', 'Param in a query must not be blank (is there a trailing comma?)');
+ const i = paramSubstring.indexOf('=');
+ assert(i !== -1, 'Param in a query must be of form key=value');
+ const k = paramSubstring.substring(0, i);
+ assert(paramKeyIsPublic(k), 'Param in a query must not be private (start with _)');
+ const v = paramSubstring.substring(i + 1);
+ return [k, parseSingleParamValue(v)];
+}
+
+function parseSingleParamValue(s) {
+ assert(
+ !badParamValueChars.test(s),
+ `param value must not match ${badParamValueChars} - was ${s}`);
+
+ return parseParamValue(s);
+}
+//# sourceMappingURL=parseQuery.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/parseQuery.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/parseQuery.js.map
new file mode 100644
index 0000000000..4580b15bf4
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/parseQuery.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"parseQuery.js","names":["assert","badParamValueChars","paramKeyIsPublic","parseParamValue","TestQueryMultiFile","TestQueryMultiTest","TestQueryMultiCase","TestQuerySingleCase","kBigSeparator","kWildcard","kPathSeparator","kParamSeparator","validQueryPart","parseQuery","s","parseQueryImpl","ex","Error","message","decodeURIComponent","suite","fileString","testString","paramsString","i1","indexOf","substring","i2","i3","parts","file","wildcard","filePathHasWildcard","parseBigPart","undefined","test","testPathHasWildcard","length","paramsParts","paramsHasWildcard","params","paramPart","k","v","parseSingleParam","kExampleQueries","separator","split","endsWithWildcard","i","part","entries","paramSubstring","parseSingleParamValue"],"sources":["../../../../src/common/internal/query/parseQuery.ts"],"sourcesContent":["import { assert } from '../../util/util.js';\nimport {\n TestParamsRW,\n JSONWithUndefined,\n badParamValueChars,\n paramKeyIsPublic,\n} from '../params_utils.js';\n\nimport { parseParamValue } from './json_param_value.js';\nimport {\n TestQuery,\n TestQueryMultiFile,\n TestQueryMultiTest,\n TestQueryMultiCase,\n TestQuerySingleCase,\n} from './query.js';\nimport { kBigSeparator, kWildcard, kPathSeparator, kParamSeparator } from './separators.js';\nimport { validQueryPart } from './validQueryPart.js';\n\nexport function parseQuery(s: string): TestQuery {\n try {\n return parseQueryImpl(s);\n } catch (ex) {\n if (ex instanceof Error) {\n ex.message += '\\n on: ' + s;\n }\n throw ex;\n }\n}\n\nfunction parseQueryImpl(s: string): TestQuery {\n // Undo encodeURIComponentSelectively\n s = decodeURIComponent(s);\n\n // bigParts are: suite, file, test, params (note kBigSeparator could appear in params)\n let suite: string;\n let fileString: string | undefined;\n let testString: string | undefined;\n let paramsString: string | undefined;\n {\n const i1 = s.indexOf(kBigSeparator);\n assert(i1 !== -1, `query string must have at least one ${kBigSeparator}`);\n suite = s.substring(0, i1);\n const i2 = s.indexOf(kBigSeparator, i1 + 1);\n if (i2 === -1) {\n fileString = s.substring(i1 + 1);\n } else {\n fileString = s.substring(i1 + 1, i2);\n const i3 = s.indexOf(kBigSeparator, i2 + 1);\n if (i3 === -1) {\n testString = s.substring(i2 + 1);\n } else {\n testString = s.substring(i2 + 1, i3);\n paramsString = s.substring(i3 + 1);\n }\n }\n }\n\n const { parts: file, wildcard: filePathHasWildcard } = parseBigPart(fileString, kPathSeparator);\n\n if (testString === undefined) {\n // Query is file-level\n assert(\n filePathHasWildcard,\n `File-level query without wildcard ${kWildcard}. Did you want a file-level query \\\n(append ${kPathSeparator}${kWildcard}) or test-level query (append ${kBigSeparator}${kWildcard})?`\n );\n return new TestQueryMultiFile(suite, file);\n }\n assert(!filePathHasWildcard, `Wildcard ${kWildcard} must be at the end of the query string`);\n\n const { parts: test, wildcard: testPathHasWildcard } = parseBigPart(testString, kPathSeparator);\n\n if (paramsString === undefined) {\n // Query is test-level\n assert(\n testPathHasWildcard,\n `Test-level query without wildcard ${kWildcard}; did you want a test-level query \\\n(append ${kPathSeparator}${kWildcard}) or case-level query (append ${kBigSeparator}${kWildcard})?`\n );\n assert(file.length > 0, 'File part of test-level query was empty (::)');\n return new TestQueryMultiTest(suite, file, test);\n }\n\n // Query is case-level\n assert(!testPathHasWildcard, `Wildcard ${kWildcard} must be at the end of the query string`);\n\n const { parts: paramsParts, wildcard: paramsHasWildcard } = parseBigPart(\n paramsString,\n kParamSeparator\n );\n\n assert(test.length > 0, 'Test part of case-level query was empty (::)');\n\n const params: TestParamsRW = {};\n for (const paramPart of paramsParts) {\n const [k, v] = parseSingleParam(paramPart);\n assert(validQueryPart.test(k), `param key names must match ${validQueryPart}`);\n params[k] = v;\n }\n if (paramsHasWildcard) {\n return new TestQueryMultiCase(suite, file, test, params);\n } else {\n return new TestQuerySingleCase(suite, file, test, params);\n }\n}\n\n// webgpu:a,b,* or webgpu:a,b,c:*\nconst kExampleQueries = `\\\nwebgpu${kBigSeparator}a${kPathSeparator}b${kPathSeparator}${kWildcard} or \\\nwebgpu${kBigSeparator}a${kPathSeparator}b${kPathSeparator}c${kBigSeparator}${kWildcard}`;\n\nfunction parseBigPart(\n s: string,\n separator: typeof kParamSeparator | typeof kPathSeparator\n): { parts: string[]; wildcard: boolean } {\n if (s === '') {\n return { parts: [], wildcard: false };\n }\n const parts = s.split(separator);\n\n let endsWithWildcard = false;\n for (const [i, part] of parts.entries()) {\n if (i === parts.length - 1) {\n endsWithWildcard = part === kWildcard;\n }\n assert(\n part.indexOf(kWildcard) === -1 || endsWithWildcard,\n `Wildcard ${kWildcard} must be complete last part of a path (e.g. ${kExampleQueries})`\n );\n }\n if (endsWithWildcard) {\n // Remove the last element of the array (which is just the wildcard).\n parts.length = parts.length - 1;\n }\n return { parts, wildcard: endsWithWildcard };\n}\n\nfunction parseSingleParam(paramSubstring: string): [string, JSONWithUndefined] {\n assert(paramSubstring !== '', 'Param in a query must not be blank (is there a trailing comma?)');\n const i = paramSubstring.indexOf('=');\n assert(i !== -1, 'Param in a query must be of form key=value');\n const k = paramSubstring.substring(0, i);\n assert(paramKeyIsPublic(k), 'Param in a query must not be private (start with _)');\n const v = paramSubstring.substring(i + 1);\n return [k, parseSingleParamValue(v)];\n}\n\nfunction parseSingleParamValue(s: string): JSONWithUndefined {\n assert(\n !badParamValueChars.test(s),\n `param value must not match ${badParamValueChars} - was ${s}`\n );\n return parseParamValue(s);\n}\n"],"mappings":"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"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/query.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/query.js
new file mode 100644
index 0000000000..cb09f6ea8a
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/query.js
@@ -0,0 +1,263 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { optionEnabled } from '../../runtime/helper/options.js';import { assert, unreachable } from '../../util/util.js';
+
+
+import { compareQueries, Ordering } from './compare.js';
+import { encodeURIComponentSelectively } from './encode_selectively.js';
+import { parseQuery } from './parseQuery.js';
+import { kBigSeparator, kPathSeparator, kWildcard } from './separators.js';
+import { stringifyPublicParams } from './stringify_params.js';
+
+/**
+ * Represents a test query of some level.
+ *
+ * TestQuery types are immutable.
+ */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * A multi-file test query, like `s:*` or `s:a,b,*`.
+ *
+ * Immutable (makes copies of constructor args).
+ */
+export class TestQueryMultiFile {
+ level = 1;
+ isMultiFile = true;
+
+
+
+ constructor(suite, file) {
+ this.suite = suite;
+ this.filePathParts = [...file];
+ }
+
+ get depthInLevel() {
+ return this.filePathParts.length;
+ }
+
+ toString() {
+ return encodeURIComponentSelectively(this.toStringHelper().join(kBigSeparator));
+ }
+
+ toStringHelper() {
+ return [this.suite, [...this.filePathParts, kWildcard].join(kPathSeparator)];
+ }
+}
+
+/**
+ * A multi-test test query, like `s:f:*` or `s:f:a,b,*`.
+ *
+ * Immutable (makes copies of constructor args).
+ */
+export class TestQueryMultiTest extends TestQueryMultiFile {
+ level = 2;
+ isMultiFile = false;
+ isMultiTest = true;
+
+
+ constructor(suite, file, test) {
+ super(suite, file);
+ assert(file.length > 0, 'multi-test (or finer) query must have file-path');
+ this.testPathParts = [...test];
+ }
+
+ get depthInLevel() {
+ return this.testPathParts.length;
+ }
+
+ toStringHelper() {
+ return [
+ this.suite,
+ this.filePathParts.join(kPathSeparator),
+ [...this.testPathParts, kWildcard].join(kPathSeparator)];
+
+ }
+}
+
+/**
+ * A multi-case test query, like `s:f:t:*` or `s:f:t:a,b,*`.
+ *
+ * Immutable (makes copies of constructor args), except for param values
+ * (which aren't normally supposed to change; they're marked readonly in TestParams).
+ */
+export class TestQueryMultiCase extends TestQueryMultiTest {
+ level = 3;
+ isMultiTest = false;
+ isMultiCase = true;
+
+
+ constructor(suite, file, test, params) {
+ super(suite, file, test);
+ assert(test.length > 0, 'multi-case (or finer) query must have test-path');
+ this.params = { ...params };
+ }
+
+ get depthInLevel() {
+ return Object.keys(this.params).length;
+ }
+
+ toStringHelper() {
+ return [
+ this.suite,
+ this.filePathParts.join(kPathSeparator),
+ this.testPathParts.join(kPathSeparator),
+ stringifyPublicParams(this.params, true)];
+
+ }
+}
+
+/**
+ * A multi-case test query, like `s:f:t:` or `s:f:t:a=1,b=1`.
+ *
+ * Immutable (makes copies of constructor args).
+ */
+export class TestQuerySingleCase extends TestQueryMultiCase {
+ level = 4;
+ isMultiCase = false;
+
+ get depthInLevel() {
+ return 0;
+ }
+
+ toStringHelper() {
+ return [
+ this.suite,
+ this.filePathParts.join(kPathSeparator),
+ this.testPathParts.join(kPathSeparator),
+ stringifyPublicParams(this.params)];
+
+ }
+}
+
+/**
+ * Parse raw expectations input into TestQueryWithExpectation[], filtering so that only
+ * expectations that are relevant for the provided query and wptURL.
+ *
+ * `rawExpectations` should be @type {{ query: string, expectation: Expectation }[]}
+ *
+ * The `rawExpectations` are parsed and validated that they are in the correct format.
+ * If `wptURL` is passed, the query string should be of the full path format such
+ * as `path/to/cts.https.html?worker=0&q=suite:test_path:test_name:foo=1;bar=2;*`.
+ * If `wptURL` is `undefined`, the query string should be only the query
+ * `suite:test_path:test_name:foo=1;bar=2;*`.
+ */
+export function parseExpectationsForTestQuery(
+rawExpectations,
+
+
+
+
+
+query,
+wptURL)
+{
+ if (!Array.isArray(rawExpectations)) {
+ unreachable('Expectations should be an array');
+ }
+ const expectations = [];
+ for (const entry of rawExpectations) {
+ assert(typeof entry === 'object');
+ const rawExpectation = entry;
+ assert(rawExpectation.query !== undefined, 'Expectation missing query string');
+ assert(rawExpectation.expectation !== undefined, 'Expectation missing expectation string');
+
+ let expectationQuery;
+ if (wptURL !== undefined) {
+ const expectationURL = new URL(`${wptURL.origin}/${entry.query}`);
+ if (expectationURL.pathname !== wptURL.pathname) {
+ continue;
+ }
+ assert(
+ expectationURL.pathname === wptURL.pathname,
+ `Invalid expectation path ${expectationURL.pathname}
+Expectation should be of the form path/to/cts.https.html?worker=0&q=suite:test_path:test_name:foo=1;bar=2;...
+ `);
+
+
+ const params = expectationURL.searchParams;
+ if (optionEnabled('worker', params) !== optionEnabled('worker', wptURL.searchParams)) {
+ continue;
+ }
+
+ const qs = params.getAll('q');
+ assert(qs.length === 1, 'currently, there must be exactly one ?q= in the expectation string');
+ expectationQuery = parseQuery(qs[0]);
+ } else {
+ expectationQuery = parseQuery(entry.query);
+ }
+
+ // Strip params from multicase expectations so that an expectation of foo=2;*
+ // is stored if the test query is bar=3;*
+ const queryForFilter =
+ expectationQuery instanceof TestQueryMultiCase ?
+ new TestQueryMultiCase(
+ expectationQuery.suite,
+ expectationQuery.filePathParts,
+ expectationQuery.testPathParts,
+ {}) :
+
+ expectationQuery;
+
+ if (compareQueries(query, queryForFilter) === Ordering.Unordered) {
+ continue;
+ }
+
+ switch (entry.expectation) {
+ case 'pass':
+ case 'skip':
+ case 'fail':
+ break;
+ default:
+ unreachable(`Invalid expectation ${entry.expectation}`);}
+
+
+ expectations.push({
+ query: expectationQuery,
+ expectation: entry.expectation
+ });
+ }
+ return expectations;
+}
+
+/**
+ * For display purposes only, produces a "relative" query string from parent to child.
+ * Used in the wpt runtime to reduce the verbosity of logs.
+ */
+export function relativeQueryString(parent, child) {
+ const ordering = compareQueries(parent, child);
+ if (ordering === Ordering.Equal) {
+ return '';
+ } else if (ordering === Ordering.StrictSuperset) {
+ const parentString = parent.toString();
+ assert(parentString.endsWith(kWildcard));
+ const childString = child.toString();
+ assert(
+ childString.startsWith(parentString.substring(0, parentString.length - 2)),
+ 'impossible?: childString does not start with parentString[:-2]');
+
+ return childString.substring(parentString.length - 2);
+ } else {
+ unreachable(
+ `relativeQueryString arguments have invalid ordering ${ordering}:\n${parent}\n${child}`);
+
+ }
+}
+//# sourceMappingURL=query.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/query.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/query.js.map
new file mode 100644
index 0000000000..09d712c589
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/query.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"query.js","names":["optionEnabled","assert","unreachable","compareQueries","Ordering","encodeURIComponentSelectively","parseQuery","kBigSeparator","kPathSeparator","kWildcard","stringifyPublicParams","TestQueryMultiFile","level","isMultiFile","constructor","suite","file","filePathParts","depthInLevel","length","toString","toStringHelper","join","TestQueryMultiTest","isMultiTest","test","testPathParts","TestQueryMultiCase","isMultiCase","params","Object","keys","TestQuerySingleCase","parseExpectationsForTestQuery","rawExpectations","query","wptURL","Array","isArray","expectations","entry","rawExpectation","undefined","expectation","expectationQuery","expectationURL","URL","origin","pathname","searchParams","qs","getAll","queryForFilter","Unordered","push","relativeQueryString","parent","child","ordering","Equal","StrictSuperset","parentString","endsWith","childString","startsWith","substring"],"sources":["../../../../src/common/internal/query/query.ts"],"sourcesContent":["import { TestParams } from '../../framework/fixture.js';\nimport { optionEnabled } from '../../runtime/helper/options.js';\nimport { assert, unreachable } from '../../util/util.js';\nimport { Expectation } from '../logging/result.js';\n\nimport { compareQueries, Ordering } from './compare.js';\nimport { encodeURIComponentSelectively } from './encode_selectively.js';\nimport { parseQuery } from './parseQuery.js';\nimport { kBigSeparator, kPathSeparator, kWildcard } from './separators.js';\nimport { stringifyPublicParams } from './stringify_params.js';\n\n/**\n * Represents a test query of some level.\n *\n * TestQuery types are immutable.\n */\nexport type TestQuery =\n | TestQuerySingleCase\n | TestQueryMultiCase\n | TestQueryMultiTest\n | TestQueryMultiFile;\n\n/**\n * - 1 = MultiFile.\n * - 2 = MultiTest.\n * - 3 = MultiCase.\n * - 4 = SingleCase.\n */\nexport type TestQueryLevel = 1 | 2 | 3 | 4;\n\nexport interface TestQueryWithExpectation {\n query: TestQuery;\n expectation: Expectation;\n}\n\n/**\n * A multi-file test query, like `s:*` or `s:a,b,*`.\n *\n * Immutable (makes copies of constructor args).\n */\nexport class TestQueryMultiFile {\n readonly level: TestQueryLevel = 1;\n readonly isMultiFile: boolean = true;\n readonly suite: string;\n readonly filePathParts: readonly string[];\n\n constructor(suite: string, file: readonly string[]) {\n this.suite = suite;\n this.filePathParts = [...file];\n }\n\n get depthInLevel() {\n return this.filePathParts.length;\n }\n\n toString(): string {\n return encodeURIComponentSelectively(this.toStringHelper().join(kBigSeparator));\n }\n\n protected toStringHelper(): string[] {\n return [this.suite, [...this.filePathParts, kWildcard].join(kPathSeparator)];\n }\n}\n\n/**\n * A multi-test test query, like `s:f:*` or `s:f:a,b,*`.\n *\n * Immutable (makes copies of constructor args).\n */\nexport class TestQueryMultiTest extends TestQueryMultiFile {\n readonly level: TestQueryLevel = 2;\n readonly isMultiFile: false = false;\n readonly isMultiTest: boolean = true;\n readonly testPathParts: readonly string[];\n\n constructor(suite: string, file: readonly string[], test: readonly string[]) {\n super(suite, file);\n assert(file.length > 0, 'multi-test (or finer) query must have file-path');\n this.testPathParts = [...test];\n }\n\n get depthInLevel() {\n return this.testPathParts.length;\n }\n\n protected toStringHelper(): string[] {\n return [\n this.suite,\n this.filePathParts.join(kPathSeparator),\n [...this.testPathParts, kWildcard].join(kPathSeparator),\n ];\n }\n}\n\n/**\n * A multi-case test query, like `s:f:t:*` or `s:f:t:a,b,*`.\n *\n * Immutable (makes copies of constructor args), except for param values\n * (which aren't normally supposed to change; they're marked readonly in TestParams).\n */\nexport class TestQueryMultiCase extends TestQueryMultiTest {\n readonly level: TestQueryLevel = 3;\n readonly isMultiTest: false = false;\n readonly isMultiCase: boolean = true;\n readonly params: TestParams;\n\n constructor(suite: string, file: readonly string[], test: readonly string[], params: TestParams) {\n super(suite, file, test);\n assert(test.length > 0, 'multi-case (or finer) query must have test-path');\n this.params = { ...params };\n }\n\n get depthInLevel() {\n return Object.keys(this.params).length;\n }\n\n protected toStringHelper(): string[] {\n return [\n this.suite,\n this.filePathParts.join(kPathSeparator),\n this.testPathParts.join(kPathSeparator),\n stringifyPublicParams(this.params, true),\n ];\n }\n}\n\n/**\n * A multi-case test query, like `s:f:t:` or `s:f:t:a=1,b=1`.\n *\n * Immutable (makes copies of constructor args).\n */\nexport class TestQuerySingleCase extends TestQueryMultiCase {\n readonly level: TestQueryLevel = 4;\n readonly isMultiCase: false = false;\n\n get depthInLevel() {\n return 0;\n }\n\n protected toStringHelper(): string[] {\n return [\n this.suite,\n this.filePathParts.join(kPathSeparator),\n this.testPathParts.join(kPathSeparator),\n stringifyPublicParams(this.params),\n ];\n }\n}\n\n/**\n * Parse raw expectations input into TestQueryWithExpectation[], filtering so that only\n * expectations that are relevant for the provided query and wptURL.\n *\n * `rawExpectations` should be @type {{ query: string, expectation: Expectation }[]}\n *\n * The `rawExpectations` are parsed and validated that they are in the correct format.\n * If `wptURL` is passed, the query string should be of the full path format such\n * as `path/to/cts.https.html?worker=0&q=suite:test_path:test_name:foo=1;bar=2;*`.\n * If `wptURL` is `undefined`, the query string should be only the query\n * `suite:test_path:test_name:foo=1;bar=2;*`.\n */\nexport function parseExpectationsForTestQuery(\n rawExpectations:\n | unknown\n | {\n query: string;\n expectation: Expectation;\n }[],\n query: TestQuery,\n wptURL?: URL\n) {\n if (!Array.isArray(rawExpectations)) {\n unreachable('Expectations should be an array');\n }\n const expectations: TestQueryWithExpectation[] = [];\n for (const entry of rawExpectations) {\n assert(typeof entry === 'object');\n const rawExpectation = entry as { query?: string; expectation?: string };\n assert(rawExpectation.query !== undefined, 'Expectation missing query string');\n assert(rawExpectation.expectation !== undefined, 'Expectation missing expectation string');\n\n let expectationQuery: TestQuery;\n if (wptURL !== undefined) {\n const expectationURL = new URL(`${wptURL.origin}/${entry.query}`);\n if (expectationURL.pathname !== wptURL.pathname) {\n continue;\n }\n assert(\n expectationURL.pathname === wptURL.pathname,\n `Invalid expectation path ${expectationURL.pathname}\nExpectation should be of the form path/to/cts.https.html?worker=0&q=suite:test_path:test_name:foo=1;bar=2;...\n `\n );\n\n const params = expectationURL.searchParams;\n if (optionEnabled('worker', params) !== optionEnabled('worker', wptURL.searchParams)) {\n continue;\n }\n\n const qs = params.getAll('q');\n assert(qs.length === 1, 'currently, there must be exactly one ?q= in the expectation string');\n expectationQuery = parseQuery(qs[0]);\n } else {\n expectationQuery = parseQuery(entry.query);\n }\n\n // Strip params from multicase expectations so that an expectation of foo=2;*\n // is stored if the test query is bar=3;*\n const queryForFilter =\n expectationQuery instanceof TestQueryMultiCase\n ? new TestQueryMultiCase(\n expectationQuery.suite,\n expectationQuery.filePathParts,\n expectationQuery.testPathParts,\n {}\n )\n : expectationQuery;\n\n if (compareQueries(query, queryForFilter) === Ordering.Unordered) {\n continue;\n }\n\n switch (entry.expectation) {\n case 'pass':\n case 'skip':\n case 'fail':\n break;\n default:\n unreachable(`Invalid expectation ${entry.expectation}`);\n }\n\n expectations.push({\n query: expectationQuery,\n expectation: entry.expectation,\n });\n }\n return expectations;\n}\n\n/**\n * For display purposes only, produces a \"relative\" query string from parent to child.\n * Used in the wpt runtime to reduce the verbosity of logs.\n */\nexport function relativeQueryString(parent: TestQuery, child: TestQuery): string {\n const ordering = compareQueries(parent, child);\n if (ordering === Ordering.Equal) {\n return '';\n } else if (ordering === Ordering.StrictSuperset) {\n const parentString = parent.toString();\n assert(parentString.endsWith(kWildcard));\n const childString = child.toString();\n assert(\n childString.startsWith(parentString.substring(0, parentString.length - 2)),\n 'impossible?: childString does not start with parentString[:-2]'\n );\n return childString.substring(parentString.length - 2);\n } else {\n unreachable(\n `relativeQueryString arguments have invalid ordering ${ordering}:\\n${parent}\\n${child}`\n );\n }\n}\n"],"mappings":"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"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/separators.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/separators.js
new file mode 100644
index 0000000000..cf0c130583
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/separators.js
@@ -0,0 +1,15 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/ /** Separator between big parts: suite:file:test:case */export const kBigSeparator = ':';
+/** Separator between path,to,file or path,to,test */
+export const kPathSeparator = ',';
+
+/** Separator between k=v;k=v */
+export const kParamSeparator = ';';
+
+/** Separator between key and value in k=v */
+export const kParamKVSeparator = '=';
+
+/** Final wildcard, if query is not single-case */
+export const kWildcard = '*';
+//# sourceMappingURL=separators.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/separators.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/separators.js.map
new file mode 100644
index 0000000000..df7bd0c9ea
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/separators.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"separators.js","names":["kBigSeparator","kPathSeparator","kParamSeparator","kParamKVSeparator","kWildcard"],"sources":["../../../../src/common/internal/query/separators.ts"],"sourcesContent":["/** Separator between big parts: suite:file:test:case */\nexport const kBigSeparator = ':';\n\n/** Separator between path,to,file or path,to,test */\nexport const kPathSeparator = ',';\n\n/** Separator between k=v;k=v */\nexport const kParamSeparator = ';';\n\n/** Separator between key and value in k=v */\nexport const kParamKVSeparator = '=';\n\n/** Final wildcard, if query is not single-case */\nexport const kWildcard = '*';\n"],"mappings":";AAAA;AAAA,G,CAAA,wDACA,OAAO,MAAMA,aAAa,GAAG,GAAG;AAEhC;AACA,OAAO,MAAMC,cAAc,GAAG,GAAG;;AAEjC;AACA,OAAO,MAAMC,eAAe,GAAG,GAAG;;AAElC;AACA,OAAO,MAAMC,iBAAiB,GAAG,GAAG;;AAEpC;AACA,OAAO,MAAMC,SAAS,GAAG,GAAG"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/stringify_params.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/stringify_params.js
new file mode 100644
index 0000000000..014f502cd4
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/stringify_params.js
@@ -0,0 +1,45 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { assert } from '../../util/util.js';import { badParamValueChars, paramKeyIsPublic } from '../params_utils.js';
+
+import { stringifyParamValue, stringifyParamValueUniquely } from './json_param_value.js';
+import { kParamKVSeparator, kParamSeparator, kWildcard } from './separators.js';
+
+export function stringifyPublicParams(p, addWildcard = false) {
+ const parts = Object.keys(p).
+ filter((k) => paramKeyIsPublic(k)).
+ map((k) => stringifySingleParam(k, p[k]));
+
+ if (addWildcard) parts.push(kWildcard);
+
+ return parts.join(kParamSeparator);
+}
+
+/**
+ * An _approximately_ unique string representing a CaseParams value.
+ */
+export function stringifyPublicParamsUniquely(p) {
+ const keys = Object.keys(p).sort();
+ return keys.
+ filter((k) => paramKeyIsPublic(k)).
+ map((k) => stringifySingleParamUniquely(k, p[k])).
+ join(kParamSeparator);
+}
+
+export function stringifySingleParam(k, v) {
+ return `${k}${kParamKVSeparator}${stringifySingleParamValue(v)}`;
+}
+
+function stringifySingleParamUniquely(k, v) {
+ return `${k}${kParamKVSeparator}${stringifyParamValueUniquely(v)}`;
+}
+
+function stringifySingleParamValue(v) {
+ const s = stringifyParamValue(v);
+ assert(
+ !badParamValueChars.test(s),
+ `JSON.stringified param value must not match ${badParamValueChars} - was ${s}`);
+
+ return s;
+}
+//# sourceMappingURL=stringify_params.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/stringify_params.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/stringify_params.js.map
new file mode 100644
index 0000000000..60e963e6cb
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/stringify_params.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"stringify_params.js","names":["assert","badParamValueChars","paramKeyIsPublic","stringifyParamValue","stringifyParamValueUniquely","kParamKVSeparator","kParamSeparator","kWildcard","stringifyPublicParams","p","addWildcard","parts","Object","keys","filter","k","map","stringifySingleParam","push","join","stringifyPublicParamsUniquely","sort","stringifySingleParamUniquely","v","stringifySingleParamValue","s","test"],"sources":["../../../../src/common/internal/query/stringify_params.ts"],"sourcesContent":["import { TestParams } from '../../framework/fixture.js';\nimport { assert } from '../../util/util.js';\nimport { JSONWithUndefined, badParamValueChars, paramKeyIsPublic } from '../params_utils.js';\n\nimport { stringifyParamValue, stringifyParamValueUniquely } from './json_param_value.js';\nimport { kParamKVSeparator, kParamSeparator, kWildcard } from './separators.js';\n\nexport function stringifyPublicParams(p: TestParams, addWildcard = false): string {\n const parts = Object.keys(p)\n .filter(k => paramKeyIsPublic(k))\n .map(k => stringifySingleParam(k, p[k]));\n\n if (addWildcard) parts.push(kWildcard);\n\n return parts.join(kParamSeparator);\n}\n\n/**\n * An _approximately_ unique string representing a CaseParams value.\n */\nexport function stringifyPublicParamsUniquely(p: TestParams): string {\n const keys = Object.keys(p).sort();\n return keys\n .filter(k => paramKeyIsPublic(k))\n .map(k => stringifySingleParamUniquely(k, p[k]))\n .join(kParamSeparator);\n}\n\nexport function stringifySingleParam(k: string, v: JSONWithUndefined) {\n return `${k}${kParamKVSeparator}${stringifySingleParamValue(v)}`;\n}\n\nfunction stringifySingleParamUniquely(k: string, v: JSONWithUndefined) {\n return `${k}${kParamKVSeparator}${stringifyParamValueUniquely(v)}`;\n}\n\nfunction stringifySingleParamValue(v: JSONWithUndefined): string {\n const s = stringifyParamValue(v);\n assert(\n !badParamValueChars.test(s),\n `JSON.stringified param value must not match ${badParamValueChars} - was ${s}`\n );\n return s;\n}\n"],"mappings":";AAAA;AAAA,GACA,SAASA,MAAM,QAAQ,oBAAoB,CAC3C,SAA4BC,kBAAkB,EAAEC,gBAAgB,QAAQ,oBAAoB;;AAE5F,SAASC,mBAAmB,EAAEC,2BAA2B,QAAQ,uBAAuB;AACxF,SAASC,iBAAiB,EAAEC,eAAe,EAAEC,SAAS,QAAQ,iBAAiB;;AAE/E,OAAO,SAASC,qBAAqB,CAACC,CAAa,EAAEC,WAAW,GAAG,KAAK,EAAU;EAChF,MAAMC,KAAK,GAAGC,MAAM,CAACC,IAAI,CAACJ,CAAC,CAAC;EACzBK,MAAM,CAAC,CAAAC,CAAC,KAAIb,gBAAgB,CAACa,CAAC,CAAC,CAAC;EAChCC,GAAG,CAAC,CAAAD,CAAC,KAAIE,oBAAoB,CAACF,CAAC,EAAEN,CAAC,CAACM,CAAC,CAAC,CAAC,CAAC;;EAE1C,IAAIL,WAAW,EAAEC,KAAK,CAACO,IAAI,CAACX,SAAS,CAAC;;EAEtC,OAAOI,KAAK,CAACQ,IAAI,CAACb,eAAe,CAAC;AACpC;;AAEA;AACA;AACA;AACA,OAAO,SAASc,6BAA6B,CAACX,CAAa,EAAU;EACnE,MAAMI,IAAI,GAAGD,MAAM,CAACC,IAAI,CAACJ,CAAC,CAAC,CAACY,IAAI,EAAE;EAClC,OAAOR,IAAI;EACRC,MAAM,CAAC,CAAAC,CAAC,KAAIb,gBAAgB,CAACa,CAAC,CAAC,CAAC;EAChCC,GAAG,CAAC,CAAAD,CAAC,KAAIO,4BAA4B,CAACP,CAAC,EAAEN,CAAC,CAACM,CAAC,CAAC,CAAC,CAAC;EAC/CI,IAAI,CAACb,eAAe,CAAC;AAC1B;;AAEA,OAAO,SAASW,oBAAoB,CAACF,CAAS,EAAEQ,CAAoB,EAAE;EACpE,OAAQ,GAAER,CAAE,GAAEV,iBAAkB,GAAEmB,yBAAyB,CAACD,CAAC,CAAE,EAAC;AAClE;;AAEA,SAASD,4BAA4B,CAACP,CAAS,EAAEQ,CAAoB,EAAE;EACrE,OAAQ,GAAER,CAAE,GAAEV,iBAAkB,GAAED,2BAA2B,CAACmB,CAAC,CAAE,EAAC;AACpE;;AAEA,SAASC,yBAAyB,CAACD,CAAoB,EAAU;EAC/D,MAAME,CAAC,GAAGtB,mBAAmB,CAACoB,CAAC,CAAC;EAChCvB,MAAM;EACJ,CAACC,kBAAkB,CAACyB,IAAI,CAACD,CAAC,CAAC;EAC1B,+CAA8CxB,kBAAmB,UAASwB,CAAE,EAAC,CAC/E;;EACD,OAAOA,CAAC;AACV"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/validQueryPart.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/validQueryPart.js
new file mode 100644
index 0000000000..9fa7135cc5
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/validQueryPart.js
@@ -0,0 +1,4 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/ /** Applies to group parts, test parts, params keys. */export const validQueryPart = /^[a-zA-Z0-9_]+$/;
+//# sourceMappingURL=validQueryPart.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/query/validQueryPart.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/validQueryPart.js.map
new file mode 100644
index 0000000000..3bd6e8e82b
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/query/validQueryPart.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"validQueryPart.js","names":["validQueryPart"],"sources":["../../../../src/common/internal/query/validQueryPart.ts"],"sourcesContent":["/** Applies to group parts, test parts, params keys. */\nexport const validQueryPart = /^[a-zA-Z0-9_]+$/;\n"],"mappings":";AAAA;AAAA,G,CAAA,uDACA,OAAO,MAAMA,cAAc,GAAG,iBAAiB"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/stack.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/stack.js
new file mode 100644
index 0000000000..bff671d204
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/stack.js
@@ -0,0 +1,83 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/ // Returns the stack trace of an Error, but without the extra boilerplate at the bottom
+// (e.g. RunCaseSpecific, processTicksAndRejections, etc.), for logging.
+export function extractImportantStackTrace(e) {let stack = e.stack;if (!stack) {
+ return '';
+ }
+ const redundantMessage = 'Error: ' + e.message + '\n';
+ if (stack.startsWith(redundantMessage)) {
+ stack = stack.substring(redundantMessage.length);
+ }
+
+ const lines = stack.split('\n');
+ for (let i = lines.length - 1; i >= 0; --i) {
+ const line = lines[i];
+ if (line.indexOf('.spec.') !== -1) {
+ return lines.slice(0, i + 1).join('\n');
+ }
+ }
+ return stack;
+}
+
+// *** Examples ***
+//
+// Node fail()
+// > Error:
+// > at CaseRecorder.fail (/Users/kainino/src/cts/src/common/framework/logger.ts:99:30)
+// > at RunCaseSpecific.exports.g.test.t [as fn] (/Users/kainino/src/cts/src/unittests/logger.spec.ts:80:7)
+// x at RunCaseSpecific.run (/Users/kainino/src/cts/src/common/framework/test_group.ts:121:18)
+// x at processTicksAndRejections (internal/process/task_queues.js:86:5)
+//
+// Node throw
+// > Error: hello
+// > at RunCaseSpecific.g.test.t [as fn] (/Users/kainino/src/cts/src/unittests/test_group.spec.ts:51:11)
+// x at RunCaseSpecific.run (/Users/kainino/src/cts/src/common/framework/test_group.ts:121:18)
+// x at processTicksAndRejections (internal/process/task_queues.js:86:5)
+//
+// Firefox fail()
+// > fail@http://localhost:8080/out/framework/logger.js:104:30
+// > expect@http://localhost:8080/out/framework/default_fixture.js:59:16
+// > @http://localhost:8080/out/unittests/util.spec.js:35:5
+// x run@http://localhost:8080/out/framework/test_group.js:119:18
+//
+// Firefox throw
+// > @http://localhost:8080/out/unittests/test_group.spec.js:48:11
+// x run@http://localhost:8080/out/framework/test_group.js:119:18
+//
+// Safari fail()
+// > fail@http://localhost:8080/out/framework/logger.js:104:39
+// > expect@http://localhost:8080/out/framework/default_fixture.js:59:20
+// > http://localhost:8080/out/unittests/util.spec.js:35:11
+// x http://localhost:8080/out/framework/test_group.js:119:20
+// x asyncFunctionResume@[native code]
+// x [native code]
+// x promiseReactionJob@[native code]
+//
+// Safari throw
+// > http://localhost:8080/out/unittests/test_group.spec.js:48:20
+// x http://localhost:8080/out/framework/test_group.js:119:20
+// x asyncFunctionResume@[native code]
+// x [native code]
+// x promiseReactionJob@[native code]
+//
+// Chrome fail()
+// x Error
+// x at CaseRecorder.fail (http://localhost:8080/out/framework/logger.js:104:30)
+// x at DefaultFixture.expect (http://localhost:8080/out/framework/default_fixture.js:59:16)
+// > at RunCaseSpecific.fn (http://localhost:8080/out/unittests/util.spec.js:35:5)
+// x at RunCaseSpecific.run (http://localhost:8080/out/framework/test_group.js:119:18)
+// x at async runCase (http://localhost:8080/out/runtime/standalone.js:37:17)
+// x at async http://localhost:8080/out/runtime/standalone.js:102:7
+//
+// Chrome throw
+// x Error: hello
+// > at RunCaseSpecific.fn (http://localhost:8080/out/unittests/test_group.spec.js:48:11)
+// x at RunCaseSpecific.run (http://localhost:8080/out/framework/test_group.js:119:18)"
+// x at async Promise.all (index 0)
+// x at async TestGroupTest.run (http://localhost:8080/out/unittests/test_group_test.js:6:5)
+// x at async RunCaseSpecific.fn (http://localhost:8080/out/unittests/test_group.spec.js:53:15)
+// x at async RunCaseSpecific.run (http://localhost:8080/out/framework/test_group.js:119:7)
+// x at async runCase (http://localhost:8080/out/runtime/standalone.js:37:17)
+// x at async http://localhost:8080/out/runtime/standalone.js:102:7
+//# sourceMappingURL=stack.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/stack.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/stack.js.map
new file mode 100644
index 0000000000..399d289119
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/stack.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"stack.js","names":["extractImportantStackTrace","e","stack","redundantMessage","message","startsWith","substring","length","lines","split","i","line","indexOf","slice","join"],"sources":["../../../src/common/internal/stack.ts"],"sourcesContent":["// Returns the stack trace of an Error, but without the extra boilerplate at the bottom\n// (e.g. RunCaseSpecific, processTicksAndRejections, etc.), for logging.\nexport function extractImportantStackTrace(e: Error): string {\n let stack = e.stack;\n if (!stack) {\n return '';\n }\n const redundantMessage = 'Error: ' + e.message + '\\n';\n if (stack.startsWith(redundantMessage)) {\n stack = stack.substring(redundantMessage.length);\n }\n\n const lines = stack.split('\\n');\n for (let i = lines.length - 1; i >= 0; --i) {\n const line = lines[i];\n if (line.indexOf('.spec.') !== -1) {\n return lines.slice(0, i + 1).join('\\n');\n }\n }\n return stack;\n}\n\n// *** Examples ***\n//\n// Node fail()\n// > Error:\n// > at CaseRecorder.fail (/Users/kainino/src/cts/src/common/framework/logger.ts:99:30)\n// > at RunCaseSpecific.exports.g.test.t [as fn] (/Users/kainino/src/cts/src/unittests/logger.spec.ts:80:7)\n// x at RunCaseSpecific.run (/Users/kainino/src/cts/src/common/framework/test_group.ts:121:18)\n// x at processTicksAndRejections (internal/process/task_queues.js:86:5)\n//\n// Node throw\n// > Error: hello\n// > at RunCaseSpecific.g.test.t [as fn] (/Users/kainino/src/cts/src/unittests/test_group.spec.ts:51:11)\n// x at RunCaseSpecific.run (/Users/kainino/src/cts/src/common/framework/test_group.ts:121:18)\n// x at processTicksAndRejections (internal/process/task_queues.js:86:5)\n//\n// Firefox fail()\n// > fail@http://localhost:8080/out/framework/logger.js:104:30\n// > expect@http://localhost:8080/out/framework/default_fixture.js:59:16\n// > @http://localhost:8080/out/unittests/util.spec.js:35:5\n// x run@http://localhost:8080/out/framework/test_group.js:119:18\n//\n// Firefox throw\n// > @http://localhost:8080/out/unittests/test_group.spec.js:48:11\n// x run@http://localhost:8080/out/framework/test_group.js:119:18\n//\n// Safari fail()\n// > fail@http://localhost:8080/out/framework/logger.js:104:39\n// > expect@http://localhost:8080/out/framework/default_fixture.js:59:20\n// > http://localhost:8080/out/unittests/util.spec.js:35:11\n// x http://localhost:8080/out/framework/test_group.js:119:20\n// x asyncFunctionResume@[native code]\n// x [native code]\n// x promiseReactionJob@[native code]\n//\n// Safari throw\n// > http://localhost:8080/out/unittests/test_group.spec.js:48:20\n// x http://localhost:8080/out/framework/test_group.js:119:20\n// x asyncFunctionResume@[native code]\n// x [native code]\n// x promiseReactionJob@[native code]\n//\n// Chrome fail()\n// x Error\n// x at CaseRecorder.fail (http://localhost:8080/out/framework/logger.js:104:30)\n// x at DefaultFixture.expect (http://localhost:8080/out/framework/default_fixture.js:59:16)\n// > at RunCaseSpecific.fn (http://localhost:8080/out/unittests/util.spec.js:35:5)\n// x at RunCaseSpecific.run (http://localhost:8080/out/framework/test_group.js:119:18)\n// x at async runCase (http://localhost:8080/out/runtime/standalone.js:37:17)\n// x at async http://localhost:8080/out/runtime/standalone.js:102:7\n//\n// Chrome throw\n// x Error: hello\n// > at RunCaseSpecific.fn (http://localhost:8080/out/unittests/test_group.spec.js:48:11)\n// x at RunCaseSpecific.run (http://localhost:8080/out/framework/test_group.js:119:18)\"\n// x at async Promise.all (index 0)\n// x at async TestGroupTest.run (http://localhost:8080/out/unittests/test_group_test.js:6:5)\n// x at async RunCaseSpecific.fn (http://localhost:8080/out/unittests/test_group.spec.js:53:15)\n// x at async RunCaseSpecific.run (http://localhost:8080/out/framework/test_group.js:119:7)\n// x at async runCase (http://localhost:8080/out/runtime/standalone.js:37:17)\n// x at async http://localhost:8080/out/runtime/standalone.js:102:7\n"],"mappings":";AAAA;AAAA,G,CAAA;AACA;AACA,OAAO,SAASA,0BAA0B,CAACC,CAAQ,EAAU,CAC3D,IAAIC,KAAK,GAAGD,CAAC,CAACC,KAAK,CACnB,IAAI,CAACA,KAAK,EAAE;IACV,OAAO,EAAE;EACX;EACA,MAAMC,gBAAgB,GAAG,SAAS,GAAGF,CAAC,CAACG,OAAO,GAAG,IAAI;EACrD,IAAIF,KAAK,CAACG,UAAU,CAACF,gBAAgB,CAAC,EAAE;IACtCD,KAAK,GAAGA,KAAK,CAACI,SAAS,CAACH,gBAAgB,CAACI,MAAM,CAAC;EAClD;;EAEA,MAAMC,KAAK,GAAGN,KAAK,CAACO,KAAK,CAAC,IAAI,CAAC;EAC/B,KAAK,IAAIC,CAAC,GAAGF,KAAK,CAACD,MAAM,GAAG,CAAC,EAAEG,CAAC,IAAI,CAAC,EAAE,EAAEA,CAAC,EAAE;IAC1C,MAAMC,IAAI,GAAGH,KAAK,CAACE,CAAC,CAAC;IACrB,IAAIC,IAAI,CAACC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE;MACjC,OAAOJ,KAAK,CAACK,KAAK,CAAC,CAAC,EAAEH,CAAC,GAAG,CAAC,CAAC,CAACI,IAAI,CAAC,IAAI,CAAC;IACzC;EACF;EACA,OAAOZ,KAAK;AACd;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/test_group.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/test_group.js
new file mode 100644
index 0000000000..74eb775c60
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/test_group.js
@@ -0,0 +1,647 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import {
+SkipTestCase,
+
+UnexpectedPassError } from
+'../framework/fixture.js';
+import {
+
+builderIterateCasesWithSubcases,
+kUnitCaseParamsBuilder } from
+
+
+'../framework/params_builder.js';
+import { globalTestConfig } from '../framework/test_config.js';
+
+import { TestCaseRecorder } from '../internal/logging/test_case_recorder.js';
+import { extractPublicParams, mergeParams } from '../internal/params_utils.js';
+import { compareQueries, Ordering } from '../internal/query/compare.js';
+import { TestQuerySingleCase } from '../internal/query/query.js';
+import { kPathSeparator } from '../internal/query/separators.js';
+import {
+stringifyPublicParams,
+stringifyPublicParamsUniquely } from
+'../internal/query/stringify_params.js';
+import { validQueryPart } from '../internal/query/validQueryPart.js';
+import { assert, unreachable } from '../util/util.js';
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+export function makeTestGroup(
+fixture)
+{
+ return new TestGroup(fixture);
+}
+
+// Interfaces for running tests
+
+
+
+
+
+
+
+
+
+
+
+export function makeTestGroupForUnitTesting(
+fixture)
+{
+ return new TestGroup(fixture);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+export class TestGroup
+{
+
+ seen = new Set();
+ tests = [];
+
+ constructor(fixture) {
+ this.fixture = fixture;
+ }
+
+ iterate() {
+ return this.tests;
+ }
+
+ checkName(name) {
+ assert(
+ // Shouldn't happen due to the rule above. Just makes sure that treating
+ // unencoded strings as encoded strings is OK.
+ name === decodeURIComponent(name),
+ `Not decodeURIComponent-idempotent: ${name} !== ${decodeURIComponent(name)}`);
+
+ assert(!this.seen.has(name), `Duplicate test name: ${name}`);
+
+ this.seen.add(name);
+ }
+
+ test(name) {
+ const testCreationStack = new Error(`Test created: ${name}`);
+
+ this.checkName(name);
+
+ const parts = name.split(kPathSeparator);
+ for (const p of parts) {
+ assert(validQueryPart.test(p), `Invalid test name part ${p}; must match ${validQueryPart}`);
+ }
+
+ const test = new TestBuilder(parts, this.fixture, testCreationStack);
+ this.tests.push(test);
+ return test;
+ }
+
+ validate() {
+ for (const test of this.tests) {
+ test.validate();
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+class TestBuilder {
+
+
+
+
+
+
+
+
+ testCases = undefined;
+ batchSize = 0;
+
+ constructor(testPath, fixture, testCreationStack) {
+ this.testPath = testPath;
+ this.isUnimplemented = false;
+ this.fixture = fixture;
+ this.testCreationStack = testCreationStack;
+ }
+
+ desc(description) {
+ this.description = description.trim();
+ return this;
+ }
+
+ specURL(url) {
+ return this;
+ }
+
+ beforeAllSubcases(fn) {
+ assert(this.beforeFn === undefined);
+ this.beforeFn = fn;
+ return this;
+ }
+
+ fn(fn) {
+
+ // MAINTENANCE_TODO: add "TODO" if there's no description? (and make sure it only ends up on
+ // actual tests, not on test parents in the tree, which is what happens if you do it here, not
+ // sure why)
+ assert(this.testFn === undefined);
+ this.testFn = fn;
+ }
+
+ batch(b) {
+ this.batchSize = b;
+ return this;
+ }
+
+ unimplemented() {
+ assert(this.testFn === undefined);
+
+ this.description =
+ (this.description ? this.description + '\n\n' : '') + 'TODO: .unimplemented()';
+ this.isUnimplemented = true;
+
+ this.testFn = () => {
+ throw new SkipTestCase('test unimplemented');
+ };
+ }
+
+ validate() {
+ const testPathString = this.testPath.join(kPathSeparator);
+ assert(this.testFn !== undefined, () => {
+ let s = `Test is missing .fn(): ${testPathString}`;
+ if (this.testCreationStack.stack) {
+ s += `\n-> test created at:\n${this.testCreationStack.stack}`;
+ }
+ return s;
+ });
+
+ if (this.testCases === undefined) {
+ return;
+ }
+
+ const seen = new Set();
+ for (const [caseParams, subcases] of builderIterateCasesWithSubcases(this.testCases)) {
+ for (const subcaseParams of subcases ?? [{}]) {
+ const params = mergeParams(caseParams, subcaseParams);
+ assert(this.batchSize === 0 || !('batch__' in params));
+
+ // stringifyPublicParams also checks for invalid params values
+ const testcaseString = stringifyPublicParams(params);
+
+ // A (hopefully) unique representation of a params value.
+ const testcaseStringUnique = stringifyPublicParamsUniquely(params);
+ assert(
+ !seen.has(testcaseStringUnique),
+ `Duplicate public test case params for test ${testPathString}: ${testcaseString}`);
+
+ seen.add(testcaseStringUnique);
+ }
+ }
+ }
+
+ params(
+ cases)
+ {
+ assert(this.testCases === undefined, 'test case is already parameterized');
+ if (cases instanceof Function) {
+ this.testCases = cases(kUnitCaseParamsBuilder);
+ } else {
+ this.testCases = cases;
+ }
+ return this;
+ }
+
+ paramsSimple(cases) {
+ assert(this.testCases === undefined, 'test case is already parameterized');
+ this.testCases = kUnitCaseParamsBuilder.combineWithParams(cases);
+ return this;
+ }
+
+ paramsSubcasesOnly(
+ subcases)
+ {
+ if (subcases instanceof Function) {
+ return this.params(subcases(kUnitCaseParamsBuilder.beginSubcases()));
+ } else {
+ return this.params(kUnitCaseParamsBuilder.beginSubcases().combineWithParams(subcases));
+ }
+ }
+
+ *iterate() {
+ assert(this.testFn !== undefined, 'No test function (.fn()) for test');
+ this.testCases ??= kUnitCaseParamsBuilder;
+ for (const [caseParams, subcases] of builderIterateCasesWithSubcases(this.testCases)) {
+ if (this.batchSize === 0 || subcases === undefined) {
+ yield new RunCaseSpecific(
+ this.testPath,
+ caseParams,
+ this.isUnimplemented,
+ subcases,
+ this.fixture,
+ this.testFn,
+ this.beforeFn,
+ this.testCreationStack);
+
+ } else {
+ const subcaseArray = Array.from(subcases);
+ if (subcaseArray.length <= this.batchSize) {
+ yield new RunCaseSpecific(
+ this.testPath,
+ caseParams,
+ this.isUnimplemented,
+ subcaseArray,
+ this.fixture,
+ this.testFn,
+ this.beforeFn,
+ this.testCreationStack);
+
+ } else {
+ for (let i = 0; i < subcaseArray.length; i = i + this.batchSize) {
+ yield new RunCaseSpecific(
+ this.testPath,
+ { ...caseParams, batch__: i / this.batchSize },
+ this.isUnimplemented,
+ subcaseArray.slice(i, Math.min(subcaseArray.length, i + this.batchSize)),
+ this.fixture,
+ this.testFn,
+ this.beforeFn,
+ this.testCreationStack);
+
+ }
+ }
+ }
+ }
+ }
+}
+
+class RunCaseSpecific {
+
+
+
+
+
+
+
+
+
+
+ constructor(
+ testPath,
+ params,
+ isUnimplemented,
+ subcases,
+ fixture,
+ fn,
+ beforeFn,
+ testCreationStack)
+ {
+ this.id = { test: testPath, params: extractPublicParams(params) };
+ this.isUnimplemented = isUnimplemented;
+ this.params = params;
+ this.subcases = subcases;
+ this.fixture = fixture;
+ this.fn = fn;
+ this.beforeFn = beforeFn;
+ this.testCreationStack = testCreationStack;
+ }
+
+ async runTest(
+ rec,
+ sharedState,
+ params,
+ throwSkip,
+ expectedStatus)
+ {
+ try {
+ rec.beginSubCase();
+ if (expectedStatus === 'skip') {
+ throw new SkipTestCase('Skipped by expectations');
+ }
+
+ const inst = new this.fixture(sharedState, rec, params);
+ try {
+ await inst.init();
+ await this.fn(inst);
+ } finally {
+ // Runs as long as constructor succeeded, even if initialization or the test failed.
+ await inst.finalize();
+ }
+ } catch (ex) {
+ // There was an exception from constructor, init, test, or finalize.
+ // An error from init or test may have been a SkipTestCase.
+ // An error from finalize may have been an eventualAsyncExpectation failure
+ // or unexpected validation/OOM error from the GPUDevice.
+ if (throwSkip && ex instanceof SkipTestCase) {
+ throw ex;
+ }
+ rec.threw(ex);
+ } finally {
+ try {
+ rec.endSubCase(expectedStatus);
+ } catch (ex) {
+ assert(ex instanceof UnexpectedPassError);
+ ex.message = `Testcase passed unexpectedly.`;
+ ex.stack = this.testCreationStack.stack;
+ rec.warn(ex);
+ }
+ }
+ }
+
+ async run(
+ rec,
+ selfQuery,
+ expectations)
+ {
+ const getExpectedStatus = (selfQueryWithSubParams) => {
+ let didSeeFail = false;
+ for (const exp of expectations) {
+ const ordering = compareQueries(exp.query, selfQueryWithSubParams);
+ if (ordering === Ordering.Unordered || ordering === Ordering.StrictSubset) {
+ continue;
+ }
+
+ switch (exp.expectation) {
+ // Skip takes precedence. If there is any expectation indicating a skip,
+ // signal it immediately.
+ case 'skip':
+ return 'skip';
+ case 'fail':
+ // Otherwise, indicate that we might expect a failure.
+ didSeeFail = true;
+ break;
+ default:
+ unreachable();}
+
+ }
+ return didSeeFail ? 'fail' : 'pass';
+ };
+
+ const { testHeartbeatCallback, maxSubcasesInFlight } = globalTestConfig;
+ try {
+ rec.start();
+ const sharedState = this.fixture.MakeSharedState(this.params);
+ try {
+ await sharedState.init();
+ if (this.beforeFn) {
+ await this.beforeFn(sharedState);
+ }
+ await sharedState.postInit();
+ testHeartbeatCallback();
+
+ let allPreviousSubcasesFinalizedPromise = Promise.resolve();
+ if (this.subcases) {
+ let totalCount = 0;
+ let skipCount = 0;
+
+ // If there are too many subcases in flight, starting the next subcase will register
+ // `resolvePromiseBlockingSubcase` and wait until `subcaseFinishedCallback` is called.
+ let subcasesInFlight = 0;
+ let resolvePromiseBlockingSubcase = undefined;
+ const subcaseFinishedCallback = () => {
+ subcasesInFlight -= 1;
+ // If there is any subcase waiting on a previous subcase to finish,
+ // unblock it now, and clear the resolve callback.
+ if (resolvePromiseBlockingSubcase) {
+ resolvePromiseBlockingSubcase();
+ resolvePromiseBlockingSubcase = undefined;
+ }
+ };
+
+ for (const subParams of this.subcases) {
+ // Make a recorder that will defer all calls until `allPreviousSubcasesFinalizedPromise`
+ // resolves. Waiting on `allPreviousSubcasesFinalizedPromise` ensures that
+ // logs from all the previous subcases have been flushed before flushing new logs.
+ const subcasePrefix = 'subcase: ' + stringifyPublicParams(subParams);
+ const subRec = new Proxy(rec, {
+ get: (target, k) => {
+ const prop = TestCaseRecorder.prototype[k];
+ if (typeof prop === 'function') {
+ testHeartbeatCallback();
+ return function (...args) {
+ void allPreviousSubcasesFinalizedPromise.then(() => {
+ // Prepend the subcase name to all error messages.
+ for (const arg of args) {
+ if (arg instanceof Error) {
+ try {
+ arg.message = subcasePrefix + '\n' + arg.message;
+ } catch {
+ // If that fails (e.g. on DOMException), try to put it in the stack:
+ let stack = subcasePrefix;
+ if (arg.stack) stack += '\n' + arg.stack;
+ try {
+ arg.stack = stack;
+ } catch {
+
+ // If that fails too, just silence it.
+ }}
+ }
+ }
+
+
+ const rv = prop.apply(target, args);
+ // Because this proxy executes functions in a deferred manner,
+ // it should never be used for functions that need to return a value.
+ assert(rv === undefined);
+ });
+ };
+ }
+ return prop;
+ }
+ });
+
+ const params = mergeParams(this.params, subParams);
+ const subcaseQuery = new TestQuerySingleCase(
+ selfQuery.suite,
+ selfQuery.filePathParts,
+ selfQuery.testPathParts,
+ params);
+
+
+ // Limit the maximum number of subcases in flight.
+ if (subcasesInFlight >= maxSubcasesInFlight) {
+ await new Promise((resolve) => {
+ // There should only be one subcase waiting at a time.
+ assert(resolvePromiseBlockingSubcase === undefined);
+ resolvePromiseBlockingSubcase = resolve;
+ });
+ }
+
+ subcasesInFlight += 1;
+ // Runs async without waiting so that subsequent subcases can start.
+ // All finalization steps will be waited on at the end of the testcase.
+ const finalizePromise = this.runTest(
+ subRec,
+ sharedState,
+ params,
+ /* throwSkip */true,
+ getExpectedStatus(subcaseQuery)).
+
+ then(() => {
+ subRec.info(new Error('OK'));
+ }).
+ catch((ex) => {
+ if (ex instanceof SkipTestCase) {
+ // Convert SkipTestCase to info messages
+ ex.message = 'subcase skipped: ' + ex.message;
+ subRec.info(ex);
+ ++skipCount;
+ } else {
+ // Since we are catching all error inside runTest(), this should never happen
+ subRec.threw(ex);
+ }
+ }).
+ finally(subcaseFinishedCallback);
+
+ allPreviousSubcasesFinalizedPromise = allPreviousSubcasesFinalizedPromise.then(
+ () => finalizePromise);
+
+ ++totalCount;
+ }
+
+ // Wait for all subcases to finalize and report their results.
+ await allPreviousSubcasesFinalizedPromise;
+
+ if (skipCount === totalCount) {
+ rec.skipped(new SkipTestCase('all subcases were skipped'));
+ }
+ } else {
+ await this.runTest(
+ rec,
+ sharedState,
+ this.params,
+ /* throwSkip */false,
+ getExpectedStatus(selfQuery));
+
+ }
+ } finally {
+ testHeartbeatCallback();
+ // Runs as long as the shared state constructor succeeded, even if initialization or a test failed.
+ await sharedState.finalize();
+ testHeartbeatCallback();
+ }
+ } catch (ex) {
+ // There was an exception from sharedState/fixture constructor, init, beforeFn, or test.
+ // An error from beforeFn may have been SkipTestCase.
+ // An error from finalize may have been an eventualAsyncExpectation failure
+ // or unexpected validation/OOM error from the GPUDevice.
+ rec.threw(ex);
+ } finally {
+ rec.finish();
+ }
+ }
+}
+//# sourceMappingURL=test_group.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/test_group.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/test_group.js.map
new file mode 100644
index 0000000000..f45df9e09c
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/test_group.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"test_group.js","names":["SkipTestCase","UnexpectedPassError","builderIterateCasesWithSubcases","kUnitCaseParamsBuilder","globalTestConfig","TestCaseRecorder","extractPublicParams","mergeParams","compareQueries","Ordering","TestQuerySingleCase","kPathSeparator","stringifyPublicParams","stringifyPublicParamsUniquely","validQueryPart","assert","unreachable","makeTestGroup","fixture","TestGroup","makeTestGroupForUnitTesting","seen","Set","tests","constructor","iterate","checkName","name","decodeURIComponent","has","add","test","testCreationStack","Error","parts","split","p","TestBuilder","push","validate","testCases","undefined","batchSize","testPath","isUnimplemented","desc","description","trim","specURL","url","beforeAllSubcases","fn","beforeFn","testFn","batch","b","unimplemented","testPathString","join","s","stack","caseParams","subcases","subcaseParams","params","testcaseString","testcaseStringUnique","cases","Function","paramsSimple","combineWithParams","paramsSubcasesOnly","beginSubcases","RunCaseSpecific","subcaseArray","Array","from","length","i","batch__","slice","Math","min","id","runTest","rec","sharedState","throwSkip","expectedStatus","beginSubCase","inst","init","finalize","ex","threw","endSubCase","message","warn","run","selfQuery","expectations","getExpectedStatus","selfQueryWithSubParams","didSeeFail","exp","ordering","query","Unordered","StrictSubset","expectation","testHeartbeatCallback","maxSubcasesInFlight","start","MakeSharedState","postInit","allPreviousSubcasesFinalizedPromise","Promise","resolve","totalCount","skipCount","subcasesInFlight","resolvePromiseBlockingSubcase","subcaseFinishedCallback","subParams","subcasePrefix","subRec","Proxy","get","target","k","prop","prototype","args","then","arg","rv","apply","subcaseQuery","suite","filePathParts","testPathParts","finalizePromise","info","catch","finally","skipped","finish"],"sources":["../../../src/common/internal/test_group.ts"],"sourcesContent":["import {\n Fixture,\n SubcaseBatchState,\n SkipTestCase,\n TestParams,\n UnexpectedPassError,\n} from '../framework/fixture.js';\nimport {\n CaseParamsBuilder,\n builderIterateCasesWithSubcases,\n kUnitCaseParamsBuilder,\n ParamsBuilderBase,\n SubcaseParamsBuilder,\n} from '../framework/params_builder.js';\nimport { globalTestConfig } from '../framework/test_config.js';\nimport { Expectation } from '../internal/logging/result.js';\nimport { TestCaseRecorder } from '../internal/logging/test_case_recorder.js';\nimport { extractPublicParams, Merged, mergeParams } from '../internal/params_utils.js';\nimport { compareQueries, Ordering } from '../internal/query/compare.js';\nimport { TestQuerySingleCase, TestQueryWithExpectation } from '../internal/query/query.js';\nimport { kPathSeparator } from '../internal/query/separators.js';\nimport {\n stringifyPublicParams,\n stringifyPublicParamsUniquely,\n} from '../internal/query/stringify_params.js';\nimport { validQueryPart } from '../internal/query/validQueryPart.js';\nimport { assert, unreachable } from '../util/util.js';\n\nexport type RunFn = (\n rec: TestCaseRecorder,\n expectations?: TestQueryWithExpectation[]\n) => Promise<void>;\n\nexport interface TestCaseID {\n readonly test: readonly string[];\n readonly params: TestParams;\n}\n\nexport interface RunCase {\n readonly id: TestCaseID;\n readonly isUnimplemented: boolean;\n run(\n rec: TestCaseRecorder,\n selfQuery: TestQuerySingleCase,\n expectations: TestQueryWithExpectation[]\n ): Promise<void>;\n}\n\n// Interface for defining tests\nexport interface TestGroupBuilder<S extends SubcaseBatchState, F extends Fixture<S>> {\n test(name: string): TestBuilderWithName<S, F>;\n}\nexport function makeTestGroup<S extends SubcaseBatchState, F extends Fixture<S>>(\n fixture: FixtureClass<S, F>\n): TestGroupBuilder<S, F> {\n return new TestGroup((fixture as unknown) as FixtureClass);\n}\n\n// Interfaces for running tests\nexport interface IterableTestGroup {\n iterate(): Iterable<IterableTest>;\n validate(): void;\n}\nexport interface IterableTest {\n testPath: string[];\n description: string | undefined;\n readonly testCreationStack: Error;\n iterate(): Iterable<RunCase>;\n}\n\nexport function makeTestGroupForUnitTesting<F extends Fixture>(\n fixture: FixtureClass<SubcaseBatchState, F>\n): TestGroup<SubcaseBatchState, F> {\n return new TestGroup(fixture);\n}\n\nexport type FixtureClass<\n S extends SubcaseBatchState = SubcaseBatchState,\n F extends Fixture<S> = Fixture<S>\n> = {\n new (sharedState: S, log: TestCaseRecorder, params: TestParams): F;\n MakeSharedState(params: TestParams): S;\n};\ntype TestFn<F extends Fixture, P extends {}> = (t: F & { params: P }) => Promise<void> | void;\ntype BeforeAllSubcasesFn<S extends SubcaseBatchState, P extends {}> = (\n s: S & { params: P }\n) => Promise<void> | void;\n\nexport class TestGroup<S extends SubcaseBatchState, F extends Fixture<S>>\n implements TestGroupBuilder<S, F> {\n private fixture: FixtureClass;\n private seen: Set<string> = new Set();\n private tests: Array<TestBuilder<S, F>> = [];\n\n constructor(fixture: FixtureClass) {\n this.fixture = fixture;\n }\n\n iterate(): Iterable<IterableTest> {\n return this.tests;\n }\n\n private checkName(name: string): void {\n assert(\n // Shouldn't happen due to the rule above. Just makes sure that treating\n // unencoded strings as encoded strings is OK.\n name === decodeURIComponent(name),\n `Not decodeURIComponent-idempotent: ${name} !== ${decodeURIComponent(name)}`\n );\n assert(!this.seen.has(name), `Duplicate test name: ${name}`);\n\n this.seen.add(name);\n }\n\n test(name: string): TestBuilderWithName<S, F> {\n const testCreationStack = new Error(`Test created: ${name}`);\n\n this.checkName(name);\n\n const parts = name.split(kPathSeparator);\n for (const p of parts) {\n assert(validQueryPart.test(p), `Invalid test name part ${p}; must match ${validQueryPart}`);\n }\n\n const test = new TestBuilder(parts, this.fixture, testCreationStack);\n this.tests.push(test);\n return (test as unknown) as TestBuilderWithName<S, F>;\n }\n\n validate(): void {\n for (const test of this.tests) {\n test.validate();\n }\n }\n}\n\ninterface TestBuilderWithName<S extends SubcaseBatchState, F extends Fixture<S>>\n extends TestBuilderWithParams<S, F, {}, {}> {\n desc(description: string): this;\n /**\n * A noop function to associate a test with the relevant part of the specification.\n *\n * @param url a link to the spec where test is extracted from.\n */\n specURL(url: string): this;\n /**\n * Parameterize the test, generating multiple cases, each possibly having subcases.\n *\n * The `unit` value passed to the `cases` callback is an immutable constant\n * `CaseParamsBuilder<{}>` representing the \"unit\" builder `[ {} ]`,\n * provided for convenience. The non-callback overload can be used if `unit` is not needed.\n */\n params<CaseP extends {}, SubcaseP extends {}>(\n cases: (unit: CaseParamsBuilder<{}>) => ParamsBuilderBase<CaseP, SubcaseP>\n ): TestBuilderWithParams<S, F, CaseP, SubcaseP>;\n /**\n * Parameterize the test, generating multiple cases, each possibly having subcases.\n *\n * Use the callback overload of this method if a \"unit\" builder is needed.\n */\n params<CaseP extends {}, SubcaseP extends {}>(\n cases: ParamsBuilderBase<CaseP, SubcaseP>\n ): TestBuilderWithParams<S, F, CaseP, SubcaseP>;\n\n /**\n * Parameterize the test, generating multiple cases, without subcases.\n */\n paramsSimple<P extends {}>(cases: Iterable<P>): TestBuilderWithParams<S, F, P, {}>;\n\n /**\n * Parameterize the test, generating one case with multiple subcases.\n */\n paramsSubcasesOnly<P extends {}>(subcases: Iterable<P>): TestBuilderWithParams<S, F, {}, P>;\n /**\n * Parameterize the test, generating one case with multiple subcases.\n *\n * The `unit` value passed to the `subcases` callback is an immutable constant\n * `SubcaseParamsBuilder<{}>`, with one empty case `{}` and one empty subcase `{}`.\n */\n paramsSubcasesOnly<P extends {}>(\n subcases: (unit: SubcaseParamsBuilder<{}, {}>) => SubcaseParamsBuilder<{}, P>\n ): TestBuilderWithParams<S, F, {}, P>;\n}\n\ninterface TestBuilderWithParams<\n S extends SubcaseBatchState,\n F extends Fixture<S>,\n CaseP extends {},\n SubcaseP extends {}\n> {\n /**\n * Limit subcases to a maximum number of per testcase.\n * @param b the maximum number of subcases per testcase.\n *\n * If the number of subcases exceeds `b`, add an internal\n * numeric, incrementing `batch__` param to split subcases\n * into groups of at most `b` subcases.\n */\n batch(b: number): this;\n /**\n * Run a function on shared subcase batch state before each\n * batch of subcases.\n * @param fn the function to run. It is called with the test\n * fixture's shared subcase batch state.\n *\n * Generally, this function should be careful to avoid mutating\n * any state on the shared subcase batch state which could result\n * in unexpected order-dependent test behavior.\n */\n beforeAllSubcases(fn: BeforeAllSubcasesFn<S, CaseP>): this;\n /**\n * Set the test function.\n * @param fn the test function.\n */\n fn(fn: TestFn<F, Merged<CaseP, SubcaseP>>): void;\n /**\n * Mark the test as unimplemented.\n */\n unimplemented(): void;\n}\n\nclass TestBuilder<S extends SubcaseBatchState, F extends Fixture> {\n readonly testPath: string[];\n isUnimplemented: boolean;\n description: string | undefined;\n readonly testCreationStack: Error;\n\n private readonly fixture: FixtureClass;\n private testFn: TestFn<Fixture, {}> | undefined;\n private beforeFn: BeforeAllSubcasesFn<SubcaseBatchState, {}> | undefined;\n private testCases?: ParamsBuilderBase<{}, {}> = undefined;\n private batchSize: number = 0;\n\n constructor(testPath: string[], fixture: FixtureClass, testCreationStack: Error) {\n this.testPath = testPath;\n this.isUnimplemented = false;\n this.fixture = fixture;\n this.testCreationStack = testCreationStack;\n }\n\n desc(description: string): this {\n this.description = description.trim();\n return this;\n }\n\n specURL(url: string): this {\n return this;\n }\n\n beforeAllSubcases(fn: BeforeAllSubcasesFn<SubcaseBatchState, {}>): this {\n assert(this.beforeFn === undefined);\n this.beforeFn = fn;\n return this;\n }\n\n fn(fn: TestFn<Fixture, {}>): void {\n // eslint-disable-next-line no-warning-comments\n // MAINTENANCE_TODO: add \"TODO\" if there's no description? (and make sure it only ends up on\n // actual tests, not on test parents in the tree, which is what happens if you do it here, not\n // sure why)\n assert(this.testFn === undefined);\n this.testFn = fn;\n }\n\n batch(b: number): this {\n this.batchSize = b;\n return this;\n }\n\n unimplemented(): void {\n assert(this.testFn === undefined);\n\n this.description =\n (this.description ? this.description + '\\n\\n' : '') + 'TODO: .unimplemented()';\n this.isUnimplemented = true;\n\n this.testFn = () => {\n throw new SkipTestCase('test unimplemented');\n };\n }\n\n validate(): void {\n const testPathString = this.testPath.join(kPathSeparator);\n assert(this.testFn !== undefined, () => {\n let s = `Test is missing .fn(): ${testPathString}`;\n if (this.testCreationStack.stack) {\n s += `\\n-> test created at:\\n${this.testCreationStack.stack}`;\n }\n return s;\n });\n\n if (this.testCases === undefined) {\n return;\n }\n\n const seen = new Set<string>();\n for (const [caseParams, subcases] of builderIterateCasesWithSubcases(this.testCases)) {\n for (const subcaseParams of subcases ?? [{}]) {\n const params = mergeParams(caseParams, subcaseParams);\n assert(this.batchSize === 0 || !('batch__' in params));\n\n // stringifyPublicParams also checks for invalid params values\n const testcaseString = stringifyPublicParams(params);\n\n // A (hopefully) unique representation of a params value.\n const testcaseStringUnique = stringifyPublicParamsUniquely(params);\n assert(\n !seen.has(testcaseStringUnique),\n `Duplicate public test case params for test ${testPathString}: ${testcaseString}`\n );\n seen.add(testcaseStringUnique);\n }\n }\n }\n\n params(\n cases: ((unit: CaseParamsBuilder<{}>) => ParamsBuilderBase<{}, {}>) | ParamsBuilderBase<{}, {}>\n ): TestBuilder<S, F> {\n assert(this.testCases === undefined, 'test case is already parameterized');\n if (cases instanceof Function) {\n this.testCases = cases(kUnitCaseParamsBuilder);\n } else {\n this.testCases = cases;\n }\n return this;\n }\n\n paramsSimple(cases: Iterable<{}>): TestBuilder<S, F> {\n assert(this.testCases === undefined, 'test case is already parameterized');\n this.testCases = kUnitCaseParamsBuilder.combineWithParams(cases);\n return this;\n }\n\n paramsSubcasesOnly(\n subcases: Iterable<{}> | ((unit: SubcaseParamsBuilder<{}, {}>) => SubcaseParamsBuilder<{}, {}>)\n ): TestBuilder<S, F> {\n if (subcases instanceof Function) {\n return this.params(subcases(kUnitCaseParamsBuilder.beginSubcases()));\n } else {\n return this.params(kUnitCaseParamsBuilder.beginSubcases().combineWithParams(subcases));\n }\n }\n\n *iterate(): IterableIterator<RunCase> {\n assert(this.testFn !== undefined, 'No test function (.fn()) for test');\n this.testCases ??= kUnitCaseParamsBuilder;\n for (const [caseParams, subcases] of builderIterateCasesWithSubcases(this.testCases)) {\n if (this.batchSize === 0 || subcases === undefined) {\n yield new RunCaseSpecific(\n this.testPath,\n caseParams,\n this.isUnimplemented,\n subcases,\n this.fixture,\n this.testFn,\n this.beforeFn,\n this.testCreationStack\n );\n } else {\n const subcaseArray = Array.from(subcases);\n if (subcaseArray.length <= this.batchSize) {\n yield new RunCaseSpecific(\n this.testPath,\n caseParams,\n this.isUnimplemented,\n subcaseArray,\n this.fixture,\n this.testFn,\n this.beforeFn,\n this.testCreationStack\n );\n } else {\n for (let i = 0; i < subcaseArray.length; i = i + this.batchSize) {\n yield new RunCaseSpecific(\n this.testPath,\n { ...caseParams, batch__: i / this.batchSize },\n this.isUnimplemented,\n subcaseArray.slice(i, Math.min(subcaseArray.length, i + this.batchSize)),\n this.fixture,\n this.testFn,\n this.beforeFn,\n this.testCreationStack\n );\n }\n }\n }\n }\n }\n}\n\nclass RunCaseSpecific implements RunCase {\n readonly id: TestCaseID;\n readonly isUnimplemented: boolean;\n\n private readonly params: {};\n private readonly subcases: Iterable<{}> | undefined;\n private readonly fixture: FixtureClass;\n private readonly fn: TestFn<Fixture, {}>;\n private readonly beforeFn?: BeforeAllSubcasesFn<SubcaseBatchState, {}>;\n private readonly testCreationStack: Error;\n\n constructor(\n testPath: string[],\n params: {},\n isUnimplemented: boolean,\n subcases: Iterable<{}> | undefined,\n fixture: FixtureClass,\n fn: TestFn<Fixture, {}>,\n beforeFn: BeforeAllSubcasesFn<SubcaseBatchState, {}> | undefined,\n testCreationStack: Error\n ) {\n this.id = { test: testPath, params: extractPublicParams(params) };\n this.isUnimplemented = isUnimplemented;\n this.params = params;\n this.subcases = subcases;\n this.fixture = fixture;\n this.fn = fn;\n this.beforeFn = beforeFn;\n this.testCreationStack = testCreationStack;\n }\n\n async runTest(\n rec: TestCaseRecorder,\n sharedState: SubcaseBatchState,\n params: TestParams,\n throwSkip: boolean,\n expectedStatus: Expectation\n ): Promise<void> {\n try {\n rec.beginSubCase();\n if (expectedStatus === 'skip') {\n throw new SkipTestCase('Skipped by expectations');\n }\n\n const inst = new this.fixture(sharedState, rec, params);\n try {\n await inst.init();\n await this.fn(inst as Fixture & { params: {} });\n } finally {\n // Runs as long as constructor succeeded, even if initialization or the test failed.\n await inst.finalize();\n }\n } catch (ex) {\n // There was an exception from constructor, init, test, or finalize.\n // An error from init or test may have been a SkipTestCase.\n // An error from finalize may have been an eventualAsyncExpectation failure\n // or unexpected validation/OOM error from the GPUDevice.\n if (throwSkip && ex instanceof SkipTestCase) {\n throw ex;\n }\n rec.threw(ex);\n } finally {\n try {\n rec.endSubCase(expectedStatus);\n } catch (ex) {\n assert(ex instanceof UnexpectedPassError);\n ex.message = `Testcase passed unexpectedly.`;\n ex.stack = this.testCreationStack.stack;\n rec.warn(ex);\n }\n }\n }\n\n async run(\n rec: TestCaseRecorder,\n selfQuery: TestQuerySingleCase,\n expectations: TestQueryWithExpectation[]\n ): Promise<void> {\n const getExpectedStatus = (selfQueryWithSubParams: TestQuerySingleCase) => {\n let didSeeFail = false;\n for (const exp of expectations) {\n const ordering = compareQueries(exp.query, selfQueryWithSubParams);\n if (ordering === Ordering.Unordered || ordering === Ordering.StrictSubset) {\n continue;\n }\n\n switch (exp.expectation) {\n // Skip takes precedence. If there is any expectation indicating a skip,\n // signal it immediately.\n case 'skip':\n return 'skip';\n case 'fail':\n // Otherwise, indicate that we might expect a failure.\n didSeeFail = true;\n break;\n default:\n unreachable();\n }\n }\n return didSeeFail ? 'fail' : 'pass';\n };\n\n const { testHeartbeatCallback, maxSubcasesInFlight } = globalTestConfig;\n try {\n rec.start();\n const sharedState = this.fixture.MakeSharedState(this.params);\n try {\n await sharedState.init();\n if (this.beforeFn) {\n await this.beforeFn(sharedState);\n }\n await sharedState.postInit();\n testHeartbeatCallback();\n\n let allPreviousSubcasesFinalizedPromise: Promise<void> = Promise.resolve();\n if (this.subcases) {\n let totalCount = 0;\n let skipCount = 0;\n\n // If there are too many subcases in flight, starting the next subcase will register\n // `resolvePromiseBlockingSubcase` and wait until `subcaseFinishedCallback` is called.\n let subcasesInFlight = 0;\n let resolvePromiseBlockingSubcase: (() => void) | undefined = undefined;\n const subcaseFinishedCallback = () => {\n subcasesInFlight -= 1;\n // If there is any subcase waiting on a previous subcase to finish,\n // unblock it now, and clear the resolve callback.\n if (resolvePromiseBlockingSubcase) {\n resolvePromiseBlockingSubcase();\n resolvePromiseBlockingSubcase = undefined;\n }\n };\n\n for (const subParams of this.subcases) {\n // Make a recorder that will defer all calls until `allPreviousSubcasesFinalizedPromise`\n // resolves. Waiting on `allPreviousSubcasesFinalizedPromise` ensures that\n // logs from all the previous subcases have been flushed before flushing new logs.\n const subcasePrefix = 'subcase: ' + stringifyPublicParams(subParams);\n const subRec = new Proxy(rec, {\n get: (target, k: keyof TestCaseRecorder) => {\n const prop = TestCaseRecorder.prototype[k];\n if (typeof prop === 'function') {\n testHeartbeatCallback();\n return function (...args: Parameters<typeof prop>) {\n void allPreviousSubcasesFinalizedPromise.then(() => {\n // Prepend the subcase name to all error messages.\n for (const arg of args) {\n if (arg instanceof Error) {\n try {\n arg.message = subcasePrefix + '\\n' + arg.message;\n } catch {\n // If that fails (e.g. on DOMException), try to put it in the stack:\n let stack = subcasePrefix;\n if (arg.stack) stack += '\\n' + arg.stack;\n try {\n arg.stack = stack;\n } catch {\n // If that fails too, just silence it.\n }\n }\n }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const rv = (prop as any).apply(target, args);\n // Because this proxy executes functions in a deferred manner,\n // it should never be used for functions that need to return a value.\n assert(rv === undefined);\n });\n };\n }\n return prop;\n },\n });\n\n const params = mergeParams(this.params, subParams);\n const subcaseQuery = new TestQuerySingleCase(\n selfQuery.suite,\n selfQuery.filePathParts,\n selfQuery.testPathParts,\n params\n );\n\n // Limit the maximum number of subcases in flight.\n if (subcasesInFlight >= maxSubcasesInFlight) {\n await new Promise<void>(resolve => {\n // There should only be one subcase waiting at a time.\n assert(resolvePromiseBlockingSubcase === undefined);\n resolvePromiseBlockingSubcase = resolve;\n });\n }\n\n subcasesInFlight += 1;\n // Runs async without waiting so that subsequent subcases can start.\n // All finalization steps will be waited on at the end of the testcase.\n const finalizePromise = this.runTest(\n subRec,\n sharedState,\n params,\n /* throwSkip */ true,\n getExpectedStatus(subcaseQuery)\n )\n .then(() => {\n subRec.info(new Error('OK'));\n })\n .catch(ex => {\n if (ex instanceof SkipTestCase) {\n // Convert SkipTestCase to info messages\n ex.message = 'subcase skipped: ' + ex.message;\n subRec.info(ex);\n ++skipCount;\n } else {\n // Since we are catching all error inside runTest(), this should never happen\n subRec.threw(ex);\n }\n })\n .finally(subcaseFinishedCallback);\n\n allPreviousSubcasesFinalizedPromise = allPreviousSubcasesFinalizedPromise.then(\n () => finalizePromise\n );\n ++totalCount;\n }\n\n // Wait for all subcases to finalize and report their results.\n await allPreviousSubcasesFinalizedPromise;\n\n if (skipCount === totalCount) {\n rec.skipped(new SkipTestCase('all subcases were skipped'));\n }\n } else {\n await this.runTest(\n rec,\n sharedState,\n this.params,\n /* throwSkip */ false,\n getExpectedStatus(selfQuery)\n );\n }\n } finally {\n testHeartbeatCallback();\n // Runs as long as the shared state constructor succeeded, even if initialization or a test failed.\n await sharedState.finalize();\n testHeartbeatCallback();\n }\n } catch (ex) {\n // There was an exception from sharedState/fixture constructor, init, beforeFn, or test.\n // An error from beforeFn may have been SkipTestCase.\n // An error from finalize may have been an eventualAsyncExpectation failure\n // or unexpected validation/OOM error from the GPUDevice.\n rec.threw(ex);\n } finally {\n rec.finish();\n }\n }\n}\n"],"mappings":"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"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/test_suite_listing.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/test_suite_listing.js
new file mode 100644
index 0000000000..fc2c8c4189
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/test_suite_listing.js
@@ -0,0 +1,4 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/export {};
+//# sourceMappingURL=test_suite_listing.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/test_suite_listing.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/test_suite_listing.js.map
new file mode 100644
index 0000000000..89ae3a6866
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/test_suite_listing.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"test_suite_listing.js","names":[],"sources":["../../../src/common/internal/test_suite_listing.ts"],"sourcesContent":["// A listing of all specs within a single suite. This is the (awaited) type of\n// `groups` in '{cts,unittests}/listing.ts' and `listing` in the auto-generated\n// 'out/{cts,unittests}/listing.js' files (see tools/gen_listings).\nexport type TestSuiteListing = TestSuiteListingEntry[];\n\nexport type TestSuiteListingEntry = TestSuiteListingEntrySpec | TestSuiteListingEntryReadme;\n\ninterface TestSuiteListingEntrySpec {\n readonly file: string[];\n}\n\ninterface TestSuiteListingEntryReadme {\n readonly file: string[];\n readonly readme: string;\n}\n"],"mappings":";AAAA;AAAA,G"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/tree.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/tree.js
new file mode 100644
index 0000000000..5245079d5d
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/tree.js
@@ -0,0 +1,576 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { assert } from '../util/util.js';
+
+
+import { compareQueries, Ordering } from './query/compare.js';
+import {
+
+TestQueryMultiCase,
+TestQuerySingleCase,
+TestQueryMultiFile,
+TestQueryMultiTest } from
+'./query/query.js';
+import { kBigSeparator, kWildcard, kPathSeparator, kParamSeparator } from './query/separators.js';
+import { stringifySingleParam } from './query/stringify_params.js';
+import { StacklessError } from './util.js';
+
+// `loadTreeForQuery()` loads a TestTree for a given queryToLoad.
+// The resulting tree is a linked-list all the way from `suite:*` to queryToLoad,
+// and under queryToLoad is a tree containing every case matched by queryToLoad.
+//
+// `subqueriesToExpand` influences the `collapsible` flag on nodes in the resulting tree.
+// A node is considered "collapsible" if none of the subqueriesToExpand is a StrictSubset
+// of that node.
+//
+// In WebKit/Blink-style web_tests, an expectation file marks individual cts.https.html "variants
+// as "Failure", "Crash", etc. By passing in the list of expectations as the subqueriesToExpand,
+// we can programmatically subdivide the cts.https.html "variants" list to be able to implement
+// arbitrarily-fine suppressions (instead of having to suppress entire test files, which would
+// lose a lot of coverage).
+//
+// `iterateCollapsedNodes()` produces the list of queries for the variants list.
+//
+// Though somewhat complicated, this system has important benefits:
+// - Avoids having to suppress entire test files, which would cause large test coverage loss.
+// - Minimizes the number of page loads needed for fine-grained suppressions.
+// (In the naive case, we could do one page load per test case - but the test suite would
+// take impossibly long to run.)
+// - Enables developers to put any number of tests in one file as appropriate, without worrying
+// about expectation granularity.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+export class TestTree {
+ /**
+ * The `queryToLoad` that this test tree was created for.
+ * Test trees are always rooted at `suite:*`, but they only contain nodes that fit
+ * within `forQuery`.
+ *
+ * This is used for `iterateCollapsedNodes` which only starts collapsing at the next
+ * `TestQueryLevel` after `forQuery`.
+ */
+
+
+
+ constructor(forQuery, root) {
+ this.forQuery = forQuery;
+ TestTree.propagateCounts(root);
+ this.root = root;
+ assert(
+ root.query.level === 1 && root.query.depthInLevel === 0,
+ 'TestTree root must be the root (suite:*)');
+
+ }
+
+ /**
+ * Iterate through the leaves of a version of the tree which has been pruned to exclude
+ * subtrees which:
+ * - are at a deeper `TestQueryLevel` than `this.forQuery`, and
+ * - were not a `Ordering.StrictSubset` of any of the `subqueriesToExpand` during tree creation.
+ */
+ iterateCollapsedNodes({
+ includeIntermediateNodes = false,
+ includeEmptySubtrees = false,
+ alwaysExpandThroughLevel
+
+
+
+
+
+
+
+ }) {
+ const expandThroughLevel = Math.max(this.forQuery.level, alwaysExpandThroughLevel);
+ return TestTree.iterateSubtreeNodes(this.root, {
+ includeIntermediateNodes,
+ includeEmptySubtrees,
+ expandThroughLevel
+ });
+ }
+
+ iterateLeaves() {
+ return TestTree.iterateSubtreeLeaves(this.root);
+ }
+
+ /**
+ * Dissolve nodes which have only one child, e.g.:
+ * a,* { a,b,* { a,b:* { ... } } }
+ * collapses down into:
+ * a,* { a,b:* { ... } }
+ * which is less needlessly verbose when displaying the tree in the standalone runner.
+ */
+ dissolveSingleChildTrees() {
+ const newRoot = dissolveSingleChildTrees(this.root);
+ assert(newRoot === this.root);
+ }
+
+ toString() {
+ return TestTree.subtreeToString('(root)', this.root, '');
+ }
+
+ static *iterateSubtreeNodes(
+ subtree,
+ opts)
+
+
+
+
+ {
+ if (opts.includeIntermediateNodes) {
+ yield subtree;
+ }
+
+ for (const [, child] of subtree.children) {
+ if ('children' in child) {
+ // Is a subtree
+ const collapsible = child.collapsible && child.query.level > opts.expandThroughLevel;
+ if (child.children.size > 0 && !collapsible) {
+ yield* TestTree.iterateSubtreeNodes(child, opts);
+ } else if (child.children.size > 0 || opts.includeEmptySubtrees) {
+ // Don't yield empty subtrees (e.g. files with no tests) unless includeEmptySubtrees
+ yield child;
+ }
+ } else {
+ // Is a leaf
+ yield child;
+ }
+ }
+ }
+
+ static *iterateSubtreeLeaves(subtree) {
+ for (const [, child] of subtree.children) {
+ if ('children' in child) {
+ yield* TestTree.iterateSubtreeLeaves(child);
+ } else {
+ yield child;
+ }
+ }
+ }
+
+ /** Propagate the subtreeTODOs/subtreeTests state upward from leaves to parent nodes. */
+ static propagateCounts(subtree) {
+ subtree.subtreeCounts ??= { tests: 0, nodesWithTODO: 0 };
+ for (const [, child] of subtree.children) {
+ if ('children' in child) {
+ const counts = TestTree.propagateCounts(child);
+ subtree.subtreeCounts.tests += counts.tests;
+ subtree.subtreeCounts.nodesWithTODO += counts.nodesWithTODO;
+ }
+ }
+ return subtree.subtreeCounts;
+ }
+
+ /** Displays counts in the format `(Nodes with TODOs) / (Total test count)`. */
+ static countsToString(tree) {
+ if (tree.subtreeCounts) {
+ return `${tree.subtreeCounts.nodesWithTODO} / ${tree.subtreeCounts.tests}`;
+ } else {
+ return '';
+ }
+ }
+
+ static subtreeToString(name, tree, indent) {
+ const collapsible = 'run' in tree ? '>' : tree.collapsible ? '+' : '-';
+ let s =
+ indent +
+ `${collapsible} ${TestTree.countsToString(tree)} ${JSON.stringify(name)} => ${tree.query}`;
+ if ('children' in tree) {
+ if (tree.description !== undefined) {
+ s += `\n${indent} | ${JSON.stringify(tree.description)}`;
+ }
+
+ for (const [name, child] of tree.children) {
+ s += '\n' + TestTree.subtreeToString(name, child, indent + ' ');
+ }
+ }
+ return s;
+ }
+}
+
+// MAINTENANCE_TODO: Consider having subqueriesToExpand actually impact the depth-order of params
+// in the tree.
+export async function loadTreeForQuery(
+loader,
+queryToLoad,
+subqueriesToExpand)
+{
+ const suite = queryToLoad.suite;
+ const specs = await loader.listing(suite);
+
+ const subqueriesToExpandEntries = Array.from(subqueriesToExpand.entries());
+ const seenSubqueriesToExpand = new Array(subqueriesToExpand.length);
+ seenSubqueriesToExpand.fill(false);
+
+ const isCollapsible = (subquery) =>
+ subqueriesToExpandEntries.every(([i, toExpand]) => {
+ const ordering = compareQueries(toExpand, subquery);
+
+ // If toExpand == subquery, no expansion is needed (but it's still "seen").
+ if (ordering === Ordering.Equal) seenSubqueriesToExpand[i] = true;
+ return ordering !== Ordering.StrictSubset;
+ });
+
+ // L0 = suite-level, e.g. suite:*
+ // L1 = file-level, e.g. suite:a,b:*
+ // L2 = test-level, e.g. suite:a,b:c,d:*
+ // L3 = case-level, e.g. suite:a,b:c,d:
+ let foundCase = false;
+ // L0 is suite:*
+ const subtreeL0 = makeTreeForSuite(suite, isCollapsible);
+ for (const entry of specs) {
+ if (entry.file.length === 0 && 'readme' in entry) {
+ // Suite-level readme.
+ setSubtreeDescriptionAndCountTODOs(subtreeL0, entry.readme);
+ continue;
+ }
+
+ {
+ const queryL1 = new TestQueryMultiFile(suite, entry.file);
+ const orderingL1 = compareQueries(queryL1, queryToLoad);
+ if (orderingL1 === Ordering.Unordered) {
+ // File path is not matched by this query.
+ continue;
+ }
+ }
+
+ if ('readme' in entry) {
+ // Entry is a README that is an ancestor or descendant of the query.
+ // (It's included for display in the standalone runner.)
+
+ // readmeSubtree is suite:a,b,*
+ // (This is always going to dedup with a file path, if there are any test spec files under
+ // the directory that has the README).
+ const readmeSubtree = addSubtreeForDirPath(
+ subtreeL0,
+ entry.file,
+ isCollapsible);
+
+ setSubtreeDescriptionAndCountTODOs(readmeSubtree, entry.readme);
+ continue;
+ }
+ // Entry is a spec file.
+
+ const spec = await loader.importSpecFile(queryToLoad.suite, entry.file);
+ // subtreeL1 is suite:a,b:*
+ const subtreeL1 = addSubtreeForFilePath(
+ subtreeL0,
+ entry.file,
+ isCollapsible);
+
+ setSubtreeDescriptionAndCountTODOs(subtreeL1, spec.description);
+
+ let groupHasTests = false;
+ for (const t of spec.g.iterate()) {
+ groupHasTests = true;
+ {
+ const queryL2 = new TestQueryMultiCase(suite, entry.file, t.testPath, {});
+ const orderingL2 = compareQueries(queryL2, queryToLoad);
+ if (orderingL2 === Ordering.Unordered) {
+ // Test path is not matched by this query.
+ continue;
+ }
+ }
+
+ // subtreeL2 is suite:a,b:c,d:*
+ const subtreeL2 = addSubtreeForTestPath(
+ subtreeL1,
+ t.testPath,
+ t.testCreationStack,
+ isCollapsible);
+
+ // This is 1 test. Set tests=1 then count TODOs.
+ subtreeL2.subtreeCounts ??= { tests: 1, nodesWithTODO: 0 };
+ if (t.description) setSubtreeDescriptionAndCountTODOs(subtreeL2, t.description);
+
+ // MAINTENANCE_TODO: If tree generation gets too slow, avoid actually iterating the cases in a
+ // file if there's no need to (based on the subqueriesToExpand).
+ for (const c of t.iterate()) {
+ {
+ const queryL3 = new TestQuerySingleCase(suite, entry.file, c.id.test, c.id.params);
+ const orderingL3 = compareQueries(queryL3, queryToLoad);
+ if (orderingL3 === Ordering.Unordered || orderingL3 === Ordering.StrictSuperset) {
+ // Case is not matched by this query.
+ continue;
+ }
+ }
+
+ // Leaf for case is suite:a,b:c,d:x=1;y=2
+ addLeafForCase(subtreeL2, c, isCollapsible);
+
+ foundCase = true;
+ }
+ }
+ if (!groupHasTests && !subtreeL1.subtreeCounts) {
+ throw new StacklessError(
+ `${subtreeL1.query} has no tests - it must have "TODO" in its description`);
+
+ }
+ }
+
+ for (const [i, sq] of subqueriesToExpandEntries) {
+ const subquerySeen = seenSubqueriesToExpand[i];
+ if (!subquerySeen) {
+ throw new StacklessError(
+ `subqueriesToExpand entry did not match anything \
+(could be wrong, or could be redundant with a previous subquery):\n ${sq.toString()}`);
+
+ }
+ }
+ assert(foundCase, `Query \`${queryToLoad.toString()}\` does not match any cases`);
+
+ return new TestTree(queryToLoad, subtreeL0);
+}
+
+function setSubtreeDescriptionAndCountTODOs(
+subtree,
+description)
+{
+ assert(subtree.description === undefined);
+ subtree.description = description.trim();
+ subtree.subtreeCounts ??= { tests: 0, nodesWithTODO: 0 };
+ if (subtree.description.indexOf('TODO') !== -1) {
+ subtree.subtreeCounts.nodesWithTODO++;
+ }
+}
+
+function makeTreeForSuite(
+suite,
+isCollapsible)
+{
+ const query = new TestQueryMultiFile(suite, []);
+ return {
+ readableRelativeName: suite + kBigSeparator,
+ query,
+ children: new Map(),
+ collapsible: isCollapsible(query)
+ };
+}
+
+function addSubtreeForDirPath(
+tree,
+file,
+isCollapsible)
+{
+ const subqueryFile = [];
+ // To start, tree is suite:*
+ // This loop goes from that -> suite:a,* -> suite:a,b,*
+ for (const part of file) {
+ subqueryFile.push(part);
+ tree = getOrInsertSubtree(part, tree, () => {
+ const query = new TestQueryMultiFile(tree.query.suite, subqueryFile);
+ return {
+ readableRelativeName: part + kPathSeparator + kWildcard,
+ query,
+ collapsible: isCollapsible(query)
+ };
+ });
+ }
+ return tree;
+}
+
+function addSubtreeForFilePath(
+tree,
+file,
+isCollapsible)
+{
+ // To start, tree is suite:*
+ // This goes from that -> suite:a,* -> suite:a,b,*
+ tree = addSubtreeForDirPath(tree, file, isCollapsible);
+ // This goes from that -> suite:a,b:*
+ const subtree = getOrInsertSubtree('', tree, () => {
+ const query = new TestQueryMultiTest(tree.query.suite, tree.query.filePathParts, []);
+ assert(file.length > 0, 'file path is empty');
+ return {
+ readableRelativeName: file[file.length - 1] + kBigSeparator + kWildcard,
+ query,
+ collapsible: isCollapsible(query)
+ };
+ });
+ return subtree;
+}
+
+function addSubtreeForTestPath(
+tree,
+test,
+testCreationStack,
+isCollapsible)
+{
+ const subqueryTest = [];
+ // To start, tree is suite:a,b:*
+ // This loop goes from that -> suite:a,b:c,* -> suite:a,b:c,d,*
+ for (const part of test) {
+ subqueryTest.push(part);
+ tree = getOrInsertSubtree(part, tree, () => {
+ const query = new TestQueryMultiTest(
+ tree.query.suite,
+ tree.query.filePathParts,
+ subqueryTest);
+
+ return {
+ readableRelativeName: part + kPathSeparator + kWildcard,
+ query,
+ collapsible: isCollapsible(query)
+ };
+ });
+ }
+ // This goes from that -> suite:a,b:c,d:*
+ return getOrInsertSubtree('', tree, () => {
+ const query = new TestQueryMultiCase(
+ tree.query.suite,
+ tree.query.filePathParts,
+ subqueryTest,
+ {});
+
+ assert(subqueryTest.length > 0, 'subqueryTest is empty');
+ return {
+ readableRelativeName: subqueryTest[subqueryTest.length - 1] + kBigSeparator + kWildcard,
+ kWildcard,
+ query,
+ testCreationStack,
+ collapsible: isCollapsible(query)
+ };
+ });
+}
+
+function addLeafForCase(
+tree,
+t,
+checkCollapsible)
+{
+ const query = tree.query;
+ let name = '';
+ const subqueryParams = {};
+
+ // To start, tree is suite:a,b:c,d:*
+ // This loop goes from that -> suite:a,b:c,d:x=1;* -> suite:a,b:c,d:x=1;y=2;*
+ for (const [k, v] of Object.entries(t.id.params)) {
+ name = stringifySingleParam(k, v);
+ subqueryParams[k] = v;
+
+ tree = getOrInsertSubtree(name, tree, () => {
+ const subquery = new TestQueryMultiCase(
+ query.suite,
+ query.filePathParts,
+ query.testPathParts,
+ subqueryParams);
+
+ return {
+ readableRelativeName: name + kParamSeparator + kWildcard,
+ query: subquery,
+ collapsible: checkCollapsible(subquery)
+ };
+ });
+ }
+
+ // This goes from that -> suite:a,b:c,d:x=1;y=2
+ const subquery = new TestQuerySingleCase(
+ query.suite,
+ query.filePathParts,
+ query.testPathParts,
+ subqueryParams);
+
+ checkCollapsible(subquery); // mark seenSubqueriesToExpand
+ insertLeaf(tree, subquery, t);
+}
+
+function getOrInsertSubtree(
+key,
+parent,
+createSubtree)
+{
+ let v;
+ const child = parent.children.get(key);
+ if (child !== undefined) {
+ assert('children' in child); // Make sure cached subtree is not actually a leaf
+ v = child;
+ } else {
+ v = { ...createSubtree(), children: new Map() };
+ parent.children.set(key, v);
+ }
+ return v;
+}
+
+function insertLeaf(parent, query, t) {
+ const leaf = {
+ readableRelativeName: readableNameForCase(query),
+ query,
+ run: (rec, expectations) => t.run(rec, query, expectations || []),
+ isUnimplemented: t.isUnimplemented
+ };
+
+ // This is a leaf (e.g. s:f:t:x=1;* -> s:f:t:x=1). The key is always ''.
+ const key = '';
+ assert(!parent.children.has(key), `Duplicate testcase: ${query}`);
+ parent.children.set(key, leaf);
+}
+
+function dissolveSingleChildTrees(tree) {
+ if ('children' in tree) {
+ const shouldDissolveThisTree =
+ tree.children.size === 1 && tree.query.depthInLevel !== 0 && tree.description === undefined;
+ if (shouldDissolveThisTree) {
+ // Loops exactly once
+ for (const [, child] of tree.children) {
+ // Recurse on child
+ return dissolveSingleChildTrees(child);
+ }
+ }
+
+ for (const [k, child] of tree.children) {
+ // Recurse on each child
+ const newChild = dissolveSingleChildTrees(child);
+ if (newChild !== child) {
+ tree.children.set(k, newChild);
+ }
+ }
+ }
+ return tree;
+}
+
+/** Generate a readable relative name for a case (used in standalone). */
+function readableNameForCase(query) {
+ const paramsKeys = Object.keys(query.params);
+ if (paramsKeys.length === 0) {
+ return query.testPathParts[query.testPathParts.length - 1] + kBigSeparator;
+ } else {
+ const lastKey = paramsKeys[paramsKeys.length - 1];
+ return stringifySingleParam(lastKey, query.params[lastKey]);
+ }
+}
+//# sourceMappingURL=tree.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/tree.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/tree.js.map
new file mode 100644
index 0000000000..44a0c7c035
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/tree.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"tree.js","names":["assert","compareQueries","Ordering","TestQueryMultiCase","TestQuerySingleCase","TestQueryMultiFile","TestQueryMultiTest","kBigSeparator","kWildcard","kPathSeparator","kParamSeparator","stringifySingleParam","StacklessError","TestTree","constructor","forQuery","root","propagateCounts","query","level","depthInLevel","iterateCollapsedNodes","includeIntermediateNodes","includeEmptySubtrees","alwaysExpandThroughLevel","expandThroughLevel","Math","max","iterateSubtreeNodes","iterateLeaves","iterateSubtreeLeaves","dissolveSingleChildTrees","newRoot","toString","subtreeToString","subtree","opts","child","children","collapsible","size","subtreeCounts","tests","nodesWithTODO","counts","countsToString","tree","name","indent","s","JSON","stringify","description","undefined","loadTreeForQuery","loader","queryToLoad","subqueriesToExpand","suite","specs","listing","subqueriesToExpandEntries","Array","from","entries","seenSubqueriesToExpand","length","fill","isCollapsible","subquery","every","i","toExpand","ordering","Equal","StrictSubset","foundCase","subtreeL0","makeTreeForSuite","entry","file","setSubtreeDescriptionAndCountTODOs","readme","queryL1","orderingL1","Unordered","readmeSubtree","addSubtreeForDirPath","spec","importSpecFile","subtreeL1","addSubtreeForFilePath","groupHasTests","t","g","iterate","queryL2","testPath","orderingL2","subtreeL2","addSubtreeForTestPath","testCreationStack","c","queryL3","id","test","params","orderingL3","StrictSuperset","addLeafForCase","sq","subquerySeen","trim","indexOf","readableRelativeName","Map","subqueryFile","part","push","getOrInsertSubtree","filePathParts","subqueryTest","checkCollapsible","subqueryParams","k","v","Object","testPathParts","insertLeaf","key","parent","createSubtree","get","set","leaf","readableNameForCase","run","rec","expectations","isUnimplemented","has","shouldDissolveThisTree","newChild","paramsKeys","keys","lastKey"],"sources":["../../../src/common/internal/tree.ts"],"sourcesContent":["import { RunCase, RunFn } from '../internal/test_group.js';\nimport { assert } from '../util/util.js';\n\nimport { TestFileLoader } from './file_loader.js';\nimport { TestParamsRW } from './params_utils.js';\nimport { compareQueries, Ordering } from './query/compare.js';\nimport {\n TestQuery,\n TestQueryMultiCase,\n TestQuerySingleCase,\n TestQueryMultiFile,\n TestQueryMultiTest,\n} from './query/query.js';\nimport { kBigSeparator, kWildcard, kPathSeparator, kParamSeparator } from './query/separators.js';\nimport { stringifySingleParam } from './query/stringify_params.js';\nimport { StacklessError } from './util.js';\n\n// `loadTreeForQuery()` loads a TestTree for a given queryToLoad.\n// The resulting tree is a linked-list all the way from `suite:*` to queryToLoad,\n// and under queryToLoad is a tree containing every case matched by queryToLoad.\n//\n// `subqueriesToExpand` influences the `collapsible` flag on nodes in the resulting tree.\n// A node is considered \"collapsible\" if none of the subqueriesToExpand is a StrictSubset\n// of that node.\n//\n// In WebKit/Blink-style web_tests, an expectation file marks individual cts.https.html \"variants\n// as \"Failure\", \"Crash\", etc. By passing in the list of expectations as the subqueriesToExpand,\n// we can programmatically subdivide the cts.https.html \"variants\" list to be able to implement\n// arbitrarily-fine suppressions (instead of having to suppress entire test files, which would\n// lose a lot of coverage).\n//\n// `iterateCollapsedNodes()` produces the list of queries for the variants list.\n//\n// Though somewhat complicated, this system has important benefits:\n// - Avoids having to suppress entire test files, which would cause large test coverage loss.\n// - Minimizes the number of page loads needed for fine-grained suppressions.\n// (In the naive case, we could do one page load per test case - but the test suite would\n// take impossibly long to run.)\n// - Enables developers to put any number of tests in one file as appropriate, without worrying\n// about expectation granularity.\n\ninterface TestTreeNodeBase<T extends TestQuery> {\n readonly query: T;\n /**\n * Readable \"relative\" name for display in standalone runner.\n * Not always the exact relative name, because sometimes there isn't\n * one (e.g. s:f:* relative to s:f,*), but something that is readable.\n */\n readonly readableRelativeName: string;\n subtreeCounts?: { tests: number; nodesWithTODO: number };\n}\n\nexport interface TestSubtree<T extends TestQuery = TestQuery> extends TestTreeNodeBase<T> {\n readonly children: Map<string, TestTreeNode>;\n readonly collapsible: boolean;\n description?: string;\n readonly testCreationStack?: Error;\n}\n\nexport interface TestTreeLeaf extends TestTreeNodeBase<TestQuerySingleCase> {\n readonly run: RunFn;\n readonly isUnimplemented?: boolean;\n subtreeCounts?: undefined;\n}\n\nexport type TestTreeNode = TestSubtree | TestTreeLeaf;\n\n/**\n * When iterating through \"collapsed\" tree nodes, indicates how many \"query levels\" to traverse\n * through before starting to collapse nodes.\n *\n * Corresponds with TestQueryLevel, but excludes 4 (SingleCase):\n * - 1 = MultiFile. Expands so every file is in the collapsed tree.\n * - 2 = MultiTest. Expands so every test is in the collapsed tree.\n * - 3 = MultiCase. Expands so every case is in the collapsed tree (i.e. collapsing disabled).\n */\nexport type ExpandThroughLevel = 1 | 2 | 3;\n\nexport class TestTree {\n /**\n * The `queryToLoad` that this test tree was created for.\n * Test trees are always rooted at `suite:*`, but they only contain nodes that fit\n * within `forQuery`.\n *\n * This is used for `iterateCollapsedNodes` which only starts collapsing at the next\n * `TestQueryLevel` after `forQuery`.\n */\n readonly forQuery: TestQuery;\n readonly root: TestSubtree;\n\n constructor(forQuery: TestQuery, root: TestSubtree) {\n this.forQuery = forQuery;\n TestTree.propagateCounts(root);\n this.root = root;\n assert(\n root.query.level === 1 && root.query.depthInLevel === 0,\n 'TestTree root must be the root (suite:*)'\n );\n }\n\n /**\n * Iterate through the leaves of a version of the tree which has been pruned to exclude\n * subtrees which:\n * - are at a deeper `TestQueryLevel` than `this.forQuery`, and\n * - were not a `Ordering.StrictSubset` of any of the `subqueriesToExpand` during tree creation.\n */\n iterateCollapsedNodes({\n includeIntermediateNodes = false,\n includeEmptySubtrees = false,\n alwaysExpandThroughLevel,\n }: {\n /** Whether to include intermediate tree nodes or only collapsed-leaves. */\n includeIntermediateNodes?: boolean;\n /** Whether to include collapsed-leaves with no children. */\n includeEmptySubtrees?: boolean;\n /** Never collapse nodes up through this level. */\n alwaysExpandThroughLevel: ExpandThroughLevel;\n }): IterableIterator<Readonly<TestTreeNode>> {\n const expandThroughLevel = Math.max(this.forQuery.level, alwaysExpandThroughLevel);\n return TestTree.iterateSubtreeNodes(this.root, {\n includeIntermediateNodes,\n includeEmptySubtrees,\n expandThroughLevel,\n });\n }\n\n iterateLeaves(): IterableIterator<Readonly<TestTreeLeaf>> {\n return TestTree.iterateSubtreeLeaves(this.root);\n }\n\n /**\n * Dissolve nodes which have only one child, e.g.:\n * a,* { a,b,* { a,b:* { ... } } }\n * collapses down into:\n * a,* { a,b:* { ... } }\n * which is less needlessly verbose when displaying the tree in the standalone runner.\n */\n dissolveSingleChildTrees(): void {\n const newRoot = dissolveSingleChildTrees(this.root);\n assert(newRoot === this.root);\n }\n\n toString(): string {\n return TestTree.subtreeToString('(root)', this.root, '');\n }\n\n static *iterateSubtreeNodes(\n subtree: TestSubtree,\n opts: {\n includeIntermediateNodes: boolean;\n includeEmptySubtrees: boolean;\n expandThroughLevel: number;\n }\n ): IterableIterator<TestTreeNode> {\n if (opts.includeIntermediateNodes) {\n yield subtree;\n }\n\n for (const [, child] of subtree.children) {\n if ('children' in child) {\n // Is a subtree\n const collapsible = child.collapsible && child.query.level > opts.expandThroughLevel;\n if (child.children.size > 0 && !collapsible) {\n yield* TestTree.iterateSubtreeNodes(child, opts);\n } else if (child.children.size > 0 || opts.includeEmptySubtrees) {\n // Don't yield empty subtrees (e.g. files with no tests) unless includeEmptySubtrees\n yield child;\n }\n } else {\n // Is a leaf\n yield child;\n }\n }\n }\n\n static *iterateSubtreeLeaves(subtree: TestSubtree): IterableIterator<TestTreeLeaf> {\n for (const [, child] of subtree.children) {\n if ('children' in child) {\n yield* TestTree.iterateSubtreeLeaves(child);\n } else {\n yield child;\n }\n }\n }\n\n /** Propagate the subtreeTODOs/subtreeTests state upward from leaves to parent nodes. */\n static propagateCounts(subtree: TestSubtree): { tests: number; nodesWithTODO: number } {\n subtree.subtreeCounts ??= { tests: 0, nodesWithTODO: 0 };\n for (const [, child] of subtree.children) {\n if ('children' in child) {\n const counts = TestTree.propagateCounts(child);\n subtree.subtreeCounts.tests += counts.tests;\n subtree.subtreeCounts.nodesWithTODO += counts.nodesWithTODO;\n }\n }\n return subtree.subtreeCounts;\n }\n\n /** Displays counts in the format `(Nodes with TODOs) / (Total test count)`. */\n static countsToString(tree: TestTreeNode): string {\n if (tree.subtreeCounts) {\n return `${tree.subtreeCounts.nodesWithTODO} / ${tree.subtreeCounts.tests}`;\n } else {\n return '';\n }\n }\n\n static subtreeToString(name: string, tree: TestTreeNode, indent: string): string {\n const collapsible = 'run' in tree ? '>' : tree.collapsible ? '+' : '-';\n let s =\n indent +\n `${collapsible} ${TestTree.countsToString(tree)} ${JSON.stringify(name)} => ${tree.query}`;\n if ('children' in tree) {\n if (tree.description !== undefined) {\n s += `\\n${indent} | ${JSON.stringify(tree.description)}`;\n }\n\n for (const [name, child] of tree.children) {\n s += '\\n' + TestTree.subtreeToString(name, child, indent + ' ');\n }\n }\n return s;\n }\n}\n\n// MAINTENANCE_TODO: Consider having subqueriesToExpand actually impact the depth-order of params\n// in the tree.\nexport async function loadTreeForQuery(\n loader: TestFileLoader,\n queryToLoad: TestQuery,\n subqueriesToExpand: TestQuery[]\n): Promise<TestTree> {\n const suite = queryToLoad.suite;\n const specs = await loader.listing(suite);\n\n const subqueriesToExpandEntries = Array.from(subqueriesToExpand.entries());\n const seenSubqueriesToExpand: boolean[] = new Array(subqueriesToExpand.length);\n seenSubqueriesToExpand.fill(false);\n\n const isCollapsible = (subquery: TestQuery) =>\n subqueriesToExpandEntries.every(([i, toExpand]) => {\n const ordering = compareQueries(toExpand, subquery);\n\n // If toExpand == subquery, no expansion is needed (but it's still \"seen\").\n if (ordering === Ordering.Equal) seenSubqueriesToExpand[i] = true;\n return ordering !== Ordering.StrictSubset;\n });\n\n // L0 = suite-level, e.g. suite:*\n // L1 = file-level, e.g. suite:a,b:*\n // L2 = test-level, e.g. suite:a,b:c,d:*\n // L3 = case-level, e.g. suite:a,b:c,d:\n let foundCase = false;\n // L0 is suite:*\n const subtreeL0 = makeTreeForSuite(suite, isCollapsible);\n for (const entry of specs) {\n if (entry.file.length === 0 && 'readme' in entry) {\n // Suite-level readme.\n setSubtreeDescriptionAndCountTODOs(subtreeL0, entry.readme);\n continue;\n }\n\n {\n const queryL1 = new TestQueryMultiFile(suite, entry.file);\n const orderingL1 = compareQueries(queryL1, queryToLoad);\n if (orderingL1 === Ordering.Unordered) {\n // File path is not matched by this query.\n continue;\n }\n }\n\n if ('readme' in entry) {\n // Entry is a README that is an ancestor or descendant of the query.\n // (It's included for display in the standalone runner.)\n\n // readmeSubtree is suite:a,b,*\n // (This is always going to dedup with a file path, if there are any test spec files under\n // the directory that has the README).\n const readmeSubtree: TestSubtree<TestQueryMultiFile> = addSubtreeForDirPath(\n subtreeL0,\n entry.file,\n isCollapsible\n );\n setSubtreeDescriptionAndCountTODOs(readmeSubtree, entry.readme);\n continue;\n }\n // Entry is a spec file.\n\n const spec = await loader.importSpecFile(queryToLoad.suite, entry.file);\n // subtreeL1 is suite:a,b:*\n const subtreeL1: TestSubtree<TestQueryMultiTest> = addSubtreeForFilePath(\n subtreeL0,\n entry.file,\n isCollapsible\n );\n setSubtreeDescriptionAndCountTODOs(subtreeL1, spec.description);\n\n let groupHasTests = false;\n for (const t of spec.g.iterate()) {\n groupHasTests = true;\n {\n const queryL2 = new TestQueryMultiCase(suite, entry.file, t.testPath, {});\n const orderingL2 = compareQueries(queryL2, queryToLoad);\n if (orderingL2 === Ordering.Unordered) {\n // Test path is not matched by this query.\n continue;\n }\n }\n\n // subtreeL2 is suite:a,b:c,d:*\n const subtreeL2: TestSubtree<TestQueryMultiCase> = addSubtreeForTestPath(\n subtreeL1,\n t.testPath,\n t.testCreationStack,\n isCollapsible\n );\n // This is 1 test. Set tests=1 then count TODOs.\n subtreeL2.subtreeCounts ??= { tests: 1, nodesWithTODO: 0 };\n if (t.description) setSubtreeDescriptionAndCountTODOs(subtreeL2, t.description);\n\n // MAINTENANCE_TODO: If tree generation gets too slow, avoid actually iterating the cases in a\n // file if there's no need to (based on the subqueriesToExpand).\n for (const c of t.iterate()) {\n {\n const queryL3 = new TestQuerySingleCase(suite, entry.file, c.id.test, c.id.params);\n const orderingL3 = compareQueries(queryL3, queryToLoad);\n if (orderingL3 === Ordering.Unordered || orderingL3 === Ordering.StrictSuperset) {\n // Case is not matched by this query.\n continue;\n }\n }\n\n // Leaf for case is suite:a,b:c,d:x=1;y=2\n addLeafForCase(subtreeL2, c, isCollapsible);\n\n foundCase = true;\n }\n }\n if (!groupHasTests && !subtreeL1.subtreeCounts) {\n throw new StacklessError(\n `${subtreeL1.query} has no tests - it must have \"TODO\" in its description`\n );\n }\n }\n\n for (const [i, sq] of subqueriesToExpandEntries) {\n const subquerySeen = seenSubqueriesToExpand[i];\n if (!subquerySeen) {\n throw new StacklessError(\n `subqueriesToExpand entry did not match anything \\\n(could be wrong, or could be redundant with a previous subquery):\\n ${sq.toString()}`\n );\n }\n }\n assert(foundCase, `Query \\`${queryToLoad.toString()}\\` does not match any cases`);\n\n return new TestTree(queryToLoad, subtreeL0);\n}\n\nfunction setSubtreeDescriptionAndCountTODOs(\n subtree: TestSubtree<TestQueryMultiFile>,\n description: string\n) {\n assert(subtree.description === undefined);\n subtree.description = description.trim();\n subtree.subtreeCounts ??= { tests: 0, nodesWithTODO: 0 };\n if (subtree.description.indexOf('TODO') !== -1) {\n subtree.subtreeCounts.nodesWithTODO++;\n }\n}\n\nfunction makeTreeForSuite(\n suite: string,\n isCollapsible: (sq: TestQuery) => boolean\n): TestSubtree<TestQueryMultiFile> {\n const query = new TestQueryMultiFile(suite, []);\n return {\n readableRelativeName: suite + kBigSeparator,\n query,\n children: new Map(),\n collapsible: isCollapsible(query),\n };\n}\n\nfunction addSubtreeForDirPath(\n tree: TestSubtree<TestQueryMultiFile>,\n file: string[],\n isCollapsible: (sq: TestQuery) => boolean\n): TestSubtree<TestQueryMultiFile> {\n const subqueryFile: string[] = [];\n // To start, tree is suite:*\n // This loop goes from that -> suite:a,* -> suite:a,b,*\n for (const part of file) {\n subqueryFile.push(part);\n tree = getOrInsertSubtree(part, tree, () => {\n const query = new TestQueryMultiFile(tree.query.suite, subqueryFile);\n return {\n readableRelativeName: part + kPathSeparator + kWildcard,\n query,\n collapsible: isCollapsible(query),\n };\n });\n }\n return tree;\n}\n\nfunction addSubtreeForFilePath(\n tree: TestSubtree<TestQueryMultiFile>,\n file: string[],\n isCollapsible: (sq: TestQuery) => boolean\n): TestSubtree<TestQueryMultiTest> {\n // To start, tree is suite:*\n // This goes from that -> suite:a,* -> suite:a,b,*\n tree = addSubtreeForDirPath(tree, file, isCollapsible);\n // This goes from that -> suite:a,b:*\n const subtree = getOrInsertSubtree('', tree, () => {\n const query = new TestQueryMultiTest(tree.query.suite, tree.query.filePathParts, []);\n assert(file.length > 0, 'file path is empty');\n return {\n readableRelativeName: file[file.length - 1] + kBigSeparator + kWildcard,\n query,\n collapsible: isCollapsible(query),\n };\n });\n return subtree;\n}\n\nfunction addSubtreeForTestPath(\n tree: TestSubtree<TestQueryMultiTest>,\n test: readonly string[],\n testCreationStack: Error,\n isCollapsible: (sq: TestQuery) => boolean\n): TestSubtree<TestQueryMultiCase> {\n const subqueryTest: string[] = [];\n // To start, tree is suite:a,b:*\n // This loop goes from that -> suite:a,b:c,* -> suite:a,b:c,d,*\n for (const part of test) {\n subqueryTest.push(part);\n tree = getOrInsertSubtree(part, tree, () => {\n const query = new TestQueryMultiTest(\n tree.query.suite,\n tree.query.filePathParts,\n subqueryTest\n );\n return {\n readableRelativeName: part + kPathSeparator + kWildcard,\n query,\n collapsible: isCollapsible(query),\n };\n });\n }\n // This goes from that -> suite:a,b:c,d:*\n return getOrInsertSubtree('', tree, () => {\n const query = new TestQueryMultiCase(\n tree.query.suite,\n tree.query.filePathParts,\n subqueryTest,\n {}\n );\n assert(subqueryTest.length > 0, 'subqueryTest is empty');\n return {\n readableRelativeName: subqueryTest[subqueryTest.length - 1] + kBigSeparator + kWildcard,\n kWildcard,\n query,\n testCreationStack,\n collapsible: isCollapsible(query),\n };\n });\n}\n\nfunction addLeafForCase(\n tree: TestSubtree<TestQueryMultiTest>,\n t: RunCase,\n checkCollapsible: (sq: TestQuery) => boolean\n): void {\n const query = tree.query;\n let name: string = '';\n const subqueryParams: TestParamsRW = {};\n\n // To start, tree is suite:a,b:c,d:*\n // This loop goes from that -> suite:a,b:c,d:x=1;* -> suite:a,b:c,d:x=1;y=2;*\n for (const [k, v] of Object.entries(t.id.params)) {\n name = stringifySingleParam(k, v);\n subqueryParams[k] = v;\n\n tree = getOrInsertSubtree(name, tree, () => {\n const subquery = new TestQueryMultiCase(\n query.suite,\n query.filePathParts,\n query.testPathParts,\n subqueryParams\n );\n return {\n readableRelativeName: name + kParamSeparator + kWildcard,\n query: subquery,\n collapsible: checkCollapsible(subquery),\n };\n });\n }\n\n // This goes from that -> suite:a,b:c,d:x=1;y=2\n const subquery = new TestQuerySingleCase(\n query.suite,\n query.filePathParts,\n query.testPathParts,\n subqueryParams\n );\n checkCollapsible(subquery); // mark seenSubqueriesToExpand\n insertLeaf(tree, subquery, t);\n}\n\nfunction getOrInsertSubtree<T extends TestQuery>(\n key: string,\n parent: TestSubtree,\n createSubtree: () => Omit<TestSubtree<T>, 'children'>\n): TestSubtree<T> {\n let v: TestSubtree<T>;\n const child = parent.children.get(key);\n if (child !== undefined) {\n assert('children' in child); // Make sure cached subtree is not actually a leaf\n v = child as TestSubtree<T>;\n } else {\n v = { ...createSubtree(), children: new Map() };\n parent.children.set(key, v);\n }\n return v;\n}\n\nfunction insertLeaf(parent: TestSubtree, query: TestQuerySingleCase, t: RunCase) {\n const leaf: TestTreeLeaf = {\n readableRelativeName: readableNameForCase(query),\n query,\n run: (rec, expectations) => t.run(rec, query, expectations || []),\n isUnimplemented: t.isUnimplemented,\n };\n\n // This is a leaf (e.g. s:f:t:x=1;* -> s:f:t:x=1). The key is always ''.\n const key = '';\n assert(!parent.children.has(key), `Duplicate testcase: ${query}`);\n parent.children.set(key, leaf);\n}\n\nfunction dissolveSingleChildTrees(tree: TestTreeNode): TestTreeNode {\n if ('children' in tree) {\n const shouldDissolveThisTree =\n tree.children.size === 1 && tree.query.depthInLevel !== 0 && tree.description === undefined;\n if (shouldDissolveThisTree) {\n // Loops exactly once\n for (const [, child] of tree.children) {\n // Recurse on child\n return dissolveSingleChildTrees(child);\n }\n }\n\n for (const [k, child] of tree.children) {\n // Recurse on each child\n const newChild = dissolveSingleChildTrees(child);\n if (newChild !== child) {\n tree.children.set(k, newChild);\n }\n }\n }\n return tree;\n}\n\n/** Generate a readable relative name for a case (used in standalone). */\nfunction readableNameForCase(query: TestQuerySingleCase): string {\n const paramsKeys = Object.keys(query.params);\n if (paramsKeys.length === 0) {\n return query.testPathParts[query.testPathParts.length - 1] + kBigSeparator;\n } else {\n const lastKey = paramsKeys[paramsKeys.length - 1];\n return stringifySingleParam(lastKey, query.params[lastKey]);\n }\n}\n"],"mappings":"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"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/util.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/util.js
new file mode 100644
index 0000000000..18935e074b
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/util.js
@@ -0,0 +1,11 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/ /**
+ * Error without a stack, which can be used to fatally exit from `tool/` scripts with a
+ * user-friendly message (and no confusing stack).
+ */export class StacklessError extends Error {constructor(message) {
+ super(message);
+ this.stack = undefined;
+ }
+}
+//# sourceMappingURL=util.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/util.js.map b/testing/web-platform/mozilla/tests/webgpu/common/internal/util.js.map
new file mode 100644
index 0000000000..e29de27b3d
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/util.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"util.js","names":["StacklessError","Error","constructor","message","stack","undefined"],"sources":["../../../src/common/internal/util.ts"],"sourcesContent":["/**\n * Error without a stack, which can be used to fatally exit from `tool/` scripts with a\n * user-friendly message (and no confusing stack).\n */\nexport class StacklessError extends Error {\n constructor(message: string) {\n super(message);\n this.stack = undefined;\n }\n}\n"],"mappings":";AAAA;AAAA,G,CAAA;AACA;AACA;AACA,GACA,OAAO,MAAMA,cAAc,SAASC,KAAK,CAAC,CACxCC,WAAW,CAACC,OAAe,EAAE;IAC3B,KAAK,CAACA,OAAO,CAAC;IACd,IAAI,CAACC,KAAK,GAAGC,SAAS;EACxB;AACF"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/internal/version.js b/testing/web-platform/mozilla/tests/webgpu/common/internal/version.js
new file mode 100644
index 0000000000..3353fd94d7
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/internal/version.js
@@ -0,0 +1,3 @@
+// AUTO-GENERATED - DO NOT EDIT. See tools/gen_version.
+
+export const version = 'b3ce8f38983dc445e66c35d83f1110ce89fba9ba';
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/options.js b/testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/options.js
new file mode 100644
index 0000000000..946c0bdc11
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/options.js
@@ -0,0 +1,18 @@
+/**
+ * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+ **/ let windowURL = undefined;
+function getWindowURL() {
+ if (windowURL === undefined) {
+ windowURL = new URL(window.location.toString());
+ }
+ return windowURL;
+}
+
+export function optionEnabled(opt, searchParams = getWindowURL().searchParams) {
+ const val = searchParams.get(opt);
+ return val !== null && val !== '0';
+}
+
+export function optionString(opt, searchParams = getWindowURL().searchParams) {
+ return searchParams.get(opt) || '';
+}
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/sys.js b/testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/sys.js
new file mode 100644
index 0000000000..db7af47dab
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/sys.js
@@ -0,0 +1,37 @@
+/**
+ * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+ **/
+function node() {
+ const { existsSync } = require('fs');
+
+ return {
+ type: 'node',
+ existsSync,
+ args: process.argv.slice(2),
+ cwd: () => process.cwd(),
+ exit: code => process.exit(code),
+ };
+}
+
+function deno() {
+ function existsSync(path) {
+ try {
+ Deno.readFileSync(path);
+ return true;
+ } catch (err) {
+ return false;
+ }
+ }
+
+ return {
+ type: 'deno',
+ existsSync,
+ args: Deno.args,
+ cwd: Deno.cwd,
+ exit: Deno.exit,
+ };
+}
+
+const sys = typeof globalThis.process !== 'undefined' ? node() : deno();
+
+export default sys;
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/test_worker-worker.js b/testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/test_worker-worker.js
new file mode 100644
index 0000000000..73eaff46b2
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/test_worker-worker.js
@@ -0,0 +1,32 @@
+/**
+ * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+ **/ import { setBaseResourcePath } from '../../framework/resources.js';
+import { DefaultTestFileLoader } from '../../internal/file_loader.js';
+import { Logger } from '../../internal/logging/logger.js';
+import { parseQuery } from '../../internal/query/parseQuery.js';
+
+import { assert } from '../../util/util.js';
+
+// Should be DedicatedWorkerGlobalScope, but importing lib "webworker" conflicts with lib "dom".
+
+const loader = new DefaultTestFileLoader();
+
+setBaseResourcePath('../../../resources');
+
+self.onmessage = async ev => {
+ const query = ev.data.query;
+ const expectations = ev.data.expectations;
+ const debug = ev.data.debug;
+
+ Logger.globalDebugMode = debug;
+ const log = new Logger();
+
+ const testcases = Array.from(await loader.loadCases(parseQuery(query)));
+ assert(testcases.length === 1, 'worker query resulted in != 1 cases');
+
+ const testcase = testcases[0];
+ const [rec, result] = log.record(testcase.query.toString());
+ await testcase.run(rec, expectations);
+
+ self.postMessage({ query, result });
+};
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/test_worker.js b/testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/test_worker.js
new file mode 100644
index 0000000000..f3f9dc62e1
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/runtime/helper/test_worker.js
@@ -0,0 +1,37 @@
+/**
+ * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+ **/ import { LogMessageWithStack } from '../../internal/logging/log_message.js';
+
+export class TestWorker {
+ resolvers = new Map();
+
+ constructor(debug) {
+ this.debug = debug;
+
+ const selfPath = import.meta.url;
+ const selfPathDir = selfPath.substring(0, selfPath.lastIndexOf('/'));
+ const workerPath = selfPathDir + '/test_worker-worker.js';
+ this.worker = new Worker(workerPath, { type: 'module' });
+ this.worker.onmessage = ev => {
+ const query = ev.data.query;
+ const result = ev.data.result;
+ if (result.logs) {
+ for (const l of result.logs) {
+ Object.setPrototypeOf(l, LogMessageWithStack.prototype);
+ }
+ }
+ this.resolvers.get(query)(result);
+
+ // MAINTENANCE_TODO(kainino0x): update the Logger with this result (or don't have a logger and
+ // update the entire results JSON somehow at some point).
+ };
+ }
+
+ async run(rec, query, expectations = []) {
+ this.worker.postMessage({ query, expectations, debug: this.debug });
+ const workerResult = await new Promise(resolve => {
+ this.resolvers.set(query, resolve);
+ });
+ rec.injectResult(workerResult);
+ }
+}
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/runtime/wpt.js b/testing/web-platform/mozilla/tests/webgpu/common/runtime/wpt.js
new file mode 100644
index 0000000000..90294223c0
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/runtime/wpt.js
@@ -0,0 +1,73 @@
+/**
+ * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+ **/ // Implements the wpt-embedded test runner (see also: wpt/cts.https.html).
+import { globalTestConfig } from '../framework/test_config.js';
+import { DefaultTestFileLoader } from '../internal/file_loader.js';
+import { prettyPrintLog } from '../internal/logging/log_message.js';
+import { Logger } from '../internal/logging/logger.js';
+import { parseQuery } from '../internal/query/parseQuery.js';
+import { parseExpectationsForTestQuery, relativeQueryString } from '../internal/query/query.js';
+import { assert } from '../util/util.js';
+
+import { optionEnabled } from './helper/options.js';
+import { TestWorker } from './helper/test_worker.js';
+
+// testharness.js API (https://web-platform-tests.org/writing-tests/testharness-api.html)
+
+setup({
+ // It's convenient for us to asynchronously add tests to the page. Prevent done() from being
+ // called implicitly when the page is finished loading.
+ explicit_done: true,
+});
+
+void (async () => {
+ const workerEnabled = optionEnabled('worker');
+ const worker = workerEnabled ? new TestWorker(false) : undefined;
+
+ globalTestConfig.unrollConstEvalLoops = optionEnabled('unroll_const_eval_loops');
+
+ const failOnWarnings =
+ typeof shouldWebGPUCTSFailOnWarnings !== 'undefined' && (await shouldWebGPUCTSFailOnWarnings);
+
+ const loader = new DefaultTestFileLoader();
+ const qs = new URLSearchParams(window.location.search).getAll('q');
+ assert(qs.length === 1, 'currently, there must be exactly one ?q=');
+ const filterQuery = parseQuery(qs[0]);
+ const testcases = await loader.loadCases(filterQuery);
+
+ const expectations =
+ typeof loadWebGPUExpectations !== 'undefined'
+ ? parseExpectationsForTestQuery(
+ await loadWebGPUExpectations,
+ filterQuery,
+ new URL(window.location.href)
+ )
+ : [];
+
+ const log = new Logger();
+
+ for (const testcase of testcases) {
+ const name = testcase.query.toString();
+ // For brevity, display the case name "relative" to the ?q= path.
+ const shortName = relativeQueryString(filterQuery, testcase.query) || '(case)';
+
+ const wpt_fn = async () => {
+ const [rec, res] = log.record(name);
+ if (worker) {
+ await worker.run(rec, name, expectations);
+ } else {
+ await testcase.run(rec, expectations);
+ }
+
+ // Unfortunately, it seems not possible to surface any logs for warn/skip.
+ if (res.status === 'fail' || (res.status === 'warn' && failOnWarnings)) {
+ const logs = (res.logs ?? []).map(prettyPrintLog);
+ assert_unreached('\n' + logs.join('\n') + '\n');
+ }
+ };
+
+ promise_test(wpt_fn, shortName);
+ }
+
+ done();
+})();
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/collect_garbage.js b/testing/web-platform/mozilla/tests/webgpu/common/util/collect_garbage.js
new file mode 100644
index 0000000000..0c078a11b9
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/collect_garbage.js
@@ -0,0 +1,59 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { resolveOnTimeout } from './util.js';
+
+
+/**
+ * Attempts to trigger JavaScript garbage collection, either using explicit methods if exposed
+ * (may be available in testing environments with special browser runtime flags set), or using
+ * some weird tricks to incur GC pressure. Adopted from the WebGL CTS.
+ */
+export async function attemptGarbageCollection() {
+
+ const w = globalThis;
+ if (w.GCController) {
+ w.GCController.collect();
+ return;
+ }
+
+ if (w.opera && w.opera.collect) {
+ w.opera.collect();
+ return;
+ }
+
+ try {
+ w.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIDOMWindowUtils).
+ garbageCollect();
+ return;
+ } catch (e) {
+
+ // ignore any failure
+ }
+ if (w.gc) {
+ w.gc();
+ return;
+ }
+
+ if (w.CollectGarbage) {
+ w.CollectGarbage();
+ return;
+ }
+
+ let i;
+ function gcRec(n) {
+ if (n < 1) return;
+
+ let temp = { i: 'ab' + i + i / 100000 };
+
+ temp = temp + 'foo';
+ temp; // dummy use of unused variable
+ gcRec(n - 1);
+ }
+ for (i = 0; i < 1000; i++) {
+ gcRec(10);
+ }
+
+ return resolveOnTimeout(35); // Let the event loop run a few frames in case it helps.
+}
+//# sourceMappingURL=collect_garbage.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/collect_garbage.js.map b/testing/web-platform/mozilla/tests/webgpu/common/util/collect_garbage.js.map
new file mode 100644
index 0000000000..59020707d6
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/collect_garbage.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"collect_garbage.js","names":["resolveOnTimeout","attemptGarbageCollection","w","globalThis","GCController","collect","opera","QueryInterface","Components","interfaces","nsIInterfaceRequestor","getInterface","nsIDOMWindowUtils","garbageCollect","e","gc","CollectGarbage","i","gcRec","n","temp"],"sources":["../../../src/common/util/collect_garbage.ts"],"sourcesContent":["import { resolveOnTimeout } from './util.js';\n\n/* eslint-disable-next-line @typescript-eslint/no-explicit-any */\ndeclare const Components: any;\n\n/**\n * Attempts to trigger JavaScript garbage collection, either using explicit methods if exposed\n * (may be available in testing environments with special browser runtime flags set), or using\n * some weird tricks to incur GC pressure. Adopted from the WebGL CTS.\n */\nexport async function attemptGarbageCollection(): Promise<void> {\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n const w: any = globalThis;\n if (w.GCController) {\n w.GCController.collect();\n return;\n }\n\n if (w.opera && w.opera.collect) {\n w.opera.collect();\n return;\n }\n\n try {\n w.QueryInterface(Components.interfaces.nsIInterfaceRequestor)\n .getInterface(Components.interfaces.nsIDOMWindowUtils)\n .garbageCollect();\n return;\n } catch (e) {\n // ignore any failure\n }\n\n if (w.gc) {\n w.gc();\n return;\n }\n\n if (w.CollectGarbage) {\n w.CollectGarbage();\n return;\n }\n\n let i: number;\n function gcRec(n: number): void {\n if (n < 1) return;\n /* eslint-disable @typescript-eslint/restrict-plus-operands */\n let temp: object | string = { i: 'ab' + i + i / 100000 };\n /* eslint-disable @typescript-eslint/restrict-plus-operands */\n temp = temp + 'foo';\n temp; // dummy use of unused variable\n gcRec(n - 1);\n }\n for (i = 0; i < 1000; i++) {\n gcRec(10);\n }\n\n return resolveOnTimeout(35); // Let the event loop run a few frames in case it helps.\n}\n"],"mappings":";AAAA;AAAA,GAAA,SAASA,gBAAgB,QAAQ,WAAW;;;AAK5C;AACA;AACA;AACA;AACA;AACA,OAAO,eAAeC,wBAAwB,GAAkB;;EAE9D,MAAMC,CAAM,GAAGC,UAAU;EACzB,IAAID,CAAC,CAACE,YAAY,EAAE;IAClBF,CAAC,CAACE,YAAY,CAACC,OAAO,EAAE;IACxB;EACF;;EAEA,IAAIH,CAAC,CAACI,KAAK,IAAIJ,CAAC,CAACI,KAAK,CAACD,OAAO,EAAE;IAC9BH,CAAC,CAACI,KAAK,CAACD,OAAO,EAAE;IACjB;EACF;;EAEA,IAAI;IACFH,CAAC,CAACK,cAAc,CAACC,UAAU,CAACC,UAAU,CAACC,qBAAqB,CAAC;IAC1DC,YAAY,CAACH,UAAU,CAACC,UAAU,CAACG,iBAAiB,CAAC;IACrDC,cAAc,EAAE;IACnB;EACF,CAAC,CAAC,OAAOC,CAAC,EAAE;;IACV;EAAA;EAGF,IAAIZ,CAAC,CAACa,EAAE,EAAE;IACRb,CAAC,CAACa,EAAE,EAAE;IACN;EACF;;EAEA,IAAIb,CAAC,CAACc,cAAc,EAAE;IACpBd,CAAC,CAACc,cAAc,EAAE;IAClB;EACF;;EAEA,IAAIC,CAAS;EACb,SAASC,KAAK,CAACC,CAAS,EAAQ;IAC9B,IAAIA,CAAC,GAAG,CAAC,EAAE;;IAEX,IAAIC,IAAqB,GAAG,EAAEH,CAAC,EAAE,IAAI,GAAGA,CAAC,GAAGA,CAAC,GAAG,MAAM,CAAC,CAAC;;IAExDG,IAAI,GAAGA,IAAI,GAAG,KAAK;IACnBA,IAAI,CAAC,CAAC;IACNF,KAAK,CAACC,CAAC,GAAG,CAAC,CAAC;EACd;EACA,KAAKF,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAG,IAAI,EAAEA,CAAC,EAAE,EAAE;IACzBC,KAAK,CAAC,EAAE,CAAC;EACX;;EAEA,OAAOlB,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/colors.js b/testing/web-platform/mozilla/tests/webgpu/common/util/colors.js
new file mode 100644
index 0000000000..7f8f11eacb
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/colors.js
@@ -0,0 +1,128 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/**
+ * The interface used for formatting strings with color metadata.
+ *
+ * Currently Colors will use the 'ansi-colors' module if it can be loaded.
+ * If it cannot be loaded, then the Colors implementation is a straight pass-through.
+ *
+ * Colors may also be a no-op if the current environment does not support colors.
+ */
+export let Colors;
+
+try {
+
+ Colors = require('ansi-colors');
+} catch {
+ const passthrough = (s) => s;
+ passthrough.enabled = false;
+ passthrough.reset = passthrough;
+ passthrough.bold = passthrough;
+ passthrough.dim = passthrough;
+ passthrough.italic = passthrough;
+ passthrough.underline = passthrough;
+ passthrough.inverse = passthrough;
+ passthrough.hidden = passthrough;
+ passthrough.strikethrough = passthrough;
+ passthrough.black = passthrough;
+ passthrough.red = passthrough;
+ passthrough.green = passthrough;
+ passthrough.yellow = passthrough;
+ passthrough.blue = passthrough;
+ passthrough.magenta = passthrough;
+ passthrough.cyan = passthrough;
+ passthrough.white = passthrough;
+ passthrough.gray = passthrough;
+ passthrough.grey = passthrough;
+ passthrough.blackBright = passthrough;
+ passthrough.redBright = passthrough;
+ passthrough.greenBright = passthrough;
+ passthrough.yellowBright = passthrough;
+ passthrough.blueBright = passthrough;
+ passthrough.magentaBright = passthrough;
+ passthrough.cyanBright = passthrough;
+ passthrough.whiteBright = passthrough;
+ passthrough.bgBlack = passthrough;
+ passthrough.bgRed = passthrough;
+ passthrough.bgGreen = passthrough;
+ passthrough.bgYellow = passthrough;
+ passthrough.bgBlue = passthrough;
+ passthrough.bgMagenta = passthrough;
+ passthrough.bgCyan = passthrough;
+ passthrough.bgWhite = passthrough;
+ passthrough.bgBlackBright = passthrough;
+ passthrough.bgRedBright = passthrough;
+ passthrough.bgGreenBright = passthrough;
+ passthrough.bgYellowBright = passthrough;
+ passthrough.bgBlueBright = passthrough;
+ passthrough.bgMagentaBright = passthrough;
+ passthrough.bgCyanBright = passthrough;
+ passthrough.bgWhiteBright = passthrough;
+ Colors = passthrough;
+}
+//# sourceMappingURL=colors.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/colors.js.map b/testing/web-platform/mozilla/tests/webgpu/common/util/colors.js.map
new file mode 100644
index 0000000000..581265e20f
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/colors.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"colors.js","names":["Colors","require","passthrough","s","enabled","reset","bold","dim","italic","underline","inverse","hidden","strikethrough","black","red","green","yellow","blue","magenta","cyan","white","gray","grey","blackBright","redBright","greenBright","yellowBright","blueBright","magentaBright","cyanBright","whiteBright","bgBlack","bgRed","bgGreen","bgYellow","bgBlue","bgMagenta","bgCyan","bgWhite","bgBlackBright","bgRedBright","bgGreenBright","bgYellowBright","bgBlueBright","bgMagentaBright","bgCyanBright","bgWhiteBright"],"sources":["../../../src/common/util/colors.ts"],"sourcesContent":["/**\n * The interface used for formatting strings to contain color metadata.\n *\n * Use the interface properties to construct a style, then use the\n * `(s: string): string` function to format the provided string with the given\n * style.\n */\nexport interface Colors {\n // Are colors enabled?\n enabled: boolean;\n\n // Returns the string formatted to contain the specified color or style.\n (s: string): string;\n\n // modifiers\n reset: Colors;\n bold: Colors;\n dim: Colors;\n italic: Colors;\n underline: Colors;\n inverse: Colors;\n hidden: Colors;\n strikethrough: Colors;\n\n // colors\n black: Colors;\n red: Colors;\n green: Colors;\n yellow: Colors;\n blue: Colors;\n magenta: Colors;\n cyan: Colors;\n white: Colors;\n gray: Colors;\n grey: Colors;\n\n // bright colors\n blackBright: Colors;\n redBright: Colors;\n greenBright: Colors;\n yellowBright: Colors;\n blueBright: Colors;\n magentaBright: Colors;\n cyanBright: Colors;\n whiteBright: Colors;\n\n // background colors\n bgBlack: Colors;\n bgRed: Colors;\n bgGreen: Colors;\n bgYellow: Colors;\n bgBlue: Colors;\n bgMagenta: Colors;\n bgCyan: Colors;\n bgWhite: Colors;\n\n // bright background colors\n bgBlackBright: Colors;\n bgRedBright: Colors;\n bgGreenBright: Colors;\n bgYellowBright: Colors;\n bgBlueBright: Colors;\n bgMagentaBright: Colors;\n bgCyanBright: Colors;\n bgWhiteBright: Colors;\n}\n\n/**\n * The interface used for formatting strings with color metadata.\n *\n * Currently Colors will use the 'ansi-colors' module if it can be loaded.\n * If it cannot be loaded, then the Colors implementation is a straight pass-through.\n *\n * Colors may also be a no-op if the current environment does not support colors.\n */\nexport let Colors: Colors;\n\ntry {\n /* eslint-disable-next-line node/no-unpublished-require */\n Colors = require('ansi-colors') as Colors;\n} catch {\n const passthrough = ((s: string) => s) as Colors;\n passthrough.enabled = false;\n passthrough.reset = passthrough;\n passthrough.bold = passthrough;\n passthrough.dim = passthrough;\n passthrough.italic = passthrough;\n passthrough.underline = passthrough;\n passthrough.inverse = passthrough;\n passthrough.hidden = passthrough;\n passthrough.strikethrough = passthrough;\n passthrough.black = passthrough;\n passthrough.red = passthrough;\n passthrough.green = passthrough;\n passthrough.yellow = passthrough;\n passthrough.blue = passthrough;\n passthrough.magenta = passthrough;\n passthrough.cyan = passthrough;\n passthrough.white = passthrough;\n passthrough.gray = passthrough;\n passthrough.grey = passthrough;\n passthrough.blackBright = passthrough;\n passthrough.redBright = passthrough;\n passthrough.greenBright = passthrough;\n passthrough.yellowBright = passthrough;\n passthrough.blueBright = passthrough;\n passthrough.magentaBright = passthrough;\n passthrough.cyanBright = passthrough;\n passthrough.whiteBright = passthrough;\n passthrough.bgBlack = passthrough;\n passthrough.bgRed = passthrough;\n passthrough.bgGreen = passthrough;\n passthrough.bgYellow = passthrough;\n passthrough.bgBlue = passthrough;\n passthrough.bgMagenta = passthrough;\n passthrough.bgCyan = passthrough;\n passthrough.bgWhite = passthrough;\n passthrough.bgBlackBright = passthrough;\n passthrough.bgRedBright = passthrough;\n passthrough.bgGreenBright = passthrough;\n passthrough.bgYellowBright = passthrough;\n passthrough.bgBlueBright = passthrough;\n passthrough.bgMagentaBright = passthrough;\n passthrough.bgCyanBright = passthrough;\n passthrough.bgWhiteBright = passthrough;\n Colors = passthrough;\n}\n"],"mappings":";AAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,IAAIA,MAAc;;AAEzB,IAAI;;EAEFA,MAAM,GAAGC,OAAO,CAAC,aAAa,CAAW;AAC3C,CAAC,CAAC,MAAM;EACN,MAAMC,WAAW,GAAI,CAACC,CAAS,KAAKA,CAAY;EAChDD,WAAW,CAACE,OAAO,GAAG,KAAK;EAC3BF,WAAW,CAACG,KAAK,GAAGH,WAAW;EAC/BA,WAAW,CAACI,IAAI,GAAGJ,WAAW;EAC9BA,WAAW,CAACK,GAAG,GAAGL,WAAW;EAC7BA,WAAW,CAACM,MAAM,GAAGN,WAAW;EAChCA,WAAW,CAACO,SAAS,GAAGP,WAAW;EACnCA,WAAW,CAACQ,OAAO,GAAGR,WAAW;EACjCA,WAAW,CAACS,MAAM,GAAGT,WAAW;EAChCA,WAAW,CAACU,aAAa,GAAGV,WAAW;EACvCA,WAAW,CAACW,KAAK,GAAGX,WAAW;EAC/BA,WAAW,CAACY,GAAG,GAAGZ,WAAW;EAC7BA,WAAW,CAACa,KAAK,GAAGb,WAAW;EAC/BA,WAAW,CAACc,MAAM,GAAGd,WAAW;EAChCA,WAAW,CAACe,IAAI,GAAGf,WAAW;EAC9BA,WAAW,CAACgB,OAAO,GAAGhB,WAAW;EACjCA,WAAW,CAACiB,IAAI,GAAGjB,WAAW;EAC9BA,WAAW,CAACkB,KAAK,GAAGlB,WAAW;EAC/BA,WAAW,CAACmB,IAAI,GAAGnB,WAAW;EAC9BA,WAAW,CAACoB,IAAI,GAAGpB,WAAW;EAC9BA,WAAW,CAACqB,WAAW,GAAGrB,WAAW;EACrCA,WAAW,CAACsB,SAAS,GAAGtB,WAAW;EACnCA,WAAW,CAACuB,WAAW,GAAGvB,WAAW;EACrCA,WAAW,CAACwB,YAAY,GAAGxB,WAAW;EACtCA,WAAW,CAACyB,UAAU,GAAGzB,WAAW;EACpCA,WAAW,CAAC0B,aAAa,GAAG1B,WAAW;EACvCA,WAAW,CAAC2B,UAAU,GAAG3B,WAAW;EACpCA,WAAW,CAAC4B,WAAW,GAAG5B,WAAW;EACrCA,WAAW,CAAC6B,OAAO,GAAG7B,WAAW;EACjCA,WAAW,CAAC8B,KAAK,GAAG9B,WAAW;EAC/BA,WAAW,CAAC+B,OAAO,GAAG/B,WAAW;EACjCA,WAAW,CAACgC,QAAQ,GAAGhC,WAAW;EAClCA,WAAW,CAACiC,MAAM,GAAGjC,WAAW;EAChCA,WAAW,CAACkC,SAAS,GAAGlC,WAAW;EACnCA,WAAW,CAACmC,MAAM,GAAGnC,WAAW;EAChCA,WAAW,CAACoC,OAAO,GAAGpC,WAAW;EACjCA,WAAW,CAACqC,aAAa,GAAGrC,WAAW;EACvCA,WAAW,CAACsC,WAAW,GAAGtC,WAAW;EACrCA,WAAW,CAACuC,aAAa,GAAGvC,WAAW;EACvCA,WAAW,CAACwC,cAAc,GAAGxC,WAAW;EACxCA,WAAW,CAACyC,YAAY,GAAGzC,WAAW;EACtCA,WAAW,CAAC0C,eAAe,GAAG1C,WAAW;EACzCA,WAAW,CAAC2C,YAAY,GAAG3C,WAAW;EACtCA,WAAW,CAAC4C,aAAa,GAAG5C,WAAW;EACvCF,MAAM,GAAGE,WAAW;AACtB"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/data_tables.js b/testing/web-platform/mozilla/tests/webgpu/common/util/data_tables.js
new file mode 100644
index 0000000000..845cebf1fc
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/data_tables.js
@@ -0,0 +1,40 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+export function keysOf(obj) {
+ return Object.keys(obj);
+}
+
+export function numericKeysOf(obj) {
+ return Object.keys(obj).map((n) => Number(n));
+}
+
+/**
+ * Creates an info lookup object from a more nicely-formatted table. See below for examples.
+ *
+ * Note: Using `as const` on the arguments to this function is necessary to infer the correct type.
+ */
+export function makeTable(
+
+
+
+
+members,
+defaults,
+table)
+
+
+{
+ const result = {};
+ for (const [k, v] of Object.entries(table)) {
+ const item = {};
+ for (let i = 0; i < members.length; ++i) {
+ item[members[i]] = v[i] ?? defaults[i];
+ }
+ result[k] = item;
+ }
+
+ return result;
+}
+//# sourceMappingURL=data_tables.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/data_tables.js.map b/testing/web-platform/mozilla/tests/webgpu/common/util/data_tables.js.map
new file mode 100644
index 0000000000..512f535412
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/data_tables.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"data_tables.js","names":["keysOf","obj","Object","keys","numericKeysOf","map","n","Number","makeTable","members","defaults","table","result","k","v","entries","item","i","length"],"sources":["../../../src/common/util/data_tables.ts"],"sourcesContent":["import { ResolveType, ZipKeysWithValues } from './types.js';\n\nexport type valueof<K> = K[keyof K];\n\nexport function keysOf<T extends string>(obj: { [k in T]: unknown }): readonly T[] {\n return (Object.keys(obj) as unknown[]) as T[];\n}\n\nexport function numericKeysOf<T>(obj: object): readonly T[] {\n return (Object.keys(obj).map(n => Number(n)) as unknown[]) as T[];\n}\n\n/**\n * Creates an info lookup object from a more nicely-formatted table. See below for examples.\n *\n * Note: Using `as const` on the arguments to this function is necessary to infer the correct type.\n */\nexport function makeTable<\n Members extends readonly string[],\n Defaults extends readonly unknown[],\n Table extends { readonly [k: string]: readonly unknown[] }\n>(\n members: Members,\n defaults: Defaults,\n table: Table\n): {\n readonly [k in keyof Table]: ResolveType<ZipKeysWithValues<Members, Table[k], Defaults>>;\n} {\n const result: { [k: string]: { [m: string]: unknown } } = {};\n for (const [k, v] of Object.entries<readonly unknown[]>(table)) {\n const item: { [m: string]: unknown } = {};\n for (let i = 0; i < members.length; ++i) {\n item[members[i]] = v[i] ?? defaults[i];\n }\n result[k] = item;\n }\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n return result as any;\n}\n"],"mappings":";AAAA;AAAA;;AAIA,OAAO,SAASA,MAAM,CAAmBC,GAA0B,EAAgB;EACjF,OAAQC,MAAM,CAACC,IAAI,CAACF,GAAG,CAAC;AAC1B;;AAEA,OAAO,SAASG,aAAa,CAAIH,GAAW,EAAgB;EAC1D,OAAQC,MAAM,CAACC,IAAI,CAACF,GAAG,CAAC,CAACI,GAAG,CAAC,CAAAC,CAAC,KAAIC,MAAM,CAACD,CAAC,CAAC,CAAC;AAC9C;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASE,SAAS;;;;;AAKvBC,OAAgB;AAChBC,QAAkB;AAClBC,KAAY;;;AAGZ;EACA,MAAMC,MAAiD,GAAG,CAAC,CAAC;EAC5D,KAAK,MAAM,CAACC,CAAC,EAAEC,CAAC,CAAC,IAAIZ,MAAM,CAACa,OAAO,CAAqBJ,KAAK,CAAC,EAAE;IAC9D,MAAMK,IAA8B,GAAG,CAAC,CAAC;IACzC,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGR,OAAO,CAACS,MAAM,EAAE,EAAED,CAAC,EAAE;MACvCD,IAAI,CAACP,OAAO,CAACQ,CAAC,CAAC,CAAC,GAAGH,CAAC,CAACG,CAAC,CAAC,IAAIP,QAAQ,CAACO,CAAC,CAAC;IACxC;IACAL,MAAM,CAACC,CAAC,CAAC,GAAGG,IAAI;EAClB;;EAEA,OAAOJ,MAAM;AACf"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/navigator_gpu.js b/testing/web-platform/mozilla/tests/webgpu/common/util/navigator_gpu.js
new file mode 100644
index 0000000000..75330edbd2
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/navigator_gpu.js
@@ -0,0 +1,75 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/ /// <reference types="@webgpu/types" />
+import { assert } from './util.js';
+/**
+ * Finds and returns the `navigator.gpu` object (or equivalent, for non-browser implementations).
+ * Throws an exception if not found.
+ */
+function defaultGPUProvider() {
+ assert(
+ typeof navigator !== 'undefined' && navigator.gpu !== undefined,
+ 'No WebGPU implementation found');
+
+ return navigator.gpu;
+}
+
+/**
+ * GPUProvider is a function that creates and returns a new GPU instance.
+ * May throw an exception if a GPU cannot be created.
+ */
+
+
+let gpuProvider = defaultGPUProvider;
+
+/**
+ * Sets the function to create and return a new GPU instance.
+ */
+export function setGPUProvider(provider) {
+ assert(impl === undefined, 'setGPUProvider() should not be after getGPU()');
+ gpuProvider = provider;
+}
+
+let impl = undefined;
+
+let defaultRequestAdapterOptions;
+
+export function setDefaultRequestAdapterOptions(options) {
+ if (impl) {
+ throw new Error('must call setDefaultRequestAdapterOptions before getGPU');
+ }
+ defaultRequestAdapterOptions = { ...options };
+}
+
+/**
+ * Finds and returns the `navigator.gpu` object (or equivalent, for non-browser implementations).
+ * Throws an exception if not found.
+ */
+export function getGPU() {
+ if (impl) {
+ return impl;
+ }
+
+ impl = gpuProvider();
+
+ if (defaultRequestAdapterOptions) {
+
+ const oldFn = impl.requestAdapter;
+ impl.requestAdapter = function (
+ options)
+ {
+ const promise = oldFn.call(this, { ...defaultRequestAdapterOptions, ...(options || {}) });
+ void promise.then(async (adapter) => {
+ if (adapter) {
+ const info = await adapter.requestAdapterInfo();
+
+ console.log(info);
+ }
+ });
+ return promise;
+ };
+ }
+
+ return impl;
+}
+//# sourceMappingURL=navigator_gpu.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/navigator_gpu.js.map b/testing/web-platform/mozilla/tests/webgpu/common/util/navigator_gpu.js.map
new file mode 100644
index 0000000000..cabc9ffd18
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/navigator_gpu.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"navigator_gpu.js","names":["assert","defaultGPUProvider","navigator","gpu","undefined","gpuProvider","setGPUProvider","provider","impl","defaultRequestAdapterOptions","setDefaultRequestAdapterOptions","options","Error","getGPU","oldFn","requestAdapter","promise","call","then","adapter","info","requestAdapterInfo","console","log"],"sources":["../../../src/common/util/navigator_gpu.ts"],"sourcesContent":["/// <reference types=\"@webgpu/types\" />\n\nimport { assert } from './util.js';\n\n/**\n * Finds and returns the `navigator.gpu` object (or equivalent, for non-browser implementations).\n * Throws an exception if not found.\n */\nfunction defaultGPUProvider(): GPU {\n assert(\n typeof navigator !== 'undefined' && navigator.gpu !== undefined,\n 'No WebGPU implementation found'\n );\n return navigator.gpu;\n}\n\n/**\n * GPUProvider is a function that creates and returns a new GPU instance.\n * May throw an exception if a GPU cannot be created.\n */\nexport type GPUProvider = () => GPU;\n\nlet gpuProvider: GPUProvider = defaultGPUProvider;\n\n/**\n * Sets the function to create and return a new GPU instance.\n */\nexport function setGPUProvider(provider: GPUProvider) {\n assert(impl === undefined, 'setGPUProvider() should not be after getGPU()');\n gpuProvider = provider;\n}\n\nlet impl: GPU | undefined = undefined;\n\nlet defaultRequestAdapterOptions: GPURequestAdapterOptions | undefined;\n\nexport function setDefaultRequestAdapterOptions(options: GPURequestAdapterOptions) {\n if (impl) {\n throw new Error('must call setDefaultRequestAdapterOptions before getGPU');\n }\n defaultRequestAdapterOptions = { ...options };\n}\n\n/**\n * Finds and returns the `navigator.gpu` object (or equivalent, for non-browser implementations).\n * Throws an exception if not found.\n */\nexport function getGPU(): GPU {\n if (impl) {\n return impl;\n }\n\n impl = gpuProvider();\n\n if (defaultRequestAdapterOptions) {\n // eslint-disable-next-line @typescript-eslint/unbound-method\n const oldFn = impl.requestAdapter;\n impl.requestAdapter = function (\n options?: GPURequestAdapterOptions\n ): Promise<GPUAdapter | null> {\n const promise = oldFn.call(this, { ...defaultRequestAdapterOptions, ...(options || {}) });\n void promise.then(async adapter => {\n if (adapter) {\n const info = await adapter.requestAdapterInfo();\n // eslint-disable-next-line no-console\n console.log(info);\n }\n });\n return promise;\n };\n }\n\n return impl;\n}\n"],"mappings":";AAAA;AAAA,G,CAAA;AAEA,SAASA,MAAM,QAAQ,WAAW;AAElC;AACA;AACA;AACA;AACA,SAASC,kBAAkB,GAAQ;EACjCD,MAAM;EACJ,OAAOE,SAAS,KAAK,WAAW,IAAIA,SAAS,CAACC,GAAG,KAAKC,SAAS;EAC/D,gCAAgC,CACjC;;EACD,OAAOF,SAAS,CAACC,GAAG;AACtB;;AAEA;AACA;AACA;AACA;;;AAGA,IAAIE,WAAwB,GAAGJ,kBAAkB;;AAEjD;AACA;AACA;AACA,OAAO,SAASK,cAAc,CAACC,QAAqB,EAAE;EACpDP,MAAM,CAACQ,IAAI,KAAKJ,SAAS,EAAE,+CAA+C,CAAC;EAC3EC,WAAW,GAAGE,QAAQ;AACxB;;AAEA,IAAIC,IAAqB,GAAGJ,SAAS;;AAErC,IAAIK,4BAAkE;;AAEtE,OAAO,SAASC,+BAA+B,CAACC,OAAiC,EAAE;EACjF,IAAIH,IAAI,EAAE;IACR,MAAM,IAAII,KAAK,CAAC,yDAAyD,CAAC;EAC5E;EACAH,4BAA4B,GAAG,EAAE,GAAGE,OAAO,CAAC,CAAC;AAC/C;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASE,MAAM,GAAQ;EAC5B,IAAIL,IAAI,EAAE;IACR,OAAOA,IAAI;EACb;;EAEAA,IAAI,GAAGH,WAAW,EAAE;;EAEpB,IAAII,4BAA4B,EAAE;;IAEhC,MAAMK,KAAK,GAAGN,IAAI,CAACO,cAAc;IACjCP,IAAI,CAACO,cAAc,GAAG;IACpBJ,OAAkC;IACN;MAC5B,MAAMK,OAAO,GAAGF,KAAK,CAACG,IAAI,CAAC,IAAI,EAAE,EAAE,GAAGR,4BAA4B,EAAE,IAAIE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;MACzF,KAAKK,OAAO,CAACE,IAAI,CAAC,OAAMC,OAAO,KAAI;QACjC,IAAIA,OAAO,EAAE;UACX,MAAMC,IAAI,GAAG,MAAMD,OAAO,CAACE,kBAAkB,EAAE;;UAE/CC,OAAO,CAACC,GAAG,CAACH,IAAI,CAAC;QACnB;MACF,CAAC,CAAC;MACF,OAAOJ,OAAO;IAChB,CAAC;EACH;;EAEA,OAAOR,IAAI;AACb"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/preprocessor.js b/testing/web-platform/mozilla/tests/webgpu/common/util/preprocessor.js
new file mode 100644
index 0000000000..23ffe4c45a
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/preprocessor.js
@@ -0,0 +1,150 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { assert } from './util.js'; // The state of the preprocessor is a stack of States.
+var
+State;
+
+
+// Have already seen a passing condition; now skipping the rest
+
+
+// The transitions in the state space are the following preprocessor directives:
+// - Sibling elif
+// - Sibling else
+// - Sibling endif
+// - Child if
+(function (State) {State[State["Seeking"] = 0] = "Seeking";State[State["Passing"] = 1] = "Passing";State[State["Skipping"] = 2] = "Skipping";})(State || (State = {}));class Directive {
+
+
+ constructor(depth) {
+ this.depth = depth;
+ }
+
+ checkDepth(stack) {
+ assert(
+ stack.length === this.depth,
+ `Number of "$"s must match nesting depth, currently ${stack.length} (e.g. $if $$if $$endif $endif)`);
+
+ }
+
+
+}
+
+class If extends Directive {
+
+
+ constructor(depth, predicate) {
+ super(depth);
+ this.predicate = predicate;
+ }
+
+ applyTo(stack) {
+ this.checkDepth(stack);
+ const parentState = stack[stack.length - 1].state;
+ stack.push({
+ allowsFollowingElse: true,
+ state:
+ parentState !== State.Passing ?
+ State.Skipping :
+ this.predicate ?
+ State.Passing :
+ State.Seeking
+ });
+ }
+}
+
+class ElseIf extends If {
+ applyTo(stack) {
+ assert(stack.length >= 1);
+ const { allowsFollowingElse, state: siblingState } = stack.pop();
+ this.checkDepth(stack);
+ assert(allowsFollowingElse, 'pp.elif after pp.else');
+ if (siblingState !== State.Seeking) {
+ stack.push({ allowsFollowingElse: true, state: State.Skipping });
+ } else {
+ super.applyTo(stack);
+ }
+ }
+}
+
+class Else extends Directive {
+ applyTo(stack) {
+ assert(stack.length >= 1);
+ const { allowsFollowingElse, state: siblingState } = stack.pop();
+ this.checkDepth(stack);
+ assert(allowsFollowingElse, 'pp.else after pp.else');
+ stack.push({
+ allowsFollowingElse: false,
+ state: siblingState === State.Seeking ? State.Passing : State.Skipping
+ });
+ }
+}
+
+class EndIf extends Directive {
+ applyTo(stack) {
+ stack.pop();
+ this.checkDepth(stack);
+ }
+}
+
+/**
+ * A simple template-based, non-line-based preprocessor implementing if/elif/else/endif.
+ *
+ * @example
+ * ```
+ * const shader = pp`
+ * ${pp._if(expr)}
+ * const x: ${type} = ${value};
+ * ${pp._elif(expr)}
+ * ${pp.__if(expr)}
+ * ...
+ * ${pp.__else}
+ * ...
+ * ${pp.__endif}
+ * ${pp._endif}`;
+ * ```
+ *
+ * @param strings - The array of constant string chunks of the template string.
+ * @param ...values - The array of interpolated `${}` values within the template string.
+ */
+export function pp(
+strings,
+...values)
+{
+ let result = '';
+ const stateStack = [{ allowsFollowingElse: false, state: State.Passing }];
+
+ for (let i = 0; i < values.length; ++i) {
+ const passing = stateStack[stateStack.length - 1].state === State.Passing;
+ if (passing) {
+ result += strings[i];
+ }
+
+ const value = values[i];
+ if (value instanceof Directive) {
+ value.applyTo(stateStack);
+ } else {
+ if (passing) {
+ result += value;
+ }
+ }
+ }
+ assert(stateStack.length === 1, 'Unterminated preprocessor condition at end of file');
+ result += strings[values.length];
+
+ return result;
+}
+pp._if = (predicate) => new If(1, predicate);
+pp._elif = (predicate) => new ElseIf(1, predicate);
+pp._else = new Else(1);
+pp._endif = new EndIf(1);
+pp.__if = (predicate) => new If(2, predicate);
+pp.__elif = (predicate) => new ElseIf(2, predicate);
+pp.__else = new Else(2);
+pp.__endif = new EndIf(2);
+pp.___if = (predicate) => new If(3, predicate);
+pp.___elif = (predicate) => new ElseIf(3, predicate);
+pp.___else = new Else(3);
+pp.___endif = new EndIf(3);
+// Add more if needed.
+//# sourceMappingURL=preprocessor.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/preprocessor.js.map b/testing/web-platform/mozilla/tests/webgpu/common/util/preprocessor.js.map
new file mode 100644
index 0000000000..b58f288efe
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/preprocessor.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"preprocessor.js","names":["assert","State","Directive","constructor","depth","checkDepth","stack","length","If","predicate","applyTo","parentState","state","push","allowsFollowingElse","Passing","Skipping","Seeking","ElseIf","siblingState","pop","Else","EndIf","pp","strings","values","result","stateStack","i","passing","value","_if","_elif","_else","_endif","__if","__elif","__else","__endif","___if","___elif","___else","___endif"],"sources":["../../../src/common/util/preprocessor.ts"],"sourcesContent":["import { assert } from './util.js';\n\n// The state of the preprocessor is a stack of States.\ntype StateStack = { allowsFollowingElse: boolean; state: State }[];\nconst enum State {\n Seeking, // Still looking for a passing condition\n Passing, // Currently inside a passing condition (the root is always in this state)\n Skipping, // Have already seen a passing condition; now skipping the rest\n}\n\n// The transitions in the state space are the following preprocessor directives:\n// - Sibling elif\n// - Sibling else\n// - Sibling endif\n// - Child if\nabstract class Directive {\n private readonly depth: number;\n\n constructor(depth: number) {\n this.depth = depth;\n }\n\n protected checkDepth(stack: StateStack): void {\n assert(\n stack.length === this.depth,\n `Number of \"$\"s must match nesting depth, currently ${stack.length} (e.g. $if $$if $$endif $endif)`\n );\n }\n\n abstract applyTo(stack: StateStack): void;\n}\n\nclass If extends Directive {\n private readonly predicate: boolean;\n\n constructor(depth: number, predicate: boolean) {\n super(depth);\n this.predicate = predicate;\n }\n\n applyTo(stack: StateStack) {\n this.checkDepth(stack);\n const parentState = stack[stack.length - 1].state;\n stack.push({\n allowsFollowingElse: true,\n state:\n parentState !== State.Passing\n ? State.Skipping\n : this.predicate\n ? State.Passing\n : State.Seeking,\n });\n }\n}\n\nclass ElseIf extends If {\n applyTo(stack: StateStack) {\n assert(stack.length >= 1);\n const { allowsFollowingElse, state: siblingState } = stack.pop()!;\n this.checkDepth(stack);\n assert(allowsFollowingElse, 'pp.elif after pp.else');\n if (siblingState !== State.Seeking) {\n stack.push({ allowsFollowingElse: true, state: State.Skipping });\n } else {\n super.applyTo(stack);\n }\n }\n}\n\nclass Else extends Directive {\n applyTo(stack: StateStack) {\n assert(stack.length >= 1);\n const { allowsFollowingElse, state: siblingState } = stack.pop()!;\n this.checkDepth(stack);\n assert(allowsFollowingElse, 'pp.else after pp.else');\n stack.push({\n allowsFollowingElse: false,\n state: siblingState === State.Seeking ? State.Passing : State.Skipping,\n });\n }\n}\n\nclass EndIf extends Directive {\n applyTo(stack: StateStack) {\n stack.pop();\n this.checkDepth(stack);\n }\n}\n\n/**\n * A simple template-based, non-line-based preprocessor implementing if/elif/else/endif.\n *\n * @example\n * ```\n * const shader = pp`\n * ${pp._if(expr)}\n * const x: ${type} = ${value};\n * ${pp._elif(expr)}\n * ${pp.__if(expr)}\n * ...\n * ${pp.__else}\n * ...\n * ${pp.__endif}\n * ${pp._endif}`;\n * ```\n *\n * @param strings - The array of constant string chunks of the template string.\n * @param ...values - The array of interpolated `${}` values within the template string.\n */\nexport function pp(\n strings: TemplateStringsArray,\n ...values: ReadonlyArray<Directive | string | number>\n): string {\n let result = '';\n const stateStack: StateStack = [{ allowsFollowingElse: false, state: State.Passing }];\n\n for (let i = 0; i < values.length; ++i) {\n const passing = stateStack[stateStack.length - 1].state === State.Passing;\n if (passing) {\n result += strings[i];\n }\n\n const value = values[i];\n if (value instanceof Directive) {\n value.applyTo(stateStack);\n } else {\n if (passing) {\n result += value;\n }\n }\n }\n assert(stateStack.length === 1, 'Unterminated preprocessor condition at end of file');\n result += strings[values.length];\n\n return result;\n}\npp._if = (predicate: boolean) => new If(1, predicate);\npp._elif = (predicate: boolean) => new ElseIf(1, predicate);\npp._else = new Else(1);\npp._endif = new EndIf(1);\npp.__if = (predicate: boolean) => new If(2, predicate);\npp.__elif = (predicate: boolean) => new ElseIf(2, predicate);\npp.__else = new Else(2);\npp.__endif = new EndIf(2);\npp.___if = (predicate: boolean) => new If(3, predicate);\npp.___elif = (predicate: boolean) => new ElseIf(3, predicate);\npp.___else = new Else(3);\npp.___endif = new EndIf(3);\n// Add more if needed.\n"],"mappings":"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"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/timeout.js b/testing/web-platform/mozilla/tests/webgpu/common/util/timeout.js
new file mode 100644
index 0000000000..32b4660a59
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/timeout.js
@@ -0,0 +1,8 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+/**
+ * Equivalent of `setTimeout`, but redirects to WPT's `step_timeout` when it is defined.
+ */
+export const timeout = typeof step_timeout !== 'undefined' ? step_timeout : setTimeout;
+//# sourceMappingURL=timeout.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/timeout.js.map b/testing/web-platform/mozilla/tests/webgpu/common/util/timeout.js.map
new file mode 100644
index 0000000000..0e8fff6f67
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/timeout.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"timeout.js","names":["timeout","step_timeout","setTimeout"],"sources":["../../../src/common/util/timeout.ts"],"sourcesContent":["/** Defined by WPT. Like `setTimeout`, but applies a timeout multiplier for slow test systems. */\ndeclare const step_timeout: undefined | typeof setTimeout;\n\n/**\n * Equivalent of `setTimeout`, but redirects to WPT's `step_timeout` when it is defined.\n */\nexport const timeout = typeof step_timeout !== 'undefined' ? step_timeout : setTimeout;\n"],"mappings":";AAAA;AAAA;AAGA;AACA;AACA;AACA,OAAO,MAAMA,OAAO,GAAG,OAAOC,YAAY,KAAK,WAAW,GAAGA,YAAY,GAAGC,UAAU"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/types.js b/testing/web-platform/mozilla/tests/webgpu/common/util/types.js
new file mode 100644
index 0000000000..2c224b6d3a
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/types.js
@@ -0,0 +1,60 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/
+
+
+
+
+
+
+
+
+
+
+export function assertTypeTrue() {}
+
+/**
+ * Computes the intersection of a set of types, given the union of those types.
+ *
+ * From: https://stackoverflow.com/a/56375136
+ */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+// K exhausted
+//# sourceMappingURL=types.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/types.js.map b/testing/web-platform/mozilla/tests/webgpu/common/util/types.js.map
new file mode 100644
index 0000000000..084ce36abb
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/types.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"types.js","names":["assertTypeTrue"],"sources":["../../../src/common/util/types.ts"],"sourcesContent":["/** Forces a type to resolve its type definitions, to make it readable/debuggable. */\nexport type ResolveType<T> = T extends object\n ? T extends infer O\n ? { [K in keyof O]: ResolveType<O[K]> }\n : never\n : T;\n\n/** Returns the type `true` iff X and Y are exactly equal */\nexport type TypeEqual<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2\n ? true\n : false;\n\n/* eslint-disable-next-line @typescript-eslint/no-unused-vars */\nexport function assertTypeTrue<T extends true>() {}\n\n/**\n * Computes the intersection of a set of types, given the union of those types.\n *\n * From: https://stackoverflow.com/a/56375136\n */\nexport type UnionToIntersection<U> =\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;\n\n/** \"Type asserts\" that `X` is a subtype of `Y`. */\ntype EnsureSubtype<X, Y> = X extends Y ? X : never;\n\ntype TupleHeadOr<T, Default> = T extends readonly [infer H, ...(readonly unknown[])] ? H : Default;\ntype TupleTailOr<T, Default> = T extends readonly [unknown, ...infer Tail] ? Tail : Default;\ntype TypeOr<T, Default> = T extends undefined ? Default : T;\n\n/**\n * Zips a key tuple type and a value tuple type together into an object.\n *\n * @template Keys Keys of the resulting object.\n * @template Values Values of the resulting object. If a key corresponds to a `Values` member that\n * is undefined or past the end, it defaults to the corresponding `Defaults` member.\n * @template Defaults Default values. If a key corresponds to a `Defaults` member that is past the\n * end, the default falls back to `undefined`.\n */\nexport type ZipKeysWithValues<\n Keys extends readonly string[],\n Values extends readonly unknown[],\n Defaults extends readonly unknown[]\n> =\n //\n Keys extends readonly [infer KHead, ...infer KTail]\n ? {\n readonly [k in EnsureSubtype<KHead, string>]: TypeOr<\n TupleHeadOr<Values, undefined>,\n TupleHeadOr<Defaults, undefined>\n >;\n } &\n ZipKeysWithValues<\n EnsureSubtype<KTail, readonly string[]>,\n TupleTailOr<Values, []>,\n TupleTailOr<Defaults, []>\n >\n : {}; // K exhausted\n"],"mappings":";AAAA;AAAA;;;;;;;;;;;AAaA,OAAO,SAASA,cAAc,GAAmB,CAAC;;AAElD;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuCU"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/util.js b/testing/web-platform/mozilla/tests/webgpu/common/util/util.js
new file mode 100644
index 0000000000..3dfd824b12
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/util.js
@@ -0,0 +1,304 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { Float16Array } from '../../external/petamoriken/float16/float16.js';import { globalTestConfig } from '../framework/test_config.js';import { Logger } from '../internal/logging/logger.js';
+
+import { keysOf } from './data_tables.js';
+import { timeout } from './timeout.js';
+
+/**
+ * Error with arbitrary `extra` data attached, for debugging.
+ * The extra data is omitted if not running the test in debug mode (`?debug=1`).
+ */
+export class ErrorWithExtra extends Error {
+
+
+ /**
+ * `extra` function is only called if in debug mode.
+ * If an `ErrorWithExtra` is passed, its message is used and its extras are passed through.
+ */
+
+
+ constructor(baseOrMessage, newExtra) {
+ const message = typeof baseOrMessage === 'string' ? baseOrMessage : baseOrMessage.message;
+ super(message);
+
+ const oldExtras = baseOrMessage instanceof ErrorWithExtra ? baseOrMessage.extra : {};
+ this.extra = Logger.globalDebugMode ?
+ { ...oldExtras, ...newExtra() } :
+ { omitted: 'pass ?debug=1' };
+ }
+}
+
+/**
+ * Asserts `condition` is true. Otherwise, throws an `Error` with the provided message.
+ */
+export function assert(condition, msg) {
+ if (!condition) {
+ throw new Error(msg && (typeof msg === 'string' ? msg : msg()));
+ }
+}
+
+/** If the argument is an Error, throw it. Otherwise, pass it back. */
+export function assertOK(value) {
+ if (value instanceof Error) {
+ throw value;
+ }
+ return value;
+}
+
+/**
+ * Resolves if the provided promise rejects; rejects if it does not.
+ */
+export async function assertReject(p, msg) {
+ try {
+ await p;
+ unreachable(msg);
+ } catch (ex) {
+
+ // Assertion OK
+ }}
+
+/**
+ * Assert this code is unreachable. Unconditionally throws an `Error`.
+ */
+export function unreachable(msg) {
+ throw new Error(msg);
+}
+
+/**
+ * The `performance` interface.
+ * It is available in all browsers, but it is not in scope by default in Node.
+ */
+const perf = typeof performance !== 'undefined' ? performance : require('perf_hooks').performance;
+
+/**
+ * Calls the appropriate `performance.now()` depending on whether running in a browser or Node.
+ */
+export function now() {
+ return perf.now();
+}
+
+/**
+ * Returns a promise which resolves after the specified time.
+ */
+export function resolveOnTimeout(ms) {
+ return new Promise((resolve) => {
+ timeout(() => {
+ resolve();
+ }, ms);
+ });
+}
+
+export class PromiseTimeoutError extends Error {}
+
+/**
+ * Returns a promise which rejects after the specified time.
+ */
+export function rejectOnTimeout(ms, msg) {
+ return new Promise((_resolve, reject) => {
+ timeout(() => {
+ reject(new PromiseTimeoutError(msg));
+ }, ms);
+ });
+}
+
+/**
+ * Takes a promise `p`, and returns a new one which rejects if `p` takes too long,
+ * and otherwise passes the result through.
+ */
+export function raceWithRejectOnTimeout(p, ms, msg) {
+ if (globalTestConfig.noRaceWithRejectOnTimeout) {
+ return p;
+ }
+ // Setup a promise that will reject after `ms` milliseconds. We cancel this timeout when
+ // `p` is finalized, so the JavaScript VM doesn't hang around waiting for the timer to
+ // complete, once the test runner has finished executing the tests.
+ const timeoutPromise = new Promise((_resolve, reject) => {
+ const handle = timeout(() => {
+ reject(new PromiseTimeoutError(msg));
+ }, ms);
+ p = p.finally(() => clearTimeout(handle));
+ });
+ return Promise.race([p, timeoutPromise]);
+}
+
+/**
+ * Takes a promise `p` and returns a new one which rejects if `p` resolves or rejects,
+ * and otherwise resolves after the specified time.
+ */
+export function assertNotSettledWithinTime(
+p,
+ms,
+msg)
+{
+ // Rejects regardless of whether p resolves or rejects.
+ const rejectWhenSettled = p.then(() => Promise.reject(new Error(msg)));
+ // Resolves after `ms` milliseconds.
+ const timeoutPromise = new Promise((resolve) => {
+ const handle = timeout(() => {
+ resolve(undefined);
+ }, ms);
+ p.finally(() => clearTimeout(handle));
+ });
+ return Promise.race([rejectWhenSettled, timeoutPromise]);
+}
+
+/**
+ * Returns a `Promise.reject()`, but also registers a dummy `.catch()` handler so it doesn't count
+ * as an uncaught promise rejection in the runtime.
+ */
+export function rejectWithoutUncaught(err) {
+ const p = Promise.reject(err);
+ // Suppress uncaught promise rejection.
+ p.catch(() => {});
+ return p;
+}
+
+/**
+ * Makes a copy of a JS `object`, with the keys reordered into sorted order.
+ */
+export function sortObjectByKey(v) {
+ const sortedObject = {};
+ for (const k of Object.keys(v).sort()) {
+ sortedObject[k] = v[k];
+ }
+ return sortedObject;
+}
+
+/**
+ * Determines whether two JS values are equal, recursing into objects and arrays.
+ * NaN is treated specially, such that `objectEquals(NaN, NaN)`.
+ */
+export function objectEquals(x, y) {
+ if (typeof x !== 'object' || typeof y !== 'object') {
+ if (typeof x === 'number' && typeof y === 'number' && Number.isNaN(x) && Number.isNaN(y)) {
+ return true;
+ }
+ return x === y;
+ }
+ if (x === null || y === null) return x === y;
+ if (x.constructor !== y.constructor) return false;
+ if (x instanceof Function) return x === y;
+ if (x instanceof RegExp) return x === y;
+ if (x === y || x.valueOf() === y.valueOf()) return true;
+ if (Array.isArray(x) && Array.isArray(y) && x.length !== y.length) return false;
+ if (x instanceof Date) return false;
+ if (!(x instanceof Object)) return false;
+ if (!(y instanceof Object)) return false;
+
+ const x1 = x;
+ const y1 = y;
+ const p = Object.keys(x);
+ return Object.keys(y).every((i) => p.indexOf(i) !== -1) && p.every((i) => objectEquals(x1[i], y1[i]));
+}
+
+/**
+ * Generates a range of values `fn(0)..fn(n-1)`.
+ */
+export function range(n, fn) {
+ return [...new Array(n)].map((_, i) => fn(i));
+}
+
+/**
+ * Generates a range of values `fn(0)..fn(n-1)`.
+ */
+export function* iterRange(n, fn) {
+ for (let i = 0; i < n; ++i) {
+ yield fn(i);
+ }
+}
+
+/** Creates a (reusable) iterable object that maps `f` over `xs`, lazily. */
+export function mapLazy(xs, f) {
+ return {
+ *[Symbol.iterator]() {
+ for (const x of xs) {
+ yield f(x);
+ }
+ }
+ };
+}
+
+const TypedArrayBufferViewInstances = [
+new Uint8Array(),
+new Uint8ClampedArray(),
+new Uint16Array(),
+new Uint32Array(),
+new Int8Array(),
+new Int16Array(),
+new Int32Array(),
+new Float16Array(),
+new Float32Array(),
+new Float64Array()];
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+export const kTypedArrayBufferViews =
+
+{
+ ...(() => {
+
+ const result = {};
+ for (const v of TypedArrayBufferViewInstances) {
+ result[v.constructor.name] = v.constructor;
+ }
+ return result;
+ })()
+};
+export const kTypedArrayBufferViewKeys = keysOf(kTypedArrayBufferViews);
+export const kTypedArrayBufferViewConstructors = Object.values(kTypedArrayBufferViews);
+
+function subarrayAsU8(
+buf,
+{ start = 0, length })
+{
+ if (buf instanceof ArrayBuffer) {
+ return new Uint8Array(buf, start, length);
+ } else if (buf instanceof Uint8Array || buf instanceof Uint8ClampedArray) {
+ // Don't wrap in new views if we don't need to.
+ if (start === 0 && (length === undefined || length === buf.byteLength)) {
+ return buf;
+ }
+ }
+ const byteOffset = buf.byteOffset + start * buf.BYTES_PER_ELEMENT;
+ const byteLength =
+ length !== undefined ?
+ length * buf.BYTES_PER_ELEMENT :
+ buf.byteLength - (byteOffset - buf.byteOffset);
+ return new Uint8Array(buf.buffer, byteOffset, byteLength);
+}
+
+/**
+ * Copy a range of bytes from one ArrayBuffer or TypedArray to another.
+ *
+ * `start`/`length` are in elements (or in bytes, if ArrayBuffer).
+ */
+export function memcpy(
+src,
+dst)
+{
+ subarrayAsU8(dst.dst, dst).set(subarrayAsU8(src.src, src));
+}
+//# sourceMappingURL=util.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/util.js.map b/testing/web-platform/mozilla/tests/webgpu/common/util/util.js.map
new file mode 100644
index 0000000000..4a8ad3bd50
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/util.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"util.js","names":["Float16Array","globalTestConfig","Logger","keysOf","timeout","ErrorWithExtra","Error","constructor","baseOrMessage","newExtra","message","oldExtras","extra","globalDebugMode","omitted","assert","condition","msg","assertOK","value","assertReject","p","unreachable","ex","perf","performance","require","now","resolveOnTimeout","ms","Promise","resolve","PromiseTimeoutError","rejectOnTimeout","_resolve","reject","raceWithRejectOnTimeout","noRaceWithRejectOnTimeout","timeoutPromise","handle","finally","clearTimeout","race","assertNotSettledWithinTime","rejectWhenSettled","then","undefined","rejectWithoutUncaught","err","catch","sortObjectByKey","v","sortedObject","k","Object","keys","sort","objectEquals","x","y","Number","isNaN","Function","RegExp","valueOf","Array","isArray","length","Date","x1","y1","every","i","indexOf","range","n","fn","map","_","iterRange","mapLazy","xs","f","Symbol","iterator","TypedArrayBufferViewInstances","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","Int8Array","Int16Array","Int32Array","Float32Array","Float64Array","kTypedArrayBufferViews","result","name","kTypedArrayBufferViewKeys","kTypedArrayBufferViewConstructors","values","subarrayAsU8","buf","start","ArrayBuffer","byteLength","byteOffset","BYTES_PER_ELEMENT","buffer","memcpy","src","dst","set"],"sources":["../../../src/common/util/util.ts"],"sourcesContent":["import { Float16Array } from '../../external/petamoriken/float16/float16.js';\nimport { globalTestConfig } from '../framework/test_config.js';\nimport { Logger } from '../internal/logging/logger.js';\n\nimport { keysOf } from './data_tables.js';\nimport { timeout } from './timeout.js';\n\n/**\n * Error with arbitrary `extra` data attached, for debugging.\n * The extra data is omitted if not running the test in debug mode (`?debug=1`).\n */\nexport class ErrorWithExtra extends Error {\n readonly extra: { [k: string]: unknown };\n\n /**\n * `extra` function is only called if in debug mode.\n * If an `ErrorWithExtra` is passed, its message is used and its extras are passed through.\n */\n constructor(message: string, extra: () => {});\n constructor(base: ErrorWithExtra, newExtra: () => {});\n constructor(baseOrMessage: string | ErrorWithExtra, newExtra: () => {}) {\n const message = typeof baseOrMessage === 'string' ? baseOrMessage : baseOrMessage.message;\n super(message);\n\n const oldExtras = baseOrMessage instanceof ErrorWithExtra ? baseOrMessage.extra : {};\n this.extra = Logger.globalDebugMode\n ? { ...oldExtras, ...newExtra() }\n : { omitted: 'pass ?debug=1' };\n }\n}\n\n/**\n * Asserts `condition` is true. Otherwise, throws an `Error` with the provided message.\n */\nexport function assert(condition: boolean, msg?: string | (() => string)): asserts condition {\n if (!condition) {\n throw new Error(msg && (typeof msg === 'string' ? msg : msg()));\n }\n}\n\n/** If the argument is an Error, throw it. Otherwise, pass it back. */\nexport function assertOK<T>(value: Error | T): T {\n if (value instanceof Error) {\n throw value;\n }\n return value;\n}\n\n/**\n * Resolves if the provided promise rejects; rejects if it does not.\n */\nexport async function assertReject(p: Promise<unknown>, msg?: string): Promise<void> {\n try {\n await p;\n unreachable(msg);\n } catch (ex) {\n // Assertion OK\n }\n}\n\n/**\n * Assert this code is unreachable. Unconditionally throws an `Error`.\n */\nexport function unreachable(msg?: string): never {\n throw new Error(msg);\n}\n\n/**\n * The `performance` interface.\n * It is available in all browsers, but it is not in scope by default in Node.\n */\nconst perf = typeof performance !== 'undefined' ? performance : require('perf_hooks').performance;\n\n/**\n * Calls the appropriate `performance.now()` depending on whether running in a browser or Node.\n */\nexport function now(): number {\n return perf.now();\n}\n\n/**\n * Returns a promise which resolves after the specified time.\n */\nexport function resolveOnTimeout(ms: number): Promise<void> {\n return new Promise(resolve => {\n timeout(() => {\n resolve();\n }, ms);\n });\n}\n\nexport class PromiseTimeoutError extends Error {}\n\n/**\n * Returns a promise which rejects after the specified time.\n */\nexport function rejectOnTimeout(ms: number, msg: string): Promise<never> {\n return new Promise((_resolve, reject) => {\n timeout(() => {\n reject(new PromiseTimeoutError(msg));\n }, ms);\n });\n}\n\n/**\n * Takes a promise `p`, and returns a new one which rejects if `p` takes too long,\n * and otherwise passes the result through.\n */\nexport function raceWithRejectOnTimeout<T>(p: Promise<T>, ms: number, msg: string): Promise<T> {\n if (globalTestConfig.noRaceWithRejectOnTimeout) {\n return p;\n }\n // Setup a promise that will reject after `ms` milliseconds. We cancel this timeout when\n // `p` is finalized, so the JavaScript VM doesn't hang around waiting for the timer to\n // complete, once the test runner has finished executing the tests.\n const timeoutPromise = new Promise((_resolve, reject) => {\n const handle = timeout(() => {\n reject(new PromiseTimeoutError(msg));\n }, ms);\n p = p.finally(() => clearTimeout(handle));\n });\n return Promise.race([p, timeoutPromise]) as Promise<T>;\n}\n\n/**\n * Takes a promise `p` and returns a new one which rejects if `p` resolves or rejects,\n * and otherwise resolves after the specified time.\n */\nexport function assertNotSettledWithinTime(\n p: Promise<unknown>,\n ms: number,\n msg: string\n): Promise<undefined> {\n // Rejects regardless of whether p resolves or rejects.\n const rejectWhenSettled = p.then(() => Promise.reject(new Error(msg)));\n // Resolves after `ms` milliseconds.\n const timeoutPromise = new Promise<undefined>(resolve => {\n const handle = timeout(() => {\n resolve(undefined);\n }, ms);\n p.finally(() => clearTimeout(handle));\n });\n return Promise.race([rejectWhenSettled, timeoutPromise]);\n}\n\n/**\n * Returns a `Promise.reject()`, but also registers a dummy `.catch()` handler so it doesn't count\n * as an uncaught promise rejection in the runtime.\n */\nexport function rejectWithoutUncaught<T>(err: unknown): Promise<T> {\n const p = Promise.reject(err);\n // Suppress uncaught promise rejection.\n p.catch(() => {});\n return p;\n}\n\n/**\n * Makes a copy of a JS `object`, with the keys reordered into sorted order.\n */\nexport function sortObjectByKey(v: { [k: string]: unknown }): { [k: string]: unknown } {\n const sortedObject: { [k: string]: unknown } = {};\n for (const k of Object.keys(v).sort()) {\n sortedObject[k] = v[k];\n }\n return sortedObject;\n}\n\n/**\n * Determines whether two JS values are equal, recursing into objects and arrays.\n * NaN is treated specially, such that `objectEquals(NaN, NaN)`.\n */\nexport function objectEquals(x: unknown, y: unknown): boolean {\n if (typeof x !== 'object' || typeof y !== 'object') {\n if (typeof x === 'number' && typeof y === 'number' && Number.isNaN(x) && Number.isNaN(y)) {\n return true;\n }\n return x === y;\n }\n if (x === null || y === null) return x === y;\n if (x.constructor !== y.constructor) return false;\n if (x instanceof Function) return x === y;\n if (x instanceof RegExp) return x === y;\n if (x === y || x.valueOf() === y.valueOf()) return true;\n if (Array.isArray(x) && Array.isArray(y) && x.length !== y.length) return false;\n if (x instanceof Date) return false;\n if (!(x instanceof Object)) return false;\n if (!(y instanceof Object)) return false;\n\n const x1 = x as { [k: string]: unknown };\n const y1 = y as { [k: string]: unknown };\n const p = Object.keys(x);\n return Object.keys(y).every(i => p.indexOf(i) !== -1) && p.every(i => objectEquals(x1[i], y1[i]));\n}\n\n/**\n * Generates a range of values `fn(0)..fn(n-1)`.\n */\nexport function range<T>(n: number, fn: (i: number) => T): T[] {\n return [...new Array(n)].map((_, i) => fn(i));\n}\n\n/**\n * Generates a range of values `fn(0)..fn(n-1)`.\n */\nexport function* iterRange<T>(n: number, fn: (i: number) => T): Iterable<T> {\n for (let i = 0; i < n; ++i) {\n yield fn(i);\n }\n}\n\n/** Creates a (reusable) iterable object that maps `f` over `xs`, lazily. */\nexport function mapLazy<T, R>(xs: Iterable<T>, f: (x: T) => R): Iterable<R> {\n return {\n *[Symbol.iterator]() {\n for (const x of xs) {\n yield f(x);\n }\n },\n };\n}\n\nconst TypedArrayBufferViewInstances = [\n new Uint8Array(),\n new Uint8ClampedArray(),\n new Uint16Array(),\n new Uint32Array(),\n new Int8Array(),\n new Int16Array(),\n new Int32Array(),\n new Float16Array(),\n new Float32Array(),\n new Float64Array(),\n] as const;\n\nexport type TypedArrayBufferView = typeof TypedArrayBufferViewInstances[number];\n\nexport type TypedArrayBufferViewConstructor<\n A extends TypedArrayBufferView = TypedArrayBufferView\n> = {\n // Interface copied from Uint8Array, and made generic.\n readonly prototype: A;\n readonly BYTES_PER_ELEMENT: number;\n\n new (): A;\n new (elements: Iterable<number>): A;\n new (array: ArrayLike<number> | ArrayBufferLike): A;\n new (buffer: ArrayBufferLike, byteOffset?: number, length?: number): A;\n new (length: number): A;\n\n from(arrayLike: ArrayLike<number>): A;\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n from(arrayLike: Iterable<number>, mapfn?: (v: number, k: number) => number, thisArg?: any): A;\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n from<T>(arrayLike: ArrayLike<T>, mapfn: (v: T, k: number) => number, thisArg?: any): A;\n of(...items: number[]): A;\n};\n\nexport const kTypedArrayBufferViews: {\n readonly [k: string]: TypedArrayBufferViewConstructor;\n} = {\n ...(() => {\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n const result: { [k: string]: any } = {};\n for (const v of TypedArrayBufferViewInstances) {\n result[v.constructor.name] = v.constructor;\n }\n return result;\n })(),\n};\nexport const kTypedArrayBufferViewKeys = keysOf(kTypedArrayBufferViews);\nexport const kTypedArrayBufferViewConstructors = Object.values(kTypedArrayBufferViews);\n\nfunction subarrayAsU8(\n buf: ArrayBuffer | TypedArrayBufferView,\n { start = 0, length }: { start?: number; length?: number }\n): Uint8Array | Uint8ClampedArray {\n if (buf instanceof ArrayBuffer) {\n return new Uint8Array(buf, start, length);\n } else if (buf instanceof Uint8Array || buf instanceof Uint8ClampedArray) {\n // Don't wrap in new views if we don't need to.\n if (start === 0 && (length === undefined || length === buf.byteLength)) {\n return buf;\n }\n }\n const byteOffset = buf.byteOffset + start * buf.BYTES_PER_ELEMENT;\n const byteLength =\n length !== undefined\n ? length * buf.BYTES_PER_ELEMENT\n : buf.byteLength - (byteOffset - buf.byteOffset);\n return new Uint8Array(buf.buffer, byteOffset, byteLength);\n}\n\n/**\n * Copy a range of bytes from one ArrayBuffer or TypedArray to another.\n *\n * `start`/`length` are in elements (or in bytes, if ArrayBuffer).\n */\nexport function memcpy(\n src: { src: ArrayBuffer | TypedArrayBufferView; start?: number; length?: number },\n dst: { dst: ArrayBuffer | TypedArrayBufferView; start?: number }\n): void {\n subarrayAsU8(dst.dst, dst).set(subarrayAsU8(src.src, src));\n}\n"],"mappings":"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"} \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/wpt_reftest_wait.js b/testing/web-platform/mozilla/tests/webgpu/common/util/wpt_reftest_wait.js
new file mode 100644
index 0000000000..c2fc1f596d
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/wpt_reftest_wait.js
@@ -0,0 +1,25 @@
+/**
+* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
+**/import { timeout } from './timeout.js'; // Copied from https://github.com/web-platform-tests/wpt/blob/master/common/reftest-wait.js
+
+/**
+ * Remove the `reftest-wait` class on the document element.
+ * The reftest runner will wait with taking a screenshot while
+ * this class is present.
+ *
+ * See https://web-platform-tests.org/writing-tests/reftests.html#controlling-when-comparison-occurs
+ */
+export function takeScreenshot() {
+ document.documentElement.classList.remove('reftest-wait');
+}
+
+/**
+ * Call `takeScreenshot()` after a delay of at least `ms` milliseconds.
+ * @param {number} ms - milliseconds
+ */
+export function takeScreenshotDelayed(ms) {
+ timeout(() => {
+ takeScreenshot();
+ }, ms);
+}
+//# sourceMappingURL=wpt_reftest_wait.js.map \ No newline at end of file
diff --git a/testing/web-platform/mozilla/tests/webgpu/common/util/wpt_reftest_wait.js.map b/testing/web-platform/mozilla/tests/webgpu/common/util/wpt_reftest_wait.js.map
new file mode 100644
index 0000000000..a231c5f611
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/webgpu/common/util/wpt_reftest_wait.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"wpt_reftest_wait.js","names":["timeout","takeScreenshot","document","documentElement","classList","remove","takeScreenshotDelayed","ms"],"sources":["../../../src/common/util/wpt_reftest_wait.ts"],"sourcesContent":["import { timeout } from './timeout.js';\n\n// Copied from https://github.com/web-platform-tests/wpt/blob/master/common/reftest-wait.js\n\n/**\n * Remove the `reftest-wait` class on the document element.\n * The reftest runner will wait with taking a screenshot while\n * this class is present.\n *\n * See https://web-platform-tests.org/writing-tests/reftests.html#controlling-when-comparison-occurs\n */\nexport function takeScreenshot() {\n document.documentElement.classList.remove('reftest-wait');\n}\n\n/**\n * Call `takeScreenshot()` after a delay of at least `ms` milliseconds.\n * @param {number} ms - milliseconds\n */\nexport function takeScreenshotDelayed(ms: number) {\n timeout(() => {\n takeScreenshot();\n }, ms);\n}\n"],"mappings":";AAAA;AAAA,GAAA,SAASA,OAAO,QAAQ,cAAc,CAAC,CAEvC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,cAAc,GAAG;EAC/BC,QAAQ,CAACC,eAAe,CAACC,SAAS,CAACC,MAAM,CAAC,cAAc,CAAC;AAC3D;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASC,qBAAqB,CAACC,EAAU,EAAE;EAChDP,OAAO,CAAC,MAAM;IACZC,cAAc,EAAE;EAClB,CAAC,EAAEM,EAAE,CAAC;AACR"} \ No newline at end of file