mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-07-26 19:04:38 +02:00
131 lines
5.2 KiB
TypeScript
131 lines
5.2 KiB
TypeScript
/*
|
|
* SPDX-FileCopyrightText: syuilo and misskey-project
|
|
* SPDX-License-Identifier: AGPL-3.0-only
|
|
*/
|
|
|
|
import { readFile } from 'node:fs/promises';
|
|
import { join } from 'node:path';
|
|
import { expect, test } from 'vitest';
|
|
import { pairedDeltaSummary } from 'diagnostics-shared/stats';
|
|
import { renderMemoryReportMarkdown } from '../src/report/markdown';
|
|
import type { MemoryReport } from '../src/types';
|
|
|
|
const fixturesDir = join(import.meta.dirname, 'fixtures');
|
|
|
|
async function loadFixture(name: string) {
|
|
return JSON.parse(await readFile(join(fixturesDir, `${name}.json`), 'utf8')) as MemoryReport;
|
|
}
|
|
|
|
function replacePssSamples(report: MemoryReport, values: number[]) {
|
|
const templates = structuredClone(report.samples);
|
|
report.samples = values.map((Pss, index) => {
|
|
const sample = structuredClone(templates[index % templates.length]);
|
|
sample.round = index + 1;
|
|
sample.phases.afterGc.memoryUsage.Pss = Pss;
|
|
const privateClean = sample.phases.afterGc.memoryUsage.Private_Clean;
|
|
sample.phases.afterGc.memoryUsage.Private_Dirty = Pss - privateClean;
|
|
return sample;
|
|
});
|
|
report.sampleCount = report.samples.length;
|
|
return report;
|
|
}
|
|
|
|
function findMetricRow(markdown: string, metric: string) {
|
|
const row = markdown.split('\n').find(line => line.startsWith(`| **${metric}**`));
|
|
if (row === undefined) throw new Error(`expected memory report to contain a ${metric} row`);
|
|
return row;
|
|
}
|
|
|
|
/**
|
|
* 出力をゴールデンファイルで固定する。
|
|
* 意図的に変更したときは `vitest -u` で更新し、__snapshots__ の差分もレビューすること。
|
|
*/
|
|
test('renders the backend memory report', async () => {
|
|
const markdown = renderMemoryReportMarkdown(await loadFixture('base'), await loadFixture('head'), {
|
|
baseHeapSnapshotUrl: 'https://example.invalid/base',
|
|
headHeapSnapshotUrl: 'https://example.invalid/head',
|
|
});
|
|
|
|
await expect(markdown).toMatchFileSnapshot('./__snapshots__/render-md.md');
|
|
});
|
|
|
|
test('throws when filtering leaves fewer than two heap snapshots per side', async () => {
|
|
const base = await loadFixture('base');
|
|
const head = await loadFixture('head');
|
|
for (const report of [base, head]) {
|
|
for (const sample of report.samples.slice(0, -1)) {
|
|
sample.phases.afterGc.heapSnapshot = null;
|
|
}
|
|
}
|
|
|
|
expect(() => renderMemoryReportMarkdown(base, head, {
|
|
baseHeapSnapshotUrl: 'https://example.invalid/base',
|
|
headHeapSnapshotUrl: 'https://example.invalid/head',
|
|
})).toThrow('At least two samples per side are required');
|
|
});
|
|
|
|
test('omits a paired-looking PSS delta that stays within observed noise', async () => {
|
|
const base = replacePssSamples(await loadFixture('base'), [290_000, 292_900, 295_800, 298_700, 301_600]);
|
|
const head = replacePssSamples(await loadFixture('head'), [292_900, 296_300, 298_700, 301_600, 290_000]);
|
|
|
|
expect(pairedDeltaSummary(
|
|
base.samples,
|
|
head.samples,
|
|
sample => sample.phases.afterGc.memoryUsage.Pss,
|
|
).median).toBe(2_900);
|
|
|
|
const markdown = renderMemoryReportMarkdown(base, head, {
|
|
baseHeapSnapshotUrl: 'https://example.invalid/base',
|
|
headHeapSnapshotUrl: 'https://example.invalid/head',
|
|
});
|
|
|
|
expect(markdown).not.toContain('| **PSS** |');
|
|
expect(markdown).toContain('<small><i>Only metrics showing significant changes are displayed.</i></small>');
|
|
expect(markdown).not.toContain('⚠️ **Warning**: Memory usage (PSS)');
|
|
});
|
|
|
|
test('keeps the convergence warning without suppressing table colour or the PSS warning', async () => {
|
|
const base = await loadFixture('base');
|
|
const head = await loadFixture('head');
|
|
base.samples[0].phases.afterGc.memoryStability.converged = false;
|
|
|
|
const markdown = renderMemoryReportMarkdown(base, head, {
|
|
baseHeapSnapshotUrl: 'https://example.invalid/base',
|
|
headHeapSnapshotUrl: 'https://example.invalid/head',
|
|
});
|
|
const memorySection = markdown.slice(0, markdown.indexOf('### V8 Heap Snapshot Statistics'));
|
|
|
|
expect(memorySection).not.toContain('inconclusive');
|
|
expect(findMetricRow(markdown, 'PSS')).toContain('\\color{orange}');
|
|
expect(markdown).toContain('⚠️ **Measurement warning**: 1 memory sample did not converge.');
|
|
expect(markdown).not.toContain('results are marked inconclusive');
|
|
expect(markdown).toContain('⚠️ **Warning**: Memory usage (PSS)');
|
|
});
|
|
|
|
test('throws for an undersampled memory comparison', async () => {
|
|
const base = await loadFixture('base');
|
|
const head = await loadFixture('head');
|
|
base.samples = base.samples.slice(0, 1);
|
|
head.samples = head.samples.slice(0, 1);
|
|
|
|
expect(() => renderMemoryReportMarkdown(base, head, {
|
|
baseHeapSnapshotUrl: 'https://example.invalid/base',
|
|
headHeapSnapshotUrl: 'https://example.invalid/head',
|
|
})).toThrow('At least two samples per side are required');
|
|
});
|
|
|
|
test('renders an unavailable percentage when the base median is zero', async () => {
|
|
const base = await loadFixture('base');
|
|
const head = await loadFixture('head');
|
|
for (const sample of base.samples) sample.phases.afterGc.memoryUsage.External = 0;
|
|
|
|
const markdown = renderMemoryReportMarkdown(base, head, {
|
|
baseHeapSnapshotUrl: 'https://example.invalid/base',
|
|
headHeapSnapshotUrl: 'https://example.invalid/head',
|
|
});
|
|
|
|
expect(findMetricRow(markdown, 'External')).toContain('<br>-');
|
|
expect(markdown).toContain('| Metric | @ Base | @ Head | Δ | MAD |');
|
|
expect(markdown).not.toContain('| Metric | @ Base | @ Head | Δ | MAD | Result |');
|
|
});
|