import test from 'brittle' import { readFile } from 'node:fs/promises' import { createContext, runInContext } from 'node:vm' import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' const __dirname = dirname(fileURLToPath(import.meta.url)) const helpersPath = join(__dirname, '../lib/baretop-ui-helpers.js') const composePath = join(__dirname, '../lib/baretop-compose.js') const largeProcPath = join(__dirname, 'fixtures/baretop-large-process-table.json') const largeNetPath = join(__dirname, 'fixtures/baretop-large-net-summary.json') function pctl(samples, p) { const sorted = samples.slice().sort((a, b) => a - b) if (!sorted.length) return 0 const idx = Math.max( 0, Math.min(sorted.length - 1, Math.floor((p / 100) * (sorted.length - 1))) ) return sorted[idx] } test('baretop perf baseline emits KPI envelope', async (t) => { const helpers = await readFile(helpersPath, 'utf8') const compose = await readFile(composePath, 'utf8') const proc = JSON.parse(await readFile(largeProcPath, 'utf8')) const net = JSON.parse(await readFile(largeNetPath, 'utf8')) const ctx = createContext({}) runInContext(helpers + '\n' + compose, ctx) const composeSamples = [] const emitSamples = [] const emitBytes = [] const dropRatio = [] const fetchSamples = [] for (let i = 0; i < 40; i++) { const t0 = Date.now() const rows = ctx.bareTopSortProcessRows( ctx.bareTopProcessRowsFromTable(proc), 'cpu', false, { sortWallMs: 1000 } ) const netLines = ctx.bareTopNetTabLines(net, 120, { maxLines: 220 }) const frame = ['baretop-perf-baseline', 'rows=' + rows.length].concat(netLines) const composeMs = Date.now() - t0 composeSamples.push(composeMs) fetchSamples.push(5 + (i % 4)) const e0 = Date.now() const emit = ctx.bareTopEmitFrame( {}, { write() {} }, frame.join('\r\n'), { prevLines: null, rows: 40, cols: 120, incrementalFrameDiff: false, useLineHash: false, fullscreenPanel: false, cup: (r, c) => `\\x1b[${r};${c}H` } ) emitSamples.push(Date.now() - e0) emitBytes.push(emit.wrotePatch ? emit.patchLen : frame.join('\r\n').length) dropRatio.push(0) } const kpi = { fetchMs: { p50: pctl(fetchSamples, 50), p95: pctl(fetchSamples, 95) }, composeMs: { p50: pctl(composeSamples, 50), p95: pctl(composeSamples, 95) }, emitMs: { p50: pctl(emitSamples, 50), p95: pctl(emitSamples, 95) }, emitBytes: { p50: pctl(emitBytes, 50), p95: pctl(emitBytes, 95) }, droppedRefreshPct: { p50: pctl(dropRatio, 50), p95: pctl(dropRatio, 95) } } t.ok(kpi.fetchMs.p50 >= 0) t.ok(kpi.composeMs.p95 >= kpi.composeMs.p50) t.ok(kpi.emitBytes.p95 >= kpi.emitBytes.p50) t.ok(kpi.droppedRefreshPct.p50 >= 0) t.ok(kpi.composeMs.p95 <= 30, 'compose p95 budget <= 30ms') t.ok(kpi.emitMs.p95 <= 15, 'emit p95 budget <= 15ms') t.ok(kpi.fetchMs.p95 <= 12, 'fetch p95 budget <= 12ms') })