package libbyteid_test; // cstage-vs-wwstage byte-identity gate over the whole lib/ surface, and // the ONE owner of wwstage-DRIVER-leg byte identity. Port of the // retired native carrier test/wcc/989_lib_byteid.c; every assertion // preserved, .s concat order strengthened from shell-glob to explicit // byte-lexicographic. // // The bootstrap gates (991-995) byte-id only the modules the selfhost // tools import. Every other lib/ module compiles through the CSTAGE // driver alone elsewhere, so a cs≠ww divergence there ships gate-green // (regex.finish did, task #21 FC0). This gate closes the class: each // lib test fixture is sep-built through BOTH driver stages and the // per-package asm compared. // // #94 sep layout: under sep EACH package compiles to its OWN // .sepwork/.s — the lib body lands in .s, NOT in // __root.s. The byte-id concats EVERY per-package .s (lib body + // lib/test's auto-bundled runner), never just __root.s: comparing the // root alone would byte-id the harness wrapper while the engine ships // uncovered. // // Three pinned outcomes, loud over blind (task #21 ruling): // ID — byte-identical across all per-package .s, zero tolerance. // DIVERGE — known cs≠ww divergence (task #59, cite). Both stages // must still compile and the asm must still DIFFER; a fix // fails the entry demanding graduation to ID. // WWREJECT — cstage compiles, wwstage errors (task #59, cite); // graduation pinned the same way. // // Fixtureless modules are covered by import-probe stubs: `import` // drags the whole directory module into its own sep package. Each probe // carries a sentinel that must appear in a per-package .unit.ww, and // the completeness scan fails loudly on any lib/ dir not enrolled here. import os; import os.exec; import strings; import testenv; import time; def MID: i32 = 0; def MDIVERGE: i32 = 1; def MWWREJECT: i32 = 2; def NENTEXPECT: i32 = 56; type ent = struct { fixture: str, // repo-relative .ww; "" -> probe entry probe: str, // inline import-probe source inc: str, // extra -I dir beyond dirname(fixture) mode: i32, cite: str, // task #59 entry for non-ID modes sentinel: str, // probe-only: proof the module body landed moddir: str, // probe-only: completeness key direct: bool, // build moddir itself; used when it exports no source name }; // Row constructors; the roster appends their results directly (#34 // closed — append accepts struct call rvalues). fn fx(f: str) ent = { let e: ent; e.fixture = f; e.probe = ""; e.inc = ""; e.mode = MID; e.cite = ""; e.sentinel = ""; e.moddir = ""; e.direct = false; return e; }; fn fxi(f: str, inc: str) ent = { let e: ent = fx(f); e.inc = inc; return e; }; fn pr(p: str, sentinel: str, moddir: str) ent = { let e: ent = fx(""); e.probe = p; e.sentinel = sentinel; e.moddir = moddir; return e; }; fn pri(p: str, inc: str, sentinel: str, moddir: str) ent = { let e: ent = pr(p, sentinel, moddir); e.inc = inc; return e; }; // Dir-mode entry: byte-id the module's composed test package (every // *_test.ww under moddir, module-reset separators, byte-lex order) through // both driver stages. The separators preserve each source file's import // scope. Needed once a package's test files share a helpers file and stop // being standalone single-file roots (the B3 strings split). fn dx(moddir: str) ent = { let e: ent = fx(""); e.moddir = moddir; return e; }; // Direct-directory entry for packages with no exported source name. Such a // package cannot have a valid nonblank import probe until blank imports exist. fn dr(moddir: str, sentinel: str) ent = { let e: ent = fx(""); e.moddir = moddir; e.sentinel = sentinel; e.direct = true; return e; }; // The 56-unit roster. Graduation history lives in git (the retired C // carrier's table comments); cites are kept only where a non-ID pin // would need them. fn corpus() []ent = { let es: []ent = alloc([], NENTEXPECT: u64)!; append(es, fx("lib/bytes/equal_test.ww")); append(es, fx("lib/bytes/index_test.ww")); append(es, fx("lib/bytes/contains_test.ww")); append(es, fx("lib/bytes/tokenize_test.ww")); append(es, fx("lib/bytes/trim_test.ww")); append(es, fx("lib/dirs/dirs_test.ww")); append(es, fxi("lib/encoding/base32/base32_test.ww", "lib/encoding")); append(es, fx("lib/encoding/hex/hex_test.ww")); append(es, fx("lib/encoding/utf8/rune_test.ww")); append(es, fx("lib/encoding/utf8/encode_test.ww")); append(es, fx("lib/encoding/utf8/decode_test.ww")); append(es, fx("lib/encoding/utf8/types_test.ww")); append(es, fx("lib/getopt/getopt_test.ww")); append(es, fxi("lib/hash/adler32/adler32_test.ww", "lib/hash")); append(es, fxi("lib/hash/crc16/crc16_test.ww", "lib/hash")); append(es, fxi("lib/hash/crc32/crc32_test.ww", "lib/hash")); append(es, fxi("lib/hash/crc64/crc64_test.ww", "lib/hash")); append(es, fxi("lib/hash/siphash/siphash_test.ww", "lib/hash")); append(es, fx("lib/math/checked/checked_test.ww")); append(es, fx("lib/math/random/random_test.ww")); append(es, fx("lib/memio/memio_test.ww")); append(es, fx("lib/os/os_test.ww")); append(es, pr("package main;\nimport os.exec;\nfn main() i32 = { return exec.termination.EXIT as i32; };\n", "package exec;", "lib/os/exec")); append(es, fx("lib/regex/regex_test.ww")); append(es, fx("lib/strconv/ftos_test.ww")); append(es, fx("lib/strconv/stof_test.ww")); append(es, fx("lib/strconv/int_test.ww")); // strings' families share helpers_test.ww (B3 split) — the // single-file roots dissolved; the composed package is the root. append(es, dx("lib/strings")); append(es, fx("lib/temp/temp_test.ww")); append(es, fx("lib/time/arithm_test.ww")); append(es, fx("lib/time/duration_test.ww")); append(es, fx("lib/time/instant_test.ww")); append(es, fx("lib/bufio/stream_test.ww")); append(es, fx("lib/bufio/scanner_test.ww")); append(es, fx("lib/fmt/fmt_test.ww")); append(es, pr("package main;\nimport sort;\nfn use(v: sort.cmpfunc) void = {};\nfn main() i32 = { return 0; };\n", "package sort;", "lib/sort")); append(es, pr("package main;\nimport path;\nfn use(v: path.buffer) void = {};\nfn main() i32 = { return 0; };\n", "package path;", "lib/path")); append(es, pr("package main;\nimport endian;\nfn main() i32 = { let b: [2]u8; return endian.begetu16(b[0:2]): i32; };\n", "package endian;", "lib/endian")); append(es, pr("package main;\nimport net;\nfn use(v: net.sockaddrin) void = {};\nfn main() i32 = { return 0; };\n", "package net;", "lib/net")); append(es, pr("package main;\nimport hash;\nfn use(v: hash.hash) void = {};\nfn main() i32 = { return 0; };\n", "package hash;", "lib/hash")); // fnv lives below the driver's default root, so its canonical // directory is found from the explicit parent import root. append(es, pri("package main;\nimport fnv;\nfn main() i32 = { let b: []u8; let _ = fnv.fnv1a(b); return 0; };\n", "lib/hash", "package fnv;", "lib/hash/fnv")); // Sentinel is a fn, not `package math;` — lib/math is also package // math, so the package clause alone would not prove the right body. append(es, pr("package main;\nimport crypto.math;\nfn main() i32 = { return math.rotl32(0u32, 0): i32; };\n", "fn rotl32", "lib/crypto/math")); append(es, dr("lib/c/libc", "package libc;")); // root-only, ZERO-dep build -S: the one driver-leg edge every // import probe misses (dep-count-0 unit composition). Folded in // from the retired 815/940/951 driver-parity carriers. No // sentinel: there is no dep unit to prove. append(es, pr("package main;\nfn main() i32 = { return 0; };\n", "", "")); append(es, fx("lib/ascii/ascii_test.ww")); append(es, fxi("lib/encoding/base64/base64_test.ww", "lib/encoding")); append(es, fx("lib/errors/errno_test.ww")); append(es, fx("lib/log/funcs_test.ww")); append(es, fx("lib/log/global_test.ww")); append(es, fx("lib/log/silent_test.ww")); append(es, fx("lib/os/stat_test.ww")); // toktest/asttest resolve `import syntax` via -I lib/ww, cf 905 append(es, fxi("lib/ww/syntax/tok_test.ww", "lib/ww")); append(es, fxi("lib/ww/syntax/ast_test.ww", "lib/ww")); append(es, fx("lib/crypto/sha256/sha256_test.ww")); append(es, fx("lib/fnmatch/fnmatch_test.ww")); append(es, fx("lib/shlex/shlex_test.ww")); assert(es.len == NENTEXPECT); return es; }; // every lib/ dir holding .ww source must be accounted for — enrolled in // the corpus (fixture dirname or probe moddir) or on this covered list; // otherwise a new module ships with zero byte-id coverage. fn covered() []str = { let cs: []str = []; // selfhost-embedded: byte-id'd by the 990-997 gates append(cs, "lib/io"); append(cs, "lib/math"); append(cs, "lib/rt"); append(cs, "lib/types"); // #17: the @test runner is AUTO-BUNDLED into every -T sep build, // so every @test fixture byte-ids it cs/ww; 911_attest_record // also compares it directly. It has no _test.ww of its own. append(cs, "lib/test"); return cs; }; fn fail(label: str, why: str) void = { let m: str = strings.concat("lib_byteid FAIL: ", label, " -- ", why, "\n"); os.write(2, m.ptr, m.len: u64); assert(false); }; fn dirnameof(p: str) str = { let last: i32 = -1; let i: i32 = 0; for (i < p.len) { if (p[i] == '/') { last = i; }; i += 1; }; assert(last > 0); return strings.sub(p, 0, last); }; fn basenameof(p: str) str = { let last: i32 = -1; let i: i32 = 0; for (i < p.len) { if (p[i] == '/') { last = i; }; i += 1; }; return strings.sub(p, last + 1, p.len); }; fn stripext(base: str) str = { assert(strings.hassuffix(base, ".ww")); return strings.sub(base, 0, base.len - 3); }; // Concatenate every per-package .s under `sepdir` 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")) { let body: str = testenv.readfile( strings.concat(sepdir, "/", names[i])); out = strings.concat(out, body); }; i += 1; }; return out; }; fn hasunit(sepdir: str) bool = { if (!testenv.isdir(sepdir)) { return false; }; let names: []str = testenv.listdir(sepdir); let i: i32 = 0; for (i < names.len) { if (strings.hassuffix(names[i], ".unit.ww")) { return true; }; i += 1; }; return false; }; fn hasasm(sepdir: str) bool = { if (!testenv.isdir(sepdir)) { return false; }; let names: []str = testenv.listdir(sepdir); let i: i32 = 0; for (i < names.len) { if (strings.hassuffix(names[i], ".s")) { return true; }; i += 1; }; return false; }; fn buildstage(td: str, name: str, drv: str, sub: str, incs: []str, stem: str, base: str) bool = { let av: []str = []; append(av, drv); append(av, sub); append(av, "-S"); let i: i32 = 0; for (i < incs.len) { append(av, "-I"); append(av, incs[i]); i += 1; }; append(av, "-o"); append(av, stem); append(av, base); let co: testenv.commandout; testenv.runcommand(td, td, name, av, (180i64 * (time.second: i64)): time.duration, &co); return co.termination == exec.termination.EXIT && co.code == 0; }; fn checkone(e: *ent) void = { let td: str = testenv.fresh(); let label: str = "import-probe"; if (e.fixture.len != 0) { label = e.fixture; } else { if (e.moddir.len != 0) { label = e.moddir; }; }; let base: str = "probe.ww"; if (e.fixture.len != 0) { base = basenameof(e.fixture); testenv.writefile(strings.concat(td, "/", base), testenv.readfile(strings.concat(testenv.repo(), "/", e.fixture))); } else { if (e.probe.len != 0) { testenv.writefile(strings.concat(td, "/", base), e.probe); } else { if (e.direct) { base = strings.concat(testenv.repo(), "/", e.moddir); } else { // Dir-mode keeps every source in its own module-reset section. base = "combined.ww"; let srcdir: str = strings.concat(testenv.repo(), "/", e.moddir); let names: []str = testenv.listdir(srcdir); let body: str = ""; let ni: i32 = 0; for (ni < names.len) { if (strings.hassuffix(names[ni], "_test.ww")) { body = strings.concat(body, "//ww:module-reset\n", testenv.readfile(strings.concat( srcdir, "/", names[ni])), "\n"); }; ni += 1; }; testenv.writefile(strings.concat(td, "/", base), body); }; }; }; // Most packages resolve from the driver's canonical lib/ root. // Nested packages whose tests use a bare leaf import carry an // explicit parent root in `inc`; pointing at the package directory // itself would depend on the retired .ww fallback. let incs: []str = []; if (e.inc.len != 0) { append(incs, strings.concat(testenv.repo(), "/", e.inc)); }; // @test fixtures and dir-mode compositions are main-less, so // `test -S` carries -T (synthesizes the entry + auto-bundles // lib/test); import probes carry their own fn main(), which -T // loud-rejects (910), so they `build -S`. let sub: str = "test"; if (e.probe.len != 0 || e.direct) { sub = "build"; }; let stem: str = "direct"; if (!e.direct) { stem = stripext(base); }; let stemc: str = strings.concat(td, "/c_", stem); let stemw: str = strings.concat(td, "/w_", stem); let ce: bool = buildstage(td, "cstage", testenv.driver("ww"), sub, incs, stemc, base); let we: bool = buildstage(td, "wwstage", testenv.driver("ww_ww"), sub, incs, stemw, base); // The cstage build resolves the units. A source probe owns __root; a // directly selected package retains its canonical import identity. // WWREJECT still requires cstage to compile. let resolvedunit: str = strings.concat(stemc, ".sepwork/__root.unit.ww"); let resolved: bool = testenv.exists(resolvedunit); if (e.direct) { resolved = hasunit(strings.concat(stemc, ".sepwork")); }; if (!ce || !resolved) { fail(label, "cstage produced no resolved unit"); }; // Probe coverage: resolution failures are loud, and the sentinel // additionally proves that the expected directory body (rather // than an accidental same-name package) landed. if (e.sentinel.len != 0) { let sepc: str = strings.concat(stemc, ".sepwork"); let names: []str = testenv.listdir(sepc); let found: bool = false; let i: i32 = 0; for (i < names.len) { if (strings.hassuffix(names[i], ".unit.ww")) { if (testenv.has(testenv.readfile( strings.concat(sepc, "/", names[i])), e.sentinel)) { found = true; }; }; i += 1; }; if (!found) { fail(label, "import silently dropped; probe covers nothing"); }; }; if (e.mode == MWWREJECT) { if (we) { fail(label, strings.concat("wwstage now compiles this; ", "graduate the ", e.cite, " pin to ID or DIVERGE")); }; testenv.clean(td); return; }; if (!we) { fail(label, "wwstage rejected"); }; let csep: str = strings.concat(stemc, ".sepwork"); let wsep: str = strings.concat(stemw, ".sepwork"); let cs: str = catasm(csep); let ws: str = catasm(wsep); // Two missing assembly sets compare equal and cover nothing. A directly // selected declaration-only package legitimately emits an empty .s file; // its resolved owner unit and sentinel above prove the source was compiled. if (!hasasm(csep) || !hasasm(wsep) || (!e.direct && (cs.len == 0 || ws.len == 0))) { fail(label, "empty .s concat"); }; let idsame: bool = testenv.same(cs, ws); if (e.mode == MID && !idsame) { fail(label, "cs vs ww asm differs (byte-id broken)"); }; if (e.mode == MDIVERGE && idsame) { fail(label, strings.concat("now byte-identical; graduate the ", e.cite, " pin to ID")); }; testenv.clean(td); }; fn accounted(dir: str, es: []ent, cov: []str) bool = { let i: i32 = 0; for (i < es.len) { if (es[i].fixture.len != 0) { if (testenv.same(dir, dirnameof(es[i].fixture))) { return true; }; } else { if (es[i].moddir.len != 0) { if (testenv.same(dir, es[i].moddir)) { return true; }; }; }; i += 1; }; i = 0; for (i < cov.len) { if (testenv.same(dir, cov[i])) { return true; }; i += 1; }; return false; }; // Directories under `dir` (repo-relative `rel`) holding .ww source, // excluding generated .sepwork trees. fn wwdirs(dir: str, rel: str) []str = { let out: []str = []; let names: []str = testenv.listdir(dir); let hasww: bool = false; let i: i32 = 0; for (i < names.len) { let p: str = strings.concat(dir, "/", names[i]); if (testenv.isdir(p)) { if (!strings.hassuffix(names[i], ".sepwork")) { let sub: []str = wwdirs(p, strings.concat(rel, "/", names[i])); let j: i32 = 0; for (j < sub.len) { append(out, sub[j]); j += 1; }; }; } else { if (strings.hassuffix(names[i], ".ww")) { hasww = true; }; }; i += 1; }; if (hasww) { append(out, rel); }; return out; }; @test fn corpuscomplete() void = { let es: []ent = corpus(); assert(es.len == NENTEXPECT); let dirs: []str = wwdirs(strings.concat(testenv.repo(), "/lib"), "lib"); // an empty scan means the walk itself broke — never pass on that assert(dirs.len > 0); let cov: []str = covered(); let i: i32 = 0; for (i < dirs.len) { if (!accounted(dirs[i], es, cov)) { fail(dirs[i], "un-enrolled lib module; add it to the corpus"); }; i += 1; }; }; @test fn roster() void = { let es: []ent = corpus(); assert(es.len == NENTEXPECT); let i: i32 = 0; for (i < es.len) { checkone(&es[i]); i += 1; }; };