as-test 1.0.12 → 1.0.14
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 +13 -0
- package/README.md +43 -0
- package/as-test.config.schema.json +71 -5
- package/assembly/as-test.intellisense.d.ts +8 -0
- package/assembly/src/fuzz.ts +277 -10
- package/bin/commands/build-core.js +5 -3
- package/bin/commands/fuzz-core.js +46 -14
- package/bin/commands/fuzz.js +2 -1
- package/bin/commands/run-core.js +144 -3
- package/bin/commands/run.js +2 -1
- package/bin/commands/test.js +3 -1
- package/bin/index.js +138 -49
- package/bin/reporters/default.js +18 -8
- package/bin/types.js +1 -3
- package/bin/util.js +347 -256
- package/package.json +8 -8
- package/transform/lib/builder.js +14 -15
- package/transform/lib/coverage.js +11 -12
- package/transform/lib/index.js +0 -1
- package/transform/lib/linker.js +3 -4
- package/transform/lib/location.js +0 -1
- package/transform/lib/log.js +0 -1
- package/transform/lib/mock.js +15 -9
- package/transform/lib/range.js +0 -1
- package/transform/lib/types.js +0 -1
- package/transform/lib/util.js +0 -1
- package/transform/lib/visitor.js +64 -65
package/bin/commands/fuzz.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
export async function executeFuzzCommand(rawArgs, configPath, selectedModes, deps) {
|
|
2
2
|
const commandArgs = deps.resolveCommandArgs(rawArgs, "fuzz");
|
|
3
|
+
const fuzzerSelectors = deps.resolveFuzzerSelectors(rawArgs, "fuzz");
|
|
3
4
|
const listFlags = deps.resolveListFlags(rawArgs, "fuzz");
|
|
4
5
|
const modeTargets = deps.resolveExecutionModes(configPath, selectedModes);
|
|
5
6
|
if (listFlags.list || listFlags.listModes) {
|
|
6
7
|
await deps.listExecutionPlan("fuzz", configPath, commandArgs, modeTargets, listFlags);
|
|
7
8
|
return;
|
|
8
9
|
}
|
|
9
|
-
await deps.runFuzzModes(configPath, commandArgs, modeTargets, rawArgs);
|
|
10
|
+
await deps.runFuzzModes(configPath, commandArgs, fuzzerSelectors, modeTargets, rawArgs);
|
|
10
11
|
}
|
package/bin/commands/run-core.js
CHANGED
|
@@ -327,6 +327,9 @@ function formatReadableLog(file, suites, modeName, buildCommand, runCommand, sna
|
|
|
327
327
|
lines.push("", "Failures:");
|
|
328
328
|
for (const failure of failures) {
|
|
329
329
|
lines.push(`FAIL ${failure.title}${failure.where.length ? ` (${failure.where})` : ""}`);
|
|
330
|
+
if (failure.suitePath.length) {
|
|
331
|
+
lines.push(`Repro: ${buildSuiteReproCommand(file, failure.suitePath, modeName)}`);
|
|
332
|
+
}
|
|
330
333
|
if (failure.message.length)
|
|
331
334
|
lines.push(`Message: ${failure.message}`);
|
|
332
335
|
if (failure.left.length)
|
|
@@ -352,6 +355,139 @@ function formatInvocation(invocation) {
|
|
|
352
355
|
.map((part) => (/[\s"'\\]/.test(part) ? JSON.stringify(part) : part))
|
|
353
356
|
.join(" ");
|
|
354
357
|
}
|
|
358
|
+
function filterSelectedSuites(suites, selectors, file, modeName) {
|
|
359
|
+
const annotated = annotateSuitePaths(suites, []);
|
|
360
|
+
const matches = resolveSuiteSelectionMatches(annotated, selectors, file);
|
|
361
|
+
const selected = new Set(matches.map((match) => match.resolvedPath));
|
|
362
|
+
return cloneSelectedSuites(annotated, selected, file, modeName);
|
|
363
|
+
}
|
|
364
|
+
function annotateSuitePaths(suites, pathParts) {
|
|
365
|
+
return suites.map((suite) => annotateSuiteNode(suite, pathParts));
|
|
366
|
+
}
|
|
367
|
+
function annotateSuiteNode(suite, pathParts) {
|
|
368
|
+
const description = String(suite?.description ?? "unknown");
|
|
369
|
+
const slug = slugifySelectorSegment(description);
|
|
370
|
+
const nextParts = [...pathParts, slug];
|
|
371
|
+
const nextSuites = Array.isArray(suite?.suites)
|
|
372
|
+
? suite.suites
|
|
373
|
+
: [];
|
|
374
|
+
const annotatedSuites = annotateSuitePaths(nextSuites, nextParts);
|
|
375
|
+
return {
|
|
376
|
+
...suite,
|
|
377
|
+
path: nextParts.join("/"),
|
|
378
|
+
suites: annotatedSuites,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
function resolveSuiteSelectionMatches(suites, selectors, file) {
|
|
382
|
+
const matches = [];
|
|
383
|
+
for (const selector of selectors) {
|
|
384
|
+
const normalized = selector.trim();
|
|
385
|
+
if (!normalized.length)
|
|
386
|
+
continue;
|
|
387
|
+
if (normalized.includes("/")) {
|
|
388
|
+
const resolved = resolveExplicitSuitePath(suites, normalized);
|
|
389
|
+
if (!resolved) {
|
|
390
|
+
throw new Error(`No suites matched "${selector}" in ${path.basename(file)}.`);
|
|
391
|
+
}
|
|
392
|
+
matches.push({
|
|
393
|
+
kind: "path",
|
|
394
|
+
raw: selector,
|
|
395
|
+
resolvedPath: resolved.path,
|
|
396
|
+
depth: resolved.depth,
|
|
397
|
+
});
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
const resolved = resolveBareSuiteSelector(suites, normalized);
|
|
401
|
+
if (!resolved) {
|
|
402
|
+
throw new Error(`No suites matched "${selector}" in ${path.basename(file)}.`);
|
|
403
|
+
}
|
|
404
|
+
matches.push({
|
|
405
|
+
kind: "bare",
|
|
406
|
+
raw: selector,
|
|
407
|
+
resolvedPath: resolved.path,
|
|
408
|
+
depth: resolved.depth,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
return matches;
|
|
412
|
+
}
|
|
413
|
+
function resolveExplicitSuitePath(suites, selector) {
|
|
414
|
+
const normalized = selector
|
|
415
|
+
.split("/")
|
|
416
|
+
.map((part) => slugifySelectorSegment(part))
|
|
417
|
+
.filter((part) => part.length)
|
|
418
|
+
.join("/");
|
|
419
|
+
if (!normalized.length)
|
|
420
|
+
return null;
|
|
421
|
+
let match = null;
|
|
422
|
+
walkSuites(suites, (suite, depth) => {
|
|
423
|
+
if (suite.path == normalized) {
|
|
424
|
+
match = { path: suite.path, depth };
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
return false;
|
|
428
|
+
});
|
|
429
|
+
return match;
|
|
430
|
+
}
|
|
431
|
+
function resolveBareSuiteSelector(suites, selector) {
|
|
432
|
+
const slug = slugifySelectorSegment(selector);
|
|
433
|
+
if (!slug.length)
|
|
434
|
+
return null;
|
|
435
|
+
const matches = [];
|
|
436
|
+
walkSuites(suites, (suite, depth) => {
|
|
437
|
+
const leaf = String(suite.path ?? "").split("/").pop() ?? "";
|
|
438
|
+
if (leaf == slug) {
|
|
439
|
+
matches.push({ path: String(suite.path), depth });
|
|
440
|
+
}
|
|
441
|
+
return false;
|
|
442
|
+
});
|
|
443
|
+
if (!matches.length)
|
|
444
|
+
return null;
|
|
445
|
+
matches.sort((a, b) => a.depth - b.depth || a.path.localeCompare(b.path));
|
|
446
|
+
const shallowest = matches[0];
|
|
447
|
+
const ambiguous = matches.filter((match) => match.depth == shallowest.depth);
|
|
448
|
+
if (ambiguous.length > 1) {
|
|
449
|
+
throw new Error(`Suite selector "${selector}" is ambiguous. Matches: ${ambiguous.map((match) => match.path).join(", ")}`);
|
|
450
|
+
}
|
|
451
|
+
return shallowest;
|
|
452
|
+
}
|
|
453
|
+
function walkSuites(suites, visitor, depth = 0) {
|
|
454
|
+
for (const suite of suites) {
|
|
455
|
+
if (visitor(suite, depth))
|
|
456
|
+
return true;
|
|
457
|
+
const childSuites = Array.isArray(suite?.suites) ? suite.suites : [];
|
|
458
|
+
if (walkSuites(childSuites, visitor, depth + 1))
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
function cloneSelectedSuites(suites, selected, file, modeName) {
|
|
464
|
+
const out = [];
|
|
465
|
+
for (const suite of suites) {
|
|
466
|
+
const childSuites = Array.isArray(suite.suites) ? suite.suites : [];
|
|
467
|
+
const selectedChildren = cloneSelectedSuites(childSuites, selected, file, modeName);
|
|
468
|
+
const keep = selected.has(String(suite.path ?? "")) || selectedChildren.length > 0;
|
|
469
|
+
if (!keep)
|
|
470
|
+
continue;
|
|
471
|
+
out.push({
|
|
472
|
+
...suite,
|
|
473
|
+
file,
|
|
474
|
+
modeName,
|
|
475
|
+
suites: selectedChildren,
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
480
|
+
function slugifySelectorSegment(value) {
|
|
481
|
+
return value
|
|
482
|
+
.trim()
|
|
483
|
+
.toLowerCase()
|
|
484
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
485
|
+
.replace(/^-+|-+$/g, "");
|
|
486
|
+
}
|
|
487
|
+
function buildSuiteReproCommand(file, suitePath, modeName) {
|
|
488
|
+
const modeArg = modeName && modeName != "default" ? ` --mode ${modeName}` : "";
|
|
489
|
+
return `ast run ${file}${modeArg} --suite ${suitePath}`;
|
|
490
|
+
}
|
|
355
491
|
function collectReadableFailures(suites, file, pathParts) {
|
|
356
492
|
const out = [];
|
|
357
493
|
for (const suite of suites) {
|
|
@@ -372,6 +508,7 @@ function collectReadableFailures(suites, file, pathParts) {
|
|
|
372
508
|
message: String(test.message ?? ""),
|
|
373
509
|
left: JSON.stringify(test.left ?? ""),
|
|
374
510
|
right: JSON.stringify(test.right ?? ""),
|
|
511
|
+
suitePath: String(suiteAny.path ?? ""),
|
|
375
512
|
});
|
|
376
513
|
}
|
|
377
514
|
const childSuites = Array.isArray(suiteAny.suites)
|
|
@@ -492,6 +629,9 @@ export async function run(flags = {}, configPath = DEFAULT_CONFIG_PATH, selector
|
|
|
492
629
|
throw new Error(`Failed to run ${path.basename(file)} in mode ${modeLabel} with ${details}`);
|
|
493
630
|
}
|
|
494
631
|
const normalized = normalizeReport(report);
|
|
632
|
+
const selectedSuites = options.suiteSelectors?.length
|
|
633
|
+
? filterSelectedSuites(normalized.suites, options.suiteSelectors, file, options.modeName ?? "default")
|
|
634
|
+
: normalized.suites;
|
|
495
635
|
snapshotStore.flush();
|
|
496
636
|
snapshotSummary.matched += snapshotStore.matched;
|
|
497
637
|
snapshotSummary.created += snapshotStore.created;
|
|
@@ -500,7 +640,7 @@ export async function run(flags = {}, configPath = DEFAULT_CONFIG_PATH, selector
|
|
|
500
640
|
reports.push({
|
|
501
641
|
file,
|
|
502
642
|
modeName: options.modeName ?? "default",
|
|
503
|
-
suites:
|
|
643
|
+
suites: selectedSuites,
|
|
504
644
|
coverage: normalized.coverage,
|
|
505
645
|
runCommand: runCommandForLog,
|
|
506
646
|
snapshotSummary: {
|
|
@@ -1665,8 +1805,9 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
|
|
|
1665
1805
|
if (stderrPendingLine.length && !shouldSuppressWasiWarningLine(stderrPendingLine)) {
|
|
1666
1806
|
stderrBuffer += stderrPendingLine;
|
|
1667
1807
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1808
|
+
const processSpawnError = spawnError;
|
|
1809
|
+
if (processSpawnError) {
|
|
1810
|
+
const errorText = processSpawnError.stack ?? processSpawnError.message;
|
|
1670
1811
|
persistCrashRecord(crashDir, {
|
|
1671
1812
|
kind: "test",
|
|
1672
1813
|
file: specFile,
|
package/bin/commands/run.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { createRunReporter, run } from "./run-core.js";
|
|
2
2
|
export async function executeRunCommand(rawArgs, flags, configPath, selectedModes, deps) {
|
|
3
3
|
const commandArgs = deps.resolveCommandArgs(rawArgs, "run");
|
|
4
|
+
const suiteSelectors = deps.resolveSuiteSelectors(rawArgs, "run");
|
|
4
5
|
const listFlags = deps.resolveListFlags(rawArgs, "run");
|
|
5
6
|
const featureToggles = deps.resolveFeatureToggles(rawArgs, "run");
|
|
6
7
|
const runFlags = {
|
|
@@ -20,5 +21,5 @@ export async function executeRunCommand(rawArgs, flags, configPath, selectedMode
|
|
|
20
21
|
await deps.listExecutionPlan("run", configPath, commandArgs, modeTargets, listFlags);
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
|
-
await deps.runRuntimeModes(runFlags, configPath, commandArgs, modeTargets);
|
|
24
|
+
await deps.runRuntimeModes(runFlags, configPath, commandArgs, suiteSelectors, modeTargets);
|
|
24
25
|
}
|
package/bin/commands/test.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export async function executeTestCommand(rawArgs, flags, configPath, selectedModes, deps) {
|
|
2
2
|
const commandArgs = deps.resolveCommandArgs(rawArgs, "test");
|
|
3
|
+
const suiteSelectors = deps.resolveSuiteSelectors(rawArgs, "test");
|
|
4
|
+
const fuzzerSelectors = deps.resolveFuzzerSelectors(rawArgs, "test");
|
|
3
5
|
const listFlags = deps.resolveListFlags(rawArgs, "test");
|
|
4
6
|
const featureToggles = deps.resolveFeatureToggles(rawArgs, "test");
|
|
5
7
|
const buildFeatureToggles = {
|
|
@@ -25,5 +27,5 @@ export async function executeTestCommand(rawArgs, flags, configPath, selectedMod
|
|
|
25
27
|
await deps.listExecutionPlan("test", configPath, commandArgs, modeTargets, listFlags, fuzzEnabled);
|
|
26
28
|
return;
|
|
27
29
|
}
|
|
28
|
-
await deps.runTestModes(runFlags, configPath, commandArgs, modeTargets, buildFeatureToggles, fuzzEnabled, fuzzOverrides);
|
|
30
|
+
await deps.runTestModes(runFlags, configPath, commandArgs, suiteSelectors, fuzzerSelectors, modeTargets, buildFeatureToggles, fuzzEnabled, fuzzOverrides);
|
|
29
31
|
}
|