test: cover driver-aware warm package reuse
This commit is contained in:
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user