test: ww-stage asserttyped gap-corpus probe (901)
The wwstage checker's asserttyped pass currently only WARNS on nil-typed nodes (a check-bail-discipline regression). Before re-arming it to a bail, this probe pins the warn set so the re-arm is verifiable — the live ww-driver suite is blind to it (the stdlib _run tests use the cstage ww driver, no asserttyped; 990 feeds only -t/-a). 901 runs the ww-stage checker (wwdump_ww -c) over the gap-bearing combined.ww fixtures and asserts the asserttyped warn count per fixture against a manifest: checked 0 (closed-root sentinel), smoke 3 (fn-ptr field call), utf8 8 (abort intrinsic), fnmatch 2 + random 16 (module-leaf==type/fn collision). Each subsequent stamping fold drives a count to 0 and edits its manifest line; the bail is safe to arm when all reach 0. A new nil-gap or a regressed class fails loud (mutation-tested both directions). Test-infra only — no compiler change.
This commit is contained in:
6
Makefile
6
Makefile
@@ -365,7 +365,8 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_opaque_guards \
|
||||
$(BIN)/test_opaque_assign_cast_run \
|
||||
$(BIN)/test_sort_run \
|
||||
$(BIN)/test_bufio_run $(BIN)/test_random_run
|
||||
$(BIN)/test_bufio_run $(BIN)/test_random_run \
|
||||
$(BIN)/test_asserttyped_gap
|
||||
|
||||
$(BIN)/test_smoke: test/wcc/000_smoke.c $(LIB)/libwcc.a | $(BIN)
|
||||
$(CC) $(CFLAGS) $(INCS) -o $@ $< -L$(LIB) -lwcc
|
||||
@@ -992,6 +993,9 @@ $(BIN)/test_self_rebuild: test/wcc/995_self_rebuild.c $(BIN)/ww_ww \
|
||||
$(BIN)/test_selfcheck: test/wcc/950_selfcheck.c $(BIN)/wwdump_ww | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_asserttyped_gap: test/wcc/901_asserttyped_gap.c $(BIN)/wwdump_ww | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_dyn_ww: test/wcc/996_dyn_ww.c $(BIN)/ww $(BIN)/w6l $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
156
test/wcc/901_asserttyped_gap.c
Normal file
156
test/wcc/901_asserttyped_gap.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 901_asserttyped_gap — ww-stage checker nil-type gap-corpus net.
|
||||
*
|
||||
* The live `make test` stdlib _run suite drives the CSTAGE `ww`
|
||||
* (cmd/ check.c), which has no asserttyped pass; 990_selfhost feeds
|
||||
* the ww-stage dumper only -t/-a (tokens/ast), never the checker. So
|
||||
* the wwstage checker's asserttyped diagnostics (check.ww:3442 — the
|
||||
* #15 / A.6.2.1e post-checker nil-type invariant gate, currently a
|
||||
* soft warn) are UNOBSERVED by every other test. Re-arming asserttyped
|
||||
* to bail would ship green while the known nil-stamp gaps stayed
|
||||
* latent (the bootstrap-coverage trap).
|
||||
*
|
||||
* This probe runs the wwstage checker (wwdump_ww -c) over the
|
||||
* gap-bearing combined.ww 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 — i.e. the
|
||||
* manifest is the expected-fail list that shrinks per fold and reaches
|
||||
* all-zero exactly when the bail is safe to arm.
|
||||
*
|
||||
* 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 3
|
||||
* C abort intrinsic callee utf8 8
|
||||
* D module-leaf == type/fn name fnmatch 2
|
||||
* D module-leaf == type/fn name random 16
|
||||
*
|
||||
* 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 <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 *
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
struct { const char *rel; const char *cls; int want; } manifest[] = {
|
||||
{ "lib/math/checked/checked_test.combined.ww",
|
||||
"A module-qual N_DOT call result", 0 },
|
||||
{ "selfhost/test/smoke.combined.ww",
|
||||
"B fn-ptr struct-field call", 3 },
|
||||
{ "lib/encoding/utf8/utf8.combined.ww",
|
||||
"C abort intrinsic callee", 8 },
|
||||
{ "lib/fnmatch/fnmatchtest.combined.ww",
|
||||
"D module-leaf == type/fn name", 2 },
|
||||
{ "lib/math/random/random_test.combined.ww",
|
||||
"D module-leaf == type/fn name", 16 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int fail = 0, n = 0;
|
||||
for (int i = 0; manifest[i].rel; i++) {
|
||||
char src[2048], errf[64], cmd[4096];
|
||||
snprintf(src, sizeof src, "%s/%s", cwd, manifest[i].rel);
|
||||
snprintf(errf, sizeof errf, "/tmp/atgap_%d_%d.err", getpid(), i);
|
||||
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);
|
||||
n++;
|
||||
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);
|
||||
fail++;
|
||||
}
|
||||
unlink(errf);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"asserttyped_gap: %d/%d fixture(s) drifted from manifest\n",
|
||||
fail, n);
|
||||
return 1;
|
||||
}
|
||||
printf("asserttyped_gap: ww-stage checker warn set matches manifest "
|
||||
"on %d gap-corpus fixtures (B+C+D pinned, A closed)\n", n);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user