mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-09-12 17:16:20 -07:00
fix(anime): cap outcome polling and upstream stalls by wall clock
- watchPlaybackOutcome now tracks a real deadline so slow property reads eat the timeout budget instead of extending it, and a zero probe interval still terminates - stream strip proxy aborts upstream GETs that go silent for 15s instead of hanging mpv on that segment forever - anime browser runtime drops a stream proxy that finishes starting after its bridge already died, instead of leaking a listener pointed at nothing
This commit is contained in:
@@ -7,12 +7,25 @@ type Harness = {
|
||||
listenerCount: () => number;
|
||||
setProperty: (name: string, value: unknown) => void;
|
||||
failProperty: (name: string) => void;
|
||||
/** Virtual milliseconds burned so far, by sleeps and by property reads. */
|
||||
elapsed: () => number;
|
||||
};
|
||||
|
||||
function createHarness(overrides?: { timeoutMs?: number; probeIntervalMs?: number }) {
|
||||
/**
|
||||
* The clock is virtual and only moves when the code under test sleeps (or,
|
||||
* with `readCostMs`, when it reads a property), so timeout behaviour is
|
||||
* asserted without any real waiting.
|
||||
*/
|
||||
function createHarness(overrides?: {
|
||||
timeoutMs?: number;
|
||||
probeIntervalMs?: number;
|
||||
readCostMs?: number;
|
||||
}) {
|
||||
const listeners = new Set<(event: PlaybackEndFileEvent) => void>();
|
||||
const properties = new Map<string, unknown>();
|
||||
const failing = new Set<string>();
|
||||
const readCostMs = overrides?.readCostMs ?? 0;
|
||||
let clock = 0;
|
||||
|
||||
const watch = watchPlaybackOutcome({
|
||||
onEndFile: (listener) => {
|
||||
@@ -20,10 +33,14 @@ function createHarness(overrides?: { timeoutMs?: number; probeIntervalMs?: numbe
|
||||
return () => listeners.delete(listener);
|
||||
},
|
||||
readProperty: async (name) => {
|
||||
clock += readCostMs;
|
||||
if (failing.has(name)) throw new Error(`Failed to read MPV property '${name}'`);
|
||||
return properties.get(name);
|
||||
},
|
||||
wait: async () => {},
|
||||
wait: async (ms) => {
|
||||
clock += ms;
|
||||
},
|
||||
now: () => clock,
|
||||
timeoutMs: overrides?.timeoutMs ?? 1000,
|
||||
probeIntervalMs: overrides?.probeIntervalMs ?? 100,
|
||||
});
|
||||
@@ -35,6 +52,7 @@ function createHarness(overrides?: { timeoutMs?: number; probeIntervalMs?: numbe
|
||||
listenerCount: () => listeners.size,
|
||||
setProperty: (name, value) => properties.set(name, value),
|
||||
failProperty: (name) => failing.add(name),
|
||||
elapsed: () => clock,
|
||||
};
|
||||
return { watch, harness };
|
||||
}
|
||||
@@ -68,10 +86,32 @@ test('ignores the end-file fired for the file being replaced', async () => {
|
||||
});
|
||||
|
||||
test('times out with a failure when nothing ever starts', async () => {
|
||||
const { watch } = createHarness({ timeoutMs: 300, probeIntervalMs: 100 });
|
||||
const { watch, harness } = createHarness({ timeoutMs: 300, probeIntervalMs: 100 });
|
||||
const outcome = await watch.wait();
|
||||
assert.equal(outcome.ok, false);
|
||||
assert.ok(!outcome.ok && outcome.error.length > 0);
|
||||
assert.equal(harness.elapsed(), 300);
|
||||
watch.dispose();
|
||||
});
|
||||
|
||||
test('slow property reads eat the budget instead of extending it', async () => {
|
||||
const { watch, harness } = createHarness({
|
||||
timeoutMs: 300,
|
||||
probeIntervalMs: 100,
|
||||
readCostMs: 250,
|
||||
});
|
||||
const outcome = await watch.wait();
|
||||
assert.equal(outcome.ok, false);
|
||||
// Two probes: 250 + 50 (the sleep clamped to what was left) then 250 again.
|
||||
assert.ok(harness.elapsed() >= 300, 'gave up before the timeout');
|
||||
assert.ok(harness.elapsed() < 900, 'read delays stretched the timeout');
|
||||
watch.dispose();
|
||||
});
|
||||
|
||||
test('a zero probe interval still terminates at the deadline', async () => {
|
||||
const { watch } = createHarness({ timeoutMs: 200, probeIntervalMs: 0, readCostMs: 50 });
|
||||
const outcome = await watch.wait();
|
||||
assert.equal(outcome.ok, false);
|
||||
watch.dispose();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user