as-test 1.2.0 → 1.4.0
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 +60 -0
- package/README.md +1 -4
- package/as-test.config.schema.json +15 -0
- package/assembly/coverage.ts +22 -26
- package/assembly/index.ts +68 -47
- package/assembly/src/expectation.ts +154 -123
- package/assembly/src/fuzz.ts +10 -10
- package/assembly/src/log.ts +3 -3
- package/assembly/src/mode.ts +55 -0
- package/assembly/src/reflect.ts +122 -0
- package/assembly/src/stringify.ts +240 -0
- package/assembly/src/suite.ts +48 -27
- package/assembly/src/tests.ts +7 -7
- package/assembly/util/wipc.ts +2 -2
- package/bin/build-worker-pool.js +9 -0
- package/bin/build-worker.js +27 -3
- package/bin/commands/build-core.js +293 -86
- package/bin/commands/build.js +3 -1
- package/bin/commands/init-core.js +253 -8
- package/bin/commands/run-core.js +165 -41
- package/bin/commands/run.js +2 -1
- package/bin/commands/test.js +3 -2
- package/bin/dependency-graph.js +0 -0
- package/bin/index.js +592 -97
- package/bin/reporters/default.js +34 -0
- package/bin/types.js +7 -0
- package/bin/util.js +52 -0
- package/package.json +4 -8
- package/transform/lib/equals.js +388 -0
- package/transform/lib/index.js +28 -0
- package/transform/lib/log.js +3 -7
- package/transform/lib/types.js +4 -2
- package/transform/lib/transform.js +0 -502
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2026-05-26 - v1.4.0
|
|
4
|
+
|
|
5
|
+
### Dependency-free value serialization (json-as removed)
|
|
6
|
+
|
|
7
|
+
- feat: as-test no longer depends on or auto-includes `json-as`. Value serialization for assertion reports, snapshots, and `log()` is now handled by a small in-tree stringifier (`assembly/src/stringify.ts`), so `npm install --save-dev as-test` is all you need — `json-as` is no longer a peer dependency.
|
|
8
|
+
- feat: `stringify<T>` renders a broad set of built-in types as JSON: booleans, integers, floats, strings (RFC 8259 escaping, including UTF-16 surrogate handling), `null`, `Date` (quoted ISO-8601), `ArrayBuffer` (array of unsigned byte values), typed arrays / `ArrayBufferView` (element array), `Array`, `StaticArray`, `Set` (value array), and `Map` (JSON object; non-string keys are coerced to quoted strings). Classes render via a transform-generated `toJSON()` or a `"<TypeName>"` placeholder.
|
|
9
|
+
- chore: removed the json-as peer-advisor (`transform/lib/peer-advisor.js`) and the json-as transform-passthrough integration test. Classes decorated `@json`/`@serializable` are skipped by the toJSON injector, so users who want json-as serialization can still add their own `toJSON()` and wire up `--transform json-as`.
|
|
10
|
+
|
|
11
|
+
### Structural deep equality for matchers
|
|
12
|
+
|
|
13
|
+
- feat: `.toBe()`, `.toEqual()`, and `.toStrictEqual()` now compare by structure rather than by reference. The EqualsTransform (`transform/lib/equals.js`) synthesises an `__as_test_equals(...)` method for every class that appears as an `expect()`/matcher operand, including nested classes reachable through their fields; the runtime entry point (`assembly/src/reflect.ts`'s `reflectEquals`) handles primitives, nullables, arrays, managed dispatch, and cycle detection, with a strict mode that also checks runtime type ids.
|
|
14
|
+
- feat: hand-written `__as_test_equals` methods are left untouched by the transform, and inheritance chains are supported via a super-call ignore-list pattern.
|
|
15
|
+
|
|
16
|
+
### Surfacing `log()` output
|
|
17
|
+
|
|
18
|
+
- feat: after a run, as-test now reports how many `log()` lines were captured and writes them to a single aggregated `.as-test/logs/latest.log`, e.g. `19 logs captured → .as-test/logs/latest.log`. The file groups logs by spec and de-duplicates identical output across modes, tagging each block with the modes that produced it: `[LOG] log.spec.ts (node:bindings, node:wasi):`.
|
|
19
|
+
- feat: `ast test --show-logs` (also on `ast run`) prints the captured logs as a clean grouped block at the end of the run instead of pointing at the file. In a normal run logs stay quiet (just the hint line); `--verbose` and non-TTY output still stream them inline as before.
|
|
20
|
+
- fix: the per-spec readable log's `Log:` section was always empty — it read a `value`/`message` field that never existed on log entries (the field is `text`). It now contains the captured logs.
|
|
21
|
+
|
|
22
|
+
### Suite / test counting
|
|
23
|
+
|
|
24
|
+
- change: every grouping block — `describe`, `test`, `it`, `only` (and their skip variants) — now counts as a **suite**, and each `expect()` assertion counts as a **test**. Previously `test`/`it`/`only` weren't counted as suites and an empty one was tallied as a single test, so an `it()` that contained assertions reported `Suites: 0`. As a result a top-level `it()`/`test()` failure now also appears in the end-of-run failure summary (with its location), instead of only the inline assertion line.
|
|
25
|
+
- fix: nested grouping blocks now actually nest. A `describe`/`it`/`test` declared inside another block is parented to the block whose callback is running (`current_suite`) rather than to a stale depth-indexed stack, so `describe`-in-`describe` no longer flattens (the inner block's children were previously attached to the outer block, leaving the inner one empty). The unused `suites`/`depth` registration globals were removed.
|
|
26
|
+
|
|
27
|
+
### Scoped beforeEach / afterEach
|
|
28
|
+
|
|
29
|
+
- feat: `beforeEach` and `afterEach` take an optional second argument listing the suite kinds they fire around — `beforeEach(() => {}, ["describe", "test"])`. With no argument the behavior is unchanged: hooks run around test cases (`test` / `it` / `only` and skip variants) and not around grouping blocks like `describe`.
|
|
30
|
+
|
|
31
|
+
### Watch mode + dependency graph
|
|
32
|
+
|
|
33
|
+
- feat: `ast test --watch` (`-w`) tracks a per-spec dependency graph (`cli/dependency-graph.ts`) built from the files `asc` actually loads during each build, so editing a shared helper re-runs only the specs that depend on it instead of the whole suite. `asc`'s bundled stdlib and the on-disk package are excluded from the graph to keep it small.
|
|
34
|
+
- feat: press `w` in watch mode to toggle auto-run off (manual invocation) and back on. While paused, edits are remembered but not run — invoke runs yourself with `a` (all) or `space` (retry failing); the footer shows how many changes are pending. Resuming re-runs everything if anything changed while paused.
|
|
35
|
+
|
|
36
|
+
### Breaking
|
|
37
|
+
|
|
38
|
+
- chore: `json-as` is no longer installed or auto-included by as-test. Projects that relied on as-test pulling in `json-as`, or on json-as-shaped serialization output in reports/snapshots, should install `json-as` themselves and add a `toJSON()` to the relevant classes. Existing snapshots whose serialized form changed will need `--overwrite-snapshots` once.
|
|
39
|
+
|
|
40
|
+
### Tooling
|
|
41
|
+
|
|
42
|
+
- ci: integration tests updated for the new serialization/equality paths (`tests/coverage-points.test.mjs`, new `tests/try-as-dedupe.test.mjs` and `tests/dependency-graph.test.mjs`).
|
|
43
|
+
|
|
44
|
+
## 2026-05-22 - v1.3.0
|
|
45
|
+
|
|
46
|
+
### `features` config array + arbitrary `--enable` passthrough
|
|
47
|
+
|
|
48
|
+
- feat: new top-level `"features": ["try-as", "simd"]` array in `as-test.config.json` (and per-mode override). `try-as` is the only as-test-internal feature today and wires the `try-as/transform` + `AS_TEST_TRY_AS=1` build flags as before. Any other name in the array is passed through to `asc` as `--enable <name>` — so `"simd"`, `"threads"`, `"reference-types"`, `"gc"`, etc. now work without hand-editing `buildOptions.args`.
|
|
49
|
+
- feat: `ast test|run|build --enable <name>` and `--disable <name>` now accept arbitrary feature names. CLI flags override the config array (CLI `--disable simd` removes a config-listed feature; CLI `--enable simd` adds it). The known special-case `coverage` flag still routes to the dedicated top-level `coverage` config field rather than the features array. Both flags accept comma-separated lists too: `--enable try-as,coverage,simd` and `--disable try-as,coverage`. The same syntax is honored by `ast init`.
|
|
50
|
+
- feat: `ast init` interactive prompt now includes a multi-select "Features" step (↑/↓ to move, space to toggle, enter to confirm) with `coverage` and `try-as` options. `--enable`/`--disable` flags on `ast init` skip the prompt with explicit selections. The generated `as-test.config.json` writes `coverage` at the top level and `features` as a string array; when try-as is selected, `try-as` is also added to `devDependencies`.
|
|
51
|
+
- chore: schema validation rejects malformed shapes (object form, non-string array entries) with a fix hint pointing at the new shape.
|
|
52
|
+
|
|
53
|
+
### `mode()` registration gate + `AS_TEST_MODE_NAME`
|
|
54
|
+
|
|
55
|
+
- feat: new `mode(matchers: string[], fn: () => void)` helper in the `as-test` runtime, plus an `AS_TEST_MODE_NAME: string` compile-time constant. Use `mode(["node:bindings"], () => { ... })` to gate suite/test registrations on the active mode name. Matcher semantics: positive entries OR; `!name` entries exclude; `[]` is a no-op; positive + negative entries combine as "any positive matches AND no negative matches."
|
|
56
|
+
- feat: build-side wiring — `build-core.ts` injects `AS_TEST_MODE_NAME=<mode>` into the asc env per-mode build, and the as-test transform rewrites the initializer of `AS_TEST_MODE_NAME` in `assembly/src/mode.ts` (an `afterParse` AST patch) so the value is baked into the wasm at compile time. Default value when no mode is selected is `"default"`.
|
|
57
|
+
- chore: bundled `assembly/__tests__/mode.spec.ts` exercises positive/negative/mixed matchers, empty matchers, and per-mode counter behaviour end-to-end against the project's own `node:bindings` / `node:wasi` modes.
|
|
58
|
+
|
|
59
|
+
### Tooling
|
|
60
|
+
|
|
61
|
+
- chore: `build:transform` now runs `prettier -w ./transform/` after the TypeScript build so generated output stays formatted.
|
|
62
|
+
|
|
3
63
|
## 2026-05-20 - v1.2.0
|
|
4
64
|
|
|
5
65
|
### Directory-preserving artifact layout
|
package/README.md
CHANGED
|
@@ -39,12 +39,9 @@ That gives you a basic config file, a sample test, and optionally a sample fuzze
|
|
|
39
39
|
If you already have a project and just want the package:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
npm install --save-dev as-test
|
|
42
|
+
npm install --save-dev as-test
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
`json-as` is a required peer dependency (used for value serialization in
|
|
46
|
-
assertions, snapshots, and `log()`)
|
|
47
|
-
|
|
48
45
|
## Docs
|
|
49
46
|
|
|
50
47
|
Full documentation lives at:
|
|
@@ -180,6 +180,14 @@
|
|
|
180
180
|
],
|
|
181
181
|
"default": false
|
|
182
182
|
},
|
|
183
|
+
"features": {
|
|
184
|
+
"type": "array",
|
|
185
|
+
"description": "Enabled feature names. \"try-as\" wires up the try-as transform; any other name is passed through to asc as --enable <name> (e.g. \"simd\", \"threads\", \"reference-types\"). CLI --enable/--disable flags override this list. Coverage has its own top-level \"coverage\" field.",
|
|
186
|
+
"items": {
|
|
187
|
+
"type": "string"
|
|
188
|
+
},
|
|
189
|
+
"default": []
|
|
190
|
+
},
|
|
183
191
|
"env": {
|
|
184
192
|
"description": "Environment variables injected when building/running. Accepts a .env file path, an array of KEY=value strings, or an object map.",
|
|
185
193
|
"oneOf": [
|
|
@@ -400,6 +408,13 @@
|
|
|
400
408
|
}
|
|
401
409
|
]
|
|
402
410
|
},
|
|
411
|
+
"features": {
|
|
412
|
+
"type": "array",
|
|
413
|
+
"description": "Mode-specific feature list. Replaces the base list entirely when set.",
|
|
414
|
+
"items": {
|
|
415
|
+
"type": "string"
|
|
416
|
+
}
|
|
417
|
+
},
|
|
403
418
|
"fuzz": {
|
|
404
419
|
"type": "object",
|
|
405
420
|
"additionalProperties": false,
|
package/assembly/coverage.ts
CHANGED
|
@@ -13,18 +13,17 @@ export class CoverPoint {
|
|
|
13
13
|
|
|
14
14
|
export class Coverage {
|
|
15
15
|
public all: CoverPoint[] = [];
|
|
16
|
-
public
|
|
17
|
-
public
|
|
18
|
-
public points: i32 = 0;
|
|
16
|
+
public byHash: Map<string, CoverPoint> = new Map<string, CoverPoint>();
|
|
17
|
+
public uncovered: i32 = 0;
|
|
19
18
|
static SN: Coverage = new Coverage();
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
export function __REGISTER(point: CoverPoint): void {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
const cov = Coverage.SN;
|
|
23
|
+
if (cov.byHash.has(point.hash)) return;
|
|
24
|
+
cov.byHash.set(point.hash, point);
|
|
25
|
+
cov.all.push(point);
|
|
26
|
+
cov.uncovered++;
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
export function __REGISTER_RAW(
|
|
@@ -38,7 +37,8 @@ export function __REGISTER_RAW(
|
|
|
38
37
|
scopeName: string = "",
|
|
39
38
|
depth: i32 = 0,
|
|
40
39
|
): void {
|
|
41
|
-
|
|
40
|
+
const cov = Coverage.SN;
|
|
41
|
+
if (cov.byHash.has(hash)) return;
|
|
42
42
|
const point = new CoverPoint();
|
|
43
43
|
point.file = file;
|
|
44
44
|
point.hash = hash;
|
|
@@ -49,32 +49,28 @@ export function __REGISTER_RAW(
|
|
|
49
49
|
point.scopeKind = scopeKind;
|
|
50
50
|
point.scopeName = scopeName;
|
|
51
51
|
point.depth = depth;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Coverage.SN.hashes.set(hash, point);
|
|
52
|
+
cov.byHash.set(hash, point);
|
|
53
|
+
cov.all.push(point);
|
|
54
|
+
cov.uncovered++;
|
|
56
55
|
}
|
|
57
56
|
|
|
57
|
+
// Hot path: invoked at every instrumented point. After first hit, subsequent
|
|
58
|
+
// hits short-circuit on `executed` before any writes.
|
|
58
59
|
export function __COVER(hash: string): void {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (Coverage.SN.hashes.has(hash)) Coverage.SN.hashes.delete(hash);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function __HASHES(): Map<string, CoverPoint> {
|
|
69
|
-
return Coverage.SN.hashes;
|
|
60
|
+
const cov = Coverage.SN;
|
|
61
|
+
if (!cov.byHash.has(hash)) return;
|
|
62
|
+
const point = cov.byHash.get(hash);
|
|
63
|
+
if (point.executed) return;
|
|
64
|
+
point.executed = true;
|
|
65
|
+
cov.uncovered--;
|
|
70
66
|
}
|
|
71
67
|
|
|
72
68
|
export function __POINTS(): i32 {
|
|
73
|
-
return Coverage.SN.
|
|
69
|
+
return Coverage.SN.all.length;
|
|
74
70
|
}
|
|
75
71
|
|
|
76
72
|
export function __UNCOVERED(): i32 {
|
|
77
|
-
return Coverage.SN.
|
|
73
|
+
return Coverage.SN.uncovered;
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
export function __ALL_POINTS(): CoverPoint[] {
|
package/assembly/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
sendFileStart,
|
|
15
15
|
sendReport,
|
|
16
16
|
} from "./util/wipc";
|
|
17
|
-
import {
|
|
17
|
+
import { escape, stringify } from "./src/stringify";
|
|
18
18
|
import { bold, green, red } from "./util/format";
|
|
19
19
|
import {
|
|
20
20
|
createFuzzer,
|
|
@@ -36,7 +36,8 @@ export {
|
|
|
36
36
|
IntegerOptions,
|
|
37
37
|
StringOptions,
|
|
38
38
|
} from "./src/fuzz";
|
|
39
|
-
export {
|
|
39
|
+
export { reflectEquals } from "./src/reflect";
|
|
40
|
+
export { stringify as __as_test_stringify } from "./src/stringify";
|
|
40
41
|
|
|
41
42
|
let entrySuites: Suite[] = [];
|
|
42
43
|
let entryFuzzers: FuzzerBase[] = [];
|
|
@@ -56,10 +57,6 @@ const FILE = isDefined(ENTRY_FILE) ? ENTRY_FILE : "unknown";
|
|
|
56
57
|
string
|
|
57
58
|
>();
|
|
58
59
|
// @ts-ignore
|
|
59
|
-
@global let suites: Suite[] = [];
|
|
60
|
-
// @ts-ignore
|
|
61
|
-
@global let depth: i32 = -1;
|
|
62
|
-
// @ts-ignore
|
|
63
60
|
@global let current_suite: Suite | null = null;
|
|
64
61
|
// @ts-ignore
|
|
65
62
|
let before_all_callback: (() => void) | null = null;
|
|
@@ -68,6 +65,11 @@ let after_all_callback: (() => void) | null = null;
|
|
|
68
65
|
|
|
69
66
|
export let before_each_callback: (() => void) | null = null;
|
|
70
67
|
export let after_each_callback: (() => void) | null = null;
|
|
68
|
+
// Suite kinds each hook fires before/after. `null` = the default set (test
|
|
69
|
+
// cases: test / it / only and their skip variants), excluding grouping blocks
|
|
70
|
+
// like `describe`. A caller-supplied list overrides this.
|
|
71
|
+
export let before_each_kinds: string[] | null = null;
|
|
72
|
+
export let after_each_kinds: string[] | null = null;
|
|
71
73
|
let __test_options!: RunOptions;
|
|
72
74
|
|
|
73
75
|
/**
|
|
@@ -240,7 +242,7 @@ export function xexpect<T>(
|
|
|
240
242
|
*/
|
|
241
243
|
export function log<T>(data: T): void {
|
|
242
244
|
if (!__as_test_log_is_enabled()) return;
|
|
243
|
-
__as_test_log_serialized(
|
|
245
|
+
__as_test_log_serialized(stringify<T>(data));
|
|
244
246
|
}
|
|
245
247
|
|
|
246
248
|
export function __as_test_log_is_enabled(): bool {
|
|
@@ -276,21 +278,43 @@ export function afterAll(callback: () => void): void {
|
|
|
276
278
|
}
|
|
277
279
|
|
|
278
280
|
/**
|
|
279
|
-
* Registers a callback
|
|
281
|
+
* Registers a callback to run before each matching block.
|
|
282
|
+
*
|
|
283
|
+
* By default it runs before each test case (`test` / `it` / `only`, plus their
|
|
284
|
+
* skip variants) and NOT before grouping blocks like `describe`. Pass `kinds`
|
|
285
|
+
* to run before exactly the listed suite kinds instead, e.g.
|
|
286
|
+
* `beforeEach(() => {}, ["describe", "test"])`.
|
|
280
287
|
*
|
|
281
|
-
* @param {() => void} callback - The function to
|
|
288
|
+
* @param {() => void} callback - The function to run.
|
|
289
|
+
* @param {string[] | null} kinds - Suite kinds to run before, or `null` for the
|
|
290
|
+
* default test-case kinds.
|
|
282
291
|
*/
|
|
283
|
-
export function beforeEach(
|
|
292
|
+
export function beforeEach(
|
|
293
|
+
callback: () => void,
|
|
294
|
+
kinds: string[] | null = null,
|
|
295
|
+
): void {
|
|
284
296
|
before_each_callback = callback;
|
|
297
|
+
before_each_kinds = kinds;
|
|
285
298
|
}
|
|
286
299
|
|
|
287
300
|
/**
|
|
288
|
-
* Registers a callback
|
|
301
|
+
* Registers a callback to run after each matching block.
|
|
302
|
+
*
|
|
303
|
+
* By default it runs after each test case (`test` / `it` / `only`, plus their
|
|
304
|
+
* skip variants) and NOT after grouping blocks like `describe`. Pass `kinds`
|
|
305
|
+
* to run after exactly the listed suite kinds instead, e.g.
|
|
306
|
+
* `afterEach(() => {}, ["describe", "test"])`.
|
|
289
307
|
*
|
|
290
|
-
* @param {() => void} callback - The function to
|
|
308
|
+
* @param {() => void} callback - The function to run.
|
|
309
|
+
* @param {string[] | null} kinds - Suite kinds to run after, or `null` for the
|
|
310
|
+
* default test-case kinds.
|
|
291
311
|
*/
|
|
292
|
-
export function afterEach(
|
|
312
|
+
export function afterEach(
|
|
313
|
+
callback: () => void,
|
|
314
|
+
kinds: string[] | null = null,
|
|
315
|
+
): void {
|
|
293
316
|
after_each_callback = callback;
|
|
317
|
+
after_each_kinds = kinds;
|
|
294
318
|
}
|
|
295
319
|
|
|
296
320
|
/**
|
|
@@ -374,10 +398,7 @@ export function run(options: RunOptions = new RunOptions()): void {
|
|
|
374
398
|
for (let i = 0; i < entrySuites.length; i++) {
|
|
375
399
|
// @ts-ignore
|
|
376
400
|
const suite = unchecked(entrySuites[i]);
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
current_suite = suite;
|
|
380
|
-
depth = -1;
|
|
401
|
+
// @ts-ignore: current_suite is a @global; null between top-level suites
|
|
381
402
|
current_suite = null;
|
|
382
403
|
|
|
383
404
|
if (hasTopLevelOnly && suite.kind != "only") {
|
|
@@ -393,8 +414,7 @@ export function run(options: RunOptions = new RunOptions()): void {
|
|
|
393
414
|
fileVerdict = "skip";
|
|
394
415
|
}
|
|
395
416
|
|
|
396
|
-
|
|
397
|
-
depth = -1;
|
|
417
|
+
// @ts-ignore: current_suite is a @global
|
|
398
418
|
current_suite = null;
|
|
399
419
|
}
|
|
400
420
|
time.end = performance.now();
|
|
@@ -402,7 +422,7 @@ export function run(options: RunOptions = new RunOptions()): void {
|
|
|
402
422
|
const report = new FileReport();
|
|
403
423
|
report.suites = entrySuites;
|
|
404
424
|
report.coverage = collectCoverage();
|
|
405
|
-
sendReport(report.
|
|
425
|
+
sendReport(report.toJSON());
|
|
406
426
|
}
|
|
407
427
|
|
|
408
428
|
function containsOnlySuites(values: Suite[]): bool {
|
|
@@ -422,11 +442,11 @@ class FuzzConfig {
|
|
|
422
442
|
class FuzzReport {
|
|
423
443
|
fuzzers: FuzzerResult[] = [];
|
|
424
444
|
|
|
425
|
-
|
|
445
|
+
toJSON(): string {
|
|
426
446
|
let out = '{"fuzzers":[';
|
|
427
447
|
for (let i = 0; i < this.fuzzers.length; i++) {
|
|
428
448
|
if (i) out += ",";
|
|
429
|
-
out += unchecked(this.fuzzers[i]).
|
|
449
|
+
out += unchecked(this.fuzzers[i]).toJSON();
|
|
430
450
|
}
|
|
431
451
|
out += "]}";
|
|
432
452
|
return out;
|
|
@@ -443,7 +463,7 @@ function runFuzzers(): void {
|
|
|
443
463
|
const result = fuzzer.run(config.seed, resolveFuzzerRuns(fuzzer, config));
|
|
444
464
|
report.fuzzers.push(result);
|
|
445
465
|
}
|
|
446
|
-
sendReport(report.
|
|
466
|
+
sendReport(report.toJSON());
|
|
447
467
|
}
|
|
448
468
|
|
|
449
469
|
function requestFuzzConfig(): FuzzConfig {
|
|
@@ -482,20 +502,19 @@ function registerSuite(
|
|
|
482
502
|
kind: string,
|
|
483
503
|
): void {
|
|
484
504
|
const suite = new Suite(description, callback, kind);
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
505
|
+
// Callbacks run lazily during the run phase, and `current_suite` is always
|
|
506
|
+
// the suite whose callback is currently executing (the same reference
|
|
507
|
+
// `expect()`/`log()` resolve against). So a describe/test/it registered from
|
|
508
|
+
// inside another block nests under it — including describe-in-describe.
|
|
509
|
+
// `current_suite` is null only at collection time, i.e. a top-level suite.
|
|
510
|
+
const parent = current_suite;
|
|
511
|
+
if (parent !== null) {
|
|
512
|
+
parent.addSuite(suite);
|
|
493
513
|
return;
|
|
494
514
|
}
|
|
495
515
|
|
|
496
516
|
suite.file = FILE;
|
|
497
517
|
entrySuites.push(suite);
|
|
498
|
-
suites.push(suite);
|
|
499
518
|
}
|
|
500
519
|
|
|
501
520
|
function resolveExpectationSuite(): Suite {
|
|
@@ -519,7 +538,7 @@ class CoverageReport {
|
|
|
519
538
|
percent: f64 = 100.0;
|
|
520
539
|
points: CoveragePointReport[] = [];
|
|
521
540
|
|
|
522
|
-
|
|
541
|
+
toJSON(): string {
|
|
523
542
|
return (
|
|
524
543
|
'{"total":' +
|
|
525
544
|
this.total.toString() +
|
|
@@ -548,26 +567,26 @@ class CoveragePointReport {
|
|
|
548
567
|
scopeName: string = "";
|
|
549
568
|
depth: i32 = 0;
|
|
550
569
|
|
|
551
|
-
|
|
570
|
+
toJSON(): string {
|
|
552
571
|
return (
|
|
553
572
|
'{"hash":' +
|
|
554
|
-
|
|
573
|
+
escape(this.hash) +
|
|
555
574
|
',"file":' +
|
|
556
|
-
|
|
575
|
+
escape(this.file) +
|
|
557
576
|
',"line":' +
|
|
558
577
|
this.line.toString() +
|
|
559
578
|
',"column":' +
|
|
560
579
|
this.column.toString() +
|
|
561
580
|
',"type":' +
|
|
562
|
-
|
|
581
|
+
escape(this.type) +
|
|
563
582
|
',"executed":' +
|
|
564
583
|
(this.executed ? "true" : "false") +
|
|
565
584
|
',"parentHash":' +
|
|
566
|
-
|
|
585
|
+
escape(this.parentHash) +
|
|
567
586
|
',"scopeKind":' +
|
|
568
|
-
|
|
587
|
+
escape(this.scopeKind) +
|
|
569
588
|
',"scopeName":' +
|
|
570
|
-
|
|
589
|
+
escape(this.scopeName) +
|
|
571
590
|
',"depth":' +
|
|
572
591
|
this.depth.toString() +
|
|
573
592
|
"}"
|
|
@@ -579,12 +598,12 @@ class FileReport {
|
|
|
579
598
|
suites: Suite[] = [];
|
|
580
599
|
coverage: CoverageReport = new CoverageReport();
|
|
581
600
|
|
|
582
|
-
|
|
601
|
+
toJSON(): string {
|
|
583
602
|
return (
|
|
584
603
|
'{"suites":' +
|
|
585
604
|
serializeSuites(this.suites) +
|
|
586
605
|
',"coverage":' +
|
|
587
|
-
this.coverage.
|
|
606
|
+
this.coverage.toJSON() +
|
|
588
607
|
"}"
|
|
589
608
|
);
|
|
590
609
|
}
|
|
@@ -595,7 +614,7 @@ function serializeSuites(values: Suite[]): string {
|
|
|
595
614
|
let out = "[";
|
|
596
615
|
for (let i = 0; i < values.length; i++) {
|
|
597
616
|
if (i) out += ",";
|
|
598
|
-
out += unchecked(values[i]).
|
|
617
|
+
out += unchecked(values[i]).toJSON();
|
|
599
618
|
}
|
|
600
619
|
out += "]";
|
|
601
620
|
return out;
|
|
@@ -606,7 +625,7 @@ function serializeCoveragePoints(values: CoveragePointReport[]): string {
|
|
|
606
625
|
let out = "[";
|
|
607
626
|
for (let i = 0; i < values.length; i++) {
|
|
608
627
|
if (i) out += ",";
|
|
609
|
-
out += unchecked(values[i]).
|
|
628
|
+
out += unchecked(values[i]).toJSON();
|
|
610
629
|
}
|
|
611
630
|
out += "]";
|
|
612
631
|
return out;
|
|
@@ -690,10 +709,10 @@ export class Result {
|
|
|
690
709
|
out += ` ${this.arg1 + this.arg2} total\n`;
|
|
691
710
|
return out;
|
|
692
711
|
}
|
|
693
|
-
|
|
712
|
+
toJSON(): string {
|
|
694
713
|
return (
|
|
695
714
|
'{"name":' +
|
|
696
|
-
|
|
715
|
+
escape(this.name) +
|
|
697
716
|
',"arg1":' +
|
|
698
717
|
this.arg1.toString() +
|
|
699
718
|
',"arg2":' +
|
|
@@ -710,7 +729,7 @@ export class Time {
|
|
|
710
729
|
return formatTime(this.end - this.start);
|
|
711
730
|
}
|
|
712
731
|
|
|
713
|
-
|
|
732
|
+
toJSON(): string {
|
|
714
733
|
return (
|
|
715
734
|
'{"start":' +
|
|
716
735
|
this.start.toString() +
|
|
@@ -752,3 +771,5 @@ function formatTime(time: f64): string {
|
|
|
752
771
|
|
|
753
772
|
return `${_us}μs`;
|
|
754
773
|
}
|
|
774
|
+
|
|
775
|
+
export { mode, AS_TEST_MODE_NAME } from "./src/mode";
|