summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/common/tools/gen_wpt_cts_html.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/common/tools/gen_wpt_cts_html.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/common/tools/gen_wpt_cts_html.ts252
1 files changed, 252 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/common/tools/gen_wpt_cts_html.ts b/dom/webgpu/tests/cts/checkout/src/common/tools/gen_wpt_cts_html.ts
new file mode 100644
index 0000000000..e8161304e9
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/common/tools/gen_wpt_cts_html.ts
@@ -0,0 +1,252 @@
+import { promises as fs } from 'fs';
+import * as path from 'path';
+
+import { DefaultTestFileLoader } from '../internal/file_loader.js';
+import {
+ TestQueryMultiCase,
+ TestQueryMultiFile,
+ TestQueryMultiTest,
+} from '../internal/query/query.js';
+import { assert } from '../util/util.js';
+
+function printUsageAndExit(rc: number): never {
+ console.error(`\
+Usage (simple, for webgpu:* suite only):
+ tools/gen_wpt_cts_html OUTPUT_FILE TEMPLATE_FILE
+ tools/gen_wpt_cts_html out-wpt/cts.https.html templates/cts.https.html
+
+Usage (config file):
+ tools/gen_wpt_cts_html CONFIG_JSON_FILE
+
+where CONFIG_JSON_FILE is a JSON file in the format documented in the code of
+gen_wpt_cts_html.ts. Example:
+ {
+ "suite": "webgpu",
+ "out": "path/to/output/cts.https.html",
+ "template": "path/to/template/cts.https.html",
+ "maxChunkTimeMS": 2000
+ }
+
+Usage (advanced) (deprecated, use config file):
+ tools/gen_wpt_cts_html OUTPUT_FILE TEMPLATE_FILE ARGUMENTS_PREFIXES_FILE EXPECTATIONS_FILE EXPECTATIONS_PREFIX [SUITE]
+ tools/gen_wpt_cts_html my/path/to/cts.https.html templates/cts.https.html arguments.txt myexpectations.txt 'path/to/cts.https.html' cts
+
+where arguments.txt is a file containing a list of arguments prefixes to both generate and expect
+in the expectations. The entire variant list generation runs *once per prefix*, so this
+multiplies the size of the variant list.
+
+ ?worker=0&q=
+ ?worker=1&q=
+
+and myexpectations.txt is a file containing a list of WPT paths to suppress, e.g.:
+
+ path/to/cts.https.html?worker=0&q=webgpu:a/foo:bar={"x":1}
+ path/to/cts.https.html?worker=1&q=webgpu:a/foo:bar={"x":1}
+
+ path/to/cts.https.html?worker=1&q=webgpu:a/foo:bar={"x":3}
+`);
+ process.exit(rc);
+}
+
+interface ConfigJSON {
+ /** Test suite to generate from. */
+ suite: string;
+ /** Output filename, relative to JSON file. */
+ out: string;
+ /** Input template filename, relative to JSON file. */
+ template: string;
+ /**
+ * Maximum time for a single WPT "variant" chunk, in milliseconds. Defaults to infinity.
+ *
+ * This data is typically captured by developers on higher-end computers, so typical test
+ * machines might execute more slowly. For this reason, use a time much less than 5 seconds
+ * (a typical default time limit in WPT test executors).
+ */
+ maxChunkTimeMS?: number;
+ /** List of argument prefixes (what comes before the test query). Defaults to `['?q=']`. */
+ argumentsPrefixes?: string[];
+ expectations?: {
+ /** File containing a list of WPT paths to suppress. */
+ file: string;
+ /** The prefix to trim from every line of the expectations_file. */
+ prefix: string;
+ };
+}
+
+interface Config {
+ suite: string;
+ out: string;
+ template: string;
+ maxChunkTimeMS: number;
+ argumentsPrefixes: string[];
+ expectations?: {
+ file: string;
+ prefix: string;
+ };
+}
+
+let config: Config;
+
+(async () => {
+ // Load the config
+ switch (process.argv.length) {
+ case 3: {
+ const configFile = process.argv[2];
+ const configJSON: ConfigJSON = JSON.parse(await fs.readFile(configFile, 'utf8'));
+ const jsonFileDir = path.dirname(configFile);
+
+ config = {
+ suite: configJSON.suite,
+ out: path.resolve(jsonFileDir, configJSON.out),
+ template: path.resolve(jsonFileDir, configJSON.template),
+ maxChunkTimeMS: configJSON.maxChunkTimeMS ?? Infinity,
+ argumentsPrefixes: configJSON.argumentsPrefixes ?? ['?q='],
+ };
+ if (configJSON.expectations) {
+ config.expectations = {
+ file: path.resolve(jsonFileDir, configJSON.expectations.file),
+ prefix: configJSON.expectations.prefix,
+ };
+ }
+ break;
+ }
+ case 4:
+ case 7:
+ case 8: {
+ const [
+ _nodeBinary,
+ _thisScript,
+ outFile,
+ templateFile,
+ argsPrefixesFile,
+ expectationsFile,
+ expectationsPrefix,
+ suite = 'webgpu',
+ ] = process.argv;
+
+ config = {
+ out: outFile,
+ template: templateFile,
+ suite,
+ maxChunkTimeMS: Infinity,
+ argumentsPrefixes: ['?q='],
+ };
+ if (process.argv.length >= 7) {
+ config.argumentsPrefixes = (await fs.readFile(argsPrefixesFile, 'utf8'))
+ .split(/\r?\n/)
+ .filter(a => a.length);
+ config.expectations = {
+ file: expectationsFile,
+ prefix: expectationsPrefix,
+ };
+ }
+ break;
+ }
+ default:
+ console.error('incorrect number of arguments!');
+ printUsageAndExit(1);
+ }
+
+ const useChunking = Number.isFinite(config.maxChunkTimeMS);
+
+ // Sort prefixes from longest to shortest
+ config.argumentsPrefixes.sort((a, b) => b.length - a.length);
+
+ // Load expectations (if any)
+ let expectationLines = new Set<string>();
+ if (config.expectations) {
+ expectationLines = new Set(
+ (await fs.readFile(config.expectations.file, 'utf8')).split(/\r?\n/).filter(l => l.length)
+ );
+ }
+
+ const expectations: Map<string, string[]> = new Map();
+ for (const prefix of config.argumentsPrefixes) {
+ expectations.set(prefix, []);
+ }
+
+ expLoop: for (const exp of expectationLines) {
+ // Take each expectation for the longest prefix it matches.
+ for (const argsPrefix of config.argumentsPrefixes) {
+ const prefix = config.expectations!.prefix + argsPrefix;
+ if (exp.startsWith(prefix)) {
+ expectations.get(argsPrefix)!.push(exp.substring(prefix.length));
+ continue expLoop;
+ }
+ }
+ console.log('note: ignored expectation: ' + exp);
+ }
+
+ const loader = new DefaultTestFileLoader();
+ const lines = [];
+ for (const prefix of config.argumentsPrefixes) {
+ const rootQuery = new TestQueryMultiFile(config.suite, []);
+ const tree = await loader.loadTree(rootQuery, {
+ subqueriesToExpand: expectations.get(prefix),
+ maxChunkTime: config.maxChunkTimeMS,
+ });
+
+ lines.push(undefined); // output blank line between prefixes
+ const prefixComment = { comment: `Prefix: "${prefix}"` }; // contents will be updated later
+ if (useChunking) lines.push(prefixComment);
+
+ const filesSeen = new Set<string>();
+ const testsSeen = new Set<string>();
+ let variantCount = 0;
+
+ const alwaysExpandThroughLevel = 2; // expand to, at minimum, every test.
+ for (const { query, subtreeCounts } of tree.iterateCollapsedNodes({
+ alwaysExpandThroughLevel,
+ })) {
+ assert(query instanceof TestQueryMultiCase);
+ const queryString = query.toString();
+ // Check for a safe-ish path length limit. Filename must be <= 255, and on Windows the whole
+ // path must be <= 259. Leave room for e.g.:
+ // 'c:\b\s\w\xxxxxxxx\layout-test-results\external\wpt\webgpu\cts_worker=0_q=...-actual.txt'
+ assert(
+ queryString.length < 185,
+ `Generated test variant would produce too-long -actual.txt filename. Possible solutions:
+- Reduce the length of the parts of the test query
+- Reduce the parameterization of the test
+- Make the test function faster and regenerate the listing_meta entry
+- Reduce the specificity of test expectations (if you're using them)
+${queryString}`
+ );
+
+ lines.push({
+ urlQueryString: prefix + query.toString(), // "?worker=0&q=..."
+ comment: useChunking ? `estimated: ${subtreeCounts?.totalTimeMS.toFixed(3)} ms` : undefined,
+ });
+
+ variantCount++;
+ filesSeen.add(new TestQueryMultiTest(query.suite, query.filePathParts, []).toString());
+ testsSeen.add(
+ new TestQueryMultiCase(query.suite, query.filePathParts, query.testPathParts, {}).toString()
+ );
+ }
+ prefixComment.comment += `; ${variantCount} variants generated from ${testsSeen.size} tests in ${filesSeen.size} files`;
+ }
+ await generateFile(lines);
+})().catch(ex => {
+ console.log(ex.stack ?? ex.toString());
+ process.exit(1);
+});
+
+async function generateFile(
+ lines: Array<{ urlQueryString?: string; comment?: string } | undefined>
+): Promise<void> {
+ let result = '';
+ result += '<!-- AUTO-GENERATED - DO NOT EDIT. See WebGPU CTS: tools/gen_wpt_cts_html. -->\n';
+
+ result += await fs.readFile(config.template, 'utf8');
+
+ for (const line of lines) {
+ if (line !== undefined) {
+ if (line.urlQueryString) result += `<meta name=variant content='${line.urlQueryString}'>`;
+ if (line.comment) result += `<!-- ${line.comment} -->`;
+ }
+ result += '\n';
+ }
+
+ await fs.writeFile(config.out, result);
+}