import { test } from 'node:test' import assert from 'node:assert/strict' import './test-support.mjs' const { PerformanceRecording } = await import('../src/performanceRecording.ts') function frame(time, drew, phase = 'idle') { return { callbackTimestampMs: time, startedAtMs: time + 0.1, drew, mode: 'Inspect', phase, updateMs: 0.05, submitMs: drew ? 0.2 : null, callbackWorkMs: drew ? 0.3 : 0.06, drawCalls: drew ? 4 : 0, triangles: drew ? 300 : 0, textures: 8, programs: 6, } } test('logs distinguish 30Hz idle callbacks from actual drawing and unavailable GPU time', () => { const log = new PerformanceRecording() log.start(0, { gpuTimerSupported: false }) for (let i = 0; i < 60; i++) log.frame(frame(i * 33, i >= 30, i >= 30 ? 'sweep' : 'idle')) log.stop(1980) const report = log.report() assert.equal(report.summary.callbacks, 60) assert.equal(report.summary.draws, 30) assert.equal(report.summary.callbacksAfterIdle.medianMs, 33) assert.equal(report.summary.callbacksAfterDraw.medianMs, 33) assert.equal(report.summary.intervalBuckets.over28To38ms, 59) assert.equal(report.summary.cpuSubmit.count, 30) assert.equal(report.summary.phases.find(phase => phase.phase === 'idle').draws, 0) assert.equal(report.summary.phases.find(phase => phase.phase === 'sweep').cpuSubmit.count, 30) assert.ok(Math.abs(report.summary.actualArrivalIntervals.medianMs - 33) < 0.001) assert.equal(report.summary.gpuRender, null) }) test('GPU results stay attached to issue frames, including results delivered after stop', () => { const log = new PerformanceRecording() log.start(0, {}) log.frame(frame(0, true, 'revealing')) log.frame(frame(33, true, 'inspecting')) log.stop(34) log.gpuSample(0, 12) log.gpuSample(-33, 100) const report = log.report() assert.deepEqual(report.gpuSamples, [{ frameIndex: 0, elapsedMs: 12 }]) assert.equal(report.frames[report.gpuSamples[0].frameIndex].phase, 'revealing') assert.equal(report.summary.gpuRender.medianMs, 12) }) test('visibility discontinuities do not become slow foreground frames, but real stalls remain logged', () => { const log = new PerformanceRecording() log.start(0, {}) log.frame(frame(0, false)) log.event(20, 'visibilitychange', { visibility: 'hidden' }) log.breakContinuity() log.frame(frame(5000, false)) log.frame(frame(6000, true)) const report = log.report() assert.equal(report.frames[1].intervalMs, null) assert.equal(report.frames[2].intervalMs, 1000) assert.equal(report.summary.callbackIntervals.worstMs, 1000) assert.equal(report.events.length, 1) }) test('recordings stop at 30 seconds and cap frames/events without growing indefinitely', () => { const log = new PerformanceRecording() log.start(0, {}) log.frame(frame(0, true)) log.frame(frame(30001, true)) assert.equal(log.active, false) assert.equal(log.report().durationMs, 30000) assert.equal(log.frameCount, 1) log.start(0, {}) for (let i = 0; i < 1000; i++) log.event(i, 'input') for (let i = 0; i < 8000; i++) log.frame(frame(i, true)) const report = log.report() assert.equal(report.frames.length, 7200) assert.equal(report.stopReason, 'frame-limit') assert.equal(report.events.length, 256) assert.equal(report.droppedEvents, 744) }) test('exports are independent snapshots and starting again clears previous evidence', () => { const log = new PerformanceRecording() const metadata = { gpu: 'test', initialState: { mode: 'Inspect' } } log.start(0, metadata) metadata.initialState.mode = 'Pack' log.frame(frame(0, true)) const first = log.report() first.frames[0].phase = 'changed' assert.equal(log.report().frames[0].phase, 'idle') assert.equal(log.report().environment.initialState.mode, 'Inspect') log.start(100, {}) log.gpuSample(0, 12) assert.equal(log.frameCount, 0) assert.equal(log.report().gpuSamples.length, 0) })