diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /dom/webgpu/tests/cts/checkout/src/common/runtime/helper | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/common/runtime/helper')
4 files changed, 272 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/common/runtime/helper/options.ts b/dom/webgpu/tests/cts/checkout/src/common/runtime/helper/options.ts new file mode 100644 index 0000000000..38974b803f --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/common/runtime/helper/options.ts @@ -0,0 +1,129 @@ +let windowURL: URL | undefined = undefined; +function getWindowURL() { + if (windowURL === undefined) { + windowURL = new URL(window.location.toString()); + } + return windowURL; +} + +export function optionEnabled( + opt: string, + searchParams: URLSearchParams = getWindowURL().searchParams +): boolean { + const val = searchParams.get(opt); + return val !== null && val !== '0'; +} + +export function optionString( + opt: string, + searchParams: URLSearchParams = getWindowURL().searchParams +): string { + return searchParams.get(opt) || ''; +} + +/** + * The possible options for the tests. + */ +export interface CTSOptions { + worker: boolean; + debug: boolean; + compatibility: boolean; + unrollConstEvalLoops: boolean; + powerPreference?: GPUPowerPreference | ''; +} + +export const kDefaultCTSOptions: CTSOptions = { + worker: false, + debug: true, + compatibility: false, + unrollConstEvalLoops: false, + powerPreference: '', +}; + +/** + * Extra per option info. + */ +export interface OptionInfo { + description: string; + parser?: (key: string, searchParams?: URLSearchParams) => boolean | string; + selectValueDescriptions?: { value: string; description: string }[]; +} + +/** + * Type for info for every option. This definition means adding an option + * will generate a compile time error if no extra info is provided. + */ +export type OptionsInfos<Type> = Record<keyof Type, OptionInfo>; + +/** + * Options to the CTS. + */ +export const kCTSOptionsInfo: OptionsInfos<CTSOptions> = { + worker: { description: 'run in a worker' }, + debug: { description: 'show more info' }, + compatibility: { description: 'run in compatibility mode' }, + unrollConstEvalLoops: { description: 'unroll const eval loops in WGSL' }, + powerPreference: { + description: 'set default powerPreference for some tests', + parser: optionString, + selectValueDescriptions: [ + { value: '', description: 'default' }, + { value: 'low-power', description: 'low-power' }, + { value: 'high-performance', description: 'high-performance' }, + ], + }, +}; + +/** + * Converts camel case to snake case. + * Examples: + * fooBar -> foo_bar + * parseHTMLFile -> parse_html_file + */ +export function camelCaseToSnakeCase(id: string) { + return id + .replace(/(.)([A-Z][a-z]+)/g, '$1_$2') + .replace(/([a-z0-9])([A-Z])/g, '$1_$2') + .toLowerCase(); +} + +/** + * Creates a Options from search parameters. + */ +function getOptionsInfoFromSearchString<Type extends CTSOptions>( + optionsInfos: OptionsInfos<Type>, + searchString: string +): Type { + const searchParams = new URLSearchParams(searchString); + const optionValues: Record<string, boolean | string> = {}; + for (const [optionName, info] of Object.entries(optionsInfos)) { + const parser = info.parser || optionEnabled; + optionValues[optionName] = parser(camelCaseToSnakeCase(optionName), searchParams); + } + return optionValues as unknown as Type; +} + +/** + * Given a test query string in the form of `suite:foo,bar,moo&opt1=val1&opt2=val2 + * returns the query and the options. + */ +export function parseSearchParamLikeWithOptions<Type extends CTSOptions>( + optionsInfos: OptionsInfos<Type>, + query: string +): { + queries: string[]; + options: Type; +} { + const searchString = query.includes('q=') || query.startsWith('?') ? query : `q=${query}`; + const queries = new URLSearchParams(searchString).getAll('q'); + const options = getOptionsInfoFromSearchString(optionsInfos, searchString); + return { queries, options }; +} + +/** + * Given a test query string in the form of `suite:foo,bar,moo&opt1=val1&opt2=val2 + * returns the query and the common options. + */ +export function parseSearchParamLikeWithCTSOptions(query: string) { + return parseSearchParamLikeWithOptions(kCTSOptionsInfo, query); +} diff --git a/dom/webgpu/tests/cts/checkout/src/common/runtime/helper/sys.ts b/dom/webgpu/tests/cts/checkout/src/common/runtime/helper/sys.ts new file mode 100644 index 0000000000..d2e07ff26d --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/common/runtime/helper/sys.ts @@ -0,0 +1,46 @@ +/* eslint no-process-exit: "off" */ +/* eslint @typescript-eslint/no-namespace: "off" */ + +function node() { + const { existsSync } = require('fs'); + + return { + type: 'node', + existsSync, + args: process.argv.slice(2), + cwd: () => process.cwd(), + exit: (code?: number | undefined) => process.exit(code), + }; +} + +declare global { + namespace Deno { + function readFileSync(path: string): Uint8Array; + const args: string[]; + const cwd: () => string; + function exit(code?: number): never; + } +} + +function deno() { + function existsSync(path: string) { + 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/dom/webgpu/tests/cts/checkout/src/common/runtime/helper/test_worker-worker.ts b/dom/webgpu/tests/cts/checkout/src/common/runtime/helper/test_worker-worker.ts new file mode 100644 index 0000000000..e8d187ea7e --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/common/runtime/helper/test_worker-worker.ts @@ -0,0 +1,48 @@ +import { setBaseResourcePath } from '../../framework/resources.js'; +import { globalTestConfig } from '../../framework/test_config.js'; +import { DefaultTestFileLoader } from '../../internal/file_loader.js'; +import { Logger } from '../../internal/logging/logger.js'; +import { parseQuery } from '../../internal/query/parseQuery.js'; +import { TestQueryWithExpectation } from '../../internal/query/query.js'; +import { setDefaultRequestAdapterOptions } from '../../util/navigator_gpu.js'; +import { assert } from '../../util/util.js'; + +import { CTSOptions } from './options.js'; + +// Should be DedicatedWorkerGlobalScope, but importing lib "webworker" conflicts with lib "dom". +/* eslint-disable-next-line @typescript-eslint/no-explicit-any */ +declare const self: any; + +const loader = new DefaultTestFileLoader(); + +setBaseResourcePath('../../../resources'); + +self.onmessage = async (ev: MessageEvent) => { + const query: string = ev.data.query; + const expectations: TestQueryWithExpectation[] = ev.data.expectations; + const ctsOptions: CTSOptions = ev.data.ctsOptions; + + const { debug, unrollConstEvalLoops, powerPreference, compatibility } = ctsOptions; + globalTestConfig.unrollConstEvalLoops = unrollConstEvalLoops; + globalTestConfig.compatibility = compatibility; + + Logger.globalDebugMode = debug; + const log = new Logger(); + + if (powerPreference || compatibility) { + setDefaultRequestAdapterOptions({ + ...(powerPreference && { powerPreference }), + // MAINTENANCE_TODO: Change this to whatever the option ends up being + ...(compatibility && { compatibilityMode: true }), + }); + } + + 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/dom/webgpu/tests/cts/checkout/src/common/runtime/helper/test_worker.ts b/dom/webgpu/tests/cts/checkout/src/common/runtime/helper/test_worker.ts new file mode 100644 index 0000000000..9bbcab0946 --- /dev/null +++ b/dom/webgpu/tests/cts/checkout/src/common/runtime/helper/test_worker.ts @@ -0,0 +1,49 @@ +import { LogMessageWithStack } from '../../internal/logging/log_message.js'; +import { TransferredTestCaseResult, LiveTestCaseResult } from '../../internal/logging/result.js'; +import { TestCaseRecorder } from '../../internal/logging/test_case_recorder.js'; +import { TestQueryWithExpectation } from '../../internal/query/query.js'; + +import { CTSOptions, kDefaultCTSOptions } from './options.js'; + +export class TestWorker { + private readonly ctsOptions: CTSOptions; + private readonly worker: Worker; + private readonly resolvers = new Map<string, (result: LiveTestCaseResult) => void>(); + + constructor(ctsOptions?: CTSOptions) { + this.ctsOptions = { ...(ctsOptions || kDefaultCTSOptions), ...{ worker: true } }; + 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: string = ev.data.query; + const result: TransferredTestCaseResult = ev.data.result; + if (result.logs) { + for (const l of result.logs) { + Object.setPrototypeOf(l, LogMessageWithStack.prototype); + } + } + this.resolvers.get(query)!(result as LiveTestCaseResult); + + // 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: TestCaseRecorder, + query: string, + expectations: TestQueryWithExpectation[] = [] + ): Promise<void> { + this.worker.postMessage({ + query, + expectations, + ctsOptions: this.ctsOptions, + }); + const workerResult = await new Promise<LiveTestCaseResult>(resolve => { + this.resolvers.set(query, resolve); + }); + rec.injectResult(workerResult); + } +} |