package sepbuild_test; // `ww build` sep-driver layout observers, both stages. Ports of the // retired native carriers test/wcc/989_sepbuild_run.c, // 989_seproot_export_run.c, 989_sepstructdef_run.c and // 989_wwispread_sep.c; assertions retained where their production boundary // remains live. // // sepbuild (#46 commit-3) — build_one_sep END-TO-END on the real lib // chain root -> os -> time (transitive-closure discovery + topo): // build+run exit 7 both stages; {time,os,__root}.wwi/.a // materialize; per-package .s/.wwi/.a/.unit.ww and the final binary // byte-id cs vs ww; every .unit.ww is the package's own sorted source // set; the runtime remains a fixed linker input rather than a source graph // action; `ww run` routes through the same sole sep path (exit 7 both stages). // // seproot — a ROOT whose `export fn use(a: *t)` reaches an unexported // local `type t` builds and emits a deterministic self-contained .wwi/.a. // The private type is carried as compiler export data without `export`, and // an importing source still cannot qualify `main.t`. // // sepstructdef (#70 BUG-2) — a dep exporting aggregate-init defs // (struct-lit + array-lit) sep-builds and links (exit 20 both // stages); d.wwi carries the value-LESS prototypes `export def info: // s;` + `export def arr: [3]i32;` and NOT the value-serialized // `export def info: s = ` form (the producer took the aggregate arm). // // wwispread (#95) — the `...` union-spread marker survives N_TTAGGED // .wwi serialization: consumer build+run exit 0 over the checked-in // 2-arm and 3-arm trees, the produced .wwi contains the literal // marker, and cs.wwi == ww.wwi. // // 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("sepbuild 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; }; // -1 encodes an abnormal (non-EXIT) termination, never a valid code. fn runcode(dir: str, name: str, argv: []str) i32 = { let co: testenv.commandout; testenv.runcommand(dir, dir, name, argv, tmo(), &co); if (co.termination != exec.termination.EXIT) { return -1; }; return co.code; }; fn samefile(label: str, what: str, a: str, b: str) void = { if (!testenv.same(testenv.readfile(a), testenv.readfile(b))) { fail(label, strings.concat("cs!=ww for ", what, " (rule 10)")); }; }; @test fn sepbuild() void = { let td: str = testenv.fresh(); let rootww: str = strings.concat(td, "/root.ww"); testenv.writefile(rootww, strings.concat( "package main;\n", "import os;\n", "fn main() i32 = { return os.getpid() - os.getpid() + 7; };\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", "-o", prog, rootww]; if (runcode(td, strings.concat("build_", tags[s]), av) != 0) { fail("sepbuild", strings.concat(drvs[s], " build failed")); }; let rav: []str = [prog]; if (runcode(td, strings.concat("runbin_", tags[s]), rav) != 7) { fail("sepbuild", strings.concat(drvs[s], " prog exit != 7")); }; s += 1; }; // discovery/topo: every reachable package action materialized. let pkgs: []str = ["time", "os", "__root"]; let i: i32 = 0; for (i < pkgs.len) { if (!testenv.exists(strings.concat(td, "/prog.cs.sepwork/", pkgs[i], ".wwi")) || !testenv.exists(strings.concat(td, "/prog.cs.sepwork/", pkgs[i], ".a"))) { fail("sepbuild", strings.concat(pkgs[i], ".wwi/.a", " missing (discovery/topo)")); }; i += 1; }; // cs==ww (rule 10): per-source-action artifacts + the final binary; // sepinit owns the command root's additional dispatcher artifacts. let sufs: []str = [".s", ".wwi", ".a", ".unit.ww"]; let p: i32 = 0; for (p < pkgs.len) { let k: i32 = 0; for (k < sufs.len) { samefile("sepbuild", strings.concat(pkgs[p], sufs[k]), strings.concat(td, "/prog.cs.sepwork/", pkgs[p], sufs[k]), strings.concat(td, "/prog.ww.sepwork/", pkgs[p], sufs[k])); k += 1; }; p += 1; }; samefile("sepbuild", "the final binary", strings.concat(td, "/prog.cs"), strings.concat(td, "/prog.ww")); // `ww run` routes through the sole sep path on both stages let cav: []str = [testenv.driver("ww"), "run", rootww]; let rccs: i32 = runcode(td, "run_cs", cav); let wav: []str = [testenv.driver("ww_ww"), "run", rootww]; let rcww: i32 = runcode(td, "run_ww", wav); if (rccs != rcww || rccs != 7) { fail("sepbuild", "`ww run` exits differ or != 7 across stages"); }; testenv.clean(td); }; @test fn seproot() void = { let td: str = testenv.fresh(); assert(os.mkdir(strings.concat(td, "/lib"), 493) == 0); assert(os.mkdir(strings.concat(td, "/lib/c"), 493) == 0); testenv.writefile(strings.concat(td, "/lib/c/mod.ww"), strings.concat( "package c;\n", "export fn cval() i32 = { return 5; };\n")); let rootww: str = strings.concat(td, "/root.ww"); testenv.writefile(rootww, strings.concat( "package main;\n", "import c;\n", "type t = struct { v: i32 };\n", "export fn use(a: *t) i32 = { return a.v; };\n", "fn main() i32 = {\n", "\tlet x: t = t { v = 42 };\n", "\treturn use(&x) - c.cval();\n", "};\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", strings.concat(td, "/lib"), "-o", prog, rootww]; if (runcode(td, strings.concat("build_", tags[s]), av) != 0) { fail("seproot", strings.concat(drvs[s], " build failed ", "(pre-fix: -I on the root rejects ", "export-fn-over-unexported-type)")); }; let rav: []str = [prog]; if (runcode(td, strings.concat("runbin_", tags[s]), rav) != 37) { fail("seproot", strings.concat(drvs[s], " prog exit != 37")); }; s += 1; }; // cs==ww for every source-action artifact, including the root archive; // sepinit owns the command root's additional dispatcher artifacts. let pkgs: []str = ["c", "__root"]; let sufs: []str = [".s", ".wwi", ".a", ".unit.ww"]; let p: i32 = 0; for (p < pkgs.len) { let k: i32 = 0; for (k < sufs.len) { samefile("seproot", strings.concat(pkgs[p], sufs[k]), strings.concat(td, "/prog.cs.sepwork/", pkgs[p], sufs[k]), strings.concat(td, "/prog.ww.sepwork/", pkgs[p], sufs[k])); k += 1; }; p += 1; }; samefile("seproot", "the final binary", strings.concat(td, "/prog.cs"), strings.concat(td, "/prog.ww")); // The reachable private nominal is compiler data, not a public source name. let s2: i32 = 0; for (s2 < 2) { let rootiface: str = strings.concat(td, "/prog.", tags[s2], ".sepwork/__root.wwi"); let ifacebytes: str = testenv.readfile(rootiface); if (!testenv.has(ifacebytes, "type t = struct { v: i32 }") || testenv.has(ifacebytes, "export type t") || !testenv.has(ifacebytes, "export fn use(a: *t) i32;")) { fail("seproot", strings.concat(drvs[s2], " did not carry a private reachable type as compiler data")); }; s2 += 1; }; // Replay the package compile with -I: both compilers accept and emit the // same self-contained export. A source importer cannot name its private t. let unit: str = strings.concat(td, "/prog.cs.sepwork/__root.unit.ww"); let ciface: str = strings.concat(td, "/prog.cs.sepwork/c.wwi"); let consumer: str = strings.concat(td, "/consumer.ww"); testenv.writefile(consumer, strings.concat( "package consumer;\n", "import main;\n", "fn forbidden(v: *main.t) void = {};\n")); let comps: []str = ["w6c", "w6c_ww"]; let c: i32 = 0; for (c < 2) { let wwi: str = strings.concat(td, "/nv.", comps[c], ".wwi"); let asmf: str = strings.concat(td, "/nv.", comps[c], ".s"); let rav: []str = [testenv.driver(comps[c]), "--entry", "-c", "--import", "c", ciface, "-I", wwi, "-o", asmf, unit]; if (runcode(td, strings.concat("reexport_", comps[c]), rav) != 0) { fail("seproot", strings.concat(comps[c], " rejected the self-contained root export")); }; let rejectav: []str = [testenv.driver(comps[c]), "-c", "--import", "main", wwi, "-o", asmf, consumer]; let reject: testenv.commandout; testenv.runcommand(td, td, strings.concat("private_", comps[c]), rejectav, tmo(), &reject); if (reject.termination != exec.termination.EXIT || reject.code == 0 || !testenv.has(reject.stderr, "package 'main' has no exported declaration 't'")) { fail("seproot", strings.concat(comps[c], " exposed the compiler-private type to source importers")); }; c += 1; }; if (!testenv.has(testenv.readfile(strings.concat(td, "/prog.cs.sepwork/__root.s")), "use")) { fail("seproot", "__root.s lacks the exported `use` symbol"); }; testenv.clean(td); }; @test fn sepstructdef() void = { let td: str = testenv.fresh(); assert(os.mkdir(strings.concat(td, "/lib"), 493) == 0); assert(os.mkdir(strings.concat(td, "/lib/d"), 493) == 0); // the struct type must be EXPORTED: an exported def over an // unexported type is a separate loud reject (check_exported_type) testenv.writefile(strings.concat(td, "/lib/d/mod.ww"), strings.concat( "package d;\n", "export type s = struct { a: i32, b: i32 };\n", "export def info: s = s { a = 7, b = 11 };\n", "export def arr: [3]i32 = [1, 2, 3];\n", "export fn geta() i32 = { return info.a; };\n", "export fn getb() i32 = { return info.b; };\n", "export fn getelem(i: i32) i32 = { return arr[i]; };\n")); let rootww: str = strings.concat(td, "/root.ww"); testenv.writefile(rootww, strings.concat( "package main;\n", "import d;\n", "fn main() i32 = {\n", "\treturn d.geta() + d.getelem(1) + d.getb();\n", "};\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", strings.concat(td, "/lib"), "-o", prog, rootww]; if (runcode(td, strings.concat("build_", tags[s]), av) != 0) { fail("sepstructdef", strings.concat(drvs[s], " build failed ", "(pre-fix: the aggregate-init def aborts the producer ", "with \"node kind 15\")")); }; let rav: []str = [prog]; if (runcode(td, strings.concat("runbin_", tags[s]), rav) != 20) { fail("sepstructdef", strings.concat(drvs[s], " prog exit != 20 ", "(external aggregate-DATA ref did not resolve)")); }; s += 1; }; let pkgs: []str = ["d", "__root"]; let sufs: []str = [".s", ".wwi", ".unit.ww"]; let p: i32 = 0; for (p < pkgs.len) { let k: i32 = 0; for (k < sufs.len) { if (testenv.same(pkgs[p], "__root") && testenv.same(sufs[k], ".wwi")) { k += 1; continue; }; samefile("sepstructdef", strings.concat(pkgs[p], sufs[k]), strings.concat(td, "/prog.cs.sepwork/", pkgs[p], sufs[k]), strings.concat(td, "/prog.ww.sepwork/", pkgs[p], sufs[k])); k += 1; }; p += 1; }; samefile("sepstructdef", "the final binary", strings.concat(td, "/prog.cs"), strings.concat(td, "/prog.ww")); // value-LESS prototypes present, value-serialized form absent (the // producer took the new aggregate arm, not the scalar arm) let wwi: str = testenv.readfile(strings.concat(td, "/prog.cs.sepwork/d.wwi")); if (!testenv.has(wwi, "export def info: s;\n")) { fail("sepstructdef", "d.wwi lacks value-less `export def info: s;`"); }; if (!testenv.has(wwi, "export def arr: [3]i32;\n")) { fail("sepstructdef", "d.wwi lacks value-less `export def arr: [3]i32;`"); }; if (testenv.has(wwi, "export def info: s = ")) { fail("sepstructdef", "d.wwi serialized the aggregate value (vacuous gate)"); }; testenv.clean(td); }; fn spreadshape(name: str, entry: str, wwi: str, marker: str) void = { let td: str = testenv.fresh(); let src: str = strings.concat(testenv.repo(), "/", entry); let drvs: []str = ["ww", "ww_ww"]; let tags: []str = ["cs", "ww"]; let s: i32 = 0; for (s < 2) { let prog: str = strings.concat(td, "/", name, ".", tags[s]); let av: []str = [testenv.driver(drvs[s]), "build", "-o", prog, src]; if (runcode(td, strings.concat("build_", tags[s]), av) != 0) { fail(name, strings.concat(drvs[s], " build failed (spread ", "member not assignable -> marker dropped?)")); }; let rav: []str = [prog]; if (runcode(td, strings.concat("runbin_", tags[s]), rav) != 0) { fail(name, strings.concat(drvs[s], " run exit != 0")); }; if (!testenv.has(testenv.readfile(strings.concat(prog, ".sepwork/", wwi)), marker)) { fail(name, strings.concat(wwi, " missing `", marker, "` (spread marker dropped in the serializer)")); }; s += 1; }; samefile(name, wwi, strings.concat(td, "/", name, ".cs.sepwork/", wwi), strings.concat(td, "/", name, ".ww.sepwork/", wwi)); testenv.clean(td); }; @test fn wwispread_twoarm() void = { spreadshape("twoarm", "test/wcc/data/wwispread/main.ww", "liba.wwi", "...inner"); }; @test fn wwispread_threearm() void = { spreadshape("threearm", "test/wcc/data/wwispread3/main.ww", "libb.wwi", "...three"); };