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.
@@ -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
  }
@@ -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: normalized.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
- if (spawnError) {
1669
- const errorText = spawnError.stack ?? spawnError.message;
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,
@@ -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
  }
@@ -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
  }