@mikrojs/native 0.21.1-next.20260927212749 → 0.22.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.
@@ -1,686 +0,0 @@
1
- /* eslint-disable no-console */
2
- import {gc, memoryUsage, MonotonicTimestamp} from 'mikro/sys'
3
- import {pendingCount as pendingHttpCount} from 'native:mikro/http'
4
- import {activeTimers} from 'native:mikro/sys'
5
-
6
- import type {ErrResult, OkResult, Result} from '../result/types.js'
7
- import type {Assert, TestFn, TestOptions} from './types.js'
8
-
9
- interface TestCase {
10
- name: string
11
- fn: TestFn
12
- skip: boolean
13
- only: boolean
14
- todo: boolean
15
- timeout?: number
16
- }
17
-
18
- interface Suite {
19
- name: string
20
- tests: TestCase[]
21
- skip: boolean
22
- only: boolean
23
- todo: boolean
24
- beforeAll?: TestFn
25
- afterAll?: TestFn
26
- beforeEach: TestFn[]
27
- afterEach: TestFn[]
28
- }
29
-
30
- const suites: Suite[] = []
31
- let currentSuite: Suite | null = null
32
- /** JS heap (bytes) the file still holds above baseline, summed over suites.
33
- * Retention accumulates, so a per-suite figure is added rather than
34
- * replacing the last one: a leak in suite 1 is still a leak once suite 5
35
- * has run. */
36
- let heapRetained = 0
37
- /** Baseline the running suite is measured against. Set to the previous
38
- * suite's closing heap, or recaptured after a beforeAll so that suite's
39
- * warmup is excluded. */
40
- let suiteBaseline = 0
41
- /** Free system heap (bytes) at the start of the run, or 0 on the host (no
42
- * system heap). Never recaptured: sysUsed is a peak, and warmup (module
43
- * loads, TLS, wifi) is memory the file genuinely needed at once, so
44
- * excluding it would understate how close the run came to OOM. Retention is
45
- * the figure that wants warmup excluded, and it has its own baseline. */
46
- let sysFreeStart = 0
47
- /** Lowest free system heap (bytes) sampled (post-gc) this run, or 0 on the
48
- * host. The file's closest sampled approach to OOM. The module re-inits
49
- * for each test file (fresh runtime per file), so this is a true per-file
50
- * figure, not a process-lifetime watermark shared across files. */
51
- let sysFreeFloor = 0
52
- /** Free system heap at the running suite's start, for its own peak. */
53
- let suiteFreeStart = 0
54
- /** Lowest free system heap sampled during the running suite. */
55
- let suiteFreeFloor = 0
56
-
57
- /**
58
- * Recapture the suite baseline. Called by the harness after each suite's
59
- * beforeAll resolves so warmup allocations (module loads, fetch/TLS
60
- * lazy-init, wifi connection) don't count toward heapDelta. A microtask
61
- * yield before the gc lets the beforeAll async frame's locals become
62
- * collectible — otherwise the baseline would be inflated by vars that
63
- * were still pinned by the suspended closure when beforeAll resolved,
64
- * and the suite would close below its baseline (negative delta).
65
- */
66
- async function captureSuiteBaseline(): Promise<void> {
67
- await Promise.resolve()
68
- gc()
69
- const {heapUsed} = memoryUsage()
70
- suiteBaseline = heapUsed
71
- }
72
-
73
- /**
74
- * Close out the running suite: fold its retention into the file total and
75
- * start the next suite from where this one ended. Returns the suite's own
76
- * figures for the suite_end event, which is what a reader needs to find the
77
- * suite behind a file-level regression.
78
- */
79
- function closeSuite(): {retained: number; sysUsed: number} {
80
- gc()
81
- const {heapUsed, systemFree} = memoryUsage()
82
- const retained = heapUsed - suiteBaseline
83
- heapRetained += retained
84
- suiteBaseline = heapUsed
85
- if (systemFree > 0 && systemFree < suiteFreeFloor) suiteFreeFloor = systemFree
86
- const sysUsed = suiteFreeStart > suiteFreeFloor ? suiteFreeStart - suiteFreeFloor : 0
87
- return {retained, sysUsed}
88
- }
89
-
90
- function newSuite(name: string, flags: {skip?: boolean; only?: boolean; todo?: boolean}): Suite {
91
- return {
92
- name,
93
- tests: [],
94
- skip: flags.skip ?? false,
95
- only: flags.only ?? false,
96
- todo: flags.todo ?? false,
97
- beforeEach: [],
98
- afterEach: [],
99
- }
100
- }
101
-
102
- function runSuiteFn(suite: Suite, fn: () => void): void {
103
- suites.push(suite)
104
- const prev = currentSuite
105
- currentSuite = suite
106
- fn()
107
- currentSuite = prev
108
- }
109
-
110
- // --- name interpolation for .each ---
111
-
112
- function interpolateName(template: string, value: unknown, index: number): string {
113
- let name = template.replaceAll('%#', String(index))
114
- name = name.replaceAll('%s', String(value))
115
- if (typeof value === 'object' && value !== null) {
116
- name = name.replaceAll('%o', JSON.stringify(value))
117
- }
118
- return name
119
- }
120
-
121
- // --- describe ---
122
-
123
- type EachDescribeFn = <T>(
124
- cases: T[],
125
- ) => (name: string, fn: (value: T, index: number) => void) => void
126
-
127
- function _describe(name: string, fn: () => void): void {
128
- runSuiteFn(newSuite(name, {}), fn)
129
- }
130
-
131
- _describe.skip = function (name: string, fn: () => void): void {
132
- runSuiteFn(newSuite(name, {skip: true}), fn)
133
- }
134
-
135
- _describe.only = function (name: string, fn: () => void): void {
136
- runSuiteFn(newSuite(name, {only: true}), fn)
137
- }
138
-
139
- _describe.todo = function (name: string, fn: () => void): void {
140
- runSuiteFn(newSuite(name, {todo: true}), fn)
141
- }
142
-
143
- _describe.fixme = function (name: string, fn: () => void): void {
144
- runSuiteFn(newSuite(name, {skip: true}), fn)
145
- }
146
-
147
- _describe.skipIf = function (condition: unknown) {
148
- return condition ? _describe.skip : _describe
149
- }
150
-
151
- _describe.runIf = function (condition: unknown) {
152
- return condition ? _describe : _describe.skip
153
- }
154
-
155
- function describeEach(
156
- variant: (name: string, fn: () => void) => void,
157
- ): <T>(cases: T[]) => (name: string, fn: (value: T, index: number) => void) => void {
158
- return function <T>(cases: T[]) {
159
- return function (nameTemplate: string, fn: (value: T, index: number) => void): void {
160
- cases.forEach((value, index) => {
161
- variant(interpolateName(nameTemplate, value, index), () => fn(value, index))
162
- })
163
- }
164
- }
165
- }
166
-
167
- _describe.each = describeEach(_describe) as EachDescribeFn
168
- ;(_describe.skip as any).each = describeEach(_describe.skip) as EachDescribeFn
169
- ;(_describe.only as any).each = describeEach(_describe.only) as EachDescribeFn
170
- ;(_describe.todo as any).each = describeEach(_describe.todo) as EachDescribeFn
171
- ;(_describe.fixme as any).each = describeEach(_describe.fixme) as EachDescribeFn
172
-
173
- export const describe = _describe as typeof _describe & {
174
- skip: typeof _describe.skip & {each: EachDescribeFn}
175
- only: typeof _describe.only & {each: EachDescribeFn}
176
- todo: typeof _describe.todo & {each: EachDescribeFn}
177
- fixme: typeof _describe.fixme & {each: EachDescribeFn}
178
- skipIf: (condition: unknown) => typeof _describe & {each: EachDescribeFn}
179
- runIf: (condition: unknown) => typeof _describe & {each: EachDescribeFn}
180
- each: EachDescribeFn
181
- }
182
-
183
- // --- test ---
184
-
185
- type EachTestFn = <T>(
186
- cases: T[],
187
- ) => (name: string, fn: (value: T, index: number) => void | Promise<void>) => void
188
-
189
- function pushTest(
190
- where: string,
191
- name: string,
192
- fn: TestFn,
193
- flags: {skip?: boolean; only?: boolean; todo?: boolean},
194
- options?: TestOptions,
195
- ): void {
196
- if (!currentSuite) throw new Error(`${where}() must be inside describe()`)
197
- currentSuite.tests.push({
198
- name,
199
- fn,
200
- skip: flags.skip ?? false,
201
- only: flags.only ?? false,
202
- todo: flags.todo ?? false,
203
- timeout: options?.timeout,
204
- })
205
- }
206
-
207
- function _test(name: string, fn: TestFn, options?: TestOptions): void {
208
- pushTest('test', name, fn, {}, options)
209
- }
210
-
211
- _test.skip = function (name: string, fn: TestFn, options?: TestOptions): void {
212
- pushTest('test.skip', name, fn, {skip: true}, options)
213
- }
214
-
215
- _test.only = function (name: string, fn: TestFn, options?: TestOptions): void {
216
- pushTest('test.only', name, fn, {only: true}, options)
217
- }
218
-
219
- _test.fixme = function (name: string, fn: TestFn, options?: TestOptions): void {
220
- pushTest('test.fixme', name, fn, {skip: true}, options)
221
- }
222
-
223
- _test.skipIf = function (condition: unknown) {
224
- return condition ? _test.skip : _test
225
- }
226
-
227
- _test.runIf = function (condition: unknown) {
228
- return condition ? _test : _test.skip
229
- }
230
-
231
- _test.todo = function (name: string): void {
232
- pushTest('test.todo', name, () => {}, {todo: true})
233
- }
234
-
235
- function testEach(
236
- variant: (name: string, fn: TestFn) => void,
237
- ): <T>(
238
- cases: T[],
239
- ) => (name: string, fn: (value: T, index: number) => void | Promise<void>) => void {
240
- return function <T>(cases: T[]) {
241
- return function (
242
- nameTemplate: string,
243
- fn: (value: T, index: number) => void | Promise<void>,
244
- ): void {
245
- cases.forEach((value, index) => {
246
- variant(interpolateName(nameTemplate, value, index), () => fn(value, index))
247
- })
248
- }
249
- }
250
- }
251
-
252
- _test.each = testEach(_test) as EachTestFn
253
- ;(_test.skip as any).each = testEach(_test.skip) as EachTestFn
254
- ;(_test.only as any).each = testEach(_test.only) as EachTestFn
255
- ;(_test.fixme as any).each = testEach(_test.fixme) as EachTestFn
256
-
257
- export const test = _test as typeof _test & {
258
- skip: typeof _test.skip & {each: EachTestFn}
259
- only: typeof _test.only & {each: EachTestFn}
260
- fixme: typeof _test.fixme & {each: EachTestFn}
261
- skipIf: (condition: unknown) => typeof _test & {each: EachTestFn}
262
- runIf: (condition: unknown) => typeof _test & {each: EachTestFn}
263
- todo: (name: string) => void
264
- each: EachTestFn
265
- }
266
-
267
- // --- hooks ---
268
-
269
- export function beforeAll(fn: TestFn): void {
270
- if (!currentSuite) throw new Error('beforeAll() must be inside describe()')
271
- currentSuite.beforeAll = fn
272
- }
273
-
274
- export function afterAll(fn: TestFn): void {
275
- if (!currentSuite) throw new Error('afterAll() must be inside describe()')
276
- currentSuite.afterAll = fn
277
- }
278
-
279
- export function beforeEach(fn: TestFn): void {
280
- if (!currentSuite) throw new Error('beforeEach() must be inside describe()')
281
- currentSuite.beforeEach.push(fn)
282
- }
283
-
284
- export function afterEach(fn: TestFn): void {
285
- if (!currentSuite) throw new Error('afterEach() must be inside describe()')
286
- currentSuite.afterEach.push(fn)
287
- }
288
-
289
- // --- assertions ---
290
-
291
- function fmt(v: unknown): string {
292
- if (v === undefined) return 'undefined'
293
- if (v === null) return 'null'
294
- if (typeof v === 'string') return JSON.stringify(v)
295
- if (v instanceof Uint8Array) return `Uint8Array[${v.join(', ')}]`
296
- try {
297
- return JSON.stringify(v)
298
- } catch {
299
- return String(v)
300
- }
301
- }
302
-
303
- class AssertError extends Error {
304
- constructor(message: string) {
305
- super(message)
306
- this.name = 'AssertError'
307
- }
308
- }
309
-
310
- function fail(message: string | undefined, detail: string): never {
311
- throw new AssertError(message ? `${message}: ${detail}` : detail)
312
- }
313
-
314
- function assertOk<T, E>(r: Result<T, E>, message?: string): asserts r is OkResult<T> {
315
- if (!r.ok) {
316
- fail(message, `expected ok result, got error: ${formatThrown(r.error)}`)
317
- }
318
- }
319
-
320
- function assertErr<T, E>(r: Result<T, E>, message?: string): asserts r is ErrResult<E> {
321
- if (r.ok) {
322
- fail(message, `expected error result, got ok: ${fmt(r.value)}`)
323
- }
324
- }
325
-
326
- export const assert: Assert = {
327
- equal(actual: unknown, expected: unknown, message?: string): void {
328
- if (!Object.is(actual, expected)) {
329
- fail(message, `expected ${fmt(expected)}, got ${fmt(actual)}`)
330
- }
331
- },
332
-
333
- notEqual(actual: unknown, unexpected: unknown, message?: string): void {
334
- if (Object.is(actual, unexpected)) {
335
- fail(message, `expected value to differ from ${fmt(unexpected)}`)
336
- }
337
- },
338
-
339
- truthy(value: unknown, message?: string): void {
340
- if (!value) {
341
- fail(message, `expected truthy, got ${fmt(value)}`)
342
- }
343
- },
344
-
345
- deepEqual(actual: unknown, expected: unknown, message?: string): void {
346
- const a = JSON.stringify(actual)
347
- const b = JSON.stringify(expected)
348
- if (a !== b) {
349
- fail(message, `expected ${b}, got ${a}`)
350
- }
351
- },
352
-
353
- throws(fn: () => void, message?: string): Error {
354
- try {
355
- fn()
356
- } catch (e) {
357
- return e instanceof Error ? e : new Error(String(e))
358
- }
359
- fail(message, 'expected function to throw')
360
- },
361
-
362
- async rejects(fn: () => Promise<unknown>, message?: string): Promise<Error> {
363
- try {
364
- await fn()
365
- } catch (e) {
366
- return e instanceof Error ? e : new Error(String(e))
367
- }
368
- fail(message, 'expected promise to reject')
369
- },
370
-
371
- type(value: unknown, expected: string, message?: string): void {
372
- if (typeof value !== expected) {
373
- fail(message, `expected typeof ${expected}, got ${typeof value}`)
374
- }
375
- },
376
-
377
- instance(value: unknown, ctor: new (...args: any[]) => any, message?: string): void {
378
- if (!(value instanceof ctor)) {
379
- fail(message, `expected instanceof ${ctor.name}, got ${fmt(value)}`)
380
- }
381
- },
382
-
383
- ok: assertOk,
384
-
385
- err: assertErr,
386
- }
387
-
388
- // --- runner ---
389
-
390
- const DEFAULT_TEST_TIMEOUT = 10_000
391
-
392
- type TestEvent =
393
- | {e: 1; s: string; n: number}
394
- | {e: 2; s: string; t: string; d: number}
395
- | {e: 3; s: string; t: string; d: number; m: string}
396
- | {e: 4; s: string; t: string}
397
- | {e: 5; s: string; hr?: number; su?: number}
398
- | {
399
- e: 6
400
- p: number
401
- f: number
402
- k: number
403
- o: number
404
- d: number
405
- hb?: number
406
- ha?: number
407
- hr?: number
408
- su?: number
409
- sf?: number
410
- tb?: number
411
- ta?: number
412
- pb?: number
413
- pa?: number
414
- }
415
- | {e: 7; s: string; m: string}
416
- | {e: 8; u: number; t: number; f?: number; mf?: number}
417
- | {e: 9; s: string; t: string}
418
-
419
- const emit: (event: TestEvent) => void =
420
- typeof (globalThis as any).__testEmit === 'function'
421
- ? (event) => (globalThis as any).__testEmit(JSON.stringify(event))
422
- : (event) => console.log('__TEST__' + JSON.stringify(event))
423
-
424
- function elapsedMs(start: MonotonicTimestamp): number {
425
- return Math.round(MonotonicTimestamp.now().since(start))
426
- }
427
-
428
- /** Format a thrown value for test output. Handles Error instances,
429
- * plain objects with `name`/`message` (like our Result error shapes
430
- * `{name: 'Network', message: '...'}`), and falls back to String(). */
431
- function formatThrown(e: unknown): string {
432
- if (e instanceof Error) return e.message
433
- if (e && typeof e === 'object') {
434
- const obj = e as {name?: unknown; message?: unknown}
435
- const name = typeof obj.name === 'string' ? obj.name : undefined
436
- const message = typeof obj.message === 'string' ? obj.message : undefined
437
- if (name && message) return `${name}: ${message}`
438
- if (message) return message
439
- if (name) return name
440
- }
441
- return String(e)
442
- }
443
-
444
- function emitHeap(): void {
445
- gc()
446
- const mem = memoryUsage()
447
- const evt: TestEvent = {e: 8, u: mem.heapUsed, t: mem.heapTotal}
448
- if (mem.systemFree > 0) {
449
- evt.f = mem.systemFree
450
- if (sysFreeFloor === 0 || mem.systemFree < sysFreeFloor) sysFreeFloor = mem.systemFree
451
- if (suiteFreeFloor === 0 || mem.systemFree < suiteFreeFloor) suiteFreeFloor = mem.systemFree
452
- }
453
- if (mem.systemMinFree > 0) evt.mf = mem.systemMinFree
454
- emit(evt)
455
- }
456
-
457
- async function run(): Promise<void> {
458
- let passed = 0
459
- let failed = 0
460
- let skipped = 0
461
- let todo = 0
462
- const startTime = MonotonicTimestamp.now()
463
-
464
- // Leak-detection baseline: captured after gc(), with all suites already
465
- // registered from module eval, so persistent harness allocations are
466
- // included in the baseline rather than counted as growth. The harness
467
- // recaptures after each suite's beforeAll resolves (see below), so
468
- // warmup costs (module loads, TLS lazy-init, wifi connection) get
469
- // folded into the baseline automatically.
470
- gc()
471
- // Destructure to primitives so the live memoryUsage() object isn't held
472
- // while the baseline is captured (it would otherwise be counted in it).
473
- const {heapUsed: startHeap, systemFree: startFree} = memoryUsage()
474
- heapRetained = 0
475
- suiteBaseline = startHeap
476
- if (startFree > 0) {
477
- sysFreeStart = startFree
478
- sysFreeFloor = startFree
479
- }
480
- const timersBefore = activeTimers()
481
- const pendingBefore = pendingHttpCount()
482
-
483
- // If any suite or test is marked .only anywhere, filter everything else.
484
- const hasOnly = suites.some((s) => s.only || s.tests.some((t) => t.only))
485
-
486
- for (const suite of suites) {
487
- // Open this suite's own peak window on the post-gc sample emitHeap takes.
488
- suiteFreeFloor = 0
489
- emitHeap()
490
- suiteFreeStart = suiteFreeFloor
491
- emit({e: 1, s: suite.name, n: suite.tests.length})
492
-
493
- if (suite.skip) {
494
- for (const t of suite.tests) {
495
- if (t.todo) {
496
- emit({e: 9, s: suite.name, t: t.name})
497
- todo++
498
- } else {
499
- emit({e: 4, s: suite.name, t: t.name})
500
- skipped++
501
- }
502
- }
503
- emit({e: 5, s: suite.name})
504
- continue
505
- }
506
-
507
- if (suite.todo) {
508
- for (const t of suite.tests) {
509
- emit({e: 9, s: suite.name, t: t.name})
510
- todo++
511
- }
512
- emit({e: 5, s: suite.name})
513
- continue
514
- }
515
-
516
- // Only-mode: skip entire suites that are not .only and contain no .only tests.
517
- const suiteHasOnlyTest = suite.tests.some((t) => t.only)
518
- if (hasOnly && !suite.only && !suiteHasOnlyTest) {
519
- for (const t of suite.tests) {
520
- if (t.todo) {
521
- emit({e: 9, s: suite.name, t: t.name})
522
- todo++
523
- } else {
524
- emit({e: 4, s: suite.name, t: t.name})
525
- skipped++
526
- }
527
- }
528
- emit({e: 5, s: suite.name})
529
- continue
530
- }
531
-
532
- // Within a participating suite, if any test is .only, skip the rest.
533
- const onlyInSuite = hasOnly && suiteHasOnlyTest
534
-
535
- if (suite.beforeAll) {
536
- try {
537
- await suite.beforeAll()
538
- } catch (e) {
539
- const msg = formatThrown(e)
540
- emit({e: 7, s: suite.name, m: msg})
541
- // Fail the suite's tests rather than skip them: a broken beforeAll is
542
- // a broken run, and skipping made it indistinguishable from a
543
- // deliberate gate. The e:7 above carries the real error; the per-test
544
- // message just points at it.
545
- for (const t of suite.tests) {
546
- if (t.todo) {
547
- emit({e: 9, s: suite.name, t: t.name})
548
- todo++
549
- } else {
550
- emit({e: 3, s: suite.name, t: t.name, d: 0, m: 'beforeAll failed'})
551
- failed++
552
- }
553
- }
554
- emit({e: 5, s: suite.name})
555
- continue
556
- }
557
- // Recapture the baseline now that beforeAll has fully resolved and
558
- // its closure frame is eligible for collection. Only this suite is
559
- // measured against it: earlier suites were already closed out into
560
- // heapRetained, so their allocations survive the recapture.
561
- await captureSuiteBaseline()
562
- }
563
-
564
- for (const t of suite.tests) {
565
- if (t.todo) {
566
- emit({e: 9, s: suite.name, t: t.name})
567
- todo++
568
- continue
569
- }
570
-
571
- if (t.skip) {
572
- emit({e: 4, s: suite.name, t: t.name})
573
- skipped++
574
- continue
575
- }
576
-
577
- if (onlyInSuite && !t.only) {
578
- emit({e: 4, s: suite.name, t: t.name})
579
- skipped++
580
- continue
581
- }
582
-
583
- emitHeap()
584
- const testTimeout = t.timeout ?? DEFAULT_TEST_TIMEOUT
585
- const testStart = MonotonicTimestamp.now()
586
- try {
587
- await new Promise<void>((resolve, reject) => {
588
- const timer = setTimeout(
589
- () => reject(new Error(`timeout (${testTimeout}ms)`)),
590
- testTimeout,
591
- )
592
- ;(async () => {
593
- for (const hook of suite.beforeEach) await hook()
594
- await t.fn()
595
- })().then(
596
- () => {
597
- clearTimeout(timer)
598
- resolve()
599
- },
600
- (e) => {
601
- clearTimeout(timer)
602
- reject(e)
603
- },
604
- )
605
- })
606
- emit({e: 2, s: suite.name, t: t.name, d: elapsedMs(testStart)})
607
- passed++
608
- } catch (e) {
609
- const msg = formatThrown(e)
610
- emit({e: 3, s: suite.name, t: t.name, d: elapsedMs(testStart), m: msg})
611
- failed++
612
- }
613
- for (const hook of suite.afterEach) {
614
- try {
615
- await hook()
616
- } catch {
617
- // afterEach errors are not fatal
618
- }
619
- }
620
- }
621
-
622
- if (suite.afterAll) {
623
- try {
624
- await suite.afterAll()
625
- } catch {
626
- // afterAll errors are not fatal
627
- }
628
- }
629
-
630
- const closed = closeSuite()
631
- const endEvt: TestEvent = {e: 5, s: suite.name, hr: closed.retained}
632
- if (closed.sysUsed > 0) endEvt.su = closed.sysUsed
633
- emit(endEvt)
634
- }
635
-
636
- gc()
637
- // Destructure to primitives (see baseline capture above) so the live
638
- // memoryUsage() object isn't held while we build the run_done event.
639
- const {heapUsed: heapAfter, systemFree: endFree} = memoryUsage()
640
- if (endFree > 0 && (sysFreeFloor === 0 || endFree < sysFreeFloor)) {
641
- sysFreeFloor = endFree
642
- }
643
- // Fold in whatever ran outside a suite's own accounting: a file with no
644
- // suites at all, and the skip/todo bookkeeping between them.
645
- heapRetained += heapAfter - suiteBaseline
646
- const timersAfter = activeTimers()
647
- const pendingAfter = pendingHttpCount()
648
-
649
- // Per-file system-heap figures. sysFreeFloor is the lowest free heap we
650
- // sampled (post-gc) this run; sysUsed is how far free heap fell from the
651
- // run's start to that low. They sum back to the starting free, so "peak"
652
- // and "min free" read as one story. Measured from the start of the run and
653
- // never rebaselined: unlike retention, a peak wants warmup counted, since
654
- // memory a beforeAll takes is memory the file needed at once. Samples are
655
- // taken between tests, so a transient peak inside a single test can dip
656
- // below what sysFreeFloor saw.
657
- const sysUsed = sysFreeStart > sysFreeFloor ? sysFreeStart - sysFreeFloor : 0
658
-
659
- const doneEvt: TestEvent = {
660
- e: 6,
661
- p: passed,
662
- f: failed,
663
- k: skipped,
664
- o: todo,
665
- d: elapsedMs(startTime),
666
- hb: startHeap,
667
- ha: heapAfter,
668
- hr: heapRetained,
669
- tb: timersBefore,
670
- ta: timersAfter,
671
- pb: pendingBefore,
672
- pa: pendingAfter,
673
- }
674
- if (sysFreeStart > 0) {
675
- doneEvt.su = sysUsed
676
- doneEvt.sf = sysFreeFloor
677
- }
678
- emit(doneEvt)
679
-
680
- // Signal the supervisor (if any) that this file is complete so it can
681
- // swap in the next runtime. No-op in non-test-harness contexts.
682
- ;(globalThis as any).__testFileDone?.()
683
- }
684
-
685
- // Auto-run after module evaluation completes
686
- setTimeout(() => void run(), 0)