Files
Sanctification/card-harness/scripts/performance-recording.test.mjs

95 lines
3.8 KiB
JavaScript

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)
})