1441 lines
58 KiB
Plaintext
1441 lines
58 KiB
Plaintext
package sepinit_test;
|
|
|
|
// Go-1.26.5-aligned blank-import/package-initialization observer. All source
|
|
// trees are temporary and every semantic/artifact leg runs through both the C
|
|
// and WW drivers with independent work roots.
|
|
|
|
import os;
|
|
import os.exec;
|
|
import strconv;
|
|
import strings;
|
|
import testenv;
|
|
import time;
|
|
|
|
fn fail(label: str, why: str) void = {
|
|
let m: str = strings.concat("sepinit FAIL: ", label, " -- ", why,
|
|
"\n");
|
|
os.write(2, m.ptr, m.len: u64);
|
|
assert(false);
|
|
};
|
|
|
|
fn tmo() time.duration = {
|
|
return (240i64 * (time.second: i64)): time.duration;
|
|
};
|
|
|
|
fn replacefile(path: str, body: str) void = {
|
|
assert(os.remove(path) == 0);
|
|
testenv.writefile(path, body);
|
|
};
|
|
|
|
fn run(dir: str, name: str, argv: []str, out: *testenv.commandout) void = {
|
|
testenv.runcommand(dir, dir, name, argv, tmo(), out);
|
|
};
|
|
|
|
fn runenv(dir: str, name: str, argv: []str, env: []str,
|
|
out: *testenv.commandout) void = {
|
|
testenv.runcommandenv(dir, dir, name, argv, env, tmo(), out);
|
|
};
|
|
|
|
fn allocpkgname(n: i32) str = {
|
|
assert(n >= 0 && n < 1000);
|
|
let out: []u8 = alloc([], 4u64)!;
|
|
append(out, 'p');
|
|
append(out, ('0': i32 + (n / 100)): u8);
|
|
append(out, ('0': i32 + ((n / 10) % 10)): u8);
|
|
append(out, ('0': i32 + (n % 10)): u8);
|
|
return strings.frombytes(out);
|
|
};
|
|
|
|
fn compilertraceenv(wrapper: str, real: str, trace: str) []str = {
|
|
let base: []str = os.getenvs();
|
|
let env: []str = alloc([], (base.len + 3): u64)!;
|
|
let i: i32 = 0;
|
|
for (i < base.len) {
|
|
if (!strings.hasprefix(base[i], "WW_W6C=")
|
|
&& !strings.hasprefix(base[i], "WW_SEPINIT_REAL_W6C=")
|
|
&& !strings.hasprefix(base[i], "WW_SEPINIT_W6C_TRACE=")) {
|
|
append(env, base[i]);
|
|
};
|
|
i += 1;
|
|
};
|
|
append(env, strings.concat("WW_W6C=", wrapper));
|
|
append(env, strings.concat("WW_SEPINIT_REAL_W6C=", real));
|
|
append(env, strings.concat("WW_SEPINIT_W6C_TRACE=", trace));
|
|
return env;
|
|
};
|
|
|
|
fn exitcode(out: *testenv.commandout) i32 = {
|
|
if (out.termination != exec.termination.EXIT) { return -1; };
|
|
return out.code;
|
|
};
|
|
|
|
fn normalized(s: str, path: str) str = {
|
|
match (strings.replace(s, path, "$WORK")) {
|
|
case let v: str => return v;
|
|
case nomem => abort("normalize allocation");
|
|
};
|
|
};
|
|
|
|
fn hasat(s: str, off: i32, want: str) bool = {
|
|
if (off < 0 || off + want.len > s.len) { return false; };
|
|
let i: i32 = 0;
|
|
for (i < want.len) {
|
|
if (s[off + i] != want[i]) { return false; };
|
|
i += 1;
|
|
};
|
|
return true;
|
|
};
|
|
|
|
fn arsize(s: str, off: i32) i32 = {
|
|
let n: i32 = 0;
|
|
let seen: bool = false;
|
|
let i: i32 = 0;
|
|
for (i < 10) {
|
|
let c: u8 = s[off + i];
|
|
if (c >= '0' && c <= '9') {
|
|
seen = true;
|
|
n = n * 10 + ((c - '0'): i32);
|
|
} else { if (c != ' ') { return -1; }; };
|
|
i += 1;
|
|
};
|
|
if (!seen) { return -1; };
|
|
return n;
|
|
};
|
|
|
|
fn checkmember(label: str, ar: str, off: i32, name: str) i32 = {
|
|
if (!hasat(ar, off, name) || !hasat(ar, off + 16, "0")
|
|
|| !hasat(ar, off + 28, "0") || !hasat(ar, off + 34, "0")
|
|
|| !hasat(ar, off + 40, "100644")
|
|
|| !hasat(ar, off + 58, "`\n")) {
|
|
fail(label, strings.concat("bad deterministic ar header for ", name));
|
|
};
|
|
let n: i32 = arsize(ar, off + 48);
|
|
if (n < 0) { fail(label, "bad ar member size"); };
|
|
return off + 60 + n + (n & 1);
|
|
};
|
|
|
|
fn checkarchives(label: str, root: str, dep: str) void = {
|
|
let ar: str = testenv.readfile(root);
|
|
if (!hasat(ar, 0, "!<arch>\n")) { fail(label, "root ar magic"); };
|
|
let next: i32 = checkmember(label, ar, 8, "pkg.o/");
|
|
next = checkmember(label, ar, next, "init.o/");
|
|
if (next != ar.len) { fail(label, "root archive has extra members"); };
|
|
let dar: str = testenv.readfile(dep);
|
|
if (!hasat(dar, 0, "!<arch>\n")) { fail(label, "dep ar magic"); };
|
|
next = checkmember(label, dar, 8, "pkg.o/");
|
|
if (next != dar.len) { fail(label, "dependency archive is not one-member"); };
|
|
};
|
|
|
|
fn comparefile(label: str, a: str, b: str) void = {
|
|
if (!testenv.same(testenv.readfile(a), testenv.readfile(b))) {
|
|
fail(label, strings.concat("stage bytes differ: ", a));
|
|
};
|
|
};
|
|
|
|
@test fn runtime_order_artifacts_and_cycles() void = {
|
|
let td: str = testenv.fresh();
|
|
let names: []str = ["a", "b", "c", "d", "graph", "vars", "cycle",
|
|
"cycles"];
|
|
let ni: i32 = 0;
|
|
for (ni < names.len) {
|
|
assert(os.mkdir(strings.concat(td, "/", names[ni]), 493) == 0);
|
|
ni += 1;
|
|
};
|
|
testenv.writefile(strings.concat(td, "/d/d.ww"), strings.concat(
|
|
"package d;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"D\".ptr, 1u64); };\n",
|
|
"fn init() void = { os.write(1, \"d\".ptr, 1u64); };\n"));
|
|
testenv.writefile(strings.concat(td, "/d/z.ww"), strings.concat(
|
|
"package d;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"!\".ptr, 1u64); };\n"));
|
|
testenv.writefile(strings.concat(td, "/a/a.ww"), strings.concat(
|
|
"package a;\nimport _ d;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"A\".ptr, 1u64); };\n"));
|
|
testenv.writefile(strings.concat(td, "/b/b.ww"), strings.concat(
|
|
"package b;\nimport _ d;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"B\".ptr, 1u64); };\n"));
|
|
testenv.writefile(strings.concat(td, "/c/c.ww"), strings.concat(
|
|
"package c;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"C\".ptr, 1u64); };\n",
|
|
"fn init() void = { os.write(1, \"c\".ptr, 1u64); };\n"));
|
|
testenv.writefile(strings.concat(td, "/graph/main.ww"), strings.concat(
|
|
"package main;\nimport _ c;\nimport _ b;\nimport _ a;\nimport os;\n",
|
|
"fn main() i32 = { os.write(1, \"M\".ptr, 1u64); return 0; };\n"));
|
|
testenv.writefile(strings.concat(td, "/vars/a.ww"), strings.concat(
|
|
"package main;\nimport os;\n",
|
|
"let A: i32 = makea();\n",
|
|
"fn makea() i32 = { os.write(1, \"A\".ptr, 1u64); return B + 1; };\n",
|
|
"let B: i32 = makeb();\n",
|
|
"fn makeb() i32 = { os.write(1, \"B\".ptr, 1u64); return 4; };\n",
|
|
"let P: *i64 = alloc(7i64)!;\n",
|
|
"fn init() void = { assert(*P == 7i64); os.write(1, \"1\".ptr, 1u64); };\n"));
|
|
testenv.writefile(strings.concat(td, "/vars/z.ww"), strings.concat(
|
|
"package main;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"2\".ptr, 1u64); };\n",
|
|
"fn main() i32 = { os.write(1, \"M\".ptr, 1u64); return A - 5; };\n"));
|
|
testenv.writefile(strings.concat(td, "/cycle/main.ww"), strings.concat(
|
|
"package main;\n",
|
|
"let A: i32 = geta();\n",
|
|
"fn geta() i32 = { return B; };\n",
|
|
"let B: i32 = getb();\n",
|
|
"fn getb() i32 = { return A; };\n",
|
|
"fn main() i32 = { return 0; };\n"));
|
|
testenv.writefile(strings.concat(td, "/cycles/main.ww"), strings.concat(
|
|
"package main;\n",
|
|
"let Tail: i32 = tail();\nfn tail() i32 = { return B; };\n",
|
|
"let B: i32 = getb();\nfn getb() i32 = { return C; };\n",
|
|
"let C: i32 = getc();\nfn getc() i32 = { return B; };\n",
|
|
"let D: i32 = getd();\nfn getd() i32 = { return D; };\n",
|
|
"fn main() i32 = { return 0; };\n"));
|
|
|
|
let stages: []str = ["ww", "ww_ww"];
|
|
let tags: []str = ["c", "ww"];
|
|
let works: []str = [strings.concat(td, "/work-c"),
|
|
strings.concat(td, "/work-ww")];
|
|
let progs: []str = [strings.concat(td, "/graph-c"),
|
|
strings.concat(td, "/graph-ww")];
|
|
let varprogs: []str = [strings.concat(td, "/vars-c"),
|
|
strings.concat(td, "/vars-ww")];
|
|
let i: i32 = 0;
|
|
for (i < 2) {
|
|
assert(os.mkdir(works[i], 493) == 0);
|
|
let av: []str = [testenv.driver(stages[i]), "build", "-w", works[i],
|
|
"-I", td, "-o", progs[i], strings.concat(td, "/graph")];
|
|
let out: testenv.commandout;
|
|
run(td, strings.concat("graph-build-", tags[i]), av, &out);
|
|
if (exitcode(&out) != 0) { fail("graph", out.stderr); };
|
|
let rav: []str = [progs[i]];
|
|
run(td, strings.concat("graph-run-", tags[i]), rav, &out);
|
|
if (exitcode(&out) != 0 || !testenv.same(out.stdout, "CcDd!ABM")) {
|
|
fail("graph", strings.concat("runtime order: ", out.stdout));
|
|
};
|
|
let vav: []str = [testenv.driver(stages[i]), "build", "-w", works[i],
|
|
"-I", td, "-o", varprogs[i], strings.concat(td, "/vars")];
|
|
run(td, strings.concat("vars-build-", tags[i]), vav, &out);
|
|
if (exitcode(&out) != 0) { fail("vars", out.stderr); };
|
|
let vrav: []str = [varprogs[i]];
|
|
run(td, strings.concat("vars-run-", tags[i]), vrav, &out);
|
|
if (exitcode(&out) != 0 || !testenv.same(out.stdout, "BA12M")) {
|
|
fail("vars", strings.concat("runtime order: ", out.stdout));
|
|
};
|
|
let dispatch: str = testenv.readfile(strings.concat(works[i],
|
|
"/graph.init.s"));
|
|
let cs: str = "CALL\t__ww..pkg.p.c.v0.r0.init(SB)";
|
|
let ds: str = "CALL\t__ww..pkg.p.d.v0.r0.init(SB)";
|
|
let acall: str = "CALL\t__ww..pkg.p.a.v0.r0.init(SB)";
|
|
let bs: str = "CALL\t__ww..pkg.p.b.v0.r0.init(SB)";
|
|
let rs: str = "CALL\t__ww..pkg.p.graph.v0.r0.init(SB)";
|
|
if (!(testenv.pos(dispatch, cs) < testenv.pos(dispatch, ds)
|
|
&& testenv.pos(dispatch, ds) < testenv.pos(dispatch, acall)
|
|
&& testenv.pos(dispatch, acall) < testenv.pos(dispatch, bs)
|
|
&& testenv.pos(dispatch, bs) < testenv.pos(dispatch, rs))) {
|
|
fail("graph", "dispatcher is not lexical-ready dependency order");
|
|
};
|
|
if (testenv.occurrences(dispatch, ds) != 1
|
|
|| testenv.occurrences(dispatch, acall) != 1
|
|
|| testenv.occurrences(dispatch, bs) != 1) {
|
|
fail("graph", "diamond task not exactly once");
|
|
};
|
|
let roots: str = testenv.readfile(strings.concat(works[i], "/graph.s"));
|
|
let deps: str = testenv.readfile(strings.concat(works[i], "/d.s"));
|
|
if (testenv.occurrences(roots, "CALL\t__ww..dispatch(SB)") != 1
|
|
|| testenv.has(deps, "__ww..dispatch")) {
|
|
fail("graph", "dispatcher call ownership");
|
|
};
|
|
let iface: str = testenv.readfile(strings.concat(works[i], "/vars.wwi"));
|
|
if (testenv.has(iface, "fn init") || testenv.has(iface, "__ww..")) {
|
|
fail("vars", "private initialization leaked to export");
|
|
};
|
|
checkarchives("graph", strings.concat(works[i], "/graph.a"),
|
|
strings.concat(works[i], "/d.a"));
|
|
i += 1;
|
|
};
|
|
let suffixes: []str = [".unit.ww", ".wwi", ".s", ".o", ".a"];
|
|
let owners: []str = ["a", "b", "c", "d", "graph", "vars"];
|
|
i = 0;
|
|
for (i < owners.len) {
|
|
let j: i32 = 0;
|
|
for (j < suffixes.len) {
|
|
comparefile("stage-byte-id",
|
|
strings.concat(works[0], "/", owners[i], suffixes[j]),
|
|
strings.concat(works[1], "/", owners[i], suffixes[j]));
|
|
j += 1;
|
|
};
|
|
i += 1;
|
|
};
|
|
let initnames: []str = ["graph.init.unit.ww", "graph.init.s",
|
|
"graph.init.o", "vars.init.unit.ww", "vars.init.s", "vars.init.o"];
|
|
i = 0;
|
|
for (i < initnames.len) {
|
|
comparefile("stage-byte-id", strings.concat(works[0], "/", initnames[i]),
|
|
strings.concat(works[1], "/", initnames[i]));
|
|
i += 1;
|
|
};
|
|
comparefile("stage-byte-id", progs[0], progs[1]);
|
|
comparefile("stage-byte-id", varprogs[0], varprogs[1]);
|
|
|
|
let cerr: str = "";
|
|
i = 0;
|
|
for (i < 2) {
|
|
let prog: str = strings.concat(td, "/cycle-", tags[i]);
|
|
let av: []str = [testenv.driver(stages[i]), "build", "-I", td,
|
|
"-o", prog, strings.concat(td, "/cycle")];
|
|
let out: testenv.commandout;
|
|
run(td, strings.concat("cycle-build-", tags[i]), av, &out);
|
|
if (exitcode(&out) == 0 || testenv.exists(prog)) {
|
|
fail("cycle", "accepted or published");
|
|
};
|
|
let work: str = strings.concat(prog, ".sepwork");
|
|
let diag: str = normalized(out.stderr, work);
|
|
if (!testenv.has(diag, "error: initialization cycle for A\n")
|
|
|| !testenv.has(diag, "A refers to B\n")
|
|
|| !testenv.has(diag, "B refers to A\n")) {
|
|
fail("cycle", diag);
|
|
};
|
|
if (i == 0) { cerr = strings.dup(diag); }
|
|
else { if (!testenv.same(cerr, diag)) { fail("cycle", "stage diagnostics"); }; };
|
|
if (testenv.exists(work)) {
|
|
fail("cycle", "cold rejection retained its scratch tree");
|
|
};
|
|
i += 1;
|
|
};
|
|
let multiref: str = "";
|
|
i = 0;
|
|
for (i < 2) {
|
|
let prog: str = strings.concat(td, "/cycles-", tags[i]);
|
|
let av: []str = [testenv.driver(stages[i]), "build", "-I", td,
|
|
"-o", prog, strings.concat(td, "/cycles")];
|
|
let out: testenv.commandout;
|
|
run(td, strings.concat("cycles-build-", tags[i]), av, &out);
|
|
let work: str = strings.concat(prog, ".sepwork");
|
|
let diag: str = normalized(out.stderr, work);
|
|
let bc: i32 = testenv.pos(diag, "error: initialization cycle for B\n");
|
|
let dd: i32 = testenv.pos(diag,
|
|
"error: initialization cycle: D refers to itself\n");
|
|
if (exitcode(&out) == 0 || testenv.exists(prog) || bc < 0 || dd <= bc
|
|
|| !testenv.has(diag, "B refers to C\n")
|
|
|| !testenv.has(diag, "C refers to B\n")
|
|
|| testenv.has(diag, "initialization cycle for Tail")
|
|
|| testenv.exists(work)) {
|
|
fail("cycles", diag);
|
|
};
|
|
if (i == 0) { multiref = strings.dup(diag); }
|
|
else { if (!testenv.same(multiref, diag)) {
|
|
fail("cycles", "C/WW multiple-cycle diagnostics");
|
|
}; };
|
|
i += 1;
|
|
};
|
|
testenv.clean(td);
|
|
};
|
|
|
|
@test fn runtime_aggregate_initializers() void = {
|
|
let td: str = testenv.fresh();
|
|
let pkg: str = strings.concat(td, "/agg");
|
|
assert(os.mkdir(pkg, 493) == 0);
|
|
testenv.writefile(strings.concat(pkg, "/main.ww"), strings.concat(
|
|
"package main;\n",
|
|
"let seq: i64;\n",
|
|
"fn next() i64 = { seq += 1; return seq; };\n",
|
|
"fn word() str = { seq += 1; return \"ok\"; };\n",
|
|
"type box = struct { nums: [2][2]i64, names: []str, };\n",
|
|
"let nested: [2][2]i64 = [[next(), next()], [next(), next()]];\n",
|
|
"let names: []str = [word(), word()];\n",
|
|
"let wide: (i64, i64, i64, i64, i64) = ",
|
|
"(next(), next(), next(), next(), next());\n",
|
|
"let value: box = box{ nums = [[next(), next()], ",
|
|
"[next(), next()]], names = [word(), word()], };\n",
|
|
"type zeroelem = struct {};\n",
|
|
"type zerobox = struct { tick: i64, empty: []u8, zeros: []zeroelem, };\n",
|
|
"let zero: zerobox = zerobox{ tick = next(), empty = [], ",
|
|
"zeros = [zeroelem{}, zeroelem{}], };\n",
|
|
"fn main() i32 = {\n",
|
|
" let junk: [32]str = [\"bad\"...];\n",
|
|
" assert(junk[31] == \"bad\");\n",
|
|
" assert(nested[0][0] == 1 && nested[1][1] == 4);\n",
|
|
" assert(names.len == 2 && names[0] == \"ok\" ",
|
|
"&& names[1] == \"ok\");\n",
|
|
" assert(wide.0 == 7 && wide.4 == 11);\n",
|
|
" assert(value.nums[0][0] == 12 && value.nums[1][1] == 15);\n",
|
|
" assert(value.names.len == 2 && value.names[0] == \"ok\" ",
|
|
"&& value.names[1] == \"ok\");\n",
|
|
" assert(zero.tick == 18 && zero.empty.ptr != nil ",
|
|
"&& zero.empty.len == 0 && zero.empty.cap == 0);\n",
|
|
" assert(zero.zeros.ptr != nil && zero.zeros.len == 2 ",
|
|
"&& zero.zeros.cap == 2);\n",
|
|
" assert(seq == 18); return 0;\n",
|
|
"};\n"));
|
|
let stages: []str = ["ww", "ww_ww"];
|
|
let tags: []str = ["c", "ww"];
|
|
let works: []str = [strings.concat(td, "/work-c"),
|
|
strings.concat(td, "/work-ww")];
|
|
let progs: []str = [strings.concat(td, "/agg-c"),
|
|
strings.concat(td, "/agg-ww")];
|
|
let i: i32 = 0;
|
|
for (i < 2) {
|
|
assert(os.mkdir(works[i], 493) == 0);
|
|
let av: []str = [testenv.driver(stages[i]), "build", "-w", works[i],
|
|
"-I", td, "-o", progs[i], pkg];
|
|
let out: testenv.commandout;
|
|
run(td, strings.concat("aggregate-build-", tags[i]), av, &out);
|
|
if (exitcode(&out) != 0) { fail("aggregate", out.stderr); };
|
|
let rav: []str = [progs[i]];
|
|
run(td, strings.concat("aggregate-run-", tags[i]), rav, &out);
|
|
if (exitcode(&out) != 0) {
|
|
fail("aggregate", strings.concat("runtime failed: ", out.stderr));
|
|
};
|
|
let asm: str = testenv.readfile(strings.concat(works[i], "/agg.s"));
|
|
let own: str = "__ww..pkg.p.agg.v0.r0.init";
|
|
if (testenv.occurrences(asm,
|
|
strings.concat(own, ".v.2.b.1")) != 3
|
|
|| testenv.occurrences(asm,
|
|
strings.concat(own, ".v.4.b.1")) != 3
|
|
|| testenv.occurrences(asm,
|
|
strings.concat(own, ".v.5.b.1")) != 2
|
|
|| testenv.occurrences(asm,
|
|
strings.concat(own, ".v.5.b.2")) != 2) {
|
|
fail("aggregate", "canonical slice backing ownership");
|
|
};
|
|
let iface: str = testenv.readfile(strings.concat(works[i], "/agg.wwi"));
|
|
if (testenv.has(iface, "__ww..") || testenv.has(iface, "fn init")) {
|
|
fail("aggregate", "runtime backing leaked to semantic export");
|
|
};
|
|
i += 1;
|
|
};
|
|
let files: []str = ["agg.unit.ww", "agg.wwi", "agg.s", "agg.o",
|
|
"agg.a", "agg.init.unit.ww", "agg.init.s", "agg.init.o"];
|
|
i = 0;
|
|
for (i < files.len) {
|
|
comparefile("aggregate-stage-byte-id",
|
|
strings.concat(works[0], "/", files[i]),
|
|
strings.concat(works[1], "/", files[i]));
|
|
i += 1;
|
|
};
|
|
comparefile("aggregate-stage-byte-id", progs[0], progs[1]);
|
|
testenv.clean(td);
|
|
};
|
|
|
|
@test fn blank_import_resolution_and_visibility() void = {
|
|
let td: str = testenv.fresh();
|
|
let dirs: []str = [
|
|
"/domain", "/domain/internal", "/domain/internal/secret",
|
|
"/domain/client", "/domain/app", "/domain/app/client",
|
|
"/domain/app/vendor", "/domain/app/vendor/lib",
|
|
"/domain/app/vendor/lib/side", "/outside", "/outsidevendor",
|
|
"/program", "/importprogram", "/blankmissing", "/selfpkg",
|
|
"/cyclea", "/cycleb", "/tools",
|
|
];
|
|
let di: i32 = 0;
|
|
for (di < dirs.len) {
|
|
assert(os.mkdir(strings.concat(td, dirs[di]), 493) == 0);
|
|
di += 1;
|
|
};
|
|
testenv.writefile(strings.concat(td, "/domain/internal/secret/secret.ww"),
|
|
strings.concat("package hiddenname;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"I\".ptr, 1u64); };\n"));
|
|
testenv.writefile(strings.concat(td, "/domain/client/main.ww"),
|
|
strings.concat("package main;\nimport _ domain.internal.secret;\n",
|
|
"import os;\nfn main() i32 = { os.write(1, \"M\".ptr, 1u64); return 0; };\n"));
|
|
testenv.writefile(strings.concat(td, "/outside/main.ww"),
|
|
"package main;\nimport _ domain.internal.secret;\nfn main() i32 = { return 0; };\n");
|
|
testenv.writefile(strings.concat(td, "/domain/app/vendor/lib/side/side.ww"),
|
|
strings.concat("package localname;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"V\".ptr, 1u64); };\n"));
|
|
testenv.writefile(strings.concat(td, "/domain/app/client/main.ww"),
|
|
strings.concat("package main;\nimport _ lib.side;\nimport os;\n",
|
|
"fn main() i32 = { os.write(1, \"M\".ptr, 1u64); return 0; };\n"));
|
|
testenv.writefile(strings.concat(td, "/outsidevendor/main.ww"),
|
|
"package main;\nimport _ domain.app.vendor.lib.side;\nfn main() i32 = { return 0; };\n");
|
|
testenv.writefile(strings.concat(td, "/program/main.ww"),
|
|
"package main;\nfn main() i32 = { return 0; };\n");
|
|
testenv.writefile(strings.concat(td, "/importprogram/main.ww"),
|
|
"package main;\nimport _ program;\nfn main() i32 = { return 0; };\n");
|
|
testenv.writefile(strings.concat(td, "/blankmissing/main.ww"),
|
|
"package main;\nimport _ nowhere;\nfn main() i32 = { return 0; };\n");
|
|
testenv.writefile(strings.concat(td, "/selfpkg/self.ww"),
|
|
"package selfpkg;\nimport _ selfpkg;\n");
|
|
testenv.writefile(strings.concat(td, "/cyclea/a.ww"),
|
|
"package cyclea;\nimport _ cycleb;\n");
|
|
testenv.writefile(strings.concat(td, "/cycleb/b.ww"),
|
|
"package cycleb;\nimport _ cyclea;\n");
|
|
|
|
let wrapper: str = strings.concat(td, "/tools/w6c-wrapper.sh");
|
|
testenv.writeexecutable(wrapper, strings.concat(
|
|
"#!/bin/sh\n",
|
|
"printf 'BEGIN' >> \"$WW_SEPINIT_W6C_TRACE\"\n",
|
|
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
|
"\"$WW_SEPINIT_W6C_TRACE\"; done\n",
|
|
"printf '\\n' >> \"$WW_SEPINIT_W6C_TRACE\"\n",
|
|
"exec \"$WW_SEPINIT_REAL_W6C\" \"$@\"\n"));
|
|
let stages: []str = ["ww", "ww_ww"];
|
|
let compilers: []str = ["w6c", "w6c_ww"];
|
|
let tags: []str = ["c", "ww"];
|
|
let diagrefs: []str = ["", "", "", "", "", ""];
|
|
let si: i32 = 0;
|
|
for (si < stages.len) {
|
|
let work: str = strings.concat(td, "/work-", tags[si]);
|
|
assert(os.mkdir(work, 493) == 0);
|
|
let trace: str = strings.concat(td, "/compiler-", tags[si], ".trace");
|
|
let env: []str = compilertraceenv(wrapper,
|
|
testenv.driver(compilers[si]), trace);
|
|
let internalbin: str = strings.concat(td, "/internal-", tags[si]);
|
|
testenv.writefile(trace, "");
|
|
let av: []str = [testenv.driver(stages[si]), "build", "-w", work,
|
|
"-I", td, "-o", internalbin,
|
|
strings.concat(td, "/domain/client")];
|
|
let out: testenv.commandout;
|
|
testenv.runcommandenv(td, td, strings.concat("blank-internal-build-",
|
|
tags[si]), av, env, tmo(), &out);
|
|
if (exitcode(&out) != 0) { fail("blank-internal", out.stderr); };
|
|
let rav: []str = [internalbin];
|
|
run(td, strings.concat("blank-internal-run-", tags[si]), rav, &out);
|
|
if (exitcode(&out) != 0 || !testenv.same(out.stdout, "IM")) {
|
|
fail("blank-internal", out.stdout);
|
|
};
|
|
|
|
let vendorbin: str = strings.concat(td, "/vendor-", tags[si]);
|
|
replacefile(trace, "");
|
|
let vav: []str = [testenv.driver(stages[si]), "build", "-w", work,
|
|
"-I", td, "-o", vendorbin,
|
|
strings.concat(td, "/domain/app/client")];
|
|
testenv.runcommandenv(td, td, strings.concat("blank-vendor-build-",
|
|
tags[si]), vav, env, tmo(), &out);
|
|
if (exitcode(&out) != 0) { fail("blank-vendor", out.stderr); };
|
|
let vrav: []str = [vendorbin];
|
|
run(td, strings.concat("blank-vendor-run-", tags[si]), vrav, &out);
|
|
if (exitcode(&out) != 0 || !testenv.same(out.stdout, "VM")) {
|
|
fail("blank-vendor", out.stdout);
|
|
};
|
|
let compilerlog: str = testenv.readfile(trace);
|
|
if (!testenv.has(compilerlog,
|
|
"<--import><domain.app.vendor.lib.side><")
|
|
|| !testenv.has(compilerlog,
|
|
"<--import-map><lib.side><domain.app.vendor.lib.side>")
|
|
|| testenv.has(compilerlog, "<_>")) {
|
|
fail("blank-vendor", compilerlog);
|
|
};
|
|
let unit: str = testenv.readfile(strings.concat(work,
|
|
"/domain.app.client.unit.ww"));
|
|
if (!testenv.has(unit, "import _ lib.side;")
|
|
|| !testenv.has(unit,
|
|
"//ww:import-map lib.side domain.app.vendor.lib.side ")) {
|
|
fail("blank-vendor", "owner unit lost blank/vendor identity");
|
|
};
|
|
let dispatch: str = testenv.readfile(strings.concat(work,
|
|
"/domain.app.client.init.unit.ww"));
|
|
if (testenv.occurrences(dispatch,
|
|
"__ww..pkg.p.domain.app.vendor.lib.side.v0.r0.init") != 1
|
|
|| testenv.has(dispatch, "localname")) {
|
|
fail("blank-vendor", "dispatcher identity used a local name");
|
|
};
|
|
|
|
let rejectdirs: []str = ["/outside", "/outsidevendor",
|
|
"/importprogram", "/blankmissing", "/selfpkg", "/cyclea"];
|
|
let rejectlabels: []str = ["internal", "vendor", "main", "missing",
|
|
"self", "cycle"];
|
|
let needles: []str = ["use of internal package domain.internal.secret not allowed",
|
|
"use of vendored package not allowed",
|
|
"package program is a program, not an importable package",
|
|
"cannot find package nowhere",
|
|
"self-import: package 'selfpkg' cannot import itself",
|
|
"ww: dependency cycle: cyclea -> cycleb -> cyclea"];
|
|
let ri: i32 = 0;
|
|
for (ri < rejectdirs.len) {
|
|
replacefile(trace, "");
|
|
let rejected: str = strings.concat(td, "/reject-", tags[si], "-",
|
|
rejectlabels[ri]);
|
|
let rejectav: []str = [testenv.driver(stages[si]), "build", "-w", work,
|
|
"-I", td, "-o", rejected,
|
|
strings.concat(td, rejectdirs[ri])];
|
|
testenv.runcommandenv(td, td, strings.concat("blank-reject-",
|
|
tags[si], "-", rejectlabels[ri]), rejectav, env, tmo(), &out);
|
|
if (exitcode(&out) == 0 || testenv.exists(rejected)
|
|
|| !testenv.has(out.stderr, needles[ri])
|
|
|| testenv.readfile(trace).len != 0) {
|
|
fail("blank-visibility", out.stderr);
|
|
};
|
|
let diag: str = normalized(out.stderr, work);
|
|
if (si == 0) { diagrefs[ri] = strings.dup(diag); }
|
|
else { if (!testenv.same(diagrefs[ri], diag)) {
|
|
fail("blank-visibility", "stage diagnostic mismatch");
|
|
}; };
|
|
ri += 1;
|
|
};
|
|
si += 1;
|
|
};
|
|
testenv.clean(td);
|
|
};
|
|
|
|
@test fn direct_recursive_and_reordered_roots_match() void = {
|
|
let td: str = testenv.fresh();
|
|
let tree: str = strings.concat(td, "/tree");
|
|
let dep: str = strings.concat(tree, "/dep");
|
|
let cmd: str = strings.concat(tree, "/cmd");
|
|
assert(os.mkdir(tree, 493) == 0);
|
|
assert(os.mkdir(dep, 493) == 0);
|
|
assert(os.mkdir(cmd, 493) == 0);
|
|
testenv.writefile(strings.concat(dep, "/dep.ww"), strings.concat(
|
|
"package hidden;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"D\".ptr, 1u64); };\n"));
|
|
testenv.writefile(strings.concat(cmd, "/main.ww"), strings.concat(
|
|
"package main;\nimport _ dep;\nimport os;\n",
|
|
"fn main() i32 = { os.write(1, \"M\".ptr, 1u64); return 0; };\n"));
|
|
let files: []str = ["dep.unit.ww", "dep.wwi", "dep.s", "dep.o",
|
|
"dep.a", "cmd.unit.ww", "cmd.wwi", "cmd.s", "cmd.o", "cmd.a",
|
|
"cmd.init.unit.ww", "cmd.init.s", "cmd.init.o"];
|
|
let refs: []str = alloc([], files.len: u64)!;
|
|
let fi: i32 = 0;
|
|
for (fi < files.len) { append(refs, ""); fi += 1; };
|
|
let binref: str = "";
|
|
let stages: []str = ["ww", "ww_ww"];
|
|
let tags: []str = ["c", "ww"];
|
|
let mode: i32 = 0;
|
|
for (mode < 2) {
|
|
let si: i32 = 0;
|
|
for (si < stages.len) {
|
|
let mt: str = "direct";
|
|
if (mode != 0) { mt = "recursive"; };
|
|
let work: str = strings.concat(td, "/work-", mt, "-", tags[si]);
|
|
let outdir: str = strings.concat(td, "/out-", mt, "-", tags[si]);
|
|
assert(os.mkdir(work, 493) == 0);
|
|
assert(os.mkdir(outdir, 493) == 0);
|
|
let out: testenv.commandout;
|
|
if (mode == 0 && si == 0) {
|
|
let av: []str = [testenv.driver(stages[si]), "build", "-w",
|
|
work, "-I", tree, "-o", outdir, cmd, dep];
|
|
run(td, "direct-c-cmd-first", av, &out);
|
|
} else { if (mode == 0) {
|
|
let av: []str = [testenv.driver(stages[si]), "build", "-w",
|
|
work, "-I", tree, "-o", outdir, dep, cmd];
|
|
run(td, "direct-ww-dep-first", av, &out);
|
|
} else {
|
|
let av: []str = [testenv.driver(stages[si]), "build", "-w",
|
|
work, "-I", tree, "-o", outdir,
|
|
strings.concat(tree, "/...")];
|
|
run(td, strings.concat("recursive-", tags[si]), av, &out);
|
|
}; };
|
|
if (exitcode(&out) != 0) { fail("direct-recursive", out.stderr); };
|
|
let bin: str = strings.concat(outdir, "/cmd");
|
|
let rav: []str = [bin];
|
|
run(td, strings.concat("direct-recursive-run-", mt, "-", tags[si]),
|
|
rav, &out);
|
|
if (exitcode(&out) != 0 || !testenv.same(out.stdout, "DM")) {
|
|
fail("direct-recursive", out.stdout);
|
|
};
|
|
let dispatch: str = testenv.readfile(strings.concat(work,
|
|
"/cmd.init.unit.ww"));
|
|
if (testenv.occurrences(dispatch,
|
|
"//ww:init-call __ww..pkg.p.dep.v0.r0.init") != 1
|
|
|| testenv.pos(dispatch,
|
|
"//ww:init-call __ww..pkg.p.dep.v0.r0.init")
|
|
>= testenv.pos(dispatch,
|
|
"//ww:init-call __ww..pkg.p.cmd.v0.r0.init")) {
|
|
fail("direct-recursive", "dispatcher order changed with roots");
|
|
};
|
|
fi = 0;
|
|
for (fi < files.len) {
|
|
let bytes: str = testenv.readfile(strings.concat(work, "/",
|
|
files[fi]));
|
|
if (mode == 0 && si == 0) { refs[fi] = strings.dup(bytes); }
|
|
else { if (!testenv.same(refs[fi], bytes)) {
|
|
fail("direct-recursive", strings.concat("artifact differs: ",
|
|
files[fi]));
|
|
}; };
|
|
fi += 1;
|
|
};
|
|
let binbytes: str = testenv.readfile(bin);
|
|
if (mode == 0 && si == 0) { binref = strings.dup(binbytes); }
|
|
else { if (!testenv.same(binref, binbytes)) {
|
|
fail("direct-recursive", "binary differs");
|
|
}; };
|
|
si += 1;
|
|
};
|
|
mode += 1;
|
|
};
|
|
testenv.clean(td);
|
|
};
|
|
|
|
fn rejectrow(td: str, label: str, src: str, expected: str) void = {
|
|
let stages: []str = ["ww", "ww_ww"];
|
|
let tags: []str = ["c", "ww"];
|
|
let i: i32 = 0;
|
|
for (i < 2) {
|
|
let prog: str = strings.concat(td, "/", label, "-", tags[i]);
|
|
let av: []str = [testenv.driver(stages[i]), "build", "-I", td,
|
|
"-o", prog, src];
|
|
let out: testenv.commandout;
|
|
run(td, strings.concat(label, "-", tags[i]), av, &out);
|
|
let work: str = strings.concat(prog, ".sepwork");
|
|
let diag: str = normalized(out.stderr, work);
|
|
if (exitcode(&out) != 1 || testenv.exists(prog)
|
|
|| testenv.exists(work) || !testenv.same(diag, expected)) {
|
|
fail(label, diag);
|
|
};
|
|
i += 1;
|
|
};
|
|
};
|
|
|
|
@test fn special_init_declarations() void = {
|
|
let td: str = testenv.fresh();
|
|
let dep: str = strings.concat(td, "/dep");
|
|
assert(os.mkdir(dep, 493) == 0);
|
|
testenv.writefile(strings.concat(dep, "/dep.ww"), strings.concat(
|
|
"package dep;\nfn init() void = {};\n",
|
|
"export fn value() i32 = { return 1; };\n"));
|
|
let labels: []str = ["direct", "qualified", "exported", "params",
|
|
"result", "error-result", "error-alias-result", "missing",
|
|
"attribute", "nonfunc", "reserved"];
|
|
let bodies: []str = [
|
|
"package main;\nfn init() void = {};\nfn main() i32 = { init(); return 0; };\n",
|
|
"package main;\nimport dep;\nfn main() i32 = { dep.init(); return 0; };\n",
|
|
"package main;\nexport fn init() void = {};\nfn main() i32 = { return 0; };\n",
|
|
"package main;\nfn init(x: i32) void = {};\nfn main() i32 = { return 0; };\n",
|
|
"package main;\nfn init() i32 = { return 1; };\nfn main() i32 = { return 0; };\n",
|
|
"package main;\nfn init() !void = {};\nfn main() i32 = { return 0; };\n",
|
|
"package main;\ntype errvoid = !void;\nfn init() errvoid = {};\nfn main() i32 = { return 0; };\n",
|
|
"package main;\nfn init() void;\nfn main() i32 = { return 0; };\n",
|
|
"package main;\n@test fn init() void = {};\nfn main() i32 = { return 0; };\n",
|
|
"package main;\nlet init: i32 = 1;\nfn main() i32 = { return init; };\n",
|
|
"package main;\n@symbol(\"__ww..dispatch\") fn claim() void = {};\nfn main() i32 = { return 0; };\n",
|
|
];
|
|
let expected: []str = [
|
|
strings.concat("$WORK/__root.unit.ww:4:19: error: undefined: init\n",
|
|
"ww: w6c failed for (root)\n"),
|
|
strings.concat("$WORK/__root.unit.ww:4:19: error: package 'dep' has no exported declaration 'init'\n",
|
|
"ww: w6c failed for (root)\n"),
|
|
strings.concat("$WORK/__root.unit.ww:3:8: error: func init cannot be exported\n",
|
|
"ww: w6c failed for (root)\n"),
|
|
strings.concat("$WORK/__root.unit.ww:3:1: error: func init must have no arguments and no return values\n",
|
|
"ww: w6c failed for (root)\n"),
|
|
strings.concat("$WORK/__root.unit.ww:3:1: error: func init must have no arguments and no return values\n",
|
|
"ww: w6c failed for (root)\n"),
|
|
strings.concat("$WORK/__root.unit.ww:3:1: error: func init must have no arguments and no return values\n",
|
|
"ww: w6c failed for (root)\n"),
|
|
strings.concat("$WORK/__root.unit.ww:4:1: error: func init must have no arguments and no return values\n",
|
|
"ww: w6c failed for (root)\n"),
|
|
strings.concat("$WORK/__root.unit.ww:3:1: error: func init must have a body\n",
|
|
"ww: w6c failed for (root)\n"),
|
|
strings.concat("$WORK/__root.unit.ww:3:7: error: func init cannot have attributes\n",
|
|
"ww: w6c failed for (root)\n"),
|
|
strings.concat("$WORK/__root.unit.ww:3:5: error: cannot declare init - must be func\n",
|
|
"$WORK/__root.unit.ww:4:26: error: undefined: init\n",
|
|
"ww: w6c failed for (root)\n"),
|
|
strings.concat("$WORK/__root.unit.ww:3:27: error: @symbol name uses reserved prefix __ww..\n",
|
|
"ww: w6c failed for (root)\n"),
|
|
];
|
|
let i: i32 = 0;
|
|
for (i < labels.len) {
|
|
let src: str = strings.concat(td, "/", labels[i], ".ww");
|
|
testenv.writefile(src, bodies[i]);
|
|
rejectrow(td, labels[i], src, expected[i]);
|
|
i += 1;
|
|
};
|
|
testenv.clean(td);
|
|
};
|
|
|
|
@test fn allocation_failure_is_command_global() void = {
|
|
let td: str = testenv.fresh();
|
|
let tree: str = strings.concat(td, "/tree");
|
|
assert(os.mkdir(tree, 493) == 0);
|
|
let target: str = strings.concat(tree, "/target");
|
|
let bridge: str = strings.concat(tree, "/bridge");
|
|
assert(os.mkdir(target, 493) == 0);
|
|
assert(os.mkdir(bridge, 493) == 0);
|
|
let body: str = "package target;\n";
|
|
let i: i32 = 0;
|
|
for (i < 600) {
|
|
let name: str = allocpkgname(i);
|
|
let dir: str = strings.concat(tree, "/", name);
|
|
assert(os.mkdir(dir, 493) == 0);
|
|
testenv.writefile(strings.concat(dir, "/side.ww"),
|
|
"package side;\nfn init() void = {};\n");
|
|
body = strings.concat(body, "import _ ", name, ";\n");
|
|
i += 1;
|
|
};
|
|
body = strings.concat(body,
|
|
"export fn value() i32 = { return 41; };\n");
|
|
testenv.writefile(strings.concat(target, "/target.ww"), body);
|
|
testenv.writefile(strings.concat(target, "/same_test.ww"), strings.concat(
|
|
"package target;\n",
|
|
"export fn internal_helper() i32 = { return value(); };\n",
|
|
"@test fn internal_runs() void = { assert(value() == 41); };\n"));
|
|
testenv.writefile(strings.concat(bridge, "/bridge.ww"), strings.concat(
|
|
"package bridge;\nimport target;\n",
|
|
"export fn helper() i32 = { return target.internal_helper(); };\n"));
|
|
testenv.writefile(strings.concat(target, "/external_test.ww"),
|
|
strings.concat("package target_test;\nimport bridge;\nimport target;\n",
|
|
"@test fn external_runs() void = {\n",
|
|
" assert(target.internal_helper() == 41);\n",
|
|
" assert(bridge.helper() == 41);\n};\n"));
|
|
let wrapper: str = strings.concat(td, "/tool-trace.sh");
|
|
testenv.writeexecutable(wrapper, strings.concat("#!/bin/sh\n",
|
|
"printf x >> \"$WW_SEPINIT_TOOL_TRACE\"\nexit 99\n"));
|
|
let stages: []str = ["ww", "ww_ww"];
|
|
let launcher: str = testenv.driver("sep-limitexec");
|
|
let accepted: [2]str;
|
|
let stagei: i32 = 0;
|
|
// Address-space layouts differ, so each implementation searches its own
|
|
// bounded ceiling. The accepted point must fail while constructing the
|
|
// combined p/ptest/pxtest/pmain and transitive-clone graph, before any tool.
|
|
for (stagei < stages.len) {
|
|
let found: bool = false;
|
|
let last: str = "";
|
|
let limit: i64 = 2621440i64;
|
|
let li: i32 = 0;
|
|
for (limit <= 33554432i64 && !found) {
|
|
let tag: str = allocpkgname(li);
|
|
let trace: str = strings.concat(td, "/alloc-", stages[stagei], "-",
|
|
tag, ".trace");
|
|
let work: str = strings.concat(td, "/alloc-work-", stages[stagei],
|
|
"-", tag);
|
|
let output: str = strings.concat(td, "/alloc-output-",
|
|
stages[stagei], "-", tag);
|
|
let status: str = strings.concat(td, "/alloc-status-",
|
|
stages[stagei], "-", tag);
|
|
testenv.writefile(trace, "");
|
|
assert(os.mkdir(work, 493) == 0);
|
|
let base: []str = os.getenvs();
|
|
let env: []str = alloc([], (base.len + 4): u64)!;
|
|
i = 0;
|
|
for (i < base.len) {
|
|
if (!strings.hasprefix(base[i], "WW_W6C=")
|
|
&& !strings.hasprefix(base[i], "WW_W6A=")
|
|
&& !strings.hasprefix(base[i], "WW_W6L=")
|
|
&& !strings.hasprefix(base[i],
|
|
"WW_SEPINIT_TOOL_TRACE=")) {
|
|
append(env, base[i]);
|
|
};
|
|
i += 1;
|
|
};
|
|
append(env, strings.concat("WW_W6C=", wrapper));
|
|
append(env, strings.concat("WW_W6A=", wrapper));
|
|
append(env, strings.concat("WW_W6L=", wrapper));
|
|
append(env, strings.concat("WW_SEPINIT_TOOL_TRACE=", trace));
|
|
let limitstr: str = strconv.i64tos(limit, strconv.base.DEC);
|
|
let av: []str = [launcher, limitstr, testenv.driver(stages[stagei]),
|
|
"test", "-c", "-w", work, "-I", tree,
|
|
"--ww-package-test", "test", "target", "target", "target",
|
|
"target_test", target, output, "-", status, target];
|
|
let out: testenv.commandout;
|
|
runenv(td, strings.concat("allocation-", stages[stagei], "-", tag),
|
|
av, env, &out);
|
|
last = strings.dup(out.stderr);
|
|
if (testenv.listdir(work).len != 0
|
|
|| nofollowexists(output)
|
|
|| nofollowexists(strings.concat(output, ".new"))
|
|
|| nofollowexists(strings.concat(output, ".sepwork"))
|
|
|| nofollowexists(status)
|
|
|| nofollowexists(strings.concat(status, ".new"))) {
|
|
fail("allocation", "failed pressure leg published state");
|
|
};
|
|
if (exitcode(&out) == 1 && out.stdout.len == 0
|
|
&& testenv.same(out.stderr, "ww: out of memory\n")
|
|
&& testenv.readfile(trace).len == 0) {
|
|
accepted[stagei] = strings.dup(out.stderr);
|
|
found = true;
|
|
};
|
|
testenv.clean(work);
|
|
limit += 131072i64;
|
|
li += 1;
|
|
};
|
|
if (!found) { fail("allocation", last); };
|
|
stagei += 1;
|
|
};
|
|
if (!testenv.same(accepted[0], accepted[1])) {
|
|
fail("allocation", "C/WW allocation diagnostic mismatch");
|
|
};
|
|
testenv.clean(td);
|
|
};
|
|
|
|
@test fn compiler_publication_is_nofollow_and_atomic() void = {
|
|
let td: str = testenv.fresh();
|
|
let src: str = strings.concat(td, "/p.ww");
|
|
testenv.writefile(src,
|
|
"package p;\nexport fn value() i32 = { return 7; };\n");
|
|
let wrapper: str = strings.concat(td, "/publish-wrapper.sh");
|
|
testenv.writeexecutable(wrapper, strings.concat(
|
|
"#!/bin/sh\n",
|
|
"out=\nprev=\n",
|
|
"for arg do\n",
|
|
" if [ \"$prev\" = -o ]; then out=$arg; fi\n",
|
|
" prev=$arg\n",
|
|
"done\n",
|
|
"backup=${out}.w6c.$$.old\n",
|
|
"printf '%s' \"$backup\" > \"$WW_SEPINIT_W6C_TRACE\"\n",
|
|
"ln -s \"${WW_SEPINIT_W6C_TRACE}.missing\" \"$backup\" || exit 98\n",
|
|
"exec \"$WW_SEPINIT_REAL_W6C\" \"$@\"\n"));
|
|
let writefail: str = strings.concat(td, "/writefail-wrapper.sh");
|
|
testenv.writeexecutable(writefail, strings.concat(
|
|
"#!/bin/sh\n",
|
|
"trap '' XFSZ\n",
|
|
"ulimit -f 0 || exit 97\n",
|
|
"exec \"$WW_SEPINIT_REAL_W6C\" \"$@\"\n"));
|
|
let compilers: []str = ["w6c", "w6c_ww"];
|
|
let tags: []str = ["c", "ww"];
|
|
let i: i32 = 0;
|
|
for (i < compilers.len) {
|
|
let asm: str = strings.concat(td, "/publish-", tags[i], ".s");
|
|
let iface: str = strings.concat(td, "/publish-", tags[i], ".wwi");
|
|
let record: str = strings.concat(td, "/publish-", tags[i], ".path");
|
|
testenv.writefile(asm, "old assembly\n");
|
|
testenv.writefile(iface, "old interface\n");
|
|
testenv.writefile(record, "");
|
|
let env: []str = compilertraceenv(wrapper,
|
|
testenv.driver(compilers[i]), record);
|
|
let av: []str = [wrapper, "-c", "--package-init-symbol",
|
|
"__ww..pkg.p.pub.v0.r0.init", "-I", iface, "-o", asm, src];
|
|
let out: testenv.commandout;
|
|
runenv(td, strings.concat("compiler-publish-", tags[i]), av, env, &out);
|
|
let backup: str = testenv.readfile(record);
|
|
let want: str = strings.concat(
|
|
"w6c: publication backup exists for ", asm, "\n");
|
|
if (exitcode(&out) != 1 || !testenv.same(out.stderr, want)
|
|
|| !nofollowexists(backup)
|
|
|| testenv.exists(strings.concat(record, ".missing"))
|
|
|| !testenv.same(testenv.readfile(asm), "old assembly\n")
|
|
|| !testenv.same(testenv.readfile(iface), "old interface\n")
|
|
|| !nostaged(td)) {
|
|
fail("compiler-publication", out.stderr);
|
|
};
|
|
assert(os.remove(backup) == 0);
|
|
|
|
let dirout: str = strings.concat(td, "/publish-dir-", tags[i]);
|
|
assert(os.mkdir(dirout, 493) == 0);
|
|
let dirav: []str = [testenv.driver(compilers[i]), "-c",
|
|
"--package-init-symbol", "__ww..pkg.p.pub.v0.r0.init",
|
|
"-I", iface, "-o", dirout, src];
|
|
run(td, strings.concat("compiler-publish-dir-", tags[i]), dirav, &out);
|
|
want = strings.concat(
|
|
"w6c: publication destination is not a regular file: ", dirout,
|
|
"\n");
|
|
if (exitcode(&out) != 1 || !testenv.same(out.stderr, want)
|
|
|| testenv.listdir(dirout).len != 0
|
|
|| !testenv.same(testenv.readfile(asm), "old assembly\n")
|
|
|| !testenv.same(testenv.readfile(iface), "old interface\n")
|
|
|| !nostaged(td)) {
|
|
fail("compiler-publication-directory", out.stderr);
|
|
};
|
|
|
|
let writeenv: []str = compilertraceenv(writefail,
|
|
testenv.driver(compilers[i]), record);
|
|
let writeav: []str = [writefail, "-c", "--package-init-symbol",
|
|
"__ww..pkg.p.pub.v0.r0.init", "-I", iface, "-o", asm, src];
|
|
runenv(td, strings.concat("compiler-write-failure-", tags[i]), writeav,
|
|
writeenv, &out);
|
|
if (exitcode(&out) != 1 || out.stderr.len != 0
|
|
|| !testenv.same(testenv.readfile(asm), "old assembly\n")
|
|
|| !testenv.same(testenv.readfile(iface), "old interface\n")
|
|
|| !nostaged(td)) {
|
|
fail("compiler-write-failure", out.stderr);
|
|
};
|
|
i += 1;
|
|
};
|
|
testenv.clean(td);
|
|
};
|
|
|
|
@test fn test_variant_initialization() void = {
|
|
let td: str = testenv.fresh();
|
|
let widget: str = strings.concat(td, "/widget");
|
|
let prodside: str = strings.concat(td, "/prodside");
|
|
let testside: str = strings.concat(td, "/testside");
|
|
let client: str = strings.concat(td, "/client");
|
|
assert(os.mkdir(widget, 493) == 0);
|
|
assert(os.mkdir(prodside, 493) == 0);
|
|
assert(os.mkdir(testside, 493) == 0);
|
|
assert(os.mkdir(client, 493) == 0);
|
|
testenv.writefile(strings.concat(prodside, "/prodside.ww"), strings.concat(
|
|
"package prodside;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"<D>\".ptr, 3u64); };\n"));
|
|
testenv.writefile(strings.concat(testside, "/testside.ww"), strings.concat(
|
|
"package testside;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"<Q>\".ptr, 3u64); };\n"));
|
|
testenv.writefile(strings.concat(widget, "/widget.ww"), strings.concat(
|
|
"package widget;\nimport _ prodside;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"<P>\".ptr, 3u64); };\n",
|
|
"export fn value() i32 = { return 1; };\n"));
|
|
testenv.writefile(strings.concat(widget, "/a_test.ww"), strings.concat(
|
|
"package widget;\nimport _ testside;\nimport os;\n",
|
|
"let InternalState: i32 = makeinternal();\n",
|
|
"fn makeinternal() i32 = { os.write(1, \"<R>\".ptr, 3u64); return 7; };\n",
|
|
"fn init() void = { assert(InternalState == 7); os.write(1, \"<I>\".ptr, 3u64); };\n",
|
|
"@test fn internal() void = { os.write(1, \"<T>\".ptr, 3u64); assert(value() == 1); };\n"));
|
|
testenv.writefile(strings.concat(widget, "/z_test.ww"), strings.concat(
|
|
"package widget_test;\nimport os;\nimport widget;\n",
|
|
"let ExternalState: i32 = makeexternal();\n",
|
|
"fn makeexternal() i32 = { os.write(1, \"<W>\".ptr, 3u64); return 9; };\n",
|
|
"fn init() void = { assert(ExternalState == 9); os.write(1, \"<E>\".ptr, 3u64); };\n",
|
|
"@test fn external() void = { os.write(1, \"<X>\".ptr, 3u64); assert(widget.value() == 1); };\n"));
|
|
testenv.writefile(strings.concat(client, "/main.ww"), strings.concat(
|
|
"package main;\nimport os;\nimport widget;\n",
|
|
"fn main() i32 = { os.write(1, \"<M>\".ptr, 3u64); return widget.value() - 1; };\n"));
|
|
let stages: []str = ["ww", "ww_ww"];
|
|
let tags: []str = ["c", "ww"];
|
|
let reference: str = "";
|
|
let variantnames: []str = [
|
|
"widget-test-main.init.unit.ww",
|
|
"widget-test-main.init.s",
|
|
"widget-test-main.init.o",
|
|
"widget-test-main.a",
|
|
];
|
|
let variantrefs: []str = ["", "", "", ""];
|
|
let i: i32 = 0;
|
|
for (i < 2) {
|
|
let work: str = strings.concat(td, "/test-work-", tags[i]);
|
|
assert(os.mkdir(work, 493) == 0);
|
|
let prog: str = strings.concat(td, "/client-", tags[i]);
|
|
let bav: []str = [testenv.driver(stages[i]), "build", "-w", work,
|
|
"-I", td, "-o", prog, client];
|
|
let out: testenv.commandout;
|
|
run(td, strings.concat("client-build-", tags[i]), bav, &out);
|
|
if (exitcode(&out) != 0) { fail("variants", out.stderr); };
|
|
let rav: []str = [prog];
|
|
run(td, strings.concat("client-run-", tags[i]), rav, &out);
|
|
if (exitcode(&out) != 0 || !testenv.same(out.stdout, "<D><P><M>")) {
|
|
fail("variants", strings.concat("production leaked tests: ", out.stdout));
|
|
};
|
|
let tav: []str = [testenv.driver(stages[i]), "test", "-w", work,
|
|
"-I", td, widget];
|
|
run(td, strings.concat("test-run-", tags[i]), tav, &out);
|
|
if (exitcode(&out) != 0) { fail("variants", out.stderr); };
|
|
let d: i32 = testenv.pos(out.stdout, "<D>");
|
|
let q: i32 = testenv.pos(out.stdout, "<Q>");
|
|
let p: i32 = testenv.pos(out.stdout, "<P>");
|
|
let rr: i32 = testenv.pos(out.stdout, "<R>");
|
|
let ii: i32 = testenv.pos(out.stdout, "<I>");
|
|
let tt: i32 = testenv.pos(out.stdout, "<T>");
|
|
let w: i32 = testenv.pos(out.stdout, "<W>");
|
|
let e: i32 = testenv.pos(out.stdout, "<E>");
|
|
let x: i32 = testenv.pos(out.stdout, "<X>");
|
|
if (!(d >= 0 && d < q && q < rr && rr < p && p < ii && ii < w
|
|
&& w < e && e < tt && tt < x)) {
|
|
fail("variants", strings.concat("test init order: ", out.stdout));
|
|
};
|
|
if (testenv.occurrences(out.stdout, "<Q>") != 1
|
|
|| testenv.occurrences(out.stdout, "<D>") != 1
|
|
|| testenv.occurrences(out.stdout, "<R>") != 1
|
|
|| testenv.occurrences(out.stdout, "<W>") != 1
|
|
|| testenv.occurrences(out.stdout, "<I>") != 1
|
|
|| testenv.occurrences(out.stdout, "<E>") != 1
|
|
|| testenv.occurrences(out.stdout, "<P>") != 1) {
|
|
fail("variants", "variant initialization count");
|
|
};
|
|
let dispatcher: str = testenv.readfile(strings.concat(work,
|
|
"/widget-test-main.init.unit.ww"));
|
|
if (testenv.occurrences(dispatcher,
|
|
"__ww..pkg.p.testside.v0.r0.init") != 1
|
|
|| testenv.occurrences(dispatcher,
|
|
"__ww..pkg.p.prodside.v0.r0.init") != 1
|
|
|| testenv.occurrences(dispatcher,
|
|
"__ww..pkg.p.widget.v1.r0.init") != 1
|
|
|| testenv.occurrences(dispatcher,
|
|
"__ww..pkg.p.widget_test.v2.r0.init") != 1
|
|
|| testenv.occurrences(dispatcher,
|
|
"__ww..pkg.p.test.v0.r0.init") != 1
|
|
|| testenv.occurrences(dispatcher,
|
|
"//ww:init-call __ww..pkg.p.__wwtestmain.widget.main.v3.r2.init") != 1
|
|
|| testenv.pos(dispatcher, "__ww..pkg.p.widget.v1.r0.init")
|
|
>= testenv.pos(dispatcher,
|
|
"__ww..pkg.p.widget_test.v2.r0.init")
|
|
|| testenv.pos(dispatcher,
|
|
"__ww..pkg.p.widget_test.v2.r0.init")
|
|
>= testenv.pos(dispatcher,
|
|
"//ww:init-call __ww..pkg.p.__wwtestmain.widget.main.v3.r2.init")
|
|
|| testenv.has(dispatcher, "__ww..pkg.p.widget.v0.r0.init")) {
|
|
fail("variants", "directory test dispatcher ownership");
|
|
};
|
|
let directstdout: str = strings.dup(out.stdout);
|
|
let recursivework: str = strings.concat(td, "/test-recursive-work-",
|
|
tags[i]);
|
|
assert(os.mkdir(recursivework, 493) == 0);
|
|
let recursiveav: []str = [testenv.driver(stages[i]), "test", "-w",
|
|
recursivework, "-I", td, strings.concat(widget, "/...")];
|
|
run(td, strings.concat("test-recursive-", tags[i]), recursiveav, &out);
|
|
if (exitcode(&out) != 0 || !testenv.same(out.stdout, directstdout)) {
|
|
fail("variants", strings.concat("recursive runtime mismatch: ",
|
|
out.stdout));
|
|
};
|
|
let vi: i32 = 0;
|
|
for (vi < variantnames.len) {
|
|
let directpath: str = strings.concat(work, "/", variantnames[vi]);
|
|
let recursivepath: str = strings.concat(recursivework, "/",
|
|
variantnames[vi]);
|
|
comparefile("variant-direct-recursive", directpath, recursivepath);
|
|
let bytes: str = testenv.readfile(directpath);
|
|
if (i == 0) { variantrefs[vi] = strings.dup(bytes); }
|
|
else { if (!testenv.same(variantrefs[vi], bytes)) {
|
|
fail("variants", strings.concat("stage artifact mismatch: ",
|
|
variantnames[vi]));
|
|
}; };
|
|
vi += 1;
|
|
};
|
|
if (i == 0) { reference = strings.dup(out.stdout); }
|
|
else { if (!testenv.same(reference, out.stdout)) {
|
|
fail("variants", "stage runtime output mismatch");
|
|
}; };
|
|
i += 1;
|
|
};
|
|
testenv.clean(td);
|
|
};
|
|
|
|
@test fn effective_internal_test_cycle_is_pretool() void = {
|
|
let td: str = testenv.fresh();
|
|
let p: str = strings.concat(td, "/p");
|
|
let q: str = strings.concat(td, "/q");
|
|
let r: str = strings.concat(td, "/r");
|
|
let tools: str = strings.concat(td, "/tools");
|
|
assert(os.mkdir(p, 493) == 0);
|
|
assert(os.mkdir(q, 493) == 0);
|
|
assert(os.mkdir(r, 493) == 0);
|
|
assert(os.mkdir(tools, 493) == 0);
|
|
testenv.writefile(strings.concat(p, "/p.ww"),
|
|
"package p;\nexport fn value() i32 = { return 1; };\n");
|
|
testenv.writefile(strings.concat(p, "/p_test.ww"), strings.concat(
|
|
"package p;\nimport q;\n",
|
|
"@test fn cycle() void = { assert(q.value() == 1); };\n"));
|
|
testenv.writefile(strings.concat(q, "/q.ww"),
|
|
"package q;\nimport r;\nexport fn value() i32 = { return r.value(); };\n");
|
|
testenv.writefile(strings.concat(r, "/r.ww"),
|
|
"package r;\nimport p;\nexport fn value() i32 = { return p.value(); };\n");
|
|
let wrapper: str = strings.concat(tools, "/w6c-wrapper.sh");
|
|
testenv.writeexecutable(wrapper, strings.concat(
|
|
"#!/bin/sh\n",
|
|
"printf x >> \"$WW_SEPINIT_W6C_TRACE\"\n",
|
|
"exec \"$WW_SEPINIT_REAL_W6C\" \"$@\"\n"));
|
|
let stages: []str = ["ww", "ww_ww"];
|
|
let compilers: []str = ["w6c", "w6c_ww"];
|
|
let tags: []str = ["c", "ww"];
|
|
let reference: str = "";
|
|
let i: i32 = 0;
|
|
for (i < stages.len) {
|
|
let work: str = strings.concat(td, "/work-", tags[i]);
|
|
let trace: str = strings.concat(td, "/trace-", tags[i]);
|
|
assert(os.mkdir(work, 493) == 0);
|
|
testenv.writefile(trace, "");
|
|
let env: []str = compilertraceenv(wrapper,
|
|
testenv.driver(compilers[i]), trace);
|
|
let av: []str = [testenv.driver(stages[i]), "test", "-w", work,
|
|
"-I", td, p];
|
|
let out: testenv.commandout;
|
|
testenv.runcommandenv(td, td, strings.concat("test-cycle-", tags[i]),
|
|
av, env, tmo(), &out);
|
|
if (exitcode(&out) == 0
|
|
|| !testenv.has(out.stderr,
|
|
"ww: dependency cycle: p -> q -> r -> p")
|
|
|| testenv.readfile(trace).len != 0
|
|
|| testenv.listdir(work).len != 0) {
|
|
fail("test-cycle", out.stderr);
|
|
};
|
|
let diag: str = normalized(out.stderr, work);
|
|
if (i == 0) { reference = strings.dup(diag); }
|
|
else { if (!testenv.same(reference, diag)) {
|
|
fail("test-cycle", "C/WW diagnostic mismatch");
|
|
}; };
|
|
i += 1;
|
|
};
|
|
testenv.clean(td);
|
|
};
|
|
|
|
fn nostaged(work: str) bool = {
|
|
let names: []str = testenv.listdir(work);
|
|
let i: i32 = 0;
|
|
for (i < names.len) {
|
|
if (strings.hassuffix(names[i], ".new")) { return false; };
|
|
i += 1;
|
|
};
|
|
return true;
|
|
};
|
|
|
|
fn nofollowexists(path: str) bool = {
|
|
let fi: os.filestat;
|
|
match (os.lstat(&fi, path)) {
|
|
case void => return true;
|
|
case let e: os.oserror => return false;
|
|
};
|
|
return false;
|
|
};
|
|
|
|
fn nofollowregular(path: str) bool = {
|
|
let fi: os.filestat;
|
|
match (os.lstat(&fi, path)) {
|
|
case void => {
|
|
return (((fi.mode: u32) & 61440u32) == (os.mode.REG: u32));
|
|
};
|
|
case let e: os.oserror => return false;
|
|
};
|
|
return false;
|
|
};
|
|
|
|
@test fn persistent_init_and_rejection() void = {
|
|
let td: str = testenv.fresh();
|
|
let side: str = strings.concat(td, "/side");
|
|
let extra: str = strings.concat(td, "/extra");
|
|
let root: str = strings.concat(td, "/root");
|
|
let tools: str = strings.concat(td, "/tools");
|
|
assert(os.mkdir(side, 493) == 0);
|
|
assert(os.mkdir(extra, 493) == 0);
|
|
assert(os.mkdir(root, 493) == 0);
|
|
assert(os.mkdir(tools, 493) == 0);
|
|
let sidefile: str = strings.concat(side, "/side.ww");
|
|
let rootfile: str = strings.concat(root, "/main.ww");
|
|
let rootbase: str = strings.concat(
|
|
"package main;\nimport _ side;\nimport os;\n",
|
|
"fn main() i32 = { os.write(1, \"M\".ptr, 1u64); return 0; };\n");
|
|
let rootextra: str = strings.concat(
|
|
"package main;\nimport _ side;\nimport _ extra;\nimport os;\n",
|
|
"fn main() i32 = { os.write(1, \"M\".ptr, 1u64); return 0; };\n");
|
|
testenv.writefile(sidefile, strings.concat(
|
|
"package side;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"S\".ptr, 1u64); };\n"));
|
|
testenv.writefile(strings.concat(extra, "/extra.ww"), strings.concat(
|
|
"package unbound;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"E\".ptr, 1u64); };\n"));
|
|
testenv.writefile(rootfile, rootbase);
|
|
let wrapper: str = strings.concat(tools, "/w6c-wrapper.sh");
|
|
testenv.writeexecutable(wrapper, strings.concat(
|
|
"#!/bin/sh\n",
|
|
"printf 'BEGIN' >> \"$WW_SEPINIT_W6C_TRACE\"\n",
|
|
"for arg in \"$@\"; do printf '<%s>' \"$arg\" >> ",
|
|
"\"$WW_SEPINIT_W6C_TRACE\"; done\n",
|
|
"printf '\\n' >> \"$WW_SEPINIT_W6C_TRACE\"\n",
|
|
"exec \"$WW_SEPINIT_REAL_W6C\" \"$@\"\n"));
|
|
let stages: []str = ["ww", "ww_ww"];
|
|
let compilers: []str = ["w6c", "w6c_ww"];
|
|
let tags: []str = ["c", "ww"];
|
|
let finalbin: str = "";
|
|
let blankbin: str = "";
|
|
let i: i32 = 0;
|
|
for (i < 2) {
|
|
if (i != 0) {
|
|
replacefile(sidefile, strings.concat(
|
|
"package side;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"S\".ptr, 1u64); };\n"));
|
|
replacefile(rootfile, rootbase);
|
|
};
|
|
let work: str = strings.concat(td, "/persist-", tags[i]);
|
|
assert(os.mkdir(work, 493) == 0);
|
|
let prog: str = strings.concat(td, "/persist-bin-", tags[i]);
|
|
let trace: str = strings.concat(td, "/persist-trace-", tags[i]);
|
|
testenv.writefile(trace, "");
|
|
let env: []str = compilertraceenv(wrapper,
|
|
testenv.driver(compilers[i]), trace);
|
|
let av: []str = [testenv.driver(stages[i]), "build", "-w", work,
|
|
"-I", td, "-o", prog, root];
|
|
let out: testenv.commandout;
|
|
testenv.runcommandenv(td, td, strings.concat("persist-seed-", tags[i]),
|
|
av, env, tmo(), &out);
|
|
if (exitcode(&out) != 0) { fail("persistence", out.stderr); };
|
|
let seededbin: str = testenv.readfile(prog);
|
|
let sidewwi: str = testenv.readfile(strings.concat(work, "/side.wwi"));
|
|
let roots: str = testenv.readfile(strings.concat(work, "/root.s"));
|
|
let rootwwi: str = testenv.readfile(strings.concat(work, "/root.wwi"));
|
|
let dispatch: str = testenv.readfile(strings.concat(work,
|
|
"/root.init.unit.ww"));
|
|
let sides: str = testenv.readfile(strings.concat(work, "/side.s"));
|
|
|
|
// A committed dispatcher voucher must itself be a regular file. An
|
|
// exact-byte symlink target cannot authorize reuse; the driver repairs
|
|
// the voucher and archive without recompiling source actions.
|
|
let initunit: str = strings.concat(work, "/root.init.unit.ww");
|
|
let inittarget: str = strings.concat(td, "/init-unit-target-", tags[i]);
|
|
testenv.writefile(inittarget, dispatch);
|
|
assert(os.remove(initunit) == 0);
|
|
assert(os.symlink(inittarget, initunit) == 0);
|
|
replacefile(trace, "");
|
|
testenv.runcommandenv(td, td,
|
|
strings.concat("persist-init-voucher-symlink-", tags[i]), av, env,
|
|
tmo(), &out);
|
|
if (exitcode(&out) != 0 || testenv.readfile(trace).len != 0
|
|
|| !nofollowregular(initunit)
|
|
|| !testenv.same(dispatch, testenv.readfile(initunit))
|
|
|| !testenv.same(seededbin, testenv.readfile(prog))) {
|
|
fail("persistence", "symlinked init voucher authorized reuse");
|
|
};
|
|
|
|
// Every reserved warm staging name is a structural input. A dangling
|
|
// symlink is occupied, must survive rejection, and must stop the whole
|
|
// request before the first compiler action.
|
|
let warmstage: str = strings.concat(work, "/side.s.new");
|
|
let warmtarget: str = strings.concat(td, "/missing-warm-", tags[i]);
|
|
assert(os.symlink(warmtarget, warmstage) == 0);
|
|
replacefile(trace, "");
|
|
testenv.runcommandenv(td, td,
|
|
strings.concat("persist-stage-collision-", tags[i]), av, env,
|
|
tmo(), &out);
|
|
let warmwant: str = strings.concat(
|
|
"ww: package staging path already exists: ", warmstage, "\n");
|
|
if (exitcode(&out) != 1 || !testenv.same(out.stderr, warmwant)
|
|
|| testenv.readfile(trace).len != 0 || !nofollowexists(warmstage)
|
|
|| testenv.exists(warmtarget)
|
|
|| !testenv.same(seededbin, testenv.readfile(prog))) {
|
|
fail("persistence", "warm dangling staging collision was mutated");
|
|
};
|
|
assert(os.remove(warmstage) == 0);
|
|
|
|
replacefile(sidefile, strings.concat(
|
|
"package side;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"T\".ptr, 1u64); };\n"));
|
|
replacefile(trace, "");
|
|
testenv.runcommandenv(td, td,
|
|
strings.concat("persist-init-change-", tags[i]), av, env, tmo(), &out);
|
|
if (exitcode(&out) != 0) { fail("persistence", out.stderr); };
|
|
let inittrace: str = testenv.readfile(trace);
|
|
if (testenv.occurrences(inittrace, "BEGIN") != 1
|
|
|| !testenv.has(inittrace,
|
|
"<--package-init-symbol><__ww..pkg.p.side.v0.r0.init>")
|
|
|| testenv.has(inittrace,
|
|
"<--package-init-symbol><__ww..pkg.p.root.v0.r0.init>")) {
|
|
fail("persistence", "init-only edit recompiled an importer");
|
|
};
|
|
if (!testenv.same(sidewwi,
|
|
testenv.readfile(strings.concat(work, "/side.wwi")))
|
|
|| !testenv.same(roots,
|
|
testenv.readfile(strings.concat(work, "/root.s")))
|
|
|| !testenv.same(rootwwi,
|
|
testenv.readfile(strings.concat(work, "/root.wwi")))
|
|
|| !testenv.same(dispatch,
|
|
testenv.readfile(strings.concat(work, "/root.init.unit.ww")))
|
|
|| testenv.same(sides,
|
|
testenv.readfile(strings.concat(work, "/side.s")))) {
|
|
fail("persistence", "init-only invalidation crossed export boundary");
|
|
};
|
|
let rav: []str = [prog];
|
|
run(td, strings.concat("persist-run-", tags[i]), rav, &out);
|
|
if (exitcode(&out) != 0 || !testenv.same(out.stdout, "TM")) {
|
|
fail("persistence", "init-only relink did not take effect");
|
|
};
|
|
let names: []str = testenv.listdir(work);
|
|
let saved: []str = alloc([], names.len: u64)!;
|
|
let k: i32 = 0;
|
|
for (k < names.len) {
|
|
append(saved, testenv.readfile(strings.concat(work, "/", names[k])));
|
|
k += 1;
|
|
};
|
|
let oldbin: str = testenv.readfile(prog);
|
|
replacefile(sidefile, strings.concat(
|
|
"package side;\nimport os;\n",
|
|
"fn init(x: i32) void = { os.write(1, \"X\".ptr, 1u64); };\n"));
|
|
replacefile(trace, "");
|
|
testenv.runcommandenv(td, td, strings.concat("persist-reject-", tags[i]),
|
|
av, env, tmo(), &out);
|
|
if (exitcode(&out) == 0
|
|
|| !testenv.has(out.stderr,
|
|
"func init must have no arguments and no return values")) {
|
|
fail("persistence", "invalid init was accepted");
|
|
};
|
|
let after: []str = testenv.listdir(work);
|
|
if (after.len != names.len || !nostaged(work)
|
|
|| !testenv.same(oldbin, testenv.readfile(prog))) {
|
|
fail("persistence", "rejection changed committed shape/product");
|
|
};
|
|
k = 0;
|
|
for (k < names.len) {
|
|
if (!testenv.same(names[k], after[k])
|
|
|| !testenv.same(saved[k],
|
|
testenv.readfile(strings.concat(work, "/", names[k])))) {
|
|
fail("persistence", strings.concat("rejection changed ", names[k]));
|
|
};
|
|
k += 1;
|
|
};
|
|
replacefile(sidefile, strings.concat(
|
|
"package side;\nimport os;\n",
|
|
"fn init() void = { os.write(1, \"T\".ptr, 1u64); };\n"));
|
|
replacefile(trace, "");
|
|
testenv.runcommandenv(td, td, strings.concat("persist-recover-", tags[i]),
|
|
av, env, tmo(), &out);
|
|
if (exitcode(&out) != 0) { fail("persistence", "retry failed"); };
|
|
if (testenv.readfile(trace).len != 0) {
|
|
fail("persistence", "rejected source polluted warm reuse");
|
|
};
|
|
if (i == 0) { finalbin = testenv.readfile(prog); }
|
|
else { if (!testenv.same(finalbin, testenv.readfile(prog))) {
|
|
fail("persistence", "final C/WW binary mismatch");
|
|
}; };
|
|
|
|
// Adding one blank-only edge recompiles exactly the new dependency and
|
|
// its owning source action, changes the reachable dispatcher, and keeps
|
|
// the owner's semantic export stable.
|
|
replacefile(rootfile, rootextra);
|
|
replacefile(trace, "");
|
|
testenv.runcommandenv(td, td, strings.concat("persist-blank-add-", tags[i]),
|
|
av, env, tmo(), &out);
|
|
if (exitcode(&out) != 0) { fail("persistence", out.stderr); };
|
|
let addtrace: str = testenv.readfile(trace);
|
|
if (testenv.occurrences(addtrace, "BEGIN") != 2
|
|
|| !testenv.has(addtrace,
|
|
"<--package-init-symbol><__ww..pkg.p.extra.v0.r0.init>")
|
|
|| !testenv.has(addtrace,
|
|
"<--package-init-symbol><__ww..pkg.p.root.v0.r0.init>")
|
|
|| !testenv.same(rootwwi,
|
|
testenv.readfile(strings.concat(work, "/root.wwi")))) {
|
|
fail("persistence", "blank-edge addition invalidation");
|
|
};
|
|
let adddispatch: str = testenv.readfile(strings.concat(work,
|
|
"/root.init.unit.ww"));
|
|
if (testenv.occurrences(adddispatch,
|
|
"//ww:init-call __ww..pkg.p.extra.v0.r0.init") != 1) {
|
|
fail("persistence", "blank-edge addition missed dispatcher");
|
|
};
|
|
run(td, strings.concat("persist-blank-add-run-", tags[i]), rav, &out);
|
|
if (exitcode(&out) != 0 || !testenv.same(out.stdout, "ETM")) {
|
|
fail("persistence", "blank-edge addition runtime");
|
|
};
|
|
if (i == 0) { blankbin = testenv.readfile(prog); }
|
|
else { if (!testenv.same(blankbin, testenv.readfile(prog))) {
|
|
fail("persistence", "blank-edge C/WW binary mismatch");
|
|
}; };
|
|
|
|
// Removing it recompiles only the owner, restores the exact old
|
|
// dispatcher closure, and cannot reuse the now-unreachable init task.
|
|
replacefile(rootfile, rootbase);
|
|
replacefile(trace, "");
|
|
testenv.runcommandenv(td, td,
|
|
strings.concat("persist-blank-remove-", tags[i]), av, env, tmo(), &out);
|
|
if (exitcode(&out) != 0) { fail("persistence", out.stderr); };
|
|
let removetrace: str = testenv.readfile(trace);
|
|
if (testenv.occurrences(removetrace, "BEGIN") != 1
|
|
|| !testenv.has(removetrace,
|
|
"<--package-init-symbol><__ww..pkg.p.root.v0.r0.init>")
|
|
|| testenv.has(removetrace,
|
|
"<--package-init-symbol><__ww..pkg.p.extra.v0.r0.init>")
|
|
|| !testenv.same(dispatch,
|
|
testenv.readfile(strings.concat(work, "/root.init.unit.ww")))) {
|
|
fail("persistence", "blank-edge removal invalidation");
|
|
};
|
|
run(td, strings.concat("persist-blank-remove-run-", tags[i]), rav, &out);
|
|
if (exitcode(&out) != 0 || !testenv.same(out.stdout, "TM")) {
|
|
fail("persistence", "blank-edge removal runtime");
|
|
};
|
|
i += 1;
|
|
};
|
|
|
|
// Cold caller-visible product staging receives the same no-follow
|
|
// preflight and cannot acquire its implicit scratch or invoke a producer.
|
|
i = 0;
|
|
for (i < 2) {
|
|
let prog: str = strings.concat(td, "/collision-bin-", tags[i]);
|
|
let stage: str = strings.concat(prog, ".new");
|
|
let target: str = strings.concat(td, "/missing-product-", tags[i]);
|
|
let trace: str = strings.concat(td, "/collision-trace-", tags[i]);
|
|
testenv.writefile(trace, "");
|
|
assert(os.symlink(target, stage) == 0);
|
|
let env: []str = compilertraceenv(wrapper,
|
|
testenv.driver(compilers[i]), trace);
|
|
let av: []str = [testenv.driver(stages[i]), "build", "-I", td,
|
|
"-o", prog, root];
|
|
let out: testenv.commandout;
|
|
testenv.runcommandenv(td, td,
|
|
strings.concat("cold-stage-collision-", tags[i]), av, env, tmo(),
|
|
&out);
|
|
let want: str = strings.concat(
|
|
"ww: product staging path already exists: ", stage, "\n");
|
|
if (exitcode(&out) != 1 || !testenv.same(out.stderr, want)
|
|
|| testenv.readfile(trace).len != 0 || !nofollowexists(stage)
|
|
|| testenv.exists(target) || testenv.exists(prog)
|
|
|| testenv.exists(strings.concat(prog, ".sepwork"))) {
|
|
fail("persistence", "cold dangling staging collision was mutated");
|
|
};
|
|
assert(os.remove(stage) == 0);
|
|
i += 1;
|
|
};
|
|
testenv.clean(td);
|
|
};
|