package seplink_test; // Archive-substrate + link-reject observers, both stages. Ports of the // retired native carriers test/wcc/989_separchive_run.c and // 989_sepcycle_dup.c; every assertion preserved. // // archive (#46 commit-5a) — `ww build` wraps each DEP package's .o in // a deterministic single-member .a and links the ROOT as a positional // .o (force-loaded): build+run exit 7 both stages; __root.a absent, // __root.o + helper.a present; cs helper.a == ww helper.a (rule 10, // the .a byte-id substrate); 3 cold cstage rebuilds emit // byte-identical helper.a (zeroed mtime/uid/gid, fixed mode/member — // a floating byte would poison the content cache key). // // archivedup (#31 PASS 3) — two dep packages force the same link // symbol via @symbol("dup_sym"); one member is pulled, the other // lands UNPULLED defining an already-defined name — exactly what // selective pull skips and the post-pull PASS 3 catches: both stages // exit non-zero, name "duplicate symbol", and leave no partial // binary. Non-vacuity flip: a distinct symbol builds + runs (exit 3). // // cycle (#46 commit-4 A) — a 3-package import cycle // root->pkga->pkgb->pkgc->pkga is LOUD-rejected: non-zero exit, the // `dependency cycle: pkga -> pkgb -> pkgc -> pkga` chain named, no // partial binary, and cs stderr == ww stderr BYTE-IDENTICAL (the // message is pure driver code, rule 10). Non-vacuity flip: the // acyclic tree builds + runs (exit 7). // // linkdup (#31) — two hand-assembled .o each defining bare `main` fed // to w6l AND w6l_ww: non-zero, "duplicate symbol", no partial binary. // ACHIEVABLE parity only — substring, NOT byte-equal stderr: cstage // prints `: duplicate symbol `, wwstage the bare form (a // pre-existing divergence filed with the carrier, not reconciled // here). Non-vacuity: a single .o + libwwrt.a links and runs clean. // // Dropped C machinery, not assertions: per-path unlink/rmdir // accounting (testenv.clean asserts the removal). import os; import os.exec; import strings; import testenv; import time; fn fail(label: str, why: str) void = { let m: str = strings.concat("seplink 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 run(dir: str, name: str, argv: []str, out: *testenv.commandout) void = { testenv.runcommand(dir, dir, name, argv, tmo(), out); }; // -1 encodes an abnormal (non-EXIT) termination, never a valid code. fn runcode(dir: str, name: str, argv: []str) i32 = { let co: testenv.commandout; run(dir, name, argv, &co); if (co.termination != exec.termination.EXIT) { return -1; }; return co.code; }; @test fn archive() void = { let td: str = testenv.fresh(); assert(os.mkdir(strings.concat(td, "/helper"), 493) == 0); testenv.writefile(strings.concat(td, "/helper/h.ww"), strings.concat( "package helper;\n", "export fn val() i32 = { return 7; };\n")); let rootww: str = strings.concat(td, "/root.ww"); testenv.writefile(rootww, strings.concat( "package main;\n", "import helper;\n", "fn main() i32 = { return helper.val(); };\n")); let drvs: []str = ["ww", "ww_ww"]; let tags: []str = ["cs", "ww"]; let s: i32 = 0; for (s < 2) { let prog: str = strings.concat(td, "/prog.", tags[s]); let av: []str = [testenv.driver(drvs[s]), "build", "-I", td, "-o", prog, rootww]; if (runcode(td, strings.concat("build_", tags[s]), av) != 0) { fail("archive", strings.concat(drvs[s], " build failed")); }; let rav: []str = [prog]; if (runcode(td, strings.concat("runbin_", tags[s]), rav) != 7) { fail("archive", strings.concat(drvs[s], " prog exit != 7")); }; s += 1; }; // layout: root stays a positional force-loaded .o, deps become .a if (testenv.exists(strings.concat(td, "/prog.cs.sepwork/__root.a"))) { fail("archive", "root wrapped in .a (should stay positional .o)"); }; if (!testenv.exists(strings.concat(td, "/prog.cs.sepwork/__root.o"))) { fail("archive", "missing root .o"); }; if (!testenv.exists(strings.concat(td, "/prog.cs.sepwork/helper.a"))) { fail("archive", "missing dep helper.a"); }; // rule 10: the .a byte-id substrate if (!testenv.same( testenv.readfile(strings.concat(td, "/prog.cs.sepwork/helper.a")), testenv.readfile(strings.concat(td, "/prog.ww.sepwork/helper.a")))) { fail("archive", "cs helper.a != ww helper.a (rule 10 .a byte-id)"); }; // determinism: 3 cold cstage rebuilds -> byte-identical .a let det0: str = strings.concat(td, "/det0"); let det1: str = strings.concat(td, "/det1"); let det2: str = strings.concat(td, "/det2"); let av0: []str = [testenv.driver("ww"), "build", "-I", td, "-o", det0, rootww]; if (runcode(td, "det0", av0) != 0) { fail("archive", "det0 build"); }; let av1: []str = [testenv.driver("ww"), "build", "-I", td, "-o", det1, rootww]; if (runcode(td, "det1", av1) != 0) { fail("archive", "det1 build"); }; let av2: []str = [testenv.driver("ww"), "build", "-I", td, "-o", det2, rootww]; if (runcode(td, "det2", av2) != 0) { fail("archive", "det2 build"); }; let a0: str = testenv.readfile(strings.concat(det0, ".sepwork/helper.a")); let a1: str = testenv.readfile(strings.concat(det1, ".sepwork/helper.a")); let a2: str = testenv.readfile(strings.concat(det2, ".sepwork/helper.a")); if (!testenv.same(a0, a1) || !testenv.same(a1, a2)) { fail("archive", strings.concat(".a not deterministic across 3 ", "builds (floating bytes poison the cache key)")); }; testenv.clean(td); }; @test fn archivedup() void = { let td: str = testenv.fresh(); assert(os.mkdir(strings.concat(td, "/pkga"), 493) == 0); assert(os.mkdir(strings.concat(td, "/pkgb"), 493) == 0); testenv.writefile(strings.concat(td, "/pkga/a.ww"), strings.concat( "package pkga;\n", "@symbol(\"dup_sym\") export fn afn() i32 = { return 1; };\n")); let pbww: str = strings.concat(td, "/pkgb/b.ww"); testenv.writefile(pbww, strings.concat( "package pkgb;\n", "@symbol(\"dup_sym\") export fn bfn() i32 = { return 2; };\n")); let droot: str = strings.concat(td, "/droot.ww"); testenv.writefile(droot, strings.concat( "package main;\n", "import pkga;\n", "import pkgb;\n", "fn main() i32 = { return pkga.afn() + pkgb.bfn(); };\n")); let drvs: []str = ["ww", "ww_ww"]; let tags: []str = ["cs", "ww"]; let s: i32 = 0; for (s < 2) { let prog: str = strings.concat(td, "/dup.", tags[s]); let av: []str = [testenv.driver(drvs[s]), "build", "-I", td, "-o", prog, droot]; let co: testenv.commandout; run(td, strings.concat("dup_", tags[s]), av, &co); if (co.termination == exec.termination.EXIT && co.code == 0) { fail("archivedup", strings.concat(drvs[s], " accepted a masked ", "cross-pkg dup through .a (PASS 3 missing?)")); }; if (!testenv.has(co.stderr, "duplicate symbol")) { fail("archivedup", strings.concat(drvs[s], " missing 'duplicate symbol' on the .a dup path")); }; if (testenv.exists(prog)) { fail("archivedup", strings.concat(drvs[s], " produced a partial binary on the dup reject")); }; s += 1; }; // non-vacuity flip: a distinct symbol builds + runs (1 + 2) assert(os.remove(pbww) == 0); testenv.writefile(pbww, strings.concat( "package pkgb;\n", "@symbol(\"uniq_sym\") export fn bfn() i32 = { return 2; };\n")); let s2: i32 = 0; for (s2 < 2) { let prog: str = strings.concat(td, "/ok.", tags[s2]); let av: []str = [testenv.driver(drvs[s2]), "build", "-I", td, "-o", prog, droot]; if (runcode(td, strings.concat("ok_", tags[s2]), av) != 0) { fail("archivedup", strings.concat(drvs[s2], " could not build ", "the distinct-symbol graph (non-vacuity)")); }; let rav: []str = [prog]; if (runcode(td, strings.concat("okrun_", tags[s2]), rav) != 3) { fail("archivedup", strings.concat(drvs[s2], " flip prog exit != 3 (non-vacuity)")); }; s2 += 1; }; testenv.clean(td); }; @test fn cycle() void = { let td: str = testenv.fresh(); assert(os.mkdir(strings.concat(td, "/pkga"), 493) == 0); assert(os.mkdir(strings.concat(td, "/pkgb"), 493) == 0); assert(os.mkdir(strings.concat(td, "/pkgc"), 493) == 0); let rootww: str = strings.concat(td, "/root.ww"); testenv.writefile(rootww, strings.concat( "package main;\n", "import pkga;\n", "fn main() i32 = { return pkga.av(); };\n")); testenv.writefile(strings.concat(td, "/pkga/a.ww"), strings.concat( "package pkga;\n", "import pkgb;\n", "export fn av() i32 = { return pkgb.bv(); };\n")); testenv.writefile(strings.concat(td, "/pkgb/b.ww"), strings.concat( "package pkgb;\n", "import pkgc;\n", "export fn bv() i32 = { return pkgc.cv(); };\n")); let fc: str = strings.concat(td, "/pkgc/c.ww"); testenv.writefile(fc, strings.concat( "package pkgc;\n", "import pkga;\n", "export fn cv() i32 = { return pkga.av(); };\n")); let drvs: []str = ["ww", "ww_ww"]; let tags: []str = ["cs", "ww"]; let errs: []str = ["", ""]; let s: i32 = 0; for (s < 2) { let prog: str = strings.concat(td, "/prog.", tags[s]); let av: []str = [testenv.driver(drvs[s]), "build", "-o", prog, rootww]; let co: testenv.commandout; run(td, strings.concat("cyc_", tags[s]), av, &co); if (co.termination == exec.termination.EXIT && co.code == 0) { fail("cycle", strings.concat(drvs[s], " build returned 0 (no-op reject)")); }; if (!testenv.has(co.stderr, "dependency cycle: pkga -> pkgb -> pkgc -> pkga")) { fail("cycle", strings.concat(drvs[s], " missing the cycle-chain message")); }; if (testenv.exists(prog)) { fail("cycle", strings.concat(drvs[s], " produced a partial binary")); }; errs[s] = co.stderr; s += 1; }; // the cycle message is pure driver code -> byte-identical stderr if (!testenv.same(errs[0], errs[1])) { fail("cycle", "cs stderr != ww stderr (rule 10)"); }; // non-vacuity flip: break the cycle -> both stages build + run assert(os.remove(fc) == 0); testenv.writefile(fc, strings.concat( "package pkgc;\n", "export fn cv() i32 = { return 7; };\n")); let s2: i32 = 0; for (s2 < 2) { let prog: str = strings.concat(td, "/ok.", tags[s2]); let av: []str = [testenv.driver(drvs[s2]), "build", "-o", prog, rootww]; if (runcode(td, strings.concat("cycok_", tags[s2]), av) != 0) { fail("cycle", strings.concat(drvs[s2], " could not build the ", "acyclic graph (non-vacuity)")); }; let rav: []str = [prog]; if (runcode(td, strings.concat("cycokrun_", tags[s2]), rav) != 7) { fail("cycle", strings.concat(drvs[s2], " flip prog exit != 7 (non-vacuity)")); }; s2 += 1; }; testenv.clean(td); }; @test fn linkdup() void = { let td: str = testenv.fresh(); let u1: str = strings.concat(td, "/u1.ww"); let u2: str = strings.concat(td, "/u2.ww"); testenv.writefile(u1, strings.concat("package main;\n", "fn main() i32 = { return 0; };\n")); testenv.writefile(u2, strings.concat("package main;\n", "fn main() i32 = { return 1; };\n")); let o1: str = strings.concat(td, "/u1.o"); let o2: str = strings.concat(td, "/u2.o"); let names: []str = ["u1", "u2"]; let n: i32 = 0; for (n < 2) { let sf: str = strings.concat(td, "/", names[n], ".s"); let cav: []str = [testenv.driver("w6c"), "-c", "-o", sf, strings.concat(td, "/", names[n], ".ww")]; if (runcode(td, strings.concat("c_", names[n]), cav) != 0) { fail("linkdup", strings.concat("w6c -c ", names[n], " failed")); }; let aav: []str = [testenv.driver("w6a"), "-o", strings.concat(td, "/", names[n], ".o"), sf]; if (runcode(td, strings.concat("a_", names[n]), aav) != 0) { fail("linkdup", strings.concat("w6a ", names[n], " failed")); }; n += 1; }; let lnks: []str = ["w6l", "w6l_ww"]; let l: i32 = 0; for (l < 2) { let prog: str = strings.concat(td, "/dupprog.", lnks[l]); let av: []str = [testenv.driver(lnks[l]), "-o", prog, o1, o2]; let co: testenv.commandout; run(td, strings.concat("dup_", lnks[l]), av, &co); if (co.termination == exec.termination.EXIT && co.code == 0) { fail("linkdup", strings.concat(lnks[l], " accepted a duplicate symbol (exit 0)")); }; if (!testenv.has(co.stderr, "duplicate symbol")) { fail("linkdup", strings.concat(lnks[l], " missing 'duplicate symbol' message")); }; if (testenv.exists(prog)) { fail("linkdup", strings.concat(lnks[l], " produced a partial binary on the reject path")); }; l += 1; }; // non-vacuity: a single .o + libwwrt.a links clean and runs let rt: str = strings.concat(testenv.repo(), "/out/lib/libwwrt.a"); let l2: i32 = 0; for (l2 < 2) { let prog: str = strings.concat(td, "/single.", lnks[l2]); let av: []str = [testenv.driver(lnks[l2]), "-o", prog, o1, rt]; if (runcode(td, strings.concat("single_", lnks[l2]), av) != 0) { fail("linkdup", strings.concat(lnks[l2], " could not link a single .o (non-vacuity)")); }; let rav: []str = [prog]; if (runcode(td, strings.concat("singlerun_", lnks[l2]), rav) != 0) { fail("linkdup", strings.concat(lnks[l2], " single prog did not run clean (non-vacuity)")); }; l2 += 1; }; testenv.clean(td); };