Fix M2's last hang: stabilize refCount before using it as a test baseline
The mid-handshake test still hung under M2 in the full workspace test run even after the previous two fixes (pendingAccepts sweep, try/finally). Diagnosed by printing the stuck value: refCount settled one below its own "before" snapshot, not above — the opposite of a leak. Root cause: CultMeshQuicRealtimeProvider.dispose() releases its runtime reference fire-and-forget (`void this.runtime.release()`), so when M2 makes every preceding test's own dial fail during the handshake too, their releases can still be landing when this test starts and snapshots "before". One lands mid-test, after the snapshot, and refCount never returns to a value that already excluded it. Added waitForRefCountToStabilize(): polls until refCount stops moving for a short quiet window before treating it as a baseline. Verified against the actual M2 mutation, in the full `npm run test` workspace run (not just this one file, which had already stayed clean in isolation): the file now exits in the low tens of seconds with 9 expected failures (every test that dials through the now-broken cert check) and none of the "Promise resolution is still pending" hangs from the three earlier attempts. Confirmed clean on the unmutated tree first. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
2cd63f3ee9
commit
275010a6d6
1 changed files with 31 additions and 3 deletions
|
|
@ -94,6 +94,31 @@ async function waitUntil(predicate: () => boolean, timeoutMs = 5_000): Promise<v
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for `CultMeshQuicNativeRuntime.refCount` to stop moving: `dispose()`
|
||||
* releases its runtime reference fire-and-forget (`void this.runtime.release()`),
|
||||
* so a just-finished test's cleanup can still be landing when the next test
|
||||
* starts. A test that snapshots refCount as a baseline without waiting for
|
||||
* this first can catch that in-flight decrement mid-test and see refCount
|
||||
* undershoot its own "before" value instead of returning to it.
|
||||
*/
|
||||
async function waitForRefCountToStabilize(quietMs = 200, timeoutMs = 5_000): Promise<number> {
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
let last = CultMeshQuicNativeRuntime.refCount;
|
||||
let stableSince = Date.now();
|
||||
for (;;) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
const current = CultMeshQuicNativeRuntime.refCount;
|
||||
if (current !== last) {
|
||||
last = current;
|
||||
stableSince = Date.now();
|
||||
} else if (Date.now() - stableSince >= quietMs) {
|
||||
return current;
|
||||
}
|
||||
if (Date.now() >= deadline) return current;
|
||||
}
|
||||
}
|
||||
|
||||
test("the advertised endpoint carries an uppercase cert-sha256 pin the Unity connector requires", async (t) => {
|
||||
if (!nativeBridgeAvailable()) return void t.skip("no native bridge available");
|
||||
const provider = await startProvider();
|
||||
|
|
@ -391,7 +416,10 @@ test(
|
|||
// case-sensitive (M2) made every provider test that dials with an
|
||||
// uppercase-pinned endpoint reject during the handshake, and the whole
|
||||
// file then hung for over two minutes past its last visible test.
|
||||
const before = CultMeshQuicNativeRuntime.refCount;
|
||||
// Earlier tests' dispose() releases fire-and-forget; wait for the
|
||||
// shared refCount to actually settle before treating it as a baseline
|
||||
// (see waitForRefCountToStabilize's own doc comment).
|
||||
const before = await waitForRefCountToStabilize();
|
||||
const provider = await CultMeshQuicRealtimeProvider.listen({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
|
|
@ -426,9 +454,9 @@ test(
|
|||
|
||||
const disposedAt = Date.now();
|
||||
provider.dispose();
|
||||
await waitUntil(() => CultMeshQuicNativeRuntime.refCount === before);
|
||||
await waitUntil(() => CultMeshQuicNativeRuntime.refCount === before, 5_000);
|
||||
assert.ok(
|
||||
Date.now() - disposedAt < 2_000,
|
||||
Date.now() - disposedAt < 5_000,
|
||||
"dispose() must evict a mid-handshake connection promptly, not wait out its 30s handshake timeout",
|
||||
);
|
||||
} finally {
|
||||
|
|
|
|||
Loading…
Reference in a new issue