/** * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts **/import { Float16Array } from '../../external/petamoriken/float16/float16.js';import { SkipTestCase } from '../framework/fixture.js';import { globalTestConfig } from '../framework/test_config.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 = globalTestConfig.enableDebugLogs ? { ...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; } /** Options for assertReject, shouldReject, and friends. */ /** * Resolves if the provided promise rejects; rejects if it does not. */ export async function assertReject( expectedName, p, { allowMissingStack = false, message } = {}) { await p.then( () => { unreachable(message); }, (ex) => { assert(ex instanceof Error, 'rejected with a non-Error object'); assert(ex.name === expectedName, `rejected with name ${ex.name} instead of ${expectedName}`); // Asserted as expected if (!allowMissingStack) { const m = message ? ` (${message})` : ''; assert(typeof ex.stack === 'string', 'threw as expected, but missing stack' + m); } } ); } /** * Assert this code is unreachable. Unconditionally throws an `Error`. */ export function unreachable(msg) { throw new Error(msg); } /** * Throw a `SkipTestCase` exception, which skips the test case. */ export function skipTestCase(msg) { throw new SkipTestCase(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); void 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; } /** * Returns true if v is a plain JavaScript object. */ export function isPlainObject(v) { return !!v && Object.getPrototypeOf(v).constructor === Object.prototype.constructor; } /** * 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)`. +/-0.0 are treated as equal * by default, but can be opted to be distinguished. * @param x the first JS values that get compared * @param y the second JS values that get compared * @param distinguishSignedZero if set to true, treat 0.0 and -0.0 as unequal. Default to false. */ export function objectEquals( x, y, distinguishSignedZero = false) { if (typeof x !== 'object' || typeof y !== 'object') { if (typeof x === 'number' && typeof y === 'number' && Number.isNaN(x) && Number.isNaN(y)) { return true; } // Object.is(0.0, -0.0) is false while (0.0 === -0.0) is true. Other than +/-0.0 and NaN cases, // Object.is works in the same way as ===. return distinguishSignedZero ? Object.is(x, y) : 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); } } }; } /** Count the number of elements `x` for which `predicate(x)` is true. */ export function count(xs, predicate) { let count = 0; for (const x of xs) { if (predicate(x)) count++; } return count; } const ReorderOrders = { forward: true, backward: true, shiftByHalf: true }; export const kReorderOrderKeys = keysOf(ReorderOrders); /** * Creates a new array from the given array with the first half * swapped with the last half. */ export function shiftByHalf(arr) { const len = arr.length; const half = len / 2 | 0; const firstHalf = arr.splice(0, half); return [...arr, ...firstHalf]; } /** * Creates a reordered array from the input array based on the Order */ export function reorder(order, arr) { switch (order) { case 'forward': return arr.slice(); case 'backward': return arr.slice().reverse(); case 'shiftByHalf':{ // should this be pseudo random? return shiftByHalf(arr); } } } /** * A typed version of Object.entries */ export function typedEntries(obj) { // The cast is done once, inside the helper function, // keeping the call site clean and type-safe. return Object.entries(obj); } const TypedArrayBufferViewInstances = [ new Uint8Array(), new Uint8ClampedArray(), new Uint16Array(), new Uint32Array(), new Int8Array(), new Int16Array(), new Int32Array(), new Float16Array(), new Float32Array(), new Float64Array(), new BigInt64Array(), new BigUint64Array()]; 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); /** * Creates a case parameter for a typedarray. * * You can't put typedarrays in case parameters directly so instead of * * ``` * u.combine('data', [ * new Uint8Array([1, 2, 3]), * new Float32Array([4, 5, 6]), * ]) * ``` * * You can use * * ``` * u.combine('data', [ * typedArrayParam('Uint8Array' [1, 2, 3]), * typedArrayParam('Float32Array' [4, 5, 6]), * ]) * ``` * * and then convert the params to typedarrays eg. * * ``` * .fn(t => { * const data = t.params.data.map(v => typedArrayFromParam(v)); * }) * ``` */ export function typedArrayParam( type, data) { return { type, data }; } export function createTypedArray( type, data) { return new kTypedArrayBufferViews[type](data); } /** * Converts a TypedArrayParam to a typedarray. See typedArrayParam */ export function typedArrayFromParam( param) { const { type, data } = param; return createTypedArray(type, data); } 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)); } /** * Used to create a value that is specified by multiplying some runtime value * by a constant and then adding a constant to it. */ /** * Filters out SpecValues that are the same. */ export function filterUniqueValueTestVariants(valueTestVariants) { return new Map( valueTestVariants.map((v) => [`m:${v.mult},a:${v.add}`, v]) ).values(); } /** * Used to create a value that is specified by multiplied some runtime value * by a constant and then adding a constant to it. This happens often in test * with limits that can only be known at runtime and yet we need a way to * add parameters to a test and those parameters must be constants. */ export function makeValueTestVariant(base, variant) { return base * variant.mult + variant.add; } /** * Use instead of features.has because feature's has takes any string * and we want to prevent typos. */ export function hasFeature(features, feature) { return features.has(feature); } /** Convenience helper for combinations of 1-2 usage bits from a list of usage bits. */ export function combinationsOfOneOrTwoUsages(usages) { const combinations = []; for (const usage0 of usages) { for (const usage1 of usages) { if (usage0 <= usage1) { combinations.push(usage0 | usage1); } } } return combinations; } /** * Checks if the browser supports immediate data (experimental). * * Checks for: * - `setImmediates` method on `GPURenderPassEncoder`, `GPUComputePassEncoder`, or `GPURenderBundleEncoder` prototypes. * - `maxImmediateSize` property on `GPUSupportedLimits` prototype. * - `immediate_address_space` feature in `gpu.wgslLanguageFeatures`. * * This helper is used to skip tests when the environment does not support immediate data functionality. */ export function supportsImmediateData(gpu) { return ( 'setImmediates' in GPURenderPassEncoder.prototype || 'setImmediates' in GPUComputePassEncoder.prototype || 'setImmediates' in GPURenderBundleEncoder.prototype || 'maxImmediateSize' in GPUSupportedLimits.prototype || gpu.wgslLanguageFeatures.has('immediate_address_space')); }