609 lines
22 KiB
TypeScript
609 lines
22 KiB
TypeScript
/**
|
|
* Unit tests for the pure `compareReports` function in
|
|
* `e2e/scripts/benchmark-compare.ts`. Mirrors the pattern of
|
|
* `scripts/__tests__/memory-compare.test.ts` — the script lives under `e2e/`
|
|
* but its regression-diffing core is plain TypeScript with no Appium deps.
|
|
*
|
|
* Behaviour under test:
|
|
* - identical reports pass, no flags;
|
|
* - >15 pp% delta (in the regression direction) is flagged;
|
|
* - >15 tg% delta (in the regression direction) is flagged;
|
|
* - effective_backend mismatch is flagged independently of numeric deltas;
|
|
* - missing rows are surfaced via `missing_in_current` / `missing_in_baseline`.
|
|
*/
|
|
|
|
import {
|
|
BenchmarkMatrixReport,
|
|
BenchmarkRunReport,
|
|
compareReports,
|
|
} from '../../e2e/scripts/benchmark-compare';
|
|
|
|
function makeRun(
|
|
overrides: Partial<BenchmarkRunReport> = {},
|
|
): BenchmarkRunReport {
|
|
return {
|
|
model_id: 'qwen3-1.7b',
|
|
quant: 'q4_0',
|
|
requested_backend: 'gpu',
|
|
effective_backend: 'opencl',
|
|
pp_avg: 300,
|
|
tg_avg: 24,
|
|
wall_ms: 20_000,
|
|
peak_memory_mb: 2048,
|
|
status: 'ok',
|
|
timestamp: '2026-04-21T00:00:00Z',
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
function makeReport(
|
|
runs: BenchmarkRunReport[],
|
|
overrides: Partial<BenchmarkMatrixReport> = {},
|
|
): BenchmarkMatrixReport {
|
|
return {
|
|
version: '1.0',
|
|
device: 'S26 Ultra',
|
|
soc: 'SM8875',
|
|
commit: 'abc123',
|
|
llama_rn_version: '0.12.0',
|
|
platform: 'android',
|
|
os_version: '16',
|
|
timestamp: '2026-04-21T00:00:00Z',
|
|
preseeded: true,
|
|
runs,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
// Suppress the WARN line `compareReports` emits when one or both sides omit
|
|
// `bench` — every test in this file uses the legacy fixture (no bench field)
|
|
// except the protocol-mismatch suite below, which sets it explicitly.
|
|
const originalConsoleError = console.error;
|
|
beforeAll(() => {
|
|
jest.spyOn(console, 'error').mockImplementation((...args: any[]) => {
|
|
if (
|
|
typeof args[0] === 'string' &&
|
|
args[0].includes('protocol comparison disabled')
|
|
) {
|
|
return;
|
|
}
|
|
originalConsoleError(...args);
|
|
});
|
|
});
|
|
afterAll(() => {
|
|
(console.error as jest.Mock).mockRestore?.();
|
|
});
|
|
|
|
describe('compareReports (benchmark-compare)', () => {
|
|
// ---------------------------------------------------------------------------
|
|
// Happy path
|
|
// ---------------------------------------------------------------------------
|
|
|
|
it('passes with no flags when both reports are identical', () => {
|
|
const runs = [
|
|
makeRun({quant: 'q4_0'}),
|
|
makeRun({quant: 'q5_k_m', pp_avg: 280, tg_avg: 22}),
|
|
];
|
|
const result = compareReports(makeReport(runs), makeReport(runs));
|
|
|
|
expect(result.pass).toBe(true);
|
|
expect(result.rows).toHaveLength(2);
|
|
expect(result.rows.every(r => !r.flagged)).toBe(true);
|
|
expect(result.rows.every(r => r.flags.length === 0)).toBe(true);
|
|
expect(result.missing_in_current).toHaveLength(0);
|
|
expect(result.missing_in_baseline).toHaveLength(0);
|
|
expect(result.threshold_pct).toBe(15); // default
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Numeric-regression flags
|
|
// ---------------------------------------------------------------------------
|
|
|
|
it('flags a >15% pp regression', () => {
|
|
const baseline = makeReport([makeRun({pp_avg: 300, tg_avg: 24})]);
|
|
// -20% pp, tg unchanged
|
|
const current = makeReport([makeRun({pp_avg: 240, tg_avg: 24})]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.pass).toBe(false);
|
|
expect(result.rows[0].flagged).toBe(true);
|
|
expect(result.rows[0].delta_pp_pct).toBeCloseTo(-20, 1);
|
|
expect(result.rows[0].flags.some(f => f.startsWith('pp_regression'))).toBe(
|
|
true,
|
|
);
|
|
expect(result.rows[0].flags.some(f => f.startsWith('tg_regression'))).toBe(
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('flags a >15% tg regression', () => {
|
|
const baseline = makeReport([makeRun({pp_avg: 300, tg_avg: 24})]);
|
|
// pp unchanged, -25% tg
|
|
const current = makeReport([makeRun({pp_avg: 300, tg_avg: 18})]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.pass).toBe(false);
|
|
expect(result.rows[0].flagged).toBe(true);
|
|
expect(result.rows[0].delta_tg_pct).toBeCloseTo(-25, 1);
|
|
expect(result.rows[0].flags.some(f => f.startsWith('tg_regression'))).toBe(
|
|
true,
|
|
);
|
|
expect(result.rows[0].flags.some(f => f.startsWith('pp_regression'))).toBe(
|
|
false,
|
|
);
|
|
});
|
|
|
|
it('does NOT flag when both deltas are inside the threshold', () => {
|
|
const baseline = makeReport([makeRun({pp_avg: 300, tg_avg: 24})]);
|
|
// -10% pp, -10% tg; both inside 15%
|
|
const current = makeReport([makeRun({pp_avg: 270, tg_avg: 21.6})]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.pass).toBe(true);
|
|
expect(result.rows[0].flagged).toBe(false);
|
|
});
|
|
|
|
it('does NOT flag improvements (positive deltas past threshold)', () => {
|
|
// The compare script reports regressions only: a +20% pp jump is an
|
|
// improvement and must NOT be flagged.
|
|
const baseline = makeReport([makeRun({pp_avg: 300, tg_avg: 24})]);
|
|
const current = makeReport([makeRun({pp_avg: 360, tg_avg: 30})]); // +20%, +25%
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.pass).toBe(true);
|
|
expect(result.rows[0].flagged).toBe(false);
|
|
expect(result.rows[0].delta_pp_pct).toBeCloseTo(20, 1);
|
|
expect(result.rows[0].delta_tg_pct).toBeCloseTo(25, 1);
|
|
});
|
|
|
|
it('uses OR semantics: flags when pp OR tg regresses (not AND)', () => {
|
|
// Contrast with memory-compare which requires BOTH thresholds.
|
|
const baseline = makeReport([makeRun({pp_avg: 300, tg_avg: 24})]);
|
|
// -20% pp but +5% tg — one dim regresses, the other improves.
|
|
const current = makeReport([makeRun({pp_avg: 240, tg_avg: 25.2})]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.pass).toBe(false);
|
|
expect(result.rows[0].flagged).toBe(true);
|
|
});
|
|
|
|
it('respects a custom --pct threshold', () => {
|
|
const baseline = makeReport([makeRun({pp_avg: 300, tg_avg: 24})]);
|
|
const current = makeReport([makeRun({pp_avg: 276, tg_avg: 24})]); // -8% pp
|
|
|
|
// Default (15%): passes
|
|
expect(compareReports(baseline, current).pass).toBe(true);
|
|
|
|
// Custom 5%: fails
|
|
const strict = compareReports(baseline, current, {pct: 5});
|
|
expect(strict.pass).toBe(false);
|
|
expect(strict.threshold_pct).toBe(5);
|
|
expect(strict.rows[0].flags.some(f => f.startsWith('pp_regression'))).toBe(
|
|
true,
|
|
);
|
|
});
|
|
|
|
it('flags status regression when current row is failed (null metrics, ok->failed)', () => {
|
|
// A failed run typically has pp_avg=null, tg_avg=null. The numeric
|
|
// deltas remain null (un-comparable), but the status flip from `ok` to
|
|
// `failed` MUST be flagged so a regression is not silenced.
|
|
const baseline = makeReport([makeRun({pp_avg: 300, tg_avg: 24})]);
|
|
const current = makeReport([
|
|
makeRun({pp_avg: null, tg_avg: null, status: 'failed'}),
|
|
]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.rows[0].delta_pp_pct).toBeNull();
|
|
expect(result.rows[0].delta_tg_pct).toBeNull();
|
|
// No numeric-regression flag because both deltas are null...
|
|
expect(
|
|
result.rows[0].flags.filter(
|
|
f => f.startsWith('pp_regression') || f.startsWith('tg_regression'),
|
|
),
|
|
).toHaveLength(0);
|
|
// ...but the status flip is the regression signal.
|
|
expect(result.rows[0].flagged).toBe(true);
|
|
expect(result.rows[0].flags).toContain('status_regression(failed)');
|
|
// Mutual exclusivity invariant: status_regression and *_null_regression
|
|
// never both fire on the same row. The null-regression flags are gated
|
|
// on `curRow.status === 'ok'`, which cannot coexist with a status flip.
|
|
expect(result.rows[0].flags).not.toContain('pp_null_regression');
|
|
expect(result.rows[0].flags).not.toContain('tg_null_regression');
|
|
expect(result.pass).toBe(false);
|
|
});
|
|
|
|
it('does NOT flag when baseline failed and current ok (a previously-broken cell now works)', () => {
|
|
// The inverse of an `ok->failed` flip: if baseline was already broken
|
|
// (status:'failed' with null metrics) and the current run recovers
|
|
// (status:'ok' with valid metrics), the row has improved and must NOT
|
|
// be flagged. status_regression is one-way.
|
|
const baseline = makeReport([
|
|
makeRun({status: 'failed', pp_avg: null, tg_avg: null}),
|
|
]);
|
|
const current = makeReport([
|
|
makeRun({status: 'ok', pp_avg: 300, tg_avg: 24}),
|
|
]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.rows[0].flagged).toBe(false);
|
|
expect(result.rows[0].flags).toHaveLength(0);
|
|
expect(result.pass).toBe(true);
|
|
});
|
|
|
|
it('treats zero baseline as un-comparable (avoids division by zero)', () => {
|
|
const baseline = makeReport([makeRun({pp_avg: 0, tg_avg: 0})]);
|
|
const current = makeReport([makeRun({pp_avg: 100, tg_avg: 10})]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.rows[0].delta_pp_pct).toBeNull();
|
|
expect(result.rows[0].delta_tg_pct).toBeNull();
|
|
expect(result.rows[0].flagged).toBe(false);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// effective_backend mismatch (independent of numeric deltas)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
it('flags an effective_backend mismatch even when pp/tg are stable', () => {
|
|
// Same numbers, but GPU run silently fell back to CPU in current.
|
|
const baseline = makeReport([
|
|
makeRun({effective_backend: 'opencl', pp_avg: 300, tg_avg: 24}),
|
|
]);
|
|
const current = makeReport([
|
|
makeRun({
|
|
effective_backend: 'cpu+opencl-partial',
|
|
pp_avg: 300,
|
|
tg_avg: 24,
|
|
}),
|
|
]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.pass).toBe(false);
|
|
expect(result.rows[0].flagged).toBe(true);
|
|
expect(
|
|
result.rows[0].flags.some(f => f.startsWith('effective_backend:')),
|
|
).toBe(true);
|
|
expect(
|
|
result.rows[0].flags.find(f => f.startsWith('effective_backend:')),
|
|
).toBe('effective_backend:opencl->cpu+opencl-partial');
|
|
// Numeric regressions NOT present.
|
|
expect(
|
|
result.rows[0].flags.some(
|
|
f => f.startsWith('pp_regression') || f.startsWith('tg_regression'),
|
|
),
|
|
).toBe(false);
|
|
});
|
|
|
|
it('stacks effective_backend mismatch with numeric regressions', () => {
|
|
const baseline = makeReport([
|
|
makeRun({effective_backend: 'opencl', pp_avg: 300, tg_avg: 24}),
|
|
]);
|
|
const current = makeReport([
|
|
makeRun({effective_backend: 'cpu', pp_avg: 100, tg_avg: 10}),
|
|
]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
expect(result.rows[0].flags.length).toBeGreaterThanOrEqual(3);
|
|
expect(result.rows[0].flags).toEqual(
|
|
expect.arrayContaining([
|
|
expect.stringMatching(/^pp_regression/),
|
|
expect.stringMatching(/^tg_regression/),
|
|
'effective_backend:opencl->cpu',
|
|
]),
|
|
);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Row-level set semantics
|
|
// ---------------------------------------------------------------------------
|
|
|
|
it('surfaces rows missing in current under missing_in_current AND fails the comparison (B3)', () => {
|
|
// A truncated current report (missing rows the baseline has) is a
|
|
// regression-class failure: the comparison cannot prove no regression
|
|
// for rows the current run did not exercise. `pass` must be false.
|
|
const baseline = makeReport([
|
|
makeRun({quant: 'q4_0'}),
|
|
makeRun({quant: 'q5_k_m', pp_avg: 280, tg_avg: 22}),
|
|
]);
|
|
const current = makeReport([makeRun({quant: 'q4_0'})]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
// 4-tuple key format with the defensive 'app-default' fallback for
|
|
// legacy v1.0 runs that lack settings_fingerprint (WHAT 4g.1).
|
|
expect(result.missing_in_current).toEqual([
|
|
'qwen3-1.7b::q5_k_m::gpu::app-default',
|
|
]);
|
|
expect(result.missing_in_baseline).toEqual([]);
|
|
expect(result.rows).toHaveLength(1);
|
|
expect(result.pass).toBe(false);
|
|
});
|
|
|
|
it('surfaces rows missing in current AND passes when intersecting rows are clean and no rows are missing (control)', () => {
|
|
// Sanity: make sure the missing-row gate does not over-fire when the
|
|
// report is fully covered. Both sides have q4_0 only.
|
|
const baseline = makeReport([makeRun({quant: 'q4_0'})]);
|
|
const current = makeReport([makeRun({quant: 'q4_0'})]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.missing_in_current).toEqual([]);
|
|
expect(result.pass).toBe(true);
|
|
});
|
|
|
|
it('surfaces new rows under missing_in_baseline', () => {
|
|
const baseline = makeReport([makeRun({quant: 'q4_0'})]);
|
|
const current = makeReport([
|
|
makeRun({quant: 'q4_0'}),
|
|
makeRun({quant: 'q8_0', pp_avg: 200, tg_avg: 16}),
|
|
]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.missing_in_baseline).toEqual([
|
|
'qwen3-1.7b::q8_0::gpu::app-default',
|
|
]);
|
|
expect(result.missing_in_current).toEqual([]);
|
|
});
|
|
|
|
it('keys rows by model::quant::requested_backend::fingerprint (4-tuple per WHAT 4g.1)', () => {
|
|
const runs = [
|
|
makeRun({requested_backend: 'cpu', effective_backend: 'cpu'}),
|
|
makeRun({requested_backend: 'gpu', effective_backend: 'opencl'}),
|
|
];
|
|
const result = compareReports(makeReport(runs), makeReport(runs));
|
|
|
|
expect(result.rows).toHaveLength(2);
|
|
const keys = result.rows.map(r => r.key).sort();
|
|
// Legacy v1.0 inputs lack fingerprint; rowKey falls back to
|
|
// 'app-default' (defensive default for direct-baseline use).
|
|
expect(keys).toEqual([
|
|
'qwen3-1.7b::q4_0::cpu::app-default',
|
|
'qwen3-1.7b::q4_0::gpu::app-default',
|
|
]);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Metadata passthrough
|
|
// ---------------------------------------------------------------------------
|
|
|
|
it('copies baseline/current commits and devices into the result', () => {
|
|
const baseline = makeReport([makeRun()], {
|
|
commit: 'base-sha',
|
|
device: 'BaselineDevice',
|
|
});
|
|
const current = makeReport([makeRun()], {
|
|
commit: 'cur-sha',
|
|
device: 'CurrentDevice',
|
|
});
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.baseline_commit).toBe('base-sha');
|
|
expect(result.current_commit).toBe('cur-sha');
|
|
expect(result.baseline_device).toBe('BaselineDevice');
|
|
expect(result.current_device).toBe('CurrentDevice');
|
|
});
|
|
|
|
it('round-trips 2-dp precision on the delta percentages', () => {
|
|
// 300 -> 289.88 is a -3.3733...% delta; rounded to 2 dp is -3.37.
|
|
const baseline = makeReport([makeRun({pp_avg: 300, tg_avg: 24})]);
|
|
const current = makeReport([makeRun({pp_avg: 289.88, tg_avg: 24})]);
|
|
const result = compareReports(baseline, current);
|
|
expect(result.rows[0].delta_pp_pct).toBe(-3.37);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// C1 defense-in-depth: null-metric regression flags. The screen-side
|
|
// invariant (status:'ok' rows always carry non-null pp/tg) is the primary
|
|
// guard; these flags fire only if that invariant ever regresses.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
it('flags pp_null_regression when both rows claim ok but current pp_avg is null', () => {
|
|
const baseline = makeReport([
|
|
makeRun({status: 'ok', pp_avg: 200, tg_avg: 20}),
|
|
]);
|
|
const current = makeReport([
|
|
makeRun({status: 'ok', pp_avg: null, tg_avg: 20}),
|
|
]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.rows[0].flagged).toBe(true);
|
|
expect(result.rows[0].flags).toContain('pp_null_regression');
|
|
expect(result.rows[0].flags).not.toContain('tg_null_regression');
|
|
expect(result.pass).toBe(false);
|
|
});
|
|
|
|
it('flags tg_null_regression when both rows claim ok but current tg_avg is null', () => {
|
|
const baseline = makeReport([
|
|
makeRun({status: 'ok', pp_avg: 200, tg_avg: 20}),
|
|
]);
|
|
const current = makeReport([
|
|
makeRun({status: 'ok', pp_avg: 200, tg_avg: null}),
|
|
]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.rows[0].flagged).toBe(true);
|
|
expect(result.rows[0].flags).toContain('tg_null_regression');
|
|
expect(result.rows[0].flags).not.toContain('pp_null_regression');
|
|
expect(result.pass).toBe(false);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// C2: bench protocol mismatch (pp/tg/pl/nr drift between baseline and
|
|
// current). When detected, `pass` is false and the CLI exits 2.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
it('passes when both reports have matching bench params', () => {
|
|
const bench = {pp: 512, tg: 128, pl: 1, nr: 3};
|
|
const baseline = makeReport([makeRun()], {bench});
|
|
const current = makeReport([makeRun()], {bench});
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.pass).toBe(true);
|
|
expect(result.bench_protocol_mismatch).toBeUndefined();
|
|
});
|
|
|
|
it('flags bench_protocol_mismatch when nr differs (1 vs 3)', () => {
|
|
const baseline = makeReport([makeRun()], {
|
|
bench: {pp: 512, tg: 128, pl: 1, nr: 1},
|
|
});
|
|
const current = makeReport([makeRun()], {
|
|
bench: {pp: 512, tg: 128, pl: 1, nr: 3},
|
|
});
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.pass).toBe(false);
|
|
expect(result.bench_protocol_mismatch).toEqual({
|
|
baseline: {pp: 512, tg: 128, pl: 1, nr: 1},
|
|
current: {pp: 512, tg: 128, pl: 1, nr: 3},
|
|
});
|
|
});
|
|
|
|
it('flags bench_protocol_mismatch when pp differs', () => {
|
|
const baseline = makeReport([makeRun()], {
|
|
bench: {pp: 256, tg: 128, pl: 1, nr: 3},
|
|
});
|
|
const current = makeReport([makeRun()], {
|
|
bench: {pp: 512, tg: 128, pl: 1, nr: 3},
|
|
});
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.pass).toBe(false);
|
|
expect(result.bench_protocol_mismatch?.baseline.pp).toBe(256);
|
|
expect(result.bench_protocol_mismatch?.current.pp).toBe(512);
|
|
});
|
|
|
|
it('gracefully degrades (no mismatch, pass=true) when baseline.bench is undefined', () => {
|
|
// The committed POCO baseline pre-dates the unification; it has no
|
|
// `bench` field. The compare script must keep loading it (with a
|
|
// stderr warning) rather than failing closed.
|
|
const baseline = makeReport([makeRun()]); // no bench
|
|
const current = makeReport([makeRun()], {
|
|
bench: {pp: 512, tg: 128, pl: 1, nr: 3},
|
|
});
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.bench_protocol_mismatch).toBeUndefined();
|
|
expect(result.pass).toBe(true);
|
|
});
|
|
|
|
it('gracefully degrades when current.bench is undefined', () => {
|
|
const baseline = makeReport([makeRun()], {
|
|
bench: {pp: 512, tg: 128, pl: 1, nr: 3},
|
|
});
|
|
const current = makeReport([makeRun()]); // no bench
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.bench_protocol_mismatch).toBeUndefined();
|
|
expect(result.pass).toBe(true);
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// v1.1 — settings_fingerprint axis + Hexagon effective_backend arms
|
|
// (WHAT 4g.1, 4g.4)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
it('keys rows by 4-tuple including settings_fingerprint when both sides carry v1.1 fields', () => {
|
|
const runs = [
|
|
makeRun({
|
|
settings_fingerprint: 'cache_type_k=f16;...',
|
|
} as Partial<BenchmarkRunReport>),
|
|
makeRun({
|
|
settings_fingerprint: 'cache_type_k=q8_0;...',
|
|
} as Partial<BenchmarkRunReport>),
|
|
];
|
|
const result = compareReports(makeReport(runs), makeReport(runs));
|
|
expect(result.rows).toHaveLength(2);
|
|
const keys = result.rows.map(r => r.key).sort();
|
|
expect(keys).toEqual([
|
|
'qwen3-1.7b::q4_0::gpu::cache_type_k=f16;...',
|
|
'qwen3-1.7b::q4_0::gpu::cache_type_k=q8_0;...',
|
|
]);
|
|
});
|
|
|
|
it('reports a baseline app-default row missing in a current sweep run as missing_in_current (WHAT 4g.3)', () => {
|
|
// Operator-facing signal: current run swept cache_type_k but did
|
|
// not also include the app-default cell.
|
|
const baseline = makeReport([
|
|
makeRun({
|
|
settings_fingerprint: 'app-default',
|
|
} as Partial<BenchmarkRunReport>),
|
|
]);
|
|
const current = makeReport([
|
|
makeRun({
|
|
settings_fingerprint: 'cache_type_k=q8_0;...',
|
|
} as Partial<BenchmarkRunReport>),
|
|
]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.missing_in_current).toEqual([
|
|
'qwen3-1.7b::q4_0::gpu::app-default',
|
|
]);
|
|
expect(result.missing_in_baseline).toEqual([
|
|
'qwen3-1.7b::q4_0::gpu::cache_type_k=q8_0;...',
|
|
]);
|
|
expect(result.pass).toBe(false);
|
|
});
|
|
|
|
it('flags effective_backend mismatch on hexagon arms (WHAT 4g.4)', () => {
|
|
// Same numeric performance, but the run silently fell back from
|
|
// hexagon to cpu+hexagon-partial. Existing string-equality check
|
|
// covers the new arm without code changes.
|
|
const baseline = makeReport([
|
|
makeRun({
|
|
effective_backend: 'hexagon',
|
|
requested_backend: 'hexagon',
|
|
} as Partial<BenchmarkRunReport>),
|
|
]);
|
|
const current = makeReport([
|
|
makeRun({
|
|
effective_backend: 'cpu+hexagon-partial',
|
|
requested_backend: 'hexagon',
|
|
} as Partial<BenchmarkRunReport>),
|
|
]);
|
|
|
|
const result = compareReports(baseline, current);
|
|
|
|
expect(result.pass).toBe(false);
|
|
expect(result.rows[0].flags).toContain(
|
|
'effective_backend:hexagon->cpu+hexagon-partial',
|
|
);
|
|
});
|
|
|
|
it('passes when hexagon arms match (regression of cpu+hexagon-partial -> cpu+hexagon-partial is not a flag)', () => {
|
|
const baseline = makeReport([
|
|
makeRun({
|
|
effective_backend: 'cpu+hexagon-partial',
|
|
requested_backend: 'hexagon',
|
|
} as Partial<BenchmarkRunReport>),
|
|
]);
|
|
const current = makeReport([
|
|
makeRun({
|
|
effective_backend: 'cpu+hexagon-partial',
|
|
requested_backend: 'hexagon',
|
|
} as Partial<BenchmarkRunReport>),
|
|
]);
|
|
const result = compareReports(baseline, current);
|
|
expect(result.pass).toBe(true);
|
|
expect(result.rows[0].flagged).toBe(false);
|
|
});
|
|
});
|