Files
ww/test/sep/sepimport_test.ww

322 lines
11 KiB
Plaintext

package sepimport_test;
// Import/namespace-resolution observers on the sep drivers, both
// stages. Ports of the retired native carriers
// test/wcc/989_coloimport_sep.c, 989_depmain_sep.c, 989_declns_sep.c
// and 989_slttypepref_run.c; every assertion preserved.
//
// coloimport (#98) — a co-located `_test` entry must not shadow the
// dir-package it imports under `ww test`: the two-pass locate_import
// walk resolves `import widget` to the DIR-package on a later -I
// entry, not the same-named sibling FILE on the srcd entry. Exit 0
// both stages (pre-fix w6c rejects `package widget does not match
// import path gadget`), widget.{unit.ww,wwi} registered in .sepwork,
// and every .s/.wwi byte-id cs vs ww (rule 10).
//
// depmain (#99) — an IMPORTED dir-package's non-exported `fn main`
// mangles on its import path (aa.bb.main / cc.main) instead of
// emitting a bare `TEXT main` that collides with the root entry;
// EXACTLY one column-0 `TEXT main,` across the whole sep build.
//
// declns (#23/#30 modfn leg) — a value-namespace `fn aa` coexists
// with an imported MODULE aa (exit 6 = aa() + aa.helper()); the _vbu
// layout flips decl order and must stay byte-identical on
// {aa.s,aa.wwi,__root.s} (silent order-dependence in the SK_USE/value
// promote is the regression); __root.s carries BOTH
// `CALL aa.helper(SB)` and `CALL main.aa(SB)`.
//
// slttypepref (#58/#50 c1) — scopelookuptype must prefer the current
// module's SK_TYPE when a param shadows a type leaf two modules both
// export: xb's `size(invalid)` resolves xb's !i64 (exit 8), never
// xa's !i8, on both stages, shadow and noshadow rows. The
// two-DIRECTORY tree is load-bearing (a single-file twin risks a
// different scope-install order — E3-C1 retarget note in the
// carrier); each (row,driver) rebuilds a fresh tree.
//
// Dropped C machinery, not assertions: the ww_ww-absent skip gate
// (the Make target declares both drivers) and the 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("sepimport 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 cmpsepwork(label: str, csdir: str, wwdir: str) void = {
if (!testenv.isdir(csdir)) { fail(label, "no cs sepwork"); };
let names: []str = testenv.listdir(csdir);
let seen: i32 = 0;
let i: i32 = 0;
for (i < names.len) {
if (strings.hassuffix(names[i], ".s")
|| strings.hassuffix(names[i], ".wwi")
|| strings.hassuffix(names[i], ".a")) {
seen += 1;
if (!testenv.same(
testenv.readfile(strings.concat(csdir, "/", names[i])),
testenv.readfile(strings.concat(wwdir, "/", names[i])))) {
fail(label, strings.concat("cs!=ww for ", names[i],
" (rule 10)"));
};
};
i += 1;
};
// an existing-but-empty sepwork would pass the loop vacuously
if (seen == 0) { fail(label, "no package artifacts in cs sepwork"); };
};
@test fn coloimport() void = {
let td: str = testenv.fresh();
let inc: str = strings.concat(testenv.repo(), "/test/wcc/data/colo98");
let entry: str = strings.concat(inc, "/widget/widget_test.ww");
let drvs: []str = ["ww", "ww_ww"];
let tags: []str = ["cs", "ww"];
let i: i32 = 0;
for (i < 2) {
let prog: str = strings.concat(td, "/widget.", tags[i], ".bin");
let av: []str = [testenv.driver(drvs[i]), "test", "-I", inc,
"-o", prog, entry];
if (runcode(td, strings.concat("colo_", tags[i]), av) != 0) {
fail("coloimport", strings.concat("`ww test` exit != 0 on ",
drvs[i], " (import folded to the sibling file?)"));
};
i += 1;
};
let csdir: str = strings.concat(td, "/widget.cs.bin.sepwork");
let wwdir: str = strings.concat(td, "/widget.ww.bin.sepwork");
if (!testenv.exists(strings.concat(csdir, "/widget.unit.ww"))) {
fail("coloimport", strings.concat("widget.unit.ww missing (import ",
"folded to the sibling file, not the dir-package)"));
};
if (!testenv.exists(strings.concat(csdir, "/widget.wwi"))) {
fail("coloimport", "widget.wwi missing");
};
cmpsepwork("coloimport", csdir, wwdir);
testenv.clean(td);
};
// column-0 anchored `TEXT main,` labels across every .s under `dir`;
// the leading '\n' prepend also counts a file-leading label.
fn countbaremain(dir: str) i32 = {
let names: []str = testenv.listdir(dir);
let n: i32 = 0;
let i: i32 = 0;
for (i < names.len) {
if (strings.hassuffix(names[i], ".s")) {
let body: str = strings.concat("\n",
testenv.readfile(strings.concat(dir, "/", names[i])));
n += testenv.occurrences(body, "\nTEXT main,");
};
i += 1;
};
return n;
};
fn depmainrow(label: str, entry: str, want: i32, deps: str,
mangled: 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 i: i32 = 0;
for (i < 2) {
let prog: str = strings.concat(td, "/", label, ".", tags[i],
".bin");
let av: []str = [testenv.driver(drvs[i]), "build", "-o", prog,
src];
if (runcode(td, strings.concat("build_", tags[i]), av) != 0) {
fail(label, strings.concat(drvs[i], " build failed (bare-main ",
"collision -> w6l duplicate symbol?)"));
};
let rav: []str = [prog];
if (runcode(td, strings.concat("run_", tags[i]), rav) != want) {
fail(label, strings.concat(drvs[i],
" run-exit != the dep main's return"));
};
i += 1;
};
let csdir: str = strings.concat(td, "/", label, ".cs.bin.sepwork");
let wwdir: str = strings.concat(td, "/", label, ".ww.bin.sepwork");
if (!testenv.has(testenv.readfile(strings.concat(csdir, "/", deps)),
mangled)) {
fail(label, strings.concat(deps, " lacks `", mangled,
"` (dep main not mangled)"));
};
if (countbaremain(csdir) != 1) {
fail(label, strings.concat("bare `TEXT main,` count != 1 ",
"(only the root entry stays bare)"));
};
cmpsepwork(label, csdir, wwdir);
testenv.clean(td);
};
@test fn depmain_aabb() void = {
depmainrow("aabb", "test/wcc/data/depmain99/main.ww", 7,
"aa.bb.s", "TEXT aa.bb.main,");
};
@test fn depmain_cc() void = {
depmainrow("cc", "test/wcc/data/depmain99/main_cc.ww", 9,
"cc.s", "TEXT cc.main,");
};
@test fn declns() void = {
let td: str = testenv.fresh();
let lays: []str = ["modfn_coexist", "modfn_coexist_vbu"];
let labs: []str = ["coexist", "coexist_vbu"];
let drvs: []str = ["ww", "ww_ww"];
let tags: []str = ["cs", "ww"];
let l: i32 = 0;
for (l < 2) {
let s: i32 = 0;
for (s < 2) {
let prog: str = strings.concat(td, "/", labs[l], ".",
tags[s]);
let src: str = strings.concat(testenv.repo(),
"/test/wcc/data/", lays[l], "/main.ww");
let av: []str = [testenv.driver(drvs[s]), "build", "-o",
prog, src];
if (runcode(td, strings.concat("b_", labs[l], "_", tags[s]),
av) != 0) {
fail(labs[l], strings.concat(drvs[s], " build failed"));
};
let rav: []str = [prog];
if (runcode(td, strings.concat("r_", labs[l], "_", tags[s]),
rav) != 6) {
fail(labs[l], strings.concat(drvs[s],
" exit != 6 (coexistence mis-resolved)"));
};
s += 1;
};
l += 1;
};
// Both the dependency and raw explicit root emit complete package artifacts.
let parts: []str = ["aa.s", "aa.wwi", "aa.a", "aa.unit.ww",
"__root.s", "__root.wwi", "__root.a", "__root.unit.ww"];
// cs==ww (rule 10) per layout over the complete package-artifact table
let l2: i32 = 0;
for (l2 < 2) {
let p: i32 = 0;
for (p < parts.len) {
if (!testenv.same(
testenv.readfile(strings.concat(td, "/", labs[l2],
".cs.sepwork/", parts[p])),
testenv.readfile(strings.concat(td, "/", labs[l2],
".ww.sepwork/", parts[p])))) {
fail(labs[l2], strings.concat("cs!=ww for ", parts[p],
" (rule 10)"));
};
p += 1;
};
l2 += 1;
};
// cross-LAYOUT byte-id, same stage: decl-order independence of the
// SK_USE/value promote. `.unit.ww` embeds the differently ORDERED
// source, so the codegen claim is tested on .s/.wwi only.
let oparts: []str = ["aa.s", "aa.wwi", "__root.s"];
let t: i32 = 0;
for (t < 2) {
let p: i32 = 0;
for (p < oparts.len) {
if (!testenv.same(
testenv.readfile(strings.concat(td, "/coexist.",
tags[t], ".sepwork/", oparts[p])),
testenv.readfile(strings.concat(td, "/coexist_vbu.",
tags[t], ".sepwork/", oparts[p])))) {
fail("declns", strings.concat("vbu!=non-vbu for ",
oparts[p], " (", tags[t],
") -- silent decl-order dependence"));
};
p += 1;
};
t += 1;
};
// non-vacuity: the two namespaces resolve to DISTINCT symbols
let t2: i32 = 0;
for (t2 < 2) {
let rs: str = testenv.readfile(strings.concat(td, "/coexist.",
tags[t2], ".sepwork/__root.s"));
if (!testenv.has(rs, "CALL\taa.helper(SB)")) {
fail("declns", strings.concat("__root.s (", tags[t2],
") lacks module-qualified `CALL aa.helper(SB)`"));
};
if (!testenv.has(rs, "CALL\tmain.aa(SB)")) {
fail("declns", strings.concat("__root.s (", tags[t2],
") lacks local-fn `CALL main.aa(SB)`"));
};
t2 += 1;
};
testenv.clean(td);
};
// fresh two-directory tree per (row,driver); relative build with the
// tree as cwd (canonical dir-package build, binary lands in cwd).
fn sltbuild(drv: str, param: str) i32 = {
let dir: str = testenv.fresh();
assert(os.mkdir(strings.concat(dir, "/xa"), 493) == 0);
assert(os.mkdir(strings.concat(dir, "/xb"), 493) == 0);
testenv.writefile(strings.concat(dir, "/xa/xa.ww"), strings.concat(
"package xa;\n",
"export type invalid = !i8;\n"));
testenv.writefile(strings.concat(dir, "/xb/xb.ww"), strings.concat(
"package xb;\n",
"import xa;\n",
"export type invalid = !i64;\n",
"export fn f(", param,
": i32) i64 = { return size(invalid): i64; };\n"));
testenv.writefile(strings.concat(dir, "/root.ww"), strings.concat(
"package main;\n",
"import xb;\n",
"import xa;\n",
"fn main() int = { return xb.f(0): int; };\n"));
let bav: []str = [testenv.driver(drv), "build", "root.ww"];
let brc: i32 = runcode(dir, "build", bav);
if (brc != 0) {
testenv.clean(dir);
return -1;
};
let rav: []str = [strings.concat(dir, "/root")];
let got: i32 = runcode(dir, "run", rav);
testenv.clean(dir);
return got;
};
fn sltrow(label: str, param: str, want: i32) void = {
let gc: i32 = sltbuild("ww", param);
if (gc != want) { fail(label, "cstage exit != want (size of the wrong module's type)"); };
let gw: i32 = sltbuild("ww_ww", param);
if (gw != gc) {
fail(label, strings.concat("cs != ww exit (scopelookuptype ",
"mod-preference divergence -- #58/#50 c1)"));
};
if (gw != want) { fail(label, "wwstage exit != want"); };
};
@test fn slt_shadow() void = { sltrow("shadow", "invalid", 8); };
@test fn slt_noshadow() void = { sltrow("noshadow", "x", 8); };