as-test 1.1.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -3
- package/README.md +14 -7
- package/as-test.config.schema.json +142 -142
- package/assembly/__fuzz__/math.fuzz.ts +17 -14
- package/assembly/__fuzz__/seed-perf.fuzz.ts +4 -2
- package/assembly/index.ts +1 -4
- package/assembly/src/expectation.ts +7 -1
- package/assembly/src/fuzz.ts +44 -23
- package/assembly/util/format.ts +10 -1
- package/assembly/util/wipc.ts +1 -2
- package/bin/build-worker-pool.js +7 -1
- package/bin/commands/build-core.js +6 -1
- package/bin/commands/build.js +1 -1
- package/bin/commands/clean-core.js +3 -1
- package/bin/commands/clean.js +0 -37
- package/bin/commands/fuzz-core.js +2 -2
- package/bin/commands/run-core.js +35 -24
- package/bin/commands/web-runner-source.js +14 -14
- package/bin/commands/web-session.js +6 -1
- package/bin/crash-store.js +3 -1
- package/bin/index.js +301 -123
- package/bin/reporters/default.js +175 -33
- package/bin/util.js +36 -11
- package/lib/build/index.js +74 -24
- package/lib/src/index.ts +96 -35
- package/package.json +1 -1
- package/transform/lib/coverage.js +3 -1
package/assembly/util/format.ts
CHANGED
|
@@ -35,7 +35,10 @@ export function formatValue<T>(value: T, deep: boolean = false): string {
|
|
|
35
35
|
let out = "Map(" + keys.length.toString() + ") { ";
|
|
36
36
|
for (let i = 0; i < keys.length; i++) {
|
|
37
37
|
if (i) out += ", ";
|
|
38
|
-
out += formatValue(
|
|
38
|
+
out += formatValue(
|
|
39
|
+
changetype<valueof<typeof keys>>(unchecked(keys[i])),
|
|
40
|
+
true,
|
|
41
|
+
);
|
|
39
42
|
out += " => ";
|
|
40
43
|
out += formatValue(
|
|
41
44
|
changetype<valueof<typeof values>>(unchecked(values[i])),
|
|
@@ -73,31 +76,37 @@ export function formatValue<T>(value: T, deep: boolean = false): string {
|
|
|
73
76
|
return nameof<T>();
|
|
74
77
|
}
|
|
75
78
|
|
|
79
|
+
|
|
76
80
|
@inline
|
|
77
81
|
export function colorText(format: i32[], text: string): string {
|
|
78
82
|
return `\u001b[${format[0].toString()}m${text}\u001b[${format[1].toString()}m`;
|
|
79
83
|
}
|
|
80
84
|
|
|
85
|
+
|
|
81
86
|
@inline
|
|
82
87
|
export function red(text: string): string {
|
|
83
88
|
return colorText([31, 39], text);
|
|
84
89
|
}
|
|
85
90
|
|
|
91
|
+
|
|
86
92
|
@inline
|
|
87
93
|
export function green(text: string): string {
|
|
88
94
|
return colorText([32, 39], text);
|
|
89
95
|
}
|
|
90
96
|
|
|
97
|
+
|
|
91
98
|
@inline
|
|
92
99
|
export function bgRed(text: string): string {
|
|
93
100
|
return colorText([41, 49], text);
|
|
94
101
|
}
|
|
95
102
|
|
|
103
|
+
|
|
96
104
|
@inline
|
|
97
105
|
export function bgGreen(text: string): string {
|
|
98
106
|
return colorText([42, 49], text);
|
|
99
107
|
}
|
|
100
108
|
|
|
109
|
+
|
|
101
110
|
@inline
|
|
102
111
|
export function bold(text: string): string {
|
|
103
112
|
return colorText([1, 22], text);
|
package/assembly/util/wipc.ts
CHANGED
|
@@ -154,8 +154,7 @@ export function requestFuzzConfig(): FuzzConfigReply {
|
|
|
154
154
|
const runs = body.slice(0, first);
|
|
155
155
|
const seed =
|
|
156
156
|
second >= 0 ? body.slice(first + 1, second) : body.slice(first + 1);
|
|
157
|
-
const kind =
|
|
158
|
-
second >= 0 && third >= 0 ? body.slice(second + 1, third) : "";
|
|
157
|
+
const kind = second >= 0 && third >= 0 ? body.slice(second + 1, third) : "";
|
|
159
158
|
const value = third >= 0 ? body.slice(third + 1) : "";
|
|
160
159
|
if (runs.length) reply.runs = I32.parseInt(runs);
|
|
161
160
|
if (seed.length) reply.seed = U64.parseInt(seed);
|
package/bin/build-worker-pool.js
CHANGED
|
@@ -21,6 +21,7 @@ export class BuildWorkerPool {
|
|
|
21
21
|
configPath: args.configPath,
|
|
22
22
|
file: args.file,
|
|
23
23
|
modeName: args.modeName,
|
|
24
|
+
buildCommand: args.buildCommand,
|
|
24
25
|
featureToggles,
|
|
25
26
|
overrides,
|
|
26
27
|
resolve,
|
|
@@ -80,7 +81,12 @@ export class BuildWorkerPool {
|
|
|
80
81
|
worker.busy = false;
|
|
81
82
|
worker.task = null;
|
|
82
83
|
if (failedTask) {
|
|
83
|
-
failedTask.
|
|
84
|
+
const modeLabel = failedTask.modeName ?? "default";
|
|
85
|
+
const fileLabel = failedTask.file;
|
|
86
|
+
const commandText = failedTask.buildCommand?.trim().length
|
|
87
|
+
? `\nBuild command: ${failedTask.buildCommand}`
|
|
88
|
+
: "";
|
|
89
|
+
failedTask.reject(new Error(`build worker exited unexpectedly while building ${fileLabel} in mode ${modeLabel}${commandText}`));
|
|
84
90
|
}
|
|
85
91
|
if (!pool || this.closed)
|
|
86
92
|
return;
|
|
@@ -8,7 +8,7 @@ import { applyMode, getPkgRunner, loadConfig, tokenizeCommand, resolveProjectMod
|
|
|
8
8
|
import { persistCrashRecord } from "../crash-store.js";
|
|
9
9
|
import { BuildWorkerPool } from "../build-worker-pool.js";
|
|
10
10
|
const DEFAULT_CONFIG_PATH = path.join(process.cwd(), "./as-test.config.json");
|
|
11
|
-
class BuildFailureError extends Error {
|
|
11
|
+
export class BuildFailureError extends Error {
|
|
12
12
|
constructor(args) {
|
|
13
13
|
super(args.message);
|
|
14
14
|
this.name = "BuildFailureError";
|
|
@@ -18,6 +18,7 @@ class BuildFailureError extends Error {
|
|
|
18
18
|
this.stdout = args.stdout;
|
|
19
19
|
this.stderr = args.stderr;
|
|
20
20
|
this.kind = args.kind;
|
|
21
|
+
this.crashLogPath = args.crashLogPath;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
export async function build(configPath = DEFAULT_CONFIG_PATH, selectors = [], modeName, featureToggles = {}, overrides = {}, resolvedConfig) {
|
|
@@ -49,10 +50,13 @@ export async function build(configPath = DEFAULT_CONFIG_PATH, selectors = [], mo
|
|
|
49
50
|
!hasCustomBuildCommand(config)) {
|
|
50
51
|
const pool = getSerialBuildWorkerPool();
|
|
51
52
|
for (const file of inputFiles) {
|
|
53
|
+
const outFile = `${config.outDir}/${resolveArtifactFileName(file, config.buildOptions.target, modeName, duplicateSpecBasenames)}`;
|
|
54
|
+
const invocation = getBuildCommand(config, pkgRunner, file, outFile, modeName, featureToggles);
|
|
52
55
|
await pool.buildFileMode({
|
|
53
56
|
configPath,
|
|
54
57
|
file,
|
|
55
58
|
modeName,
|
|
59
|
+
buildCommand: formatInvocation(invocation),
|
|
56
60
|
featureToggles,
|
|
57
61
|
overrides,
|
|
58
62
|
});
|
|
@@ -90,6 +94,7 @@ export async function build(configPath = DEFAULT_CONFIG_PATH, selectors = [], mo
|
|
|
90
94
|
stdout,
|
|
91
95
|
stderr,
|
|
92
96
|
kind,
|
|
97
|
+
crashLogPath: crash.logPath,
|
|
93
98
|
message: `Failed to build ${path.basename(file)} in mode ${modeLabel} with ${stderr || stdout || "unknown build error"}\n` +
|
|
94
99
|
`Build command: ${buildCommand}\n` +
|
|
95
100
|
`Crash log: ${crash.logPath}`,
|
package/bin/commands/build.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { closeSerialBuildWorkerPool, } from "./build-core.js";
|
|
2
2
|
export { build } from "./build-core.js";
|
|
3
|
-
export { formatInvocation, getBuildInvocationPreview, getBuildReuseInfo, } from "./build-core.js";
|
|
3
|
+
export { BuildFailureError, formatInvocation, getBuildInvocationPreview, getBuildReuseInfo, } from "./build-core.js";
|
|
4
4
|
export async function executeBuildCommand(rawArgs, configPath, selectedModes, deps) {
|
|
5
5
|
const commandArgs = deps.resolveCommandArgs(rawArgs, "build");
|
|
6
6
|
const listFlags = deps.resolveListFlags(rawArgs, "build");
|
|
@@ -112,7 +112,9 @@ function pruneNestedTargets(targets) {
|
|
|
112
112
|
if (targetPath == otherPath)
|
|
113
113
|
continue;
|
|
114
114
|
const relative = path.relative(targetPath, otherPath);
|
|
115
|
-
if (!relative.length ||
|
|
115
|
+
if (!relative.length ||
|
|
116
|
+
relative == ".." ||
|
|
117
|
+
relative.startsWith(`..${path.sep}`)) {
|
|
116
118
|
continue;
|
|
117
119
|
}
|
|
118
120
|
targets.delete(otherPath);
|
package/bin/commands/clean.js
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import { createInterface } from "readline";
|
|
3
1
|
import { clean } from "./clean-core.js";
|
|
4
2
|
import { loadConfig } from "../util.js";
|
|
5
3
|
export { clean } from "./clean-core.js";
|
|
6
4
|
export async function executeCleanCommand(rawArgs, configPath, selectedModes, resolveExecutionModes) {
|
|
7
|
-
const force = rawArgs.includes("-f") || rawArgs.includes("--force");
|
|
8
5
|
const modeTargets = selectedModes.length > 0
|
|
9
6
|
? resolveExecutionModes(configPath, selectedModes)
|
|
10
7
|
: resolveAllCleanModes(configPath);
|
|
11
|
-
if (!force && selectedModes.length == 0) {
|
|
12
|
-
await confirmFullClean(configPath);
|
|
13
|
-
}
|
|
14
8
|
await clean(configPath, modeTargets, selectedModes.length == 0);
|
|
15
9
|
}
|
|
16
10
|
function resolveAllCleanModes(configPath) {
|
|
@@ -18,34 +12,3 @@ function resolveAllCleanModes(configPath) {
|
|
|
18
12
|
const config = loadConfig(resolvedConfigPath, true);
|
|
19
13
|
return [undefined, ...Object.keys(config.modes)];
|
|
20
14
|
}
|
|
21
|
-
async function confirmFullClean(configPath) {
|
|
22
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
23
|
-
throw new Error('clean without --mode requires confirmation. Re-run with "-f" or "--force" to skip the prompt.');
|
|
24
|
-
}
|
|
25
|
-
const target = configPath ? ` in ${configPath}` : "";
|
|
26
|
-
process.stdout.write(chalk.bold.blue("◇ Confirm Clean") +
|
|
27
|
-
"\n" +
|
|
28
|
-
`│ This will remove configured build outputs, crash reports, and logs for every mode${target}.\n` +
|
|
29
|
-
"│\n");
|
|
30
|
-
const answer = await promptLine("Continue? [Y/n] ");
|
|
31
|
-
const normalized = answer.trim().toLowerCase();
|
|
32
|
-
if (normalized == "" || normalized == "y" || normalized == "yes")
|
|
33
|
-
return;
|
|
34
|
-
if (normalized == "n" || normalized == "no") {
|
|
35
|
-
process.stdout.write(chalk.dim("clean cancelled\n"));
|
|
36
|
-
process.exit(0);
|
|
37
|
-
}
|
|
38
|
-
throw new Error(`invalid answer "${answer}". Expected yes or no.`);
|
|
39
|
-
}
|
|
40
|
-
function promptLine(question) {
|
|
41
|
-
return new Promise((resolve) => {
|
|
42
|
-
const rl = createInterface({
|
|
43
|
-
input: process.stdin,
|
|
44
|
-
output: process.stdout,
|
|
45
|
-
});
|
|
46
|
-
rl.question(question, (answer) => {
|
|
47
|
-
rl.close();
|
|
48
|
-
resolve(answer);
|
|
49
|
-
});
|
|
50
|
-
});
|
|
51
|
-
}
|
|
@@ -292,10 +292,10 @@ function buildFuzzCrashEntryKey(file, modeName) {
|
|
|
292
292
|
return `${path.basename(file).replace(/\.ts$/, "")}.${sanitizeEntryName(modeName)}`;
|
|
293
293
|
}
|
|
294
294
|
function sanitizeEntryName(name) {
|
|
295
|
-
return name
|
|
295
|
+
return (name
|
|
296
296
|
.toLowerCase()
|
|
297
297
|
.replace(/[^a-z0-9]+/g, "-")
|
|
298
|
-
.replace(/^-+|-+$/g, "") || "fuzzer";
|
|
298
|
+
.replace(/^-+|-+$/g, "") || "fuzzer");
|
|
299
299
|
}
|
|
300
300
|
function captureFrames(onFrame) {
|
|
301
301
|
const originalWrite = process.stdout.write.bind(process.stdout);
|
package/bin/commands/run-core.js
CHANGED
|
@@ -2,7 +2,7 @@ import chalk from "chalk";
|
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
3
|
import { glob } from "glob";
|
|
4
4
|
import { Channel, MessageType } from "../wipc.js";
|
|
5
|
-
import { applyMode, formatTime, getExec, loadConfig, tokenizeCommand, } from "../util.js";
|
|
5
|
+
import { applyMode, formatSpecDisplayPath, formatTime, getExec, loadConfig, tokenizeCommand, } from "../util.js";
|
|
6
6
|
import * as path from "path";
|
|
7
7
|
import { pathToFileURL } from "url";
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
@@ -390,7 +390,7 @@ function resolveSuiteSelectionMatches(suites, selectors, file) {
|
|
|
390
390
|
if (normalized.includes("/")) {
|
|
391
391
|
const resolved = resolveExplicitSuitePath(suites, normalized);
|
|
392
392
|
if (!resolved) {
|
|
393
|
-
throw new Error(`No suites matched "${selector}" in ${
|
|
393
|
+
throw new Error(`No suites matched "${selector}" in ${formatSpecDisplayPath(file)}.`);
|
|
394
394
|
}
|
|
395
395
|
matches.push({
|
|
396
396
|
kind: "path",
|
|
@@ -402,7 +402,7 @@ function resolveSuiteSelectionMatches(suites, selectors, file) {
|
|
|
402
402
|
}
|
|
403
403
|
const resolved = resolveBareSuiteSelector(suites, normalized);
|
|
404
404
|
if (!resolved) {
|
|
405
|
-
throw new Error(`No suites matched "${selector}" in ${
|
|
405
|
+
throw new Error(`No suites matched "${selector}" in ${formatSpecDisplayPath(file)}.`);
|
|
406
406
|
}
|
|
407
407
|
matches.push({
|
|
408
408
|
kind: "bare",
|
|
@@ -437,7 +437,9 @@ function resolveBareSuiteSelector(suites, selector) {
|
|
|
437
437
|
return null;
|
|
438
438
|
const matches = [];
|
|
439
439
|
walkSuites(suites, (suite, depth) => {
|
|
440
|
-
const leaf = String(suite.path ?? "")
|
|
440
|
+
const leaf = String(suite.path ?? "")
|
|
441
|
+
.split("/")
|
|
442
|
+
.pop() ?? "";
|
|
441
443
|
if (leaf == slug) {
|
|
442
444
|
matches.push({ path: String(suite.path), depth });
|
|
443
445
|
}
|
|
@@ -457,7 +459,9 @@ function walkSuites(suites, visitor, depth = 0) {
|
|
|
457
459
|
for (const suite of suites) {
|
|
458
460
|
if (visitor(suite, depth))
|
|
459
461
|
return true;
|
|
460
|
-
const childSuites = Array.isArray(suite?.suites)
|
|
462
|
+
const childSuites = Array.isArray(suite?.suites)
|
|
463
|
+
? suite.suites
|
|
464
|
+
: [];
|
|
461
465
|
if (walkSuites(childSuites, visitor, depth + 1))
|
|
462
466
|
return true;
|
|
463
467
|
}
|
|
@@ -466,7 +470,9 @@ function walkSuites(suites, visitor, depth = 0) {
|
|
|
466
470
|
function cloneSelectedSuites(suites, selected, file, modeName) {
|
|
467
471
|
const out = [];
|
|
468
472
|
for (const suite of suites) {
|
|
469
|
-
const childSuites = Array.isArray(suite.suites)
|
|
473
|
+
const childSuites = Array.isArray(suite.suites)
|
|
474
|
+
? suite.suites
|
|
475
|
+
: [];
|
|
470
476
|
const selectedChildren = cloneSelectedSuites(childSuites, selected, file, modeName);
|
|
471
477
|
const keep = selected.has(String(suite.path ?? "")) || selectedChildren.length > 0;
|
|
472
478
|
if (!keep)
|
|
@@ -506,8 +512,8 @@ function collectReadableFailures(suites, file, pathParts) {
|
|
|
506
512
|
out.push({
|
|
507
513
|
title: `${nextPath.join(" > ")}#${i + 1}`,
|
|
508
514
|
where: String(test.location ?? "").length
|
|
509
|
-
? `${
|
|
510
|
-
:
|
|
515
|
+
? `${formatSpecDisplayPath(file)}:${String(test.location ?? "")}`
|
|
516
|
+
: formatSpecDisplayPath(file),
|
|
511
517
|
message: String(test.message ?? ""),
|
|
512
518
|
left: JSON.stringify(test.left ?? ""),
|
|
513
519
|
right: JSON.stringify(test.right ?? ""),
|
|
@@ -648,7 +654,7 @@ export async function run(flags = {}, configPath = DEFAULT_CONFIG_PATH, selector
|
|
|
648
654
|
catch (error) {
|
|
649
655
|
const modeLabel = options.modeName ?? "default";
|
|
650
656
|
const details = error instanceof Error ? error.message : String(error);
|
|
651
|
-
throw new Error(`Failed to run ${
|
|
657
|
+
throw new Error(`Failed to run ${formatSpecDisplayPath(file)} in mode ${modeLabel} with ${details}`);
|
|
652
658
|
}
|
|
653
659
|
const normalized = normalizeReport(report);
|
|
654
660
|
const selectedSuites = options.suiteSelectors?.length
|
|
@@ -665,6 +671,7 @@ export async function run(flags = {}, configPath = DEFAULT_CONFIG_PATH, selector
|
|
|
665
671
|
suites: selectedSuites,
|
|
666
672
|
coverage: normalized.coverage,
|
|
667
673
|
runCommand: runCommandForLog,
|
|
674
|
+
buildCommand: options.buildCommandsByFile?.[file] ?? options.buildCommand ?? "",
|
|
668
675
|
snapshotSummary: {
|
|
669
676
|
matched: snapshotStore.matched,
|
|
670
677
|
created: snapshotStore.created,
|
|
@@ -1492,7 +1499,7 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
|
|
|
1492
1499
|
const runtimeEvents = {
|
|
1493
1500
|
sawFileStart: false,
|
|
1494
1501
|
sawFileEnd: false,
|
|
1495
|
-
fileName:
|
|
1502
|
+
fileName: formatSpecDisplayPath(specFile),
|
|
1496
1503
|
fileVerdict: "none",
|
|
1497
1504
|
fileTime: "",
|
|
1498
1505
|
suiteStarts: 0,
|
|
@@ -1667,7 +1674,8 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
|
|
|
1667
1674
|
const code = await new Promise((resolve) => {
|
|
1668
1675
|
child.on("close", (exitCode) => resolve(exitCode ?? 1));
|
|
1669
1676
|
});
|
|
1670
|
-
if (stderrPendingLine.length &&
|
|
1677
|
+
if (stderrPendingLine.length &&
|
|
1678
|
+
!shouldSuppressWasiWarningLine(stderrPendingLine)) {
|
|
1671
1679
|
stderrBuffer += stderrPendingLine;
|
|
1672
1680
|
}
|
|
1673
1681
|
const processSpawnError = spawnError;
|
|
@@ -1686,8 +1694,7 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
|
|
|
1686
1694
|
if (reportStream.sawChunkStart) {
|
|
1687
1695
|
if (!reportStream.sawChunkEnd) {
|
|
1688
1696
|
parseError =
|
|
1689
|
-
parseError ??
|
|
1690
|
-
"missing report:end marker for chunked report payload";
|
|
1697
|
+
parseError ?? "missing report:end marker for chunked report payload";
|
|
1691
1698
|
}
|
|
1692
1699
|
else {
|
|
1693
1700
|
const chunkedPayload = reportStream.chunks.join("");
|
|
@@ -1806,7 +1813,7 @@ async function runWebSessionProcess(session, specFile, crashDir, modeName, snaps
|
|
|
1806
1813
|
const runtimeEvents = {
|
|
1807
1814
|
sawFileStart: false,
|
|
1808
1815
|
sawFileEnd: false,
|
|
1809
|
-
fileName:
|
|
1816
|
+
fileName: formatSpecDisplayPath(specFile),
|
|
1810
1817
|
fileVerdict: "none",
|
|
1811
1818
|
fileTime: "",
|
|
1812
1819
|
suiteStarts: 0,
|
|
@@ -1968,7 +1975,7 @@ async function runWebSessionProcess(session, specFile, crashDir, modeName, snaps
|
|
|
1968
1975
|
});
|
|
1969
1976
|
let code = 0;
|
|
1970
1977
|
try {
|
|
1971
|
-
await session.runJob(Object.fromEntries(Object.entries(env).filter((entry) => typeof entry[1] == "string")),
|
|
1978
|
+
await session.runJob(Object.fromEntries(Object.entries(env).filter((entry) => typeof entry[1] == "string")), formatSpecDisplayPath(specFile), (frame) => {
|
|
1972
1979
|
input.write(frame);
|
|
1973
1980
|
});
|
|
1974
1981
|
}
|
|
@@ -1976,8 +1983,9 @@ async function runWebSessionProcess(session, specFile, crashDir, modeName, snaps
|
|
|
1976
1983
|
code = 1;
|
|
1977
1984
|
await session.close(error instanceof Error ? error : new Error(String(error)));
|
|
1978
1985
|
stderrBuffer +=
|
|
1979
|
-
(error instanceof Error
|
|
1980
|
-
|
|
1986
|
+
(error instanceof Error
|
|
1987
|
+
? (error.stack ?? error.message)
|
|
1988
|
+
: String(error)) + "\n";
|
|
1981
1989
|
}
|
|
1982
1990
|
finally {
|
|
1983
1991
|
input.end();
|
|
@@ -1986,8 +1994,7 @@ async function runWebSessionProcess(session, specFile, crashDir, modeName, snaps
|
|
|
1986
1994
|
if (reportStream.sawChunkStart) {
|
|
1987
1995
|
if (!reportStream.sawChunkEnd) {
|
|
1988
1996
|
parseError =
|
|
1989
|
-
parseError ??
|
|
1990
|
-
"missing report:end marker for chunked report payload";
|
|
1997
|
+
parseError ?? "missing report:end marker for chunked report payload";
|
|
1991
1998
|
}
|
|
1992
1999
|
else {
|
|
1993
2000
|
const chunkedPayload = reportStream.chunks.join("");
|
|
@@ -2091,7 +2098,7 @@ function synthesizeReportFromRuntimeEvents(specFile, runtimeEvents) {
|
|
|
2091
2098
|
suites: [
|
|
2092
2099
|
{
|
|
2093
2100
|
file: specFile,
|
|
2094
|
-
description: runtimeEvents.fileName ||
|
|
2101
|
+
description: runtimeEvents.fileName || formatSpecDisplayPath(specFile),
|
|
2095
2102
|
depth: 0,
|
|
2096
2103
|
kind: "file",
|
|
2097
2104
|
verdict,
|
|
@@ -2136,7 +2143,7 @@ function appendRuntimeFailureReport(report, specFile, modeName, title, details,
|
|
|
2136
2143
|
const suites = Array.isArray(report?.suites) ? report.suites : [];
|
|
2137
2144
|
suites.push({
|
|
2138
2145
|
file: specFile,
|
|
2139
|
-
description:
|
|
2146
|
+
description: formatSpecDisplayPath(specFile),
|
|
2140
2147
|
depth: 0,
|
|
2141
2148
|
kind: "runtime-error",
|
|
2142
2149
|
verdict: "fail",
|
|
@@ -2221,9 +2228,11 @@ function readFileReport(stats, fileReport) {
|
|
|
2221
2228
|
: [];
|
|
2222
2229
|
const file = String(fileReportAny.file ?? "");
|
|
2223
2230
|
const modeName = String(fileReportAny.modeName ?? "");
|
|
2231
|
+
const runCommand = String(fileReportAny.runCommand ?? "");
|
|
2232
|
+
const buildCommand = String(fileReportAny.buildCommand ?? "");
|
|
2224
2233
|
let fileVerdict = "none";
|
|
2225
2234
|
for (const suite of suites) {
|
|
2226
|
-
fileVerdict = mergeVerdict(fileVerdict, readSuite(stats, suite, file, modeName));
|
|
2235
|
+
fileVerdict = mergeVerdict(fileVerdict, readSuite(stats, suite, file, modeName, runCommand, buildCommand));
|
|
2227
2236
|
}
|
|
2228
2237
|
if (fileVerdict == "fail") {
|
|
2229
2238
|
stats.failedFiles++;
|
|
@@ -2235,7 +2244,7 @@ function readFileReport(stats, fileReport) {
|
|
|
2235
2244
|
stats.skippedFiles++;
|
|
2236
2245
|
}
|
|
2237
2246
|
}
|
|
2238
|
-
function readSuite(stats, suite, file, modeName) {
|
|
2247
|
+
function readSuite(stats, suite, file, modeName, runCommand, buildCommand) {
|
|
2239
2248
|
const suiteAny = suite;
|
|
2240
2249
|
const kind = String(suiteAny.kind ?? "");
|
|
2241
2250
|
let verdict = normalizeVerdict(suiteAny.verdict);
|
|
@@ -2247,7 +2256,7 @@ function readSuite(stats, suite, file, modeName) {
|
|
|
2247
2256
|
? suiteAny.suites
|
|
2248
2257
|
: [];
|
|
2249
2258
|
for (const subSuite of subSuites) {
|
|
2250
|
-
verdict = mergeVerdict(verdict, readSuite(stats, subSuite, file, modeName));
|
|
2259
|
+
verdict = mergeVerdict(verdict, readSuite(stats, subSuite, file, modeName, runCommand, buildCommand));
|
|
2251
2260
|
}
|
|
2252
2261
|
const tests = Array.isArray(suiteAny.tests)
|
|
2253
2262
|
? suiteAny.tests
|
|
@@ -2285,6 +2294,8 @@ function readSuite(stats, suite, file, modeName) {
|
|
|
2285
2294
|
...suiteAny,
|
|
2286
2295
|
file,
|
|
2287
2296
|
modeName,
|
|
2297
|
+
runCommand,
|
|
2298
|
+
buildCommand,
|
|
2288
2299
|
});
|
|
2289
2300
|
}
|
|
2290
2301
|
else if (verdict == "ok") {
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
export function buildWebRunnerSource() {
|
|
2
2
|
return `// Feel free to edit this file!
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
// Runner files use the name <mode>.<type>.js, where <type> is bindings, wasi, or web.
|
|
4
|
+
// To create a runner for another mode, copy this file to <new-mode>.<type>.js.
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
import { instantiate } from "as-test/lib";
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
let exports = null;
|
|
9
|
+
const imports = {};
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
instantiate(imports)
|
|
12
|
+
.then((instance) => {
|
|
13
|
+
exports = instance.exports;
|
|
14
|
+
instance.exports.start?.();
|
|
15
|
+
// Add extra startup logic here when needed.
|
|
16
|
+
})
|
|
17
|
+
.catch((error) => {
|
|
18
|
+
throw new Error("Failed to run web module: " + String(error));
|
|
19
|
+
});
|
|
20
20
|
`;
|
|
21
21
|
}
|
|
@@ -488,7 +488,12 @@ function resolveHeadlessFlags(commandValue) {
|
|
|
488
488
|
const lower = commandValue.toLowerCase();
|
|
489
489
|
if (lower.includes("firefox"))
|
|
490
490
|
return ["-headless"];
|
|
491
|
-
return [
|
|
491
|
+
return [
|
|
492
|
+
"--headless=new",
|
|
493
|
+
"--disable-gpu",
|
|
494
|
+
"--no-first-run",
|
|
495
|
+
"--no-default-browser-check",
|
|
496
|
+
];
|
|
492
497
|
}
|
|
493
498
|
function hasExecutable(command) {
|
|
494
499
|
if (!command.length)
|
package/bin/crash-store.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { mkdirSync, writeFileSync } from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
export function persistCrashRecord(rootDir, record) {
|
|
4
|
-
const entry = record.entryKey?.length
|
|
4
|
+
const entry = record.entryKey?.length
|
|
5
|
+
? record.entryKey
|
|
6
|
+
: crashEntryKey(record.file);
|
|
5
7
|
const dir = path.resolve(process.cwd(), rootDir);
|
|
6
8
|
mkdirSync(dir, { recursive: true });
|
|
7
9
|
const jsonPath = path.join(dir, `${entry}.json`);
|