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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2026-05-04 - v1.0.15
|
|
4
|
+
|
|
5
|
+
- fix: path resolving
|
|
6
|
+
-
|
|
7
|
+
## 2026-05-04 - v1.0.14
|
|
8
|
+
|
|
9
|
+
### Fuzzing
|
|
10
|
+
|
|
11
|
+
- feat: add `FuzzSeed` helpers for `i8`, `u8`, `i16`, `u16`, `i64`, `u64`, and `bool()`.
|
|
12
|
+
- feat: make integer `FuzzSeed` helpers default to the full range of their target type when no options are provided, instead of collapsing to `0`.
|
|
13
|
+
- perf: add unchecked full-range fast paths for default integer seed generation while keeping explicit user-provided ranges validated.
|
|
14
|
+
|
|
3
15
|
## 2025-05-03 - v1.0.13
|
|
4
16
|
|
|
5
17
|
- feat: add `--fuzzer` / `--fuzzers` filtering for `ast fuzz` and `ast test --fuzz`, accept `--suite` / `--suites` as fuzz aliases, and include target-specific repro commands in fuzz failure output.
|
package/README.md
CHANGED
|
@@ -365,6 +365,36 @@ If you want to keep more than one runtime around, use modes:
|
|
|
365
365
|
}
|
|
366
366
|
```
|
|
367
367
|
|
|
368
|
+
Modes can also be full config objects. That means a mode can override fuzzing, input globs, output aliases, runtime, build flags, and the rest of the normal config surface:
|
|
369
|
+
|
|
370
|
+
```json
|
|
371
|
+
{
|
|
372
|
+
"modes": {
|
|
373
|
+
"web": {
|
|
374
|
+
"fuzz": {
|
|
375
|
+
"input": ["./assembly/__fuzz__/web/*.fuzz.ts"],
|
|
376
|
+
"runs": 200
|
|
377
|
+
},
|
|
378
|
+
"runOptions": {
|
|
379
|
+
"runtime": {
|
|
380
|
+
"browser": "chromium"
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
If you prefer to keep one mode in a separate file, point the mode directly at that config file:
|
|
389
|
+
|
|
390
|
+
```json
|
|
391
|
+
{
|
|
392
|
+
"modes": {
|
|
393
|
+
"simd": "./as-test.config.simd.json"
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
368
398
|
Run a specific mode with:
|
|
369
399
|
|
|
370
400
|
```bash
|
|
@@ -294,11 +294,43 @@
|
|
|
294
294
|
},
|
|
295
295
|
"modes": {
|
|
296
296
|
"type": "object",
|
|
297
|
-
"description": "Named build/run modes. Each mode can
|
|
297
|
+
"description": "Named build/run modes. Each mode can be a config object or a path to another as-test config file.",
|
|
298
298
|
"additionalProperties": {
|
|
299
|
-
"
|
|
300
|
-
|
|
301
|
-
|
|
299
|
+
"oneOf": [
|
|
300
|
+
{
|
|
301
|
+
"type": "string",
|
|
302
|
+
"description": "Path to another as-test config file used as the selected mode's override config."
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
"type": "object",
|
|
306
|
+
"additionalProperties": false,
|
|
307
|
+
"properties": {
|
|
308
|
+
"$schema": {
|
|
309
|
+
"type": "string"
|
|
310
|
+
},
|
|
311
|
+
"input": {
|
|
312
|
+
"type": "array",
|
|
313
|
+
"items": {
|
|
314
|
+
"type": "string"
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
"output": {
|
|
318
|
+
"oneOf": [
|
|
319
|
+
{
|
|
320
|
+
"type": "string"
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
"type": "object",
|
|
324
|
+
"additionalProperties": false,
|
|
325
|
+
"properties": {
|
|
326
|
+
"build": { "type": "string" },
|
|
327
|
+
"logs": { "type": "string" },
|
|
328
|
+
"coverage": { "type": "string" },
|
|
329
|
+
"snapshots": { "type": "string" }
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
]
|
|
333
|
+
},
|
|
302
334
|
"outDir": {
|
|
303
335
|
"type": "string",
|
|
304
336
|
"description": "Mode-specific build output directory. If omitted, defaults to <outDir>/<mode-name>."
|
|
@@ -339,6 +371,38 @@
|
|
|
339
371
|
}
|
|
340
372
|
]
|
|
341
373
|
},
|
|
374
|
+
"fuzz": {
|
|
375
|
+
"type": "object",
|
|
376
|
+
"additionalProperties": false,
|
|
377
|
+
"description": "Mode-specific fuzz overrides applied before running `ast fuzz` or `ast test --fuzz` in this mode.",
|
|
378
|
+
"properties": {
|
|
379
|
+
"input": {
|
|
380
|
+
"type": "array",
|
|
381
|
+
"items": {
|
|
382
|
+
"type": "string"
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
"runs": {
|
|
386
|
+
"type": "number"
|
|
387
|
+
},
|
|
388
|
+
"seed": {
|
|
389
|
+
"type": "number"
|
|
390
|
+
},
|
|
391
|
+
"maxInputBytes": {
|
|
392
|
+
"type": "number"
|
|
393
|
+
},
|
|
394
|
+
"target": {
|
|
395
|
+
"type": "string",
|
|
396
|
+
"enum": ["bindings"]
|
|
397
|
+
},
|
|
398
|
+
"corpusDir": {
|
|
399
|
+
"type": "string"
|
|
400
|
+
},
|
|
401
|
+
"crashDir": {
|
|
402
|
+
"type": "string"
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
},
|
|
342
406
|
"buildOptions": {
|
|
343
407
|
"type": "object",
|
|
344
408
|
"additionalProperties": false,
|
|
@@ -469,7 +533,9 @@
|
|
|
469
533
|
}
|
|
470
534
|
]
|
|
471
535
|
}
|
|
472
|
-
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
]
|
|
473
539
|
},
|
|
474
540
|
"default": {}
|
|
475
541
|
},
|
|
@@ -45,8 +45,16 @@ declare module "as-test" {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
export interface FuzzSeed {
|
|
48
|
+
boolean(): boolean;
|
|
49
|
+
bool(): boolean;
|
|
50
|
+
i8(options?: IntellisenseIntegerOptions): number;
|
|
51
|
+
u8(options?: IntellisenseIntegerOptions): number;
|
|
52
|
+
i16(options?: IntellisenseIntegerOptions): number;
|
|
53
|
+
u16(options?: IntellisenseIntegerOptions): number;
|
|
48
54
|
i32(options?: IntellisenseIntegerOptions): number;
|
|
49
55
|
u32(options?: IntellisenseIntegerOptions): number;
|
|
56
|
+
i64(options?: IntellisenseIntegerOptions): number;
|
|
57
|
+
u64(options?: IntellisenseIntegerOptions): number;
|
|
50
58
|
f32(options?: IntellisenseFloatOptions): number;
|
|
51
59
|
f64(options?: IntellisenseFloatOptions): number;
|
|
52
60
|
bytes(options?: IntellisenseBytesOptions): Uint8Array;
|
package/assembly/src/fuzz.ts
CHANGED
|
@@ -34,13 +34,20 @@ export class ArrayOptions {
|
|
|
34
34
|
max: i32 = 16;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const DEFAULT_I32_OPTIONS = new IntegerOptions<i32>();
|
|
38
|
-
const DEFAULT_U32_OPTIONS = new IntegerOptions<u32>();
|
|
39
37
|
const DEFAULT_F32_OPTIONS = new FloatOptions<f32>();
|
|
40
38
|
const DEFAULT_F64_OPTIONS = new FloatOptions<f64>();
|
|
41
39
|
const DEFAULT_STRING_OPTIONS = new StringOptions();
|
|
42
40
|
const DEFAULT_BYTES_OPTIONS = new BytesOptions();
|
|
43
41
|
const DEFAULT_ARRAY_OPTIONS = new ArrayOptions();
|
|
42
|
+
const I64_SIGN_MASK: u64 = 0x8000000000000000;
|
|
43
|
+
const EMPTY_I8_EXCLUDE: i8[] = [];
|
|
44
|
+
const EMPTY_U8_EXCLUDE: u8[] = [];
|
|
45
|
+
const EMPTY_I16_EXCLUDE: i16[] = [];
|
|
46
|
+
const EMPTY_U16_EXCLUDE: u16[] = [];
|
|
47
|
+
const EMPTY_I32_EXCLUDE: i32[] = [];
|
|
48
|
+
const EMPTY_U32_EXCLUDE: u32[] = [];
|
|
49
|
+
const EMPTY_I64_EXCLUDE: i64[] = [];
|
|
50
|
+
const EMPTY_U64_EXCLUDE: u64[] = [];
|
|
44
51
|
|
|
45
52
|
export class FuzzSeed {
|
|
46
53
|
private state: u32;
|
|
@@ -61,19 +68,99 @@ export class FuzzSeed {
|
|
|
61
68
|
return (this.nextU32() & 1) == 1;
|
|
62
69
|
}
|
|
63
70
|
|
|
71
|
+
bool(): bool {
|
|
72
|
+
return this.boolean();
|
|
73
|
+
}
|
|
74
|
+
|
|
64
75
|
pick<T>(values: T[]): T {
|
|
65
76
|
if (!values.length) panic();
|
|
66
77
|
return unchecked(values[this.nextRange(0, values.length - 1)]);
|
|
67
78
|
}
|
|
68
79
|
|
|
80
|
+
i8(options: IntegerOptions<i8> | null = null): i8 {
|
|
81
|
+
if (options == null) {
|
|
82
|
+
return this.nextI8InRange(i8.MIN_VALUE, i8.MAX_VALUE, EMPTY_I8_EXCLUDE, false);
|
|
83
|
+
}
|
|
84
|
+
return this.nextI8InRange(options.min, options.max, options.exclude, true);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
u8(options: IntegerOptions<u8> | null = null): u8 {
|
|
88
|
+
if (options == null) {
|
|
89
|
+
return this.nextU8InRange(u8.MIN_VALUE, u8.MAX_VALUE, EMPTY_U8_EXCLUDE, false);
|
|
90
|
+
}
|
|
91
|
+
return this.nextU8InRange(options.min, options.max, options.exclude, true);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
i16(options: IntegerOptions<i16> | null = null): i16 {
|
|
95
|
+
if (options == null) {
|
|
96
|
+
return this.nextI16InRange(
|
|
97
|
+
i16.MIN_VALUE,
|
|
98
|
+
i16.MAX_VALUE,
|
|
99
|
+
EMPTY_I16_EXCLUDE,
|
|
100
|
+
false,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return this.nextI16InRange(options.min, options.max, options.exclude, true);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
u16(options: IntegerOptions<u16> | null = null): u16 {
|
|
107
|
+
if (options == null) {
|
|
108
|
+
return this.nextU16InRange(
|
|
109
|
+
u16.MIN_VALUE,
|
|
110
|
+
u16.MAX_VALUE,
|
|
111
|
+
EMPTY_U16_EXCLUDE,
|
|
112
|
+
false,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
return this.nextU16InRange(options.min, options.max, options.exclude, true);
|
|
116
|
+
}
|
|
117
|
+
|
|
69
118
|
i32(options: IntegerOptions<i32> | null = null): i32 {
|
|
70
|
-
|
|
71
|
-
|
|
119
|
+
if (options == null) {
|
|
120
|
+
return this.nextI32InRange(
|
|
121
|
+
i32.MIN_VALUE,
|
|
122
|
+
i32.MAX_VALUE,
|
|
123
|
+
EMPTY_I32_EXCLUDE,
|
|
124
|
+
false,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
return this.nextI32InRange(options.min, options.max, options.exclude, true);
|
|
72
128
|
}
|
|
73
129
|
|
|
74
130
|
u32(options: IntegerOptions<u32> | null = null): u32 {
|
|
75
|
-
|
|
76
|
-
|
|
131
|
+
if (options == null) {
|
|
132
|
+
return this.nextU32InRange(
|
|
133
|
+
u32.MIN_VALUE,
|
|
134
|
+
u32.MAX_VALUE,
|
|
135
|
+
EMPTY_U32_EXCLUDE,
|
|
136
|
+
false,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
return this.nextU32InRange(options.min, options.max, options.exclude, true);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
i64(options: IntegerOptions<i64> | null = null): i64 {
|
|
143
|
+
if (options == null) {
|
|
144
|
+
return this.nextI64InRange(
|
|
145
|
+
i64.MIN_VALUE,
|
|
146
|
+
i64.MAX_VALUE,
|
|
147
|
+
EMPTY_I64_EXCLUDE,
|
|
148
|
+
false,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
return this.nextI64InRange(options.min, options.max, options.exclude, true);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
u64(options: IntegerOptions<u64> | null = null): u64 {
|
|
155
|
+
if (options == null) {
|
|
156
|
+
return this.nextU64InRange(
|
|
157
|
+
u64.MIN_VALUE,
|
|
158
|
+
u64.MAX_VALUE,
|
|
159
|
+
EMPTY_U64_EXCLUDE,
|
|
160
|
+
false,
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
return this.nextU64InRange(options.min, options.max, options.exclude, true);
|
|
77
164
|
}
|
|
78
165
|
|
|
79
166
|
f32(options: FloatOptions<f32> | null = null): f32 {
|
|
@@ -201,8 +288,16 @@ export class FuzzSeed {
|
|
|
201
288
|
return 0;
|
|
202
289
|
}
|
|
203
290
|
|
|
204
|
-
private nextI32InRange(
|
|
205
|
-
|
|
291
|
+
private nextI32InRange(
|
|
292
|
+
min: i32,
|
|
293
|
+
max: i32,
|
|
294
|
+
exclude: i32[],
|
|
295
|
+
validateRange: bool = true,
|
|
296
|
+
): i32 {
|
|
297
|
+
if (validateRange && max < min) panic();
|
|
298
|
+
if (!validateRange && min == i32.MIN_VALUE && max == i32.MAX_VALUE) {
|
|
299
|
+
return <i32>this.nextU32();
|
|
300
|
+
}
|
|
206
301
|
if (!exclude.length) {
|
|
207
302
|
return max <= min ? min : min + <i32>(this.nextU32() % <u32>(max - min + 1));
|
|
208
303
|
}
|
|
@@ -215,8 +310,120 @@ export class FuzzSeed {
|
|
|
215
310
|
return min;
|
|
216
311
|
}
|
|
217
312
|
|
|
218
|
-
private
|
|
219
|
-
|
|
313
|
+
private nextI8InRange(
|
|
314
|
+
min: i8,
|
|
315
|
+
max: i8,
|
|
316
|
+
exclude: i8[],
|
|
317
|
+
validateRange: bool = true,
|
|
318
|
+
): i8 {
|
|
319
|
+
if (validateRange && max < min) panic();
|
|
320
|
+
if (!validateRange && min == i8.MIN_VALUE && max == i8.MAX_VALUE) {
|
|
321
|
+
return <i8>this.nextU32();
|
|
322
|
+
}
|
|
323
|
+
const left = <i32>min;
|
|
324
|
+
const right = <i32>max;
|
|
325
|
+
if (!exclude.length) {
|
|
326
|
+
return <i8>(
|
|
327
|
+
right <= left ? left : left + <i32>(this.nextU32() % <u32>(right - left + 1))
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
for (let attempts = 0; attempts < 1024; attempts++) {
|
|
331
|
+
const value = <i8>(
|
|
332
|
+
right <= left ? left : left + <i32>(this.nextU32() % <u32>(right - left + 1))
|
|
333
|
+
);
|
|
334
|
+
if (!containsValue<i8>(exclude, value)) return value;
|
|
335
|
+
}
|
|
336
|
+
panic();
|
|
337
|
+
return min;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
private nextU8InRange(
|
|
341
|
+
min: u8,
|
|
342
|
+
max: u8,
|
|
343
|
+
exclude: u8[],
|
|
344
|
+
validateRange: bool = true,
|
|
345
|
+
): u8 {
|
|
346
|
+
if (validateRange && max < min) panic();
|
|
347
|
+
if (!validateRange && min == u8.MIN_VALUE && max == u8.MAX_VALUE) {
|
|
348
|
+
return <u8>this.nextU32();
|
|
349
|
+
}
|
|
350
|
+
const left = <u32>min;
|
|
351
|
+
const right = <u32>max;
|
|
352
|
+
if (!exclude.length) {
|
|
353
|
+
return <u8>(right <= left ? left : left + (this.nextU32() % (right - left + 1)));
|
|
354
|
+
}
|
|
355
|
+
for (let attempts = 0; attempts < 1024; attempts++) {
|
|
356
|
+
const value = <u8>(
|
|
357
|
+
right <= left ? left : left + (this.nextU32() % (right - left + 1))
|
|
358
|
+
);
|
|
359
|
+
if (!containsValue<u8>(exclude, value)) return value;
|
|
360
|
+
}
|
|
361
|
+
panic();
|
|
362
|
+
return min;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
private nextI16InRange(
|
|
366
|
+
min: i16,
|
|
367
|
+
max: i16,
|
|
368
|
+
exclude: i16[],
|
|
369
|
+
validateRange: bool = true,
|
|
370
|
+
): i16 {
|
|
371
|
+
if (validateRange && max < min) panic();
|
|
372
|
+
if (!validateRange && min == i16.MIN_VALUE && max == i16.MAX_VALUE) {
|
|
373
|
+
return <i16>this.nextU32();
|
|
374
|
+
}
|
|
375
|
+
const left = <i32>min;
|
|
376
|
+
const right = <i32>max;
|
|
377
|
+
if (!exclude.length) {
|
|
378
|
+
return <i16>(
|
|
379
|
+
right <= left ? left : left + <i32>(this.nextU32() % <u32>(right - left + 1))
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
for (let attempts = 0; attempts < 1024; attempts++) {
|
|
383
|
+
const value = <i16>(
|
|
384
|
+
right <= left ? left : left + <i32>(this.nextU32() % <u32>(right - left + 1))
|
|
385
|
+
);
|
|
386
|
+
if (!containsValue<i16>(exclude, value)) return value;
|
|
387
|
+
}
|
|
388
|
+
panic();
|
|
389
|
+
return min;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private nextU16InRange(
|
|
393
|
+
min: u16,
|
|
394
|
+
max: u16,
|
|
395
|
+
exclude: u16[],
|
|
396
|
+
validateRange: bool = true,
|
|
397
|
+
): u16 {
|
|
398
|
+
if (validateRange && max < min) panic();
|
|
399
|
+
if (!validateRange && min == u16.MIN_VALUE && max == u16.MAX_VALUE) {
|
|
400
|
+
return <u16>this.nextU32();
|
|
401
|
+
}
|
|
402
|
+
const left = <u32>min;
|
|
403
|
+
const right = <u32>max;
|
|
404
|
+
if (!exclude.length) {
|
|
405
|
+
return <u16>(right <= left ? left : left + (this.nextU32() % (right - left + 1)));
|
|
406
|
+
}
|
|
407
|
+
for (let attempts = 0; attempts < 1024; attempts++) {
|
|
408
|
+
const value = <u16>(
|
|
409
|
+
right <= left ? left : left + (this.nextU32() % (right - left + 1))
|
|
410
|
+
);
|
|
411
|
+
if (!containsValue<u16>(exclude, value)) return value;
|
|
412
|
+
}
|
|
413
|
+
panic();
|
|
414
|
+
return min;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
private nextU32InRange(
|
|
418
|
+
min: u32,
|
|
419
|
+
max: u32,
|
|
420
|
+
exclude: u32[],
|
|
421
|
+
validateRange: bool = true,
|
|
422
|
+
): u32 {
|
|
423
|
+
if (validateRange && max < min) panic();
|
|
424
|
+
if (!validateRange && min == u32.MIN_VALUE && max == u32.MAX_VALUE) {
|
|
425
|
+
return this.nextU32();
|
|
426
|
+
}
|
|
220
427
|
if (!exclude.length) {
|
|
221
428
|
return max <= min ? min : min + (this.nextU32() % (max - min + 1));
|
|
222
429
|
}
|
|
@@ -228,6 +435,52 @@ export class FuzzSeed {
|
|
|
228
435
|
return min;
|
|
229
436
|
}
|
|
230
437
|
|
|
438
|
+
private nextI64InRange(
|
|
439
|
+
min: i64,
|
|
440
|
+
max: i64,
|
|
441
|
+
exclude: i64[],
|
|
442
|
+
validateRange: bool = true,
|
|
443
|
+
): i64 {
|
|
444
|
+
if (validateRange && max < min) panic();
|
|
445
|
+
if (!validateRange && min == i64.MIN_VALUE && max == i64.MAX_VALUE) {
|
|
446
|
+
return <i64>this.nextU64();
|
|
447
|
+
}
|
|
448
|
+
const left = this.toOrderedU64(min);
|
|
449
|
+
const right = this.toOrderedU64(max);
|
|
450
|
+
if (!exclude.length) {
|
|
451
|
+
return this.fromOrderedU64(
|
|
452
|
+
left + this.nextU64Offset(left, right),
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
for (let attempts = 0; attempts < 1024; attempts++) {
|
|
456
|
+
const value = this.fromOrderedU64(left + this.nextU64Offset(left, right));
|
|
457
|
+
if (!containsValue<i64>(exclude, value)) return value;
|
|
458
|
+
}
|
|
459
|
+
panic();
|
|
460
|
+
return min;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
private nextU64InRange(
|
|
464
|
+
min: u64,
|
|
465
|
+
max: u64,
|
|
466
|
+
exclude: u64[],
|
|
467
|
+
validateRange: bool = true,
|
|
468
|
+
): u64 {
|
|
469
|
+
if (validateRange && max < min) panic();
|
|
470
|
+
if (!validateRange && min == u64.MIN_VALUE && max == u64.MAX_VALUE) {
|
|
471
|
+
return this.nextU64();
|
|
472
|
+
}
|
|
473
|
+
if (!exclude.length) {
|
|
474
|
+
return min + this.nextU64Offset(min, max);
|
|
475
|
+
}
|
|
476
|
+
for (let attempts = 0; attempts < 1024; attempts++) {
|
|
477
|
+
const value = min + this.nextU64Offset(min, max);
|
|
478
|
+
if (!containsValue<u64>(exclude, value)) return value;
|
|
479
|
+
}
|
|
480
|
+
panic();
|
|
481
|
+
return min;
|
|
482
|
+
}
|
|
483
|
+
|
|
231
484
|
private nextF64InRange<T>(min: T, max: T, exclude: T[]): f64 {
|
|
232
485
|
const left = <f64>min;
|
|
233
486
|
const right = <f64>max;
|
|
@@ -268,6 +521,20 @@ export class FuzzSeed {
|
|
|
268
521
|
const lo = <u64>this.nextU32();
|
|
269
522
|
return (hi << 32) | lo;
|
|
270
523
|
}
|
|
524
|
+
|
|
525
|
+
private nextU64Offset(min: u64, max: u64): u64 {
|
|
526
|
+
if (max <= min) return 0;
|
|
527
|
+
if (min == 0 && max == u64.MAX_VALUE) return this.nextU64();
|
|
528
|
+
return this.nextU64() % (max - min + 1);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
private toOrderedU64(value: i64): u64 {
|
|
532
|
+
return <u64>value ^ I64_SIGN_MASK;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
private fromOrderedU64(value: u64): i64 {
|
|
536
|
+
return <i64>(value ^ I64_SIGN_MASK);
|
|
537
|
+
}
|
|
271
538
|
}
|
|
272
539
|
|
|
273
540
|
const ASCII_ALPHABET: i32[] = rangeChars(32, 126);
|
|
@@ -20,8 +20,8 @@ class BuildFailureError extends Error {
|
|
|
20
20
|
this.kind = args.kind;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
export async function build(configPath = DEFAULT_CONFIG_PATH, selectors = [], modeName, featureToggles = {}, overrides = {}) {
|
|
24
|
-
const loadedConfig = loadConfig(configPath, false);
|
|
23
|
+
export async function build(configPath = DEFAULT_CONFIG_PATH, selectors = [], modeName, featureToggles = {}, overrides = {}, resolvedConfig) {
|
|
24
|
+
const loadedConfig = resolvedConfig ?? loadConfig(configPath, false);
|
|
25
25
|
const mode = applyMode(loadedConfig, modeName);
|
|
26
26
|
const config = Object.assign(Object.create(Object.getPrototypeOf(mode.config)), mode.config);
|
|
27
27
|
config.buildOptions = Object.assign(Object.create(Object.getPrototypeOf(mode.config.buildOptions)), mode.config.buildOptions);
|
|
@@ -44,7 +44,9 @@ export async function build(configPath = DEFAULT_CONFIG_PATH, selectors = [], mo
|
|
|
44
44
|
...config.buildOptions.env,
|
|
45
45
|
AS_TEST_COVERAGE_ENABLED: coverageEnabled ? "1" : "0",
|
|
46
46
|
};
|
|
47
|
-
if (!
|
|
47
|
+
if (!resolvedConfig &&
|
|
48
|
+
!process.env.AS_TEST_BUILD_API &&
|
|
49
|
+
!hasCustomBuildCommand(config)) {
|
|
48
50
|
const pool = getSerialBuildWorkerPool();
|
|
49
51
|
for (const file of inputFiles) {
|
|
50
52
|
await pool.buildFileMode({
|
|
@@ -12,7 +12,8 @@ const MAX_DEFAULT_SEED = 0x7fffffff;
|
|
|
12
12
|
export async function fuzz(configPath = DEFAULT_CONFIG_PATH, selectors = [], modeName, overrides = {}, fuzzerSelectors = []) {
|
|
13
13
|
const loadedConfig = loadConfig(configPath, false);
|
|
14
14
|
const mode = applyMode(loadedConfig, modeName);
|
|
15
|
-
const
|
|
15
|
+
const activeConfig = mode.config;
|
|
16
|
+
const config = resolveFuzzConfig(activeConfig.fuzz, overrides);
|
|
16
17
|
const inputPatterns = resolveFuzzInputPatterns(config.input, selectors);
|
|
17
18
|
const inputFiles = (await glob(inputPatterns)).sort((a, b) => a.localeCompare(b));
|
|
18
19
|
if (!inputFiles.length) {
|
|
@@ -22,10 +23,10 @@ export async function fuzz(configPath = DEFAULT_CONFIG_PATH, selectors = [], mod
|
|
|
22
23
|
const results = [];
|
|
23
24
|
for (const file of inputFiles) {
|
|
24
25
|
const buildStartedAt = Date.now();
|
|
25
|
-
await build(configPath, [file], modeName, { coverage: false }, { target: "bindings", args: ["--use", "AS_TEST_FUZZ=1"], kind: "fuzz" });
|
|
26
|
+
await build(configPath, [file], modeName, { coverage: false }, { target: "bindings", args: ["--use", "AS_TEST_FUZZ=1"], kind: "fuzz" }, loadedConfig);
|
|
26
27
|
const buildFinishedAt = Date.now();
|
|
27
28
|
const buildTime = buildFinishedAt - buildStartedAt;
|
|
28
|
-
results.push(await runFuzzTarget(file,
|
|
29
|
+
results.push(await runFuzzTarget(file, activeConfig.outDir, duplicateBasenames, config, fuzzerSelectors, buildStartedAt, buildFinishedAt, buildTime, modeName));
|
|
29
30
|
}
|
|
30
31
|
return results;
|
|
31
32
|
}
|
package/bin/index.js
CHANGED
|
@@ -1390,7 +1390,7 @@ async function runTestMatrix(runFlags, configPath, selectors, suiteSelectors, fu
|
|
|
1390
1390
|
if (!fuzzEnabled) {
|
|
1391
1391
|
throw await buildNoTestFilesMatchedError(configPath, selectors);
|
|
1392
1392
|
}
|
|
1393
|
-
const fuzzFiles = await resolveSelectedFuzzFiles(configPath, selectors);
|
|
1393
|
+
const fuzzFiles = await resolveSelectedFuzzFiles(configPath, selectors, modes);
|
|
1394
1394
|
if (!fuzzFiles.length) {
|
|
1395
1395
|
throw await buildNoTestFilesMatchedError(configPath, selectors, true);
|
|
1396
1396
|
}
|
|
@@ -1514,7 +1514,7 @@ async function runFuzzModes(configPath, selectors, fuzzerSelectors, modes, rawAr
|
|
|
1514
1514
|
const overrides = resolveFuzzOverrides(rawArgs, "fuzz");
|
|
1515
1515
|
const parallelSettings = resolveFuzzParallelJobs(rawArgs);
|
|
1516
1516
|
const clean = rawArgs.includes("--clean");
|
|
1517
|
-
const fuzzFiles = await resolveSelectedFuzzFiles(configPath, selectors);
|
|
1517
|
+
const fuzzFiles = await resolveSelectedFuzzFiles(configPath, selectors, modes);
|
|
1518
1518
|
const { jobs, buildJobs, runJobs } = resolveEffectiveParallelJobs(parallelSettings, fuzzFiles.length);
|
|
1519
1519
|
if (jobs > 1) {
|
|
1520
1520
|
const results = await runFuzzMatrixResultsParallel(configPath, selectors, fuzzerSelectors, modes, overrides, jobs, buildJobs, runJobs, clean);
|
|
@@ -1798,7 +1798,7 @@ async function runTestMatrixParallel(runFlags, configPath, selectors, suiteSelec
|
|
|
1798
1798
|
if (!fuzzEnabled) {
|
|
1799
1799
|
throw await buildNoTestFilesMatchedError(configPath, selectors);
|
|
1800
1800
|
}
|
|
1801
|
-
const fuzzFiles = await resolveSelectedFuzzFiles(configPath, selectors);
|
|
1801
|
+
const fuzzFiles = await resolveSelectedFuzzFiles(configPath, selectors, modes);
|
|
1802
1802
|
if (!fuzzFiles.length) {
|
|
1803
1803
|
throw await buildNoTestFilesMatchedError(configPath, selectors, true);
|
|
1804
1804
|
}
|
|
@@ -1918,7 +1918,11 @@ async function runTestMatrixParallel(runFlags, configPath, selectors, suiteSelec
|
|
|
1918
1918
|
return failed;
|
|
1919
1919
|
}
|
|
1920
1920
|
async function runFuzzMatrixResultsParallel(configPath, selectors, fuzzerSelectors, modes, overrides, jobs, buildJobs, runJobs, clean) {
|
|
1921
|
-
const
|
|
1921
|
+
const filesByMode = new Map();
|
|
1922
|
+
for (const modeName of modes) {
|
|
1923
|
+
filesByMode.set(modeName, await resolveSelectedFuzzFiles(configPath, selectors, [modeName]));
|
|
1924
|
+
}
|
|
1925
|
+
const files = [...new Set([...filesByMode.values()].flat())].sort((a, b) => a.localeCompare(b));
|
|
1922
1926
|
if (!files.length) {
|
|
1923
1927
|
throw new Error(`No fuzz files matched: ${selectors.length ? selectors.join(", ") : "configured input patterns"}`);
|
|
1924
1928
|
}
|
|
@@ -1929,6 +1933,8 @@ async function runFuzzMatrixResultsParallel(configPath, selectors, fuzzerSelecto
|
|
|
1929
1933
|
const token = renderQueuedFileStart(queueDisplay, path.basename(file));
|
|
1930
1934
|
const fileResults = [];
|
|
1931
1935
|
for (const modeName of modes) {
|
|
1936
|
+
if (!(filesByMode.get(modeName)?.includes(file) ?? false))
|
|
1937
|
+
continue;
|
|
1932
1938
|
const modeResults = await fuzz(configPath, [file], modeName, overrides, fuzzerSelectors);
|
|
1933
1939
|
fileResults.push(...modeResults);
|
|
1934
1940
|
}
|
|
@@ -1942,19 +1948,22 @@ async function runFuzzMatrixResultsParallel(configPath, selectors, fuzzerSelecto
|
|
|
1942
1948
|
return ordered.flat();
|
|
1943
1949
|
}
|
|
1944
1950
|
async function runFuzzMatrixResults(configPath, selectors, fuzzerSelectors, modes, overrides, reporter) {
|
|
1945
|
-
const files = await resolveSelectedFuzzFiles(configPath, selectors);
|
|
1946
|
-
if (!files.length) {
|
|
1947
|
-
throw new Error(`No fuzz files matched: ${selectors.length ? selectors.join(", ") : "configured input patterns"}`);
|
|
1948
|
-
}
|
|
1949
1951
|
const results = [];
|
|
1950
|
-
for (const
|
|
1951
|
-
const
|
|
1952
|
-
|
|
1952
|
+
for (const modeName of modes) {
|
|
1953
|
+
const files = await resolveSelectedFuzzFiles(configPath, selectors, [modeName]);
|
|
1954
|
+
if (!files.length) {
|
|
1955
|
+
continue;
|
|
1956
|
+
}
|
|
1957
|
+
for (const file of files) {
|
|
1958
|
+
const fileResults = [];
|
|
1953
1959
|
const modeResults = await fuzz(configPath, [file], modeName, overrides, fuzzerSelectors);
|
|
1954
1960
|
fileResults.push(...modeResults);
|
|
1955
1961
|
results.push(...modeResults);
|
|
1962
|
+
reporter?.onFuzzFileComplete?.({ file, results: fileResults });
|
|
1956
1963
|
}
|
|
1957
|
-
|
|
1964
|
+
}
|
|
1965
|
+
if (!results.length) {
|
|
1966
|
+
throw new Error(`No fuzz files matched: ${selectors.length ? selectors.join(", ") : "configured input patterns"}`);
|
|
1958
1967
|
}
|
|
1959
1968
|
return results;
|
|
1960
1969
|
}
|
|
@@ -2214,13 +2223,21 @@ async function resolveSelectedFiles(configPath, selectors, warn = true) {
|
|
|
2214
2223
|
const specs = matches.filter((file) => file.endsWith(".spec.ts"));
|
|
2215
2224
|
return [...new Set(specs)].sort((a, b) => a.localeCompare(b));
|
|
2216
2225
|
}
|
|
2217
|
-
async function resolveSelectedFuzzFiles(configPath, selectors) {
|
|
2226
|
+
async function resolveSelectedFuzzFiles(configPath, selectors, modes = [undefined]) {
|
|
2218
2227
|
const resolvedConfigPath = configPath ?? path.join(process.cwd(), "./as-test.config.json");
|
|
2219
|
-
const
|
|
2220
|
-
const
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2228
|
+
const files = new Set();
|
|
2229
|
+
for (const modeName of modes) {
|
|
2230
|
+
const loaded = loadConfig(resolvedConfigPath, false);
|
|
2231
|
+
const applied = applyMode(loaded, modeName);
|
|
2232
|
+
const config = applied.config;
|
|
2233
|
+
const patterns = resolveFuzzPatterns(config.fuzz.input, selectors);
|
|
2234
|
+
const matches = await glob(patterns);
|
|
2235
|
+
for (const file of matches) {
|
|
2236
|
+
if (file.endsWith(".fuzz.ts"))
|
|
2237
|
+
files.add(file);
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
return [...files].sort((a, b) => a.localeCompare(b));
|
|
2224
2241
|
}
|
|
2225
2242
|
async function resolveSelectedTestInputs(configPath, selectors) {
|
|
2226
2243
|
const [specs, fuzz] = await Promise.all([
|
|
@@ -2662,9 +2679,9 @@ async function listExecutionPlan(command, configPath, selectors, modes, listFlag
|
|
|
2662
2679
|
return;
|
|
2663
2680
|
const specFiles = command == "fuzz" ? [] : await resolveSelectedFiles(configPath, selectors);
|
|
2664
2681
|
const fuzzFiles = command == "fuzz"
|
|
2665
|
-
? await resolveSelectedFuzzFiles(configPath, selectors)
|
|
2682
|
+
? await resolveSelectedFuzzFiles(configPath, selectors, modes)
|
|
2666
2683
|
: command == "test" && fuzzEnabled
|
|
2667
|
-
? await resolveSelectedFuzzFiles(configPath, selectors)
|
|
2684
|
+
? await resolveSelectedFuzzFiles(configPath, selectors, modes)
|
|
2668
2685
|
: [];
|
|
2669
2686
|
const files = command == "fuzz" ? fuzzFiles : specFiles;
|
|
2670
2687
|
if (!specFiles.length && !fuzzFiles.length) {
|
|
@@ -2710,8 +2727,7 @@ async function listExecutionPlan(command, configPath, selectors, modes, listFlag
|
|
|
2710
2727
|
}
|
|
2711
2728
|
}
|
|
2712
2729
|
const envOverrides = {
|
|
2713
|
-
...
|
|
2714
|
-
...(modeName ? (config.modes[modeName]?.env ?? {}) : {}),
|
|
2730
|
+
...active.env,
|
|
2715
2731
|
...(command == "build"
|
|
2716
2732
|
? active.buildOptions.env
|
|
2717
2733
|
: command == "run" || command == "test"
|