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
|
@@ -99,24 +99,55 @@
|
|
|
99
99
|
"properties": {
|
|
100
100
|
"enabled": {
|
|
101
101
|
"type": "boolean",
|
|
102
|
-
"default":
|
|
102
|
+
"default": false
|
|
103
103
|
},
|
|
104
104
|
"includeSpecs": {
|
|
105
105
|
"type": "boolean",
|
|
106
106
|
"description": "Include *.spec.ts files in coverage collection.",
|
|
107
107
|
"default": false
|
|
108
|
+
},
|
|
109
|
+
"include": {
|
|
110
|
+
"type": "array",
|
|
111
|
+
"description": "Only include matching source files in coverage reports when this list is not empty.",
|
|
112
|
+
"items": {
|
|
113
|
+
"type": "string"
|
|
114
|
+
},
|
|
115
|
+
"default": []
|
|
116
|
+
},
|
|
117
|
+
"exclude": {
|
|
118
|
+
"type": "array",
|
|
119
|
+
"description": "Exclude matching source files from coverage reports.",
|
|
120
|
+
"items": {
|
|
121
|
+
"type": "string"
|
|
122
|
+
},
|
|
123
|
+
"default": []
|
|
108
124
|
}
|
|
109
125
|
}
|
|
110
126
|
}
|
|
111
127
|
],
|
|
112
|
-
"default":
|
|
128
|
+
"default": false
|
|
113
129
|
},
|
|
114
130
|
"env": {
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
131
|
+
"description": "Environment variables injected when building/running. Accepts a .env file path, an array of KEY=value strings, or an object map.",
|
|
132
|
+
"oneOf": [
|
|
133
|
+
{
|
|
134
|
+
"type": "string",
|
|
135
|
+
"description": "Path to a .env file, resolved relative to the config file."
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"type": "array",
|
|
139
|
+
"description": "Inline environment entries in KEY=value form.",
|
|
140
|
+
"items": {
|
|
141
|
+
"type": "string"
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"type": "object",
|
|
146
|
+
"additionalProperties": {
|
|
147
|
+
"type": "string"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
],
|
|
120
151
|
"default": {}
|
|
121
152
|
},
|
|
122
153
|
"buildOptions": {
|
|
@@ -137,14 +168,91 @@
|
|
|
137
168
|
},
|
|
138
169
|
"target": {
|
|
139
170
|
"type": "string",
|
|
140
|
-
"enum": ["wasi", "bindings"],
|
|
171
|
+
"enum": ["wasi", "bindings", "web"],
|
|
141
172
|
"default": "wasi"
|
|
173
|
+
},
|
|
174
|
+
"env": {
|
|
175
|
+
"description": "Build-only environment overrides. Merges with top-level env and mode env.",
|
|
176
|
+
"oneOf": [
|
|
177
|
+
{
|
|
178
|
+
"type": "string"
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"type": "array",
|
|
182
|
+
"items": {
|
|
183
|
+
"type": "string"
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"type": "object",
|
|
188
|
+
"additionalProperties": {
|
|
189
|
+
"type": "string"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
],
|
|
193
|
+
"default": {}
|
|
142
194
|
}
|
|
143
195
|
},
|
|
144
196
|
"default": {
|
|
145
197
|
"cmd": "",
|
|
146
198
|
"args": [],
|
|
147
|
-
"target": "wasi"
|
|
199
|
+
"target": "wasi",
|
|
200
|
+
"env": {}
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"fuzz": {
|
|
204
|
+
"type": "object",
|
|
205
|
+
"additionalProperties": false,
|
|
206
|
+
"description": "Fuzz target configuration used by `ast fuzz` and `ast test --fuzz`.",
|
|
207
|
+
"properties": {
|
|
208
|
+
"input": {
|
|
209
|
+
"type": "array",
|
|
210
|
+
"description": "Glob patterns for fuzz target files.",
|
|
211
|
+
"items": {
|
|
212
|
+
"type": "string"
|
|
213
|
+
},
|
|
214
|
+
"default": ["./assembly/__fuzz__/*.fuzz.ts"]
|
|
215
|
+
},
|
|
216
|
+
"runs": {
|
|
217
|
+
"type": "number",
|
|
218
|
+
"description": "Number of fuzz iterations per target.",
|
|
219
|
+
"default": 1000
|
|
220
|
+
},
|
|
221
|
+
"seed": {
|
|
222
|
+
"type": "number",
|
|
223
|
+
"description": "Base deterministic seed for input mutation.",
|
|
224
|
+
"default": 1337
|
|
225
|
+
},
|
|
226
|
+
"maxInputBytes": {
|
|
227
|
+
"type": "number",
|
|
228
|
+
"description": "Maximum generated input size in bytes.",
|
|
229
|
+
"default": 4096
|
|
230
|
+
},
|
|
231
|
+
"target": {
|
|
232
|
+
"type": "string",
|
|
233
|
+
"enum": ["bindings"],
|
|
234
|
+
"description": "Fuzz builds currently require bindings output.",
|
|
235
|
+
"default": "bindings"
|
|
236
|
+
},
|
|
237
|
+
"corpusDir": {
|
|
238
|
+
"type": "string",
|
|
239
|
+
"description": "Directory containing per-target seed corpus files.",
|
|
240
|
+
"default": "./.as-test/fuzz/corpus"
|
|
241
|
+
},
|
|
242
|
+
"crashDir": {
|
|
243
|
+
"type": "string",
|
|
244
|
+
"description": "Directory where crashing inputs and metadata are written.",
|
|
245
|
+
"default": "./.as-test/crashes"
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
"default": {
|
|
249
|
+
"input": ["./assembly/__fuzz__/*.fuzz.ts"],
|
|
250
|
+
"runs": 1000,
|
|
251
|
+
"seed": 1337,
|
|
252
|
+
"maxInputBytes": 4096,
|
|
253
|
+
"target": "bindings",
|
|
254
|
+
"corpusDir": "./.as-test/fuzz/corpus",
|
|
255
|
+
"crashDir": "./.as-test/crashes"
|
|
148
256
|
}
|
|
149
257
|
},
|
|
150
258
|
"modes": {
|
|
@@ -210,7 +318,27 @@
|
|
|
210
318
|
},
|
|
211
319
|
"target": {
|
|
212
320
|
"type": "string",
|
|
213
|
-
"enum": ["wasi", "bindings"]
|
|
321
|
+
"enum": ["wasi", "bindings", "web"]
|
|
322
|
+
},
|
|
323
|
+
"env": {
|
|
324
|
+
"description": "Mode-specific build environment overrides.",
|
|
325
|
+
"oneOf": [
|
|
326
|
+
{
|
|
327
|
+
"type": "string"
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
"type": "array",
|
|
331
|
+
"items": {
|
|
332
|
+
"type": "string"
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
"type": "object",
|
|
337
|
+
"additionalProperties": {
|
|
338
|
+
"type": "string"
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
]
|
|
214
342
|
}
|
|
215
343
|
}
|
|
216
344
|
},
|
|
@@ -225,6 +353,10 @@
|
|
|
225
353
|
"cmd": {
|
|
226
354
|
"type": "string",
|
|
227
355
|
"description": "Mode-specific runtime command."
|
|
356
|
+
},
|
|
357
|
+
"browser": {
|
|
358
|
+
"type": "string",
|
|
359
|
+
"description": "Mode-specific browser for web targets. Use chrome, chromium, firefox, webkit, or an executable path."
|
|
228
360
|
}
|
|
229
361
|
}
|
|
230
362
|
},
|
|
@@ -257,15 +389,48 @@
|
|
|
257
389
|
"required": ["name"]
|
|
258
390
|
}
|
|
259
391
|
]
|
|
392
|
+
},
|
|
393
|
+
"env": {
|
|
394
|
+
"description": "Mode-specific runtime environment overrides.",
|
|
395
|
+
"oneOf": [
|
|
396
|
+
{
|
|
397
|
+
"type": "string"
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
"type": "array",
|
|
401
|
+
"items": {
|
|
402
|
+
"type": "string"
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
"type": "object",
|
|
407
|
+
"additionalProperties": {
|
|
408
|
+
"type": "string"
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
]
|
|
260
412
|
}
|
|
261
413
|
}
|
|
262
414
|
},
|
|
263
415
|
"env": {
|
|
264
|
-
"
|
|
265
|
-
"
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
416
|
+
"description": "Mode-wide environment variables merged before build/run-specific env.",
|
|
417
|
+
"oneOf": [
|
|
418
|
+
{
|
|
419
|
+
"type": "string"
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
"type": "array",
|
|
423
|
+
"items": {
|
|
424
|
+
"type": "string"
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
"type": "object",
|
|
429
|
+
"additionalProperties": {
|
|
430
|
+
"type": "string"
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
]
|
|
269
434
|
}
|
|
270
435
|
}
|
|
271
436
|
},
|
|
@@ -283,10 +448,16 @@
|
|
|
283
448
|
"type": "string",
|
|
284
449
|
"description": "Runtime command; supports placeholders like <file> and <name>.",
|
|
285
450
|
"default": "node ./.as-test/runners/default.wasi.js <file>"
|
|
451
|
+
},
|
|
452
|
+
"browser": {
|
|
453
|
+
"type": "string",
|
|
454
|
+
"description": "Browser for web targets. Use chrome, chromium, firefox, webkit, or an executable path.",
|
|
455
|
+
"default": ""
|
|
286
456
|
}
|
|
287
457
|
},
|
|
288
458
|
"default": {
|
|
289
|
-
"cmd": "node ./.as-test/runners/default.wasi.js <file>"
|
|
459
|
+
"cmd": "node ./.as-test/runners/default.wasi.js <file>",
|
|
460
|
+
"browser": ""
|
|
290
461
|
}
|
|
291
462
|
},
|
|
292
463
|
"reporter": {
|
|
@@ -324,13 +495,35 @@
|
|
|
324
495
|
}
|
|
325
496
|
],
|
|
326
497
|
"default": ""
|
|
498
|
+
},
|
|
499
|
+
"env": {
|
|
500
|
+
"description": "Run-only environment overrides. Merges with top-level env and mode env.",
|
|
501
|
+
"oneOf": [
|
|
502
|
+
{
|
|
503
|
+
"type": "string"
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
"type": "array",
|
|
507
|
+
"items": {
|
|
508
|
+
"type": "string"
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
"type": "object",
|
|
513
|
+
"additionalProperties": {
|
|
514
|
+
"type": "string"
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
],
|
|
518
|
+
"default": {}
|
|
327
519
|
}
|
|
328
520
|
},
|
|
329
521
|
"default": {
|
|
330
522
|
"runtime": {
|
|
331
523
|
"cmd": "node ./.as-test/runners/default.wasi.js <file>"
|
|
332
524
|
},
|
|
333
|
-
"reporter": ""
|
|
525
|
+
"reporter": "",
|
|
526
|
+
"env": {}
|
|
334
527
|
}
|
|
335
528
|
}
|
|
336
529
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { expect, fuzz, FuzzSeed } from "as-test";
|
|
2
|
+
|
|
3
|
+
fuzz("index windows stay ordered", (start: i32, end: i32): bool => {
|
|
4
|
+
expect(start <= end).toBe(true);
|
|
5
|
+
return end - start <= 64;
|
|
6
|
+
}).generate((seed: FuzzSeed, run: (start: i32, end: i32) => bool): void => {
|
|
7
|
+
const start = seed.i32({ min: 0, max: 64 });
|
|
8
|
+
const width = seed.i32({ min: 0, max: 64 });
|
|
9
|
+
run(start, start + width);
|
|
10
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { expect, fuzz, FuzzSeed } from "..";
|
|
2
|
+
|
|
3
|
+
fuzz("byte-sized integers stay within range", (value: i32): bool => {
|
|
4
|
+
expect(value >= 0).toBe(true);
|
|
5
|
+
return value <= 255;
|
|
6
|
+
}).generate((seed: FuzzSeed, run: (value: i32) => bool): void => {
|
|
7
|
+
run(seed.i32({ min: 0, max: 255 }));
|
|
8
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { expect, fuzz, FuzzSeed } from "as-test";
|
|
2
|
+
|
|
3
|
+
fuzz("bounded integer addition", (left: i32, right: i32): bool => {
|
|
4
|
+
const sum = left + right;
|
|
5
|
+
expect(sum - right).toBe(left);
|
|
6
|
+
return sum >= i32.MIN_VALUE; // Fails if an expectation fails or false is returned
|
|
7
|
+
}).generate((seed: FuzzSeed, run: (left: i32, right: i32) => bool): void => {
|
|
8
|
+
run(seed.i32({ min: -1000, max: 1000 }), seed.i32({ min: -1000, max: 1000 }));
|
|
9
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { expect, fuzz, FuzzSeed } from "as-test";
|
|
2
|
+
|
|
3
|
+
fuzz(
|
|
4
|
+
"ascii strings survive concatenation boundaries",
|
|
5
|
+
(input: string): bool => {
|
|
6
|
+
const wrapped = "[" + input + "]";
|
|
7
|
+
const restored = wrapped.substr(1, input.length);
|
|
8
|
+
|
|
9
|
+
expect(restored).toBe(input);
|
|
10
|
+
return input.length <= 40;
|
|
11
|
+
},
|
|
12
|
+
).generate((seed: FuzzSeed, run: (input: string) => bool): void => {
|
|
13
|
+
run(
|
|
14
|
+
seed.string({
|
|
15
|
+
charset: "ascii",
|
|
16
|
+
min: 0,
|
|
17
|
+
max: 40,
|
|
18
|
+
exclude: [0x00, 0x0a, 0x0d],
|
|
19
|
+
}),
|
|
20
|
+
);
|
|
21
|
+
});
|
package/assembly/index.ts
CHANGED
|
@@ -9,32 +9,48 @@ import {
|
|
|
9
9
|
CoverPoint,
|
|
10
10
|
} from "as-test/assembly/coverage";
|
|
11
11
|
import { Log } from "./src/log";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
requestFuzzConfig as requestHostFuzzConfig,
|
|
14
|
+
sendFileEnd,
|
|
15
|
+
sendFileStart,
|
|
16
|
+
sendReport,
|
|
17
|
+
} from "./util/wipc";
|
|
13
18
|
import { quote } from "./util/json";
|
|
19
|
+
import {
|
|
20
|
+
createFuzzer,
|
|
21
|
+
FuzzerBase,
|
|
22
|
+
FuzzerResult,
|
|
23
|
+
prepareFuzzIteration,
|
|
24
|
+
} from "./src/fuzz";
|
|
25
|
+
export {
|
|
26
|
+
ArrayOptions,
|
|
27
|
+
BytesOptions,
|
|
28
|
+
FloatOptions,
|
|
29
|
+
Fuzzer0,
|
|
30
|
+
Fuzzer1,
|
|
31
|
+
Fuzzer2,
|
|
32
|
+
Fuzzer3,
|
|
33
|
+
FuzzerBase,
|
|
34
|
+
FuzzerResult,
|
|
35
|
+
FuzzSeed,
|
|
36
|
+
IntegerOptions,
|
|
37
|
+
StringOptions,
|
|
38
|
+
} from "./src/fuzz";
|
|
39
|
+
export { __as_test_deep_equal } from "./src/expectation";
|
|
40
|
+
export { __as_test_json_value } from "./util/json";
|
|
14
41
|
|
|
15
42
|
let entrySuites: Suite[] = [];
|
|
43
|
+
let entryFuzzers: FuzzerBase[] = [];
|
|
16
44
|
|
|
17
45
|
// @ts-ignore
|
|
18
46
|
const FILE = isDefined(ENTRY_FILE) ? ENTRY_FILE : "unknown";
|
|
19
47
|
|
|
20
|
-
class ImportSnapshot {
|
|
21
|
-
hasValue: bool = false;
|
|
22
|
-
value: u32 = 0;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const DEFAULT_IMPORT_SNAPSHOT_VERSION = "default";
|
|
26
|
-
|
|
27
48
|
// Globals
|
|
28
49
|
// @ts-ignore
|
|
29
50
|
@global let __mock_global: Map<string, u32> = new Map<string, u32>();
|
|
30
51
|
// @ts-ignore
|
|
31
52
|
@global let __mock_import: Map<string, u32> = new Map<string, u32>();
|
|
32
53
|
// @ts-ignore
|
|
33
|
-
@global let __mock_import_snapshots: Map<string, ImportSnapshot> = new Map<
|
|
34
|
-
string,
|
|
35
|
-
ImportSnapshot
|
|
36
|
-
>();
|
|
37
|
-
// @ts-ignore
|
|
38
54
|
@global let __mock_import_target_by_index: Map<u32, string> = new Map<
|
|
39
55
|
u32,
|
|
40
56
|
string
|
|
@@ -108,6 +124,27 @@ export function it(description: string, callback: () => void): void {
|
|
|
108
124
|
registerSuite(description, callback, "it");
|
|
109
125
|
}
|
|
110
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Creates a focused test case.
|
|
129
|
+
*/
|
|
130
|
+
export function only(description: string, callback: () => void): void {
|
|
131
|
+
registerSuite(description, callback, "only");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Creates a skipped focused test case.
|
|
136
|
+
*/
|
|
137
|
+
export function xonly(description: string, callback: () => void): void {
|
|
138
|
+
registerSuite(description, callback, "xonly");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Creates a todo test case placeholder.
|
|
143
|
+
*/
|
|
144
|
+
export function todo(description: string): void {
|
|
145
|
+
registerSuite(description, (): void => {}, "todo");
|
|
146
|
+
}
|
|
147
|
+
|
|
111
148
|
/**
|
|
112
149
|
* Creates a skipped test group.
|
|
113
150
|
*/
|
|
@@ -129,6 +166,24 @@ export function xit(description: string, callback: () => void): void {
|
|
|
129
166
|
registerSuite(description, callback, "xit");
|
|
130
167
|
}
|
|
131
168
|
|
|
169
|
+
export function fuzz<T extends Function>(
|
|
170
|
+
description: string,
|
|
171
|
+
callback: T,
|
|
172
|
+
): FuzzerBase {
|
|
173
|
+
const entry = createFuzzer(description, callback);
|
|
174
|
+
entryFuzzers.push(entry);
|
|
175
|
+
return entry;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function xfuzz<T extends Function>(
|
|
179
|
+
description: string,
|
|
180
|
+
callback: T,
|
|
181
|
+
): FuzzerBase {
|
|
182
|
+
const entry = createFuzzer(description, callback, true);
|
|
183
|
+
entryFuzzers.push(entry);
|
|
184
|
+
return entry;
|
|
185
|
+
}
|
|
186
|
+
|
|
132
187
|
/**
|
|
133
188
|
* Creates an expectation object for making assertions within a test case.
|
|
134
189
|
*
|
|
@@ -270,40 +325,12 @@ export function unmockImport(oldFn: string): void {
|
|
|
270
325
|
}
|
|
271
326
|
|
|
272
327
|
/**
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
* Accepts either:
|
|
276
|
-
* - `snapshotImport(importOrPath, version)`
|
|
277
|
-
* - `snapshotImport(importOrPath, () => { ... })` (uses default version)
|
|
278
|
-
*
|
|
279
|
-
* `imp` accepts either a string import path (e.g. "mock.foo") or the imported function.
|
|
328
|
+
* Capture the current return value of a zero-arg callback and return a new function
|
|
329
|
+
* that always returns the captured value.
|
|
280
330
|
*/
|
|
281
|
-
export function
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
// @ts-ignore
|
|
285
|
-
versionOrCapture();
|
|
286
|
-
saveImportSnapshot(importKey, DEFAULT_IMPORT_SNAPSHOT_VERSION);
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
saveImportSnapshot(importKey, versionKey<V>(versionOrCapture));
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Restore a single import mock value for the given version.
|
|
294
|
-
*
|
|
295
|
-
* Accepts either a string import path (e.g. "mock.foo") or the imported function.
|
|
296
|
-
*/
|
|
297
|
-
export function restoreImport<T, V>(imp: T, version: V): void {
|
|
298
|
-
const importKey = resolveImportKey<T>(imp);
|
|
299
|
-
const snapshotKey = importSnapshotKey(importKey, versionKey<V>(version));
|
|
300
|
-
if (!__mock_import_snapshots.has(snapshotKey)) return;
|
|
301
|
-
const snapshot = __mock_import_snapshots.get(snapshotKey);
|
|
302
|
-
if (snapshot.hasValue) {
|
|
303
|
-
__mock_import.set(importKey, snapshot.value);
|
|
304
|
-
} else {
|
|
305
|
-
__mock_import.delete(importKey);
|
|
306
|
-
}
|
|
331
|
+
export function snapshotFn<T>(callback: () => T): () => T {
|
|
332
|
+
const value = callback();
|
|
333
|
+
return (): T => value;
|
|
307
334
|
}
|
|
308
335
|
|
|
309
336
|
/**
|
|
@@ -339,9 +366,15 @@ class RunOptions {
|
|
|
339
366
|
* ```
|
|
340
367
|
*/
|
|
341
368
|
export function run(options: RunOptions = new RunOptions()): void {
|
|
369
|
+
// @ts-ignore
|
|
370
|
+
if (isDefined(AS_TEST_FUZZ)) {
|
|
371
|
+
runFuzzers();
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
342
374
|
__test_options = options;
|
|
343
375
|
const time = new Time();
|
|
344
376
|
let fileVerdict = "none";
|
|
377
|
+
const hasTopLevelOnly = containsOnlySuites(entrySuites);
|
|
345
378
|
sendFileStart(FILE);
|
|
346
379
|
time.start = performance.now();
|
|
347
380
|
for (let i = 0; i < entrySuites.length; i++) {
|
|
@@ -353,7 +386,11 @@ export function run(options: RunOptions = new RunOptions()): void {
|
|
|
353
386
|
depth = -1;
|
|
354
387
|
current_suite = null;
|
|
355
388
|
|
|
356
|
-
suite.
|
|
389
|
+
if (hasTopLevelOnly && suite.kind != "only") {
|
|
390
|
+
suite.skip();
|
|
391
|
+
} else {
|
|
392
|
+
suite.run();
|
|
393
|
+
}
|
|
357
394
|
if (suite.verdict == "fail") {
|
|
358
395
|
fileVerdict = "fail";
|
|
359
396
|
} else if (fileVerdict != "fail" && suite.verdict == "ok") {
|
|
@@ -374,6 +411,53 @@ export function run(options: RunOptions = new RunOptions()): void {
|
|
|
374
411
|
sendReport(report.serialize());
|
|
375
412
|
}
|
|
376
413
|
|
|
414
|
+
function containsOnlySuites(values: Suite[]): bool {
|
|
415
|
+
for (let i = 0; i < values.length; i++) {
|
|
416
|
+
if (unchecked(values[i]).kind == "only") return true;
|
|
417
|
+
}
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
class FuzzConfig {
|
|
422
|
+
runs: i32 = 1000;
|
|
423
|
+
seed: u64 = 1337;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
class FuzzReport {
|
|
427
|
+
fuzzers: FuzzerResult[] = [];
|
|
428
|
+
|
|
429
|
+
serialize(): string {
|
|
430
|
+
let out = '{"fuzzers":[';
|
|
431
|
+
for (let i = 0; i < this.fuzzers.length; i++) {
|
|
432
|
+
if (i) out += ",";
|
|
433
|
+
out += unchecked(this.fuzzers[i]).serialize();
|
|
434
|
+
}
|
|
435
|
+
out += "]}";
|
|
436
|
+
return out;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function runFuzzers(): void {
|
|
441
|
+
__test_options = new RunOptions();
|
|
442
|
+
const config = requestFuzzConfig();
|
|
443
|
+
const report = new FuzzReport();
|
|
444
|
+
for (let i = 0; i < entryFuzzers.length; i++) {
|
|
445
|
+
const fuzzer = unchecked(entryFuzzers[i]);
|
|
446
|
+
prepareFuzzIteration();
|
|
447
|
+
const result = fuzzer.run(config.seed, config.runs);
|
|
448
|
+
report.fuzzers.push(result);
|
|
449
|
+
}
|
|
450
|
+
sendReport(report.serialize());
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function requestFuzzConfig(): FuzzConfig {
|
|
454
|
+
const out = new FuzzConfig();
|
|
455
|
+
const reply = requestHostFuzzConfig();
|
|
456
|
+
out.runs = reply.runs;
|
|
457
|
+
out.seed = reply.seed;
|
|
458
|
+
return out;
|
|
459
|
+
}
|
|
460
|
+
|
|
377
461
|
function registerSuite(
|
|
378
462
|
description: string,
|
|
379
463
|
callback: () => void,
|
|
@@ -515,7 +599,7 @@ function toCoveragePointReport(point: CoverPoint): CoveragePointReport {
|
|
|
515
599
|
}
|
|
516
600
|
|
|
517
601
|
function snapshotKey(): string {
|
|
518
|
-
if (!current_suite) return FILE + "::global
|
|
602
|
+
if (!current_suite) return FILE + "::global";
|
|
519
603
|
const suite = current_suite!;
|
|
520
604
|
const parts = new Array<string>();
|
|
521
605
|
let cursor: Suite | null = suite;
|
|
@@ -523,48 +607,19 @@ function snapshotKey(): string {
|
|
|
523
607
|
parts.unshift(cursor.description);
|
|
524
608
|
cursor = cursor.parent;
|
|
525
609
|
}
|
|
526
|
-
|
|
527
|
-
return FILE + "::" + path + "::" + suite.tests.length.toString();
|
|
610
|
+
return FILE + "::" + parts.join(" > ");
|
|
528
611
|
}
|
|
529
612
|
|
|
530
|
-
function
|
|
531
|
-
if (
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
const index = imp.index as u32;
|
|
537
|
-
if (__mock_import_target_by_index.has(index)) {
|
|
538
|
-
return __mock_import_target_by_index.get(index);
|
|
539
|
-
}
|
|
540
|
-
return index.toString();
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
function importSnapshotKey(importKey: string, version: string): string {
|
|
544
|
-
return importKey + "::" + version;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
function versionKey<V>(version: V): string {
|
|
548
|
-
if (isString<V>()) {
|
|
549
|
-
// @ts-ignore
|
|
550
|
-
return version as string;
|
|
551
|
-
}
|
|
552
|
-
if (isInteger<V>()) {
|
|
553
|
-
// @ts-ignore
|
|
554
|
-
return (<i64>version).toString();
|
|
555
|
-
}
|
|
556
|
-
ERROR("snapshot/restore version must be string or integer");
|
|
557
|
-
return "";
|
|
613
|
+
export function nextUnnamedSnapshotKey(baseKey: string): string {
|
|
614
|
+
if (!current_suite) return baseKey;
|
|
615
|
+
const suite = current_suite!;
|
|
616
|
+
suite.snapshotCount++;
|
|
617
|
+
if (suite.snapshotCount <= 1) return baseKey;
|
|
618
|
+
return baseKey + " #" + suite.snapshotCount.toString();
|
|
558
619
|
}
|
|
559
620
|
|
|
560
|
-
function
|
|
561
|
-
|
|
562
|
-
const snapshot = new ImportSnapshot();
|
|
563
|
-
if (__mock_import.has(importKey)) {
|
|
564
|
-
snapshot.hasValue = true;
|
|
565
|
-
snapshot.value = __mock_import.get(importKey);
|
|
566
|
-
}
|
|
567
|
-
__mock_import_snapshots.set(snapshotKey, snapshot);
|
|
621
|
+
export function namedSnapshotKey(baseKey: string, name: string): string {
|
|
622
|
+
return baseKey + " [" + name + "]";
|
|
568
623
|
}
|
|
569
624
|
|
|
570
625
|
export class Result {
|