package wwdump_test; // wwdump_ww observer gates. Ports of the retired native carriers // test/wcc/901_asserttyped_gap.c and 989_wwdumpgate_run.c; every // assertion preserved. WWSTAGE-ONLY by construction: the cstage // wwdump implements only -t/-a, and the asserttyped pass lives in // wwstage check.ww — there is no cstage twin for either claim // (wwstage-warn-audit rule: drive the wwstage checker with *_ww // binaries, never cstage tools). // // asserttyped (#15 / A.6.2.1e) — the wwstage checker's post-checker // nil-type invariant gate is UNOBSERVED by every other suite (the // ordinary suites drive cstage check.c; only -t/-a run elsewhere). // Run `wwdump_ww -c` over the gap-bearing manifest and pin the count // of line-leading "asserttyped:" stderr diagnostics per fixture; the // manifest is the all-closed floor (every class A-G at zero), so any // fresh nil-gap or regressed class fails loud and names its fixture. // wwdump_ww's exit code is deliberately NOT gated: the diagnostics // land on stderr regardless, and the armed bail flips the exit code — // the line count is the one signal stable across the fold sequence. // #90 sep-feed: `ww build -S` resolves production fixtures and `ww test // -S` resolves test fixtures to an owner unit and direct exports; this raw // wwdump-only gate explicitly composes those artifacts because wwdump has no // package-input CLI. The import-free test/wcc/901_*.ww companions stay // raw-fed. // // wwdumpgate (#52, F15 c4) — the -c and -r arms gate on parse-stage // errors instead of silently emitting over a broken AST: parse-errored // input under -c exits nonzero with exactly 0 stdout bytes (pre-fix: // rc=0 with truncated asm — and -c IS the 994 byte-identity probe // arm); -r likewise exits nonzero; the valid-file -c control exits 0 // with asm on stdout. The C carrier's wwdump_ww-missing wiring check // is carried by the Make prerequisite plus runcommand's launch // assert. import os; import os.exec; import strings; import testenv; import time; fn fail(label: str, why: str) void = { let m: str = strings.concat("wwdump FAIL: ", label, " -- ", why, "\n"); os.write(2, m.ptr, m.len: u64); assert(false); }; fn tmo() time.duration = { return (180i64 * (time.second: i64)): time.duration; }; // Line-leading occurrences; the '\n' prepend counts a stream-leading // diagnostic too. fn countdiag(stderr: str) i32 = { return testenv.occurrences(strings.concat("\n", stderr), "\nasserttyped:"); }; // Gap classes A-G (residual-carrier-audit.json 901 entry); want is // the all-closed floor. sep rows compose the resolved unit first. @test fn asserttyped() void = { let rels: []str = [ "lib/math/checked/checked_test.ww", "test/wcc/data/selfhost_smoke/case.ww", "lib/encoding/utf8/utf8.ww", "lib/fnmatch/fnmatch_test.ww", "lib/math/random/random_test.ww", "test/wcc/901_enum_corpus.ww", "test/wcc/901_forrange_tuple.ww", "test/wcc/901_massign_blank.ww"]; let classes: []str = [ "A module-qual N_DOT call result", "B fn-ptr struct-field call", "C abort intrinsic callee", "D module-leaf == type/fn name", "D module-leaf == type/fn name", "E computed enum-member value-expr", "F for-range tuple-destructure bind", "G tuple multi-assign discard `_`"]; let seps: []bool = [true, true, true, true, true, false, false, false]; let wants: []i32 = [0, 0, 0, 0, 0, 0, 0, 0]; let i: i32 = 0; for (i < rels.len) { let td: str = testenv.fresh(); let src: str = strings.concat(testenv.repo(), "/", rels[i]); if (seps[i]) { let stem: str = strings.concat(td, "/unit"); let bo: testenv.commandout; let action: str = "build"; if (strings.hassuffix(rels[i], "_test.ww")) { action = "test"; }; let bav: []str = [testenv.driver("ww"), action, "-S", "-o", stem, src]; testenv.runcommand(td, td, "resolve", bav, tmo(), &bo); if (bo.termination != exec.termination.EXIT || bo.code != 0) { fail(classes[i], strings.concat(rels[i], ": no resolved sep unit (ww build/test -S failed)")); }; let work: str = strings.concat(stem, ".sepwork/"); let owner: str = strings.concat(work, "__root.unit.ww"); if (!testenv.exists(owner)) { fail(classes[i], strings.concat(rels[i], ": __root.unit.ww missing after ww build/test -S")); }; let deps: []str = []; if (i == 0) { append(deps, "math.checked"); append(deps, "types"); }; if (i == 1) { append(deps, "ascii"); append(deps, "os"); append(deps, "strconv"); }; if (i == 3) { append(deps, "fnmatch"); }; if (i == 4) { append(deps, "math.random"); append(deps, "test"); }; let composed: str = ""; let di: i32 = 0; for (di < deps.len) { composed = strings.concat(composed, "//ww:module ", deps[di], "\n", testenv.readfile(strings.concat(work, deps[di], ".wwi")), "\n"); di += 1; }; composed = strings.concat(composed, testenv.readfile(owner)); src = strings.concat(td, "/wwdump.raw.ww"); testenv.writefile(src, composed); }; let co: testenv.commandout; let av: []str = [testenv.driver("wwdump_ww"), "-c", src]; testenv.runcommand(td, td, "check", av, tmo(), &co); assert(co.termination == exec.termination.EXIT); let got: i32 = countdiag(co.stderr); if (got != wants[i]) { // surface the drifted diagnostics before the abort os.write(2, co.stderr.ptr, co.stderr.len: u64); fail(classes[i], strings.concat(rels[i], ": asserttyped count drifted from the manifest")); }; testenv.clean(td); i += 1; }; }; fn dumprow(td: str, name: str, mode: str, src: str, out: *testenv.commandout) void = { let av: []str = [testenv.driver("wwdump_ww"), mode, src]; testenv.runcommand(td, td, name, av, tmo(), out); assert(out.termination == exec.termination.EXIT); }; @test fn wwdumpgate() void = { let td: str = testenv.fresh(); let badp: str = strings.concat(td, "/bad.ww"); testenv.writefile(badp, strings.concat( "package main;\n\n", "export fn main(argc: i32, argv: **u8) i32 = {\n", "\treturn 0;\n};\n\n", "fn broken( {\n")); let okp: str = strings.concat(td, "/ok.ww"); testenv.writefile(okp, "package main;\nexport fn main() i32 = { return 0; };\n"); let co: testenv.commandout; dumprow(td, "bad_a", "-a", badp, &co); if (co.code == 0) { fail("bad_a", "AST dump returned rc=0 on parse-errored input"); }; dumprow(td, "bad_c", "-c", badp, &co); if (co.code == 0 || co.stdout.len != 0) { fail("bad_c", "asm emitted or rc=0 on parse-errored input (#52)"); }; dumprow(td, "bad_r", "-r", badp, &co); if (co.code == 0) { fail("bad_r", "resolve report with rc=0 on parse-errored input"); }; dumprow(td, "ok_c", "-c", okp, &co); if (co.code != 0 || co.stdout.len == 0) { fail("ok_c", "healthy file rejected or produced no asm"); }; testenv.clean(td); };