Space the timer delays in two order-sensitive tests and raise the static-spatial timeout

mock-timers' "named and namespace imports keep the loop's timers" and
6811-node-island-uncaught-listener check the order in which real timers
fire, with delays 1 to 5 ms apart that are scheduled one after another.
On a busy runner more time than that can pass between scheduling two of
them, and the order flips in either lane: the sanitized node:test run saw
Node's reference print 'named promise' first, and the sanitized
llvm-differential run saw 'island timer 1' before 'native timer after'.
The delays are now 100 ms apart in mock-timers (100, 200, 300) and the
island's timers (150, 250) start 100 ms after the native ones (10, 50).
Node prints the same output as before.

static-spatial bundles three.js and builds it with the sanitizer, which
passed the default 300 s test timeout on a busy runner. Its cases take
three.test.ts's 1200 s timeout.
This commit is contained in:
Chris Tate
2026-10-28 12:49:21 -05:00
parent 2f3b926de8
commit 577f086658
4 changed files with 15 additions and 7 deletions
@@ -1,4 +1,4 @@
exports.timers = () => {
setTimeout(() => console.log("island timer 1"), 15);
setTimeout(() => console.log("island timer 2"), 40);
setTimeout(() => console.log("island timer 1"), 150);
setTimeout(() => console.log("island timer 2"), 250);
};
@@ -7,12 +7,14 @@ import { callSync } from "@scriptc/node";
process.on("uncaughtException", (error) => {
console.log("caught", error.message);
});
// The island's timers (150 and 250 ms) are scheduled before these (10 and
// 50 ms), with gaps wide enough that a slow run keeps the delay order.
callSync("./island.cjs", "timers");
setTimeout(() => {
console.log("native timer throws");
throw new Error("native boom");
}, 5);
setTimeout(() => console.log("native timer after"), 10);
}, 10);
setTimeout(() => console.log("native timer after"), 50);
setImmediate(() => {
queueMicrotask(() => {
throw new Error("from a microtask");
+6 -3
View File
@@ -271,21 +271,24 @@ describe("imports", () => {
it("named and namespace imports keep the loop's timers", async (t) => {
t.mock.timers.enable({ apis: ["setTimeout"] });
const log: string[] = [];
// The three timers are real and scheduled one after another, so their
// delays are 100 ms apart: a slow run (a sanitized build on a busy
// machine) still sees them fire in delay order.
await Promise.all([
sleep(5).then(() => {
sleep(300).then(() => {
log.push("named promise");
}),
new Promise<void>((resolve) =>
namedSetTimeout(() => {
log.push("named");
resolve();
}, 2),
}, 100),
),
new Promise<void>((resolve) =>
timersNamespace.setTimeout(() => {
log.push("namespace");
resolve();
}, 3),
}, 200),
),
]);
t.mock.timers.tick(10);
+3
View File
@@ -29,6 +29,9 @@ const cases = [
].flatMap((fixture) => ["native", "wasm32-wasi"].map((target) => ({ fixture, target })));
test.for(cases)(
"published $fixture runs statically on $target",
// Bundling three.js and building it with the sanitizer can pass the
// default 300 s on a busy runner, as in three.test.ts.
{ timeout: 1_200_000 },
async ({ fixture, target }, context) => {
if (target === "wasm32-wasi" && !hasZig) context.skip();
const dir = await mkdtemp("/tmp/scriptc-spatial-");