test: port the wwdump_ww gate observers to ww; retire 901_asserttyped_gap + 989_wwdumpgate_run
One commit for both carriers: the asserttyped manifest and the -c/-r parse-error gate are the same wwstage-only wwdump_ww subprocess surface. The 901_*.ww raw gap companions stay in test/wcc, fed via testenv.repo().
This commit is contained in:
3
Makefile
3
Makefile
@@ -419,7 +419,8 @@ MISC_WW_TARGETS = $(MISC_WW_TESTS:%=wwtest/%)
|
||||
# diagnostic gates, driver-CLI parity, @symbol FFI asm needles, .wwi
|
||||
# wide-rune round-trips, the c6 sep soak). Compiler/driver gates like
|
||||
# test/sep: they run under test-compiler.
|
||||
TOOL_WW_TESTS = test/tool/rejects_test.ww test/tool/driver_test.ww
|
||||
TOOL_WW_TESTS = test/tool/rejects_test.ww test/tool/driver_test.ww \
|
||||
test/tool/wwdump_test.ww
|
||||
TOOL_WW_TARGETS = $(TOOL_WW_TESTS:%=wwtest/%)
|
||||
BOOTSTRAP_WRAPPER_SOURCES = test/wcc/950_selfcheck.c \
|
||||
test/wcc/991_w6a_ww.c \
|
||||
|
||||
152
test/tool/wwdump_test.ww
Normal file
152
test/tool/wwdump_test.ww
Normal file
@@ -0,0 +1,152 @@
|
||||
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: import-bearing fixtures feed their RESOLVED sep unit
|
||||
// (`ww build -S` composes <stem>.sepwork/__root.unit.ww); 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 bav: []str = [testenv.driver("ww"), "build", "-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 -S failed)"));
|
||||
};
|
||||
src = strings.concat(stem, ".sepwork/__root.unit.ww");
|
||||
if (!testenv.exists(src)) {
|
||||
fail(classes[i], strings.concat(rels[i],
|
||||
": __root.unit.ww missing after ww build -S"));
|
||||
};
|
||||
};
|
||||
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_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);
|
||||
};
|
||||
@@ -1,241 +0,0 @@
|
||||
/*
|
||||
* 901_asserttyped_gap — ww-stage checker nil-type gap-corpus net.
|
||||
*
|
||||
* The ordinary suites drive the CSTAGE `ww` (cmd/ check.c), which has
|
||||
* no asserttyped pass, and nothing else feeds the ww-stage dumper's
|
||||
* checker (-c) — only -t/-a (tokens/ast) run elsewhere. So the
|
||||
* wwstage checker's asserttyped diagnostics (check.ww — the
|
||||
* #15 / A.6.2.1e post-checker nil-type invariant gate) are UNOBSERVED
|
||||
* by every other test, yet the gate is now ARMED (a non-exempt
|
||||
* nil-typed value node writes its diagnostic and os.exit(1)s). This
|
||||
* probe is the visible counterpart: it counts the per-file diagnostics
|
||||
* without gating on the bail's exit so a regression names its class.
|
||||
*
|
||||
* This probe runs the wwstage checker (wwdump_ww -c) over the
|
||||
* gap-bearing corpus, counts the per-file asserttyped diagnostics on
|
||||
* stderr, and pins each count against the manifest below. A fresh
|
||||
* nil-gap (count up) or a regressed fixed class (count up from 0) fails
|
||||
* loud; a fold that closes a class drives its count down, which fails
|
||||
* until the manifest is edited to match. Every class has reached
|
||||
* all-zero, so the manifest is now the all-closed floor that holds the
|
||||
* armed bail green.
|
||||
*
|
||||
* #90 sep-feed: the E3 flip retired the combined.ww amalgamator, so the
|
||||
* library/selfhost gap fixtures (A-D) feed their RESOLVED sep unit (see
|
||||
* resolveunit — `ww build -S` composes <stem>.sepwork/__root.unit.ww
|
||||
* from the dep `.wwi` stubs + the root body) rather than a pre-built
|
||||
* <stem>.combined.ww. The single-file, import-free test fixtures (E-G)
|
||||
* carry no imports and stay raw-fed.
|
||||
*
|
||||
* Gap classes (counts verified empirically at this revision):
|
||||
* A module-qual N_DOT call result checked_test 0 (closed)
|
||||
* B fn-ptr struct-field call smoke 0 (closed)
|
||||
* C abort intrinsic callee utf8 0 (exempt)
|
||||
* D module-leaf == type/fn name fnmatch 0 (closed)
|
||||
* D module-leaf == type/fn name random 0 (closed)
|
||||
* E computed enum-member value-expr enum_corpus 0 (closed)
|
||||
* F for-range tuple-destructure bind forrange_tuple 0 (closed)
|
||||
* G tuple multi-assign discard `_` massign_blank 0 (closed)
|
||||
*
|
||||
* Exit code of wwdump_ww is intentionally not gated: the diagnostics
|
||||
* land on stderr regardless of the run's success, and arming the bail
|
||||
* will itself flip that exit code — the stderr line count is the one
|
||||
* signal stable across the whole fold sequence.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *
|
||||
absbin(void)
|
||||
{
|
||||
const char *b = getenv("BIN");
|
||||
if (!b) b = "out/bin";
|
||||
if (b[0] == '/') return b;
|
||||
static char buf[2048];
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return NULL;
|
||||
snprintf(buf, sizeof buf, "%s/%s", cwd, b);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int
|
||||
slurp(const char *path, char **outbuf, size_t *outlen)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return -1;
|
||||
fseek(f, 0, SEEK_END);
|
||||
long n = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
if (n < 0) { fclose(f); return -1; }
|
||||
char *b = malloc((size_t)n + 1);
|
||||
if (!b) { fclose(f); return -1; }
|
||||
if (fread(b, 1, (size_t)n, f) != (size_t)n) { free(b); fclose(f); return -1; }
|
||||
b[n] = '\0';
|
||||
fclose(f);
|
||||
*outbuf = b;
|
||||
*outlen = (size_t)n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* resolveunit — compose <fixture>'s RESOLVED translation unit the way the
|
||||
* sep driver does. `ww build -S -o <owned-stem>` reads the fixture and its
|
||||
* sibling package sources in place while putting caller-owned compiler
|
||||
* artifacts below the private workspace, then this probe reads the exact
|
||||
* <stem>.sepwork/__root.unit.ww artifact —
|
||||
* the single self-contained unit w6c/wwdump consume for the root. Feeding the
|
||||
* raw module file instead would leave its import refs (os/fmt/strconv …)
|
||||
* unresolved, a partial unit the asserttyped invariant must never see.
|
||||
* Returns the unit path in `out`, 0 on success, and -1 otherwise. */
|
||||
static int
|
||||
resolveunit(const char *bin, const char *cwd, const char *fixture,
|
||||
const char *work, char *out, size_t outsz)
|
||||
{
|
||||
char cmd[4096], src[2048], stem[2048];
|
||||
snprintf(src, sizeof src, "%s/%s", cwd, fixture);
|
||||
snprintf(stem, sizeof stem, "%s/unit", work);
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 180 %s/ww build -S -o %s %s >/dev/null 2>&1",
|
||||
bin, stem, src);
|
||||
if (runwait(cmd) != 0) return -1;
|
||||
snprintf(out, outsz, "%s.sepwork/__root.unit.ww", stem);
|
||||
return access(out, 0) == 0 ? 0 : -1;
|
||||
}
|
||||
|
||||
/* Count line-leading "asserttyped:" diagnostics; each warn is one such
|
||||
* line and no other wwdump output carries the prefix. */
|
||||
static int
|
||||
count_asserttyped(const char *path)
|
||||
{
|
||||
char *b = NULL;
|
||||
size_t n = 0;
|
||||
if (slurp(path, &b, &n) < 0) return -1;
|
||||
const char *needle = "asserttyped:";
|
||||
size_t nl = strlen(needle);
|
||||
int c = 0;
|
||||
for (size_t i = 0; i + nl <= n; i++) {
|
||||
if ((i == 0 || b[i - 1] == '\n') && memcmp(b + i, needle, nl) == 0)
|
||||
c++;
|
||||
}
|
||||
free(b);
|
||||
return c;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = absbin();
|
||||
if (!bin) return 1;
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
|
||||
/* sep=1 fixtures feed their resolved sep unit (resolveunit); sep=0
|
||||
* fixtures are import-free single files fed raw. */
|
||||
struct { const char *rel; const char *cls; int want; int sep; } manifest[] = {
|
||||
{ "lib/math/checked/checked_test.ww",
|
||||
"A module-qual N_DOT call result", 0, 1 },
|
||||
{ "test/wcc/data/selfhost_smoke/case.ww",
|
||||
"B fn-ptr struct-field call", 0, 1 },
|
||||
{ "lib/encoding/utf8/utf8.ww",
|
||||
"C abort intrinsic callee", 0, 1 },
|
||||
{ "lib/fnmatch/fnmatch_test.ww",
|
||||
"D module-leaf == type/fn name", 0, 1 },
|
||||
{ "lib/math/random/random_test.ww",
|
||||
"D module-leaf == type/fn name", 0, 1 },
|
||||
{ "test/wcc/901_enum_corpus.ww",
|
||||
"E computed enum-member value-expr", 0, 0 },
|
||||
{ "test/wcc/901_forrange_tuple.ww",
|
||||
"F for-range tuple-destructure bind", 0, 0 },
|
||||
{ "test/wcc/901_massign_blank.ww",
|
||||
"G tuple multi-assign discard `_`", 0, 0 },
|
||||
{ NULL, NULL, 0, 0 },
|
||||
};
|
||||
|
||||
int fail = 0, n = 0;
|
||||
for (int i = 0; manifest[i].rel; i++) {
|
||||
char work[] = "/tmp/atgap_XXXXXX";
|
||||
char src[2048], errf[256], cmd[4096], unit[1280];
|
||||
char stem[256], scratch[288];
|
||||
int rowfail = 0;
|
||||
n++;
|
||||
if (mkdtemp(work) == NULL) {
|
||||
fprintf(stderr, "asserttyped_gap FAIL: %s [%s] "
|
||||
"workspace acquisition failed\n",
|
||||
manifest[i].rel, manifest[i].cls);
|
||||
fail++;
|
||||
continue;
|
||||
}
|
||||
snprintf(stem, sizeof stem, "%s/unit", work);
|
||||
snprintf(scratch, sizeof scratch, "%s.sepwork", stem);
|
||||
snprintf(errf, sizeof errf, "%s/asserttyped.err", work);
|
||||
if (manifest[i].sep) {
|
||||
if (resolveunit(bin, cwd, manifest[i].rel, work, unit,
|
||||
sizeof unit) != 0) {
|
||||
fprintf(stderr, "asserttyped_gap FAIL: %s [%s] "
|
||||
"no resolved sep unit\n",
|
||||
manifest[i].rel, manifest[i].cls);
|
||||
rowfail = 1;
|
||||
goto row_done;
|
||||
}
|
||||
snprintf(src, sizeof src, "%s", unit);
|
||||
} else {
|
||||
snprintf(src, sizeof src, "%s/%s", cwd, manifest[i].rel);
|
||||
}
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 180 %s/wwdump_ww -c %s >/dev/null 2>%s",
|
||||
bin, src, errf);
|
||||
runwait(cmd);
|
||||
int got = count_asserttyped(errf);
|
||||
if (got != manifest[i].want) {
|
||||
fprintf(stderr,
|
||||
"asserttyped_gap FAIL: %s [%s] expected %d, got %d\n",
|
||||
manifest[i].rel, manifest[i].cls,
|
||||
manifest[i].want, got);
|
||||
char dump[4096];
|
||||
snprintf(dump, sizeof dump,
|
||||
"grep '^asserttyped:' %s 1>&2", errf);
|
||||
runwait(dump);
|
||||
rowfail = 1;
|
||||
}
|
||||
|
||||
row_done:
|
||||
{
|
||||
int cleanfail = 0;
|
||||
if (unlink(errf) != 0 && errno != ENOENT) cleanfail = 1;
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", scratch);
|
||||
if (runwait(cmd) != 0) cleanfail = 1;
|
||||
if (unlink(stem) != 0 && errno != ENOENT) cleanfail = 1;
|
||||
if (rmdir(work) != 0) cleanfail = 1;
|
||||
if (cleanfail) {
|
||||
fprintf(stderr, "asserttyped_gap FAIL: %s [%s] "
|
||||
"workspace cleanup failed\n",
|
||||
manifest[i].rel, manifest[i].cls);
|
||||
rowfail = 1;
|
||||
}
|
||||
}
|
||||
if (rowfail) fail++;
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"asserttyped_gap: %d/%d fixture(s) drifted from manifest\n",
|
||||
fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("asserttyped_gap: ww-stage checker diagnostic set matches manifest "
|
||||
"on %d gap-corpus fixtures (A-G all closed; bail armed)\n", n);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* 989_wwdumpgate_run (#52, F15 c4) — wwdump_ww's `-c` and `-r` arms must gate
|
||||
* on parse-stage errors instead of silently emitting on a broken AST.
|
||||
*
|
||||
* THE BUG (wwstage wwdump_ww only, cat-A silent wrong output under rc=0): the
|
||||
* `-c` codegen arm ran checkfile+cgfile with NO parse-error gate — ps.errs
|
||||
* was never read — so a parse-errored decl was silently dropped from the AST
|
||||
* and the rest of the file compiled to asm with exit 0 (and `-c` IS the 994
|
||||
* byte-identity probe, so the gate tool itself could ship wrong asm silently).
|
||||
* The `-r` resolve report arm was likewise ungated. THE FIX: add the
|
||||
* `l.errs>0 || ps.errs>0` gate after parsefile in both arms, mirroring the
|
||||
* w6c compiler gate (selfhost/cmd/w6c/main.ww:162 / cmd/w6c/main.c).
|
||||
*
|
||||
* WWSTAGE-ONLY: the C wwdump (cstage) implements only -t/-a, not -c/-r — the
|
||||
* codegen/resolve dump arms are a wwstage-wwdump_ww feature, so there is no
|
||||
* cstage -c/-r twin to diff against; the gate reference is the w6c compiler.
|
||||
*
|
||||
* row | mode | input | result
|
||||
* -------+------+---------------+----------------------------------
|
||||
* bad_c | -c | parse error | rc != 0, zero asm bytes
|
||||
* bad_r | -r | parse error | rc != 0
|
||||
* ok_c | -c | valid file | rc == 0, asm emitted (control)
|
||||
*
|
||||
* bad_c / bad_r were RED pre-c4 (rc=0 with truncated asm / a resolve report).
|
||||
* ok_c pins the healthy emission path unperturbed.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const char *BAD =
|
||||
"package main;\n\n"
|
||||
"export fn main(argc: i32, argv: **u8) i32 = {\n\treturn 0;\n};\n\n"
|
||||
"fn broken( {\n";
|
||||
static const char *OK =
|
||||
"package main;\nexport fn main() i32 = { return 0; };\n";
|
||||
|
||||
/* Run `<tool> <mode> <src>` capturing asm on stdout; returns rc, sets
|
||||
* *asmbytes to the stdout byte count when non-NULL. */
|
||||
static int
|
||||
run_dump(const char *tool, const char *mode, const char *src, long *asmbytes)
|
||||
{
|
||||
char outp[128], cmd[1024];
|
||||
snprintf(outp, sizeof outp, "/tmp/wwdg_%d_out", getpid());
|
||||
snprintf(cmd, sizeof cmd, "%s %s %s > %s 2>/dev/null",
|
||||
tool, mode, src, outp);
|
||||
int rc = runwait(cmd);
|
||||
if (asmbytes) {
|
||||
FILE *f = fopen(outp, "rb");
|
||||
long n = 0;
|
||||
if (f) { fseek(f, 0, SEEK_END); n = ftell(f); fclose(f); }
|
||||
*asmbytes = n;
|
||||
}
|
||||
unlink(outp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
write_file(const char *path, const char *body)
|
||||
{
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(body, f);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char wtool[1024];
|
||||
snprintf(wtool, sizeof wtool, "%s/wwdump_ww", bin);
|
||||
if (access(wtool, X_OK) != 0) {
|
||||
/* wwdump_ww is the unit under test; absence is a wiring fault. */
|
||||
fprintf(stderr, "wwdumpgate: missing %s\n", wtool);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char badp[64], okp[64];
|
||||
snprintf(badp, sizeof badp, "/tmp/wwdg_bad_%d.ww", getpid());
|
||||
snprintf(okp, sizeof okp, "/tmp/wwdg_ok_%d.ww", getpid());
|
||||
if (write_file(badp, BAD) || write_file(okp, OK)) return 1;
|
||||
|
||||
int fail = 0, total = 0;
|
||||
long ab;
|
||||
|
||||
/* bad_c: loud, zero asm */
|
||||
total++;
|
||||
if (run_dump(wtool, "-c", badp, &ab) == 0 || ab != 0) {
|
||||
fprintf(stderr, "wwdumpgate[bad_c]: emitted asm with rc=0 on "
|
||||
"parse-errored input (#52); asm=%ld\n", ab);
|
||||
fail++;
|
||||
}
|
||||
|
||||
/* bad_r: loud */
|
||||
total++;
|
||||
if (run_dump(wtool, "-r", badp, NULL) == 0) {
|
||||
fprintf(stderr, "wwdumpgate[bad_r]: resolve report with rc=0 on "
|
||||
"parse-errored input (#52)\n");
|
||||
fail++;
|
||||
}
|
||||
|
||||
/* ok_c control: rc=0 with asm */
|
||||
total++;
|
||||
if (run_dump(wtool, "-c", okp, &ab) != 0 || ab <= 0) {
|
||||
fprintf(stderr, "wwdumpgate[ok_c]: healthy file rejected/no asm "
|
||||
"(asm=%ld)\n", ab);
|
||||
fail++;
|
||||
}
|
||||
|
||||
unlink(badp); unlink(okp);
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "wwdumpgate_run: %d/%d check(s) failed\n", fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("wwdumpgate_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user