Skip to content

Commit eab435f

Browse files
committed
fix(test): authRetryCallers waits by the clock — its turn count ran out on the Linux release gate
v1.3.0's release gate failed in authRetryCallers.test.js: "never happened: the renewal", in the first inline-completion case that polls. The suite had passed on every machine it had been run on, all of them Macs. until() gave a condition 400 turns of the event loop to come true. Inline completion waits out its debounce on a real timer, and a timer is at least a millisecond even when the setting is 0. How many turns fit into that millisecond is the machine's business: macOS a 0 ms timer fires after ~70 turns; 400 turns take ~5 ms Linux it fires after ~1,500 turns; 400 turns take ~0.3 ms So on the runner the count ran out before the timer had fired, and the request the case was waiting on had not been sent yet. Nothing is wrong with the code under test. The three cases that poll across the debounce are the ones exposed; which of them fails first varies from run to run. until() now waits by the clock: up to two seconds, the patience within() already has. A condition that never comes true still fails, with the same message. settle() stays a count of turns, and says what that is good for: only what no timer stands in the way of. Both places that use it are that. Reproduced before fixing, without the runner: in a Linux container the tag fails 3 runs of 3, and on a Mac with every timer made 15 ms slower it fails with the runner's exact message. With the fix: 60 of 60 in the container, all 48 suites there three times over, and all 48 on a Mac with and without the slowed timers. No other suite counts turns across a timer; the slowed-timer run would have shown one. Not run on the GitHub runner itself yet, and the container has Node 18 where the runner has 24. The next commit makes the pull request do that run.
1 parent 214e67d commit eab435f

1 file changed

Lines changed: 15 additions & 3 deletions

File tree

‎extensions/levelcode-ai/test/authRetryCallers.test.js‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,11 +210,23 @@ const { registerInlineComplete } = require('../inlineComplete');
210210

211211
// ── the network: the only stand-in ───────────────────────────────────────────────────────────────
212212
const tick = () => new Promise((resolve) => setImmediate(resolve));
213+
/** How long anything here is given to happen. Reached only by what never does. */
214+
const PATIENCE_MS = 2000;
215+
/**
216+
* Wait for `cond` — by the CLOCK, not by counting turns of the event loop. Inline completion waits
217+
* out its debounce on a real timer, a millisecond even when the setting is 0, and how many turns fit
218+
* into a millisecond is the machine's business: a few dozen on a Mac, well over a thousand on the
219+
* Linux runner the release gate uses. A count that was plenty on one ran out on the other before
220+
* the timer had fired.
221+
*/
213222
async function until(cond, what) {
214-
for (let i = 0; i < 400; i++) { if (cond()) { return; } await tick(); }
215-
assert.fail('never happened: ' + what);
223+
const deadline = Date.now() + PATIENCE_MS;
224+
while (!cond()) {
225+
if (Date.now() > deadline) { assert.fail('never happened: ' + what); }
226+
await tick();
227+
}
216228
}
217-
/** Let everything already in motion get as far as it can. */
229+
/** Let everything already in motion get as far as it can. Turns, not time — so only for what no timer stands in the way of. */
218230
async function settle() { for (let i = 0; i < 40; i++) { await tick(); } }
219231
function deferred() { let resolve; const promise = new Promise((r) => { resolve = r; }); return { promise, resolve }; }
220232
function within(promise, ms, what) {

0 commit comments

Comments
 (0)