test: prove declared package import semantics

This commit is contained in:
2026-08-14 03:08:16 +09:00
parent 6efe9b70d4
commit a841278333
11 changed files with 1001 additions and 131 deletions

View File

@@ -319,8 +319,8 @@ fn workescape(s: str) str = {
return strings.frombytes(out);
};
fn localidentity(dir: str, leaf: str) str = {
let out: []u8 = alloc([], (dir.len * 4 + leaf.len + 16): u64)!;
fn localidentity(dir: str, declaredname: str) str = {
let out: []u8 = alloc([], (dir.len * 4 + 16): u64)!;
let prefix: str = "__wwlocal.p";
let i: i32 = 0;
for (i < prefix.len) { append(out, prefix[i]); i += 1; };
@@ -343,9 +343,6 @@ fn localidentity(dir: str, leaf: str) str = {
};
i += 1;
};
append(out, '.');
i = 0;
for (i < leaf.len) { append(out, leaf[i]); i += 1; };
return strings.frombytes(out);
};
@@ -1241,14 +1238,16 @@ fn hexbytes(value: str) str = {
let externalmaincompile: str = linecontaining(ctrace,
"pkg_test-external-test-main.unit.ww");
assert(same(samemaincompile, strings.concat(
"-T --entry --test-support-module test -c --import pkg ",
"-T --entry --test-support-module test ",
"--test-target-package pkg -c --import pkg ",
sharedwork, "pkg-internal-test.wwi --import test ",
sharedwork, "test.wwi -I ", sharedwork,
"pkg-internal-test-main.wwi -o ", sharedwork,
"pkg-internal-test-main.s ", sharedwork,
"pkg-internal-test-main.unit.ww")));
assert(same(externalmaincompile, strings.concat(
"-T --entry --test-support-module test -c --import pkg_test ",
"-T --entry --test-support-module test ",
"--test-target-package pkg_test -c --import pkg_test ",
sharedwork, "pkg_test-external-test.wwi --import test ",
sharedwork, "test.wwi -I ", sharedwork,
"pkg_test-external-test-main.wwi -o ", sharedwork,
@@ -1495,9 +1494,9 @@ fn hexbytes(value: str) str = {
aliasunit];
runcommand(root, strings.concat("alias-unowned-", aliasstages[i]),
rawav, (30i64 * (time.second: i64)): time.duration, &out);
assert(out.termination == exec.termination.EXIT && out.code != 0);
assert(has(out.stderr, "package")
&& has(out.stderr, "does not match import path"));
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
assert(os.exists(asmout));
let badav: []str = [driver(aliasstages[i]), "-c",
"--test-support-module", "arbitrary", "-o", asmout, aliasunit];
runcommand(root, strings.concat("alias-arbitrary-", aliasstages[i]),
@@ -4226,9 +4225,13 @@ fn hexbytes(value: str) str = {
let leftbody: str = strings.concat(
"package parityleft;\n",
"import ", baseidentity, ";\n",
"import ", baseidentity, ";\n",
"export fn value() i32 = { return ",
"paritybase.value(); };\n");
let leftz: str = strings.concat(
"package parityleft;\n",
"import ", baseidentity, ";\n",
"export fn repeated_binding() i32 = { return ",
"paritybase.value(); };\n");
let rightbody: str = strings.concat(
"package parityright;\n",
"import ", baseidentity, ";\n",
@@ -4242,7 +4245,8 @@ fn hexbytes(value: str) str = {
"parityright.value() + 1; };\n");
writefile(strings.concat(base, "/a.ww"), basea);
writefile(strings.concat(base, "/z.ww"), basez);
writefile(strings.concat(left, "/left.ww"), leftbody);
writefile(strings.concat(left, "/a.ww"), leftbody);
writefile(strings.concat(left, "/z.ww"), leftz);
writefile(strings.concat(right, "/right.ww"), rightbody);
writefile(strings.concat(target, "/main.ww"), rootbody);
@@ -4316,7 +4320,8 @@ fn hexbytes(value: str) str = {
"//ww:module-reset ", baseidentity, "\n", basea,
"\n//ww:module-reset ", baseidentity, "\n", basez, "\n"),
strings.concat("//ww:module-reset ", leftidentity, "\n",
leftbody, "\n"),
leftbody, "\n//ww:module-reset ", leftidentity, "\n",
leftz, "\n"),
strings.concat("//ww:module-reset ", rightidentity, "\n",
rightbody, "\n"),
strings.concat("//ww:module-reset ", rootidentity, "\n", rootbody,
@@ -4425,9 +4430,8 @@ fn hexbytes(value: str) str = {
assert(has(assembly, strings.concat(identities[ai], ".value")));
};
if (ai == 3) {
assert(has(exportf, strings.concat("\npackage paritycommand",
";\n")));
assert(!has(exportf, "\npackage main;\n"));
assert(has(exportf, "\npackage main;\n"));
assert(!has(exportf, "\npackage paritycommand;\n"));
};
if (si == 0) {
referenceexports[ai] = strings.dup(exportf);
@@ -4476,18 +4480,23 @@ fn hexbytes(value: str) str = {
assert(same(linecontaining(ctrace, strings.concat(artifacts[3],
".unit.ww")), rootline));
assert(!has(rootline, strings.concat(artifacts[0], ".wwi")));
// The primary command declaration is accepted only when the compiler
// receives command classification from --entry. Imported interfaces and
// ordinary package compiles retain strict identity-leaf validation.
// Canonical owner and declared name stay independent even in a raw
// package compile. --entry selects link-root code generation; it is not
// permission to replace canonical owner with the declared name.
let rejectwwi: str = strings.concat(root, "/reject-", tags[si], ".wwi");
let rejectasm: str = strings.concat(root, "/reject-", tags[si], ".s");
let rejectav: []str = [driver(compilers[si]), "-c", "-I", rejectwwi,
"-o", rejectasm, strings.concat(work, artifacts[3], ".unit.ww")];
let rejectav: []str = [driver(compilers[si]), "-c", "--import",
leftidentity,
strings.concat(work, artifacts[1], ".wwi"),
"--import", rightidentity,
strings.concat(work, artifacts[2], ".wwi"),
"-I", rejectwwi, "-o", rejectasm,
strings.concat(work, artifacts[3], ".unit.ww")];
runcommand(root, strings.concat("command-without-entry-", tags[si]),
rejectav, (60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "does not match import path"));
assert(has(out.stderr, rootidentity));
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
assert(os.exists(rejectwwi) && os.exists(rejectasm));
let ownerrejectwwi: str = strings.concat(root, "/owner-reject-",
tags[si], ".wwi");
let ownerrejectasm: str = strings.concat(root, "/owner-reject-",
@@ -4726,11 +4735,11 @@ fn hexbytes(value: str) str = {
"package main_test;\n",
"import ", commandid, ";\n",
"@test fn command_external() void = { ",
"assert(cmdtool.value() == 41); };\n"));
"assert(main.value() == 41); };\n"));
writefile(strings.concat(importer, "/importer.ww"), strings.concat(
"package importer;\n",
"import ", commandid, ";\n",
"export fn value() i32 = { return cmdtool.value(); };\n"));
"export fn value() i32 = { return main.value(); };\n"));
writeexecutable(wrapper, strings.concat(
"#!/bin/sh\n",
"printf 'BEGIN' >> \"$WW_COMMAND_COMPILER_TRACE\"\n",
@@ -4830,8 +4839,12 @@ fn hexbytes(value: str) str = {
strings.concat(artifacts[4], ".unit.new"));
assert(has(internalmainline, strings.concat("<--import><", internalid,
"><", work, artifacts[1], ".wwi>")));
assert(has(internalmainline, strings.concat(
"<--test-target-package><", internalid, ">")));
assert(has(externalmainline, strings.concat("<--import><", externalid,
"><", work, artifacts[2], ".wwi>")));
assert(has(externalmainline, strings.concat(
"<--test-target-package><", externalid, ">")));
assert(occurrences(ctrace, strings.concat("<", work, artifacts[0],
".unit.new>")) == 1);
assert(has(readfile(strings.concat(work, artifacts[0], ".s")),
@@ -4870,7 +4883,7 @@ fn hexbytes(value: str) str = {
};
// The same command action is not generally source-importable. It is
// classified before leaf validation so this Go-like diagnostic is stable,
// classified from its declaration so this Go-like diagnostic is stable,
// and graph failure occurs before any compiler invocation.
clean(trace);
writefile(trace, "");
@@ -7844,6 +7857,547 @@ fn runtimepath(relative: str) str = {
// 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).
@test fn declared_name_identity_and_file_import_scope() void = {
let root: str = fresh();
let source: str = strings.concat(root, "/source");
let tools: str = strings.concat(root, "/tools");
let codec: str = strings.concat(source, "/acme/codec");
let bridge: str = strings.concat(source, "/acme/bridge");
let direct: str = strings.concat(source, "/cmd/direct");
let app: str = strings.concat(source, "/cmd/app");
let scopeone: str = strings.concat(source, "/scope/one");
let scopetwo: str = strings.concat(source, "/scope/two");
let scoped: str = strings.concat(source, "/cmd/scoped");
let repeated: str = strings.concat(source, "/cmd/repeated");
let leafbad: str = strings.concat(source, "/cmd/leafbad");
let leak: str = strings.concat(source, "/cmd/leak");
let duplicate: str = strings.concat(source, "/cmd/duplicate");
let collision: str = strings.concat(source, "/cmd/collision");
let crosscollision: str = strings.concat(source, "/cmd/crosscollision");
let conflict: str = strings.concat(source, "/conflict/pkg");
let leafmain: str = strings.concat(source, "/domain/main");
let program: str = strings.concat(source, "/domain/program");
let leafmainuser: str = strings.concat(source, "/cmd/leafmain");
let programuser: str = strings.concat(source, "/cmd/programuser");
let testcodec: str = strings.concat(source, "/testpkg/codec");
let testhelper: str = strings.concat(source, "/testonly/helper");
let vendored: str = strings.concat(source,
"/vend/vendor/short/codec");
let vendorclient: str = strings.concat(source, "/vend/client");
let dirs: []str = [source, tools, codec, bridge, direct, app, scopeone,
scopetwo, scoped, repeated, leafbad, leak, duplicate, collision,
crosscollision, conflict, leafmain, program, leafmainuser, programuser, testcodec,
testhelper, vendored, vendorclient];
let di: i32 = 0;
for (di < dirs.len) { mkdirall(dirs[di]); di += 1; };
let wirebody: str = strings.concat(
"package wire;\n",
"export fn value() i32 = { return 42; };\n");
let cablebody: str = strings.concat(
"package cable;\n",
"export fn value() i32 = { return 42; };\n");
let codecfile: str = strings.concat(codec, "/codec.ww");
writefile(codecfile, wirebody);
writefile(strings.concat(bridge, "/bridge.ww"), strings.concat(
"package bridge;\nimport acme.codec;\n",
// WW retains its direct-import bare-declaration convenience. It is
// useful here because changing only the dependency declaration leaves
// this source unchanged while still forcing its action to reconsider.
"export fn bridged() i32 = { return value(); };\n"));
writefile(strings.concat(direct, "/main.ww"), strings.concat(
"package main;\nimport acme.codec;\n",
"fn main() i32 = { return wire.value(); };\n"));
writefile(strings.concat(app, "/main.ww"), strings.concat(
"package main;\nimport acme.bridge;\n",
"fn main() i32 = { return bridge.bridged(); };\n"));
writefile(strings.concat(scopeone, "/one.ww"),
"package same;\nexport fn value() i32 = { return 20; };\n");
writefile(strings.concat(scopetwo, "/two.ww"),
"package same;\nexport fn value() i32 = { return 22; };\n");
writefile(strings.concat(scoped, "/a.ww"), strings.concat(
"package main;\nimport scope.one;\n",
"fn left() i32 = { return same.value(); };\n"));
writefile(strings.concat(scoped, "/b.ww"), strings.concat(
"package main;\nimport scope.two;\n",
"fn right() i32 = { return same.value(); };\n"));
writefile(strings.concat(scoped, "/main.ww"),
"package main;\nfn main() i32 = { return left() + right(); };\n");
writefile(strings.concat(repeated, "/a.ww"), strings.concat(
"package main;\nimport acme.codec;\n",
"fn first() i32 = { return wire.value(); };\n"));
writefile(strings.concat(repeated, "/b.ww"), strings.concat(
"package main;\nimport acme.codec;\n",
"fn second() i32 = { return wire.value(); };\n"));
writefile(strings.concat(repeated, "/main.ww"), strings.concat(
"package main;\n",
"fn main() i32 = { return first() - second(); };\n"));
writefile(strings.concat(leafbad, "/main.ww"), strings.concat(
"package main;\nimport acme.codec;\n",
"fn main() i32 = { return codec.value(); };\n"));
writefile(strings.concat(leak, "/a.ww"), strings.concat(
"package main;\nimport acme.codec;\n",
"fn anchor() i32 = { return 1; };\n"));
writefile(strings.concat(leak, "/b.ww"),
"package main;\nfn leaked() i32 = { return wire.value(); };\n");
writefile(strings.concat(leak, "/main.ww"),
"package main;\nfn main() i32 = { return leaked(); };\n");
writefile(strings.concat(duplicate, "/main.ww"), strings.concat(
"package main;\nimport scope.one;\nimport scope.two;\n",
"fn main() i32 = { return same.value(); };\n"));
writefile(strings.concat(collision, "/main.ww"), strings.concat(
"package main;\nimport scope.one;\n",
"fn same() i32 = { return 0; };\n",
"fn main() i32 = { return same(); };\n"));
writefile(strings.concat(crosscollision, "/a.ww"), strings.concat(
"package main;\nimport scope.one;\n",
"fn fromdep() i32 = { return same.value(); };\n"));
writefile(strings.concat(crosscollision, "/b.ww"), strings.concat(
"package main;\nfn same() i32 = { return 0; };\n",
"fn main() i32 = { return fromdep(); };\n"));
writefile(strings.concat(conflict, "/a.ww"),
"package first;\nexport fn a() i32 = { return 1; };\n");
writefile(strings.concat(conflict, "/b.ww"),
"package second;\nexport fn b() i32 = { return 2; };\n");
writefile(strings.concat(leafmain, "/utility.ww"),
"package utility;\nexport fn value() i32 = { return 7; };\n");
writefile(strings.concat(program, "/program.ww"),
"package main;\nexport fn value() i32 = { return 9; };\n");
writefile(strings.concat(leafmainuser, "/main.ww"), strings.concat(
"package main;\nimport domain.main;\n",
"fn main() i32 = { return utility.value(); };\n"));
writefile(strings.concat(programuser, "/main.ww"), strings.concat(
"package main;\nimport domain.program;\n",
"fn main() i32 = { return main.value(); };\n"));
writefile(strings.concat(testhelper, "/helper.ww"),
"package helper;\nexport fn value() i32 = { return 1; };\n");
writefile(strings.concat(testcodec, "/codec.ww"), wirebody);
writefile(strings.concat(testcodec, "/internal_test.ww"), strings.concat(
"package wire;\nimport testonly.helper;\n",
"@test fn internal_name_uses_declaration() void = {\n",
" assert(value() + helper.value() == 43);\n};\n"));
writefile(strings.concat(testcodec, "/external_test.ww"), strings.concat(
"package wire_test;\nimport testpkg.codec;\n",
"@test fn external_name_uses_declaration() void = {\n",
" assert(wire.value() == 42);\n};\n"));
writefile(strings.concat(vendored, "/codec.ww"),
"package cable;\nexport fn value() i32 = { return 13; };\n");
writefile(strings.concat(vendorclient, "/a.ww"), strings.concat(
"package main;\nimport short.codec;\n",
"fn vendora() i32 = { return cable.value(); };\n"));
writefile(strings.concat(vendorclient, "/b.ww"), strings.concat(
"package main;\nimport short.codec;\n",
"fn vendorb() i32 = { return cable.value(); };\n"));
writefile(strings.concat(vendorclient, "/main.ww"),
"package main;\nfn main() i32 = { return vendora(); };\n");
let compilerwrapper: str = strings.concat(tools, "/w6c.sh");
writeexecutable(compilerwrapper, strings.concat(
"#!/bin/sh\n",
"printf 'BEGIN' >> \"$WW_NAME_COMPILER_TRACE\"\n",
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
"\"$WW_NAME_COMPILER_TRACE\"; done\n",
"printf '\\n' >> \"$WW_NAME_COMPILER_TRACE\"\n",
"exec \"$WW_NAME_REAL_COMPILER\" \"$@\"\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 traces: []str = [strings.concat(root, "/c-compiler"),
strings.concat(root, "/ww-compiler")];
let directbins: []str = [strings.concat(root, "/c-direct"),
strings.concat(root, "/ww-direct")];
let appbins: []str = [strings.concat(root, "/c-app"),
strings.concat(root, "/ww-app")];
let scopedbins: []str = [strings.concat(root, "/c-scoped"),
strings.concat(root, "/ww-scoped")];
let repeatedbins: []str = [strings.concat(root, "/c-repeated"),
strings.concat(root, "/ww-repeated")];
let leafmainbins: []str = [strings.concat(root, "/c-leafmain"),
strings.concat(root, "/ww-leafmain")];
let vendorbins: []str = [strings.concat(root, "/c-vendor"),
strings.concat(root, "/ww-vendor")];
let actions: []str = ["acme.codec", "acme.bridge", "cmd.direct",
"cmd.app", "scope.one", "scope.two", "cmd.scoped",
"cmd.repeated", "domain.main", "cmd.leafmain",
"vend.vendor.short.codec", "vend.client"];
let suffixes: []str = [".unit.ww", ".wwi", ".a"];
let artifactrefs: []str = alloc([], (actions.len * suffixes.len): u64)!;
let ari: i32 = 0;
for (ari < actions.len * suffixes.len) {
append(artifactrefs, "");
ari += 1;
};
let binaryrefs: []str = ["", "", "", "", "", ""];
let diagnosticrefs: []str = ["", "", "", "", "", ""];
let directtraceref: str = "";
let scopedtraceref: str = "";
let repeatedtraceref: str = "";
let changedtraceref: str = "";
let initialwwiref: str = "";
let testoutref: str = "";
let baseenv: []str = os.getenvs();
let si: i32 = 0;
for (si < stages.len) {
rewritefile(codecfile, wirebody);
assert(os.mkdir(works[si], 448i32) == 0);
writefile(traces[si], "");
let env: []str = alloc([], (baseenv.len + 7): 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_SRCLIB=")
&& !strings.hasprefix(baseenv[ei],
"WW_NAME_COMPILER_TRACE=")
&& !strings.hasprefix(baseenv[ei],
"WW_NAME_REAL_COMPILER=")) {
append(env, baseenv[ei]);
};
ei += 1;
};
append(env, strings.concat("WW_W6C=", compilerwrapper));
append(env, strings.concat("WW_W6A=", driver(assemblers[si])));
append(env, strings.concat("WW_W6L=", driver(linkers[si])));
append(env, strings.concat("WW_SRCLIB=", repo(), "/lib"));
append(env, strings.concat("WW_NAME_COMPILER_TRACE=", traces[si]));
append(env, strings.concat("WW_NAME_REAL_COMPILER=",
driver(compilers[si])));
// A directly selected library keeps canonical action/artifact identity
// even though its declaration is unrelated to the path leaf.
let out: commandout;
let selectav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, codec];
runcommandenv(root, strings.concat("name-select-", stages[si]),
selectav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
assert(os.exists(strings.concat(works[si], "/acme.codec.wwi")));
assert(!os.exists(strings.concat(works[si], "/wire.wwi")));
let initialwwi: str = readfile(strings.concat(works[si],
"/acme.codec.wwi"));
assert(has(initialwwi, "//ww:module acme.codec\npackage wire;\n"));
if (si == 0) { initialwwiref = strings.dup(initialwwi); }
else { assert(same(initialwwiref, initialwwi)); };
rewritefile(traces[si], "");
let directav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", directbins[si], direct];
runcommandenv(root, strings.concat("name-direct-", stages[si]),
directav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0);
let directtrace: str = readfile(traces[si]);
let directline: str = linecontaining(directtrace,
"cmd.direct.unit.new");
assert(occurrences(directline, "<--import><acme.codec>") == 1);
assert(!has(directline, "wire.wwi"));
let ndirect: str = normalizedtrace(directtrace,
strings.concat(works[si], "/"), directbins[si]);
if (si == 0) { directtraceref = strings.dup(ndirect); }
else { assert(same(directtraceref, ndirect)); };
let runav: []str = [directbins[si]];
runcommand(root, strings.concat("name-direct-run-", stages[si]), runav,
(30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
// The same declared qualifier belongs to each importing source file,
// not to the package-wide namespace.
rewritefile(traces[si], "");
let scopedav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", scopedbins[si], scoped];
runcommandenv(root, strings.concat("name-scoped-", stages[si]),
scopedav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let scopedtrace: str = readfile(traces[si]);
let scopedline: str = linecontaining(scopedtrace,
"cmd.scoped.unit.new");
assert(occurrences(scopedline, "<--import><scope.one>") == 1);
assert(occurrences(scopedline, "<--import><scope.two>") == 1);
let nscoped: str = normalizedtrace(scopedtrace,
strings.concat(works[si], "/"), scopedbins[si]);
if (si == 0) { scopedtraceref = strings.dup(nscoped); }
else { assert(same(scopedtraceref, nscoped)); };
let scopedrun: []str = [scopedbins[si]];
runcommand(root, strings.concat("name-scoped-run-", stages[si]),
scopedrun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
// Two per-file binding records for one canonical package still form
// exactly one direct graph edge and one compiler input.
rewritefile(traces[si], "");
let repeatedav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", repeatedbins[si], repeated];
runcommandenv(root, strings.concat("name-repeated-", stages[si]),
repeatedav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let repeatedunit: str = readfile(strings.concat(works[si],
"/cmd.repeated.unit.ww"));
assert(occurrences(repeatedunit, "import acme.codec;") == 2);
let repeatedtrace: str = readfile(traces[si]);
let repeatedline: str = linecontaining(repeatedtrace,
"cmd.repeated.unit.new");
assert(occurrences(repeatedline, "<--import><acme.codec>") == 1);
assert(occurrences(repeatedtrace, "acme.codec.unit.new") == 0);
let nrepeated: str = normalizedtrace(repeatedtrace,
strings.concat(works[si], "/"), repeatedbins[si]);
if (si == 0) { repeatedtraceref = strings.dup(nrepeated); }
else { assert(same(repeatedtraceref, nrepeated)); };
let repeatedrun: []str = [repeatedbins[si]];
runcommand(root, strings.concat("name-repeated-run-", stages[si]),
repeatedrun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
// A path ending in main remains importable when its declaration is not
// main. A different path declaring main is rejected by loaded name.
let leafmainav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", leafmainbins[si], leafmainuser];
runcommandenv(root, strings.concat("name-leafmain-", stages[si]),
leafmainav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let leafmainrun: []str = [leafmainbins[si]];
runcommand(root, strings.concat("name-leafmain-run-", stages[si]),
leafmainrun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 7);
// Compiler-owned binding failures are deterministic across stages and
// never commit an action voucher/artifact or publish a binary.
let rejectwork: str = strings.concat(root, "/reject-work-", stages[si]);
assert(os.mkdir(rejectwork, 448i32) == 0);
let rejecttargets: []str = [leafbad, leak, duplicate, collision,
crosscollision, programuser];
let rejectactions: []str = ["cmd.leafbad", "cmd.leak",
"cmd.duplicate", "cmd.collision", "cmd.crosscollision",
"cmd.programuser"];
let rejectneedles: []str = ["undefined: codec",
"package 'wire' is not directly imported",
"same redeclared in this block",
"same already declared through import of package same",
"same already declared through import of package same",
"package domain.program is a program, not an importable package"];
let rj: i32 = 0;
for (rj < rejecttargets.len) {
let badbin: str = strings.concat(root, "/reject-bin-", stages[si],
"-", rejectactions[rj]);
let badav: []str = [driver(stages[si]), "build", "-w", rejectwork,
"-I", source, "-o", badbin, rejecttargets[rj]];
runcommandenv(root, strings.concat("name-reject-", stages[si],
"-", rejectactions[rj]), badav, env,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, rejectneedles[rj]));
let normalized: str = normalizedtrace(out.stderr,
strings.concat(rejectwork, "/"), badbin);
if (rj == 2) {
assert(same(normalized, strings.concat(
"$WORK/cmd.duplicate.unit.new:4:1: error: same redeclared in this block\n",
"\t$WORK/cmd.duplicate.unit.new:3:1: other declaration of same\n",
"$WORK/cmd.duplicate.unit.new:4:1: error: \"scope.two\" imported as same and not used\n",
"ww: w6c failed for cmd.duplicate\n")));
};
if (rj == 3) {
assert(same(normalized, strings.concat(
"$WORK/cmd.collision.unit.new:3:1: error: \"scope.one\" imported as same and not used\n",
"$WORK/cmd.collision.unit.new:4:1: error: same already declared through import of package same (\"scope.one\")\n",
"\t$WORK/cmd.collision.unit.new:3:1: other declaration of same\n",
"ww: w6c failed for cmd.collision\n")));
};
if (si == 0) { diagnosticrefs[rj] = strings.dup(normalized); }
else { assert(same(diagnosticrefs[rj], normalized)); };
assert(!os.exists(badbin));
let committed: []str = [".unit.ww", ".wwi", ".s", ".o", ".a",
".unit.new", ".wwi.new", ".s.new", ".o.new", ".a.new"];
let ci: i32 = 0;
for (ci < committed.len) {
assert(!os.exists(strings.concat(rejectwork, "/",
rejectactions[rj], committed[ci])));
ci += 1;
};
rj += 1;
};
let conflictav: []str = [driver(stages[si]), "build", "-w",
rejectwork, "-I", source, conflict];
runcommandenv(root, strings.concat("name-conflict-", stages[si]),
conflictav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, "conflicting package names"));
assert(!os.exists(strings.concat(rejectwork,
"/conflict.pkg.unit.ww")));
// Production/internal/external naming uses the declaration, while every
// action and generated-main artifact retains canonical variant identity.
let testwork: str = strings.concat(root, "/test-work-", stages[si]);
assert(os.mkdir(testwork, 448i32) == 0);
rewritefile(traces[si], "");
let testav: []str = [driver(stages[si]), "test", "-w", testwork,
"-I", source, testcodec];
runcommandenv(root, strings.concat("name-test-", stages[si]), testav,
env, (240i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(has(out.stdout, "internal_name_uses_declaration ... ok\n"));
assert(has(out.stdout, "external_name_uses_declaration ... ok\n"));
assert(out.stderr.len == 0);
if (si == 0) { testoutref = strings.dup(out.stdout); }
else { assert(same(testoutref, out.stdout)); };
let productionunit: str = readfile(strings.concat(testwork,
"/testpkg.codec.unit.ww"));
let internalunit: str = readfile(strings.concat(testwork,
"/testpkg.codec-internal-test.unit.ww"));
let externalunit: str = readfile(strings.concat(testwork,
"/testpkg.codec_test-external-test.unit.ww"));
assert(!has(productionunit, "testonly.helper"));
assert(has(internalunit, "import testonly.helper;"));
assert(!has(externalunit, "testonly.helper"));
assert(has(externalunit, "package wire_test;"));
assert(os.exists(strings.concat(testwork,
"/testpkg.codec-internal-test-main.a")));
assert(os.exists(strings.concat(testwork,
"/testpkg.codec_test-external-test-main.a")));
assert(os.exists(strings.concat(testwork, "/test.a")));
assert(!has(readfile(strings.concat(testwork,
"/testpkg.codec-internal-test-main.unit.ww")), "testonly.helper"));
assert(!has(readfile(strings.concat(testwork,
"/testpkg.codec_test-external-test-main.unit.ww")),
"testonly.helper"));
let recursivebuildwork: str = strings.concat(root,
"/recursive-build-", stages[si]);
assert(os.mkdir(recursivebuildwork, 448i32) == 0);
let recursivebuildav: []str = [driver(stages[si]), "build", "-w",
recursivebuildwork, "-I", source,
strings.concat(source, "/testpkg/...")];
runcommandenv(root, strings.concat("name-recursive-build-", stages[si]),
recursivebuildav, env,
(180i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(os.exists(strings.concat(recursivebuildwork,
"/testpkg.codec.wwi")));
let recursivetestwork: str = strings.concat(root,
"/recursive-test-", stages[si]);
assert(os.mkdir(recursivetestwork, 448i32) == 0);
let recursivetestav: []str = [driver(stages[si]), "test", "-w",
recursivetestwork, "-I", source,
strings.concat(source, "/testpkg/...")];
runcommandenv(root, strings.concat("name-recursive-test-", stages[si]),
recursivetestav, env,
(240i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(has(out.stdout, "external_name_uses_declaration ... ok\n"));
// Vendor expansion owns the action/map identity; the loaded declaration
// independently supplies cable as the source-visible qualifier.
rewritefile(traces[si], "");
let vendorav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", vendorbins[si], vendorclient];
runcommandenv(root, strings.concat("name-vendor-", stages[si]),
vendorav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let vendorunit: str = readfile(strings.concat(works[si],
"/vend.client.unit.ww"));
assert(occurrences(vendorunit, "import short.codec;") == 2);
assert(has(vendorunit, strings.concat(
"//ww:import-map short.codec vend.vendor.short.codec ",
hexbytes(vendored), "\n")));
assert(occurrences(vendorunit,
"//ww:import-map short.codec vend.vendor.short.codec ") == 1);
let vendortrace: str = readfile(traces[si]);
let vendorline: str = linecontaining(vendortrace,
"vend.client.unit.new");
assert(occurrences(vendorline,
"<--import><vend.vendor.short.codec>") == 1);
assert(occurrences(vendorline, strings.concat(
"<--import-map><short.codec><vend.vendor.short.codec>")) == 1);
let vendorwwi: str = readfile(strings.concat(works[si],
"/vend.vendor.short.codec.wwi"));
assert(has(vendorwwi, strings.concat(
"//ww:module vend.vendor.short.codec\npackage cable;\n")));
assert(!os.exists(strings.concat(works[si], "/short.codec.a")));
let vendorrun: []str = [vendorbins[si]];
runcommand(root, strings.concat("name-vendor-run-", stages[si]),
vendorrun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 13);
// Build the bare-import bridge and its command, establish a no-op warm
// rebuild, then change only the dependency's declared name. The direct
// importer is reconsidered; its unchanged export stops propagation.
rewritefile(traces[si], "");
let appav: []str = [driver(stages[si]), "build", "-w", works[si],
"-I", source, "-o", appbins[si], app];
runcommandenv(root, strings.concat("name-app-cold-", stages[si]), appav,
env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let apprun: []str = [appbins[si]];
runcommand(root, strings.concat("name-app-run-", stages[si]), apprun,
(30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
rewritefile(traces[si], "");
runcommandenv(root, strings.concat("name-app-warm-", stages[si]), appav,
env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(readfile(traces[si]).len == 0);
rewritefile(codecfile, cablebody);
rewritefile(traces[si], "");
runcommandenv(root, strings.concat("name-app-renamed-", stages[si]),
appav, env, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let changedtrace: str = readfile(traces[si]);
assert(occurrences(changedtrace, "\n") == 2);
assert(occurrences(changedtrace, "/acme.codec.unit.new") == 1);
assert(occurrences(changedtrace, "/acme.bridge.unit.new") == 1);
assert(!has(changedtrace, "/cmd.app.unit.new"));
let nchanged: str = normalizedtrace(changedtrace,
strings.concat(works[si], "/"), appbins[si]);
if (si == 0) { changedtraceref = strings.dup(nchanged); }
else { assert(same(changedtraceref, nchanged)); };
let renamedwwi: str = readfile(strings.concat(works[si],
"/acme.codec.wwi"));
assert(has(renamedwwi, "//ww:module acme.codec\npackage cable;\n"));
assert(!has(renamedwwi, "package wire;"));
runcommand(root, strings.concat("name-app-renamed-run-", stages[si]),
apprun, (30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 42);
// New semantic artifacts and binaries are byte-identical across the
// independently cold Cstage and WWstage work roots.
ari = 0;
for (ari < actions.len) {
let xi: i32 = 0;
for (xi < suffixes.len) {
let index: i32 = ari * suffixes.len + xi;
let bytes: str = readfile(strings.concat(works[si], "/",
actions[ari], suffixes[xi]));
if (si == 0) { artifactrefs[index] = strings.dup(bytes); }
else { assert(same(artifactrefs[index], bytes)); };
xi += 1;
};
ari += 1;
};
let stagebins: []str = [directbins[si], appbins[si], scopedbins[si],
repeatedbins[si], leafmainbins[si], vendorbins[si]];
let bini: i32 = 0;
for (bini < stagebins.len) {
let bytes: str = readfile(stagebins[bini]);
if (si == 0) { binaryrefs[bini] = strings.dup(bytes); }
else { assert(same(binaryrefs[bini], bytes)); };
bini += 1;
};
si += 1;
};
clean(root);
};
@test fn compile_artifact_naming() void = {
let root: str = fresh();
let pdir: str = strings.concat(root, "/pkg");