overtake 0.0.1-rc1 → 0.0.1-rc2
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/README.md +3 -1
- package/cli.js +13 -22
- package/index.js +240 -134
- package/package.json +4 -7
- package/reporter.js +90 -0
- package/index.d.ts +0 -15
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Overtake
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Performance benchmark for NodeJS
|
|
4
4
|
|
|
5
5
|
[![Build Status][github-image]][github-url]
|
|
6
6
|
[![NPM version][npm-image]][npm-url]
|
|
@@ -81,6 +81,8 @@ benchmark('mongodb vs postgres', () => {
|
|
|
81
81
|
});
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
Make sure you have installed used modules and run
|
|
85
|
+
|
|
84
86
|
```bash
|
|
85
87
|
yarn overtake
|
|
86
88
|
```
|
package/cli.js
CHANGED
|
@@ -1,28 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import { promisify } from 'util';
|
|
3
4
|
import glob from 'glob';
|
|
4
|
-
import {
|
|
5
|
+
import { Overtake } from './index.js';
|
|
6
|
+
import { defaultReporter } from './reporter.js';
|
|
5
7
|
|
|
8
|
+
const globAsync = promisify(glob);
|
|
6
9
|
const pattern = process.argv[2] || '**/__benchmarks__/**/*.js';
|
|
10
|
+
const overtake = new Overtake({});
|
|
7
11
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
perform,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
glob(pattern, async (err, files) => {
|
|
17
|
-
try {
|
|
18
|
-
for (const file of files) {
|
|
19
|
-
const filepath = Path.resolve(file);
|
|
20
|
-
await import(filepath);
|
|
21
|
-
}
|
|
22
|
-
} catch (e) {
|
|
23
|
-
console.error(e.stack);
|
|
24
|
-
}
|
|
25
|
-
for (const suite of suites) {
|
|
26
|
-
await runner(suite);
|
|
12
|
+
(async () => {
|
|
13
|
+
const files = await globAsync(pattern);
|
|
14
|
+
await overtake.load(files);
|
|
15
|
+
if (overtake.reporters.length === 0) {
|
|
16
|
+
reporter(defaultReporter);
|
|
27
17
|
}
|
|
28
|
-
|
|
18
|
+
await overtake.run();
|
|
19
|
+
})().catch((e) => console.error(e));
|
package/index.js
CHANGED
|
@@ -1,67 +1,185 @@
|
|
|
1
|
+
import Path from 'path';
|
|
1
2
|
import WorkerThreads from 'worker_threads';
|
|
3
|
+
import { Event } from 'evnty';
|
|
2
4
|
|
|
3
|
-
export const
|
|
5
|
+
export const NOOP = () => {};
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
export class Perform {
|
|
8
|
+
title = '';
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
const SPINNER_INTERVAL = 80;
|
|
10
|
+
count = 0;
|
|
9
11
|
|
|
10
|
-
|
|
12
|
+
args;
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function benchmark(title, init) {
|
|
19
|
-
const suite = {
|
|
20
|
-
title,
|
|
21
|
-
current: true,
|
|
22
|
-
measures: [],
|
|
23
|
-
performs: [],
|
|
24
|
-
setup: NOOP,
|
|
25
|
-
teardown: NOOP,
|
|
26
|
-
};
|
|
27
|
-
suites.push(suite);
|
|
28
|
-
init();
|
|
29
|
-
suite.current = false;
|
|
14
|
+
constructor(overtake, title, count, args) {
|
|
15
|
+
this.title = title;
|
|
16
|
+
this.count = count;
|
|
17
|
+
this.args = args;
|
|
18
|
+
}
|
|
30
19
|
}
|
|
31
20
|
|
|
32
|
-
export
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
21
|
+
export class Measure {
|
|
22
|
+
title = '';
|
|
23
|
+
|
|
24
|
+
init = NOOP;
|
|
25
|
+
|
|
26
|
+
constructor(overtake, title, init = NOOP) {
|
|
27
|
+
this.title = title;
|
|
28
|
+
this.init = init;
|
|
36
29
|
}
|
|
37
|
-
return suite;
|
|
38
30
|
}
|
|
39
31
|
|
|
40
|
-
export
|
|
41
|
-
|
|
42
|
-
suite.setup = init;
|
|
43
|
-
}
|
|
32
|
+
export class Suite {
|
|
33
|
+
title = '';
|
|
44
34
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
35
|
+
setup = NOOP;
|
|
36
|
+
|
|
37
|
+
measures = [];
|
|
38
|
+
|
|
39
|
+
performs = [];
|
|
40
|
+
|
|
41
|
+
teardown = NOOP;
|
|
49
42
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
#title = '';
|
|
44
|
+
|
|
45
|
+
#init = NOOP;
|
|
46
|
+
|
|
47
|
+
#overtake = null;
|
|
48
|
+
|
|
49
|
+
constructor(overtake, title, init = NOOP) {
|
|
50
|
+
this.#overtake = overtake;
|
|
51
|
+
this.title = title;
|
|
52
|
+
this.#init = init;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async init() {
|
|
56
|
+
const unsubscribes = [
|
|
57
|
+
this.#overtake.onSetupRegister.on((setup) => (this.setup = setup)),
|
|
58
|
+
this.#overtake.onMeasureRegister.on((measure) => this.measures.push(measure)),
|
|
59
|
+
this.#overtake.onPerformRegister.on((perform) => this.performs.push(perform)),
|
|
60
|
+
this.#overtake.onTeardownRegister.on((teardown) => (this.teardown = teardown)),
|
|
61
|
+
];
|
|
62
|
+
await this.#init();
|
|
63
|
+
unsubscribes.forEach((unsubscribe) => unsubscribe());
|
|
64
|
+
}
|
|
53
65
|
}
|
|
54
66
|
|
|
55
|
-
export
|
|
56
|
-
|
|
57
|
-
|
|
67
|
+
export class Script {
|
|
68
|
+
onLoad = new Event();
|
|
69
|
+
|
|
70
|
+
filename = '';
|
|
71
|
+
|
|
72
|
+
suites = [];
|
|
73
|
+
|
|
74
|
+
constructor(filename) {
|
|
75
|
+
this.filename = filename;
|
|
76
|
+
}
|
|
58
77
|
}
|
|
59
78
|
|
|
60
|
-
export
|
|
61
|
-
|
|
79
|
+
export class Overtake {
|
|
80
|
+
onLoad = new Event();
|
|
81
|
+
|
|
82
|
+
onRun = new Event();
|
|
83
|
+
|
|
84
|
+
onComplete = new Event();
|
|
85
|
+
|
|
86
|
+
onScriptRegister = new Event();
|
|
87
|
+
|
|
88
|
+
onScriptStart = new Event();
|
|
89
|
+
|
|
90
|
+
onScriptComplete = new Event();
|
|
91
|
+
|
|
92
|
+
onSuiteRegister = new Event();
|
|
93
|
+
|
|
94
|
+
onSuiteStart = new Event();
|
|
95
|
+
|
|
96
|
+
onSuiteComplete = new Event();
|
|
97
|
+
|
|
98
|
+
onSetupRegister = new Event();
|
|
99
|
+
|
|
100
|
+
onTeardownRegister = new Event();
|
|
101
|
+
|
|
102
|
+
onMeasureRegister = new Event();
|
|
103
|
+
|
|
104
|
+
onMeasureStart = new Event();
|
|
105
|
+
|
|
106
|
+
onMeasureComplete = new Event();
|
|
107
|
+
|
|
108
|
+
onPerformRegister = new Event();
|
|
109
|
+
|
|
110
|
+
onPerformStart = new Event();
|
|
111
|
+
|
|
112
|
+
onPerformProgress = new Event();
|
|
113
|
+
|
|
114
|
+
onPerformComplete = new Event();
|
|
115
|
+
|
|
116
|
+
onReport = new Event();
|
|
117
|
+
|
|
118
|
+
scripts = [];
|
|
119
|
+
|
|
120
|
+
reporters = [];
|
|
121
|
+
|
|
122
|
+
constructor(options = {}) {
|
|
123
|
+
Object.assign(globalThis, {
|
|
124
|
+
benchmark: (title, init) => this.onSuiteRegister(new Suite(this, title, init)),
|
|
125
|
+
setup: (init) => this.onSetupRegister(init),
|
|
126
|
+
teardown: (init) => this.onTeardownRegister(init),
|
|
127
|
+
measure: (title, init) => this.onMeasureRegister(new Measure(this, title, init)),
|
|
128
|
+
perform: (title, count, args) => this.onPerformRegister(new Perform(this, title, count, args)),
|
|
129
|
+
reporter: (reporter) => this.reporters.push(reporter(this)),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async load(files) {
|
|
134
|
+
this.onLoad(this);
|
|
135
|
+
for (const file of files) {
|
|
136
|
+
const filename = Path.resolve(file);
|
|
137
|
+
const script = new Script(filename);
|
|
138
|
+
const unsubscribe = this.onSuiteRegister.on((suite) => {
|
|
139
|
+
script.suites.push(suite);
|
|
140
|
+
});
|
|
141
|
+
await import(filename);
|
|
142
|
+
unsubscribe();
|
|
143
|
+
this.scripts.push(script);
|
|
144
|
+
this.onScriptRegister(script);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async run() {
|
|
149
|
+
this.onRun();
|
|
150
|
+
for (const script of this.scripts) {
|
|
151
|
+
this.onScriptStart(script);
|
|
152
|
+
for (const suite of script.suites) {
|
|
153
|
+
await suite.init().catch((e) => console.error(e));
|
|
154
|
+
this.onSuiteStart(suite);
|
|
155
|
+
for (const measure of suite.measures) {
|
|
156
|
+
this.onMeasureStart(measure);
|
|
157
|
+
for (const perform of suite.performs) {
|
|
158
|
+
this.onPerformStart(perform);
|
|
159
|
+
const result = await runWorker(
|
|
160
|
+
{
|
|
161
|
+
setup: suite.setup,
|
|
162
|
+
teardown: suite.teardown,
|
|
163
|
+
init: measure.init,
|
|
164
|
+
count: perform.count,
|
|
165
|
+
args: perform.args,
|
|
166
|
+
},
|
|
167
|
+
this.onPerformProgress
|
|
168
|
+
);
|
|
169
|
+
this.onPerformComplete(perform);
|
|
170
|
+
this.onReport(result);
|
|
171
|
+
}
|
|
172
|
+
this.onMeasureComplete(perform);
|
|
173
|
+
}
|
|
174
|
+
this.onSuiteComplete(perform);
|
|
175
|
+
}
|
|
176
|
+
this.onScriptComplete(perform);
|
|
177
|
+
}
|
|
178
|
+
this.onComplete(this);
|
|
179
|
+
}
|
|
62
180
|
}
|
|
63
181
|
|
|
64
|
-
export async function runWorker({ args, count, ...options }) {
|
|
182
|
+
export async function runWorker({ args, count, ...options }, onProgress) {
|
|
65
183
|
const setupCode = options.setup.toString();
|
|
66
184
|
const teardownCode = options.teardown.toString();
|
|
67
185
|
const initCode = options.init.toString();
|
|
@@ -72,81 +190,42 @@ export async function runWorker({ args, count, ...options }) {
|
|
|
72
190
|
count,
|
|
73
191
|
args,
|
|
74
192
|
});
|
|
75
|
-
let i = 0;
|
|
76
|
-
const spinnerSize = SPINNER.length - 1;
|
|
77
|
-
|
|
78
|
-
const timerId = setInterval(() => renderSpinner(i++ % spinnerSize), SPINNER_INTERVAL);
|
|
79
193
|
|
|
80
194
|
const worker = new WorkerThreads.Worker(new URL('runner.js', import.meta.url), { argv: [params] });
|
|
81
195
|
return new Promise((resolve) => {
|
|
82
|
-
worker.on('message',
|
|
196
|
+
worker.on('message', (data) => {
|
|
197
|
+
if (data.type === 'progress') {
|
|
198
|
+
onProgress(data);
|
|
199
|
+
} else if (data.type === 'report') {
|
|
200
|
+
resolve(data);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
83
203
|
worker.on('error', (error) => resolve({ success: false, error: error.message }));
|
|
84
|
-
}).finally((result) => {
|
|
85
|
-
clearInterval(timerId);
|
|
86
|
-
renderSpinner(spinnerSize);
|
|
87
|
-
|
|
88
|
-
return result;
|
|
89
204
|
});
|
|
90
205
|
}
|
|
91
206
|
|
|
92
|
-
export async function runner(suite) {
|
|
93
|
-
console.group(`\nStart ${suite.title} benchmark`);
|
|
94
|
-
|
|
95
|
-
for (let measureIdx = 0; measureIdx < suite.measures.length; measureIdx++) {
|
|
96
|
-
const currentMeasure = suite.measures[measureIdx];
|
|
97
|
-
const reports = {};
|
|
98
|
-
console.group(`\n Measuring performance of ${currentMeasure.title}`);
|
|
99
|
-
for (let performIdx = 0; performIdx < suite.performs.length; performIdx++) {
|
|
100
|
-
const currentPerform = suite.performs[performIdx];
|
|
101
|
-
const report = await runWorker({
|
|
102
|
-
setup: suite.setup,
|
|
103
|
-
teardown: suite.teardown,
|
|
104
|
-
init: currentMeasure.init,
|
|
105
|
-
count: currentPerform.count,
|
|
106
|
-
args: currentPerform.args,
|
|
107
|
-
});
|
|
108
|
-
reports[currentPerform.title] = { title: perform.title, report };
|
|
109
|
-
if (report.success) {
|
|
110
|
-
reports[currentPerform.title] = {
|
|
111
|
-
Count: currentPerform.count,
|
|
112
|
-
'Setup, ms': formatFloat(report.setup),
|
|
113
|
-
'Work, ms': formatFloat(report.work),
|
|
114
|
-
'Avg, ms': formatFloat(report.avg),
|
|
115
|
-
'Mode, ms': report.mode,
|
|
116
|
-
};
|
|
117
|
-
} else {
|
|
118
|
-
reports[`${currentPerform.title} ${currentMeasure.title}`] = {
|
|
119
|
-
Count: currentPerform.count,
|
|
120
|
-
'Setup, ms': '?',
|
|
121
|
-
'Work, ms': '?',
|
|
122
|
-
'Avg, ms': '?',
|
|
123
|
-
'Mode, ms': '?',
|
|
124
|
-
Error: report.error,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
console.groupEnd();
|
|
129
|
-
console.table(reports);
|
|
130
|
-
}
|
|
131
|
-
console.groupEnd();
|
|
132
|
-
}
|
|
133
|
-
|
|
134
207
|
const FALSE_START = () => {
|
|
135
208
|
throw new Error('False start');
|
|
136
209
|
};
|
|
137
210
|
|
|
138
211
|
export async function start(input) {
|
|
139
|
-
const { setupCode, teardownCode, initCode, count, args = [[]] } = JSON.parse(input);
|
|
212
|
+
const { setupCode, teardownCode, initCode, count, reportInterval = 500, args = [[]] } = JSON.parse(input);
|
|
140
213
|
const setup = Function(`return ${setupCode};`)();
|
|
141
214
|
const teardown = Function(`return ${teardownCode};`)();
|
|
142
215
|
const init = Function(`return ${initCode};`)();
|
|
216
|
+
const send = WorkerThreads.parentPort ? (data) => WorkerThreads.parentPort.postMessage(data) : (data) => console.log(data);
|
|
143
217
|
|
|
144
218
|
let i = count;
|
|
145
219
|
let done = FALSE_START;
|
|
146
220
|
|
|
147
221
|
const timings = [];
|
|
148
222
|
const argSize = args.length;
|
|
223
|
+
|
|
224
|
+
send({ type: 'progress', stage: 'setup' });
|
|
225
|
+
const startMark = performance.now();
|
|
149
226
|
const context = await setup();
|
|
227
|
+
const setupMark = performance.now();
|
|
228
|
+
|
|
150
229
|
const initArgs = [() => done()];
|
|
151
230
|
if (init.length > 2) {
|
|
152
231
|
initArgs.unshift(args);
|
|
@@ -154,70 +233,97 @@ export async function start(input) {
|
|
|
154
233
|
if (init.length > 1) {
|
|
155
234
|
initArgs.unshift(context);
|
|
156
235
|
}
|
|
157
|
-
|
|
236
|
+
|
|
237
|
+
send({ type: 'progress', stage: 'init' });
|
|
238
|
+
const initMark = performance.now();
|
|
158
239
|
const action = await init(...initArgs);
|
|
159
|
-
const
|
|
240
|
+
const initDoneMark = performance.now();
|
|
160
241
|
|
|
161
242
|
try {
|
|
243
|
+
let lastCheck = performance.now();
|
|
162
244
|
const loop = (resolve, reject) => {
|
|
163
|
-
const
|
|
245
|
+
const idx = count - i;
|
|
246
|
+
const argIdx = idx % argSize;
|
|
164
247
|
const timerId = setTimeout(reject, 10000, new Error('Timeout'));
|
|
165
248
|
|
|
166
249
|
done = () => {
|
|
167
|
-
|
|
168
|
-
const elapsed =
|
|
250
|
+
const doneTick = performance.now();
|
|
251
|
+
const elapsed = doneTick - startTickTime;
|
|
169
252
|
clearTimeout(timerId);
|
|
170
253
|
done = FALSE_START;
|
|
171
254
|
timings.push(elapsed);
|
|
172
|
-
|
|
255
|
+
if (doneTick - lastCheck > reportInterval) {
|
|
256
|
+
lastCheck = doneTick;
|
|
257
|
+
send({ type: 'progress', stage: 'cycles', progress: idx / count });
|
|
258
|
+
}
|
|
173
259
|
resolve();
|
|
174
260
|
};
|
|
261
|
+
|
|
175
262
|
const startTickTime = performance.now();
|
|
176
|
-
action(...args[argIdx],
|
|
263
|
+
action(...args[argIdx], idx);
|
|
177
264
|
};
|
|
265
|
+
const cyclesMark = performance.now();
|
|
178
266
|
|
|
267
|
+
send({ type: 'progress', stage: 'cycles', progress: 0 });
|
|
179
268
|
while (i--) {
|
|
180
269
|
await new Promise(loop);
|
|
181
270
|
}
|
|
271
|
+
send({ type: 'progress', stage: 'teardown' });
|
|
272
|
+
const teardownMark = performance.now();
|
|
273
|
+
await teardown(context);
|
|
182
274
|
const completeMark = performance.now();
|
|
275
|
+
send({ type: 'progress', stage: 'complete', progress: (count - i) / count });
|
|
183
276
|
|
|
184
|
-
|
|
277
|
+
timings.sort((a, b) => a - b);
|
|
185
278
|
|
|
186
|
-
timings
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
const
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
279
|
+
const min = timings[0];
|
|
280
|
+
const max = timings[timings.length - 1];
|
|
281
|
+
const range = max - min || Number.MIN_VALUE;
|
|
282
|
+
const sum = timings.reduce((a, b) => a + b, 0);
|
|
283
|
+
const avg = sum / timings.length;
|
|
284
|
+
|
|
285
|
+
const step = range / 99 || Number.MIN_VALUE;
|
|
286
|
+
const buckets = Array(100)
|
|
287
|
+
.fill(0)
|
|
288
|
+
.map((_, idx) => [min + idx * step, 0]);
|
|
289
|
+
|
|
290
|
+
// Calc mode O(n)
|
|
291
|
+
timings.forEach((timing, idx) => {
|
|
292
|
+
const index = Math.round((timing - min) / step);
|
|
293
|
+
buckets[index][1] += 1;
|
|
294
|
+
});
|
|
295
|
+
buckets.sort((a, b) => a[1] - b[1]);
|
|
296
|
+
|
|
297
|
+
const medIdx = Math.trunc((50 * timings.length) / 100);
|
|
298
|
+
const med = timings[medIdx];
|
|
299
|
+
const p90Idx = Math.trunc((90 * timings.length) / 100);
|
|
300
|
+
const p90 = timings[p90Idx];
|
|
301
|
+
const p95Idx = Math.trunc((95 * timings.length) / 100);
|
|
302
|
+
const p95 = timings[p95Idx];
|
|
303
|
+
const p99Idx = Math.trunc((99 * timings.length) / 100);
|
|
304
|
+
const p99 = timings[p99Idx];
|
|
305
|
+
const mode = buckets[buckets.length - 1][0];
|
|
306
|
+
|
|
307
|
+
send({
|
|
308
|
+
type: 'report',
|
|
309
|
+
success: true,
|
|
310
|
+
count: timings.length,
|
|
209
311
|
min,
|
|
210
312
|
max,
|
|
313
|
+
sum,
|
|
211
314
|
avg,
|
|
315
|
+
med,
|
|
316
|
+
mode,
|
|
212
317
|
p90,
|
|
213
318
|
p95,
|
|
214
319
|
p99,
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
320
|
+
setup: setupMark - startMark,
|
|
321
|
+
init: initDoneMark - initMark,
|
|
322
|
+
cycles: teardownMark - cyclesMark,
|
|
323
|
+
teardown: completeMark - teardownMark,
|
|
324
|
+
total: completeMark - setupMark,
|
|
219
325
|
});
|
|
220
326
|
} catch (error) {
|
|
221
|
-
|
|
327
|
+
send({ type: 'report', success: false, error: error.stack });
|
|
222
328
|
}
|
|
223
329
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "overtake",
|
|
3
|
-
"version": "0.0.1-
|
|
3
|
+
"version": "0.0.1-rc2",
|
|
4
4
|
"description": "NodeJS performance benchmark",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"overtake": "cli.js"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"
|
|
13
|
-
"test": "yarn node --experimental-vm-modules $(yarn bin jest)",
|
|
12
|
+
"start": "node ./cli.js",
|
|
13
|
+
"test": "yarn node --experimental-vm-modules $(yarn bin jest) --detectOpenHandles",
|
|
14
14
|
"lint": "eslint .",
|
|
15
15
|
"prepare": "husky install"
|
|
16
16
|
},
|
|
@@ -33,16 +33,13 @@
|
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@jest/globals": "^27.5.1",
|
|
35
35
|
"@types/jest": "^27.4.1",
|
|
36
|
-
"eslint": "^8.14.0",
|
|
37
|
-
"eslint-config-airbnb-base": "^15.0.0",
|
|
38
|
-
"eslint-config-prettier": "^8.5.0",
|
|
39
|
-
"eslint-plugin-import": "^2.26.0",
|
|
40
36
|
"husky": "^7.0.4",
|
|
41
37
|
"jest": "^27.5.1",
|
|
42
38
|
"prettier": "^2.6.2",
|
|
43
39
|
"pretty-quick": "^3.1.3"
|
|
44
40
|
},
|
|
45
41
|
"dependencies": {
|
|
42
|
+
"evnty": "^0.7.4",
|
|
46
43
|
"glob": "^8.0.1"
|
|
47
44
|
}
|
|
48
45
|
}
|
package/reporter.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏', '⠿'];
|
|
2
|
+
const SPINNER_INTERVAL = 100;
|
|
3
|
+
const SPINNER_PADDING = 5;
|
|
4
|
+
|
|
5
|
+
const ACCURACY = 6;
|
|
6
|
+
|
|
7
|
+
/** TODO
|
|
8
|
+
* [PROGRESS] script/file.js
|
|
9
|
+
* Test suite title
|
|
10
|
+
* ➤ Measure
|
|
11
|
+
* ✓ Perform
|
|
12
|
+
* ✕ Perform
|
|
13
|
+
* */
|
|
14
|
+
|
|
15
|
+
const renderString = (message, x, direction) => {
|
|
16
|
+
const size = message.length;
|
|
17
|
+
process.stdout.moveCursor(0, -1);
|
|
18
|
+
process.stdout.cursorTo(x);
|
|
19
|
+
process.stdout.clearLine(direction);
|
|
20
|
+
if (direction === -1) {
|
|
21
|
+
process.stdout.cursorTo(0);
|
|
22
|
+
}
|
|
23
|
+
process.stdout.write(message);
|
|
24
|
+
process.stdout.moveCursor(-process.stdout.rows, 1);
|
|
25
|
+
|
|
26
|
+
return size;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const renderSpinner = (index) => renderString(SPINNER[index], 0, -1);
|
|
30
|
+
|
|
31
|
+
const paddings = [];
|
|
32
|
+
|
|
33
|
+
const renderMessage = (column, message) => {
|
|
34
|
+
const padding = SPINNER_PADDING + column * 10;
|
|
35
|
+
paddings[column + 1] = renderString(message, padding, 1);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export function formatFloat(value, digits = ACCURACY) {
|
|
39
|
+
return parseFloat(value.toFixed(digits));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function defaultReporter(overtake) {
|
|
43
|
+
const spinnerSize = SPINNER.length - 1;
|
|
44
|
+
let i = 0;
|
|
45
|
+
let timerId = null;
|
|
46
|
+
|
|
47
|
+
overtake.onLoad.on(() => {});
|
|
48
|
+
overtake.onRun.on(() => {
|
|
49
|
+
console.log();
|
|
50
|
+
});
|
|
51
|
+
overtake.onComplete.on(() => {});
|
|
52
|
+
overtake.onScriptRegister.on(() => {});
|
|
53
|
+
overtake.onScriptStart.on(() => {});
|
|
54
|
+
overtake.onScriptComplete.on(() => {});
|
|
55
|
+
overtake.onSuiteRegister.on(() => {});
|
|
56
|
+
overtake.onSuiteStart.on(() => {});
|
|
57
|
+
overtake.onSuiteComplete.on(() => {});
|
|
58
|
+
overtake.onSetupRegister.on(() => {});
|
|
59
|
+
overtake.onTeardownRegister.on(() => {});
|
|
60
|
+
overtake.onMeasureRegister.on(() => {});
|
|
61
|
+
overtake.onMeasureStart.on((measure) => {
|
|
62
|
+
console.log(measure.title);
|
|
63
|
+
});
|
|
64
|
+
overtake.onMeasureComplete.on(() => {});
|
|
65
|
+
overtake.onPerformRegister.on(() => {});
|
|
66
|
+
overtake.onPerformStart.on((perform) => {
|
|
67
|
+
console.log(perform.title);
|
|
68
|
+
console.log();
|
|
69
|
+
i = 0;
|
|
70
|
+
timerId = setInterval(() => renderSpinner(i++ % spinnerSize), SPINNER_INTERVAL);
|
|
71
|
+
});
|
|
72
|
+
overtake.onPerformComplete.on(() => {
|
|
73
|
+
clearInterval(timerId);
|
|
74
|
+
renderSpinner(spinnerSize);
|
|
75
|
+
});
|
|
76
|
+
overtake.onPerformProgress.on(({ stage, progress }) => {
|
|
77
|
+
renderMessage(0, stage);
|
|
78
|
+
if (typeof progress !== 'undefined') {
|
|
79
|
+
renderMessage(1, `${(progress * 100).toFixed(0)}%`.padStart(4, ' '));
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
overtake.onReport.on((report) => {
|
|
83
|
+
if (report.success) {
|
|
84
|
+
renderMessage(2, `total:${report.total.toFixed(0)}ms mode:${report.mode.toFixed(ACCURACY)}ms`);
|
|
85
|
+
} else {
|
|
86
|
+
renderMessage(1, report.error);
|
|
87
|
+
}
|
|
88
|
+
console.log();
|
|
89
|
+
});
|
|
90
|
+
}
|
package/index.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
declare global {
|
|
2
|
-
declare function benchmark(title: string, init: () => void): void;
|
|
3
|
-
|
|
4
|
-
declare function setup<C>(init: () => C): any;
|
|
5
|
-
|
|
6
|
-
declare function teardown<C>(teardown: (context: C) => void): void;
|
|
7
|
-
|
|
8
|
-
declare function measure(title: string, init: (next: () => void) => void): void;
|
|
9
|
-
declare function measure<C>(title: string, init: (context: C, next: () => void) => void): void;
|
|
10
|
-
declare function measure<C, A>(title: string, init: (context: C, args: A, next: () => void) => void): void;
|
|
11
|
-
|
|
12
|
-
declare function perform<A>(title: string, counter: number, args: A): void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export { benchmark, setup, teardown, measure, perform };
|