Birth test/sep/ (single-file ww observers on test/testenv, run by the new SEP_WW_TARGETS pattern rule under test-compiler) with sepbuild_test.ww: the build_one_sep real-chain gate incl. the bodies==.wwi keystone recompile, the #69 root-without--I contract, the #70 aggregate-def .wwi prototypes, and the #95 spread-marker round-trip. Every carrier assertion has a successor row; the C cleanup-accounting ledgers collapse into testenv.clean.
465 lines
16 KiB
Plaintext
465 lines
16 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; 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 <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)"));
|
|
};
|
|
};
|
|
|
|
// ---- 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
|
|
// <path>` dep section (its .wwi content) is replaced by <path>'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");
|
|
};
|