as-test 1.0.0 → 1.0.3
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 +116 -1
- package/README.md +138 -406
- package/as-test.config.schema.json +210 -17
- package/assembly/__fuzz__/array.fuzz.ts +10 -0
- package/assembly/__fuzz__/bytes.fuzz.ts +8 -0
- package/assembly/__fuzz__/math.fuzz.ts +9 -0
- package/assembly/__fuzz__/string.fuzz.ts +21 -0
- package/assembly/index.ts +141 -86
- package/assembly/src/expectation.ts +104 -19
- package/assembly/src/fuzz.ts +723 -0
- package/assembly/src/log.ts +6 -1
- package/assembly/src/suite.ts +45 -3
- package/assembly/util/json.ts +38 -4
- package/assembly/util/wipc.ts +35 -26
- package/bin/build-worker-pool.js +149 -0
- package/bin/build-worker.js +43 -0
- package/bin/commands/build-core.js +221 -29
- package/bin/commands/build.js +1 -0
- package/bin/commands/fuzz-core.js +306 -0
- package/bin/commands/fuzz.js +10 -0
- package/bin/commands/init-core.js +129 -24
- package/bin/commands/run-core.js +525 -123
- package/bin/commands/run.js +4 -1
- package/bin/commands/test.js +8 -3
- package/bin/commands/web-runner-source.js +634 -0
- package/bin/crash-store.js +64 -0
- package/bin/index.js +1484 -169
- package/bin/reporters/default.js +281 -49
- package/bin/reporters/tap.js +83 -2
- package/bin/types.js +19 -2
- package/bin/util.js +315 -33
- package/bin/wipc.js +79 -0
- package/package.json +19 -9
- package/transform/lib/coverage.js +1 -2
- package/transform/lib/index.js +3 -3
- package/transform/lib/log.js +1 -1
|
@@ -4,7 +4,8 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
|
4
4
|
import * as path from "path";
|
|
5
5
|
import { createInterface } from "readline";
|
|
6
6
|
import { getCliVersion } from "../util.js";
|
|
7
|
-
|
|
7
|
+
import { buildWebRunnerSource } from "./web-runner-source.js";
|
|
8
|
+
const TARGETS = ["wasi", "bindings", "web"];
|
|
8
9
|
const EXAMPLE_MODES = ["minimal", "full", "none"];
|
|
9
10
|
export async function init(rawArgs) {
|
|
10
11
|
const options = parseInitArgs(rawArgs);
|
|
@@ -21,6 +22,7 @@ export async function init(rawArgs) {
|
|
|
21
22
|
root: path.resolve(process.cwd(), options.dir),
|
|
22
23
|
target: options.target ?? "wasi",
|
|
23
24
|
example: options.example ?? "minimal",
|
|
25
|
+
fuzzExample: options.fuzzExample ?? false,
|
|
24
26
|
installDependenciesNow: options.install ?? false,
|
|
25
27
|
}
|
|
26
28
|
: await runInteractiveOnboarding(options, rl);
|
|
@@ -28,7 +30,7 @@ export async function init(rawArgs) {
|
|
|
28
30
|
console.log(chalk.bold.red("◆ Cancelled"));
|
|
29
31
|
return;
|
|
30
32
|
}
|
|
31
|
-
printPlan(answers.root, answers.target, answers.example, answers.installDependenciesNow);
|
|
33
|
+
printPlan(answers.root, answers.target, answers.example, answers.fuzzExample, answers.installDependenciesNow);
|
|
32
34
|
if (!options.yes) {
|
|
33
35
|
const cont = await askYesNo("Continue with these changes?", rl, true);
|
|
34
36
|
if (!cont) {
|
|
@@ -36,7 +38,7 @@ export async function init(rawArgs) {
|
|
|
36
38
|
return;
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
|
-
const summary = applyInit(answers.root, answers.target, answers.example, options.force);
|
|
41
|
+
const summary = applyInit(answers.root, answers.target, answers.example, answers.fuzzExample, options.force);
|
|
40
42
|
printSummary(summary);
|
|
41
43
|
console.log(chalk.bold.green("◆ Finished!"));
|
|
42
44
|
if (answers.installDependenciesNow) {
|
|
@@ -73,6 +75,14 @@ function parseInitArgs(rawArgs) {
|
|
|
73
75
|
options.install = true;
|
|
74
76
|
continue;
|
|
75
77
|
}
|
|
78
|
+
if (arg == "--fuzz-example") {
|
|
79
|
+
options.fuzzExample = true;
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (arg == "--no-fuzz-example") {
|
|
83
|
+
options.fuzzExample = false;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
76
86
|
if (arg == "--target") {
|
|
77
87
|
const next = rawArgs[i + 1];
|
|
78
88
|
if (next && !next.startsWith("-")) {
|
|
@@ -80,7 +90,7 @@ function parseInitArgs(rawArgs) {
|
|
|
80
90
|
i++;
|
|
81
91
|
continue;
|
|
82
92
|
}
|
|
83
|
-
throw new Error("--target requires a value: wasi|bindings");
|
|
93
|
+
throw new Error("--target requires a value: wasi|bindings|web");
|
|
84
94
|
}
|
|
85
95
|
if (arg.startsWith("--target=")) {
|
|
86
96
|
options.target = parseTarget(arg.slice("--target=".length));
|
|
@@ -133,7 +143,7 @@ function parseInitArgs(rawArgs) {
|
|
|
133
143
|
options.example = positional.shift();
|
|
134
144
|
}
|
|
135
145
|
if (positional.length > 0) {
|
|
136
|
-
throw new Error(`Unknown init argument(s): ${positional.join(", ")}. Usage: init [dir] [--target wasi|bindings] [--example minimal|full|none] [--install] [--yes] [--force] [--dir <path>]`);
|
|
146
|
+
throw new Error(`Unknown init argument(s): ${positional.join(", ")}. Usage: init [dir] [--target wasi|bindings|web] [--example minimal|full|none] [--fuzz-example|--no-fuzz-example] [--install] [--yes] [--force] [--dir <path>]`);
|
|
137
147
|
}
|
|
138
148
|
return options;
|
|
139
149
|
}
|
|
@@ -176,6 +186,10 @@ async function runInteractiveOnboarding(options, face) {
|
|
|
176
186
|
value: "bindings",
|
|
177
187
|
label: "bindings (default runner: node .as-test/runners/default.bindings.js)",
|
|
178
188
|
},
|
|
189
|
+
{
|
|
190
|
+
value: "web",
|
|
191
|
+
label: "web (default runner: node .as-test/runners/default.web.js <file>)",
|
|
192
|
+
},
|
|
179
193
|
], face, "wasi"));
|
|
180
194
|
if (options.target || onboardingMode == "quick") {
|
|
181
195
|
printPromptAndSelectionLine("Build target", target);
|
|
@@ -191,6 +205,13 @@ async function runInteractiveOnboarding(options, face) {
|
|
|
191
205
|
if (options.example || onboardingMode == "quick") {
|
|
192
206
|
printPromptAndSelectionLine("Example template", example);
|
|
193
207
|
}
|
|
208
|
+
const fuzzExample = options.fuzzExample ??
|
|
209
|
+
(onboardingMode == "quick"
|
|
210
|
+
? false
|
|
211
|
+
: await askYesNo("Add a basic fuzzer example?", face, false));
|
|
212
|
+
if (options.fuzzExample !== undefined || onboardingMode == "quick") {
|
|
213
|
+
printPromptAndSelectionLine("Add a basic fuzzer example?", fuzzExample ? "Yes" : "No");
|
|
214
|
+
}
|
|
194
215
|
const installDependenciesNow = options.install ??
|
|
195
216
|
(onboardingMode == "quick"
|
|
196
217
|
? false
|
|
@@ -202,6 +223,7 @@ async function runInteractiveOnboarding(options, face) {
|
|
|
202
223
|
root: resolvedRoot,
|
|
203
224
|
target,
|
|
204
225
|
example,
|
|
226
|
+
fuzzExample,
|
|
205
227
|
installDependenciesNow,
|
|
206
228
|
};
|
|
207
229
|
}
|
|
@@ -213,9 +235,9 @@ function printOnboardingHeader() {
|
|
|
213
235
|
// );
|
|
214
236
|
}
|
|
215
237
|
function printOnboardingIntro() {
|
|
216
|
-
console.log(chalk.
|
|
217
|
-
console.log(chalk.
|
|
218
|
-
console.log(chalk.
|
|
238
|
+
console.log(chalk.bold.blue("╔═╗ ╔═╗ ╔═╗ ╔═╗ ╔═╗ ╔═╗"));
|
|
239
|
+
console.log(chalk.bold.blue("╠═╣ ╚═╗ ══ ║ ╠═ ╚═╗ ║ "));
|
|
240
|
+
console.log(chalk.bold.blue("╩ ╩ ╚═╝ ╩ ╚═╝ ╚═╝ ╩ "));
|
|
219
241
|
console.log("");
|
|
220
242
|
// console.log(chalk.bold("┌") + " " + chalk.bold.blueBright(""));
|
|
221
243
|
// console.log("│");
|
|
@@ -284,7 +306,7 @@ function printSelectionLine(answer) {
|
|
|
284
306
|
}
|
|
285
307
|
function parseTarget(value) {
|
|
286
308
|
if (!isTarget(value)) {
|
|
287
|
-
throw new Error(`Invalid target "${value}". Expected wasi|bindings`);
|
|
309
|
+
throw new Error(`Invalid target "${value}". Expected wasi|bindings|web`);
|
|
288
310
|
}
|
|
289
311
|
return value;
|
|
290
312
|
}
|
|
@@ -300,7 +322,7 @@ function isTarget(value) {
|
|
|
300
322
|
function isExampleMode(value) {
|
|
301
323
|
return EXAMPLE_MODES.includes(value);
|
|
302
324
|
}
|
|
303
|
-
function printPlan(root, target, example, install) {
|
|
325
|
+
function printPlan(root, target, example, fuzzExample, install) {
|
|
304
326
|
const displayRoot = () => {
|
|
305
327
|
const rel = path.relative(process.cwd(), root).split(path.sep).join("/");
|
|
306
328
|
if (!rel || rel == ".")
|
|
@@ -363,11 +385,12 @@ function printPlan(root, target, example, install) {
|
|
|
363
385
|
{ path: ".as-test/coverage", isDir: true },
|
|
364
386
|
{ path: ".as-test/snapshots", isDir: true },
|
|
365
387
|
{ path: "assembly", isDir: true },
|
|
388
|
+
{ path: "assembly/tsconfig.json", isDir: false },
|
|
366
389
|
{ path: "assembly/__tests__", isDir: true },
|
|
367
390
|
{ path: "as-test.config.json", isDir: false },
|
|
368
391
|
{ path: "package.json", isDir: false },
|
|
369
392
|
];
|
|
370
|
-
if (target == "wasi" || target == "bindings") {
|
|
393
|
+
if (target == "wasi" || target == "bindings" || target == "web") {
|
|
371
394
|
fileEntries.push({ path: ".as-test/runners", isDir: true });
|
|
372
395
|
fileEntries.push({
|
|
373
396
|
path: ".as-test/runners/default.bindings.js",
|
|
@@ -377,6 +400,10 @@ function printPlan(root, target, example, install) {
|
|
|
377
400
|
path: ".as-test/runners/default.wasi.js",
|
|
378
401
|
isDir: false,
|
|
379
402
|
});
|
|
403
|
+
fileEntries.push({
|
|
404
|
+
path: ".as-test/runners/default.web.js",
|
|
405
|
+
isDir: false,
|
|
406
|
+
});
|
|
380
407
|
}
|
|
381
408
|
if (example != "none") {
|
|
382
409
|
fileEntries.push({
|
|
@@ -384,10 +411,18 @@ function printPlan(root, target, example, install) {
|
|
|
384
411
|
isDir: false,
|
|
385
412
|
});
|
|
386
413
|
}
|
|
414
|
+
if (fuzzExample) {
|
|
415
|
+
fileEntries.push({ path: "assembly/__fuzz__", isDir: true });
|
|
416
|
+
fileEntries.push({
|
|
417
|
+
path: "assembly/__fuzz__/example.fuzz.ts",
|
|
418
|
+
isDir: false,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
387
421
|
const treeRoot = buildTree(fileEntries);
|
|
388
422
|
console.log(chalk.bold.blue("◇ Planned Changes"));
|
|
389
423
|
console.log("│" + chalk.dim(` - Target: ${target}`));
|
|
390
424
|
console.log("│" + chalk.dim(` - Example: ${example}`));
|
|
425
|
+
console.log("│" + chalk.dim(` - Fuzzer example: ${fuzzExample ? "yes" : "no"}`));
|
|
391
426
|
console.log("│" + chalk.dim(` - Directory: ${displayRoot()}`));
|
|
392
427
|
console.log("│" + chalk.dim(` - Install dependencies: ${install ? "yes" : "no"}`));
|
|
393
428
|
console.log("│" + chalk.bold.blue(" File Changes"));
|
|
@@ -397,7 +432,7 @@ function printPlan(root, target, example, install) {
|
|
|
397
432
|
}
|
|
398
433
|
console.log("│");
|
|
399
434
|
}
|
|
400
|
-
function applyInit(root, target, example, force) {
|
|
435
|
+
function applyInit(root, target, example, fuzzExample, force) {
|
|
401
436
|
const summary = {
|
|
402
437
|
created: [],
|
|
403
438
|
updated: [],
|
|
@@ -408,18 +443,34 @@ function applyInit(root, target, example, force) {
|
|
|
408
443
|
ensureDir(root, ".as-test/coverage", summary);
|
|
409
444
|
ensureDir(root, ".as-test/snapshots", summary);
|
|
410
445
|
ensureDir(root, "assembly/__tests__", summary);
|
|
411
|
-
if (
|
|
446
|
+
if (fuzzExample) {
|
|
447
|
+
ensureDir(root, "assembly/__fuzz__", summary);
|
|
448
|
+
}
|
|
449
|
+
if (target == "wasi" || target == "bindings" || target == "web") {
|
|
412
450
|
ensureDir(root, ".as-test/runners", summary);
|
|
413
451
|
}
|
|
414
452
|
ensureGitignoreIncludesAsTestDirs(root, summary);
|
|
453
|
+
writeJson(path.join(root, "assembly/tsconfig.json"), buildAssemblyTsconfig(), summary, "assembly/tsconfig.json");
|
|
415
454
|
const configPath = path.join(root, "as-test.config.json");
|
|
416
455
|
const config = {
|
|
417
456
|
$schema: "node_modules/as-test/as-test.config.schema.json",
|
|
418
457
|
input: ["assembly/__tests__/*.spec.ts"],
|
|
419
458
|
output: ".as-test/",
|
|
420
459
|
config: "none",
|
|
421
|
-
coverage:
|
|
460
|
+
coverage: false,
|
|
422
461
|
env: {},
|
|
462
|
+
...(fuzzExample
|
|
463
|
+
? {
|
|
464
|
+
fuzz: {
|
|
465
|
+
input: ["assembly/__fuzz__/*.fuzz.ts"],
|
|
466
|
+
runs: 1000,
|
|
467
|
+
seed: 1337,
|
|
468
|
+
target: "bindings",
|
|
469
|
+
corpusDir: ".as-test/corpus",
|
|
470
|
+
crashDir: ".as-test/crashes",
|
|
471
|
+
},
|
|
472
|
+
}
|
|
473
|
+
: {}),
|
|
423
474
|
buildOptions: {
|
|
424
475
|
target,
|
|
425
476
|
},
|
|
@@ -427,11 +478,23 @@ function applyInit(root, target, example, force) {
|
|
|
427
478
|
runtime: {
|
|
428
479
|
cmd: target == "wasi"
|
|
429
480
|
? "node .as-test/runners/default.wasi.js <file>"
|
|
430
|
-
:
|
|
481
|
+
: target == "bindings"
|
|
482
|
+
? "node .as-test/runners/default.bindings.js <file>"
|
|
483
|
+
: "node .as-test/runners/default.web.js <file>",
|
|
431
484
|
},
|
|
432
485
|
reporter: "default",
|
|
433
486
|
},
|
|
434
|
-
modes:
|
|
487
|
+
modes: target == "web"
|
|
488
|
+
? {
|
|
489
|
+
"web-headless": {
|
|
490
|
+
runOptions: {
|
|
491
|
+
runtime: {
|
|
492
|
+
cmd: "node .as-test/runners/default.web.js --headless <file>",
|
|
493
|
+
},
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
}
|
|
497
|
+
: {},
|
|
435
498
|
};
|
|
436
499
|
writeJson(configPath, config, summary, "as-test.config.json");
|
|
437
500
|
if (example != "none") {
|
|
@@ -439,14 +502,22 @@ function applyInit(root, target, example, force) {
|
|
|
439
502
|
const content = example == "minimal" ? buildMinimalExampleSpec() : buildFullExampleSpec();
|
|
440
503
|
writeManagedFile(examplePath, content, force, summary, "assembly/__tests__/example.spec.ts");
|
|
441
504
|
}
|
|
442
|
-
if (
|
|
505
|
+
if (fuzzExample) {
|
|
506
|
+
const fuzzPath = path.join(root, "assembly/__fuzz__/example.fuzz.ts");
|
|
507
|
+
writeManagedFile(fuzzPath, buildBasicFuzzerExample(), force, summary, "assembly/__fuzz__/example.fuzz.ts");
|
|
508
|
+
}
|
|
509
|
+
if (target == "wasi" || target == "bindings" || target == "web") {
|
|
443
510
|
const runnerPath = path.join(root, ".as-test/runners/default.wasi.js");
|
|
444
511
|
writeManagedFile(runnerPath, buildWasiRunner(), force, summary, ".as-test/runners/default.wasi.js");
|
|
445
512
|
}
|
|
446
|
-
if (target == "wasi" || target == "bindings") {
|
|
513
|
+
if (target == "wasi" || target == "bindings" || target == "web") {
|
|
447
514
|
const runnerPath = path.join(root, ".as-test/runners/default.bindings.js");
|
|
448
515
|
writeManagedFile(runnerPath, buildBindingsRunner(), force, summary, ".as-test/runners/default.bindings.js");
|
|
449
516
|
}
|
|
517
|
+
if (target == "wasi" || target == "bindings" || target == "web") {
|
|
518
|
+
const runnerPath = path.join(root, ".as-test/runners/default.web.js");
|
|
519
|
+
writeManagedFile(runnerPath, buildWebRunnerSource(), force, summary, ".as-test/runners/default.web.js");
|
|
520
|
+
}
|
|
450
521
|
const pkgPath = path.join(root, "package.json");
|
|
451
522
|
const pkg = existsSync(pkgPath)
|
|
452
523
|
? JSON.parse(readFileSync(pkgPath, "utf8"))
|
|
@@ -458,6 +529,9 @@ function applyInit(root, target, example, force) {
|
|
|
458
529
|
if (!scripts.test) {
|
|
459
530
|
scripts.test = "ast test";
|
|
460
531
|
}
|
|
532
|
+
if (fuzzExample && !scripts.fuzz) {
|
|
533
|
+
scripts.fuzz = "ast fuzz";
|
|
534
|
+
}
|
|
461
535
|
if (!pkg.type) {
|
|
462
536
|
pkg.type = "module";
|
|
463
537
|
}
|
|
@@ -501,7 +575,13 @@ function ensureDir(root, rel, summary) {
|
|
|
501
575
|
function ensureGitignoreIncludesAsTestDirs(root, summary) {
|
|
502
576
|
const rel = ".gitignore";
|
|
503
577
|
const fullPath = path.join(root, rel);
|
|
504
|
-
const entries = [
|
|
578
|
+
const entries = [
|
|
579
|
+
"# Include essential as-test artifacts",
|
|
580
|
+
"!.as-test/",
|
|
581
|
+
".as-test/*",
|
|
582
|
+
"!.as-test/runners/",
|
|
583
|
+
"!.as-test/snapshots/",
|
|
584
|
+
];
|
|
505
585
|
const existed = existsSync(fullPath);
|
|
506
586
|
const source = existed ? readFileSync(fullPath, "utf8") : "";
|
|
507
587
|
const lines = source.split(/\r?\n/);
|
|
@@ -521,6 +601,12 @@ function ensureGitignoreIncludesAsTestDirs(root, summary) {
|
|
|
521
601
|
else
|
|
522
602
|
summary.created.push(rel);
|
|
523
603
|
}
|
|
604
|
+
function buildAssemblyTsconfig() {
|
|
605
|
+
return {
|
|
606
|
+
extends: "assemblyscript/std/assembly.json",
|
|
607
|
+
include: ["./**/*.ts"],
|
|
608
|
+
};
|
|
609
|
+
}
|
|
524
610
|
function writeJson(fullPath, value, summary, displayPath) {
|
|
525
611
|
const rel = displayPath ??
|
|
526
612
|
path.relative(process.cwd(), fullPath) ??
|
|
@@ -827,19 +913,17 @@ function resolveInstallCommand(root) {
|
|
|
827
913
|
return { command: "npm", args: ["install"] };
|
|
828
914
|
}
|
|
829
915
|
function buildMinimalExampleSpec() {
|
|
830
|
-
return `import { describe, expect, test
|
|
916
|
+
return `import { describe, expect, test } from "as-test";
|
|
831
917
|
|
|
832
918
|
describe("example", () => {
|
|
833
919
|
test("adds numbers", () => {
|
|
834
920
|
expect(1 + 2).toBe(3);
|
|
835
921
|
});
|
|
836
922
|
});
|
|
837
|
-
|
|
838
|
-
run();
|
|
839
923
|
`;
|
|
840
924
|
}
|
|
841
925
|
function buildFullExampleSpec() {
|
|
842
|
-
return `import { afterAll, beforeAll, describe, expect, it, log,
|
|
926
|
+
return `import { afterAll, beforeAll, describe, expect, it, log, test } from "as-test";
|
|
843
927
|
|
|
844
928
|
beforeAll(() => {
|
|
845
929
|
log("setup");
|
|
@@ -869,8 +953,24 @@ describe("strings", () => {
|
|
|
869
953
|
expect("as-test").toStartWith("as");
|
|
870
954
|
});
|
|
871
955
|
});
|
|
956
|
+
`;
|
|
957
|
+
}
|
|
958
|
+
function buildBasicFuzzerExample() {
|
|
959
|
+
return `import { expect, fuzz, FuzzSeed } from "as-test";
|
|
872
960
|
|
|
873
|
-
|
|
961
|
+
fuzz("basic string fuzzer", (value: string): bool => {
|
|
962
|
+
expect(value.length >= 0).toBe(true);
|
|
963
|
+
return value.length <= 24;
|
|
964
|
+
}).generate((seed: FuzzSeed, run: (value: string) => bool): void => {
|
|
965
|
+
run(
|
|
966
|
+
seed.string({
|
|
967
|
+
charset: "ascii",
|
|
968
|
+
min: 0,
|
|
969
|
+
max: 24,
|
|
970
|
+
exclude: [0x00, 0x0a, 0x0d],
|
|
971
|
+
}),
|
|
972
|
+
);
|
|
973
|
+
});
|
|
874
974
|
`;
|
|
875
975
|
}
|
|
876
976
|
function buildWasiRunner() {
|
|
@@ -909,6 +1009,11 @@ try {
|
|
|
909
1009
|
const binary = readFileSync(wasmPath);
|
|
910
1010
|
const module = new WebAssembly.Module(binary);
|
|
911
1011
|
const instance = new WebAssembly.Instance(module, {
|
|
1012
|
+
env: {
|
|
1013
|
+
__as_test_request_fuzz_config() {
|
|
1014
|
+
return 0;
|
|
1015
|
+
},
|
|
1016
|
+
},
|
|
912
1017
|
wasi_snapshot_preview1: wasi.wasiImport,
|
|
913
1018
|
});
|
|
914
1019
|
wasi.start(instance);
|