Files
ww/test/sep/sepbuild_test.ww

358 lines
13 KiB
Plaintext

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 -> {rt,time} (transitive-closure discovery + topo):
// build+run exit 7 both stages; {time,rt,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; `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 <pkg>.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", "rt", "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-package artifacts + the final binary
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 the complete package artifacts, including the root archive.
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");
};