Auto-throttle visualizer under sustained Opus decode pressure; strip streaming investigation instrumentation
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* 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 <n> 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`);
|
||||
Reference in New Issue
Block a user