modestbench 0.0.1
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 +45 -0
- package/LICENSE.md +55 -0
- package/README.md +699 -0
- package/dist/bootstrap.cjs +37 -0
- package/dist/bootstrap.cjs.map +1 -0
- package/dist/bootstrap.d.cts +17 -0
- package/dist/bootstrap.d.cts.map +1 -0
- package/dist/bootstrap.d.ts +17 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +33 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/cli/commands/history.cjs +459 -0
- package/dist/cli/commands/history.cjs.map +1 -0
- package/dist/cli/commands/history.d.cts +34 -0
- package/dist/cli/commands/history.d.cts.map +1 -0
- package/dist/cli/commands/history.d.ts +34 -0
- package/dist/cli/commands/history.d.ts.map +1 -0
- package/dist/cli/commands/history.js +422 -0
- package/dist/cli/commands/history.js.map +1 -0
- package/dist/cli/commands/init.cjs +566 -0
- package/dist/cli/commands/init.cjs.map +1 -0
- package/dist/cli/commands/init.d.cts +26 -0
- package/dist/cli/commands/init.d.cts.map +1 -0
- package/dist/cli/commands/init.d.ts +26 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +562 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/run.cjs +285 -0
- package/dist/cli/commands/run.cjs.map +1 -0
- package/dist/cli/commands/run.d.cts +37 -0
- package/dist/cli/commands/run.d.cts.map +1 -0
- package/dist/cli/commands/run.d.ts +37 -0
- package/dist/cli/commands/run.d.ts.map +1 -0
- package/dist/cli/commands/run.js +248 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/index.cjs +523 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.cts +58 -0
- package/dist/cli/index.d.cts.map +1 -0
- package/dist/cli/index.d.ts +58 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +515 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/manager.cjs +370 -0
- package/dist/config/manager.cjs.map +1 -0
- package/dist/config/manager.d.cts +46 -0
- package/dist/config/manager.d.cts.map +1 -0
- package/dist/config/manager.d.ts +46 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +333 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/schema.cjs +182 -0
- package/dist/config/schema.cjs.map +1 -0
- package/dist/config/schema.d.cts +51 -0
- package/dist/config/schema.d.cts.map +1 -0
- package/dist/config/schema.d.ts +51 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +145 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/constants.cjs +22 -0
- package/dist/constants.cjs.map +1 -0
- package/dist/constants.d.cts +10 -0
- package/dist/constants.d.cts.map +1 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +19 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/benchmark-schema.cjs +135 -0
- package/dist/core/benchmark-schema.cjs.map +1 -0
- package/dist/core/benchmark-schema.d.cts +139 -0
- package/dist/core/benchmark-schema.d.cts.map +1 -0
- package/dist/core/benchmark-schema.d.ts +139 -0
- package/dist/core/benchmark-schema.d.ts.map +1 -0
- package/dist/core/benchmark-schema.js +132 -0
- package/dist/core/benchmark-schema.js.map +1 -0
- package/dist/core/engine.cjs +669 -0
- package/dist/core/engine.cjs.map +1 -0
- package/dist/core/engine.d.cts +128 -0
- package/dist/core/engine.d.cts.map +1 -0
- package/dist/core/engine.d.ts +128 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +632 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/engines/accurate-engine.cjs +292 -0
- package/dist/core/engines/accurate-engine.cjs.map +1 -0
- package/dist/core/engines/accurate-engine.d.cts +63 -0
- package/dist/core/engines/accurate-engine.d.cts.map +1 -0
- package/dist/core/engines/accurate-engine.d.ts +63 -0
- package/dist/core/engines/accurate-engine.d.ts.map +1 -0
- package/dist/core/engines/accurate-engine.js +288 -0
- package/dist/core/engines/accurate-engine.js.map +1 -0
- package/dist/core/engines/index.cjs +21 -0
- package/dist/core/engines/index.cjs.map +1 -0
- package/dist/core/engines/index.d.cts +16 -0
- package/dist/core/engines/index.d.cts.map +1 -0
- package/dist/core/engines/index.d.ts +16 -0
- package/dist/core/engines/index.d.ts.map +1 -0
- package/dist/core/engines/index.js +16 -0
- package/dist/core/engines/index.js.map +1 -0
- package/dist/core/engines/tinybench-engine.cjs +286 -0
- package/dist/core/engines/tinybench-engine.cjs.map +1 -0
- package/dist/core/engines/tinybench-engine.d.cts +18 -0
- package/dist/core/engines/tinybench-engine.d.cts.map +1 -0
- package/dist/core/engines/tinybench-engine.d.ts +18 -0
- package/dist/core/engines/tinybench-engine.d.ts.map +1 -0
- package/dist/core/engines/tinybench-engine.js +282 -0
- package/dist/core/engines/tinybench-engine.js.map +1 -0
- package/dist/core/error-manager.cjs +303 -0
- package/dist/core/error-manager.cjs.map +1 -0
- package/dist/core/error-manager.d.cts +77 -0
- package/dist/core/error-manager.d.cts.map +1 -0
- package/dist/core/error-manager.d.ts +77 -0
- package/dist/core/error-manager.d.ts.map +1 -0
- package/dist/core/error-manager.js +299 -0
- package/dist/core/error-manager.js.map +1 -0
- package/dist/core/loader.cjs +287 -0
- package/dist/core/loader.cjs.map +1 -0
- package/dist/core/loader.d.cts +55 -0
- package/dist/core/loader.d.cts.map +1 -0
- package/dist/core/loader.d.ts +55 -0
- package/dist/core/loader.d.ts.map +1 -0
- package/dist/core/loader.js +250 -0
- package/dist/core/loader.js.map +1 -0
- package/dist/core/stats-utils.cjs +99 -0
- package/dist/core/stats-utils.cjs.map +1 -0
- package/dist/core/stats-utils.d.cts +50 -0
- package/dist/core/stats-utils.d.cts.map +1 -0
- package/dist/core/stats-utils.d.ts +50 -0
- package/dist/core/stats-utils.d.ts.map +1 -0
- package/dist/core/stats-utils.js +94 -0
- package/dist/core/stats-utils.js.map +1 -0
- package/dist/index.cjs +64 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +22 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/progress/manager.cjs +325 -0
- package/dist/progress/manager.cjs.map +1 -0
- package/dist/progress/manager.d.cts +125 -0
- package/dist/progress/manager.d.cts.map +1 -0
- package/dist/progress/manager.d.ts +125 -0
- package/dist/progress/manager.d.ts.map +1 -0
- package/dist/progress/manager.js +321 -0
- package/dist/progress/manager.js.map +1 -0
- package/dist/reporters/csv.cjs +250 -0
- package/dist/reporters/csv.cjs.map +1 -0
- package/dist/reporters/csv.d.cts +92 -0
- package/dist/reporters/csv.d.cts.map +1 -0
- package/dist/reporters/csv.d.ts +92 -0
- package/dist/reporters/csv.d.ts.map +1 -0
- package/dist/reporters/csv.js +246 -0
- package/dist/reporters/csv.js.map +1 -0
- package/dist/reporters/human.cjs +516 -0
- package/dist/reporters/human.cjs.map +1 -0
- package/dist/reporters/human.d.cts +86 -0
- package/dist/reporters/human.d.cts.map +1 -0
- package/dist/reporters/human.d.ts +86 -0
- package/dist/reporters/human.d.ts.map +1 -0
- package/dist/reporters/human.js +509 -0
- package/dist/reporters/human.js.map +1 -0
- package/dist/reporters/index.cjs +17 -0
- package/dist/reporters/index.cjs.map +1 -0
- package/dist/reporters/index.d.cts +10 -0
- package/dist/reporters/index.d.cts.map +1 -0
- package/dist/reporters/index.d.ts +10 -0
- package/dist/reporters/index.d.ts.map +1 -0
- package/dist/reporters/index.js +10 -0
- package/dist/reporters/index.js.map +1 -0
- package/dist/reporters/json.cjs +215 -0
- package/dist/reporters/json.cjs.map +1 -0
- package/dist/reporters/json.d.cts +79 -0
- package/dist/reporters/json.d.cts.map +1 -0
- package/dist/reporters/json.d.ts +79 -0
- package/dist/reporters/json.d.ts.map +1 -0
- package/dist/reporters/json.js +211 -0
- package/dist/reporters/json.js.map +1 -0
- package/dist/reporters/registry.cjs +255 -0
- package/dist/reporters/registry.cjs.map +1 -0
- package/dist/reporters/registry.d.cts +155 -0
- package/dist/reporters/registry.d.cts.map +1 -0
- package/dist/reporters/registry.d.ts +155 -0
- package/dist/reporters/registry.d.ts.map +1 -0
- package/dist/reporters/registry.js +249 -0
- package/dist/reporters/registry.js.map +1 -0
- package/dist/reporters/simple.cjs +328 -0
- package/dist/reporters/simple.cjs.map +1 -0
- package/dist/reporters/simple.d.cts +51 -0
- package/dist/reporters/simple.d.cts.map +1 -0
- package/dist/reporters/simple.d.ts +51 -0
- package/dist/reporters/simple.d.ts.map +1 -0
- package/dist/reporters/simple.js +321 -0
- package/dist/reporters/simple.js.map +1 -0
- package/dist/schema/modestbench-config.schema.json +162 -0
- package/dist/storage/history.cjs +456 -0
- package/dist/storage/history.cjs.map +1 -0
- package/dist/storage/history.d.cts +99 -0
- package/dist/storage/history.d.cts.map +1 -0
- package/dist/storage/history.d.ts +99 -0
- package/dist/storage/history.d.ts.map +1 -0
- package/dist/storage/history.js +452 -0
- package/dist/storage/history.js.map +1 -0
- package/dist/types/cli.cjs +21 -0
- package/dist/types/cli.cjs.map +1 -0
- package/dist/types/cli.d.cts +296 -0
- package/dist/types/cli.d.cts.map +1 -0
- package/dist/types/cli.d.ts +296 -0
- package/dist/types/cli.d.ts.map +1 -0
- package/dist/types/cli.js +18 -0
- package/dist/types/cli.js.map +1 -0
- package/dist/types/core.cjs +14 -0
- package/dist/types/core.cjs.map +1 -0
- package/dist/types/core.d.cts +380 -0
- package/dist/types/core.d.cts.map +1 -0
- package/dist/types/core.d.ts +380 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/types/core.js +13 -0
- package/dist/types/core.js.map +1 -0
- package/dist/types/index.cjs +27 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +11 -0
- package/dist/types/index.d.cts.map +1 -0
- package/dist/types/index.d.ts +11 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/interfaces.cjs +10 -0
- package/dist/types/interfaces.cjs.map +1 -0
- package/dist/types/interfaces.d.cts +381 -0
- package/dist/types/interfaces.d.cts.map +1 -0
- package/dist/types/interfaces.d.ts +381 -0
- package/dist/types/interfaces.d.ts.map +1 -0
- package/dist/types/interfaces.js +9 -0
- package/dist/types/interfaces.js.map +1 -0
- package/dist/types/utility.cjs +92 -0
- package/dist/types/utility.cjs.map +1 -0
- package/dist/types/utility.d.cts +330 -0
- package/dist/types/utility.d.cts.map +1 -0
- package/dist/types/utility.d.ts +330 -0
- package/dist/types/utility.d.ts.map +1 -0
- package/dist/types/utility.js +78 -0
- package/dist/types/utility.js.map +1 -0
- package/package.json +211 -0
- package/src/bootstrap.ts +35 -0
- package/src/cli/commands/history.ts +569 -0
- package/src/cli/commands/init.ts +658 -0
- package/src/cli/commands/run.ts +346 -0
- package/src/cli/index.ts +642 -0
- package/src/config/manager.ts +387 -0
- package/src/config/schema.ts +188 -0
- package/src/constants.ts +21 -0
- package/src/core/benchmark-schema.ts +185 -0
- package/src/core/engine.ts +888 -0
- package/src/core/engines/accurate-engine.ts +408 -0
- package/src/core/engines/index.ts +16 -0
- package/src/core/engines/tinybench-engine.ts +335 -0
- package/src/core/error-manager.ts +372 -0
- package/src/core/loader.ts +324 -0
- package/src/core/stats-utils.ts +135 -0
- package/src/index.ts +46 -0
- package/src/progress/manager.ts +415 -0
- package/src/reporters/csv.ts +368 -0
- package/src/reporters/human.ts +707 -0
- package/src/reporters/index.ts +10 -0
- package/src/reporters/json.ts +302 -0
- package/src/reporters/registry.ts +349 -0
- package/src/reporters/simple.ts +459 -0
- package/src/storage/history.ts +600 -0
- package/src/types/cli.ts +312 -0
- package/src/types/core.ts +414 -0
- package/src/types/index.ts +18 -0
- package/src/types/interfaces.ts +451 -0
- package/src/types/utility.ts +446 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ModestBench Progress Manager
|
|
3
|
+
*
|
|
4
|
+
* Tracks execution progress, estimates completion times, and manages progress
|
|
5
|
+
* callbacks for real-time updates during benchmark runs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
BenchmarkRun,
|
|
10
|
+
ProgressManager,
|
|
11
|
+
ProgressState,
|
|
12
|
+
} from '../types/index.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Progress callback function type
|
|
16
|
+
*/
|
|
17
|
+
type ProgressCallback = (state: ProgressState) => void;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Progress calculation utilities
|
|
21
|
+
*/
|
|
22
|
+
interface ProgressMetrics {
|
|
23
|
+
readonly currentThroughput: number;
|
|
24
|
+
readonly estimatedTotal: number;
|
|
25
|
+
readonly recentTimings: number[];
|
|
26
|
+
readonly startTime: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Default progress manager implementation
|
|
31
|
+
*/
|
|
32
|
+
export class ModestBenchProgressManager implements ProgressManager {
|
|
33
|
+
private callbacks: ProgressCallback[] = [];
|
|
34
|
+
|
|
35
|
+
private lastUpdate = 0;
|
|
36
|
+
|
|
37
|
+
private readonly maxRecentTimings = 10;
|
|
38
|
+
|
|
39
|
+
private metrics: null | ProgressMetrics = null;
|
|
40
|
+
|
|
41
|
+
private state: ProgressState;
|
|
42
|
+
|
|
43
|
+
private readonly updateThrottleMs = 100; // Limit updates to avoid spam
|
|
44
|
+
|
|
45
|
+
constructor() {
|
|
46
|
+
this.state = this.createInitialState();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Clean up progress tracking resources
|
|
51
|
+
*/
|
|
52
|
+
cleanup(): void {
|
|
53
|
+
this.callbacks = [];
|
|
54
|
+
this.metrics = null;
|
|
55
|
+
this.state = this.createInitialState();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Estimate completion time
|
|
60
|
+
*/
|
|
61
|
+
estimateCompletion(): Date | null {
|
|
62
|
+
if (
|
|
63
|
+
!this.metrics ||
|
|
64
|
+
this.state.totalTasks === 0 ||
|
|
65
|
+
this.state.tasksCompleted === 0
|
|
66
|
+
) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const remainingTasks = this.state.totalTasks - this.state.tasksCompleted;
|
|
71
|
+
|
|
72
|
+
if (remainingTasks <= 0) {
|
|
73
|
+
return new Date(); // Already complete
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Calculate average throughput from recent timings
|
|
77
|
+
const throughput = this.calculateThroughput();
|
|
78
|
+
|
|
79
|
+
if (throughput <= 0) {
|
|
80
|
+
return null; // Can't estimate with no throughput data
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const estimatedRemainingMs = (remainingTasks / throughput) * 1000;
|
|
84
|
+
return new Date(Date.now() + estimatedRemainingMs);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Force an immediate progress update (bypassing throttling)
|
|
89
|
+
*/
|
|
90
|
+
forceUpdate(): void {
|
|
91
|
+
const oldThrottle = this.lastUpdate;
|
|
92
|
+
this.lastUpdate = 0; // Reset throttle
|
|
93
|
+
this.update({}); // Trigger update with no changes
|
|
94
|
+
this.lastUpdate = oldThrottle; // Restore throttle timing
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Format elapsed time as human-readable string
|
|
99
|
+
*/
|
|
100
|
+
getFormattedElapsed(): string {
|
|
101
|
+
const seconds = Math.floor(this.state.elapsed / 1000);
|
|
102
|
+
const minutes = Math.floor(seconds / 60);
|
|
103
|
+
const hours = Math.floor(minutes / 60);
|
|
104
|
+
|
|
105
|
+
if (hours > 0) {
|
|
106
|
+
return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
107
|
+
} else if (minutes > 0) {
|
|
108
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
109
|
+
} else {
|
|
110
|
+
return `${seconds}s`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Format estimated remaining time as human-readable string
|
|
116
|
+
*/
|
|
117
|
+
getFormattedEstimate(): null | string {
|
|
118
|
+
const completion = this.estimateCompletion();
|
|
119
|
+
if (!completion) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const remaining = completion.getTime() - Date.now();
|
|
124
|
+
if (remaining <= 0) {
|
|
125
|
+
return 'Complete';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const seconds = Math.floor(remaining / 1000);
|
|
129
|
+
const minutes = Math.floor(seconds / 60);
|
|
130
|
+
const hours = Math.floor(minutes / 60);
|
|
131
|
+
|
|
132
|
+
if (hours > 0) {
|
|
133
|
+
return `~${hours}h ${minutes % 60}m remaining`;
|
|
134
|
+
} else if (minutes > 0) {
|
|
135
|
+
return `~${minutes}m ${seconds % 60}s remaining`;
|
|
136
|
+
} else {
|
|
137
|
+
return `~${seconds}s remaining`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get detailed progress metrics
|
|
143
|
+
*/
|
|
144
|
+
getMetrics(): null | {
|
|
145
|
+
elapsedMs: number;
|
|
146
|
+
estimatedCompletion: Date | null;
|
|
147
|
+
remainingTasks: number;
|
|
148
|
+
throughput: number;
|
|
149
|
+
} {
|
|
150
|
+
if (!this.metrics) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const throughput = this.calculateThroughput();
|
|
155
|
+
const estimatedCompletion = this.estimateCompletion();
|
|
156
|
+
const elapsedMs = Date.now() - this.metrics.startTime;
|
|
157
|
+
const remainingTasks = Math.max(
|
|
158
|
+
0,
|
|
159
|
+
this.state.totalTasks - this.state.tasksCompleted,
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
elapsedMs,
|
|
164
|
+
estimatedCompletion,
|
|
165
|
+
remainingTasks,
|
|
166
|
+
throughput,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get progress as a fraction (0.0 to 1.0)
|
|
172
|
+
*/
|
|
173
|
+
getProgressFraction(): number {
|
|
174
|
+
return this.state.percentage / 100;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Get current progress state
|
|
179
|
+
*/
|
|
180
|
+
getState(): ProgressState {
|
|
181
|
+
return { ...this.state };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Increment the completed files counter
|
|
186
|
+
*/
|
|
187
|
+
incrementFiles(): void {
|
|
188
|
+
this.update({ filesCompleted: this.state.filesCompleted + 1 });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Increment the completed suites counter
|
|
193
|
+
*/
|
|
194
|
+
incrementSuites(): void {
|
|
195
|
+
this.update({ suitesCompleted: this.state.suitesCompleted + 1 });
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Increment the completed tasks counter
|
|
200
|
+
*/
|
|
201
|
+
incrementTasks(): void {
|
|
202
|
+
this.update({ tasksCompleted: this.state.tasksCompleted + 1 });
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Initialize progress tracking for a benchmark run
|
|
207
|
+
*/
|
|
208
|
+
initialize(run: BenchmarkRun): void {
|
|
209
|
+
// Use summary totals if available (from pre-calculation), otherwise calculate from files
|
|
210
|
+
const totalFiles = run.summary?.totalFiles ?? run.files.length;
|
|
211
|
+
let totalSuites = run.summary?.totalSuites ?? 0;
|
|
212
|
+
let totalTasks = run.summary?.totalTasks ?? 0;
|
|
213
|
+
|
|
214
|
+
// If we don't have summary data and have detailed run information, calculate actual totals
|
|
215
|
+
if (!run.summary?.totalTasks && run.files.length > 0) {
|
|
216
|
+
for (const file of run.files) {
|
|
217
|
+
for (const suite of file.suites) {
|
|
218
|
+
totalSuites++;
|
|
219
|
+
totalTasks += suite.tasks.length;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
this.state = {
|
|
225
|
+
elapsed: 0,
|
|
226
|
+
filesCompleted: 0,
|
|
227
|
+
percentage: 0,
|
|
228
|
+
suitesCompleted: 0,
|
|
229
|
+
tasksCompleted: 0,
|
|
230
|
+
totalFiles,
|
|
231
|
+
totalSuites,
|
|
232
|
+
totalTasks,
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
this.metrics = {
|
|
236
|
+
currentThroughput: 0,
|
|
237
|
+
estimatedTotal: totalTasks, // Use tasks as the primary progress unit
|
|
238
|
+
recentTimings: [],
|
|
239
|
+
startTime: Date.now(),
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
this.lastUpdate = Date.now();
|
|
243
|
+
this.notifyCallbacks();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Check if the run is complete
|
|
248
|
+
*/
|
|
249
|
+
isComplete(): boolean {
|
|
250
|
+
return (
|
|
251
|
+
this.state.tasksCompleted >= this.state.totalTasks &&
|
|
252
|
+
this.state.totalTasks > 0
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Register a callback for progress updates
|
|
258
|
+
*/
|
|
259
|
+
onProgress(callback: ProgressCallback): void {
|
|
260
|
+
this.callbacks.push(callback);
|
|
261
|
+
} /**
|
|
262
|
+
* Remove a progress callback
|
|
263
|
+
*/
|
|
264
|
+
|
|
265
|
+
removeCallback(callback: ProgressCallback): boolean {
|
|
266
|
+
const index = this.callbacks.indexOf(callback);
|
|
267
|
+
if (index >= 0) {
|
|
268
|
+
this.callbacks.splice(index, 1);
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Set the current file being processed
|
|
276
|
+
*/
|
|
277
|
+
setCurrentFile(file: string): void {
|
|
278
|
+
this.update({ currentFile: file });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Set the current suite being processed
|
|
283
|
+
*/
|
|
284
|
+
setCurrentSuite(suite: string): void {
|
|
285
|
+
this.update({ currentSuite: suite });
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Set the current task being processed
|
|
290
|
+
*/
|
|
291
|
+
setCurrentTask(task: string): void {
|
|
292
|
+
this.update({ currentTask: task });
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Update progress state
|
|
297
|
+
*/
|
|
298
|
+
update(updates: Partial<ProgressState>): void {
|
|
299
|
+
const now = Date.now();
|
|
300
|
+
|
|
301
|
+
// Throttle updates to avoid excessive callbacks
|
|
302
|
+
if (now - this.lastUpdate < this.updateThrottleMs) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Calculate elapsed time
|
|
307
|
+
const elapsed = this.metrics ? now - this.metrics.startTime : 0;
|
|
308
|
+
|
|
309
|
+
// Apply updates
|
|
310
|
+
this.state = {
|
|
311
|
+
...this.state,
|
|
312
|
+
...updates,
|
|
313
|
+
elapsed,
|
|
314
|
+
percentage: this.calculatePercentage(updates),
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// Update metrics for completion estimation
|
|
318
|
+
this.updateMetrics(now);
|
|
319
|
+
|
|
320
|
+
this.lastUpdate = now;
|
|
321
|
+
this.notifyCallbacks();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Calculate progress percentage from current state
|
|
326
|
+
*/
|
|
327
|
+
private calculatePercentage(updates: Partial<ProgressState>): number {
|
|
328
|
+
const currentState = { ...this.state, ...updates };
|
|
329
|
+
|
|
330
|
+
if (currentState.totalTasks === 0) {
|
|
331
|
+
return 0;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return Math.min(
|
|
335
|
+
100,
|
|
336
|
+
Math.max(
|
|
337
|
+
0,
|
|
338
|
+
(currentState.tasksCompleted / currentState.totalTasks) * 100,
|
|
339
|
+
),
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Calculate average throughput from recent measurements
|
|
345
|
+
*/
|
|
346
|
+
private calculateThroughput(): number {
|
|
347
|
+
if (!this.metrics || this.metrics.recentTimings.length === 0) {
|
|
348
|
+
return 0;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Use moving average of recent throughput measurements
|
|
352
|
+
const sum = this.metrics.recentTimings.reduce(
|
|
353
|
+
(acc, timing) => acc + timing,
|
|
354
|
+
0,
|
|
355
|
+
);
|
|
356
|
+
return sum / this.metrics.recentTimings.length;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Create initial progress state
|
|
361
|
+
*/
|
|
362
|
+
private createInitialState(): ProgressState {
|
|
363
|
+
return {
|
|
364
|
+
elapsed: 0,
|
|
365
|
+
filesCompleted: 0,
|
|
366
|
+
percentage: 0,
|
|
367
|
+
suitesCompleted: 0,
|
|
368
|
+
tasksCompleted: 0,
|
|
369
|
+
totalFiles: 0,
|
|
370
|
+
totalSuites: 0,
|
|
371
|
+
totalTasks: 0,
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Notify all registered callbacks of state changes
|
|
377
|
+
*/
|
|
378
|
+
private notifyCallbacks(): void {
|
|
379
|
+
for (const callback of this.callbacks) {
|
|
380
|
+
try {
|
|
381
|
+
callback(this.state);
|
|
382
|
+
} catch (error) {
|
|
383
|
+
console.error('Error in progress callback:', error);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Update throughput metrics
|
|
390
|
+
*/
|
|
391
|
+
private updateMetrics(now: number): void {
|
|
392
|
+
if (!this.metrics) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Track timing for throughput calculation
|
|
397
|
+
const elapsed = now - this.metrics.startTime;
|
|
398
|
+
if (elapsed > 0 && this.state.tasksCompleted > 0) {
|
|
399
|
+
const currentThroughput = this.state.tasksCompleted / (elapsed / 1000); // tasks per second
|
|
400
|
+
|
|
401
|
+
// Add to recent timings for moving average
|
|
402
|
+
this.metrics.recentTimings.push(currentThroughput);
|
|
403
|
+
|
|
404
|
+
// Keep only the most recent timings
|
|
405
|
+
if (this.metrics.recentTimings.length > this.maxRecentTimings) {
|
|
406
|
+
this.metrics.recentTimings.shift();
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
this.metrics = {
|
|
410
|
+
...this.metrics,
|
|
411
|
+
currentThroughput,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|