test: cover driver-aware warm package reuse

This commit is contained in:
2026-08-12 15:51:50 +09:00
parent 5eec5d3711
commit badde25fa9

View File

@@ -802,6 +802,268 @@ fn writediamond(td: str, reverse: bool) str = {
testenv.clean(td); testenv.clean(td);
}; };
// Unit bytes cannot identify driver-owned graph, archive, and commit semantics.
// Keep their executable content beside the compiler/assembler identities so a
// warm voucher never crosses a builder change.
@test fn persistent_build_reuse_tracks_driver_identity() void = {
let td: str = testenv.fresh();
let source: str = strings.concat(td, "/source tree");
let app: str = strings.concat(source, "/app");
let dep: str = strings.concat(source, "/dep");
let leaf: str = strings.concat(source, "/leaf");
let tools: str = strings.concat(td, "/tool wrappers");
let cdrivers: str = strings.concat(td, "/c driver copy");
let wdrivers: str = strings.concat(td, "/ww driver copy");
let cwd: str = strings.concat(td, "/isolated cwd");
let work: str = strings.concat(td, "/persistent work");
let bin: str = strings.concat(td, "/published binary");
mkdir(source); mkdir(app); mkdir(dep); mkdir(leaf); mkdir(tools);
mkdir(cdrivers); mkdir(wdrivers); mkdir(cwd); mkdir(work);
testenv.writefile(strings.concat(leaf, "/leaf.ww"), strings.concat(
"package leaf;\n",
"export fn value() i32 = { return 40; };\n"));
testenv.writefile(strings.concat(dep, "/dep.ww"), strings.concat(
"package dep;\nimport leaf;\n",
"export fn value() i32 = { return leaf.value() + 2; };\n"));
testenv.writefile(strings.concat(app, "/main.ww"), strings.concat(
"package main;\nimport dep;\n",
"fn main() i32 = { return dep.value(); };\n"));
let compiler: str = strings.concat(tools, "/w6c wrapper.sh");
let assembler: str = strings.concat(tools, "/w6a wrapper.sh");
let linker: str = strings.concat(tools, "/w6l wrapper.sh");
testenv.writeexecutable(compiler, strings.concat(
"#!/bin/sh\n",
"printf 'BEGIN' >> \"$WW_ROOT_COMPILER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_ROOT_COMPILER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_ROOT_COMPILER_TRACE\"\n",
"exec \"$WW_ROOT_REAL_COMPILER\" \"$@\"\n"));
testenv.writeexecutable(assembler, strings.concat(
"#!/bin/sh\n",
"printf 'BEGIN' >> \"$WW_ROOT_ASSEMBLER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_ROOT_ASSEMBLER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_ROOT_ASSEMBLER_TRACE\"\n",
"exec \"$WW_ROOT_REAL_ASSEMBLER\" \"$@\"\n"));
testenv.writeexecutable(linker, strings.concat(
"#!/bin/sh\n",
"printf 'BEGIN' >> \"$WW_ROOT_LINKER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_ROOT_LINKER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_ROOT_LINKER_TRACE\"\n",
"exec \"$WW_ROOT_REAL_LINKER\" \"$@\"\n"));
let stages: []str = ["ww", "ww_ww"];
let compilers: []str = ["w6c", "w6c_ww"];
let assemblers: []str = ["w6a", "w6a_ww"];
let linkers: []str = ["w6l", "w6l_ww"];
let tags: []str = ["c", "ww"];
let copied: []str = [strings.concat(cdrivers, "/ww"),
strings.concat(wdrivers, "/ww_ww")];
let runtime: str = strings.concat(testenv.repo(), "/out/lib");
let referenceunit: str = "";
let referencewwi: str = "";
let referencearchive: str = "";
let referencebin: str = "";
let referencecompiler: str = "";
let referenceassembler: str = "";
let referencelinker: str = "";
let referenceerror: str = "";
let si: i32 = 0;
for (si < stages.len) {
let compilertrace: str = strings.concat(td, "/", tags[si],
" compiler trace");
let assemblertrace: str = strings.concat(td, "/", tags[si],
" assembler trace");
let linkertrace: str = strings.concat(td, "/", tags[si],
" linker trace");
testenv.writefile(compilertrace, "");
testenv.writefile(assemblertrace, "");
testenv.writefile(linkertrace, "");
let driverbytes: str = testenv.readfile(testenv.driver(stages[si]));
testenv.writeexecutable(copied[si], driverbytes);
let env: []str = roottraceenv(source, runtime,
compiler, assembler, linker,
compilertrace, assemblertrace, linkertrace,
testenv.driver(compilers[si]),
testenv.driver(assemblers[si]),
testenv.driver(linkers[si]));
let av: []str = [copied[si], "build", "-w", work,
"-o", bin, "app"];
let out: testenv.commandout;
testenv.runcommandenv(cwd, td, strings.concat("driver-cold-",
tags[si]), av, env, tmo(), &out);
if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0) {
fail("driver-identity", strings.concat(tags[si],
" cold build failed: ", out.stderr));
};
let rootunitpath: str = strings.concat(work, "/__root.unit.ww");
let depunitpath: str = strings.concat(work, "/dep.unit.ww");
let rootunit: str = testenv.readfile(rootunitpath);
let depunit: str = testenv.readfile(depunitpath);
if (!testenv.has(rootunit, "//ww:module dep\n")
|| testenv.has(rootunit, "//ww:module leaf\n")
|| !testenv.has(depunit, "//ww:module leaf\n")) {
fail("driver-identity", "warm units crossed the direct-export boundary");
};
if (!testenv.exists(strings.concat(work, "/leaf.wwi"))
|| !testenv.exists(strings.concat(work, "/leaf.a"))
|| !testenv.exists(strings.concat(work, "/dep.wwi"))
|| !testenv.exists(strings.concat(work, "/dep.a"))
|| testenv.exists(strings.concat(work, "/__root.wwi"))
|| testenv.exists(strings.concat(work, "/__root.a"))
|| !testenv.same(testenv.readfile(strings.concat(work,
"/.wwtool.ww")), testenv.readfile(copied[si]))
|| !testenv.same(testenv.readfile(strings.concat(work,
"/.wwtool.w6c")), testenv.readfile(compiler))
|| !testenv.same(testenv.readfile(strings.concat(work,
"/.wwtool.w6a")), testenv.readfile(assembler))
|| !testenv.same(testenv.readfile(strings.concat(work,
"/.wwtool.stamp")),
"ww workdir fmt 5 mode build asm 0\n")) {
fail("driver-identity", "persistent artifacts or identities are incomplete");
};
let coldwwi: str = testenv.readfile(strings.concat(work, "/dep.wwi"));
let coldarchive: str = testenv.readfile(strings.concat(work, "/dep.a"));
let coldbin: str = testenv.readfile(bin);
let coldcompiler: str = testenv.readfile(compilertrace);
let coldassembler: str = testenv.readfile(assemblertrace);
let coldlinker: str = testenv.readfile(linkertrace);
if (testenv.occurrences(coldcompiler, "\n") != 3
|| !testenv.has(coldcompiler, strings.concat(
"BEGIN<-c><-I><", work, "/dep.wwi.new><-o><", work,
"/dep.s.new><", work, "/dep.unit.new>"))
|| !testenv.has(coldcompiler, strings.concat(
"BEGIN<-c><-o><", work, "/__root.s.new><", work,
"/__root.unit.new>"))
|| testenv.occurrences(coldassembler, "\n") != 3
|| !testenv.has(coldassembler, strings.concat(
"BEGIN<-o><", work, "/dep.o.new><", work,
"/dep.s.new>"))) {
fail("driver-identity", "cold compiler or assembler argv changed");
};
if (testenv.occurrences(coldlinker, "\n") != 1
|| !testenv.has(coldlinker, strings.concat("BEGIN<-o><", bin,
"><", work, "/__root.o>"))
|| testenv.pos(coldlinker, strings.concat("<", work,
"/dep.a>")) < 0
|| testenv.pos(coldlinker, strings.concat("<", work,
"/leaf.a>")) < testenv.pos(coldlinker, strings.concat("<",
work, "/dep.a>"))
|| !testenv.has(coldlinker, strings.concat("<", runtime,
"/libwwrt.a>"))
|| testenv.has(coldlinker, ".wwi>")) {
fail("driver-identity", "cold link omitted the reachable archive closure");
};
let runav: []str = [bin];
if (code(cwd, strings.concat("driver-cold-run-", tags[si]), runav)
!= 42) {
fail("driver-identity", "cold binary returned the wrong value");
};
testenv.runcommandenv(cwd, td, strings.concat("driver-warm-",
tags[si]), av, env, tmo(), &out);
if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0
|| !testenv.same(coldcompiler, testenv.readfile(compilertrace))
|| !testenv.same(coldassembler, testenv.readfile(assemblertrace))
|| testenv.occurrences(testenv.readfile(linkertrace), "\n") != 2
|| !testenv.same(rootunit, testenv.readfile(rootunitpath))
|| !testenv.same(coldwwi, testenv.readfile(strings.concat(work,
"/dep.wwi")))
|| !testenv.same(coldarchive, testenv.readfile(strings.concat(work,
"/dep.a")))
|| !testenv.same(coldbin, testenv.readfile(bin))) {
fail("driver-identity", "unchanged warm build did not reuse packages");
};
assert(os.remove(copied[si]) == 0);
testenv.writeexecutable(copied[si], strings.concat(driverbytes,
"\nww driver identity mutation\n"));
testenv.runcommandenv(cwd, td, strings.concat("driver-changed-",
tags[si]), av, env, tmo(), &out);
if (out.termination != exec.termination.EXIT || out.code != 0
|| out.stderr.len != 0) {
fail("driver-identity", strings.concat(tags[si],
" changed-driver build failed: ", out.stderr));
};
let changedcompiler: str = testenv.readfile(compilertrace);
let changedassembler: str = testenv.readfile(assemblertrace);
let changedlinker: str = testenv.readfile(linkertrace);
if (testenv.occurrences(changedcompiler, strings.concat("<", work,
"/leaf.unit.new>")) != 2
|| testenv.occurrences(changedcompiler, strings.concat("<", work,
"/dep.unit.new>")) != 2
|| testenv.occurrences(changedcompiler, strings.concat("<", work,
"/__root.unit.new>")) != 2
|| testenv.occurrences(changedcompiler, "\n") != 6
|| testenv.occurrences(changedassembler, "\n") != 6
|| testenv.occurrences(changedlinker, "\n") != 3) {
fail("driver-identity", "driver change reused stale package actions");
};
if (!testenv.same(testenv.readfile(strings.concat(work,
"/.wwtool.ww")), testenv.readfile(copied[si]))
|| !testenv.same(rootunit, testenv.readfile(rootunitpath))
|| !testenv.same(coldwwi, testenv.readfile(strings.concat(work,
"/dep.wwi")))
|| !testenv.same(coldarchive, testenv.readfile(strings.concat(work,
"/dep.a")))
|| !testenv.same(coldbin, testenv.readfile(bin))
|| code(cwd, strings.concat("driver-changed-run-", tags[si]),
runav) != 42) {
fail("driver-identity", "driver invalidation changed package products");
};
let beforecompiler: str = strings.dup(changedcompiler);
let beforeassembler: str = strings.dup(changedassembler);
let beforelinker: str = strings.dup(changedlinker);
let missingwork: str = strings.concat(td, "/missing workdir");
let missingbin: str = strings.concat(td, "/missing binary");
let badav: []str = [copied[si], "build", "-w", missingwork,
"-o", missingbin, "app"];
testenv.runcommandenv(cwd, td, strings.concat("driver-missing-",
tags[si]), badav, env, tmo(), &out);
let wantdiag: str = strings.concat("ww: workdir ", missingwork,
" is not a directory\n");
if (out.termination != exec.termination.EXIT || out.code != 1
|| !testenv.same(out.stderr, wantdiag)
|| !testenv.same(beforecompiler, testenv.readfile(compilertrace))
|| !testenv.same(beforeassembler, testenv.readfile(assemblertrace))
|| !testenv.same(beforelinker, testenv.readfile(linkertrace))
|| testenv.exists(missingbin)) {
fail("driver-identity", "invalid workdir crossed the tool boundary");
};
if (si == 0) {
referenceunit = strings.dup(rootunit);
referencewwi = strings.dup(coldwwi);
referencearchive = strings.dup(coldarchive);
referencebin = strings.dup(coldbin);
referencecompiler = strings.dup(changedcompiler);
referenceassembler = strings.dup(changedassembler);
referencelinker = strings.dup(changedlinker);
referenceerror = strings.dup(out.stderr);
} else { if (!testenv.same(referenceunit, rootunit)
|| !testenv.same(referencewwi, coldwwi)
|| !testenv.same(referencearchive, coldarchive)
|| !testenv.same(referencebin, coldbin)
|| !testenv.same(referencecompiler, changedcompiler)
|| !testenv.same(referenceassembler, changedassembler)
|| !testenv.same(referencelinker, changedlinker)
|| !testenv.same(referenceerror, out.stderr)) {
fail("driver-identity", "Cstage and WWstage warm behavior differs");
}; };
testenv.clean(work);
assert(os.remove(bin) == 0);
if (si + 1 < stages.len) { mkdir(work); };
si += 1;
};
testenv.clean(td);
};
@test fn builtin_type_precedence() void = { @test fn builtin_type_precedence() void = {
let td: str = testenv.fresh(); let td: str = testenv.fresh();
let dep: str = strings.concat(td, "/builtinshadow"); mkdir(dep); let dep: str = strings.concat(td, "/builtinshadow"); mkdir(dep);