data-structure-typed 2.0.5 → 2.1.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 +1 -1
- package/COMMANDS.md +17 -0
- package/benchmark/report.html +13 -77
- package/benchmark/report.json +145 -177
- package/dist/cjs/data-structures/base/iterable-element-base.d.ts +186 -83
- package/dist/cjs/data-structures/base/iterable-element-base.js +149 -107
- package/dist/cjs/data-structures/base/iterable-element-base.js.map +1 -1
- package/dist/cjs/data-structures/base/iterable-entry-base.d.ts +95 -119
- package/dist/cjs/data-structures/base/iterable-entry-base.js +59 -116
- package/dist/cjs/data-structures/base/iterable-entry-base.js.map +1 -1
- package/dist/cjs/data-structures/base/linear-base.d.ts +250 -192
- package/dist/cjs/data-structures/base/linear-base.js +137 -274
- package/dist/cjs/data-structures/base/linear-base.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/avl-tree-counter.d.ts +126 -158
- package/dist/cjs/data-structures/binary-tree/avl-tree-counter.js +171 -205
- package/dist/cjs/data-structures/binary-tree/avl-tree-counter.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/avl-tree-multi-map.d.ts +100 -69
- package/dist/cjs/data-structures/binary-tree/avl-tree-multi-map.js +135 -87
- package/dist/cjs/data-structures/binary-tree/avl-tree-multi-map.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/avl-tree.d.ts +138 -149
- package/dist/cjs/data-structures/binary-tree/avl-tree.js +208 -195
- package/dist/cjs/data-structures/binary-tree/avl-tree.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/binary-tree.d.ts +476 -632
- package/dist/cjs/data-structures/binary-tree/binary-tree.js +594 -865
- package/dist/cjs/data-structures/binary-tree/binary-tree.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/bst.d.ts +258 -306
- package/dist/cjs/data-structures/binary-tree/bst.js +505 -481
- package/dist/cjs/data-structures/binary-tree/bst.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/red-black-tree.d.ts +107 -179
- package/dist/cjs/data-structures/binary-tree/red-black-tree.js +114 -209
- package/dist/cjs/data-structures/binary-tree/red-black-tree.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/tree-counter.d.ts +132 -154
- package/dist/cjs/data-structures/binary-tree/tree-counter.js +172 -203
- package/dist/cjs/data-structures/binary-tree/tree-counter.js.map +1 -1
- package/dist/cjs/data-structures/binary-tree/tree-multi-map.d.ts +72 -69
- package/dist/cjs/data-structures/binary-tree/tree-multi-map.js +105 -85
- package/dist/cjs/data-structures/binary-tree/tree-multi-map.js.map +1 -1
- package/dist/cjs/data-structures/graph/abstract-graph.d.ts +238 -233
- package/dist/cjs/data-structures/graph/abstract-graph.js +267 -237
- package/dist/cjs/data-structures/graph/abstract-graph.js.map +1 -1
- package/dist/cjs/data-structures/graph/directed-graph.d.ts +108 -224
- package/dist/cjs/data-structures/graph/directed-graph.js +146 -233
- package/dist/cjs/data-structures/graph/directed-graph.js.map +1 -1
- package/dist/cjs/data-structures/graph/map-graph.d.ts +49 -55
- package/dist/cjs/data-structures/graph/map-graph.js +56 -59
- package/dist/cjs/data-structures/graph/map-graph.js.map +1 -1
- package/dist/cjs/data-structures/graph/undirected-graph.d.ts +103 -146
- package/dist/cjs/data-structures/graph/undirected-graph.js +129 -149
- package/dist/cjs/data-structures/graph/undirected-graph.js.map +1 -1
- package/dist/cjs/data-structures/hash/hash-map.d.ts +164 -338
- package/dist/cjs/data-structures/hash/hash-map.js +270 -457
- package/dist/cjs/data-structures/hash/hash-map.js.map +1 -1
- package/dist/cjs/data-structures/heap/heap.d.ts +214 -289
- package/dist/cjs/data-structures/heap/heap.js +340 -349
- package/dist/cjs/data-structures/heap/heap.js.map +1 -1
- package/dist/cjs/data-structures/heap/max-heap.d.ts +11 -47
- package/dist/cjs/data-structures/heap/max-heap.js +11 -66
- package/dist/cjs/data-structures/heap/max-heap.js.map +1 -1
- package/dist/cjs/data-structures/heap/min-heap.d.ts +12 -47
- package/dist/cjs/data-structures/heap/min-heap.js +11 -66
- package/dist/cjs/data-structures/heap/min-heap.js.map +1 -1
- package/dist/cjs/data-structures/linked-list/doubly-linked-list.d.ts +231 -347
- package/dist/cjs/data-structures/linked-list/doubly-linked-list.js +368 -494
- package/dist/cjs/data-structures/linked-list/doubly-linked-list.js.map +1 -1
- package/dist/cjs/data-structures/linked-list/singly-linked-list.d.ts +261 -310
- package/dist/cjs/data-structures/linked-list/singly-linked-list.js +447 -466
- package/dist/cjs/data-structures/linked-list/singly-linked-list.js.map +1 -1
- package/dist/cjs/data-structures/linked-list/skip-linked-list.d.ts +0 -107
- package/dist/cjs/data-structures/linked-list/skip-linked-list.js +0 -100
- package/dist/cjs/data-structures/linked-list/skip-linked-list.js.map +1 -1
- package/dist/cjs/data-structures/priority-queue/max-priority-queue.d.ts +12 -56
- package/dist/cjs/data-structures/priority-queue/max-priority-queue.js +11 -78
- package/dist/cjs/data-structures/priority-queue/max-priority-queue.js.map +1 -1
- package/dist/cjs/data-structures/priority-queue/min-priority-queue.d.ts +11 -57
- package/dist/cjs/data-structures/priority-queue/min-priority-queue.js +10 -79
- package/dist/cjs/data-structures/priority-queue/min-priority-queue.js.map +1 -1
- package/dist/cjs/data-structures/priority-queue/priority-queue.d.ts +2 -61
- package/dist/cjs/data-structures/priority-queue/priority-queue.js +8 -83
- package/dist/cjs/data-structures/priority-queue/priority-queue.js.map +1 -1
- package/dist/cjs/data-structures/queue/deque.d.ts +227 -254
- package/dist/cjs/data-structures/queue/deque.js +309 -348
- package/dist/cjs/data-structures/queue/deque.js.map +1 -1
- package/dist/cjs/data-structures/queue/queue.d.ts +180 -201
- package/dist/cjs/data-structures/queue/queue.js +265 -248
- package/dist/cjs/data-structures/queue/queue.js.map +1 -1
- package/dist/cjs/data-structures/stack/stack.d.ts +124 -102
- package/dist/cjs/data-structures/stack/stack.js +181 -125
- package/dist/cjs/data-structures/stack/stack.js.map +1 -1
- package/dist/cjs/data-structures/trie/trie.d.ts +164 -165
- package/dist/cjs/data-structures/trie/trie.js +189 -172
- package/dist/cjs/data-structures/trie/trie.js.map +1 -1
- package/dist/cjs/interfaces/binary-tree.d.ts +56 -6
- package/dist/cjs/interfaces/graph.d.ts +16 -0
- package/dist/cjs/types/data-structures/base/base.d.ts +1 -1
- package/dist/cjs/types/data-structures/graph/abstract-graph.d.ts +4 -0
- package/dist/cjs/types/utils/utils.d.ts +1 -0
- package/dist/cjs/utils/utils.d.ts +1 -1
- package/dist/cjs/utils/utils.js +2 -1
- package/dist/cjs/utils/utils.js.map +1 -1
- package/dist/esm/data-structures/base/iterable-element-base.d.ts +186 -83
- package/dist/esm/data-structures/base/iterable-element-base.js +155 -107
- package/dist/esm/data-structures/base/iterable-element-base.js.map +1 -1
- package/dist/esm/data-structures/base/iterable-entry-base.d.ts +95 -119
- package/dist/esm/data-structures/base/iterable-entry-base.js +59 -116
- package/dist/esm/data-structures/base/iterable-entry-base.js.map +1 -1
- package/dist/esm/data-structures/base/linear-base.d.ts +250 -192
- package/dist/esm/data-structures/base/linear-base.js +137 -274
- package/dist/esm/data-structures/base/linear-base.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/avl-tree-counter.d.ts +126 -158
- package/dist/esm/data-structures/binary-tree/avl-tree-counter.js +171 -212
- package/dist/esm/data-structures/binary-tree/avl-tree-counter.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/avl-tree-multi-map.d.ts +100 -69
- package/dist/esm/data-structures/binary-tree/avl-tree-multi-map.js +133 -94
- package/dist/esm/data-structures/binary-tree/avl-tree-multi-map.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/avl-tree.d.ts +138 -149
- package/dist/esm/data-structures/binary-tree/avl-tree.js +206 -200
- package/dist/esm/data-structures/binary-tree/avl-tree.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/binary-tree.d.ts +476 -632
- package/dist/esm/data-structures/binary-tree/binary-tree.js +598 -874
- package/dist/esm/data-structures/binary-tree/binary-tree.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/bst.d.ts +258 -306
- package/dist/esm/data-structures/binary-tree/bst.js +507 -487
- package/dist/esm/data-structures/binary-tree/bst.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/red-black-tree.d.ts +107 -179
- package/dist/esm/data-structures/binary-tree/red-black-tree.js +114 -215
- package/dist/esm/data-structures/binary-tree/red-black-tree.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/tree-counter.d.ts +132 -154
- package/dist/esm/data-structures/binary-tree/tree-counter.js +175 -209
- package/dist/esm/data-structures/binary-tree/tree-counter.js.map +1 -1
- package/dist/esm/data-structures/binary-tree/tree-multi-map.d.ts +72 -69
- package/dist/esm/data-structures/binary-tree/tree-multi-map.js +103 -92
- package/dist/esm/data-structures/binary-tree/tree-multi-map.js.map +1 -1
- package/dist/esm/data-structures/graph/abstract-graph.d.ts +238 -233
- package/dist/esm/data-structures/graph/abstract-graph.js +267 -237
- package/dist/esm/data-structures/graph/abstract-graph.js.map +1 -1
- package/dist/esm/data-structures/graph/directed-graph.d.ts +108 -224
- package/dist/esm/data-structures/graph/directed-graph.js +145 -233
- package/dist/esm/data-structures/graph/directed-graph.js.map +1 -1
- package/dist/esm/data-structures/graph/map-graph.d.ts +49 -55
- package/dist/esm/data-structures/graph/map-graph.js +56 -59
- package/dist/esm/data-structures/graph/map-graph.js.map +1 -1
- package/dist/esm/data-structures/graph/undirected-graph.d.ts +103 -146
- package/dist/esm/data-structures/graph/undirected-graph.js +128 -149
- package/dist/esm/data-structures/graph/undirected-graph.js.map +1 -1
- package/dist/esm/data-structures/hash/hash-map.d.ts +164 -338
- package/dist/esm/data-structures/hash/hash-map.js +270 -457
- package/dist/esm/data-structures/hash/hash-map.js.map +1 -1
- package/dist/esm/data-structures/heap/heap.d.ts +214 -289
- package/dist/esm/data-structures/heap/heap.js +329 -349
- package/dist/esm/data-structures/heap/heap.js.map +1 -1
- package/dist/esm/data-structures/heap/max-heap.d.ts +11 -47
- package/dist/esm/data-structures/heap/max-heap.js +11 -66
- package/dist/esm/data-structures/heap/max-heap.js.map +1 -1
- package/dist/esm/data-structures/heap/min-heap.d.ts +12 -47
- package/dist/esm/data-structures/heap/min-heap.js +11 -66
- package/dist/esm/data-structures/heap/min-heap.js.map +1 -1
- package/dist/esm/data-structures/linked-list/doubly-linked-list.d.ts +231 -347
- package/dist/esm/data-structures/linked-list/doubly-linked-list.js +368 -495
- package/dist/esm/data-structures/linked-list/doubly-linked-list.js.map +1 -1
- package/dist/esm/data-structures/linked-list/singly-linked-list.d.ts +261 -310
- package/dist/esm/data-structures/linked-list/singly-linked-list.js +448 -467
- package/dist/esm/data-structures/linked-list/singly-linked-list.js.map +1 -1
- package/dist/esm/data-structures/linked-list/skip-linked-list.d.ts +0 -107
- package/dist/esm/data-structures/linked-list/skip-linked-list.js +0 -100
- package/dist/esm/data-structures/linked-list/skip-linked-list.js.map +1 -1
- package/dist/esm/data-structures/priority-queue/max-priority-queue.d.ts +12 -56
- package/dist/esm/data-structures/priority-queue/max-priority-queue.js +11 -78
- package/dist/esm/data-structures/priority-queue/max-priority-queue.js.map +1 -1
- package/dist/esm/data-structures/priority-queue/min-priority-queue.d.ts +11 -57
- package/dist/esm/data-structures/priority-queue/min-priority-queue.js +10 -79
- package/dist/esm/data-structures/priority-queue/min-priority-queue.js.map +1 -1
- package/dist/esm/data-structures/priority-queue/priority-queue.d.ts +2 -61
- package/dist/esm/data-structures/priority-queue/priority-queue.js +8 -83
- package/dist/esm/data-structures/priority-queue/priority-queue.js.map +1 -1
- package/dist/esm/data-structures/queue/deque.d.ts +227 -254
- package/dist/esm/data-structures/queue/deque.js +313 -348
- package/dist/esm/data-structures/queue/deque.js.map +1 -1
- package/dist/esm/data-structures/queue/queue.d.ts +180 -201
- package/dist/esm/data-structures/queue/queue.js +263 -248
- package/dist/esm/data-structures/queue/queue.js.map +1 -1
- package/dist/esm/data-structures/stack/stack.d.ts +124 -102
- package/dist/esm/data-structures/stack/stack.js +181 -125
- package/dist/esm/data-structures/stack/stack.js.map +1 -1
- package/dist/esm/data-structures/trie/trie.d.ts +164 -165
- package/dist/esm/data-structures/trie/trie.js +193 -172
- package/dist/esm/data-structures/trie/trie.js.map +1 -1
- package/dist/esm/interfaces/binary-tree.d.ts +56 -6
- package/dist/esm/interfaces/graph.d.ts +16 -0
- package/dist/esm/types/data-structures/base/base.d.ts +1 -1
- package/dist/esm/types/data-structures/graph/abstract-graph.d.ts +4 -0
- package/dist/esm/types/utils/utils.d.ts +1 -0
- package/dist/esm/utils/utils.d.ts +1 -1
- package/dist/esm/utils/utils.js +2 -1
- package/dist/esm/utils/utils.js.map +1 -1
- package/dist/umd/data-structure-typed.js +4685 -6477
- package/dist/umd/data-structure-typed.min.js +8 -6
- package/dist/umd/data-structure-typed.min.js.map +1 -1
- package/package.json +3 -4
- package/src/data-structures/base/iterable-element-base.ts +238 -115
- package/src/data-structures/base/iterable-entry-base.ts +96 -120
- package/src/data-structures/base/linear-base.ts +271 -277
- package/src/data-structures/binary-tree/avl-tree-counter.ts +198 -216
- package/src/data-structures/binary-tree/avl-tree-multi-map.ts +192 -101
- package/src/data-structures/binary-tree/avl-tree.ts +239 -206
- package/src/data-structures/binary-tree/binary-tree.ts +660 -889
- package/src/data-structures/binary-tree/bst.ts +568 -570
- package/src/data-structures/binary-tree/red-black-tree.ts +161 -222
- package/src/data-structures/binary-tree/tree-counter.ts +199 -218
- package/src/data-structures/binary-tree/tree-multi-map.ts +131 -97
- package/src/data-structures/graph/abstract-graph.ts +339 -264
- package/src/data-structures/graph/directed-graph.ts +146 -236
- package/src/data-structures/graph/map-graph.ts +63 -60
- package/src/data-structures/graph/undirected-graph.ts +129 -152
- package/src/data-structures/hash/hash-map.ts +274 -496
- package/src/data-structures/heap/heap.ts +389 -402
- package/src/data-structures/heap/max-heap.ts +12 -76
- package/src/data-structures/heap/min-heap.ts +13 -76
- package/src/data-structures/linked-list/doubly-linked-list.ts +426 -530
- package/src/data-structures/linked-list/singly-linked-list.ts +495 -517
- package/src/data-structures/linked-list/skip-linked-list.ts +1 -108
- package/src/data-structures/priority-queue/max-priority-queue.ts +12 -87
- package/src/data-structures/priority-queue/min-priority-queue.ts +11 -88
- package/src/data-structures/priority-queue/priority-queue.ts +3 -92
- package/src/data-structures/queue/deque.ts +381 -357
- package/src/data-structures/queue/queue.ts +310 -264
- package/src/data-structures/stack/stack.ts +217 -131
- package/src/data-structures/trie/trie.ts +240 -175
- package/src/interfaces/binary-tree.ts +240 -6
- package/src/interfaces/graph.ts +37 -0
- package/src/types/data-structures/base/base.ts +5 -5
- package/src/types/data-structures/graph/abstract-graph.ts +5 -0
- package/src/types/utils/utils.ts +2 -0
- package/src/utils/utils.ts +9 -14
- package/test/integration/index.html +1 -1
- package/test/performance/benchmark-runner.ts +528 -0
- package/test/performance/reportor.mjs +43 -43
- package/test/performance/runner-config.json +39 -0
- package/test/performance/single-suite-runner.ts +69 -0
- package/test/unit/data-structures/binary-tree/avl-tree-counter.test.ts +3 -3
- package/test/unit/data-structures/binary-tree/avl-tree-multi-map.test.ts +5 -5
- package/test/unit/data-structures/binary-tree/avl-tree.test.ts +4 -4
- package/test/unit/data-structures/binary-tree/binary-tree.test.ts +350 -90
- package/test/unit/data-structures/binary-tree/bst.test.ts +12 -9
- package/test/unit/data-structures/binary-tree/red-black-tree.test.ts +2 -2
- package/test/unit/data-structures/binary-tree/tree-counter.test.ts +25 -24
- package/test/unit/data-structures/binary-tree/tree-multi-map.test.ts +3 -3
- package/test/unit/data-structures/graph/abstract-graph.test.ts +0 -4
- package/test/unit/data-structures/graph/directed-graph.test.ts +1 -1
- package/test/unit/data-structures/heap/heap.test.ts +14 -21
- package/test/unit/data-structures/heap/max-heap.test.ts +5 -9
- package/test/unit/data-structures/heap/min-heap.test.ts +1 -4
- package/test/unit/data-structures/linked-list/doubly-linked-list.test.ts +14 -14
- package/test/unit/data-structures/linked-list/singly-linked-list.test.ts +0 -7
- package/test/unit/data-structures/priority-queue/max-priority-queue.test.ts +8 -11
- package/test/unit/data-structures/priority-queue/min-priority-queue.test.ts +1 -4
- package/test/unit/data-structures/priority-queue/priority-queue.test.ts +1 -4
- package/test/unit/data-structures/queue/queue.test.ts +4 -5
- package/test/unit/utils/utils.test.ts +0 -1
- package/test/performance/data-structures/binary-tree/avl-tree.test.mjs +0 -71
- package/test/performance/data-structures/binary-tree/red-black-tree.test.mjs +0 -81
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
import * as Benchmark from 'benchmark';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as fastGlob from 'fast-glob';
|
|
5
|
+
import { fork } from 'child_process';
|
|
6
|
+
import { ConsoleColor, numberFix } from '../utils';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Optimized benchmark runner
|
|
10
|
+
* Features:
|
|
11
|
+
* - Optional per-suite isolation via child_process (--isolate)
|
|
12
|
+
* - GC + cooldown between suites (--gc, --cooldown-ms=50)
|
|
13
|
+
* - Shuffle or custom order (--shuffle, --order=heap,avl-tree,...)
|
|
14
|
+
* - Arg tokens still filter test files like before
|
|
15
|
+
* - Maintains JSON/HTML report + README injection
|
|
16
|
+
*
|
|
17
|
+
* Example:
|
|
18
|
+
* ts-node benchmark-runner.optimized.ts --isolate --gc --cooldown-ms=80 heap set get
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
// ---- CLI parsing (lightweight) ----
|
|
22
|
+
type Flags = {
|
|
23
|
+
isolate: boolean;
|
|
24
|
+
gc: boolean;
|
|
25
|
+
cooldownMs: number;
|
|
26
|
+
shuffle: boolean;
|
|
27
|
+
order?: string[];
|
|
28
|
+
orderFile?: string;
|
|
29
|
+
include?: string[];
|
|
30
|
+
exclude?: string[];
|
|
31
|
+
label?: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function parseArgs(argv: string[]) {
|
|
35
|
+
const flags: Flags = {
|
|
36
|
+
isolate: false,
|
|
37
|
+
gc: false,
|
|
38
|
+
cooldownMs: 50,
|
|
39
|
+
shuffle: false,
|
|
40
|
+
order: undefined,
|
|
41
|
+
orderFile: undefined,
|
|
42
|
+
include: undefined,
|
|
43
|
+
exclude: undefined,
|
|
44
|
+
label: undefined
|
|
45
|
+
};
|
|
46
|
+
const filters: string[] = [];
|
|
47
|
+
argv.forEach(raw => {
|
|
48
|
+
if (!raw.startsWith('--')) {
|
|
49
|
+
filters.push(raw);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const [k, v] = raw.replace(/^--/, '').split('=');
|
|
53
|
+
switch (k) {
|
|
54
|
+
case 'isolate':
|
|
55
|
+
flags.isolate = true;
|
|
56
|
+
break;
|
|
57
|
+
case 'gc':
|
|
58
|
+
flags.gc = true;
|
|
59
|
+
break;
|
|
60
|
+
case 'shuffle':
|
|
61
|
+
flags.shuffle = true;
|
|
62
|
+
break;
|
|
63
|
+
case 'cooldown-ms':
|
|
64
|
+
flags.cooldownMs = v ? Number(v) : flags.cooldownMs;
|
|
65
|
+
break;
|
|
66
|
+
case 'order':
|
|
67
|
+
flags.order = (v ?? '')
|
|
68
|
+
.split(',')
|
|
69
|
+
.map(s => s.trim())
|
|
70
|
+
.filter(Boolean);
|
|
71
|
+
break;
|
|
72
|
+
case 'order-file':
|
|
73
|
+
flags.orderFile = v || '';
|
|
74
|
+
break;
|
|
75
|
+
case 'include':
|
|
76
|
+
flags.include = (v ?? '')
|
|
77
|
+
.split(',')
|
|
78
|
+
.map(s => s.trim())
|
|
79
|
+
.filter(Boolean);
|
|
80
|
+
break;
|
|
81
|
+
case 'exclude':
|
|
82
|
+
flags.exclude = (v ?? '')
|
|
83
|
+
.split(',')
|
|
84
|
+
.map(s => s.trim())
|
|
85
|
+
.filter(Boolean);
|
|
86
|
+
break;
|
|
87
|
+
case 'label':
|
|
88
|
+
flags.label = v || '';
|
|
89
|
+
break;
|
|
90
|
+
default:
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
return { flags, filters };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const argv = process.argv.slice(2);
|
|
98
|
+
const { flags, filters } = parseArgs(argv);
|
|
99
|
+
|
|
100
|
+
const { GREEN, BOLD, END, YELLOW, GRAY, CYAN, BG_YELLOW } = ConsoleColor;
|
|
101
|
+
|
|
102
|
+
// ---- Optional runOrder config support (order/include/exclude/label) ----
|
|
103
|
+
type RunConfig = { order?: string[]; include?: string[]; exclude?: string[]; label?: string };
|
|
104
|
+
|
|
105
|
+
function loadRunConfigFromFile(filePath?: string): RunConfig | null {
|
|
106
|
+
const p1 = filePath ? path.resolve(process.cwd(), filePath) : path.resolve(__dirname, 'runner-config.json');
|
|
107
|
+
const p2 = filePath ? undefined : path.resolve(__dirname, 'run-order.json');
|
|
108
|
+
const candidates = [p1, p2].filter(Boolean) as string[];
|
|
109
|
+
for (const p of candidates) {
|
|
110
|
+
try {
|
|
111
|
+
if (fs.existsSync(p)) {
|
|
112
|
+
const data = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
113
|
+
if (Array.isArray(data)) {
|
|
114
|
+
console.log(`${YELLOW}Using run config from file:${END} ${p}`);
|
|
115
|
+
return { order: data as string[] };
|
|
116
|
+
} else if (data && typeof data === 'object') {
|
|
117
|
+
const cfg: RunConfig = {};
|
|
118
|
+
if (Array.isArray((data as any).order))
|
|
119
|
+
cfg.order = (data as any).order.filter((x: any) => typeof x === 'string');
|
|
120
|
+
if (Array.isArray((data as any).include))
|
|
121
|
+
cfg.include = (data as any).include.filter((x: any) => typeof x === 'string');
|
|
122
|
+
if (Array.isArray((data as any).exclude))
|
|
123
|
+
cfg.exclude = (data as any).exclude.filter((x: any) => typeof x === 'string');
|
|
124
|
+
if (typeof (data as any).label === 'string') cfg.label = (data as any).label;
|
|
125
|
+
console.log(`${YELLOW}Using run config from file:${END} ${p}`);
|
|
126
|
+
return cfg;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {
|
|
130
|
+
console.warn(`Failed to load run config from ${p}:`, e);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function loadRunOrderFromFile(filePath?: string): string[] | null {
|
|
137
|
+
const p1 = filePath ? path.resolve(process.cwd(), filePath) : path.resolve(__dirname, 'runner-config.json');
|
|
138
|
+
const p2 = filePath ? undefined : path.resolve(__dirname, 'run-order.json');
|
|
139
|
+
const candidates = [p1, p2].filter(Boolean) as string[];
|
|
140
|
+
for (const p of candidates) {
|
|
141
|
+
try {
|
|
142
|
+
if (fs.existsSync(p)) {
|
|
143
|
+
const arr = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
144
|
+
if (Array.isArray(arr) && arr.every((x: any) => typeof x === 'string')) {
|
|
145
|
+
console.log(`${YELLOW}Using runOrder from file:${END} ${p}`);
|
|
146
|
+
return arr;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
} catch (e) {
|
|
150
|
+
console.warn(`Failed to load order from ${p}:`, e);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const defaultRunOrder = [
|
|
157
|
+
'heap',
|
|
158
|
+
'avl-tree',
|
|
159
|
+
'red-black-tree',
|
|
160
|
+
'doubly-linked-list',
|
|
161
|
+
'linked-hash-map',
|
|
162
|
+
'hash-map',
|
|
163
|
+
'map-graph',
|
|
164
|
+
'graph',
|
|
165
|
+
'directed-graph',
|
|
166
|
+
'undirected-graph',
|
|
167
|
+
'queue',
|
|
168
|
+
'deque',
|
|
169
|
+
'priority-queue',
|
|
170
|
+
'singly-linked-list',
|
|
171
|
+
'binary-tree-overall',
|
|
172
|
+
'bst',
|
|
173
|
+
'trie',
|
|
174
|
+
'stack'
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
const cfg = loadRunConfigFromFile(flags.orderFile);
|
|
178
|
+
const fileOrder = cfg?.order || loadRunOrderFromFile(flags.orderFile);
|
|
179
|
+
const runOrder = flags.order && flags.order.length ? flags.order : fileOrder || defaultRunOrder;
|
|
180
|
+
|
|
181
|
+
const getRelativePath = (file: string) => path.relative(__dirname, file);
|
|
182
|
+
|
|
183
|
+
// ---- Selection helpers (include/exclude) ----
|
|
184
|
+
function fileRel(file: string) {
|
|
185
|
+
return path.relative(testDir, file).replace(/\\/g, '/');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function makeMatcher(rule: string): (s: string) => boolean {
|
|
189
|
+
// If there is no wildcard, the substring matches and is compatible with your original behavior
|
|
190
|
+
if (!/[?*]/.test(rule)) {
|
|
191
|
+
return (s: string) => s.includes(rule);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Use POSIX delimiters uniformly
|
|
195
|
+
const norm = rule.replace(/\\/g, '/');
|
|
196
|
+
|
|
197
|
+
// Escape regular special characters first, but keep * and ?
|
|
198
|
+
const esc = norm.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
199
|
+
|
|
200
|
+
// To process ** (across multi-level directories), use sentinel placeholders first to avoid conflicts with the subsequent replacement of *
|
|
201
|
+
const GLOBSTAR = '<<GLOBSTAR>>';
|
|
202
|
+
const withSentinel = esc.replace(/\*\*/g, GLOBSTAR);
|
|
203
|
+
|
|
204
|
+
// * => Multiple characters that do not cross directories, ? => Single characters that do not cross directories
|
|
205
|
+
const seg = withSentinel.replace(/\*/g, '[^/]*').replace(/\?/g, '[^/]');
|
|
206
|
+
|
|
207
|
+
// Restore ** => any character (can cross directories)
|
|
208
|
+
const regexSource = '^' + seg.split(GLOBSTAR).join('.*') + '$';
|
|
209
|
+
const re = new RegExp(regexSource);
|
|
210
|
+
|
|
211
|
+
return (s: string) => re.test(s.replace(/\\/g, '/'));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function applyIncludeExclude(files: string[], include?: string[], exclude?: string[]) {
|
|
215
|
+
let res = [...files];
|
|
216
|
+
if (include && include.length) {
|
|
217
|
+
const matchers = include.map(makeMatcher);
|
|
218
|
+
res = res.filter(f => matchers.some(m => m(fileRel(f))));
|
|
219
|
+
}
|
|
220
|
+
if (exclude && exclude.length) {
|
|
221
|
+
const matchers = exclude.map(makeMatcher);
|
|
222
|
+
res = res.filter(f => !matchers.some(m => m(fileRel(f))));
|
|
223
|
+
}
|
|
224
|
+
return res;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const coloredLabeled = (label: string, file: string) => {
|
|
228
|
+
const relativeFilePath = getRelativePath(file);
|
|
229
|
+
const directory = path.dirname(relativeFilePath);
|
|
230
|
+
const fileName = path.basename(relativeFilePath);
|
|
231
|
+
return `${BG_YELLOW} ${label} ${END} ${GRAY}${directory}/${END}${CYAN}${fileName}${END}`;
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const parentDirectory = path.resolve(__dirname, '../..');
|
|
235
|
+
const reportDistPath = path.join(parentDirectory, 'benchmark');
|
|
236
|
+
|
|
237
|
+
const testDir = path.join(__dirname, 'data-structures');
|
|
238
|
+
const allFiles = fastGlob.sync([path.join(testDir, '**', '*.test.ts'), path.join(testDir, '**', '*.test.mjs')]);
|
|
239
|
+
let testFiles: string[] = [];
|
|
240
|
+
|
|
241
|
+
// Filters: same semantics as your original runner (non -- args are match substrings)
|
|
242
|
+
if (filters.length > 0) {
|
|
243
|
+
console.log(`arguments: ${filters.join(' ')}`);
|
|
244
|
+
testFiles = allFiles.filter(file => filters.every(word => file.includes(word)));
|
|
245
|
+
} else {
|
|
246
|
+
testFiles = [...allFiles];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Apply include/exclude from config and CLI
|
|
250
|
+
const includeRules = [...(cfg?.include || []), ...(flags.include || [])];
|
|
251
|
+
const excludeRules = [...(cfg?.exclude || []), ...(flags.exclude || [])];
|
|
252
|
+
if (includeRules.length || excludeRules.length) {
|
|
253
|
+
testFiles = applyIncludeExclude(testFiles, includeRules, excludeRules);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// sort by runOrder, optionally shuffle
|
|
257
|
+
function sortByOrder(files: string[]): string[] {
|
|
258
|
+
type Item = { file: string; name: string; idx: number };
|
|
259
|
+
const items: Item[] = files.map(file => {
|
|
260
|
+
const name = path.basename(file, '.test.ts');
|
|
261
|
+
const idx = runOrder.indexOf(name);
|
|
262
|
+
return { file, name, idx: idx === -1 ? Number.MAX_SAFE_INTEGER : idx };
|
|
263
|
+
});
|
|
264
|
+
items.sort((a, b) => a.idx - b.idx || a.name.localeCompare(b.name));
|
|
265
|
+
return items.map(i => i.file);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function shuffle<T>(arr: T[]): T[] {
|
|
269
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
270
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
271
|
+
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
272
|
+
}
|
|
273
|
+
return arr;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const discoveredTotal = allFiles.length;
|
|
277
|
+
|
|
278
|
+
testFiles = sortByOrder(testFiles);
|
|
279
|
+
if (flags.shuffle) shuffle(testFiles);
|
|
280
|
+
|
|
281
|
+
const plannedCount = testFiles.length;
|
|
282
|
+
const isIndividual = filters.length > 0;
|
|
283
|
+
|
|
284
|
+
// ---------------- report utils (kept from original) ----------------
|
|
285
|
+
const report: { [key: string]: any } = {};
|
|
286
|
+
let completedCount = 0;
|
|
287
|
+
|
|
288
|
+
function ensureDist() {
|
|
289
|
+
if (!fs.existsSync(reportDistPath)) fs.mkdirSync(reportDistPath, { recursive: true });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function writeReportHTMLAndJSON(htmlTables: string) {
|
|
293
|
+
ensureDist();
|
|
294
|
+
const filePath = path.join(reportDistPath, 'report.json');
|
|
295
|
+
const htmlFilePath = path.join(reportDistPath, 'report.html');
|
|
296
|
+
fs.writeFileSync(filePath, JSON.stringify(report, null, 2));
|
|
297
|
+
const html = `<!DOCTYPE html>
|
|
298
|
+
<html lang="en"><head><meta charset="UTF-8"/><title>Benchmark Report</title>
|
|
299
|
+
<style>
|
|
300
|
+
body { margin:0; padding:0; font-family: ui-sans-serif, system-ui, -apple-system; }
|
|
301
|
+
.json-to-html-title { font-size: 3rem; font-weight: bold; }
|
|
302
|
+
.content { padding: 2rem; }
|
|
303
|
+
.content table { width:100%; table-layout:fixed; border-collapse:collapse; margin-top:10px; font-size:16px; }
|
|
304
|
+
.content table th, .content table td { padding: 8px 12px; text-align:left; border:1px solid #ddd; }
|
|
305
|
+
.content table th { background:#f2f2f2; font-weight:bold; }
|
|
306
|
+
.content table tr:nth-child(odd) { background:#fff; }
|
|
307
|
+
</style></head><body><div class="content">
|
|
308
|
+
<div class="json-to-html-title">Benchmark Report</div>
|
|
309
|
+
${htmlTables}
|
|
310
|
+
</div></body></html>`;
|
|
311
|
+
|
|
312
|
+
if (!isIndividual) {
|
|
313
|
+
replaceMarkdownContent(
|
|
314
|
+
'[//]: # (No deletion!!! Start of Replace Section)',
|
|
315
|
+
'[//]: # (No deletion!!! End of Replace Section)',
|
|
316
|
+
htmlTables
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
fs.writeFileSync(htmlFilePath, html);
|
|
320
|
+
console.log(`Performance ${BOLD}${GREEN}report${END} file generated in file://${BOLD}${GREEN}${htmlFilePath}${END}`);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function replaceMarkdownContent(startMarker: string, endMarker: string, newText: string) {
|
|
324
|
+
const filePath = path.join(parentDirectory, 'README.md');
|
|
325
|
+
fs.readFile(filePath, 'utf8', (err, data) => {
|
|
326
|
+
if (err) {
|
|
327
|
+
console.error(`Unable to read ${filePath}:`, err);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
const startIndex = data.indexOf(startMarker);
|
|
331
|
+
const endIndex = data.indexOf(endMarker);
|
|
332
|
+
if (startIndex === -1 || endIndex === -1 || startIndex >= endIndex) {
|
|
333
|
+
console.warn('Markers not found or invalid range; skip README injection.');
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const updatedMarkdown = data.slice(0, startIndex + startMarker.length) + '\n' + newText + data.slice(endIndex);
|
|
337
|
+
fs.writeFile(filePath, updatedMarkdown, 'utf8', err2 => {
|
|
338
|
+
if (err2) console.error(`Unable to write to ${filePath}:`, err2);
|
|
339
|
+
else console.log(`The content has been successfully replaced in file://${BOLD}${GREEN}${filePath}${END}`);
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function toHtmlTables() {
|
|
345
|
+
let htmlTables = '';
|
|
346
|
+
for (const key of Object.keys(report)) {
|
|
347
|
+
const block = report[key];
|
|
348
|
+
const rows = block.benchmarks as Array<Record<string, any>>;
|
|
349
|
+
if (!rows || !rows.length) continue;
|
|
350
|
+
const headers = Object.keys(rows[0]);
|
|
351
|
+
const table = [
|
|
352
|
+
'<table>',
|
|
353
|
+
'<thead>',
|
|
354
|
+
'<tr>' + headers.map(h => `<th>${h}</th>`).join('') + '</tr>',
|
|
355
|
+
'</thead>',
|
|
356
|
+
'<tbody>',
|
|
357
|
+
...rows.map(r => '<tr>' + headers.map(h => `<td>${r[h]}</td>`).join('') + '</tr>'),
|
|
358
|
+
'</tbody>',
|
|
359
|
+
'</table>'
|
|
360
|
+
].join('');
|
|
361
|
+
htmlTables += `<h2>${key}</h2>` + table;
|
|
362
|
+
}
|
|
363
|
+
return htmlTables;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ---------------- in-process mode (improved hygiene) ----------------
|
|
367
|
+
|
|
368
|
+
async function runInProcess(files: string[]) {
|
|
369
|
+
const durations: number[] = [];
|
|
370
|
+
let index = 0;
|
|
371
|
+
for (const file of files) {
|
|
372
|
+
index++;
|
|
373
|
+
const base = path.basename(file);
|
|
374
|
+
const testName = base.replace(/\.test\.[^.]+$/, '');
|
|
375
|
+
console.log(`${BOLD}${GREEN}[${index}/${plannedCount}]${END} ${coloredLabeled('Run', file)}`);
|
|
376
|
+
|
|
377
|
+
const mod = require(file);
|
|
378
|
+
const suite: Benchmark.Suite | undefined = mod?.suite ?? mod?.default?.suite;
|
|
379
|
+
if (!suite) {
|
|
380
|
+
report[testName] = { benchmarks: [] };
|
|
381
|
+
completedCount++;
|
|
382
|
+
console.log(
|
|
383
|
+
`Progress: ${BOLD}${GREEN}${completedCount}${END}/${BOLD}${GREEN}${plannedCount}${END} Last ${testName}: ${YELLOW}SKIP${END}`
|
|
384
|
+
);
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
suite.forEach((b: any) => {
|
|
390
|
+
if (typeof b.fn === 'function') b.fn.call(b);
|
|
391
|
+
});
|
|
392
|
+
} catch {}
|
|
393
|
+
|
|
394
|
+
if (flags.gc && global.gc) global.gc();
|
|
395
|
+
await new Promise(r => setTimeout(r, flags.cooldownMs));
|
|
396
|
+
|
|
397
|
+
const t0 = Date.now();
|
|
398
|
+
await new Promise<void>(resolve => {
|
|
399
|
+
suite
|
|
400
|
+
.on('complete', function (this: Benchmark.Suite) {
|
|
401
|
+
const rows = this.map((benchmark: any) => ({
|
|
402
|
+
'test name': benchmark.name,
|
|
403
|
+
'time taken (ms)': numberFix(benchmark.times.period * 1000, 2),
|
|
404
|
+
'sample mean (secs)': numberFix(benchmark.stats.mean, 2),
|
|
405
|
+
'sample deviation': numberFix(benchmark.stats.deviation, 2)
|
|
406
|
+
}));
|
|
407
|
+
report[testName] = { benchmarks: rows };
|
|
408
|
+
resolve();
|
|
409
|
+
})
|
|
410
|
+
.run({ async: false });
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
const dt = (Date.now() - t0) / 1000;
|
|
414
|
+
durations.push(dt);
|
|
415
|
+
const avg = durations.reduce((a, b) => a + b, 0) / durations.length;
|
|
416
|
+
const remaining = plannedCount - ++completedCount;
|
|
417
|
+
const etaSec = Math.max(0, Math.round(remaining * avg));
|
|
418
|
+
const mins = Math.floor(etaSec / 60)
|
|
419
|
+
.toString()
|
|
420
|
+
.padStart(2, '0');
|
|
421
|
+
const secs = (etaSec % 60).toString().padStart(2, '0');
|
|
422
|
+
console.log(
|
|
423
|
+
`Progress: ${BOLD}${GREEN}${completedCount}${END}/${BOLD}${GREEN}${plannedCount}${END} Last ${testName}: ${GREEN}OK${END} in ${numberFix(dt, 2)}s | ETA ~ ${mins}:${secs}`
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// ---------------- isolated-per-suite mode
|
|
429
|
+
type ChildMessage = {
|
|
430
|
+
testName: string;
|
|
431
|
+
benchmarks: Array<Record<string, any>>;
|
|
432
|
+
runTime: number;
|
|
433
|
+
file: string;
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
async function runIsolated(files: string[]) {
|
|
437
|
+
const durations: number[] = [];
|
|
438
|
+
let index = 0;
|
|
439
|
+
|
|
440
|
+
for (const file of files) {
|
|
441
|
+
index++;
|
|
442
|
+
const testName = path.basename(file).replace(/\.test\.[^.]+$/, '');
|
|
443
|
+
console.log(`${BOLD}${GREEN}[${index}/${plannedCount}]${END} ${coloredLabeled('Fork', file)}`);
|
|
444
|
+
|
|
445
|
+
await new Promise<void>((resolve, reject) => {
|
|
446
|
+
const childEntry = path.resolve(__dirname, './single-suite-runner.ts'); // ensure .ts
|
|
447
|
+
|
|
448
|
+
// Always run with Node + ts-node/register in child.
|
|
449
|
+
const execArgv = ['-r', 'ts-node/register/transpile-only'];
|
|
450
|
+
|
|
451
|
+
// If user wants GC, put it into NODE_OPTIONS (so Node consumes it, not ts-node).
|
|
452
|
+
const env = {
|
|
453
|
+
...process.env,
|
|
454
|
+
NODE_OPTIONS: ((process.env.NODE_OPTIONS || '') + (flags.gc ? ' --expose-gc' : '')).trim()
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
const t0 = Date.now();
|
|
458
|
+
const cp = fork(childEntry, [file], {
|
|
459
|
+
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
|
460
|
+
execArgv,
|
|
461
|
+
execPath: process.execPath,
|
|
462
|
+
env
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
let got = false;
|
|
466
|
+
cp.on('message', (m: ChildMessage) => {
|
|
467
|
+
got = true;
|
|
468
|
+
report[m.testName] = { benchmarks: m.benchmarks };
|
|
469
|
+
});
|
|
470
|
+
cp.on('exit', code => {
|
|
471
|
+
completedCount++;
|
|
472
|
+
const dt = (Date.now() - t0) / 1000;
|
|
473
|
+
durations.push(dt);
|
|
474
|
+
|
|
475
|
+
const avg = durations.reduce((a, b) => a + b, 0) / durations.length;
|
|
476
|
+
const remaining = files.length - completedCount;
|
|
477
|
+
const etaSec = Math.max(0, Math.round(remaining * avg));
|
|
478
|
+
const mins = Math.floor(etaSec / 60)
|
|
479
|
+
.toString()
|
|
480
|
+
.padStart(2, '0');
|
|
481
|
+
const secs = (etaSec % 60).toString().padStart(2, '0');
|
|
482
|
+
const label =
|
|
483
|
+
code === 0 && got ? `${GREEN}OK${END}` : code === 0 && !got ? `${YELLOW}SKIP${END}` : `${YELLOW}ERR${END}`;
|
|
484
|
+
|
|
485
|
+
console.log(
|
|
486
|
+
`Progress: ${BOLD}${GREEN}${completedCount}${END}/${BOLD}${GREEN}${plannedCount}${END}`,
|
|
487
|
+
`Last ${testName}: ${label} in ${numberFix(dt, 2)}s | ETA ~ ${mins}:${secs}`
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
if (code !== 0) reject(new Error(`Child failed: ${code}`));
|
|
491
|
+
else resolve();
|
|
492
|
+
});
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
await new Promise(r => setTimeout(r, flags.cooldownMs));
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// ---------------- entry ----------------
|
|
500
|
+
(async function main() {
|
|
501
|
+
if (!testFiles.length) {
|
|
502
|
+
console.log(`${YELLOW}No test files matched.${END}`);
|
|
503
|
+
process.exit(0);
|
|
504
|
+
}
|
|
505
|
+
console.log(
|
|
506
|
+
`${BOLD}${GREEN}Running ${plannedCount} planned suite(s)${END} out of ${discoveredTotal} discovered ${flags.isolate ? '(isolated)' : '(in-process)'} ${flags.shuffle ? '[shuffled]' : ''} ${flags.label ? '[' + flags.label + ']' : ''}`
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
if (flags.isolate) await runIsolated(testFiles);
|
|
510
|
+
else await runInProcess(testFiles);
|
|
511
|
+
|
|
512
|
+
// Render report (same as original)
|
|
513
|
+
const htmlTables = toHtmlTables();
|
|
514
|
+
writeReportHTMLAndJSON(htmlTables);
|
|
515
|
+
|
|
516
|
+
// Summary
|
|
517
|
+
const counts = { ok: 0, skip: 0, err: 0 };
|
|
518
|
+
for (const key of Object.keys(report)) {
|
|
519
|
+
const arr = report[key]?.benchmarks || [];
|
|
520
|
+
if (!Array.isArray(arr)) continue;
|
|
521
|
+
if (arr.length === 0) counts.skip++;
|
|
522
|
+
else counts.ok++;
|
|
523
|
+
}
|
|
524
|
+
// We don't track 'err' explicitly here because child errors stop the run; when needed, you can extend message payload.
|
|
525
|
+
console.log(
|
|
526
|
+
`${BOLD}${GREEN}Summary:${END} planned=${plannedCount}, discovered=${discoveredTotal}, ok=${counts.ok}, skipped=${counts.skip}`
|
|
527
|
+
);
|
|
528
|
+
})();
|