as-test 1.0.13 → 1.0.15
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 +12 -0
- package/README.md +30 -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 +4 -3
- package/bin/index.js +38 -22
- package/bin/types.js +1 -3
- package/bin/util.js +346 -255
- package/package.json +1 -1
package/bin/util.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "fs";
|
|
2
|
-
import { BuildOptions, Config, CoverageOptions, FuzzConfig, ModeConfig, ReporterConfig, RunOptions, Runtime, } from "./types.js";
|
|
2
|
+
import { BuildOptions, Config, CoverageOptions, CoverageIgnoreOptions, FuzzConfig, ModeConfig, ReporterConfig, RunOptions, Runtime, } from "./types.js";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { createRequire } from "module";
|
|
5
5
|
import { delimiter, dirname, join, resolve } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
|
+
const CONFIG_META = new WeakMap();
|
|
7
8
|
export function formatTime(ms) {
|
|
8
9
|
if (ms < 0) {
|
|
9
10
|
throw new Error("Time should be a non-negative number.");
|
|
@@ -28,110 +29,121 @@ export function formatTime(ms) {
|
|
|
28
29
|
return `${us}us`;
|
|
29
30
|
}
|
|
30
31
|
export function loadConfig(CONFIG_PATH, warn = false) {
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
const resolvedPath = resolve(CONFIG_PATH);
|
|
33
|
+
const raw = readConfigRaw(resolvedPath, warn);
|
|
34
|
+
return parseConfigRaw(raw, resolvedPath);
|
|
35
|
+
}
|
|
36
|
+
function readConfigRaw(configPath, warn) {
|
|
37
|
+
if (!existsSync(configPath)) {
|
|
38
|
+
if (warn) {
|
|
33
39
|
console.log(`${chalk.bgMagentaBright(" WARN ")}${chalk.dim(":")} Could not locate config file in the current directory! Continuing with default config.`);
|
|
34
|
-
return new Config();
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
const rawText = readFileSync(CONFIG_PATH, "utf8");
|
|
38
|
-
let parsed;
|
|
39
|
-
try {
|
|
40
|
-
parsed = JSON.parse(rawText);
|
|
41
40
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
config
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
const rawText = readFileSync(configPath, "utf8");
|
|
44
|
+
let parsed;
|
|
45
|
+
try {
|
|
46
|
+
parsed = JSON.parse(rawText);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
50
|
+
throw new Error(`invalid config JSON at ${configPath}\n${message}\nfix JSON syntax and rerun.`);
|
|
51
|
+
}
|
|
52
|
+
if (!parsed || typeof parsed != "object" || Array.isArray(parsed)) {
|
|
53
|
+
throw new Error(`invalid config at ${configPath}\nroot value must be an object. Example: { "input": ["./assembly/__tests__/*.spec.ts"] }`);
|
|
54
|
+
}
|
|
55
|
+
const raw = parsed;
|
|
56
|
+
validateConfig(raw, configPath);
|
|
57
|
+
return raw;
|
|
58
|
+
}
|
|
59
|
+
function parseConfigRaw(raw, configPath) {
|
|
60
|
+
const configDir = dirname(configPath);
|
|
61
|
+
const config = Object.assign(new Config(), raw);
|
|
62
|
+
applyOutputConfig(raw.output, raw, config);
|
|
63
|
+
config.env = parseEnvValue(raw.env, configDir, "$.env");
|
|
64
|
+
const runOptionsRaw = raw.runOptions ?? {};
|
|
65
|
+
config.buildOptions = Object.assign(new BuildOptions(), raw.buildOptions ?? {});
|
|
66
|
+
config.buildOptions.cmd =
|
|
67
|
+
typeof config.buildOptions.cmd == "string" ? config.buildOptions.cmd : "";
|
|
68
|
+
config.buildOptions.args = Array.isArray(config.buildOptions.args)
|
|
69
|
+
? config.buildOptions.args.filter((item) => typeof item == "string")
|
|
70
|
+
: [];
|
|
71
|
+
config.buildOptions.env = parseEnvValue(raw.buildOptions?.env, configDir, "$.buildOptions.env");
|
|
72
|
+
config.buildOptions.target =
|
|
73
|
+
typeof config.buildOptions.target == "string" &&
|
|
74
|
+
config.buildOptions.target.length
|
|
75
|
+
? config.buildOptions.target
|
|
76
|
+
: "wasi";
|
|
77
|
+
config.runOptions = Object.assign(new RunOptions(), runOptionsRaw);
|
|
78
|
+
const reporterRaw = runOptionsRaw.reporter;
|
|
79
|
+
if (typeof reporterRaw == "string") {
|
|
80
|
+
config.runOptions.reporter = reporterRaw;
|
|
81
|
+
}
|
|
82
|
+
else if (reporterRaw && typeof reporterRaw == "object") {
|
|
83
|
+
const reporterConfig = Object.assign(new ReporterConfig(), reporterRaw);
|
|
84
|
+
reporterConfig.name =
|
|
85
|
+
typeof reporterConfig.name == "string" ? reporterConfig.name : "";
|
|
86
|
+
reporterConfig.options = Array.isArray(reporterConfig.options)
|
|
87
|
+
? reporterConfig.options.filter((value) => typeof value == "string")
|
|
61
88
|
: [];
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
: "wasi";
|
|
68
|
-
config.runOptions = Object.assign(new RunOptions(), runOptionsRaw);
|
|
69
|
-
const reporterRaw = runOptionsRaw.reporter;
|
|
70
|
-
if (typeof reporterRaw == "string") {
|
|
71
|
-
config.runOptions.reporter = reporterRaw;
|
|
72
|
-
}
|
|
73
|
-
else if (reporterRaw && typeof reporterRaw == "object") {
|
|
74
|
-
const reporterConfig = Object.assign(new ReporterConfig(), reporterRaw);
|
|
75
|
-
reporterConfig.name =
|
|
76
|
-
typeof reporterConfig.name == "string" ? reporterConfig.name : "";
|
|
77
|
-
reporterConfig.options = Array.isArray(reporterConfig.options)
|
|
78
|
-
? reporterConfig.options.filter((value) => typeof value == "string")
|
|
79
|
-
: [];
|
|
80
|
-
reporterConfig.outDir =
|
|
81
|
-
typeof reporterConfig.outDir == "string" ? reporterConfig.outDir : "";
|
|
82
|
-
reporterConfig.outFile =
|
|
83
|
-
typeof reporterConfig.outFile == "string" ? reporterConfig.outFile : "";
|
|
84
|
-
config.runOptions.reporter = reporterConfig;
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
config.runOptions.reporter = "";
|
|
88
|
-
}
|
|
89
|
-
const runtimeRaw = runOptionsRaw.runtime;
|
|
90
|
-
const runtime = new Runtime();
|
|
91
|
-
const legacyRun = typeof runOptionsRaw.run == "string" && runOptionsRaw.run.length
|
|
92
|
-
? runOptionsRaw.run
|
|
93
|
-
: "";
|
|
94
|
-
const cmd = runtimeRaw && typeof runtimeRaw.cmd == "string" && runtimeRaw.cmd.length
|
|
95
|
-
? runtimeRaw.cmd
|
|
96
|
-
: runtimeRaw &&
|
|
97
|
-
typeof runtimeRaw.run == "string" &&
|
|
98
|
-
runtimeRaw.run.length
|
|
99
|
-
? runtimeRaw.run
|
|
100
|
-
: legacyRun
|
|
101
|
-
? legacyRun
|
|
102
|
-
: runtime.cmd;
|
|
103
|
-
runtime.cmd = cmd;
|
|
104
|
-
runtime.browser =
|
|
105
|
-
runtimeRaw && typeof runtimeRaw.browser == "string"
|
|
106
|
-
? runtimeRaw.browser
|
|
107
|
-
: "";
|
|
108
|
-
config.runOptions.runtime = runtime;
|
|
109
|
-
config.runOptions.env = parseEnvValue(runOptionsRaw.env, configDir, "$.runOptions.env");
|
|
110
|
-
const fuzzRaw = raw.fuzz ?? {};
|
|
111
|
-
config.fuzz = Object.assign(new FuzzConfig(), fuzzRaw);
|
|
112
|
-
config.fuzz.input = Array.isArray(config.fuzz.input)
|
|
113
|
-
? config.fuzz.input.filter((item) => typeof item == "string")
|
|
114
|
-
: typeof fuzzRaw.input == "string"
|
|
115
|
-
? [fuzzRaw.input]
|
|
116
|
-
: new FuzzConfig().input;
|
|
117
|
-
config.fuzz.runs = normalizePositiveNumber(config.fuzz.runs, 1000);
|
|
118
|
-
config.fuzz.seed = normalizeNonNegativeNumber(config.fuzz.seed, -1);
|
|
119
|
-
config.fuzz.maxInputBytes = normalizePositiveNumber(config.fuzz.maxInputBytes, 4096);
|
|
120
|
-
config.fuzz.target =
|
|
121
|
-
typeof config.fuzz.target == "string" && config.fuzz.target.length
|
|
122
|
-
? config.fuzz.target
|
|
123
|
-
: "bindings";
|
|
124
|
-
config.fuzz.corpusDir =
|
|
125
|
-
typeof config.fuzz.corpusDir == "string" && config.fuzz.corpusDir.length
|
|
126
|
-
? config.fuzz.corpusDir
|
|
127
|
-
: "./.as-test/fuzz/corpus";
|
|
128
|
-
config.fuzz.crashDir =
|
|
129
|
-
typeof config.fuzz.crashDir == "string" && config.fuzz.crashDir.length
|
|
130
|
-
? config.fuzz.crashDir
|
|
131
|
-
: "./.as-test/crashes";
|
|
132
|
-
config.modes = parseModes(raw.modes, configDir);
|
|
133
|
-
return config;
|
|
89
|
+
reporterConfig.outDir =
|
|
90
|
+
typeof reporterConfig.outDir == "string" ? reporterConfig.outDir : "";
|
|
91
|
+
reporterConfig.outFile =
|
|
92
|
+
typeof reporterConfig.outFile == "string" ? reporterConfig.outFile : "";
|
|
93
|
+
config.runOptions.reporter = reporterConfig;
|
|
134
94
|
}
|
|
95
|
+
else {
|
|
96
|
+
config.runOptions.reporter = "";
|
|
97
|
+
}
|
|
98
|
+
const runtimeRaw = runOptionsRaw.runtime;
|
|
99
|
+
const runtime = new Runtime();
|
|
100
|
+
const legacyRun = typeof runOptionsRaw.run == "string" && runOptionsRaw.run.length
|
|
101
|
+
? runOptionsRaw.run
|
|
102
|
+
: "";
|
|
103
|
+
const cmd = runtimeRaw && typeof runtimeRaw.cmd == "string" && runtimeRaw.cmd.length
|
|
104
|
+
? runtimeRaw.cmd
|
|
105
|
+
: runtimeRaw &&
|
|
106
|
+
typeof runtimeRaw.run == "string" &&
|
|
107
|
+
runtimeRaw.run.length
|
|
108
|
+
? runtimeRaw.run
|
|
109
|
+
: legacyRun
|
|
110
|
+
? legacyRun
|
|
111
|
+
: runtime.cmd;
|
|
112
|
+
runtime.cmd = cmd;
|
|
113
|
+
runtime.browser =
|
|
114
|
+
runtimeRaw && typeof runtimeRaw.browser == "string"
|
|
115
|
+
? runtimeRaw.browser
|
|
116
|
+
: "";
|
|
117
|
+
config.runOptions.runtime = runtime;
|
|
118
|
+
config.runOptions.env = parseEnvValue(runOptionsRaw.env, configDir, "$.runOptions.env");
|
|
119
|
+
const fuzzRaw = raw.fuzz ?? {};
|
|
120
|
+
config.fuzz = Object.assign(new FuzzConfig(), fuzzRaw);
|
|
121
|
+
config.fuzz.input = Array.isArray(config.fuzz.input)
|
|
122
|
+
? config.fuzz.input.filter((item) => typeof item == "string")
|
|
123
|
+
: typeof fuzzRaw.input == "string"
|
|
124
|
+
? [fuzzRaw.input]
|
|
125
|
+
: new FuzzConfig().input;
|
|
126
|
+
config.fuzz.runs = normalizePositiveNumber(config.fuzz.runs, 1000);
|
|
127
|
+
config.fuzz.seed = normalizeNonNegativeNumber(config.fuzz.seed, -1);
|
|
128
|
+
config.fuzz.maxInputBytes = normalizePositiveNumber(config.fuzz.maxInputBytes, 4096);
|
|
129
|
+
config.fuzz.target =
|
|
130
|
+
typeof config.fuzz.target == "string" && config.fuzz.target.length
|
|
131
|
+
? config.fuzz.target
|
|
132
|
+
: "bindings";
|
|
133
|
+
config.fuzz.corpusDir =
|
|
134
|
+
typeof config.fuzz.corpusDir == "string" && config.fuzz.corpusDir.length
|
|
135
|
+
? config.fuzz.corpusDir
|
|
136
|
+
: "./.as-test/fuzz/corpus";
|
|
137
|
+
config.fuzz.crashDir =
|
|
138
|
+
typeof config.fuzz.crashDir == "string" && config.fuzz.crashDir.length
|
|
139
|
+
? config.fuzz.crashDir
|
|
140
|
+
: "./.as-test/crashes";
|
|
141
|
+
config.modes = parseModes(raw.modes, configDir);
|
|
142
|
+
CONFIG_META.set(config, {
|
|
143
|
+
sourcePath: configPath,
|
|
144
|
+
raw,
|
|
145
|
+
});
|
|
146
|
+
return config;
|
|
135
147
|
}
|
|
136
148
|
const TOP_LEVEL_KEYS = new Set([
|
|
137
149
|
"$schema",
|
|
@@ -163,17 +175,7 @@ const FUZZ_OPTION_KEYS = new Set([
|
|
|
163
175
|
"corpusDir",
|
|
164
176
|
"crashDir",
|
|
165
177
|
]);
|
|
166
|
-
const MODE_KEYS = new Set([
|
|
167
|
-
"outDir",
|
|
168
|
-
"logs",
|
|
169
|
-
"coverageDir",
|
|
170
|
-
"snapshotDir",
|
|
171
|
-
"config",
|
|
172
|
-
"coverage",
|
|
173
|
-
"buildOptions",
|
|
174
|
-
"runOptions",
|
|
175
|
-
"env",
|
|
176
|
-
]);
|
|
178
|
+
const MODE_KEYS = new Set([...TOP_LEVEL_KEYS].filter((key) => key != "modes"));
|
|
177
179
|
function validateConfig(raw, configPath) {
|
|
178
180
|
const issues = [];
|
|
179
181
|
validateUnknownKeys(raw, TOP_LEVEL_KEYS, "$", issues);
|
|
@@ -629,22 +631,36 @@ function validateModesField(raw, key, pathPrefix, issues) {
|
|
|
629
631
|
return;
|
|
630
632
|
}
|
|
631
633
|
for (const [modeName, modeRaw] of Object.entries(value)) {
|
|
634
|
+
if (typeof modeRaw == "string") {
|
|
635
|
+
if (!modeRaw.length) {
|
|
636
|
+
issues.push({
|
|
637
|
+
path: `${pathPrefix}.${key}.${modeName}`,
|
|
638
|
+
message: "must not be an empty string",
|
|
639
|
+
fix: 'set to a config file path like "./as-test.config.simd.json"',
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
632
644
|
if (!modeRaw || typeof modeRaw != "object" || Array.isArray(modeRaw)) {
|
|
633
645
|
issues.push({
|
|
634
646
|
path: `${pathPrefix}.${key}.${modeName}`,
|
|
635
|
-
message: "must be
|
|
647
|
+
message: "must be a config object or config file path string",
|
|
636
648
|
});
|
|
637
649
|
continue;
|
|
638
650
|
}
|
|
639
651
|
const modeObj = modeRaw;
|
|
640
652
|
const modePath = `${pathPrefix}.${key}.${modeName}`;
|
|
641
653
|
validateUnknownKeys(modeObj, MODE_KEYS, modePath, issues);
|
|
654
|
+
validateStringField(modeObj, "$schema", modePath, issues);
|
|
655
|
+
validateInputField(modeObj, "input", modePath, issues);
|
|
656
|
+
validateOutputField(modeObj, "output", modePath, issues);
|
|
642
657
|
validateStringField(modeObj, "outDir", modePath, issues);
|
|
643
658
|
validateStringField(modeObj, "logs", modePath, issues);
|
|
644
659
|
validateStringField(modeObj, "coverageDir", modePath, issues);
|
|
645
660
|
validateStringField(modeObj, "snapshotDir", modePath, issues);
|
|
646
661
|
validateStringField(modeObj, "config", modePath, issues);
|
|
647
662
|
validateCoverageField(modeObj, "coverage", modePath, issues);
|
|
663
|
+
validateFuzzField(modeObj, "fuzz", modePath, issues);
|
|
648
664
|
validateEnvField(modeObj, "env", modePath, issues);
|
|
649
665
|
validateBuildOptionsField(modeObj, "buildOptions", modePath, issues);
|
|
650
666
|
validateRunOptionsField(modeObj, "runOptions", modePath, issues);
|
|
@@ -774,84 +790,16 @@ function parseModes(raw, configDir) {
|
|
|
774
790
|
const out = {};
|
|
775
791
|
const entries = Object.entries(raw);
|
|
776
792
|
for (const [name, value] of entries) {
|
|
777
|
-
if (!value || typeof value != "object" || Array.isArray(value))
|
|
778
|
-
continue;
|
|
779
|
-
const modeRaw = value;
|
|
780
793
|
const mode = new ModeConfig();
|
|
781
|
-
if (typeof
|
|
782
|
-
mode.
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
}
|
|
787
|
-
if (typeof modeRaw.coverageDir == "string" && modeRaw.coverageDir.length) {
|
|
788
|
-
mode.coverageDir = modeRaw.coverageDir;
|
|
789
|
-
}
|
|
790
|
-
if (typeof modeRaw.snapshotDir == "string" && modeRaw.snapshotDir.length) {
|
|
791
|
-
mode.snapshotDir = modeRaw.snapshotDir;
|
|
792
|
-
}
|
|
793
|
-
if (typeof modeRaw.config == "string" && modeRaw.config.length) {
|
|
794
|
-
mode.config = modeRaw.config;
|
|
795
|
-
}
|
|
796
|
-
if (typeof modeRaw.coverage == "boolean") {
|
|
797
|
-
mode.coverage = modeRaw.coverage;
|
|
798
|
-
}
|
|
799
|
-
else if (modeRaw.coverage && typeof modeRaw.coverage == "object") {
|
|
800
|
-
mode.coverage = Object.assign(new CoverageOptions(), modeRaw.coverage);
|
|
801
|
-
}
|
|
802
|
-
if (modeRaw.buildOptions && typeof modeRaw.buildOptions == "object") {
|
|
803
|
-
const buildRaw = modeRaw.buildOptions;
|
|
804
|
-
const build = {};
|
|
805
|
-
if (typeof buildRaw.cmd == "string") {
|
|
806
|
-
build.cmd = buildRaw.cmd;
|
|
807
|
-
}
|
|
808
|
-
if (Array.isArray(buildRaw.args)) {
|
|
809
|
-
build.args = buildRaw.args.filter((item) => typeof item == "string");
|
|
810
|
-
}
|
|
811
|
-
build.env = parseEnvValue(buildRaw.env, configDir, `$.modes.${name}.buildOptions.env`);
|
|
812
|
-
if (typeof buildRaw.target == "string" && buildRaw.target.length) {
|
|
813
|
-
build.target = buildRaw.target;
|
|
814
|
-
}
|
|
815
|
-
mode.buildOptions = build;
|
|
816
|
-
}
|
|
817
|
-
if (modeRaw.runOptions && typeof modeRaw.runOptions == "object") {
|
|
818
|
-
const runRaw = modeRaw.runOptions;
|
|
819
|
-
const run = {};
|
|
820
|
-
if (runRaw.runtime && typeof runRaw.runtime == "object") {
|
|
821
|
-
const runtimeRaw = runRaw.runtime;
|
|
822
|
-
const runtime = new Runtime();
|
|
823
|
-
if (typeof runtimeRaw.cmd == "string" && runtimeRaw.cmd.length) {
|
|
824
|
-
runtime.cmd = runtimeRaw.cmd;
|
|
825
|
-
}
|
|
826
|
-
else if (typeof runtimeRaw.run == "string" && runtimeRaw.run.length) {
|
|
827
|
-
runtime.cmd = runtimeRaw.run;
|
|
828
|
-
}
|
|
829
|
-
else {
|
|
830
|
-
runtime.cmd = "";
|
|
831
|
-
}
|
|
832
|
-
runtime.browser =
|
|
833
|
-
typeof runtimeRaw.browser == "string" ? runtimeRaw.browser : "";
|
|
834
|
-
run.runtime = runtime;
|
|
835
|
-
}
|
|
836
|
-
if (typeof runRaw.reporter == "string") {
|
|
837
|
-
run.reporter = runRaw.reporter;
|
|
838
|
-
}
|
|
839
|
-
else if (runRaw.reporter && typeof runRaw.reporter == "object") {
|
|
840
|
-
const reporter = Object.assign(new ReporterConfig(), runRaw.reporter);
|
|
841
|
-
reporter.name = typeof reporter.name == "string" ? reporter.name : "";
|
|
842
|
-
reporter.options = Array.isArray(reporter.options)
|
|
843
|
-
? reporter.options.filter((item) => typeof item == "string")
|
|
844
|
-
: [];
|
|
845
|
-
reporter.outDir =
|
|
846
|
-
typeof reporter.outDir == "string" ? reporter.outDir : "";
|
|
847
|
-
reporter.outFile =
|
|
848
|
-
typeof reporter.outFile == "string" ? reporter.outFile : "";
|
|
849
|
-
run.reporter = reporter;
|
|
850
|
-
}
|
|
851
|
-
run.env = parseEnvValue(runRaw.env, configDir, `$.modes.${name}.runOptions.env`);
|
|
852
|
-
mode.runOptions = run;
|
|
794
|
+
if (typeof value == "string") {
|
|
795
|
+
mode.path = resolve(configDir, value);
|
|
796
|
+
mode.config = parseConfigRaw({}, join(configDir, `__mode__.${name}.json`));
|
|
797
|
+
out[name] = mode;
|
|
798
|
+
continue;
|
|
853
799
|
}
|
|
854
|
-
|
|
800
|
+
if (!value || typeof value != "object" || Array.isArray(value))
|
|
801
|
+
continue;
|
|
802
|
+
mode.config = parseConfigRaw(value, join(configDir, `__mode__.${name}.json`));
|
|
855
803
|
out[name] = mode;
|
|
856
804
|
}
|
|
857
805
|
return out;
|
|
@@ -947,6 +895,209 @@ function normalizeNonNegativeNumber(value, fallback) {
|
|
|
947
895
|
}
|
|
948
896
|
return Math.floor(value);
|
|
949
897
|
}
|
|
898
|
+
function getConfigMeta(config) {
|
|
899
|
+
const meta = CONFIG_META.get(config);
|
|
900
|
+
if (!meta) {
|
|
901
|
+
throw new Error("missing config metadata");
|
|
902
|
+
}
|
|
903
|
+
return meta;
|
|
904
|
+
}
|
|
905
|
+
function cloneCoverageOptions(coverage) {
|
|
906
|
+
if (typeof coverage == "boolean")
|
|
907
|
+
return coverage;
|
|
908
|
+
const cloned = Object.assign(new CoverageOptions(), coverage);
|
|
909
|
+
cloned.include = [...(coverage.include ?? [])];
|
|
910
|
+
cloned.exclude = [...(coverage.exclude ?? [])];
|
|
911
|
+
cloned.ignore = Object.assign(new CoverageIgnoreOptions(), coverage.ignore);
|
|
912
|
+
cloned.ignore.labels = [...(coverage.ignore.labels ?? [])];
|
|
913
|
+
cloned.ignore.names = [...(coverage.ignore.names ?? [])];
|
|
914
|
+
cloned.ignore.locations = [...(coverage.ignore.locations ?? [])];
|
|
915
|
+
cloned.ignore.snippets = [...(coverage.ignore.snippets ?? [])];
|
|
916
|
+
return cloned;
|
|
917
|
+
}
|
|
918
|
+
function cloneBuildOptions(options) {
|
|
919
|
+
const cloned = Object.assign(new BuildOptions(), options);
|
|
920
|
+
cloned.args = [...options.args];
|
|
921
|
+
cloned.env = { ...options.env };
|
|
922
|
+
return cloned;
|
|
923
|
+
}
|
|
924
|
+
function cloneRuntime(runtime) {
|
|
925
|
+
return Object.assign(new Runtime(), runtime);
|
|
926
|
+
}
|
|
927
|
+
function cloneReporterConfig(reporter) {
|
|
928
|
+
if (typeof reporter == "string")
|
|
929
|
+
return reporter;
|
|
930
|
+
const cloned = Object.assign(new ReporterConfig(), reporter);
|
|
931
|
+
cloned.options = [...(reporter.options ?? [])];
|
|
932
|
+
return cloned;
|
|
933
|
+
}
|
|
934
|
+
function cloneRunOptions(options) {
|
|
935
|
+
const cloned = Object.assign(new RunOptions(), options);
|
|
936
|
+
cloned.runtime = cloneRuntime(options.runtime);
|
|
937
|
+
cloned.reporter = cloneReporterConfig(options.reporter);
|
|
938
|
+
cloned.env = { ...options.env };
|
|
939
|
+
return cloned;
|
|
940
|
+
}
|
|
941
|
+
function cloneFuzzConfig(config) {
|
|
942
|
+
const cloned = Object.assign(new FuzzConfig(), config);
|
|
943
|
+
cloned.input = [...config.input];
|
|
944
|
+
return cloned;
|
|
945
|
+
}
|
|
946
|
+
function cloneModeConfig(config) {
|
|
947
|
+
const cloned = new ModeConfig();
|
|
948
|
+
cloned.path = config.path;
|
|
949
|
+
cloned.config = cloneConfig(config.config);
|
|
950
|
+
return cloned;
|
|
951
|
+
}
|
|
952
|
+
function cloneConfig(config) {
|
|
953
|
+
const cloned = Object.assign(new Config(), config);
|
|
954
|
+
cloned.input = [...config.input];
|
|
955
|
+
cloned.env = { ...config.env };
|
|
956
|
+
cloned.buildOptions = cloneBuildOptions(config.buildOptions);
|
|
957
|
+
cloned.runOptions = cloneRunOptions(config.runOptions);
|
|
958
|
+
cloned.fuzz = cloneFuzzConfig(config.fuzz);
|
|
959
|
+
cloned.coverage = cloneCoverageOptions(config.coverage);
|
|
960
|
+
cloned.modes = Object.fromEntries(Object.entries(config.modes).map(([name, mode]) => [name, cloneModeConfig(mode)]));
|
|
961
|
+
CONFIG_META.set(cloned, getConfigMeta(config));
|
|
962
|
+
return cloned;
|
|
963
|
+
}
|
|
964
|
+
function outputOverridesField(raw, field) {
|
|
965
|
+
if (field in raw)
|
|
966
|
+
return true;
|
|
967
|
+
if (!raw.output || typeof raw.output != "object" || Array.isArray(raw.output)) {
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
970
|
+
const output = raw.output;
|
|
971
|
+
if (field == "outDir")
|
|
972
|
+
return typeof output.build == "string" && output.build.length > 0;
|
|
973
|
+
if (field == "logs")
|
|
974
|
+
return typeof output.logs == "string" && output.logs.length > 0;
|
|
975
|
+
if (field == "coverageDir") {
|
|
976
|
+
return typeof output.coverage == "string" && output.coverage.length > 0;
|
|
977
|
+
}
|
|
978
|
+
return typeof output.snapshots == "string" && output.snapshots.length > 0;
|
|
979
|
+
}
|
|
980
|
+
function mergeBuildOptions(base, override, raw) {
|
|
981
|
+
const merged = cloneBuildOptions(base);
|
|
982
|
+
if ("cmd" in raw)
|
|
983
|
+
merged.cmd = override.cmd;
|
|
984
|
+
if ("args" in raw)
|
|
985
|
+
merged.args = [...override.args];
|
|
986
|
+
if ("target" in raw)
|
|
987
|
+
merged.target = override.target;
|
|
988
|
+
if ("env" in raw) {
|
|
989
|
+
merged.env = {
|
|
990
|
+
...merged.env,
|
|
991
|
+
...override.env,
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
return merged;
|
|
995
|
+
}
|
|
996
|
+
function mergeRunOptions(base, override, raw) {
|
|
997
|
+
const merged = cloneRunOptions(base);
|
|
998
|
+
if ("runtime" in raw || "run" in raw) {
|
|
999
|
+
const runtimeRaw = raw.runtime;
|
|
1000
|
+
if ("run" in raw || (runtimeRaw && ("cmd" in runtimeRaw || "run" in runtimeRaw))) {
|
|
1001
|
+
merged.runtime.cmd = override.runtime.cmd;
|
|
1002
|
+
}
|
|
1003
|
+
if (runtimeRaw && "browser" in runtimeRaw) {
|
|
1004
|
+
merged.runtime.browser = override.runtime.browser;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
if ("reporter" in raw) {
|
|
1008
|
+
merged.reporter = cloneReporterConfig(override.reporter);
|
|
1009
|
+
}
|
|
1010
|
+
if ("env" in raw) {
|
|
1011
|
+
merged.env = {
|
|
1012
|
+
...merged.env,
|
|
1013
|
+
...override.env,
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
return merged;
|
|
1017
|
+
}
|
|
1018
|
+
function mergeFuzzConfig(base, override, raw) {
|
|
1019
|
+
const merged = cloneFuzzConfig(base);
|
|
1020
|
+
if ("input" in raw)
|
|
1021
|
+
merged.input = [...override.input];
|
|
1022
|
+
if ("runs" in raw)
|
|
1023
|
+
merged.runs = override.runs;
|
|
1024
|
+
if ("seed" in raw)
|
|
1025
|
+
merged.seed = override.seed;
|
|
1026
|
+
if ("maxInputBytes" in raw)
|
|
1027
|
+
merged.maxInputBytes = override.maxInputBytes;
|
|
1028
|
+
if ("target" in raw)
|
|
1029
|
+
merged.target = override.target;
|
|
1030
|
+
if ("corpusDir" in raw)
|
|
1031
|
+
merged.corpusDir = override.corpusDir;
|
|
1032
|
+
if ("crashDir" in raw)
|
|
1033
|
+
merged.crashDir = override.crashDir;
|
|
1034
|
+
return merged;
|
|
1035
|
+
}
|
|
1036
|
+
function mergeRootConfig(base, override) {
|
|
1037
|
+
const merged = cloneConfig(base);
|
|
1038
|
+
const raw = getConfigMeta(override).raw;
|
|
1039
|
+
if ("$schema" in raw)
|
|
1040
|
+
merged.$schema = override.$schema;
|
|
1041
|
+
if ("input" in raw)
|
|
1042
|
+
merged.input = [...override.input];
|
|
1043
|
+
if (outputOverridesField(raw, "outDir"))
|
|
1044
|
+
merged.outDir = override.outDir;
|
|
1045
|
+
if (outputOverridesField(raw, "logs"))
|
|
1046
|
+
merged.logs = override.logs;
|
|
1047
|
+
if (outputOverridesField(raw, "coverageDir")) {
|
|
1048
|
+
merged.coverageDir = override.coverageDir;
|
|
1049
|
+
}
|
|
1050
|
+
if (outputOverridesField(raw, "snapshotDir")) {
|
|
1051
|
+
merged.snapshotDir = override.snapshotDir;
|
|
1052
|
+
}
|
|
1053
|
+
if ("config" in raw)
|
|
1054
|
+
merged.config = override.config;
|
|
1055
|
+
if ("coverage" in raw)
|
|
1056
|
+
merged.coverage = cloneCoverageOptions(override.coverage);
|
|
1057
|
+
if ("env" in raw) {
|
|
1058
|
+
merged.env = {
|
|
1059
|
+
...merged.env,
|
|
1060
|
+
...override.env,
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
if (raw.buildOptions && typeof raw.buildOptions == "object" && !Array.isArray(raw.buildOptions)) {
|
|
1064
|
+
merged.buildOptions = mergeBuildOptions(merged.buildOptions, override.buildOptions, raw.buildOptions);
|
|
1065
|
+
}
|
|
1066
|
+
if (raw.runOptions && typeof raw.runOptions == "object" && !Array.isArray(raw.runOptions)) {
|
|
1067
|
+
merged.runOptions = mergeRunOptions(merged.runOptions, override.runOptions, raw.runOptions);
|
|
1068
|
+
}
|
|
1069
|
+
if (raw.fuzz && typeof raw.fuzz == "object" && !Array.isArray(raw.fuzz)) {
|
|
1070
|
+
merged.fuzz = mergeFuzzConfig(merged.fuzz, override.fuzz, raw.fuzz);
|
|
1071
|
+
}
|
|
1072
|
+
CONFIG_META.set(merged, getConfigMeta(override));
|
|
1073
|
+
return merged;
|
|
1074
|
+
}
|
|
1075
|
+
function applyPerModeOutputDefaults(base, merged, override, modeName) {
|
|
1076
|
+
const raw = getConfigMeta(override).raw;
|
|
1077
|
+
if (!outputOverridesField(raw, "outDir")) {
|
|
1078
|
+
merged.outDir = appendPathSegment(base.outDir, modeName);
|
|
1079
|
+
}
|
|
1080
|
+
if (!outputOverridesField(raw, "logs") && base.logs != "none") {
|
|
1081
|
+
merged.logs = appendPathSegment(base.logs, modeName);
|
|
1082
|
+
}
|
|
1083
|
+
if (!outputOverridesField(raw, "coverageDir") && base.coverageDir != "none") {
|
|
1084
|
+
merged.coverageDir = appendPathSegment(base.coverageDir, modeName);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
function resolveModeOverrideConfig(root, modeName) {
|
|
1088
|
+
const mode = root.modes[modeName];
|
|
1089
|
+
if (!mode) {
|
|
1090
|
+
throw new Error(`unknown mode "${modeName}"`);
|
|
1091
|
+
}
|
|
1092
|
+
if (mode.path) {
|
|
1093
|
+
const override = loadConfig(mode.path, false);
|
|
1094
|
+
if (Object.keys(override.modes).length) {
|
|
1095
|
+
throw new Error(`mode "${modeName}" config file cannot declare nested modes`);
|
|
1096
|
+
}
|
|
1097
|
+
return override;
|
|
1098
|
+
}
|
|
1099
|
+
return cloneConfig(mode.config);
|
|
1100
|
+
}
|
|
950
1101
|
export function resolveModeNames(rawArgs) {
|
|
951
1102
|
const names = [];
|
|
952
1103
|
for (let i = 0; i < rawArgs.length; i++) {
|
|
@@ -975,12 +1126,7 @@ function appendModeTokens(out, value) {
|
|
|
975
1126
|
}
|
|
976
1127
|
export function applyMode(config, modeName) {
|
|
977
1128
|
if (!modeName) {
|
|
978
|
-
const merged =
|
|
979
|
-
merged.buildOptions = Object.assign(new BuildOptions(), config.buildOptions);
|
|
980
|
-
merged.runOptions = Object.assign(new RunOptions(), config.runOptions);
|
|
981
|
-
merged.runOptions.runtime = Object.assign(new Runtime(), config.runOptions.runtime);
|
|
982
|
-
merged.buildOptions.env = { ...config.buildOptions.env };
|
|
983
|
-
merged.runOptions.env = { ...config.runOptions.env };
|
|
1129
|
+
const merged = cloneConfig(config);
|
|
984
1130
|
merged.outDir = appendPathSegment(config.outDir, "default");
|
|
985
1131
|
if (config.logs != "none") {
|
|
986
1132
|
merged.logs = appendPathSegment(config.logs, "default");
|
|
@@ -988,7 +1134,6 @@ export function applyMode(config, modeName) {
|
|
|
988
1134
|
if (config.coverageDir != "none") {
|
|
989
1135
|
merged.coverageDir = appendPathSegment(config.coverageDir, "default");
|
|
990
1136
|
}
|
|
991
|
-
merged.fuzz = Object.assign(new FuzzConfig(), config.fuzz);
|
|
992
1137
|
merged.fuzz.crashDir = appendPathSegment(config.fuzz.crashDir, "default");
|
|
993
1138
|
merged.fuzz.corpusDir = appendPathSegment(config.fuzz.corpusDir, "default");
|
|
994
1139
|
const env = {
|
|
@@ -1003,71 +1148,17 @@ export function applyMode(config, modeName) {
|
|
|
1003
1148
|
env,
|
|
1004
1149
|
};
|
|
1005
1150
|
}
|
|
1006
|
-
|
|
1007
|
-
if (!mode) {
|
|
1151
|
+
if (!config.modes[modeName]) {
|
|
1008
1152
|
const known = Object.keys(config.modes);
|
|
1009
1153
|
const available = known.length ? known.join(", ") : "(none)";
|
|
1010
1154
|
throw new Error(`unknown mode "${modeName}". Available modes: ${available}`);
|
|
1011
1155
|
}
|
|
1012
|
-
const
|
|
1013
|
-
merged
|
|
1014
|
-
|
|
1015
|
-
merged.runOptions.runtime = Object.assign(new Runtime(), config.runOptions.runtime);
|
|
1016
|
-
merged.buildOptions.env = { ...config.buildOptions.env };
|
|
1017
|
-
merged.runOptions.env = { ...config.runOptions.env };
|
|
1018
|
-
if (mode.outDir)
|
|
1019
|
-
merged.outDir = mode.outDir;
|
|
1020
|
-
else
|
|
1021
|
-
merged.outDir = appendPathSegment(config.outDir, modeName);
|
|
1022
|
-
if (mode.logs)
|
|
1023
|
-
merged.logs = mode.logs;
|
|
1024
|
-
else if (config.logs != "none")
|
|
1025
|
-
merged.logs = appendPathSegment(config.logs, modeName);
|
|
1026
|
-
if (mode.coverageDir)
|
|
1027
|
-
merged.coverageDir = mode.coverageDir;
|
|
1028
|
-
else if (config.coverageDir != "none")
|
|
1029
|
-
merged.coverageDir = appendPathSegment(config.coverageDir, modeName);
|
|
1030
|
-
if (mode.snapshotDir)
|
|
1031
|
-
merged.snapshotDir = mode.snapshotDir;
|
|
1032
|
-
if (mode.config)
|
|
1033
|
-
merged.config = mode.config;
|
|
1034
|
-
if (mode.coverage != undefined)
|
|
1035
|
-
merged.coverage = mode.coverage;
|
|
1036
|
-
if (mode.buildOptions.target)
|
|
1037
|
-
merged.buildOptions.target = mode.buildOptions.target;
|
|
1038
|
-
if (mode.buildOptions.cmd != undefined)
|
|
1039
|
-
merged.buildOptions.cmd = mode.buildOptions.cmd;
|
|
1040
|
-
if (mode.buildOptions.args) {
|
|
1041
|
-
merged.buildOptions.args = [
|
|
1042
|
-
...merged.buildOptions.args,
|
|
1043
|
-
...mode.buildOptions.args,
|
|
1044
|
-
];
|
|
1045
|
-
}
|
|
1046
|
-
if (mode.buildOptions.env) {
|
|
1047
|
-
merged.buildOptions.env = {
|
|
1048
|
-
...merged.buildOptions.env,
|
|
1049
|
-
...mode.buildOptions.env,
|
|
1050
|
-
};
|
|
1051
|
-
}
|
|
1052
|
-
if (mode.runOptions.runtime?.cmd) {
|
|
1053
|
-
merged.runOptions.runtime.cmd = mode.runOptions.runtime.cmd;
|
|
1054
|
-
}
|
|
1055
|
-
if (mode.runOptions.runtime?.browser != undefined) {
|
|
1056
|
-
merged.runOptions.runtime.browser = mode.runOptions.runtime.browser;
|
|
1057
|
-
}
|
|
1058
|
-
if (mode.runOptions.reporter != undefined) {
|
|
1059
|
-
merged.runOptions.reporter = mode.runOptions.reporter;
|
|
1060
|
-
}
|
|
1061
|
-
if (mode.runOptions.env) {
|
|
1062
|
-
merged.runOptions.env = {
|
|
1063
|
-
...merged.runOptions.env,
|
|
1064
|
-
...mode.runOptions.env,
|
|
1065
|
-
};
|
|
1066
|
-
}
|
|
1156
|
+
const modeOverride = resolveModeOverrideConfig(config, modeName);
|
|
1157
|
+
const merged = mergeRootConfig(config, modeOverride);
|
|
1158
|
+
applyPerModeOutputDefaults(config, merged, modeOverride, modeName);
|
|
1067
1159
|
const env = {
|
|
1068
1160
|
...process.env,
|
|
1069
|
-
...
|
|
1070
|
-
...mode.env,
|
|
1161
|
+
...merged.env,
|
|
1071
1162
|
};
|
|
1072
1163
|
if (merged.runOptions.runtime.browser.length) {
|
|
1073
1164
|
env.BROWSER = merged.runOptions.runtime.browser;
|