From 66cc498dd6a2101d5e1668523cbb040224864a9f Mon Sep 17 00:00:00 2001 From: Trivikram Kamat <16024985+trivikr@users.noreply.github.com> Date: Wed, 14 Oct 2026 08:18:11 -0700 Subject: [PATCH] Make Async Array tests deterministic The map, mapAsync and forEach tests asserted that the helpers yielded at least once, but yielding is time based (2ms in test mode). On fast machines the whole array was processed before the threshold, so the tests failed intermittently. Stall on the first item for longer than the yield threshold so the first time check always triggers a yield. --- client-node-tests/src/converter.test.ts | 28 ++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/client-node-tests/src/converter.test.ts b/client-node-tests/src/converter.test.ts index 85bbbb8c0..f81fe712b 100644 --- a/client-node-tests/src/converter.test.ts +++ b/client-node-tests/src/converter.test.ts @@ -89,6 +89,24 @@ function assertDiagnosticCode(value: string | number | DiagnosticCode | undefine suite('Async Array', () => { + // In test mode the async helpers yield once at least 2ms have elapsed in a + // batch. On fast machines the whole array can be processed before that, so + // the tests stall on the first item to guarantee at least one yield. + const yieldAfter = 2; + function stallOnFirstCall(func: (...args: T) => R): (...args: T) => R { + let first = true; + return (...args: T): R => { + if (first) { + first = false; + const end = Date.now() + yieldAfter + 1; + while (Date.now() < end) { + // Busy wait to make the batch exceed the yield time. + } + } + return func(...args); + }; + } + suiteSetup(() => { async.setTestMode(); }); @@ -103,7 +121,7 @@ suite('Async Array', () => { ranges[i] = proto.Range.create(i, i, i, i); } let yielded = 0; - const converted = await async.map(ranges, p2c.asRange, undefined, { yieldAfter: 2, yieldCallback: () => { yielded++; }}); + const converted = await async.map(ranges, stallOnFirstCall((range: proto.Range) => p2c.asRange(range)), undefined, { yieldAfter, yieldCallback: () => { yielded++; }}); ok(yielded > 0); strictEqual(converted.length, ranges.length); for (let i = 0; i < converted.length; i++) { @@ -118,13 +136,13 @@ suite('Async Array', () => { ranges[i] = proto.Range.create(i, i, i, i); } let yielded = 0; - const converted = await async.mapAsync(ranges, (item): Promise => { + const converted = await async.mapAsync(ranges, stallOnFirstCall((item: proto.Range): Promise => { return new Promise((resolve) => { proto.RAL().timer.setImmediate(() => { resolve(p2c.asRange(item)); }); }); - }, undefined, { yieldAfter: 2, yieldCallback: () => { yielded++; }}); + }), undefined, { yieldAfter, yieldCallback: () => { yielded++; }}); ok(yielded > 0); strictEqual(converted.length, ranges.length); for (let i = 0; i < converted.length; i++) { @@ -140,10 +158,10 @@ suite('Async Array', () => { } let sum: number = 0; let yielded = 0; - await async.forEach(ranges, (item) => { + await async.forEach(ranges, stallOnFirstCall((item: proto.Range) => { const codeRange = p2c.asRange(item); sum += codeRange.start.line; - }, undefined, { yieldAfter: 2, yieldCallback: () => { yielded++; }}); + }), undefined, { yieldAfter, yieldCallback: () => { yielded++; }}); ok(yielded > 0); strictEqual(sum, 450015000); }).timeout(5000);