diff --git a/packages/runtime/src/scr_map.test.ts b/packages/runtime/src/scr_map.test.ts index b567884fc..035b3a0eb 100644 --- a/packages/runtime/src/scr_map.test.ts +++ b/packages/runtime/src/scr_map.test.ts @@ -14,7 +14,14 @@ test("number-key maps keep Map semantics across linear, direct and hashed indexe try { await exec("clang", [ "-std=c11", - "-O1", + // The runtime link set includes scr_json.c, scr_lib.c and scr_hash.c. + // With ASan/UBSan their optimized builds dominate the test's cost: -O1 + // takes about 90s (scr_hash.c alone 29s, its unrolled digest rounds + // instrumented) and regularly starved past this test's timeout when the + // lane shared the box. The test checks Map semantics under sanitizers, + // not codegen, so -O0 keeps every assertion and sanitizer check and + // builds in about 15s. + "-O0", "-Wall", "-Wextra", "-fsanitize=address,undefined", diff --git a/tests/harness/three-library.test.ts b/tests/harness/three-library.test.ts index 23aa4c5e6..b14c9b779 100644 --- a/tests/harness/three-library.test.ts +++ b/tests/harness/three-library.test.ts @@ -7,59 +7,68 @@ import { skipUnownedTest } from "./shard.js"; const fixture = join(import.meta.dirname, "../library-mode/wasm"); const hasClang = spawnSync("clang", ["--version"]).status === 0; +// Release compiles the whole three.js library into one LLVM module that a +// single process optimizes and codegens on one core, which takes 5-10 +// minutes; sanitized dev builds are slower still. Lanes measured 331-581s for +// release against the old 600s budget, so both get 1_200_000ms. The plain dev +// case runs at about a fifth of release and keeps 600_000ms. +function timeoutFor(optimization: "dev" | "release"): number { + return optimization === "release" || process.env["SCRIPTC_SAN"] === "1" ? 1_200_000 : 600_000; +} // The two optimizations take similar time (about 10 minutes each under ASan): // SCRIPTC_TEST_SHARD runs them on different shards (tests/harness/shard.ts). // skipUnownedTest partitions a describe block's tests. describe("three.js native library", () => { beforeEach((context) => skipUnownedTest(context, {})); - test.skipIf(!hasClang).each(["dev", "release"] as const)( - "three.js sends animated geometry, materials and resource lifecycle to a native host in %s", - async (optimization) => { - const directory = await mkdtemp("/tmp/scriptc-three-library-"); - const sanitize = process.env["SCRIPTC_SAN"] === "1"; - try { - const profile = JSON.parse(await readFile(join(fixture, "three.json"), "utf8")); - const profilePath = join(directory, "profile.json"); - await writeFile( - profilePath, - JSON.stringify({ - ...profile, - name: "three-native-embedding", - entry: join(fixture, "three.mjs"), - optimization, - }), - ); - const built = await compileLibrary({ profilePath, outDir: directory, sanitize }); - if (!built.ok) - throw new Error( - built.diagnostics - .map((diagnostic) => `${diagnostic.code}: ${diagnostic.message}`) - .join("\n"), + for (const optimization of ["dev", "release"] as const) { + test.skipIf(!hasClang)( + `three.js sends animated geometry, materials and resource lifecycle to a native host in ${optimization}`, + async () => { + const directory = await mkdtemp("/tmp/scriptc-three-library-"); + const sanitize = process.env["SCRIPTC_SAN"] === "1"; + try { + const profile = JSON.parse(await readFile(join(fixture, "three.json"), "utf8")); + const profilePath = join(directory, "profile.json"); + await writeFile( + profilePath, + JSON.stringify({ + ...profile, + name: "three-native-embedding", + entry: join(fixture, "three.mjs"), + optimization, + }), ); - const binary = join(directory, "host"); - const linked = spawnSync( - "clang", - [ - "-std=c11", - ...(sanitize ? ["-fsanitize=address,undefined"] : []), - join(fixture, "three-native.c"), - built.archivePath, - "-lm", - "-o", - binary, - ], - { encoding: "utf8" }, - ); - expect(linked.status, linked.stderr).toBe(0); - const actual = spawnSync(binary, [], { encoding: "utf8", timeout: 30_000 }); - expect(actual.status, actual.stderr).toBe(0); - const reference = spawnSync( - process.execPath, - [ - "--no-warnings", - "--input-type=module", - "-e", - ` + const built = await compileLibrary({ profilePath, outDir: directory, sanitize }); + if (!built.ok) + throw new Error( + built.diagnostics + .map((diagnostic) => `${diagnostic.code}: ${diagnostic.message}`) + .join("\n"), + ); + const binary = join(directory, "host"); + const linked = spawnSync( + "clang", + [ + "-std=c11", + ...(sanitize ? ["-fsanitize=address,undefined"] : []), + join(fixture, "three-native.c"), + built.archivePath, + "-lm", + "-o", + binary, + ], + { encoding: "utf8" }, + ); + expect(linked.status, linked.stderr).toBe(0); + const actual = spawnSync(binary, [], { encoding: "utf8", timeout: 30_000 }); + expect(actual.status, actual.stderr).toBe(0); + const reference = spawnSync( + process.execPath, + [ + "--no-warnings", + "--input-type=module", + "-e", + ` const events=[]; for (const name of ["vertex","surface","resource"]) globalThis[name]=(...values)=>events.push([name,...values.map(value=>Number(value.toFixed(9)))]); const {frame,dispose}=await import(${JSON.stringify(join(fixture, "three.mjs"))}); @@ -67,21 +76,22 @@ describe("three.js native library", () => { dispose(); console.log(JSON.stringify(events)); `, - ], - { encoding: "utf8" }, - ); - expect(reference.status, reference.stderr).toBe(0); - const events = JSON.parse(reference.stdout); - expect( - actual.stdout - .trim() - .split("\n") - .map((line) => JSON.parse(line)), - ).toEqual([...events, ...events]); - } finally { - await rm(directory, { recursive: true, force: true }); - } - }, - 600_000, - ); + ], + { encoding: "utf8" }, + ); + expect(reference.status, reference.stderr).toBe(0); + const events = JSON.parse(reference.stdout); + expect( + actual.stdout + .trim() + .split("\n") + .map((line) => JSON.parse(line)), + ).toEqual([...events, ...events]); + } finally { + await rm(directory, { recursive: true, force: true }); + } + }, + timeoutFor(optimization), + ); + } });