fix: allow lexical import shadowing
This commit is contained in:
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user