89 lines
5.9 KiB
JavaScript
89 lines
5.9 KiB
JavaScript
const test = require('node:test'), assert = require('node:assert/strict');
|
|
const { create } = require('../packages/level-resources/perf');
|
|
|
|
test('diagnostics preserve values and errors, aggregate hot paths and bound retained output', async () => {
|
|
let time = 0; const lines = [];
|
|
const perf = create({ enabled: true, now: () => time, output: line => lines.push(JSON.parse(line.slice('[LevelPerf] '.length))) });
|
|
perf.session({ version: 'test' });
|
|
const end = perf.begin('level.load', { id: 222 });
|
|
const sentinel = Error('sentinel');
|
|
for (let i = 0; i < 2000; i++) assert.equal(perf.measure('hash', () => { time += 2; return 7; }, 100), 7);
|
|
assert.throws(() => perf.measure('failed', () => { throw sentinel; }), e => e === sentinel);
|
|
await assert.rejects(perf.measureAsync('async.failed', () => Promise.reject(sentinel)), e => e === sentinel);
|
|
assert.equal(await perf.measureAsync('read', async () => 'bytes'), 'bytes');
|
|
end({ status: 'ok' }); end(); perf.flush();
|
|
assert(lines.length < 10, 'hot paths must not print one log per resource');
|
|
const summary = lines.find(l => l.event === 'summary' && l.metrics.hash);
|
|
assert.equal(summary.metrics.hash.count, 2000); assert.equal(summary.metrics.hash.totalMs, 4000);
|
|
assert.equal(summary.metrics.hash.maxMs, 2); assert.equal(summary.metrics.hash.bytes, 200000);
|
|
assert.equal(summary.metrics.failed.errors, 1);
|
|
assert.equal(lines.filter(l => l.event === 'end').length, 1);
|
|
assert.equal(lines.find(l => l.event === 'begin').spanId, lines.find(l => l.event === 'end').spanId);
|
|
for (let i = 0; i < 500; i++) perf.emit('sample');
|
|
assert.equal(perf.dump().split('\n').length, 400);
|
|
perf.setEnabled(false); const count = lines.length;
|
|
perf.emit('hidden'); perf.measure('hash', () => 1); perf.flush(); assert.equal(lines.length, count);
|
|
});
|
|
|
|
test('event-loop lag excludes background time and logging failures do not affect tasks', () => {
|
|
let time = 0; const lines = [];
|
|
const perf = create({ enabled: true, now: () => time, output: line => lines.push(line) });
|
|
time = 500; perf.tick(); assert.equal(lines.filter(l => l.includes('eventLoopLag')).length, 1);
|
|
perf.setActive(false); time += 10000; perf.tick(); perf.setActive(true); time += 250; perf.tick();
|
|
assert.equal(lines.filter(l => l.includes('eventLoopLag')).length, 1);
|
|
const broken = create({ enabled: true, output() { throw Error('console unavailable'); } });
|
|
assert.equal(broken.measure('ok', () => 42), 42); broken.begin('ok')();
|
|
});
|
|
|
|
test('image details aggregate thousands of loads with bounded slow and failure samples', () => {
|
|
let time = 0; const lines = [];
|
|
const perf = create({ enabled: true, now: () => time, output: line => lines.push(JSON.parse(line.slice('[LevelPerf] '.length))) });
|
|
const group = perf.begin('images.group', { kind: 'block', count: 1000 });
|
|
for (let i = 0; i < 1000; i++) {
|
|
for (const stage of ['frame', 'engineLoad', 'createFrame', 'reuse']) {
|
|
const end = perf.begin('images.' + stage, { path: 'image-' + i }); time++; end({ status: 'ok' });
|
|
}
|
|
const end = perf.begin('images.asset', { path: 'image-' + i, kind: 'png' }); time += 10 + i;
|
|
end({ status: i % 2 ? 'ok' : 'error', source: '本地缓存', lookupMs: 2, cachePrepareMs: 0, engineLoadMs: 8 + i, validateMs: 0 }); end();
|
|
const download = perf.begin('images.download', { path: 'image-' + i, queueMs: 5 }); time += 20;
|
|
download({ status: 'error', networkMs: 20, installMs: 0 });
|
|
}
|
|
group({ status: 'ok' }); perf.flush();
|
|
assert.equal(lines.length, 4, 'enabled + group begin/end + one summary, independent of image count');
|
|
const summary = lines.find(row => row.event === 'summary');
|
|
assert.equal(summary.metrics['images.asset'].count, 1000);
|
|
assert.equal(summary.metrics['images.asset'].errors, 500);
|
|
assert.equal(summary.metrics['images.asset.source.local'].count, 1000);
|
|
assert.equal(summary.metrics['images.asset.lookupMs'].totalMs, 2000);
|
|
assert.equal(summary.metrics['images.download.queueMs'].totalMs, 5000);
|
|
assert.equal(summary.metrics['images.download'].errors, 1000);
|
|
assert.equal(summary.images.assets.length, 3); assert.equal(summary.images.assets[0].path, 'image-999');
|
|
assert.equal(summary.images.downloads.length, 3); assert.equal(summary.images.failures.length, 3);
|
|
assert.equal(lines.find(row => row.event === 'end').kind, 'block');
|
|
const count = lines.length; perf.flush(); assert.equal(lines.length, count, 'samples reset after flush');
|
|
});
|
|
|
|
test('silent image stages remain observable while pending and disabled diagnostics stay silent', () => {
|
|
let time = 0; const lines = [];
|
|
const perf = create({ enabled: true, now: () => time, output: line => lines.push(JSON.parse(line.slice('[LevelPerf] '.length))) });
|
|
const end = perf.begin('images.engineLoad', { path: 'door' }); time = 3000; perf.flush();
|
|
const summary = lines.find(row => row.event === 'summary');
|
|
assert.equal(summary.pendingCount, 1); assert.equal(summary.pending[0].stage, 'images.engineLoad');
|
|
perf.setEnabled(false); const count = lines.length; end({ status: 'error' }); perf.flush();
|
|
assert.equal(lines.length, count);
|
|
});
|
|
|
|
test('scene resource diagnostics aggregate thousands of dependencies into three slow samples', () => {
|
|
let time = 0; const rows = [];
|
|
const perf = create({ enabled: true, now: () => time, output: line => rows.push(JSON.parse(line.slice(12))) });
|
|
for (let i = 0; i < 1000; i++) {
|
|
const end = perf.begin('scene.resource.parse', { resource: 'asset-' + i, requestId: 9 });
|
|
time += i; end({ status: 'ok' });
|
|
}
|
|
perf.flush(); assert.equal(rows.length, 2);
|
|
assert.equal(rows[1].metrics['scene.resource.parse'].count, 1000);
|
|
assert.equal(rows[1].scene['scene.resource.parse'].length, 3);
|
|
assert.equal(rows[1].scene['scene.resource.parse'][0].resource, 'asset-999');
|
|
perf.flush(); assert.equal(rows.length, 2);
|
|
});
|