fix: allow lexical import shadowing

This commit is contained in:
2026-08-22 17:53:31 +09:00
parent 89f519d5cb
commit 6279d46652
19 changed files with 1420 additions and 270 deletions

View File

@@ -19743,3 +19743,642 @@ fn runtimepath(relative: str) str = {
&& !directoryhasfragment(root, ".install"));
clean(root);
};
// A file-local import package-name object participates in ordinary lexical
// lookup: a closer local wins from its declaration point through its scope,
// and the package qualifier is visible again afterward. Exercise that rule
// through raw and package builds, imported packages, every test source role,
// filtered retained execution, and transactional rejection/restoration.
@test fn lexical_import_bindings_shadow_normally() void = {
let root: str = fresh();
let source: str = strings.concat(root, "/source");
let wire: str = strings.concat(source, "/dep/wire");
let aliasdep: str = strings.concat(source, "/dep/aliasdep");
let builtindep: str = strings.concat(source, "/dep/builtindep");
let palette: str = strings.concat(source, "/dep/palette");
let shadowlib: str = strings.concat(source, "/dep/shadowlib");
let matrix: str = strings.concat(source, "/matrix");
let client: str = strings.concat(source, "/client");
let sibling: str = strings.concat(source, "/sibling");
let invalid: str = strings.concat(source, "/invalid");
let invalidscalar: str = strings.concat(source, "/invalidscalar");
let invalidtype: str = strings.concat(source, "/invalidtype");
let warm: str = strings.concat(source, "/warm");
let roleprod: str = strings.concat(source, "/roleprod");
let rolesame: str = strings.concat(source, "/rolesame");
let roleexternal: str = strings.concat(source, "/roleexternal");
let roleonly: str = strings.concat(source, "/roleonly");
let retainedcase: str = strings.concat(source, "/retainedcase");
let dirs: []str = [wire, aliasdep, builtindep, palette, shadowlib,
matrix, client, sibling, invalid, invalidscalar, invalidtype, warm,
roleprod, rolesame, roleexternal, roleonly, retainedcase];
let di: i32 = 0;
for (di < dirs.len) { mkdirall(dirs[di]); di += 1; };
writefile(strings.concat(wire, "/wire.ww"), strings.concat(
"package wire;\n",
"export type record = struct { n: i32 };\n",
"export type count = i32;\n",
"export fn value() i32 = { return 11; };\n"));
writefile(strings.concat(aliasdep, "/aliasdep.ww"),
"package aliasdep;\nexport fn value() i32 = { return 13; };\n");
writefile(strings.concat(builtindep, "/builtindep.ww"),
"package builtindep;\nexport fn value() i32 = { return 17; };\n");
writefile(strings.concat(palette, "/palette.ww"), strings.concat(
"package palette;\n",
"export type shade = enum i32 { RED = 100 };\n",
"export fn value() i32 = { return shade.RED: i32; };\n"));
// The initializer still sees the import, the declared local wins
// afterward, a nested binding is restored on exit, aliases behave like
// default names, and local dot/call fields beat package and enum shortcuts.
writefile(strings.concat(matrix, "/main.ww"), strings.concat(
"package main;\n",
"import dep.wire;\n",
"import stable dep.aliasdep;\n",
"import len dep.builtindep;\n",
"import shade dep.palette;\n",
"type box = struct { n: i32, RED: i32, call: *fn() i32 };\n",
"fn seven() i32 = { return 7; };\n",
"fn typedpair() (i32, wire.count) = { return 5, 6; };\n",
"fn localafter() bool = { let before: i32 = wire.value(); ",
"let wire: i32 = wire.value() + 1; ",
"return before == 11 && wire == 12; };\n",
"fn nestedrestore() bool = { let before: i32 = wire.value(); ",
"if (true) { let wire: i32 = 3; ",
"if (wire != 3) { return false; }; }; ",
"return before == 11 && wire.value() == 11; };\n",
"fn aliases() bool = { let a: i32 = stable.value(); ",
"let stable: i32 = stable.value() + 1; ",
"let b: i32 = len.value(); let len: i32 = len.value() + 1; ",
"return a == 13 && stable == 14 && b == 17 && len == 18; };\n",
"fn localfields() bool = { let wire: box = box { n = 5, ",
"RED = 0, call = &seven }; let imported: i32 = shade.value(); ",
"let shade: box = box { n = 0, RED = 19, call = &seven }; ",
"return wire.n == 5 && (*wire.call)() == 7 ",
"&& imported == 100 && shade.RED == 19; };\n",
"fn tupledecl() bool = { ",
"let (wire: i32, second: wire.count) = typedpair(); ",
"return wire == 5 && (second: i32) == 6; };\n",
"fn ordinaryfor() bool = { let total: i32 = wire.value(); ",
"for (let wire: i32 = 1; wire < 3; wire += 1) ",
"{ total += wire; }; ",
"return total == 14 && wire.value() == 11; };\n",
"fn main() i32 = { if (!localafter()) { return 1; }; ",
"if (!nestedrestore()) { return 2; }; ",
"if (!aliases()) { return 3; }; ",
"if (!localfields()) { return 4; }; ",
"if (!tupledecl()) { return 5; }; ",
"if (!ordinaryfor()) { return 6; }; return 42; };\n"));
let stages: []str = ["ww", "ww_ww"];
let tags: []str = ["c", "ww"];
let artifactpaths: []str = [
"/dep.wire.unit.ww", "/dep.wire.wwi", "/dep.wire.s",
"/dep.wire.o", "/dep.wire.a",
"/dep.aliasdep.unit.ww", "/dep.aliasdep.wwi", "/dep.aliasdep.s",
"/dep.aliasdep.o", "/dep.aliasdep.a",
"/dep.builtindep.unit.ww", "/dep.builtindep.wwi",
"/dep.builtindep.s", "/dep.builtindep.o", "/dep.builtindep.a",
"/dep.palette.unit.ww", "/dep.palette.wwi", "/dep.palette.s",
"/dep.palette.o", "/dep.palette.a",
"/matrix.unit.ww", "/matrix.wwi", "/matrix.s", "/matrix.o",
"/matrix.a", "/matrix.init.unit.ww", "/matrix.init.s",
"/matrix.init.o"];
let artifactrefs: []str = alloc([], artifactpaths.len: u64)!;
let matrixbin: str = "";
let matrixoutrefs: []str = ["", ""];
let matrixerrrefs: []str = ["", ""];
let si: i32 = 0;
for (si < stages.len) {
let work: str = strings.concat(root, "/matrix-work-", tags[si]);
let output: str = strings.concat(root, "/matrix-output-", tags[si]);
mkdirall(work);
let av: []str = [driver(stages[si]), "build", "-w", work,
"-I", source, "-o", output, "matrix"];
let out: commandout;
runcommand(root, strings.concat("import-shadow-matrix-", tags[si]), av,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(output)
&& !os.exists(strings.concat(output, ".new"))
&& !os.exists(strings.concat(output, ".sepwork"))
&& !directoryhasnew(work)
&& !directoryhasfragment(work, ".wwtxn.")
&& !directoryhasfragment(work, ".install")
&& !directoryhasfragment(work, ".sepwork"));
matrixoutrefs[si] = strings.dup(out.stdout);
matrixerrrefs[si] = strings.dup(out.stderr);
let runav: []str = [output];
runcommand(root, strings.concat("import-shadow-matrix-run-", tags[si]),
runav, time.second, &out);
expectexit(&out, 42);
assert(out.stdout.len == 0 && out.stderr.len == 0);
let ai: i32 = 0;
for (ai < artifactpaths.len) {
let bytes: str = readfile(strings.concat(work, artifactpaths[ai]));
if (si == 0) { append(artifactrefs, strings.dup(bytes)); }
else { assert(same(artifactrefs[ai], bytes)); };
ai += 1;
};
if (si == 0) { matrixbin = strings.dup(readfile(output)); }
else { assert(same(matrixbin, readfile(output))); };
si += 1;
};
assert(same(matrixoutrefs[0], matrixoutrefs[1])
&& same(matrixerrrefs[0], matrixerrrefs[1]));
// A raw source operand and a package reached through an import graph use
// the same checker and declaration-point rule.
let raw: str = strings.concat(root, "/raw-shadow.ww");
writefile(raw, strings.concat(
"package main;\nimport dep.wire;\n",
"fn main() i32 = { let wire: i32 = wire.value() + 31; ",
"return wire; };\n"));
writefile(strings.concat(shadowlib, "/shadowlib.ww"), strings.concat(
"package shadowlib;\nimport dep.wire;\n",
"export fn value() i32 = { let before: i32 = wire.value(); ",
"let wire: i32 = wire.value() + 31; ",
"if (before != 11) { return 1; }; return wire; };\n"));
writefile(strings.concat(client, "/main.ww"), strings.concat(
"package main;\nimport dep.shadowlib;\n",
"fn main() i32 = { return shadowlib.value(); };\n"));
writefile(strings.concat(sibling, "/imported.ww"), strings.concat(
"package main;\nimport dep.wire;\n",
"fn imported() i32 = { return wire.value(); };\n"));
writefile(strings.concat(sibling, "/main.ww"), strings.concat(
"package main;\ntype box = struct { n: i32 };\n",
"fn main() i32 = { let wire: box = box { n = 31 }; ",
"return imported() + wire.n; };\n"));
let rawbin: str = "";
let clientbin: str = "";
let siblingbin: str = "";
si = 0;
for (si < stages.len) {
let rawwork: str = strings.concat(root, "/raw-work-", tags[si]);
let rawout: str = strings.concat(root, "/raw-output-", tags[si]);
mkdirall(rawwork);
let rawav: []str = [driver(stages[si]), "build", "-w", rawwork,
"-I", source, "-o", rawout, raw];
let out: commandout;
runcommand(root, strings.concat("import-shadow-raw-", tags[si]), rawav,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(rawout)
&& !os.exists(strings.concat(rawout, ".sepwork"))
&& !directoryhasnew(rawwork)
&& !directoryhasfragment(rawwork, ".wwtxn.")
&& !directoryhasfragment(rawwork, ".sepwork"));
let runav: []str = [rawout];
runcommand(root, strings.concat("import-shadow-raw-run-", tags[si]),
runav, time.second, &out);
expectexit(&out, 42);
assert(out.stdout.len == 0 && out.stderr.len == 0);
if (si == 0) { rawbin = strings.dup(readfile(rawout)); }
else { assert(same(rawbin, readfile(rawout))); };
let clientwork: str = strings.concat(root, "/client-work-", tags[si]);
let clientout: str = strings.concat(root, "/client-output-", tags[si]);
mkdirall(clientwork);
let clientav: []str = [driver(stages[si]), "build", "-w", clientwork,
"-I", source, "-o", clientout, "client"];
runcommand(root, strings.concat("import-shadow-client-", tags[si]),
clientav, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& os.exists(clientout)
&& !os.exists(strings.concat(clientout, ".sepwork"))
&& !directoryhasnew(clientwork)
&& !directoryhasfragment(clientwork, ".wwtxn.")
&& !directoryhasfragment(clientwork, ".sepwork"));
let clientrun: []str = [clientout];
runcommand(root, strings.concat("import-shadow-client-run-", tags[si]),
clientrun, time.second, &out);
expectexit(&out, 42);
assert(out.stdout.len == 0 && out.stderr.len == 0);
if (si == 0) { clientbin = strings.dup(readfile(clientout)); }
else { assert(same(clientbin, readfile(clientout))); };
let siblingwork: str = strings.concat(root, "/sibling-work-", tags[si]);
let siblingout: str = strings.concat(root, "/sibling-output-", tags[si]);
mkdirall(siblingwork);
let siblingav: []str = [driver(stages[si]), "build", "-w", siblingwork,
"-I", source, "-o", siblingout, "sibling"];
runcommand(root, strings.concat("import-shadow-sibling-", tags[si]),
siblingav, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& os.exists(siblingout)
&& !os.exists(strings.concat(siblingout, ".sepwork"))
&& !directoryhasnew(siblingwork)
&& !directoryhasfragment(siblingwork, ".wwtxn.")
&& !directoryhasfragment(siblingwork, ".sepwork"));
let siblingrun: []str = [siblingout];
runcommand(root, strings.concat("import-shadow-sibling-run-", tags[si]),
siblingrun, time.second, &out);
expectexit(&out, 42);
assert(out.stdout.len == 0 && out.stderr.len == 0);
if (si == 0) { siblingbin = strings.dup(readfile(siblingout)); }
else { assert(same(siblingbin, readfile(siblingout))); };
si += 1;
};
// A selector on a closer local is not package use. The otherwise-unused
// import is diagnosed first by source position, ahead of the later invalid
// local selector, and the request commits no cold work or publication.
writefile(strings.concat(invalid, "/main.ww"), strings.concat(
"package main;\nimport dep.wire;\n",
"fn main() i32 = { let wire: i32 = 2; ",
"return wire.n; };\n"));
writefile(strings.concat(invalidscalar, "/main.ww"), strings.concat(
"package main;\nimport dep.wire;\n",
"fn main() i32 = { let before: i32 = wire.value(); ",
"let wire: i32 = 2; if (before == 0) { return 0; }; ",
"return wire.n; };\n"));
writefile(strings.concat(invalidscalar, "/main_test.ww"),
"package main;\n@test fn never() void = { assert(main() == 0); };\n");
writefile(strings.concat(invalidtype, "/main.ww"), strings.concat(
"package main;\nimport dep.wire;\n",
"fn main() i32 = { let before: i32 = wire.value(); ",
"let wire: i32 = 2; let later: wire.record; ",
"return before + wire; };\n"));
let invaliddiag: str = "";
si = 0;
for (si < stages.len) {
let work: str = strings.concat(root, "/invalid-work-", tags[si]);
let output: str = strings.concat(root, "/invalid-output-", tags[si]);
mkdirall(work);
let av: []str = [driver(stages[si]), "build", "-w", work,
"-I", source, "-o", output, "invalid"];
let out: commandout;
runcommand(root, strings.concat("import-shadow-invalid-", tags[si]), av,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0
&& same(primarydiagnostic(out.stderr),
"\"dep.wire\" imported and not used")
&& occurrences(out.stderr, "imported and not used") == 1
&& occurrences(out.stderr, "selector 'n' undefined") == 1
&& !has(out.stderr, "shadows imported module")
&& !has(out.stderr, "use of package wire not in selector")
&& !has(out.stderr, "asserttyped:")
&& !has(out.stderr, "undefined reference")
&& !os.exists(output)
&& !os.exists(strings.concat(output, ".new"))
&& !os.exists(strings.concat(output, ".sepwork"))
&& directoryisempty(work));
let normalized: str = normalizedtrace(out.stderr,
strings.concat(work, "/"), output);
if (si == 0) { invaliddiag = strings.dup(normalized); }
else { assert(same(invaliddiag, normalized)); };
si += 1;
};
// Once the closer binding exists, value and type selectors must stay on
// that local object. Invalid local value selection is one positioned
// checker error, and a dotted type cannot recover the hidden package name.
let invalidtargets: []str = ["invalidscalar", "invalidtype"];
let invalidcores: []str = ["selector 'n' undefined",
"unknown type 'wire.record'"];
let invalidlocations: []str = [":4:109: error: selector 'n' undefined",
":4:81: error: unknown type 'wire.record'"];
let invalidshaperefs: []str = ["", ""];
let invalidretainedrefs: []str = ["", ""];
si = 0;
for (si < stages.len) {
let ii: i32 = 0;
for (ii < invalidtargets.len) {
let work: str = strings.concat(root, "/invalid-shape-work-",
tags[si], "-", boundarypkgname(ii));
let output: str = strings.concat(root, "/invalid-shape-output-",
tags[si], "-", boundarypkgname(ii));
mkdirall(work);
let av: []str = [driver(stages[si]), "build", "-w", work,
"-I", source, "-o", output, invalidtargets[ii]];
let out: commandout;
runcommand(root, strings.concat("import-shadow-invalid-shape-",
tags[si], "-", boundarypkgname(ii)), av,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0
&& same(primarydiagnostic(out.stderr), invalidcores[ii])
&& has(out.stderr, invalidlocations[ii])
&& occurrences(out.stderr, invalidcores[ii]) == 1
&& !has(out.stderr, "shadows imported module")
&& !has(out.stderr, "asserttyped:")
&& !has(out.stderr, "calling non-function")
&& !has(out.stderr, "undefined reference")
&& !os.exists(output)
&& !os.exists(strings.concat(output, ".new"))
&& !os.exists(strings.concat(output, ".sepwork"))
&& directoryisempty(work));
let normalized: str = normalizedtrace(out.stderr,
strings.concat(work, "/"), output);
if (si == 0) {
invalidshaperefs[ii] = strings.dup(normalized);
} else { assert(same(invalidshaperefs[ii], normalized)); };
ii += 1;
};
// A cold retained test request must reject before producing a runnable
// or any committed/staged work artifact.
let testwork: str = strings.concat(root,
"/invalid-retained-work-", tags[si]);
let retained: str = strings.concat(root,
"/invalid-retained-", tags[si], ".test");
mkdirall(testwork);
let testav: []str = [driver(stages[si]), "test", "-w", testwork,
"-I", source, "-run", "never", "-o", retained,
"invalidscalar"];
let tout: commandout;
runcommand(root, strings.concat("import-shadow-invalid-retained-",
tags[si]), testav,
(120i64 * (time.second: i64)): time.duration, &tout);
expectexit(&tout, 1);
assert(same(tout.stdout, "FAIL\n")
&& same(primarydiagnostic(tout.stderr), invalidcores[0])
&& has(tout.stderr, invalidlocations[0])
&& occurrences(tout.stderr, invalidcores[0]) == 1
&& !has(tout.stderr, "asserttyped:")
&& !os.exists(retained)
&& !os.exists(strings.concat(retained, ".new"))
&& !os.exists(strings.concat(retained, ".sepwork"))
&& directoryisempty(testwork));
let testnormalized: str = normalizedtrace(tout.stderr,
strings.concat(testwork, "/"), retained);
if (si == 0) {
invalidretainedrefs[si] = strings.dup(testnormalized);
} else {
invalidretainedrefs[si] = strings.dup(testnormalized);
assert(same(invalidretainedrefs[0], invalidretainedrefs[1]));
};
si += 1;
};
// Production called by a test, same-package test source, external-test
// source, and an honest test-only package all accept the same lexical rule.
writefile(strings.concat(roleprod, "/prod.ww"), strings.concat(
"package roleprod;\nimport dep.wire;\n",
"fn value() i32 = { let wire: i32 = wire.value() + 31; ",
"return wire; };\n"));
writefile(strings.concat(roleprod, "/prod_test.ww"),
"package roleprod;\n@test fn shadow_ok() void = { assert(value() == 42); };\n");
writefile(strings.concat(rolesame, "/prod.ww"),
"package rolesame;\nfn anchor() i32 = { return 1; };\n");
writefile(strings.concat(rolesame, "/same_test.ww"), strings.concat(
"package rolesame;\nimport dep.wire;\n",
"@test fn shadow_ok() void = { let before: i32 = wire.value(); ",
"let wire: i32 = wire.value() + 31; ",
"assert(anchor() == 1 && before == 11 && wire == 42); };\n"));
writefile(strings.concat(roleexternal, "/prod.ww"),
"package roleexternal;\nexport fn anchor() i32 = { return 1; };\n");
writefile(strings.concat(roleexternal, "/external_test.ww"), strings.concat(
"package roleexternal_test;\nimport dep.wire;\n",
"@test fn shadow_ok() void = { let wire: i32 = wire.value() + 31; ",
"assert(wire == 42); };\n"));
writefile(strings.concat(roleonly, "/only_test.ww"), strings.concat(
"package roleonly;\nimport dep.wire;\n",
"@test fn shadow_ok() void = { let wire: i32 = wire.value() + 31; ",
"assert(wire == 42); };\n"));
let roletargets: []str = ["roleprod", "rolesame", "roleexternal",
"roleonly"];
let roleoutrefs: []str = ["", "", "", ""];
let roleerrrefs: []str = ["", "", "", ""];
let rolebinrefs: []str = ["", "", "", ""];
si = 0;
for (si < stages.len) {
let ri: i32 = 0;
for (ri < roletargets.len) {
let work: str = strings.concat(root, "/role-work-", tags[si], "-",
boundarypkgname(ri));
let output: str = strings.concat(root, "/role-output-", tags[si], "-",
boundarypkgname(ri));
mkdirall(work);
let av: []str = [driver(stages[si]), "test", "-w", work,
"-I", source, "-run", "shadow_ok", "-o", output,
roletargets[ri]];
let out: commandout;
runcommand(root, strings.concat("import-shadow-role-", tags[si], "-",
boundarypkgname(ri)), av,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stderr.len == 0 && os.exists(output)
&& !os.exists(strings.concat(output, ".sepwork"))
&& occurrences(out.stdout, ".shadow_ok ... ok\n") == 1
&& occurrences(out.stdout,
"1 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1
&& !directoryhasnew(work)
&& !directoryhasfragment(work, ".wwtxn.")
&& !directoryhasfragment(work, ".install")
&& !directoryhasfragment(work, ".sepwork"));
if (si == 0) {
roleoutrefs[ri] = strings.dup(out.stdout);
roleerrrefs[ri] = strings.dup(out.stderr);
rolebinrefs[ri] = strings.dup(readfile(output));
} else {
assert(same(roleoutrefs[ri], out.stdout)
&& same(roleerrrefs[ri], out.stderr)
&& same(rolebinrefs[ri], readfile(output)));
};
ri += 1;
};
si += 1;
};
// Warm semantic rejection preserves every committed action byte and public
// binary. Restoring exact input reuses the same generation byte-for-byte.
let warmfile: str = strings.concat(warm, "/main.ww");
let warmvalid: str = strings.concat(
"package main;\nimport dep.wire;\n",
"fn main() i32 = { let wire: i32 = wire.value() + 31; ",
"return wire; };\n");
let warminvalid: str = strings.concat(
"package main;\nimport dep.wire;\n",
"fn main() i32 = { let wire: i32 = 42; ",
"return wire.n; };\n");
writefile(warmfile, warmvalid);
let warmpaths: []str = ["/.wwtool.ww", "/.wwtool.w6c", "/.wwtool.w6a",
"/.wwtool.stamp", "/dep.wire.unit.ww", "/dep.wire.wwi",
"/dep.wire.s", "/dep.wire.o", "/dep.wire.a", "/warm.unit.ww",
"/warm.wwi", "/warm.s", "/warm.o", "/warm.a",
"/warm.init.unit.ww", "/warm.init.s", "/warm.init.o"];
let warmcross: []str = alloc([], warmpaths.len: u64)!;
let warmbincross: str = "";
let warmdiag: str = "";
// Retention uses a production function called by the selected test. The
// unselected aborting row proves filtering survives retained/direct routes.
let retainedfile: str = strings.concat(retainedcase, "/prod.ww");
let retainedvalid: str = strings.concat(
"package retainedcase;\nimport dep.wire;\n",
"fn value() i32 = { let wire: i32 = wire.value() + 31; ",
"return wire; };\n");
let retainedinvalid: str = strings.concat(
"package retainedcase;\nimport dep.wire;\n",
"fn value() i32 = { let wire: i32 = 42; ",
"return wire.n; };\n");
writefile(retainedfile, retainedvalid);
writefile(strings.concat(retainedcase, "/retained_test.ww"), strings.concat(
"package retainedcase;\n",
"@test fn kept() void = { assert(value() == 42); };\n",
"@test fn not_selected() void = { abort(); };\n"));
let retainedcross: str = "";
let retainedoutrefs: []str = ["", ""];
let retainederrrefs: []str = ["", ""];
let retainedinvaliddiagrefs: []str = ["", ""];
let directoutrefs: []str = ["", ""];
let directerrrefs: []str = ["", ""];
si = 0;
for (si < stages.len) {
let work: str = strings.concat(root, "/warm-work-", tags[si]);
let output: str = strings.concat(root, "/warm-output-", tags[si]);
mkdirall(work);
let av: []str = [driver(stages[si]), "build", "-w", work,
"-I", source, "-o", output, "warm"];
let out: commandout;
runcommand(root, strings.concat("import-shadow-warm-cold-", tags[si]), av,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0 && os.exists(output));
let snapshots: []str = alloc([], warmpaths.len: u64)!;
let wi: i32 = 0;
for (wi < warmpaths.len) {
let bytes: str = strings.dup(readfile(strings.concat(work,
warmpaths[wi])));
append(snapshots, bytes);
if (si == 0) { append(warmcross, strings.dup(bytes)); }
else if (wi >= 4) { assert(same(warmcross[wi], bytes)); };
wi += 1;
};
let binbytes: str = strings.dup(readfile(output));
if (si == 0) { warmbincross = strings.dup(binbytes); }
else { assert(same(warmbincross, binbytes)); };
rewritefile(warmfile, warminvalid);
runcommand(root, strings.concat("import-shadow-warm-invalid-", tags[si]),
av, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(out.stdout.len == 0
&& same(primarydiagnostic(out.stderr),
"\"dep.wire\" imported and not used")
&& occurrences(out.stderr, "selector 'n' undefined") == 1
&& same(binbytes, readfile(output))
&& !os.exists(strings.concat(output, ".new"))
&& !os.exists(strings.concat(output, ".sepwork")));
let normalized: str = normalizedtrace(out.stderr,
strings.concat(work, "/"), output);
if (si == 0) { warmdiag = strings.dup(normalized); }
else { assert(same(warmdiag, normalized)); };
wi = 0;
for (wi < warmpaths.len) {
assert(same(snapshots[wi], readfile(strings.concat(work,
warmpaths[wi]))));
wi += 1;
};
assert(!directoryhasnew(work)
&& !directoryhasfragment(work, ".wwtxn.")
&& !directoryhasfragment(work, ".install")
&& !directoryhasfragment(work, ".sepwork"));
rewritefile(warmfile, warmvalid);
runcommand(root, strings.concat("import-shadow-warm-restored-", tags[si]),
av, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stdout.len == 0 && out.stderr.len == 0
&& same(binbytes, readfile(output))
&& !os.exists(strings.concat(output, ".sepwork"))
&& !directoryhasnew(work)
&& !directoryhasfragment(work, ".wwtxn.")
&& !directoryhasfragment(work, ".install")
&& !directoryhasfragment(work, ".sepwork"));
wi = 0;
for (wi < warmpaths.len) {
assert(same(snapshots[wi], readfile(strings.concat(work,
warmpaths[wi]))));
wi += 1;
};
let testwork: str = strings.concat(root, "/retained-work-", tags[si]);
let retained: str = strings.concat(root, "/retained-", tags[si], ".test");
mkdirall(testwork);
let testav: []str = [driver(stages[si]), "test", "-w", testwork,
"-I", source, "-run", "kept", "-o", retained,
"retainedcase"];
runcommand(root, strings.concat("import-shadow-retained-cold-", tags[si]),
testav, (120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stderr.len == 0 && os.exists(retained)
&& !os.exists(strings.concat(retained, ".sepwork"))
&& occurrences(out.stdout, "retainedcase.kept ... ok\n") == 1
&& occurrences(out.stdout,
"1 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1
&& !has(out.stdout, "not_selected")
&& !directoryhasnew(testwork)
&& !directoryhasfragment(testwork, ".wwtxn.")
&& !directoryhasfragment(testwork, ".sepwork"));
retainedoutrefs[si] = strings.dup(out.stdout);
retainederrrefs[si] = strings.dup(out.stderr);
let retainedbytes: str = strings.dup(readfile(retained));
if (si == 0) { retainedcross = strings.dup(retainedbytes); }
else { assert(same(retainedcross, retainedbytes)); };
rewritefile(retainedfile, retainedinvalid);
runcommand(root, strings.concat("import-shadow-retained-invalid-",
tags[si]), testav,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(same(out.stdout, "FAIL\n")
&& same(primarydiagnostic(out.stderr),
"\"dep.wire\" imported and not used")
&& occurrences(out.stderr, "selector 'n' undefined") == 1
&& same(retainedbytes, readfile(retained))
&& !os.exists(strings.concat(retained, ".new"))
&& !os.exists(strings.concat(retained, ".sepwork"))
&& !directoryhasnew(testwork)
&& !directoryhasfragment(testwork, ".wwtxn.")
&& !directoryhasfragment(testwork, ".install")
&& !directoryhasfragment(testwork, ".sepwork"));
let retainedinvalidnormalized: str = normalizedtrace(out.stderr,
strings.concat(testwork, "/"), retained);
retainedinvaliddiagrefs[si] = strings.dup(retainedinvalidnormalized);
if (si == 1) {
assert(same(retainedinvaliddiagrefs[0],
retainedinvaliddiagrefs[1]));
};
let directav: []str = [retained, "-package=retainedcase", "kept"];
runcommand(root, strings.concat("import-shadow-retained-direct-",
tags[si]), directav,
(30i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(out.stderr.len == 0
&& occurrences(out.stdout, "retainedcase.kept ... ok\n") == 1
&& occurrences(out.stdout,
"1 passed, 0 failed, 0 skipped, 0 harness errors\n") == 1
&& !has(out.stdout, "not_selected"));
directoutrefs[si] = strings.dup(out.stdout);
directerrrefs[si] = strings.dup(out.stderr);
rewritefile(retainedfile, retainedvalid);
runcommand(root, strings.concat("import-shadow-retained-restored-",
tags[si]), testav,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(same(out.stdout, retainedoutrefs[si])
&& same(out.stderr, retainederrrefs[si])
&& same(retainedbytes, readfile(retained))
&& !os.exists(strings.concat(retained, ".sepwork"))
&& !directoryhasnew(testwork)
&& !directoryhasfragment(testwork, ".wwtxn.")
&& !directoryhasfragment(testwork, ".install")
&& !directoryhasfragment(testwork, ".sepwork"));
si += 1;
};
assert(same(retainedoutrefs[0], retainedoutrefs[1])
&& same(retainederrrefs[0], retainederrrefs[1])
&& same(directoutrefs[0], directoutrefs[1])
&& same(directerrrefs[0], directerrrefs[1]));
assert(!directoryhasnew(root)
&& !directoryhasfragment(root, ".wwtxn.")
&& !directoryhasfragment(root, ".install")
&& !directoryhasfragment(root, ".sepwork")
&& !directoryhasfragment(root, ".capture")
&& !directoryhasfragment(root, ".result")
&& !directoryhasfragment(root, ".request"));
clean(root);
};