/** * decodePressure hysteresis tests — the Part-1 auto-throttle signal logic. * * These cover the four named behaviours that make the visualizer-throttle safe: it engages only on * SUSTAINED pressure, releases only after SUSTAINED recovery, never flaps on/off, and is a complete * no-op when decode is healthy. The clock is injected so every transition is asserted at an exact * timestamp — no real timers, fully deterministic. * * Run (no test runner configured; Node 22+ strips TS types natively — see OpusStreamDecoder.test.ts): * dotnet build DeepDrftPublic/DeepDrftPublic.csproj * cp DeepDrftPublic/Interop/audio/decodePressure.test.ts DeepDrftPublic/wwwroot/js/audio/ * node DeepDrftPublic/wwwroot/js/audio/decodePressure.test.ts * * A thrown error / non-zero exit signals failure; "ALL TESTS PASSED" signals success. */ import { DecodePressureSignal, ENGAGE_EVENTS, ENGAGE_WINDOW_MS, RELEASE_QUIET_MS, MIN_ENGAGED_MS, } from './decodePressure.js'; // --- tiny inline harness (no dependencies) --------------------------------------------------- let passed = 0; const failures: string[] = []; function test(name: string, fn: () => void): void { try { fn(); passed++; } catch (e) { failures.push(`FAIL: ${name}\n ${(e as Error).message}`); } } function assertTrue(actual: boolean, msg?: string): void { if (actual !== true) throw new Error(`${msg ?? 'assertTrue'}: expected true, got ${String(actual)}`); } function assertFalse(actual: boolean, msg?: string): void { if (actual !== false) throw new Error(`${msg ?? 'assertFalse'}: expected false, got ${String(actual)}`); } /** A signal driven by a hand-advanced clock, so every transition is asserted at an exact time. */ function makeSignal() { let now = 1000; // start at a non-zero base so "no prior stress" (-Infinity) is unambiguous const sig = new DecodePressureSignal(() => now); return { sig, at(ms: number) { now = ms; }, advance(ms: number) { now += ms; }, now() { return now; }, }; } // --- no engage when healthy ------------------------------------------------------------------ test('healthy stream never engages (no reports at all)', () => { const { sig, advance } = makeSignal(); for (let i = 0; i < 10; i++) { advance(1000); assertFalse(sig.isUnderPressure(), 'healthy must never be under pressure'); } }); test('a single transient stress does not engage', () => { const { sig, advance } = makeSignal(); sig.report(); assertFalse(sig.isUnderPressure(), 'one event is not sustained'); advance(500); assertFalse(sig.isUnderPressure(), 'still not sustained'); }); test('fewer than ENGAGE_EVENTS within the window does not engage', () => { const { sig, advance } = makeSignal(); for (let i = 0; i < ENGAGE_EVENTS - 1; i++) { sig.report(); advance(10); } assertFalse(sig.isUnderPressure(), 'one short of the threshold must not engage'); }); test('stress spread wider than the window never accumulates enough to engage', () => { const { sig, advance } = makeSignal(); // One report per full window: the prune drops each before the next, so the live count never // reaches ENGAGE_EVENTS even after many reports. for (let i = 0; i < ENGAGE_EVENTS * 3; i++) { sig.report(); assertFalse(sig.isUnderPressure(), 'spread-out stress is not sustained'); advance(ENGAGE_WINDOW_MS); } }); // --- engages on sustained pressure ----------------------------------------------------------- test('ENGAGE_EVENTS within the window engages', () => { const { sig, advance } = makeSignal(); for (let i = 0; i < ENGAGE_EVENTS; i++) { sig.report(); advance(10); // all comfortably inside ENGAGE_WINDOW_MS } assertTrue(sig.isUnderPressure(), 'sustained pressure must engage'); }); // --- releases after recovery ----------------------------------------------------------------- test('releases after sustained quiet past the min dwell', () => { const { sig, advance } = makeSignal(); for (let i = 0; i < ENGAGE_EVENTS; i++) { sig.report(); advance(10); } assertTrue(sig.isUnderPressure(), 'engaged'); // Quiet long enough to satisfy BOTH the min engaged dwell and the release-quiet window. advance(Math.max(MIN_ENGAGED_MS, RELEASE_QUIET_MS) + 1); assertFalse(sig.isUnderPressure(), 'sustained recovery must release'); }); test('re-engages after a release when a fresh burst arrives', () => { const { sig, advance } = makeSignal(); for (let i = 0; i < ENGAGE_EVENTS; i++) { sig.report(); advance(10); } assertTrue(sig.isUnderPressure(), 'engaged first time'); advance(Math.max(MIN_ENGAGED_MS, RELEASE_QUIET_MS) + 1); assertFalse(sig.isUnderPressure(), 'released'); for (let i = 0; i < ENGAGE_EVENTS; i++) { sig.report(); advance(10); } assertTrue(sig.isUnderPressure(), 'a fresh sustained burst re-engages'); }); // --- no flap --------------------------------------------------------------------------------- test('stays engaged during a brief quiet shorter than the release window', () => { const { sig, advance } = makeSignal(); for (let i = 0; i < ENGAGE_EVENTS; i++) { sig.report(); advance(10); } assertTrue(sig.isUnderPressure(), 'engaged'); // A gap shorter than RELEASE_QUIET_MS must NOT release — that is the anti-flap guarantee. advance(RELEASE_QUIET_MS - 100); assertTrue(sig.isUnderPressure(), 'a brief quiet must not drop the throttle'); }); test('continued stress holds the throttle engaged indefinitely', () => { const { sig, advance } = makeSignal(); for (let i = 0; i < ENGAGE_EVENTS; i++) { sig.report(); advance(10); } assertTrue(sig.isUnderPressure(), 'engaged'); // Keep reporting at a cadence under the release window; it must never release. for (let i = 0; i < 20; i++) { advance(RELEASE_QUIET_MS - 100); sig.report(); assertTrue(sig.isUnderPressure(), 'ongoing stress keeps it engaged'); } }); // --- report ---------------------------------------------------------------------------------- if (failures.length > 0) { console.error(failures.join('\n')); throw new Error(`${failures.length} test(s) failed, ${passed} passed`); } console.log(`ALL ${passed} TESTS PASSED`);