Key the differential oracle cache on the inherited environment (#1266)

This commit is contained in:
Chris Tate
2026-10-18 03:51:05 -05:00
committed by GitHub
3 changed files with 44 additions and 3 deletions
+4 -3
View File
@@ -22,6 +22,7 @@ import { describe, expect, test } from "vitest";
import ts5 from "typescript";
import { compile } from "@scriptc/compiler";
import { corpusSelect, shardSuffix } from "./shard.js";
import { oracleEnvKey } from "./oracle-env.js";
const execFileAsync = promisify(execFile);
const repoRoot = join(import.meta.dirname, "../..");
@@ -238,8 +239,8 @@ function programInputs(file: string): string[] {
// Node's verdict for a corpus program is a pure function of the program bytes,
// the shims, and the Node build (corpus stdout is deterministic by
// construction — it must be identical to a non-Node native binary's output). So
// cache it, keyed by all of those plus the invocation shape (SCRIPTC_TEST_ENV
// and the cwd). Only the SPAWN is skipped: the native side always runs live
// cache it, keyed by all of those plus the invocation shape (the inherited
// environment, see oracle-env.ts, and the cwd). Only the SPAWN is skipped: the native side always runs live
// and the comparison itself never changes. SCRIPTC_NO_CACHE=1 (or an unset
// SCRIPTC_CACHE_DIR) disables the cache in both directions — no reads, no writes.
// Storage shares the compile cache's root and its LRU sweep (see native-toolchain.ts).
@@ -265,7 +266,7 @@ function oracleKeyBase(): Promise<string> {
.update(readFileSync(fileURLToPath(comptimeShim)))
.update("\0")
.update("\0")
.update(process.env["SCRIPTC_TEST_ENV"] ?? "")
.update(oracleEnvKey(process.env))
.update("\0")
.update(process.cwd())
.update("\0")
+25
View File
@@ -0,0 +1,25 @@
import { expect, test } from "vitest";
import { oracleEnvKey } from "./oracle-env.js";
test("the oracle key changes with any inherited variable", () => {
const base = { PATH: "/bin", SCRIPTC_TEST_ENV: "from-harness" };
expect(oracleEnvKey({ ...base, NODE_ENV: "production" })).not.toBe(
oracleEnvKey({ ...base, NODE_ENV: "test" }),
);
expect(oracleEnvKey({ ...base, NODE_ENV: "test" })).not.toBe(oracleEnvKey(base));
expect(oracleEnvKey({ ...base, FOO: "a=b" })).not.toBe(
oracleEnvKey({ ...base, FOO: "a", B: "" }),
);
});
test("the oracle key ignores the runner's worker numbers and variable order", () => {
expect(oracleEnvKey({ A: "1", B: "2", VITEST_WORKER_ID: "1", VITEST_POOL_ID: "1" })).toBe(
oracleEnvKey({
VITEST_POOL_ID: "7",
B: "2",
TINYPOOL_WORKER_ID: "3",
A: "1",
VITEST_WORKER_ID: "9",
}),
);
});
+15
View File
@@ -0,0 +1,15 @@
/* The environment part of the differential oracle cache key. The Node
* oracle runs with the harness's inherited environment, so a program whose
* output reads a variable (NODE_ENV, SCRIPTC_TEST_ENV, ...) must not reuse a
* verdict recorded under a different value. The variables that number the
* test runner's own workers differ between workers of one run and are left
* out, so every worker shares the cache. */
const WORKER_VARIABLES = new Set(["VITEST_POOL_ID", "VITEST_WORKER_ID", "TINYPOOL_WORKER_ID"]);
export function oracleEnvKey(env: NodeJS.ProcessEnv): string {
return Object.keys(env)
.filter((name) => !WORKER_VARIABLES.has(name) && env[name] !== undefined)
.sort()
.map((name) => `${name}=${env[name]}\0`)
.join("");
}