util.js (12540B)
1 /** 2 * AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts 3 **/import { Float16Array } from '../../external/petamoriken/float16/float16.js';import { SkipTestCase } from '../framework/fixture.js';import { globalTestConfig } from '../framework/test_config.js'; 4 5 import { keysOf } from './data_tables.js'; 6 import { timeout } from './timeout.js'; 7 8 /** 9 * Error with arbitrary `extra` data attached, for debugging. 10 * The extra data is omitted if not running the test in debug mode (`?debug=1`). 11 */ 12 export class ErrorWithExtra extends Error { 13 14 15 /** 16 * `extra` function is only called if in debug mode. 17 * If an `ErrorWithExtra` is passed, its message is used and its extras are passed through. 18 */ 19 20 21 constructor(baseOrMessage, newExtra) { 22 const message = typeof baseOrMessage === 'string' ? baseOrMessage : baseOrMessage.message; 23 super(message); 24 25 const oldExtras = baseOrMessage instanceof ErrorWithExtra ? baseOrMessage.extra : {}; 26 this.extra = globalTestConfig.enableDebugLogs ? 27 { ...oldExtras, ...newExtra() } : 28 { omitted: 'pass ?debug=1' }; 29 } 30 } 31 32 /** 33 * Asserts `condition` is true. Otherwise, throws an `Error` with the provided message. 34 */ 35 export function assert(condition, msg) { 36 if (!condition) { 37 throw new Error(msg && (typeof msg === 'string' ? msg : msg())); 38 } 39 } 40 41 /** If the argument is an Error, throw it. Otherwise, pass it back. */ 42 export function assertOK(value) { 43 if (value instanceof Error) { 44 throw value; 45 } 46 return value; 47 } 48 49 /** Options for assertReject, shouldReject, and friends. */ 50 51 52 /** 53 * Resolves if the provided promise rejects; rejects if it does not. 54 */ 55 export async function assertReject( 56 expectedName, 57 p, 58 { allowMissingStack = false, message } = {}) 59 { 60 await p.then( 61 () => { 62 unreachable(message); 63 }, 64 (ex) => { 65 assert(ex instanceof Error, 'rejected with a non-Error object'); 66 assert(ex.name === expectedName, `rejected with name ${ex.name} instead of ${expectedName}`); 67 // Asserted as expected 68 if (!allowMissingStack) { 69 const m = message ? ` (${message})` : ''; 70 assert(typeof ex.stack === 'string', 'threw as expected, but missing stack' + m); 71 } 72 } 73 ); 74 } 75 76 /** 77 * Assert this code is unreachable. Unconditionally throws an `Error`. 78 */ 79 export function unreachable(msg) { 80 throw new Error(msg); 81 } 82 83 /** 84 * Throw a `SkipTestCase` exception, which skips the test case. 85 */ 86 export function skipTestCase(msg) { 87 throw new SkipTestCase(msg); 88 } 89 90 /** 91 * The `performance` interface. 92 * It is available in all browsers, but it is not in scope by default in Node. 93 */ 94 95 const perf = typeof performance !== 'undefined' ? performance : require('perf_hooks').performance; 96 97 /** 98 * Calls the appropriate `performance.now()` depending on whether running in a browser or Node. 99 */ 100 export function now() { 101 return perf.now(); 102 } 103 104 /** 105 * Returns a promise which resolves after the specified time. 106 */ 107 export function resolveOnTimeout(ms) { 108 return new Promise((resolve) => { 109 timeout(() => { 110 resolve(); 111 }, ms); 112 }); 113 } 114 115 export class PromiseTimeoutError extends Error {} 116 117 /** 118 * Returns a promise which rejects after the specified time. 119 */ 120 export function rejectOnTimeout(ms, msg) { 121 return new Promise((_resolve, reject) => { 122 timeout(() => { 123 reject(new PromiseTimeoutError(msg)); 124 }, ms); 125 }); 126 } 127 128 /** 129 * Takes a promise `p`, and returns a new one which rejects if `p` takes too long, 130 * and otherwise passes the result through. 131 */ 132 export function raceWithRejectOnTimeout(p, ms, msg) { 133 if (globalTestConfig.noRaceWithRejectOnTimeout) { 134 return p; 135 } 136 // Setup a promise that will reject after `ms` milliseconds. We cancel this timeout when 137 // `p` is finalized, so the JavaScript VM doesn't hang around waiting for the timer to 138 // complete, once the test runner has finished executing the tests. 139 const timeoutPromise = new Promise((_resolve, reject) => { 140 const handle = timeout(() => { 141 reject(new PromiseTimeoutError(msg)); 142 }, ms); 143 p = p.finally(() => clearTimeout(handle)); 144 }); 145 return Promise.race([p, timeoutPromise]); 146 } 147 148 /** 149 * Takes a promise `p` and returns a new one which rejects if `p` resolves or rejects, 150 * and otherwise resolves after the specified time. 151 */ 152 export function assertNotSettledWithinTime( 153 p, 154 ms, 155 msg) 156 { 157 // Rejects regardless of whether p resolves or rejects. 158 const rejectWhenSettled = p.then(() => Promise.reject(new Error(msg))); 159 // Resolves after `ms` milliseconds. 160 const timeoutPromise = new Promise((resolve) => { 161 const handle = timeout(() => { 162 resolve(undefined); 163 }, ms); 164 void p.finally(() => clearTimeout(handle)); 165 }); 166 return Promise.race([rejectWhenSettled, timeoutPromise]); 167 } 168 169 /** 170 * Returns a `Promise.reject()`, but also registers a dummy `.catch()` handler so it doesn't count 171 * as an uncaught promise rejection in the runtime. 172 */ 173 export function rejectWithoutUncaught(err) { 174 const p = Promise.reject(err); 175 // Suppress uncaught promise rejection. 176 p.catch(() => {}); 177 return p; 178 } 179 180 /** 181 * Returns true if v is a plain JavaScript object. 182 */ 183 export function isPlainObject(v) { 184 return !!v && Object.getPrototypeOf(v).constructor === Object.prototype.constructor; 185 } 186 187 /** 188 * Makes a copy of a JS `object`, with the keys reordered into sorted order. 189 */ 190 export function sortObjectByKey(v) { 191 const sortedObject = {}; 192 for (const k of Object.keys(v).sort()) { 193 sortedObject[k] = v[k]; 194 } 195 return sortedObject; 196 } 197 198 /** 199 * Determines whether two JS values are equal, recursing into objects and arrays. 200 * NaN is treated specially, such that `objectEquals(NaN, NaN)`. +/-0.0 are treated as equal 201 * by default, but can be opted to be distinguished. 202 * @param x the first JS values that get compared 203 * @param y the second JS values that get compared 204 * @param distinguishSignedZero if set to true, treat 0.0 and -0.0 as unequal. Default to false. 205 */ 206 export function objectEquals( 207 x, 208 y, 209 distinguishSignedZero = false) 210 { 211 if (typeof x !== 'object' || typeof y !== 'object') { 212 if (typeof x === 'number' && typeof y === 'number' && Number.isNaN(x) && Number.isNaN(y)) { 213 return true; 214 } 215 // Object.is(0.0, -0.0) is false while (0.0 === -0.0) is true. Other than +/-0.0 and NaN cases, 216 // Object.is works in the same way as ===. 217 return distinguishSignedZero ? Object.is(x, y) : x === y; 218 } 219 if (x === null || y === null) return x === y; 220 if (x.constructor !== y.constructor) return false; 221 if (x instanceof Function) return x === y; 222 if (x instanceof RegExp) return x === y; 223 if (x === y || x.valueOf() === y.valueOf()) return true; 224 if (Array.isArray(x) && Array.isArray(y) && x.length !== y.length) return false; 225 if (x instanceof Date) return false; 226 if (!(x instanceof Object)) return false; 227 if (!(y instanceof Object)) return false; 228 229 const x1 = x; 230 const y1 = y; 231 const p = Object.keys(x); 232 return Object.keys(y).every((i) => p.indexOf(i) !== -1) && p.every((i) => objectEquals(x1[i], y1[i])); 233 } 234 235 /** 236 * Generates a range of values `fn(0)..fn(n-1)`. 237 */ 238 export function range(n, fn) { 239 return [...new Array(n)].map((_, i) => fn(i)); 240 } 241 242 /** 243 * Generates a range of values `fn(0)..fn(n-1)`. 244 */ 245 export function* iterRange(n, fn) { 246 for (let i = 0; i < n; ++i) { 247 yield fn(i); 248 } 249 } 250 251 /** Creates a (reusable) iterable object that maps `f` over `xs`, lazily. */ 252 export function mapLazy(xs, f) { 253 return { 254 *[Symbol.iterator]() { 255 for (const x of xs) { 256 yield f(x); 257 } 258 } 259 }; 260 } 261 262 /** Count the number of elements `x` for which `predicate(x)` is true. */ 263 export function count(xs, predicate) { 264 let count = 0; 265 for (const x of xs) { 266 if (predicate(x)) count++; 267 } 268 return count; 269 } 270 271 const ReorderOrders = { 272 forward: true, 273 backward: true, 274 shiftByHalf: true 275 }; 276 277 export const kReorderOrderKeys = keysOf(ReorderOrders); 278 279 /** 280 * Creates a new array from the given array with the first half 281 * swapped with the last half. 282 */ 283 export function shiftByHalf(arr) { 284 const len = arr.length; 285 const half = len / 2 | 0; 286 const firstHalf = arr.splice(0, half); 287 return [...arr, ...firstHalf]; 288 } 289 290 /** 291 * Creates a reordered array from the input array based on the Order 292 */ 293 export function reorder(order, arr) { 294 switch (order) { 295 case 'forward': 296 return arr.slice(); 297 case 'backward': 298 return arr.slice().reverse(); 299 case 'shiftByHalf':{ 300 // should this be pseudo random? 301 return shiftByHalf(arr); 302 } 303 } 304 } 305 306 /** 307 * A typed version of Object.entries 308 */ 309 310 export function typedEntries(obj) { 311 // The cast is done once, inside the helper function, 312 // keeping the call site clean and type-safe. 313 return Object.entries(obj); 314 } 315 316 const TypedArrayBufferViewInstances = [ 317 new Uint8Array(), 318 new Uint8ClampedArray(), 319 new Uint16Array(), 320 new Uint32Array(), 321 new Int8Array(), 322 new Int16Array(), 323 new Int32Array(), 324 new Float16Array(), 325 new Float32Array(), 326 new Float64Array(), 327 new BigInt64Array(), 328 new BigUint64Array()]; 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 export const kTypedArrayBufferViews = 354 355 { 356 ...(() => { 357 358 const result = {}; 359 for (const v of TypedArrayBufferViewInstances) { 360 result[v.constructor.name] = v.constructor; 361 } 362 return result; 363 })() 364 }; 365 export const kTypedArrayBufferViewKeys = keysOf(kTypedArrayBufferViews); 366 export const kTypedArrayBufferViewConstructors = Object.values(kTypedArrayBufferViews); 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 /** 388 * Creates a case parameter for a typedarray. 389 * 390 * You can't put typedarrays in case parameters directly so instead of 391 * 392 * ``` 393 * u.combine('data', [ 394 * new Uint8Array([1, 2, 3]), 395 * new Float32Array([4, 5, 6]), 396 * ]) 397 * ``` 398 * 399 * You can use 400 * 401 * ``` 402 * u.combine('data', [ 403 * typedArrayParam('Uint8Array' [1, 2, 3]), 404 * typedArrayParam('Float32Array' [4, 5, 6]), 405 * ]) 406 * ``` 407 * 408 * and then convert the params to typedarrays eg. 409 * 410 * ``` 411 * .fn(t => { 412 * const data = t.params.data.map(v => typedArrayFromParam(v)); 413 * }) 414 * ``` 415 */ 416 export function typedArrayParam( 417 type, 418 data) 419 { 420 return { type, data }; 421 } 422 423 export function createTypedArray( 424 type, 425 data) 426 { 427 return new kTypedArrayBufferViews[type](data); 428 } 429 430 /** 431 * Converts a TypedArrayParam to a typedarray. See typedArrayParam 432 */ 433 export function typedArrayFromParam( 434 param) 435 { 436 const { type, data } = param; 437 return createTypedArray(type, data); 438 } 439 440 function subarrayAsU8( 441 buf, 442 { start = 0, length }) 443 { 444 if (buf instanceof ArrayBuffer) { 445 return new Uint8Array(buf, start, length); 446 } else if (buf instanceof Uint8Array || buf instanceof Uint8ClampedArray) { 447 // Don't wrap in new views if we don't need to. 448 if (start === 0 && (length === undefined || length === buf.byteLength)) { 449 return buf; 450 } 451 } 452 const byteOffset = buf.byteOffset + start * buf.BYTES_PER_ELEMENT; 453 const byteLength = 454 length !== undefined ? 455 length * buf.BYTES_PER_ELEMENT : 456 buf.byteLength - (byteOffset - buf.byteOffset); 457 return new Uint8Array(buf.buffer, byteOffset, byteLength); 458 } 459 460 /** 461 * Copy a range of bytes from one ArrayBuffer or TypedArray to another. 462 * 463 * `start`/`length` are in elements (or in bytes, if ArrayBuffer). 464 */ 465 export function memcpy( 466 src, 467 dst) 468 { 469 subarrayAsU8(dst.dst, dst).set(subarrayAsU8(src.src, src)); 470 } 471 472 /** 473 * Used to create a value that is specified by multiplying some runtime value 474 * by a constant and then adding a constant to it. 475 */ 476 477 478 479 480 481 /** 482 * Filters out SpecValues that are the same. 483 */ 484 export function filterUniqueValueTestVariants(valueTestVariants) { 485 return new Map( 486 valueTestVariants.map((v) => [`m:${v.mult},a:${v.add}`, v]) 487 ).values(); 488 } 489 490 /** 491 * Used to create a value that is specified by multiplied some runtime value 492 * by a constant and then adding a constant to it. This happens often in test 493 * with limits that can only be known at runtime and yet we need a way to 494 * add parameters to a test and those parameters must be constants. 495 */ 496 export function makeValueTestVariant(base, variant) { 497 return base * variant.mult + variant.add; 498 } 499 500 /** 501 * Use instead of features.has because feature's has takes any string 502 * and we want to prevent typos. 503 */ 504 export function hasFeature(features, feature) { 505 506 return features.has(feature); 507 } 508 509 /** Convenience helper for combinations of 1-2 usage bits from a list of usage bits. */ 510 export function combinationsOfOneOrTwoUsages(usages) { 511 const combinations = []; 512 for (const usage0 of usages) { 513 for (const usage1 of usages) { 514 if (usage0 <= usage1) { 515 combinations.push(usage0 | usage1); 516 } 517 } 518 } 519 return combinations; 520 }