test: port the @test drop/keep/link observer to ww; retire 911_attest_drop
This commit is contained in:
177
test/tool/attest_test.ww
Normal file
177
test/tool/attest_test.ww
Normal file
@@ -0,0 +1,177 @@
|
||||
package attest_test;
|
||||
|
||||
// The #6 @test DROP/KEEP/LINK emission property, driven by w6c /
|
||||
// w6c_ww DIRECTLY on the import-free test/wcc/data/attest_* fixtures.
|
||||
// Port of the retired native carrier test/wcc/911_attest_drop.c;
|
||||
// every assertion preserved.
|
||||
//
|
||||
// Direct-frontend is the only host that can compare -T and non-T on
|
||||
// the SAME input: `ww test` is always -T, `ww build` always non-T.
|
||||
// A @test fn is spliced out of a non-test build (harec-faithful,
|
||||
// ref/harec/src/check.c:3941 — checked but never emitted) and kept
|
||||
// under -T (the synth entry references it); the -T synth's `test.run`
|
||||
// callee stays an external CALL, so no driver resolution is needed.
|
||||
//
|
||||
// nondrop — attest_nondrop.ww compiled four ways (cs/ww x non-T/-T):
|
||||
// the plain fn's TEXT def is present in both modes; the three @test
|
||||
// TEXT defs are absent non-T and present under -T. The fixture layout
|
||||
// exercises the splice loop's unlink edges (head @test, surviving
|
||||
// plain fn, two consecutive @test). The presence rows run against the
|
||||
// cstage .s; the cs==ww byte-id in BOTH modes (rule 10) carries them
|
||||
// onto the wwstage .s by construction.
|
||||
//
|
||||
// undefbody — a @test body referencing an undefined symbol rejects in
|
||||
// non-T on both frontends: the body is type-checked BEFORE the splice
|
||||
// drops it (harec checks at :3913, drops at :3941).
|
||||
//
|
||||
// linkfail — non-test code CALLs a @test fn: non-T compile+assemble
|
||||
// succeed per stage triple, but the link MUST fail, and the link
|
||||
// stderr must name calldropped_test (non-vacuity: the failure is ON
|
||||
// the dropped @test symbol — a libwwrt path drift failing every link
|
||||
// would otherwise mask the property).
|
||||
|
||||
import os;
|
||||
import os.exec;
|
||||
import strings;
|
||||
import testenv;
|
||||
import time;
|
||||
|
||||
fn fail(label: str, why: str) void = {
|
||||
let m: str = strings.concat("attest FAIL: ", label, " -- ", why,
|
||||
"\n");
|
||||
os.write(2, m.ptr, m.len: u64);
|
||||
assert(false);
|
||||
};
|
||||
|
||||
fn tmo() time.duration = {
|
||||
return (60i64 * (time.second: i64)): time.duration;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// column-0 anchored TEXT label; the '\n' prepend counts a
|
||||
// file-leading label too.
|
||||
fn hastext(s: str, sym: str) bool = {
|
||||
return testenv.occurrences(strings.concat("\n", s),
|
||||
strings.concat("\nTEXT ", sym, ",")) > 0;
|
||||
};
|
||||
|
||||
@test fn nondrop() void = {
|
||||
let td: str = testenv.fresh();
|
||||
let fixture: str = strings.concat(testenv.repo(),
|
||||
"/test/wcc/data/attest_nondrop.ww");
|
||||
let comps: []str = ["w6c", "w6c", "w6c_ww", "w6c_ww"];
|
||||
let testmode: []bool = [false, true, false, true];
|
||||
let names: []str = ["cp", "ct", "wp", "wt"];
|
||||
let asms: []str = ["", "", "", ""];
|
||||
let j: i32 = 0;
|
||||
for (j < 4) {
|
||||
let outf: str = strings.concat(td, "/", names[j], ".s");
|
||||
let av: []str = alloc([], 5u64)!;
|
||||
append(av, testenv.driver(comps[j]));
|
||||
if (testmode[j]) { append(av, "-T"); };
|
||||
append(av, fixture);
|
||||
append(av, "-o");
|
||||
append(av, outf);
|
||||
if (runcode(td, names[j], av) != 0) {
|
||||
fail("nondrop", strings.concat(comps[j],
|
||||
" nondrop compile failed"));
|
||||
};
|
||||
asms[j] = testenv.readfile(outf);
|
||||
j += 1;
|
||||
};
|
||||
|
||||
let syms: []str = ["data.nondrop_keep", "data.nondrop_test_a",
|
||||
"data.nondrop_test_b", "data.nondrop_test_c",
|
||||
"data.nondrop_keep", "data.nondrop_test_a",
|
||||
"data.nondrop_test_b", "data.nondrop_test_c"];
|
||||
let rowmode: []bool = [false, false, false, false,
|
||||
true, true, true, true];
|
||||
let present: []bool = [true, false, false, false,
|
||||
true, true, true, true];
|
||||
let whats: []str = ["plain fn kept non-T", "head @test dropped non-T",
|
||||
"mid @test dropped non-T", "consecutive @test dropped non-T",
|
||||
"plain fn kept under -T", "head @test kept under -T",
|
||||
"mid @test kept under -T", "consecutive @test kept under -T"];
|
||||
let i: i32 = 0;
|
||||
for (i < syms.len) {
|
||||
let s: str = asms[0];
|
||||
if (rowmode[i]) { s = asms[1]; };
|
||||
if (hastext(s, syms[i]) != present[i]) {
|
||||
fail("nondrop", whats[i]);
|
||||
};
|
||||
i += 1;
|
||||
};
|
||||
if (!testenv.same(asms[0], asms[2])) {
|
||||
fail("nondrop", "non-T @test-drop asm cs!=ww (rule 10)");
|
||||
};
|
||||
if (!testenv.same(asms[1], asms[3])) {
|
||||
fail("nondrop", "-T @test-keep asm cs!=ww (rule 10)");
|
||||
};
|
||||
testenv.clean(td);
|
||||
};
|
||||
|
||||
@test fn undefbody() void = {
|
||||
let td: str = testenv.fresh();
|
||||
let fixture: str = strings.concat(testenv.repo(),
|
||||
"/test/wcc/data/attest_undefbody.ww");
|
||||
let comps: []str = ["w6c", "w6c_ww"];
|
||||
let c: i32 = 0;
|
||||
for (c < 2) {
|
||||
let av: []str = [testenv.driver(comps[c]), fixture, "-o",
|
||||
"/dev/null"];
|
||||
if (runcode(td, strings.concat("reject_", comps[c]), av) == 0) {
|
||||
fail("undefbody", strings.concat(comps[c],
|
||||
" (non-T) accepted an undefined symbol in a @test body"));
|
||||
};
|
||||
c += 1;
|
||||
};
|
||||
testenv.clean(td);
|
||||
};
|
||||
|
||||
@test fn linkfail() void = {
|
||||
let td: str = testenv.fresh();
|
||||
let fixture: str = strings.concat(testenv.repo(),
|
||||
"/test/wcc/data/attest_calldropped.ww");
|
||||
let rt: str = strings.concat(testenv.repo(), "/out/lib/libwwrt.a");
|
||||
let comps: []str = ["w6c", "w6c_ww"];
|
||||
let asmts: []str = ["w6a", "w6a_ww"];
|
||||
let linkts: []str = ["w6l", "w6l_ww"];
|
||||
let s: i32 = 0;
|
||||
for (s < 2) {
|
||||
let asmf: str = strings.concat(td, "/lf.", comps[s], ".s");
|
||||
let obj: str = strings.concat(td, "/lf.", comps[s], ".o");
|
||||
let exe: str = strings.concat(td, "/lf.", comps[s], ".exe");
|
||||
let cav: []str = [testenv.driver(comps[s]), fixture, "-o", asmf];
|
||||
if (runcode(td, strings.concat("cc_", comps[s]), cav) != 0) {
|
||||
fail("linkfail", strings.concat(comps[s],
|
||||
" non-T calldropped compile failed"));
|
||||
};
|
||||
let aav: []str = [testenv.driver(asmts[s]), "-o", obj, asmf];
|
||||
if (runcode(td, strings.concat("as_", asmts[s]), aav) != 0) {
|
||||
fail("linkfail", strings.concat(asmts[s],
|
||||
" calldropped assemble failed"));
|
||||
};
|
||||
let lo: testenv.commandout;
|
||||
let lav: []str = [testenv.driver(linkts[s]), "-o", exe, obj, rt];
|
||||
testenv.runcommand(td, td, strings.concat("ld_", linkts[s]), lav,
|
||||
tmo(), &lo);
|
||||
assert(lo.termination == exec.termination.EXIT);
|
||||
if (lo.code == 0) {
|
||||
fail("linkfail", strings.concat(linkts[s],
|
||||
" linked calldropped (expected undefined-reference to ",
|
||||
"the dropped @test sym)"));
|
||||
};
|
||||
if (!testenv.has(lo.stderr, "calldropped_test")) {
|
||||
fail("linkfail", strings.concat(linkts[s],
|
||||
" link failed but not on the dropped @test sym (masked)"));
|
||||
};
|
||||
s += 1;
|
||||
};
|
||||
testenv.clean(td);
|
||||
};
|
||||
Reference in New Issue
Block a user