as-test 1.1.4 → 1.1.6-patch.1
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 +24 -0
- package/assembly/__fuzz__/nested/array.fuzz.ts +13 -0
- package/bin/commands/build-core.js +27 -49
- package/bin/commands/fuzz-core.js +13 -53
- package/bin/commands/run-core.js +98 -99
- package/bin/commands/web-session.js +4 -0
- package/bin/crash-store.js +1 -1
- package/bin/index.js +37 -75
- package/bin/util.js +87 -6
- package/package.json +1 -1
- package/transform/lib/coverage.js +17 -16
- package/transform/lib/index.js +3 -1
- package/transform/lib/types.js +3 -0
- package/transform/lib/visitor.js +70 -67
package/bin/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { executeInitCommand } from "./commands/init.js";
|
|
|
10
10
|
import { executeDoctorCommand } from "./commands/doctor.js";
|
|
11
11
|
import { executeCleanCommand } from "./commands/clean.js";
|
|
12
12
|
import { fuzz } from "./commands/fuzz-core.js";
|
|
13
|
-
import { applyMode, formatTime, formatSpecDisplayPath, getDefaultModeNames, getCliVersion, loadConfig, resolveModeNames, } from "./util.js";
|
|
13
|
+
import { applyMode, formatTime, formatSpecDisplayPath, getDefaultModeNames, getCliVersion, loadConfig, resolveArtifactPath, resolveModeNames, resolveSpecRelativePath, } from "./util.js";
|
|
14
14
|
import * as path from "path";
|
|
15
15
|
import { spawnSync } from "child_process";
|
|
16
16
|
import { glob } from "glob";
|
|
@@ -1140,7 +1140,7 @@ async function runTestSequential(runFlags, configPath, selectors, suiteSelectors
|
|
|
1140
1140
|
const results = [];
|
|
1141
1141
|
let failed = false;
|
|
1142
1142
|
const buildIntervals = [];
|
|
1143
|
-
const
|
|
1143
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
1144
1144
|
for (const file of files) {
|
|
1145
1145
|
const buildStartedAt = Date.now();
|
|
1146
1146
|
let result;
|
|
@@ -1148,7 +1148,7 @@ async function runTestSequential(runFlags, configPath, selectors, suiteSelectors
|
|
|
1148
1148
|
await build(configPath, [file], modeName, buildFeatureToggles);
|
|
1149
1149
|
buildIntervals.push({ start: buildStartedAt, end: Date.now() });
|
|
1150
1150
|
const buildInvocation = await getBuildInvocationPreview(configPath, file, modeName, buildFeatureToggles);
|
|
1151
|
-
const artifactKey =
|
|
1151
|
+
const artifactKey = resolveArtifactStem(file, inputPatterns);
|
|
1152
1152
|
result = await run(runFlags, configPath, [file], false, {
|
|
1153
1153
|
reporter,
|
|
1154
1154
|
webSession,
|
|
@@ -1156,7 +1156,7 @@ async function runTestSequential(runFlags, configPath, selectors, suiteSelectors
|
|
|
1156
1156
|
emitRunStart: false,
|
|
1157
1157
|
emitRunComplete: false,
|
|
1158
1158
|
logFileName: `test.${artifactKey}.log.json`,
|
|
1159
|
-
coverageFileName:
|
|
1159
|
+
coverageFileName: `${artifactKey}.log.json`,
|
|
1160
1160
|
buildCommand: formatBuildInvocation(buildInvocation),
|
|
1161
1161
|
modeName,
|
|
1162
1162
|
});
|
|
@@ -1351,7 +1351,7 @@ async function runRuntimeMatrix(runFlags, configPath, selectors, suiteSelectors,
|
|
|
1351
1351
|
failed: false,
|
|
1352
1352
|
passed: false,
|
|
1353
1353
|
}));
|
|
1354
|
-
const
|
|
1354
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
1355
1355
|
const buildIntervals = [];
|
|
1356
1356
|
for (let fileIndex = 0; fileIndex < files.length; fileIndex++) {
|
|
1357
1357
|
const file = files[fileIndex];
|
|
@@ -1365,7 +1365,7 @@ async function runRuntimeMatrix(runFlags, configPath, selectors, suiteSelectors,
|
|
|
1365
1365
|
const modeName = modes[i];
|
|
1366
1366
|
try {
|
|
1367
1367
|
const buildInvocation = await getBuildInvocationPreview(configPath, file, modeName, {});
|
|
1368
|
-
const artifactKey =
|
|
1368
|
+
const artifactKey = resolveArtifactStem(file, inputPatterns);
|
|
1369
1369
|
const result = await run(runFlags, configPath, [file], false, {
|
|
1370
1370
|
reporter: silentReporter,
|
|
1371
1371
|
reporterKind: "default",
|
|
@@ -1373,7 +1373,7 @@ async function runRuntimeMatrix(runFlags, configPath, selectors, suiteSelectors,
|
|
|
1373
1373
|
emitRunStart: false,
|
|
1374
1374
|
emitRunComplete: false,
|
|
1375
1375
|
logFileName: `run.${artifactKey}.log.json`,
|
|
1376
|
-
coverageFileName:
|
|
1376
|
+
coverageFileName: `${artifactKey}.log.json`,
|
|
1377
1377
|
buildCommand: formatBuildInvocation(buildInvocation),
|
|
1378
1378
|
modeName,
|
|
1379
1379
|
});
|
|
@@ -1531,7 +1531,7 @@ async function runTestMatrix(runFlags, configPath, selectors, suiteSelectors, fu
|
|
|
1531
1531
|
failed: false,
|
|
1532
1532
|
passed: false,
|
|
1533
1533
|
}));
|
|
1534
|
-
const
|
|
1534
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
1535
1535
|
const buildIntervals = [];
|
|
1536
1536
|
for (let fileIndex = 0; fileIndex < files.length; fileIndex++) {
|
|
1537
1537
|
const file = files[fileIndex];
|
|
@@ -1554,14 +1554,14 @@ async function runTestMatrix(runFlags, configPath, selectors, suiteSelectors, fu
|
|
|
1554
1554
|
});
|
|
1555
1555
|
buildIntervals.push({ start: buildStartedAt, end: Date.now() });
|
|
1556
1556
|
const buildInvocation = await getBuildInvocationPreview(configPath, file, modeName, buildFeatureToggles);
|
|
1557
|
-
const artifactKey =
|
|
1557
|
+
const artifactKey = resolveArtifactStem(file, inputPatterns);
|
|
1558
1558
|
result = await run(runFlags, configPath, [file], false, {
|
|
1559
1559
|
reporter: silentReporter,
|
|
1560
1560
|
reporterKind: "default",
|
|
1561
1561
|
emitRunStart: false,
|
|
1562
1562
|
emitRunComplete: false,
|
|
1563
1563
|
logFileName: `test.${artifactKey}.log.json`,
|
|
1564
|
-
coverageFileName:
|
|
1564
|
+
coverageFileName: `${artifactKey}.log.json`,
|
|
1565
1565
|
buildCommand: formatBuildInvocation(buildInvocation),
|
|
1566
1566
|
modeName,
|
|
1567
1567
|
});
|
|
@@ -1740,7 +1740,7 @@ async function runRuntimeMatrixParallel(runFlags, configPath, selectors, suiteSe
|
|
|
1740
1740
|
const silentReporter = {};
|
|
1741
1741
|
const modeLabels = modes.map((modeName) => modeName ?? "default");
|
|
1742
1742
|
const showPerModeTimes = Boolean(runFlags.verbose);
|
|
1743
|
-
const
|
|
1743
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
1744
1744
|
const ordered = new Array(files.length);
|
|
1745
1745
|
const useQueueDisplay = reporterSession.reporterKind == "default";
|
|
1746
1746
|
const queueDisplay = new ParallelQueueDisplay(useQueueDisplay && !runFlags.clean);
|
|
@@ -1769,7 +1769,7 @@ async function runRuntimeMatrixParallel(runFlags, configPath, selectors, suiteSe
|
|
|
1769
1769
|
});
|
|
1770
1770
|
buildIntervals.push({ start: buildStartedAt, end: Date.now() });
|
|
1771
1771
|
const buildInvocation = await getBuildInvocationPreview(configPath, file, modeName, {});
|
|
1772
|
-
const artifactKey =
|
|
1772
|
+
const artifactKey = resolveArtifactStem(file, inputPatterns);
|
|
1773
1773
|
result = await run(runFlags, configPath, [file], false, {
|
|
1774
1774
|
reporter: silentReporter,
|
|
1775
1775
|
reporterKind: "default",
|
|
@@ -1777,7 +1777,7 @@ async function runRuntimeMatrixParallel(runFlags, configPath, selectors, suiteSe
|
|
|
1777
1777
|
emitRunStart: false,
|
|
1778
1778
|
emitRunComplete: false,
|
|
1779
1779
|
logFileName: `run.${artifactKey}.log.json`,
|
|
1780
|
-
coverageFileName:
|
|
1780
|
+
coverageFileName: `${artifactKey}.log.json`,
|
|
1781
1781
|
buildCommand: formatBuildInvocation(buildInvocation),
|
|
1782
1782
|
modeName,
|
|
1783
1783
|
});
|
|
@@ -1853,7 +1853,7 @@ async function runTestSingleParallel(runFlags, configPath, selectors, suiteSelec
|
|
|
1853
1853
|
snapshotEnabled,
|
|
1854
1854
|
createSnapshots: runFlags.createSnapshots,
|
|
1855
1855
|
});
|
|
1856
|
-
const
|
|
1856
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
1857
1857
|
const results = new Array(files.length);
|
|
1858
1858
|
const useQueueDisplay = reporterSession.reporterKind == "default";
|
|
1859
1859
|
const queueDisplay = new ParallelQueueDisplay(useQueueDisplay && !runFlags.clean);
|
|
@@ -1881,14 +1881,14 @@ async function runTestSingleParallel(runFlags, configPath, selectors, suiteSelec
|
|
|
1881
1881
|
});
|
|
1882
1882
|
buildIntervals.push({ start: buildStartedAt, end: Date.now() });
|
|
1883
1883
|
const buildInvocation = await getBuildInvocationPreview(configPath, file, modeName, buildFeatureToggles);
|
|
1884
|
-
const artifactKey =
|
|
1884
|
+
const artifactKey = resolveArtifactStem(file, inputPatterns);
|
|
1885
1885
|
result = await run({ ...runFlags, clean: true }, configPath, [file], false, {
|
|
1886
1886
|
reporter: buffered?.reporter,
|
|
1887
1887
|
reporterKind: buffered?.reporterKind,
|
|
1888
1888
|
suiteSelectors,
|
|
1889
1889
|
emitRunComplete: false,
|
|
1890
1890
|
logFileName: `test.${artifactKey}.log.json`,
|
|
1891
|
-
coverageFileName:
|
|
1891
|
+
coverageFileName: `${artifactKey}.log.json`,
|
|
1892
1892
|
buildCommand: formatBuildInvocation(buildInvocation),
|
|
1893
1893
|
modeName,
|
|
1894
1894
|
});
|
|
@@ -1967,7 +1967,7 @@ async function runTestMatrixParallel(runFlags, configPath, selectors, suiteSelec
|
|
|
1967
1967
|
const silentReporter = {};
|
|
1968
1968
|
const modeLabels = modes.map((modeName) => modeName ?? "default");
|
|
1969
1969
|
const showPerModeTimes = Boolean(runFlags.verbose);
|
|
1970
|
-
const
|
|
1970
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
1971
1971
|
const ordered = new Array(files.length);
|
|
1972
1972
|
const useQueueDisplay = reporterSession.reporterKind == "default";
|
|
1973
1973
|
const queueDisplay = new ParallelQueueDisplay(useQueueDisplay && !runFlags.clean);
|
|
@@ -1996,7 +1996,7 @@ async function runTestMatrixParallel(runFlags, configPath, selectors, suiteSelec
|
|
|
1996
1996
|
});
|
|
1997
1997
|
buildIntervals.push({ start: buildStartedAt, end: Date.now() });
|
|
1998
1998
|
const buildInvocation = await getBuildInvocationPreview(configPath, file, modeName, buildFeatureToggles);
|
|
1999
|
-
const artifactKey =
|
|
1999
|
+
const artifactKey = resolveArtifactStem(file, inputPatterns);
|
|
2000
2000
|
result = await run(runFlags, configPath, [file], false, {
|
|
2001
2001
|
reporter: silentReporter,
|
|
2002
2002
|
reporterKind: "default",
|
|
@@ -2004,7 +2004,7 @@ async function runTestMatrixParallel(runFlags, configPath, selectors, suiteSelec
|
|
|
2004
2004
|
emitRunStart: false,
|
|
2005
2005
|
emitRunComplete: false,
|
|
2006
2006
|
logFileName: `test.${artifactKey}.log.json`,
|
|
2007
|
-
coverageFileName:
|
|
2007
|
+
coverageFileName: `${artifactKey}.log.json`,
|
|
2008
2008
|
buildCommand: formatBuildInvocation(buildInvocation),
|
|
2009
2009
|
modeName,
|
|
2010
2010
|
});
|
|
@@ -2707,57 +2707,19 @@ function isBareSuiteSelector(selector) {
|
|
|
2707
2707
|
function stripSuiteSuffix(selector) {
|
|
2708
2708
|
return selector.replace(/\.spec\.ts$/, "").replace(/\.ts$/, "");
|
|
2709
2709
|
}
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
function resolvePerFileArtifactKey(file, duplicateSpecBasenames) {
|
|
2724
|
-
const base = path.basename(file);
|
|
2725
|
-
let raw = base;
|
|
2726
|
-
if (duplicateSpecBasenames.has(base)) {
|
|
2727
|
-
const disambiguator = resolvePerFileDisambiguator(file);
|
|
2728
|
-
if (disambiguator.length) {
|
|
2729
|
-
raw = `${base}.${disambiguator}`;
|
|
2730
|
-
}
|
|
2731
|
-
}
|
|
2732
|
-
return raw.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
2733
|
-
}
|
|
2734
|
-
function resolvePerFileDisambiguator(file) {
|
|
2735
|
-
const relDir = path.dirname(path.relative(process.cwd(), file));
|
|
2736
|
-
if (!relDir.length || relDir == ".")
|
|
2737
|
-
return "";
|
|
2738
|
-
return relDir
|
|
2739
|
-
.replace(/[\\/]+/g, "__")
|
|
2740
|
-
.replace(/[^A-Za-z0-9._-]/g, "_")
|
|
2741
|
-
.replace(/^_+|_+$/g, "");
|
|
2742
|
-
}
|
|
2743
|
-
function resolveArtifactFileNameForPreview(file, target, modeName, duplicateSpecBasenames) {
|
|
2744
|
-
const base = path
|
|
2745
|
-
.basename(file)
|
|
2746
|
-
.replace(/\.spec\.ts$/, "")
|
|
2747
|
-
.replace(/\.ts$/, "");
|
|
2748
|
-
const legacy = !modeName
|
|
2749
|
-
? `${path.basename(file).replace(".ts", ".wasm")}`
|
|
2750
|
-
: `${base}.${modeName}.${target}.wasm`;
|
|
2751
|
-
if (!duplicateSpecBasenames.has(path.basename(file))) {
|
|
2752
|
-
return legacy;
|
|
2753
|
-
}
|
|
2754
|
-
const disambiguator = resolvePerFileDisambiguator(file);
|
|
2755
|
-
if (!disambiguator.length) {
|
|
2756
|
-
return legacy;
|
|
2757
|
-
}
|
|
2758
|
-
const ext = path.extname(legacy);
|
|
2759
|
-
const stem = ext.length ? legacy.slice(0, -ext.length) : legacy;
|
|
2760
|
-
return `${stem}.${disambiguator}${ext}`;
|
|
2710
|
+
// Returns the spec relative path (under the configured input base) with the
|
|
2711
|
+
// trailing ".ts" stripped, suitable for use as a stable per-file key for
|
|
2712
|
+
// coverage and log filenames.
|
|
2713
|
+
function resolveArtifactStem(file, inputPatterns) {
|
|
2714
|
+
return resolveSpecRelativePath(file, inputPatterns).replace(/\.ts$/i, "");
|
|
2715
|
+
}
|
|
2716
|
+
async function loadInputPatterns(configPath) {
|
|
2717
|
+
const resolvedConfigPath = configPath ?? path.join(process.cwd(), "./as-test.config.json");
|
|
2718
|
+
return loadConfig(resolvedConfigPath, false).input;
|
|
2719
|
+
}
|
|
2720
|
+
async function loadFuzzInputPatterns(configPath) {
|
|
2721
|
+
const resolvedConfigPath = configPath ?? path.join(process.cwd(), "./as-test.config.json");
|
|
2722
|
+
return loadConfig(resolvedConfigPath, false).fuzz.input;
|
|
2761
2723
|
}
|
|
2762
2724
|
async function ensureWebBrowsersReady(configPath, modes, browserOverride) {
|
|
2763
2725
|
const resolvedConfigPath = configPath ?? path.join(process.cwd(), "./as-test.config.json");
|
|
@@ -3235,8 +3197,8 @@ async function listExecutionPlan(command, configPath, selectors, modes, listFlag
|
|
|
3235
3197
|
? `No fuzz files matched: ${scope}`
|
|
3236
3198
|
: `No test files matched: ${scope}`);
|
|
3237
3199
|
}
|
|
3238
|
-
const
|
|
3239
|
-
const
|
|
3200
|
+
const inputPatterns = await loadInputPatterns(configPath);
|
|
3201
|
+
const fuzzInputPatterns = await loadFuzzInputPatterns(configPath);
|
|
3240
3202
|
if (specFiles.length) {
|
|
3241
3203
|
process.stdout.write(chalk.bold("Resolved files:\n"));
|
|
3242
3204
|
for (const file of specFiles) {
|
|
@@ -3284,21 +3246,21 @@ async function listExecutionPlan(command, configPath, selectors, modes, listFlag
|
|
|
3284
3246
|
if (specFiles.length) {
|
|
3285
3247
|
process.stdout.write(" artifacts:\n");
|
|
3286
3248
|
for (const file of specFiles) {
|
|
3287
|
-
const artifactName =
|
|
3249
|
+
const artifactName = resolveArtifactPath(file, inputPatterns);
|
|
3288
3250
|
process.stdout.write(` - ${path.join(active.outDir, artifactName)}\n`);
|
|
3289
3251
|
}
|
|
3290
3252
|
}
|
|
3291
3253
|
if (fuzzFiles.length && command == "test") {
|
|
3292
3254
|
process.stdout.write(" fuzz artifacts:\n");
|
|
3293
3255
|
for (const file of fuzzFiles) {
|
|
3294
|
-
const artifactName =
|
|
3256
|
+
const artifactName = resolveArtifactPath(file, fuzzInputPatterns);
|
|
3295
3257
|
process.stdout.write(` - ${path.join(active.outDir, artifactName)}\n`);
|
|
3296
3258
|
}
|
|
3297
3259
|
}
|
|
3298
3260
|
else if (command == "fuzz") {
|
|
3299
3261
|
process.stdout.write(" artifacts:\n");
|
|
3300
3262
|
for (const file of fuzzFiles) {
|
|
3301
|
-
const artifactName =
|
|
3263
|
+
const artifactName = resolveArtifactPath(file, fuzzInputPatterns);
|
|
3302
3264
|
process.stdout.write(` - ${path.join(active.outDir, artifactName)}\n`);
|
|
3303
3265
|
}
|
|
3304
3266
|
}
|
package/bin/util.js
CHANGED
|
@@ -946,15 +946,16 @@ function cloneCoverageOptions(coverage) {
|
|
|
946
946
|
if (typeof coverage == "boolean")
|
|
947
947
|
return coverage;
|
|
948
948
|
const cloned = Object.assign(new CoverageOptions(), coverage);
|
|
949
|
+
const ignore = coverage.ignore ?? new CoverageIgnoreOptions();
|
|
949
950
|
cloned.mode = coverage.mode ?? "project";
|
|
950
951
|
cloned.dependencies = [...(coverage.dependencies ?? [])];
|
|
951
952
|
cloned.include = [...(coverage.include ?? [])];
|
|
952
953
|
cloned.exclude = [...(coverage.exclude ?? [])];
|
|
953
|
-
cloned.ignore = Object.assign(new CoverageIgnoreOptions(),
|
|
954
|
-
cloned.ignore.labels = [...(
|
|
955
|
-
cloned.ignore.names = [...(
|
|
956
|
-
cloned.ignore.locations = [...(
|
|
957
|
-
cloned.ignore.snippets = [...(
|
|
954
|
+
cloned.ignore = Object.assign(new CoverageIgnoreOptions(), ignore);
|
|
955
|
+
cloned.ignore.labels = [...(ignore.labels ?? [])];
|
|
956
|
+
cloned.ignore.names = [...(ignore.names ?? [])];
|
|
957
|
+
cloned.ignore.locations = [...(ignore.locations ?? [])];
|
|
958
|
+
cloned.ignore.snippets = [...(ignore.snippets ?? [])];
|
|
958
959
|
return cloned;
|
|
959
960
|
}
|
|
960
961
|
function cloneBuildOptions(options) {
|
|
@@ -1293,8 +1294,8 @@ function appendPathSegment(basePath, segment) {
|
|
|
1293
1294
|
}
|
|
1294
1295
|
export function getCliVersion() {
|
|
1295
1296
|
const candidates = [
|
|
1296
|
-
join(process.cwd(), "package.json"),
|
|
1297
1297
|
join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"),
|
|
1298
|
+
join(process.cwd(), "package.json"),
|
|
1298
1299
|
];
|
|
1299
1300
|
for (const pkgPath of candidates) {
|
|
1300
1301
|
if (!existsSync(pkgPath))
|
|
@@ -1397,3 +1398,83 @@ export function resolveProjectModule(specifier) {
|
|
|
1397
1398
|
}
|
|
1398
1399
|
return null;
|
|
1399
1400
|
}
|
|
1401
|
+
// picomatch-compatible glob metacharacters; first occurrence marks the start
|
|
1402
|
+
// of the dynamic part of a pattern and everything before it is the static base.
|
|
1403
|
+
const GLOB_META_RE = /[*?[\](){}!|+@]/;
|
|
1404
|
+
// Longest non-glob prefix of a single pattern, returned with native separators.
|
|
1405
|
+
// Examples:
|
|
1406
|
+
// "assembly/__tests__/**/*.spec.ts" -> "assembly/__tests__"
|
|
1407
|
+
// "**/*.spec.ts" -> ""
|
|
1408
|
+
// "assembly/foo.spec.ts" -> "assembly" (no glob → use dirname)
|
|
1409
|
+
// "/abs/path/**/*.ts" -> "/abs/path"
|
|
1410
|
+
export function resolveGlobBase(pattern) {
|
|
1411
|
+
const normalized = pattern.replace(/\\/g, "/");
|
|
1412
|
+
const metaIdx = normalized.search(GLOB_META_RE);
|
|
1413
|
+
let base;
|
|
1414
|
+
if (metaIdx < 0) {
|
|
1415
|
+
const dir = dirname(normalized);
|
|
1416
|
+
base = dir == "." ? "" : dir;
|
|
1417
|
+
}
|
|
1418
|
+
else {
|
|
1419
|
+
const slice = normalized.slice(0, metaIdx);
|
|
1420
|
+
const lastSlash = slice.lastIndexOf("/");
|
|
1421
|
+
base = lastSlash < 0 ? "" : slice.slice(0, lastSlash);
|
|
1422
|
+
}
|
|
1423
|
+
if (!base.length)
|
|
1424
|
+
return "";
|
|
1425
|
+
return base.split("/").join(sep);
|
|
1426
|
+
}
|
|
1427
|
+
// Strip the most-specific matching configured input base off `file`, returning
|
|
1428
|
+
// the path relative to that base (with native separators). If no base matches,
|
|
1429
|
+
// returns the basename of the file. Comparison is component-wise — so
|
|
1430
|
+
// "assembly/__tests" is not a prefix of "assembly/__tests__/foo.spec.ts".
|
|
1431
|
+
export function resolveSpecRelativePath(file, inputPatterns) {
|
|
1432
|
+
const patterns = Array.isArray(inputPatterns)
|
|
1433
|
+
? inputPatterns
|
|
1434
|
+
: [inputPatterns];
|
|
1435
|
+
const absFile = resolve(process.cwd(), file);
|
|
1436
|
+
const fileComponents = toComponents(absFile);
|
|
1437
|
+
let bestBaseAbs = null;
|
|
1438
|
+
let bestLength = -1;
|
|
1439
|
+
for (const pattern of patterns) {
|
|
1440
|
+
const base = resolveGlobBase(pattern);
|
|
1441
|
+
const absBase = base.length
|
|
1442
|
+
? resolve(process.cwd(), base)
|
|
1443
|
+
: resolve(process.cwd());
|
|
1444
|
+
const baseComponents = toComponents(absBase);
|
|
1445
|
+
if (!isComponentPrefix(baseComponents, fileComponents))
|
|
1446
|
+
continue;
|
|
1447
|
+
if (baseComponents.length > bestLength) {
|
|
1448
|
+
bestBaseAbs = absBase;
|
|
1449
|
+
bestLength = baseComponents.length;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
if (bestBaseAbs == null)
|
|
1453
|
+
return basename(file);
|
|
1454
|
+
const rel = relative(bestBaseAbs, absFile);
|
|
1455
|
+
return rel.length ? rel : basename(file);
|
|
1456
|
+
}
|
|
1457
|
+
// Compute the artifact path (relative to outDir) for a given spec/fuzz source
|
|
1458
|
+
// file. Strips ".ts" only, keeping ".spec" / ".fuzz" suffixes so spec and
|
|
1459
|
+
// fuzz artifacts can coexist in the same outDir.
|
|
1460
|
+
// assembly/__tests__/array.spec.ts -> "array.spec.wasm"
|
|
1461
|
+
// assembly/__tests__/nested/array.spec.ts -> "nested/array.spec.wasm"
|
|
1462
|
+
// assembly/__fuzz__/nested/array.fuzz.ts -> "nested/array.fuzz.wasm"
|
|
1463
|
+
export function resolveArtifactPath(file, inputPatterns) {
|
|
1464
|
+
const rel = resolveSpecRelativePath(file, inputPatterns);
|
|
1465
|
+
return rel.replace(/\.ts$/i, ".wasm");
|
|
1466
|
+
}
|
|
1467
|
+
function toComponents(absPath) {
|
|
1468
|
+
return absPath
|
|
1469
|
+
.split(/[\\/]+/)
|
|
1470
|
+
.filter((segment, idx) => segment.length || idx == 0);
|
|
1471
|
+
}
|
|
1472
|
+
function isComponentPrefix(prefix, full) {
|
|
1473
|
+
if (prefix.length > full.length)
|
|
1474
|
+
return false;
|
|
1475
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
1476
|
+
if (prefix[i] !== full[i])
|
|
1477
|
+
return false;
|
|
1478
|
+
}
|
|
1479
|
+
return true;
|
|
1480
|
+
}
|
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@ import { BlockStatement, ExpressionStatement, Node, } from "assemblyscript/dist/
|
|
|
2
2
|
import { RangeTransform } from "./range.js";
|
|
3
3
|
import { isStdlib, SimpleParser } from "./util.js";
|
|
4
4
|
import { Visitor } from "./visitor.js";
|
|
5
|
+
import { NodeKind } from "./types.js";
|
|
5
6
|
const COVERAGE_IGNORED_CALLS = new Set([
|
|
6
7
|
"beforeAll",
|
|
7
8
|
"afterAll",
|
|
@@ -114,7 +115,7 @@ export class CoverageTransform extends Visitor {
|
|
|
114
115
|
const coverStmt = createCoverStatement(point.hash, node);
|
|
115
116
|
replacer.visit(coverStmt);
|
|
116
117
|
this.globalStatements.push(registerStmt);
|
|
117
|
-
if (node.kind ==
|
|
118
|
+
if (node.kind == NodeKind.Block) {
|
|
118
119
|
const block = node;
|
|
119
120
|
block.statements.unshift(coverStmt);
|
|
120
121
|
return block;
|
|
@@ -150,7 +151,7 @@ export class CoverageTransform extends Visitor {
|
|
|
150
151
|
this.visit(node.expression, node);
|
|
151
152
|
this.visit(node.typeArguments, node);
|
|
152
153
|
for (const arg of node.args) {
|
|
153
|
-
if (arg.kind ==
|
|
154
|
+
if (arg.kind == NodeKind.Function)
|
|
154
155
|
continue;
|
|
155
156
|
this.visit(arg, node);
|
|
156
157
|
}
|
|
@@ -278,7 +279,7 @@ export class CoverageTransform extends Visitor {
|
|
|
278
279
|
const registerStmt = createRegisterStatement(point);
|
|
279
280
|
replacer.visit(registerStmt);
|
|
280
281
|
this.globalStatements.push(registerStmt);
|
|
281
|
-
if (node.body.kind ===
|
|
282
|
+
if (node.body.kind === NodeKind.Export) {
|
|
282
283
|
const coverStmt = SimpleParser.parseStatement(`{
|
|
283
284
|
__COVER("${point.hash}")
|
|
284
285
|
return $$REPLACE_ME
|
|
@@ -297,10 +298,8 @@ export class CoverageTransform extends Visitor {
|
|
|
297
298
|
node.body.statements.unshift(coverStmt);
|
|
298
299
|
}
|
|
299
300
|
else if (node.body instanceof ExpressionStatement) {
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
const bodyBlock = node.body;
|
|
303
|
-
bodyBlock.statements.unshift(coverStmt);
|
|
301
|
+
const exprBody = node.body;
|
|
302
|
+
exprBody.expression = createCoverExpression(point.hash, exprBody.expression, node);
|
|
304
303
|
}
|
|
305
304
|
this.withScope(point, () => {
|
|
306
305
|
this.visit(node.name, node);
|
|
@@ -327,14 +326,14 @@ export class CoverageTransform extends Visitor {
|
|
|
327
326
|
const ifFalse = node.ifFalse;
|
|
328
327
|
const path = node.range.source.normalizedPath;
|
|
329
328
|
if (ifTrue &&
|
|
330
|
-
ifTrue.kind !==
|
|
329
|
+
ifTrue.kind !== NodeKind.Block &&
|
|
331
330
|
!isBuiltinStatement(ifTrue)) {
|
|
332
331
|
node.ifTrue = this.instrumentStatementBody(path, ifTrue, "IfBranch");
|
|
333
332
|
visitIfTrue = true;
|
|
334
333
|
visitIfFalse = !!ifFalse;
|
|
335
334
|
}
|
|
336
335
|
if (ifFalse &&
|
|
337
|
-
ifFalse.kind !==
|
|
336
|
+
ifFalse.kind !== NodeKind.Block &&
|
|
338
337
|
!isBuiltinStatement(ifFalse)) {
|
|
339
338
|
node.ifFalse = this.instrumentStatementBody(path, ifFalse, "IfBranch");
|
|
340
339
|
visitIfTrue = true;
|
|
@@ -444,6 +443,8 @@ export class CoverageTransform extends Visitor {
|
|
|
444
443
|
super.visitReturnStatement(node);
|
|
445
444
|
if (!node.value || isBuiltinCallExpression(node.value))
|
|
446
445
|
return;
|
|
446
|
+
if (node.value.kind === NodeKind.This)
|
|
447
|
+
return;
|
|
447
448
|
const path = node.range.source.normalizedPath;
|
|
448
449
|
const point = this.createPoint(path, node.value, "Return");
|
|
449
450
|
const replacer = new RangeTransform(node);
|
|
@@ -533,35 +534,35 @@ function getCallName(node) {
|
|
|
533
534
|
return getExpressionName(node.expression);
|
|
534
535
|
}
|
|
535
536
|
function isBuiltinStatement(node) {
|
|
536
|
-
if (node.kind !==
|
|
537
|
+
if (node.kind !== NodeKind.Expression)
|
|
537
538
|
return false;
|
|
538
539
|
return isBuiltinCallExpression(node.expression);
|
|
539
540
|
}
|
|
540
541
|
function isBuiltinCallExpression(node) {
|
|
541
542
|
const unwrapped = unwrapParenthesized(node);
|
|
542
|
-
if (unwrapped.kind !==
|
|
543
|
+
if (unwrapped.kind !== NodeKind.Call)
|
|
543
544
|
return false;
|
|
544
545
|
const call = unwrapped;
|
|
545
546
|
const expression = unwrapParenthesized(call.expression);
|
|
546
|
-
if (expression.kind !==
|
|
547
|
+
if (expression.kind !== NodeKind.Identifier)
|
|
547
548
|
return false;
|
|
548
549
|
const name = expression.text;
|
|
549
550
|
return COVERAGE_IGNORED_BUILTINS.has(name);
|
|
550
551
|
}
|
|
551
552
|
function unwrapParenthesized(node) {
|
|
552
553
|
let current = node;
|
|
553
|
-
while (current.kind ===
|
|
554
|
+
while (current.kind === NodeKind.Parenthesized) {
|
|
554
555
|
current = current.expression;
|
|
555
556
|
}
|
|
556
557
|
return current;
|
|
557
558
|
}
|
|
558
559
|
function getExpressionName(node) {
|
|
559
560
|
switch (node.kind) {
|
|
560
|
-
case
|
|
561
|
+
case NodeKind.Identifier:
|
|
561
562
|
return node.text;
|
|
562
|
-
case
|
|
563
|
+
case NodeKind.PropertyAccess:
|
|
563
564
|
return node.property.text;
|
|
564
|
-
case
|
|
565
|
+
case NodeKind.Parenthesized:
|
|
565
566
|
return getExpressionName(node.expression);
|
|
566
567
|
default:
|
|
567
568
|
return null;
|
package/transform/lib/index.js
CHANGED
|
@@ -27,7 +27,9 @@ export default class Transformer extends Transform {
|
|
|
27
27
|
}
|
|
28
28
|
});
|
|
29
29
|
const entrySource = sources.find((v) => v.sourceKind == 1);
|
|
30
|
-
const entryFile = entrySource
|
|
30
|
+
const entryFile = entrySource
|
|
31
|
+
? entrySource.normalizedPath.replace(/\.ts$/, "")
|
|
32
|
+
: "unknown";
|
|
31
33
|
const mockedImportTargets = collectMockImportTargets(sources);
|
|
32
34
|
for (const target of mockedImportTargets) {
|
|
33
35
|
mock.importMocked.add(target);
|