diff --git a/out/common/framework/data_cache.js b/out/common/framework/data_cache.js new file mode 100644 index 000000000000..b8e267d3e971 --- /dev/null +++ b/out/common/framework/data_cache.js @@ -0,0 +1,176 @@ +/** +* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts +**/ /** + * Utilities to improve the performance of the CTS, by caching data that is + * expensive to build using a two-level cache (in-memory, pre-computed file). + */import { assert } from '../util/util.js'; + + + + + +/** Logger is a basic debug logger function */ + + +/** + * DataCacheNode represents a single cache entry in the LRU DataCache. + * DataCacheNode is a doubly linked list, so that least-recently-used entries can be removed, and + * cache hits can move the node to the front of the list. + */ +class DataCacheNode { + constructor(path, data) { + this.path = path; + this.data = data; + } + + /** insertAfter() re-inserts this node in the doubly-linked list after `prev` */ + insertAfter(prev) { + this.unlink(); + this.next = prev.next; + this.prev = prev; + prev.next = this; + if (this.next) { + this.next.prev = this; + } + } + + /** unlink() removes this node from the doubly-linked list */ + unlink() { + const prev = this.prev; + const next = this.next; + if (prev) { + prev.next = next; + } + if (next) { + next.prev = prev; + } + this.prev = null; + this.next = null; + } + + // The file path this node represents + // The deserialized data for this node + prev = null; // The previous node in the doubly-linked list + next = null; // The next node in the doubly-linked list +} + +/** DataCache is an interface to a LRU-cached data store used to hold data cached by path */ +export class DataCache { + constructor() { + this.lruHeadNode.next = this.lruTailNode; + this.lruTailNode.prev = this.lruHeadNode; + } + + /** 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 + const node = this.cache.get(cacheable.path); + if (node !== undefined) { + this.log('in-memory cache hit'); + node.insertAfter(this.lruHeadNode); + return Promise.resolve(node.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`); + const data = cacheable.deserialize(serialized); + this.addToCache(cacheable.path, data); + return data; + } + } + // Not found anywhere. Build the data, and cache for future lookup. + this.log(`cache: building (${cacheable.path})`); + const data = await cacheable.build(); + this.addToCache(cacheable.path, data); + return data; + } + + /** + * addToCache() creates a new node for `path` and `data`, inserting the new node at the front of + * the doubly-linked list. If the number of entries in the cache exceeds this.maxCount, then the + * least recently used entry is evicted + * @param path the file path for the data + * @param data the deserialized data + */ + addToCache(path, data) { + if (this.cache.size >= this.maxCount) { + const toEvict = this.lruTailNode.prev; + assert(toEvict !== null); + toEvict.unlink(); + this.cache.delete(toEvict.path); + this.log(`evicting ${toEvict.path}`); + } + const node = new DataCacheNode(path, data); + node.insertAfter(this.lruHeadNode); + this.cache.set(path, node); + this.log(`added ${path}. new count: ${this.cache.size}`); + } + + log(msg) { + if (this.debugLogger !== null) { + this.debugLogger(`DataCache: ${msg}`); + } + } + + // Max number of entries in the cache before LRU entries are evicted. + maxCount = 4; + + cache = new Map(); + lruHeadNode = new DataCacheNode('', null); // placeholder node (no path or data) + lruTailNode = new DataCacheNode('', null); // placeholder node (no path or data) + 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`. + */ +//# sourceMappingURL=data_cache.js.map \ No newline at end of file diff --git a/out/common/framework/data_cache.js.map b/out/common/framework/data_cache.js.map new file mode 100644 index 000000000000..9bdb420bf920 --- /dev/null +++ b/out/common/framework/data_cache.js.map @@ -0,0 +1 @@ +{"version":3,"file":"data_cache.js","names":["assert","DataCacheNode","constructor","path","data","insertAfter","prev","unlink","next","DataCache","lruHeadNode","lruTailNode","setStore","dataStore","setDebugLogger","logger","debugLogger","fetch","cacheable","node","cache","get","undefined","log","Promise","resolve","unavailableFiles","has","serialized","load","err","add","deserialize","addToCache","build","size","maxCount","toEvict","delete","set","msg","Map","Set","dataCache","isBuildingDataCache","getIsBuildingDataCache","setIsBuildingDataCache","value"],"sources":["../../../src/common/framework/data_cache.ts"],"sourcesContent":["/**\n * Utilities to improve the performance of the CTS, by caching data that is\n * expensive to build using a two-level cache (in-memory, pre-computed file).\n */\n\nimport { assert } from '../util/util.js';\n\ninterface DataStore {\n load(path: string): Promise;\n}\n\n/** Logger is a basic debug logger function */\nexport type Logger = (s: string) => void;\n\n/**\n * DataCacheNode represents a single cache entry in the LRU DataCache.\n * DataCacheNode is a doubly linked list, so that least-recently-used entries can be removed, and\n * cache hits can move the node to the front of the list.\n */\nclass DataCacheNode {\n public constructor(path: string, data: unknown) {\n this.path = path;\n this.data = data;\n }\n\n /** insertAfter() re-inserts this node in the doubly-linked list after `prev` */\n public insertAfter(prev: DataCacheNode) {\n this.unlink();\n this.next = prev.next;\n this.prev = prev;\n prev.next = this;\n if (this.next) {\n this.next.prev = this;\n }\n }\n\n /** unlink() removes this node from the doubly-linked list */\n public unlink() {\n const prev = this.prev;\n const next = this.next;\n if (prev) {\n prev.next = next;\n }\n if (next) {\n next.prev = prev;\n }\n this.prev = null;\n this.next = null;\n }\n\n public readonly path: string; // The file path this node represents\n public readonly data: unknown; // The deserialized data for this node\n public prev: DataCacheNode | null = null; // The previous node in the doubly-linked list\n public next: DataCacheNode | null = null; // The next node in the doubly-linked list\n}\n\n/** DataCache is an interface to a LRU-cached data store used to hold data cached by path */\nexport class DataCache {\n public constructor() {\n this.lruHeadNode.next = this.lruTailNode;\n this.lruTailNode.prev = this.lruHeadNode;\n }\n\n /** setDataStore() sets the backing data store used by the data cache */\n public setStore(dataStore: DataStore) {\n this.dataStore = dataStore;\n }\n\n /** setDebugLogger() sets the verbose logger */\n public setDebugLogger(logger: Logger) {\n this.debugLogger = logger;\n }\n\n /**\n * fetch() retrieves cacheable data from the data cache, first checking the\n * in-memory cache, then the data store (if specified), then resorting to\n * building the data and storing it in the cache.\n */\n public async fetch(cacheable: Cacheable): Promise {\n {\n // First check the in-memory cache\n const node = this.cache.get(cacheable.path);\n if (node !== undefined) {\n this.log('in-memory cache hit');\n node.insertAfter(this.lruHeadNode);\n return Promise.resolve(node.data as Data);\n }\n }\n this.log('in-memory cache miss');\n // In in-memory cache miss.\n // Next, try the data store.\n if (this.dataStore !== null && !this.unavailableFiles.has(cacheable.path)) {\n let serialized: Uint8Array | undefined;\n try {\n serialized = await this.dataStore.load(cacheable.path);\n this.log('loaded serialized');\n } catch (err) {\n // not found in data store\n this.log(`failed to load (${cacheable.path}): ${err}`);\n this.unavailableFiles.add(cacheable.path);\n }\n if (serialized !== undefined) {\n this.log(`deserializing`);\n const data = cacheable.deserialize(serialized);\n this.addToCache(cacheable.path, data);\n return data;\n }\n }\n // Not found anywhere. Build the data, and cache for future lookup.\n this.log(`cache: building (${cacheable.path})`);\n const data = await cacheable.build();\n this.addToCache(cacheable.path, data);\n return data;\n }\n\n /**\n * addToCache() creates a new node for `path` and `data`, inserting the new node at the front of\n * the doubly-linked list. If the number of entries in the cache exceeds this.maxCount, then the\n * least recently used entry is evicted\n * @param path the file path for the data\n * @param data the deserialized data\n */\n private addToCache(path: string, data: unknown) {\n if (this.cache.size >= this.maxCount) {\n const toEvict = this.lruTailNode.prev;\n assert(toEvict !== null);\n toEvict.unlink();\n this.cache.delete(toEvict.path);\n this.log(`evicting ${toEvict.path}`);\n }\n const node = new DataCacheNode(path, data);\n node.insertAfter(this.lruHeadNode);\n this.cache.set(path, node);\n this.log(`added ${path}. new count: ${this.cache.size}`);\n }\n\n private log(msg: string) {\n if (this.debugLogger !== null) {\n this.debugLogger(`DataCache: ${msg}`);\n }\n }\n\n // Max number of entries in the cache before LRU entries are evicted.\n private readonly maxCount = 4;\n\n private cache = new Map();\n private lruHeadNode = new DataCacheNode('', null); // placeholder node (no path or data)\n private lruTailNode = new DataCacheNode('', null); // placeholder node (no path or data)\n private unavailableFiles = new Set();\n private dataStore: DataStore | null = null;\n private debugLogger: Logger | null = null;\n}\n\n/** The data cache */\nexport const dataCache = new DataCache();\n\n/** true if the current process is building the cache */\nlet isBuildingDataCache = false;\n\n/** @returns true if the data cache is currently being built */\nexport function getIsBuildingDataCache() {\n return isBuildingDataCache;\n}\n\n/** Sets whether the data cache is currently being built */\nexport function setIsBuildingDataCache(value = true) {\n isBuildingDataCache = value;\n}\n\n/**\n * Cacheable is the interface to something that can be stored into the\n * DataCache.\n * The 'npm run gen_cache' tool will look for module-scope variables of this\n * interface, with the name `d`.\n */\nexport interface Cacheable {\n /** the globally unique path for the cacheable data */\n readonly path: string;\n\n /**\n * build() builds the cacheable data.\n * This is assumed to be an expensive operation and will only happen if the\n * cache does not already contain the built data.\n */\n build(): Promise;\n\n /**\n * serialize() encodes `data` to a binary representation so that it can be stored in a cache file.\n */\n serialize(data: Data): Uint8Array;\n\n /**\n * deserialize() is the inverse of serialize(), decoding the binary representation back to a Data\n * object.\n */\n deserialize(binary: Uint8Array): Data;\n}\n"],"mappings":"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"} \ No newline at end of file diff --git a/out/common/framework/fixture.js b/out/common/framework/fixture.js new file mode 100644 index 000000000000..e3b4365964f4 --- /dev/null +++ b/out/common/framework/fixture.js @@ -0,0 +1,399 @@ +/** +* 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( + recorder, + /** The case parameters for this test fixture shared state. Subcase params are not included. */ + params) + {this.recorder = recorder;this.params = 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(recorder, params) { + return new SubcaseBatchState(recorder, 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 if ('destroyAsync' in o) { + await o.destroyAsync(); + } else if ('close' in o) { + o.close(); + } else { + // HTMLVideoElement + o.src = ''; + o.srcObject = null; + } + } + } + + /** + * Tracks an object to be cleaned up after the test finishes. + * + * Usually when creating buffers/textures/query sets, you can use the helpers in GPUTest instead. + */ + trackForCleanup(o) { + if (o instanceof Promise) { + this.eventualAsyncExpectation(() => + o.then( + (o) => this.trackForCleanup(o), + () => {} + ) + ); + return o; + } + + if (o instanceof GPUDevice) { + this.objectsToCleanUp.push({ + async destroyAsync() { + o.destroy(); + await o.lost; + } + }); + } else { + 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; + } + + /** Call requestDevice() and track the device for cleanup. */ + requestDeviceTracked(adapter, desc = undefined) { + + return this.trackForCleanup(adapter.requestDevice(desc)); + } + + /** Log a debug message. */ + debug(msg) { + this.rec.debug(new Error(msg)); + } + + /** + * Log an info message. + * **Use sparingly. Use `debug()` instead if logs are only needed with debug logging enabled.** + */ + info(msg) { + this.rec.info(new Error(msg)); + } + + /** Throws an exception marking the subcase as skipped. */ + skip(msg) { + throw new SkipTestCase(msg); + } + + /** Throws an exception marking the subcase as skipped if condition is true */ + skipIf(cond, msg = '') { + if (cond) { + this.skip(typeof msg === 'function' ? msg() : 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, + { allowMissingStack = false, message } = {}) + { + this.eventualAsyncExpectation(async (niceStack) => { + const m = message ? ': ' + message : ''; + try { + await p; + niceStack.message = 'DID NOT REJECT' + m; + this.rec.expectationFailed(niceStack); + } catch (ex) { + this.expectErrorValue(expectedName, ex, niceStack); + if (!allowMissingStack) { + if (!(ex instanceof Error && typeof ex.stack === 'string')) { + const exMessage = ex instanceof Error ? ex.message : '?'; + niceStack.message = `rejected as expected, but missing stack (${exMessage})${m}`; + this.rec.expectationFailed(niceStack); + } + } + } + }); + } + + /** + * Expect that the provided function throws (if `true` or `string`) or not (if `false`). + * If a string is provided, expect that the throw exception has that name. + * + * MAINTENANCE_TODO: Change to `string | false` so the exception name is always checked. + */ + shouldThrow( + expectedError, + fn, + { allowMissingStack = false, message } = {}) + { + const m = message ? ': ' + message : ''; + 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)); + if (!allowMissingStack) { + if (!(ex instanceof Error && typeof ex.stack === 'string')) { + this.rec.expectationFailed(new Error('threw as expected, but missing stack' + 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 }); + }); + } +} + + + +/** + * FixtureClass encapsulates a constructor for fixture and a corresponding + * shared state factory function. An interface version of the type is also + * defined for mixin declaration use ONLY. The interface version is necessary + * because mixin classes need a constructor with a single any[] rest + * parameter. + */ +//# sourceMappingURL=fixture.js.map \ No newline at end of file diff --git a/out/common/framework/fixture.js.map b/out/common/framework/fixture.js.map new file mode 100644 index 000000000000..e85bfc0ef577 --- /dev/null +++ b/out/common/framework/fixture.js.map @@ -0,0 +1 @@ +{"version":3,"file":"fixture.js","names":["assert","unreachable","SkipTestCase","Error","UnexpectedPassError","TestCaseRecorder","SubcaseBatchState","constructor","recorder","params","init","postInit","finalize","Fixture","eventualExpectations","numOutstandingAsyncExpectations","objectsToCleanUp","MakeSharedState","sharedState","rec","_sharedState","_params","length","p","shift","ex","threw","o","WEBGL_lose_context","getExtension","loseContext","destroy","destroyAsync","close","src","srcObject","trackForCleanup","Promise","eventualAsyncExpectation","then","GPUDevice","push","lost","tryTrackForCleanup","WebGLRenderingContext","WebGL2RenderingContext","requestDeviceTracked","adapter","desc","undefined","requestDevice","debug","msg","info","skip","skipIf","cond","warn","fail","expectationFailed","immediateAsyncExpectation","fn","ret","promise","expectErrorValue","expectedError","niceStack","message","actualName","name","shouldResolve","m","shouldReject","expectedName","allowMissingStack","stack","exMessage","shouldThrow","expect","expectOK","error","mode","handleError","Array","isArray","e","eventualExpectOK"],"sources":["../../../src/common/framework/fixture.ts"],"sourcesContent":["import { TestCaseRecorder } from '../internal/logging/test_case_recorder.js';\nimport { JSONWithUndefined } from '../internal/params_utils.js';\nimport { assert, ExceptionCheckOptions, unreachable } from '../util/util.js';\n\nexport class SkipTestCase extends Error {}\nexport class UnexpectedPassError extends Error {}\n\nexport { TestCaseRecorder } from '../internal/logging/test_case_recorder.js';\n\n/** The fully-general type for params passed to a test function invocation. */\nexport type TestParams = {\n readonly [k: string]: JSONWithUndefined;\n};\n\ntype DestroyableObject =\n | { destroy(): void }\n | { destroyAsync(): Promise }\n | { close(): void }\n | { getExtension(extensionName: 'WEBGL_lose_context'): WEBGL_lose_context }\n | HTMLVideoElement;\n\nexport class SubcaseBatchState {\n constructor(\n protected readonly recorder: TestCaseRecorder,\n /** The case parameters for this test fixture shared state. Subcase params are not included. */\n public readonly params: TestParams\n ) {}\n\n /**\n * Runs before the `.before()` function.\n * @internal MAINTENANCE_TODO: Make this not visible to test code?\n */\n async init() {}\n /**\n * Runs between the `.before()` function and the subcases.\n * @internal MAINTENANCE_TODO: Make this not visible to test code?\n */\n async postInit() {}\n /**\n * Runs after all subcases finish.\n * @internal MAINTENANCE_TODO: Make this not visible to test code?\n */\n async finalize() {}\n}\n\n/**\n * A Fixture is a class used to instantiate each test sub/case at run time.\n * A new instance of the Fixture is created for every single test subcase\n * (i.e. every time the test function is run).\n */\nexport class Fixture {\n private _params: unknown;\n private _sharedState: S;\n /**\n * Interface for recording logs and test status.\n *\n * @internal\n */\n readonly rec: TestCaseRecorder;\n private eventualExpectations: Array> = [];\n private numOutstandingAsyncExpectations = 0;\n private objectsToCleanUp: DestroyableObject[] = [];\n\n public static MakeSharedState(recorder: TestCaseRecorder, params: TestParams): SubcaseBatchState {\n return new SubcaseBatchState(recorder, params);\n }\n\n /** @internal */\n constructor(sharedState: S, rec: TestCaseRecorder, params: TestParams) {\n this._sharedState = sharedState;\n this.rec = rec;\n this._params = params;\n }\n\n /**\n * Returns the (case+subcase) parameters for this test function invocation.\n */\n get params(): unknown {\n return this._params;\n }\n\n /**\n * Gets the test fixture's shared state. This object is shared between subcases\n * within the same testcase.\n */\n get sharedState(): S {\n return this._sharedState;\n }\n\n /**\n * Override this to do additional pre-test-function work in a derived fixture.\n * This has to be a member function instead of an async `createFixture` function, because\n * we need to be able to ergonomically override it in subclasses.\n *\n * @internal MAINTENANCE_TODO: Make this not visible to test code?\n */\n async init(): Promise {}\n\n /**\n * Override this to do additional post-test-function work in a derived fixture.\n *\n * Called even if init was unsuccessful.\n *\n * @internal MAINTENANCE_TODO: Make this not visible to test code?\n */\n async finalize(): Promise {\n assert(\n this.numOutstandingAsyncExpectations === 0,\n 'there were outstanding immediateAsyncExpectations (e.g. expectUncapturedError) at the end of the test'\n );\n\n // Loop to exhaust the eventualExpectations in case they chain off each other.\n while (this.eventualExpectations.length) {\n const p = this.eventualExpectations.shift()!;\n try {\n await p;\n } catch (ex) {\n this.rec.threw(ex);\n }\n }\n\n // And clean up any objects now that they're done being used.\n for (const o of this.objectsToCleanUp) {\n if ('getExtension' in o) {\n const WEBGL_lose_context = o.getExtension('WEBGL_lose_context');\n if (WEBGL_lose_context) WEBGL_lose_context.loseContext();\n } else if ('destroy' in o) {\n o.destroy();\n } else if ('destroyAsync' in o) {\n await o.destroyAsync();\n } else if ('close' in o) {\n o.close();\n } else {\n // HTMLVideoElement\n o.src = '';\n o.srcObject = null;\n }\n }\n }\n\n /**\n * Tracks an object to be cleaned up after the test finishes.\n *\n * Usually when creating buffers/textures/query sets, you can use the helpers in GPUTest instead.\n */\n trackForCleanup>(o: T): T {\n if (o instanceof Promise) {\n this.eventualAsyncExpectation(() =>\n o.then(\n o => this.trackForCleanup(o),\n () => {}\n )\n );\n return o;\n }\n\n if (o instanceof GPUDevice) {\n this.objectsToCleanUp.push({\n async destroyAsync() {\n o.destroy();\n await o.lost;\n },\n });\n } else {\n this.objectsToCleanUp.push(o);\n }\n return o;\n }\n\n /** Tracks an object, if it's destroyable, to be cleaned up after the test finishes. */\n tryTrackForCleanup(o: T): T {\n if (typeof o === 'object' && o !== null) {\n if (\n 'destroy' in o ||\n 'close' in o ||\n o instanceof WebGLRenderingContext ||\n o instanceof WebGL2RenderingContext\n ) {\n this.objectsToCleanUp.push(o as unknown as DestroyableObject);\n }\n }\n return o;\n }\n\n /** Call requestDevice() and track the device for cleanup. */\n requestDeviceTracked(adapter: GPUAdapter, desc: GPUDeviceDescriptor | undefined = undefined) {\n // eslint-disable-next-line no-restricted-syntax\n return this.trackForCleanup(adapter.requestDevice(desc));\n }\n\n /** Log a debug message. */\n debug(msg: string): void {\n this.rec.debug(new Error(msg));\n }\n\n /**\n * Log an info message.\n * **Use sparingly. Use `debug()` instead if logs are only needed with debug logging enabled.**\n */\n info(msg: string): void {\n this.rec.info(new Error(msg));\n }\n\n /** Throws an exception marking the subcase as skipped. */\n skip(msg: string): never {\n throw new SkipTestCase(msg);\n }\n\n /** Throws an exception marking the subcase as skipped if condition is true */\n skipIf(cond: boolean, msg: string | (() => string) = '') {\n if (cond) {\n this.skip(typeof msg === 'function' ? msg() : msg);\n }\n }\n\n /** Log a warning and increase the result status to \"Warn\". */\n warn(msg?: string): void {\n this.rec.warn(new Error(msg));\n }\n\n /** Log an error and increase the result status to \"ExpectFailed\". */\n fail(msg?: string): void {\n this.rec.expectationFailed(new Error(msg));\n }\n\n /**\n * Wraps an async function. Tracks its status to fail if the test tries to report a test status\n * before the async work has finished.\n */\n protected async immediateAsyncExpectation(fn: () => Promise): Promise {\n this.numOutstandingAsyncExpectations++;\n const ret = await fn();\n this.numOutstandingAsyncExpectations--;\n return ret;\n }\n\n /**\n * Wraps an async function, passing it an `Error` object recording the original stack trace.\n * The async work will be implicitly waited upon before reporting a test status.\n */\n protected eventualAsyncExpectation(fn: (niceStack: Error) => Promise): void {\n const promise = fn(new Error());\n this.eventualExpectations.push(promise);\n }\n\n private expectErrorValue(expectedError: string | true, ex: unknown, niceStack: Error): void {\n if (!(ex instanceof Error)) {\n niceStack.message = `THREW non-error value, of type ${typeof ex}: ${ex}`;\n this.rec.expectationFailed(niceStack);\n return;\n }\n const actualName = ex.name;\n if (expectedError !== true && actualName !== expectedError) {\n niceStack.message = `THREW ${actualName}, instead of ${expectedError}: ${ex}`;\n this.rec.expectationFailed(niceStack);\n } else {\n niceStack.message = `OK: threw ${actualName}: ${ex.message}`;\n this.rec.debug(niceStack);\n }\n }\n\n /** Expect that the provided promise resolves (fulfills). */\n shouldResolve(p: Promise, msg?: string): void {\n this.eventualAsyncExpectation(async niceStack => {\n const m = msg ? ': ' + msg : '';\n try {\n await p;\n niceStack.message = 'resolved as expected' + m;\n } catch (ex) {\n niceStack.message = `REJECTED${m}`;\n if (ex instanceof Error) {\n niceStack.message += '\\n' + ex.message;\n }\n this.rec.expectationFailed(niceStack);\n }\n });\n }\n\n /** Expect that the provided promise rejects, with the provided exception name. */\n shouldReject(\n expectedName: string,\n p: Promise,\n { allowMissingStack = false, message }: ExceptionCheckOptions = {}\n ): void {\n this.eventualAsyncExpectation(async niceStack => {\n const m = message ? ': ' + message : '';\n try {\n await p;\n niceStack.message = 'DID NOT REJECT' + m;\n this.rec.expectationFailed(niceStack);\n } catch (ex) {\n this.expectErrorValue(expectedName, ex, niceStack);\n if (!allowMissingStack) {\n if (!(ex instanceof Error && typeof ex.stack === 'string')) {\n const exMessage = ex instanceof Error ? ex.message : '?';\n niceStack.message = `rejected as expected, but missing stack (${exMessage})${m}`;\n this.rec.expectationFailed(niceStack);\n }\n }\n }\n });\n }\n\n /**\n * Expect that the provided function throws (if `true` or `string`) or not (if `false`).\n * If a string is provided, expect that the throw exception has that name.\n *\n * MAINTENANCE_TODO: Change to `string | false` so the exception name is always checked.\n */\n shouldThrow(\n expectedError: string | boolean,\n fn: () => void,\n { allowMissingStack = false, message }: ExceptionCheckOptions = {}\n ) {\n const m = message ? ': ' + message : '';\n try {\n fn();\n if (expectedError === false) {\n this.rec.debug(new Error('did not throw, as expected' + m));\n } else {\n this.rec.expectationFailed(new Error('unexpectedly did not throw' + m));\n }\n } catch (ex) {\n if (expectedError === false) {\n this.rec.expectationFailed(new Error('threw unexpectedly' + m));\n } else {\n this.expectErrorValue(expectedError, ex, new Error(m));\n if (!allowMissingStack) {\n if (!(ex instanceof Error && typeof ex.stack === 'string')) {\n this.rec.expectationFailed(new Error('threw as expected, but missing stack' + m));\n }\n }\n }\n }\n }\n\n /** Expect that a condition is true. */\n expect(cond: boolean, msg?: string): boolean {\n if (cond) {\n const m = msg ? ': ' + msg : '';\n this.rec.debug(new Error('expect OK' + m));\n } else {\n this.rec.expectationFailed(new Error(msg));\n }\n return cond;\n }\n\n /**\n * If the argument is an `Error`, fail (or warn). If it's `undefined`, no-op.\n * If the argument is an array, apply the above behavior on each of elements.\n */\n expectOK(\n error: Error | undefined | (Error | undefined)[],\n { mode = 'fail', niceStack }: { mode?: 'fail' | 'warn'; niceStack?: Error } = {}\n ): void {\n const handleError = (error: Error | undefined) => {\n if (error instanceof Error) {\n if (niceStack) {\n error.stack = niceStack.stack;\n }\n if (mode === 'fail') {\n this.rec.expectationFailed(error);\n } else if (mode === 'warn') {\n this.rec.warn(error);\n } else {\n unreachable();\n }\n }\n };\n\n if (Array.isArray(error)) {\n for (const e of error) {\n handleError(e);\n }\n } else {\n handleError(error);\n }\n }\n\n eventualExpectOK(\n error: Promise,\n { mode = 'fail' }: { mode?: 'fail' | 'warn' } = {}\n ) {\n this.eventualAsyncExpectation(async niceStack => {\n this.expectOK(await error, { mode, niceStack });\n });\n }\n}\n\nexport type SubcaseBatchStateFromFixture = F extends Fixture ? S : never;\n\n/**\n * FixtureClass encapsulates a constructor for fixture and a corresponding\n * shared state factory function. An interface version of the type is also\n * defined for mixin declaration use ONLY. The interface version is necessary\n * because mixin classes need a constructor with a single any[] rest\n * parameter.\n */\nexport type FixtureClass = {\n new (sharedState: SubcaseBatchStateFromFixture, log: TestCaseRecorder, params: TestParams): F;\n MakeSharedState(recorder: TestCaseRecorder, params: TestParams): SubcaseBatchStateFromFixture;\n};\nexport type FixtureClassInterface = {\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n new (...args: any[]): F;\n MakeSharedState(recorder: TestCaseRecorder, params: TestParams): SubcaseBatchStateFromFixture;\n};\nexport type FixtureClassWithMixin = FC extends FixtureClass\n ? FixtureClass\n : never;\n"],"mappings":"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"} \ No newline at end of file diff --git a/out/common/framework/metadata.js b/out/common/framework/metadata.js new file mode 100644 index 000000000000..fd33396dfc59 --- /dev/null +++ b/out/common/framework/metadata.js @@ -0,0 +1,30 @@ +/** +* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts +**/import { assert } from '../util/util.js'; /** Metadata about tests (that can't be derived at runtime). */ + + + + + + + + + + + + + +export function loadMetadataForSuite(suiteDir) { + assert(typeof require !== 'undefined', 'loadMetadataForSuite is only implemented on Node'); + + const fs = require('fs'); + + const metadataFile = `${suiteDir}/listing_meta.json`; + if (!fs.existsSync(metadataFile)) { + return null; + } + + const metadata = JSON.parse(fs.readFileSync(metadataFile, 'utf8')); + return metadata; +} +//# sourceMappingURL=metadata.js.map \ No newline at end of file diff --git a/out/common/framework/metadata.js.map b/out/common/framework/metadata.js.map new file mode 100644 index 000000000000..16feb0f7e94f --- /dev/null +++ b/out/common/framework/metadata.js.map @@ -0,0 +1 @@ +{"version":3,"file":"metadata.js","names":["assert","loadMetadataForSuite","suiteDir","require","fs","metadataFile","existsSync","metadata","JSON","parse","readFileSync"],"sources":["../../../src/common/framework/metadata.ts"],"sourcesContent":["import { assert } from '../util/util.js';\n\n/** Metadata about tests (that can't be derived at runtime). */\nexport type TestMetadata = {\n /**\n * Estimated average time-per-subcase, in milliseconds.\n * This is used to determine chunking granularity when exporting to WPT with\n * chunking enabled (like out-wpt/cts-chunked2sec.https.html).\n */\n subcaseMS: number;\n};\n\nexport type TestMetadataListing = {\n [testQuery: string]: TestMetadata;\n};\n\nexport function loadMetadataForSuite(suiteDir: string): TestMetadataListing | null {\n assert(typeof require !== 'undefined', 'loadMetadataForSuite is only implemented on Node');\n /* eslint-disable-next-line n/no-restricted-require */\n const fs = require('fs');\n\n const metadataFile = `${suiteDir}/listing_meta.json`;\n if (!fs.existsSync(metadataFile)) {\n return null;\n }\n\n const metadata: TestMetadataListing = JSON.parse(fs.readFileSync(metadataFile, 'utf8'));\n return metadata;\n}\n"],"mappings":";;GAAA,SAASA,MAAM,QAAQ,iBAAiB,CAAC,CAEzC;;;;;;;;;;;;;;AAcA,OAAO,SAASC,oBAAoBA,CAACC,QAAgB,EAA8B;EACjFF,MAAM,CAAC,OAAOG,OAAO,KAAK,WAAW,EAAE,kDAAkD,CAAC;;EAE1F,MAAMC,EAAE,GAAGD,OAAO,CAAC,IAAI,CAAC;;EAExB,MAAME,YAAY,GAAI,GAAEH,QAAS,oBAAmB;EACpD,IAAI,CAACE,EAAE,CAACE,UAAU,CAACD,YAAY,CAAC,EAAE;IAChC,OAAO,IAAI;EACb;;EAEA,MAAME,QAA6B,GAAGC,IAAI,CAACC,KAAK,CAACL,EAAE,CAACM,YAAY,CAACL,YAAY,EAAE,MAAM,CAAC,CAAC;EACvF,OAAOE,QAAQ;AACjB"} \ No newline at end of file diff --git a/out/common/framework/params_builder.js b/out/common/framework/params_builder.js new file mode 100644 index 000000000000..b5407cdbdefa --- /dev/null +++ b/out/common/framework/params_builder.js @@ -0,0 +1,390 @@ +/** +* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts +**/import { mergeParams, mergeParamsChecked } from '../internal/params_utils.js';import { comparePublicParamsPaths, Ordering } from '../internal/query/compare.js';import { stringifyPublicParams } from '../internal/query/stringify_params.js'; + +import { assert, mapLazy, objectEquals } 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.) + */ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/** + * Determines the resulting parameter object type which would be generated by an object of + * the given ParamsBuilder type. + */ + + + + + + + + + +// ================================================================ +// Implementation +// ================================================================ + +/** + * Iterable over pairs of either: + * - `[case params, Iterable]` if there are subcases. + * - `[case params, undefined]` if not. + */ + + + + +/** + * 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, +caseFilter) +{ + + + + + return builder.iterateCasesWithSubcases(caseFilter); +} + +/** + * 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(caseFilter) { + for (const caseP of this.cases(caseFilter)) { + if (caseFilter) { + // this.cases() only filters out cases which conflict with caseFilter. Now that we have + // the final caseP, filter out cases which are missing keys that caseFilter requires. + const ordering = comparePublicParamsPaths(caseP, caseFilter); + if (ordering === Ordering.StrictSuperset || ordering === Ordering.Unordered) { + continue; + } + } + + yield [caseP, undefined]; + } + } + + [Symbol.iterator]() { + return this.cases(null); + } + + /** @inheritDoc */ + expandWithParams( + expander) + { + const baseGenerator = this.cases; + return new CaseParamsBuilder(function* (caseFilter) { + for (const a of baseGenerator(caseFilter)) { + for (const b of expander(a)) { + if (caseFilter) { + // If the expander generated any key-value pair that conflicts with caseFilter, skip. + const kvPairs = Object.entries(b); + if (kvPairs.some(([k, v]) => k in caseFilter && !objectEquals(caseFilter[k], v))) { + continue; + } + } + + yield mergeParamsChecked(a, b); + } + } + }); + } + + /** @inheritDoc */ + expand( + key, + expander) + { + const baseGenerator = this.cases; + return new CaseParamsBuilder(function* (caseFilter) { + for (const a of baseGenerator(caseFilter)) { + assert(!(key in a), `New key '${key}' already exists in ${JSON.stringify(a)}`); + + for (const v of expander(a)) { + // If the expander generated a value for this key that conflicts with caseFilter, skip. + if (caseFilter && key in caseFilter) { + if (!objectEquals(caseFilter[key], v)) { + continue; + } + } + yield { ...a, [key]: v }; + } + } + }); + } + + /** @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 baseGenerator = this.cases; + return new CaseParamsBuilder(function* (caseFilter) { + for (const a of baseGenerator(caseFilter)) { + if (pred(a)) yield a; + } + }); + } + + /** @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(caseFilter) { + for (const caseP of this.cases(caseFilter)) { + if (caseFilter) { + // this.cases() only filters out cases which conflict with caseFilter. Now that we have + // the final caseP, filter out cases which are missing keys that caseFilter requires. + const ordering = comparePublicParamsPaths(caseP, caseFilter); + if (ordering === Ordering.StrictSuperset || ordering === Ordering.Unordered) { + continue; + } + } + + const subcases = Array.from(this.subcases(caseP)); + if (subcases.length) { + yield [ + caseP, + subcases]; + + } + } + } + + /** @inheritDoc */ + expandWithParams( + expander) + { + const baseGenerator = this.subcases; + return new SubcaseParamsBuilder(this.cases, function* (base) { + for (const a of baseGenerator(base)) { + for (const b of expander(mergeParams(base, a))) { + yield mergeParamsChecked(a, b); + } + } + }); + } + + /** @inheritDoc */ + expand( + key, + expander) + { + const baseGenerator = this.subcases; + return new SubcaseParamsBuilder(this.cases, function* (base) { + for (const a of baseGenerator(base)) { + const before = mergeParams(base, a); + assert(!(key in before), () => `Key '${key}' already exists in ${JSON.stringify(before)}`); + + for (const v of expander(before)) { + yield { ...a, [key]: v }; + } + } + }); + } + + /** @inheritDoc */ + combineWithParams( + newParams) + { + assertNotGenerator(newParams); + return this.expandWithParams(() => newParams); + } + + /** @inheritDoc */ + combine( + key, + values) + { + assertNotGenerator(values); + return this.expand(key, () => values); + } + + /** @inheritDoc */ + filter(pred) { + const baseGenerator = this.subcases; + return new SubcaseParamsBuilder(this.cases, function* (base) { + for (const a of baseGenerator(base)) { + if (pred(mergeParams(base, a))) yield a; + } + }); + } + + /** @inheritDoc */ + unless(pred) { + return this.filter((x) => !pred(x)); + } +} + +/** 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' + ); + } +} +//# sourceMappingURL=params_builder.js.map \ No newline at end of file diff --git a/out/common/framework/params_builder.js.map b/out/common/framework/params_builder.js.map new file mode 100644 index 000000000000..e9c6c1574d58 --- /dev/null +++ b/out/common/framework/params_builder.js.map @@ -0,0 +1 @@ +{"version":3,"file":"params_builder.js","names":["mergeParams","mergeParamsChecked","comparePublicParamsPaths","Ordering","stringifyPublicParams","assert","mapLazy","objectEquals","ParamsBuilderBase","constructor","cases","builderIterateCasesWithSubcases","builder","caseFilter","iterateCasesWithSubcases","CaseParamsBuilder","caseP","ordering","StrictSuperset","Unordered","undefined","Symbol","iterator","expandWithParams","expander","baseGenerator","a","b","kvPairs","Object","entries","some","k","v","expand","key","JSON","stringify","combineWithParams","newParams","assertNotGenerator","seenValues","Set","params","paramsStr","has","add","combine","values","mapped","filter","pred","unless","x","beginSubcases","SubcaseParamsBuilder","kUnitCaseParamsBuilder","generator","subcases","Array","from","length","base","before"],"sources":["../../../src/common/framework/params_builder.ts"],"sourcesContent":["import { Merged, mergeParams, mergeParamsChecked } from '../internal/params_utils.js';\nimport { comparePublicParamsPaths, Ordering } from '../internal/query/compare.js';\nimport { stringifyPublicParams } from '../internal/query/stringify_params.js';\nimport { DeepReadonly } from '../util/types.js';\nimport { assert, mapLazy, objectEquals } from '../util/util.js';\n\nimport { TestParams } from './fixture.js';\n\n// ================================================================\n// \"Public\" ParamsBuilder API / Documentation\n// ================================================================\n\n/**\n * Provides doc comments for the methods of CaseParamsBuilder and SubcaseParamsBuilder.\n * (Also enforces rough interface match between them.)\n */\nexport interface ParamsBuilder {\n /**\n * Expands each item in `this` into zero or more items.\n * Each item has its parameters expanded with those returned by the `expander`.\n *\n * **Note:** When only a single key is being added, use the simpler `expand` for readability.\n *\n * ```text\n * this = [ a , b , c ]\n * this.map(expander) = [ f(a) f(b) f(c) ]\n * = [[a1, a2, a3] , [ b1 ] , [] ]\n * merge and flatten = [ merge(a, a1), merge(a, a2), merge(a, a3), merge(b, b1) ]\n * ```\n */\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n expandWithParams(expander: (_: any) => any): any;\n\n /**\n * Expands each item in `this` into zero or more items. Each item has its parameters expanded\n * with one new key, `key`, and the values returned by `expander`.\n */\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n expand(key: string, expander: (_: any) => any): any;\n\n /**\n * Expands each item in `this` to multiple items, one for each item in `newParams`.\n *\n * In other words, takes the cartesian product of [ the items in `this` ] and `newParams`.\n *\n * **Note:** When only a single key is being added, use the simpler `combine` for readability.\n *\n * ```text\n * this = [ {a:1}, {b:2} ]\n * newParams = [ {x:1}, {y:2} ]\n * this.combineP(newParams) = [ {a:1,x:1}, {a:1,y:2}, {b:2,x:1}, {b:2,y:2} ]\n * ```\n */\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n combineWithParams(newParams: Iterable): any;\n\n /**\n * Expands each item in `this` to multiple items with `{ [name]: value }` for each value.\n *\n * In other words, takes the cartesian product of [ the items in `this` ]\n * and `[ {[name]: value} for each value in values ]`\n */\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n combine(key: string, newParams: Iterable): any;\n\n /**\n * Filters `this` to only items for which `pred` returns true.\n */\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n filter(pred: (_: any) => boolean): any;\n\n /**\n * Filters `this` to only items for which `pred` returns false.\n */\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n unless(pred: (_: any) => boolean): any;\n}\n\n/**\n * Determines the resulting parameter object type which would be generated by an object of\n * the given ParamsBuilder type.\n */\nexport type ParamTypeOf<\n /* eslint-disable-next-line @typescript-eslint/no-explicit-any */\n T extends ParamsBuilder,\n> = T extends SubcaseParamsBuilder\n ? Merged\n : T extends CaseParamsBuilder\n ? CaseP\n : never;\n\n// ================================================================\n// Implementation\n// ================================================================\n\n/**\n * Iterable over pairs of either:\n * - `[case params, Iterable]` if there are subcases.\n * - `[case params, undefined]` if not.\n */\nexport type CaseSubcaseIterable = Iterable<\n readonly [DeepReadonly, Iterable> | undefined]\n>;\n\n/**\n * Base class for `CaseParamsBuilder` and `SubcaseParamsBuilder`.\n */\nexport abstract class ParamsBuilderBase {\n protected readonly cases: (caseFilter: TestParams | null) => Generator;\n\n constructor(cases: (caseFilter: TestParams | null) => Generator) {\n this.cases = cases;\n }\n\n /**\n * Hidden from test files. Use `builderIterateCasesWithSubcases` to access this.\n */\n protected abstract iterateCasesWithSubcases(\n caseFilter: TestParams | null\n ): CaseSubcaseIterable;\n}\n\n/**\n * Calls the (normally hidden) `iterateCasesWithSubcases()` method.\n */\nexport function builderIterateCasesWithSubcases(\n builder: ParamsBuilderBase<{}, {}>,\n caseFilter: TestParams | null\n) {\n interface IterableParamsBuilder {\n iterateCasesWithSubcases(caseFilter: TestParams | null): CaseSubcaseIterable<{}, {}>;\n }\n\n return (builder as unknown as IterableParamsBuilder).iterateCasesWithSubcases(caseFilter);\n}\n\n/**\n * Builder for combinatorial test **case** parameters.\n *\n * CaseParamsBuilder is immutable. Each method call returns a new, immutable object,\n * modifying the list of cases according to the method called.\n *\n * This means, for example, that the `unit` passed into `TestBuilder.params()` can be reused.\n */\nexport class CaseParamsBuilder\n extends ParamsBuilderBase\n implements Iterable>, ParamsBuilder\n{\n *iterateCasesWithSubcases(caseFilter: TestParams | null): CaseSubcaseIterable {\n for (const caseP of this.cases(caseFilter)) {\n if (caseFilter) {\n // this.cases() only filters out cases which conflict with caseFilter. Now that we have\n // the final caseP, filter out cases which are missing keys that caseFilter requires.\n const ordering = comparePublicParamsPaths(caseP, caseFilter);\n if (ordering === Ordering.StrictSuperset || ordering === Ordering.Unordered) {\n continue;\n }\n }\n\n yield [caseP as DeepReadonly, undefined];\n }\n }\n\n [Symbol.iterator](): Iterator> {\n return this.cases(null) as Iterator>;\n }\n\n /** @inheritDoc */\n expandWithParams(\n expander: (_: CaseP) => Iterable\n ): CaseParamsBuilder> {\n const baseGenerator = this.cases;\n return new CaseParamsBuilder(function* (caseFilter) {\n for (const a of baseGenerator(caseFilter)) {\n for (const b of expander(a)) {\n if (caseFilter) {\n // If the expander generated any key-value pair that conflicts with caseFilter, skip.\n const kvPairs = Object.entries(b);\n if (kvPairs.some(([k, v]) => k in caseFilter && !objectEquals(caseFilter[k], v))) {\n continue;\n }\n }\n\n yield mergeParamsChecked(a, b);\n }\n }\n });\n }\n\n /** @inheritDoc */\n expand(\n key: NewPKey,\n expander: (_: CaseP) => Iterable\n ): CaseParamsBuilder> {\n const baseGenerator = this.cases;\n return new CaseParamsBuilder(function* (caseFilter) {\n for (const a of baseGenerator(caseFilter)) {\n assert(!(key in a), `New key '${key}' already exists in ${JSON.stringify(a)}`);\n\n for (const v of expander(a)) {\n // If the expander generated a value for this key that conflicts with caseFilter, skip.\n if (caseFilter && key in caseFilter) {\n if (!objectEquals(caseFilter[key], v)) {\n continue;\n }\n }\n yield { ...a, [key]: v } as Merged;\n }\n }\n });\n }\n\n /** @inheritDoc */\n combineWithParams(\n newParams: Iterable\n ): CaseParamsBuilder> {\n assertNotGenerator(newParams);\n const seenValues = new Set();\n for (const params of newParams) {\n const paramsStr = stringifyPublicParams(params);\n assert(!seenValues.has(paramsStr), `Duplicate entry in combine[WithParams]: ${paramsStr}`);\n seenValues.add(paramsStr);\n }\n\n return this.expandWithParams(() => newParams);\n }\n\n /** @inheritDoc */\n combine(\n key: NewPKey,\n values: Iterable\n ): CaseParamsBuilder> {\n assertNotGenerator(values);\n const mapped = mapLazy(values, v => ({ [key]: v }) as { [name in NewPKey]: NewPValue });\n return this.combineWithParams(mapped);\n }\n\n /** @inheritDoc */\n filter(pred: (_: CaseP) => boolean): CaseParamsBuilder {\n const baseGenerator = this.cases;\n return new CaseParamsBuilder(function* (caseFilter) {\n for (const a of baseGenerator(caseFilter)) {\n if (pred(a)) yield a;\n }\n });\n }\n\n /** @inheritDoc */\n unless(pred: (_: CaseP) => boolean): CaseParamsBuilder {\n return this.filter(x => !pred(x));\n }\n\n /**\n * \"Finalize\" the list of cases and begin defining subcases.\n * Returns a new SubcaseParamsBuilder. Methods called on SubcaseParamsBuilder\n * generate new subcases instead of new cases.\n */\n beginSubcases(): SubcaseParamsBuilder {\n return new SubcaseParamsBuilder(this.cases, function* () {\n yield {};\n });\n }\n}\n\n/**\n * The unit CaseParamsBuilder, representing a single case with no params: `[ {} ]`.\n *\n * `punit` is passed to every `.params()`/`.paramsSubcasesOnly()` call, so `kUnitCaseParamsBuilder`\n * is only explicitly needed if constructing a ParamsBuilder outside of a test builder.\n */\nexport const kUnitCaseParamsBuilder = new CaseParamsBuilder(function* () {\n yield {};\n});\n\n/**\n * Builder for combinatorial test _subcase_ parameters.\n *\n * SubcaseParamsBuilder is immutable. Each method call returns a new, immutable object,\n * modifying the list of subcases according to the method called.\n */\nexport class SubcaseParamsBuilder\n extends ParamsBuilderBase\n implements ParamsBuilder\n{\n protected readonly subcases: (_: CaseP) => Generator;\n\n constructor(\n cases: (caseFilter: TestParams | null) => Generator,\n generator: (_: CaseP) => Generator\n ) {\n super(cases);\n this.subcases = generator;\n }\n\n *iterateCasesWithSubcases(caseFilter: TestParams | null): CaseSubcaseIterable {\n for (const caseP of this.cases(caseFilter)) {\n if (caseFilter) {\n // this.cases() only filters out cases which conflict with caseFilter. Now that we have\n // the final caseP, filter out cases which are missing keys that caseFilter requires.\n const ordering = comparePublicParamsPaths(caseP, caseFilter);\n if (ordering === Ordering.StrictSuperset || ordering === Ordering.Unordered) {\n continue;\n }\n }\n\n const subcases = Array.from(this.subcases(caseP));\n if (subcases.length) {\n yield [\n caseP as DeepReadonly,\n subcases as DeepReadonly<(typeof subcases)[number]>[],\n ];\n }\n }\n }\n\n /** @inheritDoc */\n expandWithParams(\n expander: (_: Merged) => Iterable\n ): SubcaseParamsBuilder> {\n const baseGenerator = this.subcases;\n return new SubcaseParamsBuilder(this.cases, function* (base) {\n for (const a of baseGenerator(base)) {\n for (const b of expander(mergeParams(base, a))) {\n yield mergeParamsChecked(a, b);\n }\n }\n });\n }\n\n /** @inheritDoc */\n expand(\n key: NewPKey,\n expander: (_: Merged) => Iterable\n ): SubcaseParamsBuilder> {\n const baseGenerator = this.subcases;\n return new SubcaseParamsBuilder(this.cases, function* (base) {\n for (const a of baseGenerator(base)) {\n const before = mergeParams(base, a);\n assert(!(key in before), () => `Key '${key}' already exists in ${JSON.stringify(before)}`);\n\n for (const v of expander(before)) {\n yield { ...a, [key]: v } as Merged;\n }\n }\n });\n }\n\n /** @inheritDoc */\n combineWithParams(\n newParams: Iterable\n ): SubcaseParamsBuilder> {\n assertNotGenerator(newParams);\n return this.expandWithParams(() => newParams);\n }\n\n /** @inheritDoc */\n combine(\n key: NewPKey,\n values: Iterable\n ): SubcaseParamsBuilder> {\n assertNotGenerator(values);\n return this.expand(key, () => values);\n }\n\n /** @inheritDoc */\n filter(pred: (_: Merged) => boolean): SubcaseParamsBuilder {\n const baseGenerator = this.subcases;\n return new SubcaseParamsBuilder(this.cases, function* (base) {\n for (const a of baseGenerator(base)) {\n if (pred(mergeParams(base, a))) yield a;\n }\n });\n }\n\n /** @inheritDoc */\n unless(pred: (_: Merged) => boolean): SubcaseParamsBuilder {\n return this.filter(x => !pred(x));\n }\n}\n\n/** Assert an object is not a Generator (a thing returned from a generator function). */\nfunction assertNotGenerator(x: object) {\n if ('constructor' in x) {\n assert(\n x.constructor !== (function* () {})().constructor,\n 'Argument must not be a generator, as generators are not reusable'\n );\n }\n}\n"],"mappings":"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"} \ No newline at end of file diff --git a/out/common/framework/resources.js b/out/common/framework/resources.js new file mode 100644 index 000000000000..9c475604defa --- /dev/null +++ b/out/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()`, ``, `