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; every assertion preserved. // // sepbuild (#46 commit-3) — build_one_sep END-TO-END on the real lib // chain root -> os -> {rt,time} (transitive-closure discovery + topo): // build+run exit 7 both stages; {time,rt,os}.wwi + __root.s // materialize (#69: the root is compiled without -I, so no // __root.wwi); per-package .s/.wwi/.unit.ww and the final binary // byte-id cs vs ww; the KEYSTONE (spec 3.1) recompiles the driver's // own non-leaf .unit.ww with each dep's .wwi section replaced by that // dep's full directory bodies and requires byte-identical .s — the // proof the .wwi conveys exactly the dep facts P's codegen needs; // `ww run` routes through the same sole sep path (exit 7 both // stages). // // seproot (#69 BUG-1) — a ROOT whose `export fn use(a: *t)` names an // unexported local `type t` builds (exit 37 both stages) because the // root is compiled WITHOUT the -I .wwi-producer flag: __root.wwi must // NOT exist; replaying the driver's own __root.unit.ww through // w6c/w6c_ww with -I must REJECT (the isolated bug) while -c -o alone // must accept; __root.s carries the exported fn. // // 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)")); }; }; // ---- sepbuild (#46): real chain root->os->{rt,time} -------------------- // A package directory's *.ww bodies (less *test.ww), byte-sorted, each // newline-terminated — mirrors the driver's body enumeration so the // substituted bodies-unit matches the sep-unit structurally. fn dirbodies(dir: str) str = { let out: str = ""; let names: []str = testenv.listdir(dir); let i: i32 = 0; for (i < names.len) { if (strings.hassuffix(names[i], ".ww") && !strings.hassuffix(names[i], "test.ww")) { out = strings.concat(out, testenv.readfile(strings.concat(dir, "/", names[i])), "\n"); }; i += 1; }; return out; }; // Byte-offset borrow: strings.sub is rune-indexed, and the driver // units embed lib comment bytes outside ASCII, so rune indices would // mis-address the byte cursor below (readfile's borrow idiom). fn bslice(s: str, start: i32, end: i32) str = { assert(start <= end && end <= s.len); let r: str; r.ptr = s.ptr + (start: u64); r.len = end - start; return r; }; // Transform a driver sep-unit into a bodies-unit: every `//ww:module // ` dep section (its .wwi content) is replaced by 's full // directory bodies (dots -> slashes under libdir); the trailing // `//ww:module-reset` primary body is copied verbatim. The keystone's // other arm. fn composebodies(unit: str, libdir: str) str = { let out: str = ""; let n: i32 = unit.len; let i: i32 = 0; for (i < n) { let j: i32 = i; for (j < n && unit[j] != '\n') { j += 1; }; let line: str = bslice(unit, i, j); if (strings.hasprefix(line, "//ww:module-reset")) { out = strings.concat(out, bslice(unit, i, n)); return out; }; if (strings.hasprefix(line, "//ww:module ")) { out = strings.concat(out, line, "\n"); let path: str = bslice(line, 12, line.len); let d: []u8 = alloc([], (path.len + 1): u64)!; let k: i32 = 0; for (k < path.len) { if (path[k] == '.') { append(d, '/'); } else { append(d, path[k]); }; k += 1; }; out = strings.concat(out, dirbodies(strings.concat(libdir, "/", strings.frombytes(d)))); i = j + 1; for (i < n) { let e: i32 = i; for (e < n && unit[e] != '\n') { e += 1; }; if (strings.hasprefix(bslice(unit, i, e), "//ww:module")) { break; }; i = e + 1; }; continue; }; i = j + 1; }; return out; }; @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: the transitive package set materialized. #69: the // root emits no .wwi (compiled without -I); its .s stands in. let pkgs: []str = ["time", "rt", "os", "__root"]; let i: i32 = 0; for (i < pkgs.len) { let suffix: str = ".wwi"; if (testenv.same(pkgs[i], "__root")) { suffix = ".s"; }; if (!testenv.exists(strings.concat(td, "/prog.cs.sepwork/", pkgs[i], suffix))) { fail("sepbuild", strings.concat(pkgs[i], suffix, " missing (discovery/topo)")); }; i += 1; }; // cs==ww (rule 10): per-package artifacts + the final binary 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("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")); // KEYSTONE through the driver, non-leaf packages only (leaves have // no dep sections, so their keystone is vacuous) let libdir: str = strings.concat(testenv.repo(), "/lib"); let keypkgs: []str = ["os", "__root"]; let kp: i32 = 0; for (kp < keypkgs.len) { let unit: str = testenv.readfile(strings.concat(td, "/prog.cs.sepwork/", keypkgs[kp], ".unit.ww")); let bodiesf: str = strings.concat(td, "/", keypkgs[kp], ".bodies.ww"); testenv.writefile(bodiesf, composebodies(unit, libdir)); let bodiess: str = strings.concat(td, "/", keypkgs[kp], ".bodies.s"); let av: []str = [testenv.driver("w6c"), "-c", "-o", bodiess, bodiesf]; if (runcode(td, strings.concat("key_", keypkgs[kp]), av) != 0) { fail("sepbuild", strings.concat(keypkgs[kp], " bodies -c failed")); }; if (!testenv.same(testenv.readfile(bodiess), testenv.readfile(strings.concat(td, "/prog.cs.sepwork/", keypkgs[kp], ".s")))) { fail("sepbuild", strings.concat(keypkgs[kp], " bodies.s != driver sep.s (the .wwi does not convey ", "the dep facts P needs)")); }; kp += 1; }; // `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); }; // ---- seproot (#69 BUG-1): export-over-unexported-type root ------------- @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; the root's .wwi is intentionally absent (asserted below) let pkgs: []str = ["c", "__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("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 fix: the root is compiled WITHOUT -I, so no __root.wwi let s2: i32 = 0; for (s2 < 2) { if (testenv.exists(strings.concat(td, "/prog.", tags[s2], ".sepwork/__root.wwi"))) { fail("seproot", strings.concat(drvs[s2], " produced __root.wwi (-I still passed for the root)")); }; s2 += 1; }; // replay the pre-fix invocation on the driver's own __root.unit.ww: // with -I the export-check FIRES (the isolated bug); without it the // post-fix invocation accepts. Both compilers. let unit: str = strings.concat(td, "/prog.cs.sepwork/__root.unit.ww"); 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]), "-c", "-I", wwi, "-o", asmf, unit]; if (runcode(td, strings.concat("nvI_", comps[c]), rav) == 0) { fail("seproot", strings.concat(comps[c], " -c -I accepted the ", "root export-over-unexported-type (vacuous gate)")); }; let aav: []str = [testenv.driver(comps[c]), "-c", "-o", asmf, unit]; if (runcode(td, strings.concat("nvO_", comps[c]), aav) != 0) { fail("seproot", strings.concat(comps[c], " -c -o (no -I) ", "rejected the root unit (post-fix invocation)")); }; 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); }; // ---- sepstructdef (#70 BUG-2): aggregate-init def prototypes ----------- @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); }; // ---- wwispread (#95): `...` spread marker round-trip ------------------- 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"); };