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, "!\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, "!\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><") || !testenv.has(compilerlog, "<--import-map>") || 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 body: str = "package main;\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, "fn main() i32 = { return 0; };\n"); let src: str = strings.concat(td, "/main.ww"); testenv.writefile(src, body); let wrapper: str = strings.concat(td, "/w6c-trace.sh"); testenv.writeexecutable(wrapper, strings.concat("#!/bin/sh\n", "printf x >> \"$WW_SEPINIT_W6C_TRACE\"\nexit 99\n")); let stages: []str = ["ww", "ww_ww"]; let compilers: []str = ["w6c", "w6c_ww"]; let launcher: str = testenv.driver("sep-limitexec"); let found: bool = false; let last: str = ""; let limit: i64 = 2621440i64; let li: i32 = 0; // Both implementations must reject under the same address-space ceiling. // Search a bounded same-limit ladder because loader mappings differ across // supported hosts; the 600-action graph makes the driver-allocation window // broad, while the compiler trace pins failure before the first tool. for (limit <= 16777216i64 && !found) { let limitstr: str = strconv.i64tos(limit, strconv.base.DEC); let good: [2]bool; let diags: [2]str; i = 0; for (i < stages.len) { let tag: str = allocpkgname(li); let trace: str = strings.concat(td, "/alloc-", stages[i], "-", tag, ".trace"); testenv.writefile(trace, ""); let env: []str = compilertraceenv(wrapper, testenv.driver(compilers[i]), trace); let output: str = strings.concat(td, "/alloc-", stages[i], "-", tag); let av: []str = [launcher, limitstr, testenv.driver(stages[i]), "build", "-I", tree, "-o", output, src]; let out: testenv.commandout; runenv(td, strings.concat("allocation-", stages[i], "-", tag), av, env, &out); last = strings.dup(out.stderr); good[i] = exitcode(&out) == 1 && testenv.same(out.stderr, "ww: out of memory\n") && testenv.readfile(trace).len == 0; diags[i] = strings.dup(out.stderr); if (testenv.exists(output) || testenv.exists(strings.concat(output, ".sepwork"))) { fail("allocation", "failed pressure leg published state"); }; i += 1; }; if (good[0] && good[1]) { if (!testenv.same(diags[0], diags[1])) { fail("allocation", "C/WW allocation diagnostic mismatch"); }; found = true; }; limit += 131072i64; li += 1; }; if (!found) { fail("allocation", last); }; 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, \"\".ptr, 3u64); };\n")); testenv.writefile(strings.concat(testside, "/testside.ww"), strings.concat( "package testside;\nimport os;\n", "fn init() void = { os.write(1, \"\".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, \"

\".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, \"\".ptr, 3u64); return 7; };\n", "fn init() void = { assert(InternalState == 7); os.write(1, \"\".ptr, 3u64); };\n", "@test fn internal() void = { os.write(1, \"\".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, \"\".ptr, 3u64); return 9; };\n", "fn init() void = { assert(ExternalState == 9); os.write(1, \"\".ptr, 3u64); };\n", "@test fn external() void = { os.write(1, \"\".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, \"\".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-internal-test-main.init.unit.ww", "widget-internal-test-main.init.s", "widget-internal-test-main.init.o", "widget-internal-test-main.a", "widget_test-external-test-main.init.unit.ww", "widget_test-external-test-main.init.s", "widget_test-external-test-main.init.o", "widget_test-external-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, "

")) { 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, ""); let q: i32 = testenv.pos(out.stdout, ""); let p: i32 = testenv.pos(out.stdout, "

"); let rr: i32 = testenv.pos(out.stdout, ""); let ii: i32 = testenv.pos(out.stdout, ""); let tt: i32 = testenv.pos(out.stdout, ""); let tail: str = ""; if (tt >= 0) { tail = strings.sub(out.stdout, tt + 3, out.stdout.len); }; let de: i32 = testenv.pos(tail, ""); let pe: i32 = testenv.pos(tail, "

"); let w: i32 = testenv.pos(tail, ""); let e: i32 = testenv.pos(tail, ""); let x: i32 = testenv.pos(tail, ""); if (!(d >= 0 && d < q && q < rr && rr < p && p < ii && ii < tt && de >= 0 && de < pe && pe < w && w < e && e < x)) { fail("variants", strings.concat("test init order: ", out.stdout)); }; if (testenv.occurrences(out.stdout, "") != 1 || testenv.occurrences(out.stdout, "") != 2 || testenv.occurrences(out.stdout, "") != 1 || testenv.occurrences(out.stdout, "") != 1 || testenv.occurrences(out.stdout, "") != 1 || testenv.occurrences(out.stdout, "") != 1 || testenv.occurrences(out.stdout, "

") != 2) { fail("variants", "variant initialization count"); }; let internal: str = testenv.readfile(strings.concat(work, "/widget-internal-test-main.init.unit.ww")); if (testenv.occurrences(internal, "__ww..pkg.p.testside.v0.r0.init") != 1 || testenv.occurrences(internal, "__ww..pkg.p.prodside.v0.r0.init") != 1 || testenv.occurrences(internal, "__ww..pkg.p.widget.v1.r0.init") != 1 || testenv.occurrences(internal, "__ww..pkg.p.test.v0.r0.init") != 1 || testenv.occurrences(internal, "//ww:init-call __ww..pkg.p.__wwtestmain.widget.internal.main.v3.r2.init") != 1 || testenv.pos(internal, "__ww..pkg.p.test.v0.r0.init") >= testenv.pos(internal, "//ww:init-call __ww..pkg.p.__wwtestmain.widget.internal.main.v3.r2.init") || testenv.has(internal, "__ww..pkg.p.widget.v0.r0.init")) { fail("variants", "internal variant dispatcher ownership"); }; let external: str = testenv.readfile(strings.concat(work, "/widget_test-external-test-main.init.unit.ww")); if (testenv.occurrences(external, "__ww..pkg.p.widget.v0.r0.init") != 1 || testenv.occurrences(external, "__ww..pkg.p.prodside.v0.r0.init") != 1 || testenv.occurrences(external, "__ww..pkg.p.widget_test.v2.r0.init") != 1 || testenv.occurrences(external, "__ww..pkg.p.test.v0.r0.init") != 1 || testenv.occurrences(external, "//ww:init-call __ww..pkg.p.__wwtestmain.widget_test.external.main.v3.r2.init") != 1 || testenv.pos(external, "__ww..pkg.p.test.v0.r0.init") >= testenv.pos(external, "//ww:init-call __ww..pkg.p.__wwtestmain.widget_test.external.main.v3.r2.init") || testenv.has(external, "testside")) { fail("variants", "external variant 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 in initialization closure") || 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); };