test: prove local vendor package semantics

This commit is contained in:
2026-08-13 19:18:05 +09:00
parent b84fb3ff25
commit 5d50788e46
2 changed files with 1204 additions and 45 deletions

View File

@@ -435,6 +435,20 @@ fn normalizedtrace(trace: str, work: str, output: str) str = {
};
};
fn hexbytes(value: str) str = {
let out: []u8 = alloc([], (value.len * 2 + 1): u64)!;
let digits: str = "0123456789abcdef";
let i: i32 = 0;
for (i < value.len) {
let high: i32 = (value[i] / 16u8): i32;
let low: i32 = (value[i] % 16u8): i32;
append(out, digits[high]);
append(out, digits[low]);
i += 1;
};
return strings.frombytes(out);
};
@test fn deterministic_routing_and_filters() void = {
let root: str = fresh();
let target: str = packagepath("routing");
@@ -5639,7 +5653,932 @@ fn runtimepath(relative: str) str = {
clean(root);
};
// The package-level -o contract: -c -o names the single package's
@test fn vendor_directory_import_resolution() void = {
let root: str = fresh();
let source: str = strings.concat(root, "/source");
let runtime: str = strings.concat(root, "/runtime");
let tools: str = strings.concat(root, "/tools");
let leaf: str = strings.concat(source, "/lib/leaf");
let nearest: str = strings.concat(source,
"/domain/app/vendor/lib/math");
let outer: str = strings.concat(source, "/domain/vendor/lib/math");
let rootvendor: str = strings.concat(source, "/vendor/lib/math");
let ordinary: str = strings.concat(source, "/lib/math");
let client: str = strings.concat(source, "/domain/app/client");
let one: str = strings.concat(source, "/domain/app/one");
let two: str = strings.concat(source, "/domain/app/two");
let outerclient: str = strings.concat(source, "/domain/other/client");
let rootclient: str = strings.concat(source, "/other/client");
let emptyclient: str = strings.concat(source, "/domain/empty/client");
let testonlyclient: str = strings.concat(source,
"/domain/testonly/client");
let bareclient: str = strings.concat(source, "/domain/bare/client");
let outsider: str = strings.concat(source, "/outsider");
let prefixclient: str = strings.concat(source, "/domainx/client");
let directclient: str = strings.concat(source, "/domain/app/direct");
let directhijack: str = strings.concat(directclient,
"/vendor/domain/app/vendor/lib/math");
let nested: str = strings.concat(source,
"/nested/owner/vendor/outer/vendor/lib/math");
let nestedclient: str = strings.concat(source,
"/nested/owner/vendor/outer/client");
let ordinaryclient: str = strings.concat(source, "/ordinaryclient");
let vendorx: str = strings.concat(source, "/vendorx/lib/math");
let terminalvendor: str = strings.concat(source, "/terminal/vendor");
let physicalclient: str = strings.concat(source,
"/domain/app/physicalclient");
let aliasclient: str = strings.concat(source, "/aliasclient");
let physicaldirect: str = strings.concat(source,
"/domain/app/physicaldirect");
let aliasdirect: str = strings.concat(source, "/aliasdirect");
let escapephysical: str = strings.concat(root, "/escape/client");
let escapeclient: str = strings.concat(source, "/domain/app/escape");
let shapeclient: str = strings.concat(source, "/shape/owner/client");
let shapevendor: str = strings.concat(source,
"/shape/owner/vendor/lib/math");
let shapevault: str = strings.concat(root, "/vault/deep/different");
let testclient: str = strings.concat(source, "/domain/app/testclient");
let testbad: str = strings.concat(source, "/testbad");
let supportcase: str = strings.concat(source, "/supportcase");
let toolsource: str = strings.concat(root, "/toolsource");
let toolsupport: str = strings.concat(toolsource, "/test");
let toolvendor: str = strings.concat(toolsource,
"/test/vendor/fnmatch");
let vendortest: str = strings.concat(source,
"/literal/owner/vendor/lib/math");
let nestedvendortest: str = strings.concat(vendortest,
"/vendor/lib/math");
let alpha: str = strings.concat(source, "/alpha/client");
let beta: str = strings.concat(source, "/beta/client");
let alphavendor: str = strings.concat(alpha, "/vendor/lib/math");
let betavendor: str = strings.concat(beta, "/vendor/lib/math");
let pairvault: str = strings.concat(root, "/pair-vault/math");
let boundary: str = strings.concat(root, "/boundary");
let active: str = strings.concat(boundary, "/active");
let boundaryclient: str = strings.concat(active, "/domain/app");
let boundaryordinary: str = strings.concat(active, "/lib/math");
let boundaryabove: str = strings.concat(boundary, "/vendor/lib/math");
let cleanroot: str = strings.concat(root, "/clean");
let cleanclient: str = strings.concat(cleanroot, "/domain/app");
let cleanordinary: str = strings.concat(cleanroot, "/lib/math");
let dirs: []str = [leaf, nearest, outer, rootvendor, ordinary, client,
one, two, outerclient, rootclient, emptyclient,
strings.concat(emptyclient, "/vendor/lib/math/fake.ww"),
testonlyclient, strings.concat(testonlyclient, "/vendor/lib/math"),
bareclient, strings.concat(bareclient, "/vendor/lib/math"), outsider,
prefixclient, directclient, directhijack, nested, nestedclient,
ordinaryclient,
vendorx, terminalvendor, physicalclient, physicaldirect, escapephysical,
shapeclient, strings.concat(source, "/shape/owner/vendor/lib"),
shapevault, testclient, testbad, supportcase, toolsupport,
toolvendor, vendortest, nestedvendortest,
alpha, beta, strings.concat(alpha, "/vendor/lib"),
strings.concat(beta, "/vendor/lib"), pairvault, boundaryclient,
boundaryordinary, boundaryabove, cleanclient, cleanordinary,
runtime, tools];
let di: i32 = 0;
for (di < dirs.len) { mkdirall(dirs[di]); di += 1; };
writefile(strings.concat(runtime, "/libwwrt.a"),
readfile(strings.concat(repo(), "/out/lib/libwwrt.a")));
writefile(strings.concat(leaf, "/leaf.ww"), strings.concat(
"package leaf;\n",
"export fn value() i32 = { return 1; };\n"));
let neara: str = strings.concat(
"package math;\nimport lib.leaf;\n",
"export fn value() i32 = { return leaf.value() + 10; };\n");
let nearz: str = strings.concat(
"package math;\n",
"fn private_value() i32 = { return 99; };\n");
writefile(strings.concat(nearest, "/a.ww"), neara);
writefile(strings.concat(nearest, "/z.ww"), nearz);
writefile(strings.concat(outer, "/math.ww"),
"package math;\nexport fn value() i32 = { return 22; };\n");
writefile(strings.concat(rootvendor, "/math.ww"),
"package math;\nexport fn value() i32 = { return 33; };\n");
writefile(strings.concat(ordinary, "/math.ww"),
"package math;\nexport fn value() i32 = { return 44; };\n");
let mainbody: str = strings.concat(
"package main;\nimport lib.math;\n",
"fn main() i32 = { return math.value(); };\n");
writefile(strings.concat(client, "/main.ww"), mainbody);
writefile(strings.concat(one, "/main.ww"), mainbody);
writefile(strings.concat(two, "/main.ww"), mainbody);
writefile(strings.concat(outerclient, "/main.ww"), mainbody);
writefile(strings.concat(rootclient, "/main.ww"), mainbody);
writefile(strings.concat(emptyclient, "/main.ww"), mainbody);
writefile(strings.concat(testonlyclient, "/main.ww"), mainbody);
writefile(strings.concat(testonlyclient,
"/vendor/lib/math/only_test.ww"),
"package math;\n@test fn only() void = { assert(true); };\n");
writefile(strings.concat(bareclient, "/main.ww"), mainbody);
writefile(strings.concat(bareclient, "/vendor/lib/math/.ww"),
"package math;\n");
let expanded: str = "domain.app.vendor.lib.math";
let directbody: str = strings.concat(
"package main;\nimport ", expanded, ";\n",
"fn main() i32 = { return math.value(); };\n");
writefile(strings.concat(outsider, "/main.ww"), directbody);
writefile(strings.concat(prefixclient, "/main.ww"), directbody);
writefile(strings.concat(directclient, "/main.ww"), directbody);
writefile(strings.concat(directhijack, "/math.ww"),
"package math;\nexport fn value() i32 = { return 99; };\n");
writefile(strings.concat(nested, "/math.ww"),
"package math;\nexport fn value() i32 = { return 77; };\n");
writefile(strings.concat(nestedclient, "/main.ww"), mainbody);
writefile(strings.concat(vendorx, "/math.ww"),
"package math;\nexport fn value() i32 = { return 4; };\n");
writefile(strings.concat(terminalvendor, "/vendor.ww"),
"package vendor;\nexport fn value() i32 = { return 8; };\n");
writefile(strings.concat(ordinaryclient, "/main.ww"), strings.concat(
"package main;\nimport vendorx.lib.math;\nimport terminal.vendor;\n",
"fn main() i32 = { return math.value() + vendor.value(); };\n"));
writefile(strings.concat(physicalclient, "/main.ww"), mainbody);
assert(os.symlink(physicalclient, aliasclient) == 0);
writefile(strings.concat(physicaldirect, "/main.ww"), directbody);
assert(os.symlink(physicaldirect, aliasdirect) == 0);
writefile(strings.concat(escapephysical, "/main.ww"), mainbody);
assert(os.symlink(escapephysical, escapeclient) == 0);
writefile(strings.concat(shapevault, "/math.ww"),
"package math;\nexport fn value() i32 = { return 17; };\n");
assert(os.symlink(shapevault, shapevendor) == 0);
writefile(strings.concat(shapeclient, "/main.ww"), mainbody);
writefile(strings.concat(testclient, "/testclient.ww"), strings.concat(
"package testclient;\nimport lib.math;\n",
"export fn value() i32 = { return math.value(); };\n"));
writefile(strings.concat(testclient, "/same_test.ww"), strings.concat(
"package testclient;\nimport lib.math;\n",
"@test fn same_vendor() void = { assert(math.value() == 11); };\n"));
writefile(strings.concat(testclient, "/external_test.ww"), strings.concat(
"package testclient_test;\nimport lib.math;\n",
"import domain.app.testclient;\n",
"@test fn external_vendor() void = {\n",
" assert(math.value() + testclient.value() == 22);\n};\n"));
writefile(strings.concat(testbad, "/testbad.ww"),
"package testbad;\nexport fn value() i32 = { return 1; };\n");
writefile(strings.concat(testbad, "/same_test.ww"), strings.concat(
"package testbad;\nimport ", expanded, ";\n",
"@test fn forbidden_vendor() void = { assert(false); };\n"));
writefile(strings.concat(supportcase, "/supportcase.ww"),
"package supportcase;\nexport fn value() i32 = { return 1; };\n");
writefile(strings.concat(supportcase, "/supportcase_test.ww"),
"package supportcase;\n@test fn support_runs() void = { assert(value() == 1); };\n");
writefile(strings.concat(toolsupport, "/run.ww"),
readfile(strings.concat(repo(), "/lib/test/run.ww")));
writefile(strings.concat(toolvendor, "/fnmatch.ww"),
readfile(strings.concat(repo(), "/lib/fnmatch/fnmatch.ww")));
let tooldeps: []str = ["ascii", "bytes", "encoding", "os", "rt",
"strings", "time", "types"];
di = 0;
for (di < tooldeps.len) {
assert(os.symlink(strings.concat(repo(), "/lib/", tooldeps[di]),
strings.concat(toolsource, "/", tooldeps[di])) == 0);
di += 1;
};
writefile(strings.concat(vendortest, "/math.ww"),
"package math;\nexport fn value() i32 = { return 7; };\n");
writefile(strings.concat(vendortest, "/math_test.ww"), strings.concat(
"package math_test;\nimport lib.math;\n",
"@test fn nested_external_vendor() void = {\n",
" assert(math.value() == 9);\n};\n"));
writefile(strings.concat(nestedvendortest, "/math.ww"),
"package math;\nexport fn value() i32 = { return 9; };\n");
writefile(strings.concat(pairvault, "/math.ww"),
"package math;\nexport fn value() i32 = { return 61; };\n");
assert(os.symlink(pairvault, alphavendor) == 0);
assert(os.symlink(pairvault, betavendor) == 0);
writefile(strings.concat(alpha, "/main.ww"), mainbody);
writefile(strings.concat(beta, "/main.ww"), mainbody);
writefile(strings.concat(boundaryclient, "/main.ww"), mainbody);
writefile(strings.concat(boundaryordinary, "/math.ww"),
"package math;\nexport fn value() i32 = { return 55; };\n");
writefile(strings.concat(boundaryabove, "/math.ww"),
"package math;\nexport fn value() i32 = { return 66; };\n");
writefile(strings.concat(cleanclient, "/main.ww"), mainbody);
writefile(strings.concat(cleanordinary, "/math.ww"),
"package math;\nexport fn value() i32 = { return 44; };\n");
let compilerwrapper: str = strings.concat(tools, "/w6c.sh");
let assemblerwrapper: str = strings.concat(tools, "/w6a.sh");
let linkerwrapper: str = strings.concat(tools, "/w6l.sh");
writeexecutable(compilerwrapper, strings.concat(
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_VENDOR_COMPILER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_VENDOR_COMPILER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_VENDOR_COMPILER_TRACE\"\n",
"exec \"$WW_VENDOR_REAL_COMPILER\" \"$@\"\n"));
writeexecutable(assemblerwrapper, strings.concat(
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_VENDOR_ASSEMBLER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_VENDOR_ASSEMBLER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_VENDOR_ASSEMBLER_TRACE\"\n",
"exec \"$WW_VENDOR_REAL_ASSEMBLER\" \"$@\"\n"));
writeexecutable(linkerwrapper, strings.concat(
"#!/bin/sh\nprintf 'BEGIN' >> \"$WW_VENDOR_LINKER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_VENDOR_LINKER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_VENDOR_LINKER_TRACE\"\n",
"exec \"$WW_VENDOR_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 works: []str = [strings.concat(root, "/c-work"),
strings.concat(root, "/ww-work")];
let bins: []str = [strings.concat(root, "/c-bin"),
strings.concat(root, "/ww-bin")];
let compilertraces: []str = [strings.concat(root, "/c-compiler"),
strings.concat(root, "/ww-compiler")];
let assemblertraces: []str = [strings.concat(root, "/c-assembler"),
strings.concat(root, "/ww-assembler")];
let linkertraces: []str = [strings.concat(root, "/c-linker"),
strings.concat(root, "/ww-linker")];
let coreartifacts: []str = ["lib.leaf", expanded, "domain.app.client"];
let coresuffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
let references: []str = alloc([],
(coreartifacts.len * coresuffixes.len): u64)!;
let ri: i32 = 0;
for (ri < coreartifacts.len * coresuffixes.len) {
append(references, ""); ri += 1;
};
let referencebin: str = "";
let referencecompiler: str = "";
let referenceassembler: str = "";
let referencelinker: str = "";
let referencedirectdiag: str = "";
let referenceoutsidediag: str = "";
let combinedrefs: []str = ["", "", "", "", "", ""];
let referencetestout: str = "";
let referencetesterr: str = "";
let testrefs: []str = ["", "", "", "", "", ""];
let referencesupportout: str = "";
let supportrefs: []str = ["", "", ""];
let referenceorderdiag: str = "";
let referenceescapeddiag: str = "";
let referencealiasdirectdiag: str = "";
let referenceprefixdiag: str = "";
let candidate_diag_refs: []str = ["", ""];
let supplementalrefs: []str = ["", "", "", "", "", "", "", "",
""];
let referencevendortestout: str = "";
let referencevendortesterr: str = "";
let referencetestbaddiag: str = "";
let baseenv: []str = os.getenvs();
let si: i32 = 0;
for (si < stages.len) {
assert(os.mkdir(works[si], 448i32) == 0);
writefile(compilertraces[si], "");
writefile(assemblertraces[si], "");
writefile(linkertraces[si], "");
let env: []str = alloc([], (baseenv.len + 11): u64)!;
let ei: i32 = 0;
for (ei < baseenv.len) {
if (!strings.hasprefix(baseenv[ei], "WW_W6C=")
&& !strings.hasprefix(baseenv[ei], "WW_W6A=")
&& !strings.hasprefix(baseenv[ei], "WW_W6L=")
&& !strings.hasprefix(baseenv[ei], "WW_LIB=")
&& !strings.hasprefix(baseenv[ei], "WW_SRCLIB=")
&& !strings.hasprefix(baseenv[ei],
"WW_VENDOR_COMPILER_TRACE=")
&& !strings.hasprefix(baseenv[ei],
"WW_VENDOR_ASSEMBLER_TRACE=")
&& !strings.hasprefix(baseenv[ei],
"WW_VENDOR_LINKER_TRACE=")
&& !strings.hasprefix(baseenv[ei],
"WW_VENDOR_REAL_COMPILER=")
&& !strings.hasprefix(baseenv[ei],
"WW_VENDOR_REAL_ASSEMBLER=")
&& !strings.hasprefix(baseenv[ei],
"WW_VENDOR_REAL_LINKER=")) {
append(env, baseenv[ei]);
};
ei += 1;
};
append(env, strings.concat("WW_W6C=", compilerwrapper));
append(env, strings.concat("WW_W6A=", assemblerwrapper));
append(env, strings.concat("WW_W6L=", linkerwrapper));
append(env, strings.concat("WW_LIB=", runtime));
append(env, strings.concat("WW_VENDOR_COMPILER_TRACE=",
compilertraces[si]));
append(env, strings.concat("WW_VENDOR_ASSEMBLER_TRACE=",
assemblertraces[si]));
append(env, strings.concat("WW_VENDOR_LINKER_TRACE=",
linkertraces[si]));
append(env, strings.concat("WW_VENDOR_REAL_COMPILER=",
driver(compilers[si])));
append(env, strings.concat("WW_VENDOR_REAL_ASSEMBLER=",
driver(assemblers[si])));
append(env, strings.concat("WW_VENDOR_REAL_LINKER=",
driver(linkers[si])));
let av: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", bins[si], client];
let out: commandout;
runcommandenv(root, strings.concat("vendor-cold-", stages[si]), av,
env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
let wantnear: str = strings.concat(
"//ww:module-reset ", expanded, "\n", neara, "\n",
"//ww:module-reset ", expanded, "\n", nearz, "\n",
"//ww:vendor-dir ", hexbytes(nearest), "\n");
assert(same(readfile(strings.concat(works[si], "/", expanded,
".unit.ww")), wantnear));
let clientunit: str = readfile(strings.concat(works[si],
"/domain.app.client.unit.ww"));
assert(has(clientunit, strings.concat("//ww:import-map lib.math ",
expanded, " ", hexbytes(nearest), "\n")));
let ai: i32 = 0;
for (ai < coreartifacts.len) {
let xi: i32 = 0;
for (xi < coresuffixes.len) {
let bytes: str = readfile(strings.concat(works[si], "/",
coreartifacts[ai], coresuffixes[xi]));
let index: i32 = ai * coresuffixes.len + xi;
if (si == 0) { references[index] = strings.dup(bytes); }
else { assert(same(references[index], bytes)); };
xi += 1;
};
ai += 1;
};
let ctrace: str = readfile(compilertraces[si]);
let atrace: str = readfile(assemblertraces[si]);
let ltrace: str = readfile(linkertraces[si]);
assert(occurrences(ctrace, "\n") == 3);
assert(occurrences(atrace, "\n") == 3);
assert(occurrences(ltrace, "\n") == 1);
let nearline: str = linecontaining(ctrace,
strings.concat(expanded, ".unit.new"));
assert(occurrences(nearline, "<--import><lib.leaf>") == 1);
assert(!has(nearline, "domain.app.client.wwi"));
let clientline: str = linecontaining(ctrace,
"domain.app.client.unit.new");
assert(occurrences(clientline, strings.concat("<--import><", expanded,
"><", works[si], "/", expanded, ".wwi>")) == 1);
assert(occurrences(clientline, strings.concat(
"<--import-map><lib.math><", expanded, ">")) == 1);
assert(!has(clientline, "lib.leaf.wwi"));
assert(occurrences(ltrace, strings.concat("<", works[si],
"/domain.app.client.a>")) == 1);
assert(occurrences(ltrace, strings.concat("<", works[si], "/",
expanded, ".a>")) == 1);
assert(occurrences(ltrace, strings.concat("<", works[si],
"/lib.leaf.a>")) == 1);
assert(!has(ltrace, ".wwi>"));
let nc: str = normalizedtrace(ctrace,
strings.concat(works[si], "/"), bins[si]);
let na: str = normalizedtrace(atrace,
strings.concat(works[si], "/"), bins[si]);
let nl: str = normalizedtrace(ltrace,
strings.concat(works[si], "/"), bins[si]);
if (si == 0) {
referencecompiler = strings.dup(nc);
referenceassembler = strings.dup(na);
referencelinker = strings.dup(nl);
referencebin = strings.dup(readfile(bins[si]));
} else {
assert(same(referencecompiler, nc));
assert(same(referenceassembler, na));
assert(same(referencelinker, nl));
assert(same(referencebin, readfile(bins[si])));
};
let runav: []str = [bins[si]];
runcommand(root, strings.concat("vendor-run-", stages[si]), runav,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 11);
let voucher: str = readfile(strings.concat(works[si], "/",
expanded, ".unit.ww"));
let toolww: str = readfile(strings.concat(works[si], "/.wwtool.ww"));
let toolc: str = readfile(strings.concat(works[si], "/.wwtool.w6c"));
let toola: str = readfile(strings.concat(works[si], "/.wwtool.w6a"));
let stamp: str = readfile(strings.concat(works[si], "/.wwtool.stamp"));
rewritefile(compilertraces[si], "");
rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
runcommandenv(root, strings.concat("vendor-warm-", stages[si]), av,
env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
assert(occurrences(readfile(linkertraces[si]), "\n") == 1);
rewritefile(compilertraces[si], "");
rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
let badout: str = strings.concat(root, "/warm-bad-", stages[si]);
let badav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", badout, outsider];
runcommandenv(root, strings.concat("vendor-warm-bad-", stages[si]),
badav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
let outsidediag: str = strings.concat(outsider,
"/main.ww:2:1: error: use of vendored package not allowed\n");
assert(same(out.stderr, outsidediag));
if (si == 0) { referenceoutsidediag = strings.dup(out.stderr); }
else { assert(same(referenceoutsidediag, out.stderr)); };
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
assert(readfile(linkertraces[si]).len == 0);
assert(!os.exists(badout));
assert(same(voucher, readfile(strings.concat(works[si], "/",
expanded, ".unit.ww"))));
assert(same(toolww, readfile(strings.concat(works[si], "/.wwtool.ww"))));
assert(same(toolc, readfile(strings.concat(works[si], "/.wwtool.w6c"))));
assert(same(toola, readfile(strings.concat(works[si], "/.wwtool.w6a"))));
assert(same(stamp, readfile(strings.concat(works[si],
"/.wwtool.stamp"))));
ai = 0;
for (ai < coreartifacts.len) {
let xi: i32 = 0;
for (xi < coresuffixes.len) {
let index: i32 = ai * coresuffixes.len + xi;
assert(same(references[index], readfile(strings.concat(
works[si], "/", coreartifacts[ai], coresuffixes[xi]))));
xi += 1;
};
ai += 1;
};
let coldout: str = strings.concat(root, "/cold-bad-", stages[si]);
let coldav: []str = [driver(stages[si]), "build", "-I", source,
"-o", coldout, outsider];
runcommandenv(root, strings.concat("vendor-cold-bad-", stages[si]),
coldav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(same(out.stderr, outsidediag));
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
assert(readfile(linkertraces[si]).len == 0);
assert(!os.exists(coldout));
assert(!os.exists(strings.concat(coldout, ".sepwork")));
let directout: str = strings.concat(root, "/direct-bad-", stages[si]);
let directav: []str = [driver(stages[si]), "build", "-I", source,
"-o", directout, directclient];
runcommandenv(root, strings.concat("vendor-direct-bad-", stages[si]),
directav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
let directdiag: str = strings.concat(directclient,
"/main.ww:2:1: error: ", expanded,
" must be imported as lib.math\n");
assert(same(out.stderr, directdiag));
if (si == 0) { referencedirectdiag = strings.dup(out.stderr); }
else { assert(same(referencedirectdiag, out.stderr)); };
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
assert(readfile(linkertraces[si]).len == 0);
assert(!os.exists(directout));
assert(!os.exists(strings.concat(directout, ".sepwork")));
// The lexical importer is outside domain/app, but the symlink target is
// physically inside it. Visibility therefore passes and the later
// effective-spelling diagnostic proves physical importer containment.
let aliasdirectout: str = strings.concat(root,
"/alias-direct-bad-", stages[si]);
let aliasdirectav: []str = [driver(stages[si]), "build", "-I", source,
"-o", aliasdirectout, aliasdirect];
runcommandenv(root, strings.concat("vendor-alias-direct-bad-",
stages[si]), aliasdirectav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
let aliasdirectdiag: str = strings.concat(aliasdirect,
"/main.ww:2:1: error: ", expanded,
" must be imported as lib.math\n");
assert(same(out.stderr, aliasdirectdiag));
if (si == 0) { referencealiasdirectdiag = strings.dup(out.stderr); }
else { assert(same(referencealiasdirectdiag, out.stderr)); };
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
assert(readfile(linkertraces[si]).len == 0);
assert(!os.exists(aliasdirectout));
assert(!os.exists(strings.concat(aliasdirectout, ".sepwork")));
// Remaining resolution rows reuse the stage workdir, but every output
// remains independently executable and compared across stages below.
let targets: []str = [outerclient, rootclient, emptyclient,
boundaryclient, cleanclient, nestedclient, ordinaryclient,
aliasclient, shapeclient];
let roots: []str = [source, source, source, active, cleanroot, source,
source, source, source];
let codes: []i32 = [22, 33, 22, 55, 44, 77, 12, 33, 17];
let labels: []str = ["outer", "root", "empty", "boundary", "ordinary",
"nested", "names", "alias", "shape"];
let ti: i32 = 0;
for (ti < targets.len) {
let output: str = strings.concat(root, "/", labels[ti], "-",
stages[si]);
let tav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", roots[ti], "-o", output, targets[ti]];
runcommandenv(root, strings.concat("vendor-", labels[ti], "-",
stages[si]), tav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let rr: []str = [output];
runcommand(root, strings.concat("vendor-run-", labels[ti], "-",
stages[si]), rr,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, codes[ti]);
if (si == 0) {
supplementalrefs[ti] = strings.dup(readfile(output));
} else {
assert(same(supplementalrefs[ti], readfile(output)));
};
ti += 1;
};
let escapedout: str = strings.concat(root, "/escape-bad-", stages[si]);
let escapedav: []str = [driver(stages[si]), "build", "-I", source,
"-o", escapedout, escapeclient];
runcommandenv(root, strings.concat("vendor-escape-bad-", stages[si]),
escapedav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "use of vendored package not allowed\n"));
if (si == 0) { referenceescapeddiag = strings.dup(out.stderr); }
else { assert(same(referenceescapeddiag, out.stderr)); };
let prefixout: str = strings.concat(root, "/prefix-bad-", stages[si]);
let prefixav: []str = [driver(stages[si]), "build", "-I", source,
"-o", prefixout, prefixclient];
runcommandenv(root, strings.concat("vendor-prefix-bad-", stages[si]),
prefixav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "use of vendored package not allowed\n"));
if (si == 0) { referenceprefixdiag = strings.dup(out.stderr); }
else { assert(same(referenceprefixdiag, out.stderr)); };
let candidateclients: []str = [testonlyclient, bareclient];
let candidatefiles: []str = ["only_test.ww", ".ww"];
let ci: i32 = 0;
for (ci < candidateclients.len) {
let candidateout: str = strings.concat(root, "/candidate-bad-",
stages[si], "-", candidatefiles[ci]);
let cav: []str = [driver(stages[si]), "build", "-I", source,
"-o", candidateout, candidateclients[ci]];
runcommandenv(root, strings.concat("vendor-candidate-bad-",
stages[si], "-", candidatefiles[ci]), cav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "/vendor/lib/math: directory contains no WW package sources\n"));
assert(!os.exists(strings.concat(candidateout, ".sepwork")));
if (si == 0) {
candidate_diag_refs[ci] = strings.dup(out.stderr);
} else {
assert(same(candidate_diag_refs[ci], out.stderr));
};
ci += 1;
};
let selected: str = strings.concat(root, "/selected-", stages[si], ".a");
let selectedav: []str = [driver(stages[si]), "build", "-I", source,
"-o", selected, nearest];
runcommandenv(root, strings.concat("vendor-selected-", stages[si]),
selectedav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(same(references[1 * coresuffixes.len + 4], readfile(selected)));
assert(same(references[1 * coresuffixes.len + 1],
readfile(strings.concat(selected, ".wwi"))));
// One command-global union shares one vendored action between three
// importers, keeps an outer physical copy distinct, and also keeps two
// expanded identities distinct when their lexical vendor routes converge
// through symlinks on one physical directory.
rewritefile(compilertraces[si], "");
rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
let combinedwork: str = strings.concat(root, "/combined-work-",
stages[si]);
assert(os.mkdir(combinedwork, 448i32) == 0);
let combinedtargets: []str = [client, one, two, outerclient, alpha,
beta, pairvault];
let combinedpackages: []str = ["main", "main", "main", "main",
"main", "main", "math"];
let combinedcodes: []i32 = [0, 0, 0, 0, 0, 0, 0];
let combinedoutputs: []str = [
strings.concat(root, "/combined-client-", stages[si]),
strings.concat(root, "/combined-one-", stages[si]),
strings.concat(root, "/combined-two-", stages[si]),
strings.concat(root, "/combined-outer-", stages[si]),
strings.concat(root, "/combined-alpha-", stages[si]),
strings.concat(root, "/combined-beta-", stages[si]),
strings.concat(root, "/combined-local-", stages[si])];
let combinedstatuses: []str = [
strings.concat(combinedoutputs[0], ".status"),
strings.concat(combinedoutputs[1], ".status"),
strings.concat(combinedoutputs[2], ".status"),
strings.concat(combinedoutputs[3], ".status"),
strings.concat(combinedoutputs[4], ".status"),
strings.concat(combinedoutputs[5], ".status"),
strings.concat(combinedoutputs[6], ".status")];
let combinedav: []str = alloc([], (8 + combinedtargets.len * 6): u64)!;
append(combinedav, driver(stages[si]));
append(combinedav, "test"); append(combinedav, "-c");
append(combinedav, "-w"); append(combinedav, combinedwork);
append(combinedav, "-I"); append(combinedav, source);
let ui: i32 = 0;
for (ui < combinedtargets.len) {
append(combinedav, "--ww-package-test");
append(combinedav, "production");
append(combinedav, combinedpackages[ui]);
append(combinedav, combinedtargets[ui]);
append(combinedav, combinedoutputs[ui]);
append(combinedav, combinedstatuses[ui]);
ui += 1;
};
append(combinedav, client);
runcommandenv(root, strings.concat("vendor-combined-", stages[si]),
combinedav, env, (180i64 * (time.second: i64)): time.duration,
&out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
let combinedtrace: str = readfile(compilertraces[si]);
assert(occurrences(combinedtrace, strings.concat("/", expanded,
".unit.new")) == 1);
assert(occurrences(combinedtrace,
"/domain.vendor.lib.math.unit.new") == 1);
assert(occurrences(combinedtrace,
"/alpha.client.vendor.lib.math.unit.new") == 1);
assert(occurrences(combinedtrace,
"/beta.client.vendor.lib.math.unit.new") == 1);
assert(has(combinedtrace, localidentity(pairvault, "math")));
assert(occurrences(combinedtrace, "/lib.leaf.unit.new") == 1);
let combinedidentities: []str = [expanded,
"alpha.client.vendor.lib.math", "beta.client.vendor.lib.math"];
let combinedsuffixes: []str = [".wwi", ".a"];
ui = 0;
for (ui < combinedidentities.len) {
let xi: i32 = 0;
for (xi < combinedsuffixes.len) {
let bytes: str = readfile(strings.concat(combinedwork, "/",
combinedidentities[ui], combinedsuffixes[xi]));
let index: i32 = ui * combinedsuffixes.len + xi;
if (si == 0) {
combinedrefs[index] = strings.dup(bytes);
} else {
assert(same(combinedrefs[index], bytes));
};
xi += 1;
};
ui += 1;
};
assert(os.exists(strings.concat(combinedwork,
"/domain.vendor.lib.math.wwi")));
assert(os.exists(strings.concat(combinedwork,
"/domain.vendor.lib.math.a")));
ui = 0;
for (ui < combinedoutputs.len) {
let cav: []str = [combinedoutputs[ui]];
runcommand(root, strings.concat("vendor-combined-run-", stages[si],
"-", labels[ui]), cav,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, combinedcodes[ui]);
ui += 1;
};
// A target interned by the allowed product is still checked at the
// forbidden source edge. Reversing product descriptors changes neither
// the diagnostic nor any status/tool state.
let orderwork: str = strings.concat(root, "/vendor-order-work-",
stages[si]);
assert(os.mkdir(orderwork, 448i32) == 0);
let orderallowed: str = strings.concat(root, "/vendor-order-allowed-",
stages[si]);
let orderforbidden: str = strings.concat(root,
"/vendor-order-forbidden-", stages[si]);
let orderallowedstatus: str = strings.concat(orderallowed, ".status");
let orderforbiddenstatus: str = strings.concat(orderforbidden,
".status");
writefile(orderallowedstatus, "sentinel-allowed\n");
writefile(orderforbiddenstatus, "sentinel-forbidden\n");
let orderforward: []str = [driver(stages[si]), "test", "-c", "-w",
orderwork, "-I", source,
"--ww-package-test", "production", "main", client,
orderallowed, orderallowedstatus,
"--ww-package-test", "production", "main", outsider,
orderforbidden, orderforbiddenstatus, client];
let orderreverse: []str = [driver(stages[si]), "test", "-c", "-w",
orderwork, "-I", source,
"--ww-package-test", "production", "main", outsider,
orderforbidden, orderforbiddenstatus,
"--ww-package-test", "production", "main", client,
orderallowed, orderallowedstatus, client];
let orderrequests: [][]str = [orderforward, orderreverse];
let orderdiags: []str = ["", ""];
let oi: i32 = 0;
for (oi < orderrequests.len) {
rewritefile(compilertraces[si], "");
rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
runcommandenv(root, strings.concat("vendor-order-", stages[si],
"-", labels[oi]), orderrequests[oi], env,
(180i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(same(out.stderr, outsidediag));
orderdiags[oi] = strings.dup(out.stderr);
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
assert(readfile(linkertraces[si]).len == 0);
assert(same(readfile(orderallowedstatus), "sentinel-allowed\n"));
assert(same(readfile(orderforbiddenstatus),
"sentinel-forbidden\n"));
assert(!os.exists(strings.concat(orderwork, "/.wwtool.stamp")));
assert(!os.exists(orderallowed) && !os.exists(orderforbidden));
oi += 1;
};
assert(same(orderdiags[0], orderdiags[1]));
if (si == 0) { referenceorderdiag = strings.dup(orderdiags[0]); }
else { assert(same(referenceorderdiag, orderdiags[0])); };
// Production, same-package test, and external test sources all retain
// their short spelling while consuming the one expanded direct export.
let testworkroot: str = strings.concat(root, "/vendor-test-work-",
stages[si]);
assert(os.mkdir(testworkroot, 448i32) == 0);
rewritefile(compilertraces[si], "");
rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
let testav: []str = [driver(stages[si]), "test", "-w", testworkroot,
"-I", source, testclient];
runcommandenv(root, strings.concat("vendor-tests-", stages[si]), testav,
env, (180i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(has(out.stdout, "same_vendor ... ok\n"));
assert(has(out.stdout, "external_vendor ... ok\n"));
if (si == 0) {
referencetestout = strings.dup(out.stdout);
referencetesterr = strings.dup(out.stderr);
} else {
assert(same(referencetestout, out.stdout));
assert(same(referencetesterr, out.stderr));
};
let testwork: str = strings.concat(testworkroot, "/",
requestworkkey(testclient), "/");
let testactions: []str = ["domain.app.testclient-internal-test",
"domain.app.testclient_test-external-test"];
let testsuffixes: []str = [".unit.ww", ".wwi", ".a"];
ui = 0;
for (ui < testactions.len) {
let xi: i32 = 0;
for (xi < testsuffixes.len) {
let bytes: str = readfile(strings.concat(testwork,
testactions[ui], testsuffixes[xi]));
let index: i32 = ui * testsuffixes.len + xi;
if (si == 0) { testrefs[index] = strings.dup(bytes); }
else { assert(same(testrefs[index], bytes)); };
xi += 1;
};
assert(has(readfile(strings.concat(testwork, testactions[ui],
".unit.ww")), strings.concat("//ww:import-map lib.math ",
expanded, " ", hexbytes(nearest), "\n")));
ui += 1;
};
let testtrace: str = readfile(compilertraces[si]);
assert(occurrences(testtrace, strings.concat("/", expanded,
".unit.new")) == 1);
let internalline: str = linecontaining(testtrace,
"domain.app.testclient-internal-test.unit.new");
let externalline: str = linecontaining(testtrace,
"domain.app.testclient_test-external-test.unit.new");
assert(occurrences(internalline, strings.concat("<--import><", expanded,
"><", testwork, expanded, ".wwi>")) == 1);
assert(occurrences(externalline, strings.concat("<--import><", expanded,
"><", testwork, expanded, ".wwi>")) == 1);
assert(occurrences(internalline, strings.concat(
"<--import-map><lib.math><", expanded, ">")) == 1);
assert(occurrences(externalline, strings.concat(
"<--import-map><lib.math><", expanded, ">")) == 1);
assert(!has(internalline, "lib.leaf.wwi"));
assert(!has(externalline, "lib.leaf.wwi"));
let internalmainline: str = linecontaining(testtrace,
"domain.app.testclient-internal-test-main.unit.new");
let externalmainline: str = linecontaining(testtrace,
"domain.app.testclient_test-external-test-main.unit.new");
assert(!has(internalmainline, expanded));
assert(!has(externalmainline, expanded));
assert(!has(readfile(linkertraces[si]), ".wwi>"));
rewritefile(compilertraces[si], "");
rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
runcommandenv(root, strings.concat("vendor-tests-warm-", stages[si]),
testav, env, (180i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(same(referencetestout, out.stdout));
assert(same(referencetesterr, out.stderr));
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
// A directly selected vendored test root is legal, and its external
// source still performs the ordinary nearest-vendor search first.
rewritefile(compilertraces[si], "");
rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
let vendortestav: []str = [driver(stages[si]), "test", "-I", source,
vendortest];
runcommandenv(root, strings.concat("vendor-literal-test-", stages[si]),
vendortestav, env,
(180i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(has(out.stdout, "nested_external_vendor ... ok\n"));
if (si == 0) {
referencevendortestout = strings.dup(out.stdout);
referencevendortesterr = strings.dup(out.stderr);
} else {
assert(same(referencevendortestout, out.stdout));
assert(same(referencevendortesterr, out.stderr));
};
assert(has(readfile(compilertraces[si]),
"literal.owner.vendor.lib.math.vendor.lib.math.unit.ww"));
// A cold forbidden public test request is rejected before the
// coordinator leaves its hashed persistent request directory behind.
let testbadworkroot: str = strings.concat(root, "/vendor-test-bad-work-",
stages[si]);
assert(os.mkdir(testbadworkroot, 448i32) == 0);
rewritefile(compilertraces[si], "");
rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
let testbadav: []str = [driver(stages[si]), "test", "-w",
testbadworkroot, "-I", source, testbad];
runcommandenv(root, strings.concat("vendor-test-bad-", stages[si]),
testbadav, env, (180i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
let testbaddiag: str = strings.concat(testbad,
"/same_test.ww:2:1: error: use of vendored package not allowed\n");
assert(has(out.stderr, testbaddiag));
if (si == 0) { referencetestbaddiag = strings.dup(out.stderr); }
else { assert(same(referencetestbaddiag, out.stderr)); };
assert(!os.exists(strings.concat(testbadworkroot, "/",
requestworkkey(testbad))));
assert(readfile(compilertraces[si]).len == 0);
assert(readfile(assemblertraces[si]).len == 0);
assert(readfile(linkertraces[si]).len == 0);
// The coordinator's synthetic support/main edges stay synthetic, while
// imports written in the real test-support source use this same resolver.
let supportenv: []str = alloc([], (env.len + 1): u64)!;
ei = 0;
for (ei < env.len) { append(supportenv, env[ei]); ei += 1; };
append(supportenv, strings.concat("WW_SRCLIB=", toolsource));
let supportworkroot: str = strings.concat(root, "/vendor-support-work-",
stages[si]);
assert(os.mkdir(supportworkroot, 448i32) == 0);
rewritefile(compilertraces[si], "");
rewritefile(assemblertraces[si], "");
rewritefile(linkertraces[si], "");
let supportav: []str = [driver(stages[si]), "test", "-w",
supportworkroot, "-I", source, supportcase];
runcommandenv(root, strings.concat("vendor-support-", stages[si]),
supportav, supportenv,
(240i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(has(out.stdout, "support_runs ... ok\n"));
assert(out.stderr.len == 0);
if (si == 0) { referencesupportout = strings.dup(out.stdout); }
else { assert(same(referencesupportout, out.stdout)); };
let supportwork: str = strings.concat(supportworkroot, "/",
requestworkkey(supportcase), "/");
let supportunit: str = readfile(strings.concat(supportwork,
"test.unit.ww"));
assert(has(supportunit, strings.concat("//ww:import-map fnmatch ",
"test.vendor.fnmatch ", hexbytes(toolvendor), "\n")));
assert(has(readfile(strings.concat(supportwork,
"test.vendor.fnmatch.unit.ww")), strings.concat(
"//ww:vendor-dir ", hexbytes(toolvendor), "\n")));
let supporttrace: str = readfile(compilertraces[si]);
let supportline: str = linecontaining(supporttrace, "/test.unit.new");
assert(occurrences(supportline,
"<--import><test.vendor.fnmatch>") == 1);
assert(occurrences(supportline,
"<--import-map><fnmatch><test.vendor.fnmatch>") == 1);
assert(!has(supportline, "/ascii.wwi>"));
assert(occurrences(supporttrace,
"/test.vendor.fnmatch.unit.new") == 1);
let supportmainline: str = linecontaining(supporttrace,
"supportcase-internal-test-main.unit.new");
assert(!has(supportmainline, "test.vendor.fnmatch"));
assert(!has(readfile(linkertraces[si]), ".wwi>"));
let supportsuffixes: []str = [".unit.ww", ".wwi", ".a"];
ui = 0;
for (ui < supportsuffixes.len) {
let bytes: str = readfile(strings.concat(supportwork,
"test.vendor.fnmatch", supportsuffixes[ui]));
if (si == 0) { supportrefs[ui] = strings.dup(bytes); }
else { assert(same(supportrefs[ui], bytes)); };
ui += 1;
};
si += 1;
};
clean(root);
};
// The package-level -o contract: -c -o names the single package's
// artifact in place of the fixed <package>.test stem; -o without -c
// has nothing to name (runs execute from the temp root); one name
// cannot fan out over multiple packages (Go's `go test -o` rule).