package m1_test; // M1 path-mangle RUN-level observers over runtime-written dir-package // trees, both driver stages. Ports of the retired native carriers // test/wcc/989_m1mangle_run.c and 989_m1usehint_run.c; every assertion // preserved. The symbol-level cousin claims live in // test/byteid/mangle_test.ww; these rows own the build/link/run legs. // // m1mangle (#22 + #32) rows, each a tree beside a root main.ww built // with `build -o main main.ww` (cwd = tree, Hare CWD==module-dir // mirror) and run: // nested_call | dir import path-mangles; qualified-ref hint // | resolves the PATH aa.bb.val -> exit 42 // imported_main | imported pkg's private `fn main` mangles aa.bb.main // | instead of colliding with the root entry -> 7 // priv_global | module-PRIVATE value global mangles aa.bb.pv -> 9 // single_level | control: single-level cc.v unchanged -> 5 // deep_nest | 2-level nest aa.bb.cc.val path-mangle -> 3 // // m1usehint (#40) — the cgen mangle-hint must be MODULE-scoped, not // unit-global: two dir-packages both `package math` export the same // leaf `pick`, and each referencing module's `math.pick()` must bind // its OWN import (a.math=111 / b.math=222; mis-route exits 1/2): // leaf_collision | same-leaf exported fns, distinct bodies -> exit 0 // collision_diff | same, mismatched signatures (checker curmod- // | preference agrees with the hint) -> exit 0 // plus the rule-10 byte-id leg: cstage vs wwstage sepwork *.s concat // on the leaf_collision unit, byte-identical (concat order // strengthened from the carrier's shell glob to explicit // byte-lexicographic listdir). // // Dropped C machinery, not assertions: the ww_ww-absent skip gate // (the Make target declares both drivers) and the per-path owned-flag // cleanup ledger (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("m1 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; }; // Every per-package .s under `sepdir`, concatenated in byte-sorted // order; "" when the directory is missing or holds no .s. fn catasm(sepdir: str) str = { if (!testenv.isdir(sepdir)) { return ""; }; let names: []str = testenv.listdir(sepdir); let out: str = ""; let i: i32 = 0; for (i < names.len) { if (strings.hassuffix(names[i], ".s")) { out = strings.concat(out, testenv.readfile(strings.concat(sepdir, "/", names[i]))); }; i += 1; }; return out; }; fn mkdirs(td: str, dirs: []str) void = { let d: i32 = 0; for (d < dirs.len) { assert(os.mkdir(strings.concat(td, "/", dirs[d]), 493) == 0); d += 1; }; }; // dirs are '/'-joined mkdir steps, parents before children. fn m1row(label: str, dirs: []str, rel: str, content: str, root: str, want: i32) void = { let drvs: []str = ["ww", "ww_ww"]; let s: i32 = 0; for (s < 2) { let td: str = testenv.fresh(); mkdirs(td, dirs); testenv.writefile(strings.concat(td, "/", rel), content); testenv.writefile(strings.concat(td, "/main.ww"), root); let av: []str = [testenv.driver(drvs[s]), "build", "-o", "main", "main.ww"]; if (runcode(td, strings.concat("build_", drvs[s]), av) != 0) { fail(label, strings.concat(drvs[s], " build failed (broken ", "mangle hint or skip rule -> link failure)")); }; let rav: []str = [strings.concat(td, "/main")]; if (runcode(td, strings.concat("run_", drvs[s]), rav) != want) { fail(label, strings.concat(drvs[s], " run-exit differs from the row's want")); }; testenv.clean(td); s += 1; }; }; @test fn nested_call() void = { let dirs: []str = ["aa", "aa/bb"]; m1row("nested_call", dirs, "aa/bb/bb.ww", strings.concat( "package bb;\n", "export fn val() int = { return 42; };\n"), strings.concat( "package main;\n", "import aa.bb;\n", "export fn main() int = { return bb.val(); };\n"), 42); }; @test fn imported_main() void = { let dirs: []str = ["aa", "aa/bb"]; m1row("imported_main", dirs, "aa/bb/bb.ww", strings.concat( "package bb;\n", "fn main() int = { return 7; };\n", "export fn run() int = { return main(); };\n"), strings.concat( "package main;\n", "import aa.bb;\n", "export fn main() int = { return bb.run(); };\n"), 7); }; @test fn priv_global() void = { let dirs: []str = ["aa", "aa/bb"]; m1row("priv_global", dirs, "aa/bb/bb.ww", strings.concat( "package bb;\n", "let pv: int = 9;\n", "export fn getpv() int = { return pv; };\n"), strings.concat( "package main;\n", "import aa.bb;\n", "export fn main() int = { return bb.getpv(); };\n"), 9); }; @test fn single_level() void = { let dirs: []str = ["cc"]; m1row("single_level", dirs, "cc/cc.ww", strings.concat( "package cc;\n", "export fn v() int = { return 5; };\n"), strings.concat( "package main;\n", "import cc;\n", "export fn main() int = { return cc.v(); };\n"), 5); }; @test fn deep_nest() void = { let dirs: []str = ["aa", "aa/bb", "aa/bb/cc"]; m1row("deep_nest", dirs, "aa/bb/cc/cc.ww", strings.concat( "package cc;\n", "export fn val() int = { return 3; };\n"), strings.concat( "package main;\n", "import aa.bb.cc;\n", "export fn main() int = { return cc.val(); };\n"), 3); }; fn uselayout(td: str, apick: str, onecall: str) void = { let dirs: []str = ["a", "a/math", "b", "b/math", "one", "two"]; mkdirs(td, dirs); testenv.writefile(strings.concat(td, "/a/math/math.ww"), strings.concat("package math;\n", apick)); testenv.writefile(strings.concat(td, "/b/math/math.ww"), strings.concat( "package math;\n", "export fn pick() int = { return 222; };\n")); testenv.writefile(strings.concat(td, "/one/one.ww"), strings.concat( "package one;\n", "import a.math;\n", "export fn getone() int = { return ", onecall, "; };\n")); testenv.writefile(strings.concat(td, "/two/two.ww"), strings.concat( "package two;\n", "import b.math;\n", "export fn gettwo() int = { return math.pick(); };\n")); testenv.writefile(strings.concat(td, "/main.ww"), strings.concat( "package main;\n", "import one;\n", "import two;\n", "export fn main() int = {\n", " if (one.getone() != 111) { return 1; };\n", " if (two.gettwo() != 222) { return 2; };\n", " return 0;\n", "};\n")); }; fn userow(label: str, apick: str, onecall: str) void = { let drvs: []str = ["ww", "ww_ww"]; let s: i32 = 0; for (s < 2) { let td: str = testenv.fresh(); uselayout(td, apick, onecall); let av: []str = [testenv.driver(drvs[s]), "build", "-o", "main", "main.ww"]; if (runcode(td, strings.concat("build_", drvs[s]), av) != 0) { fail(label, strings.concat(drvs[s], " build failed")); }; let rav: []str = [strings.concat(td, "/main")]; if (runcode(td, strings.concat("run_", drvs[s]), rav) != 0) { fail(label, strings.concat(drvs[s], " run-exit != 0 (a ", "file-global first-match hint routes one math.pick() to ", "the wrong package -- #40)")); }; testenv.clean(td); s += 1; }; }; @test fn leaf_collision() void = { userow("leaf_collision", "export fn pick() int = { return 111; };\n", "math.pick()"); }; @test fn collision_diff() void = { userow("collision_diff", "export fn pick(x: int) int = { return x + 100; };\n", "math.pick(11)"); }; @test fn usehint_byteid() void = { let td: str = testenv.fresh(); uselayout(td, "export fn pick() int = { return 111; };\n", "math.pick()"); let drvs: []str = ["ww", "ww_ww"]; let tags: []str = ["cstage", "wwstage"]; let asms: []str = ["", ""]; let s: i32 = 0; for (s < 2) { let av: []str = [testenv.driver(drvs[s]), "build", "-o", tags[s], "main.ww"]; if (runcode(td, strings.concat("bid_", tags[s]), av) != 0) { fail("usehint_byteid", strings.concat(tags[s], " build failed")); }; asms[s] = catasm(strings.concat(td, "/", tags[s], ".sepwork")); s += 1; }; if (asms[0].len == 0 || asms[1].len == 0) { fail("usehint_byteid", "empty .s concat"); }; if (!testenv.same(asms[0], asms[1])) { fail("usehint_byteid", "cstage.s != wwstage.s (byte-id break)"); }; testenv.clean(td); };