test/wcc: carrier ownership repair and driver-contract adaptation

Every surviving carrier now owns its artifacts: checked mkdir/mkdtemp/
fopen acquisition, one all-exit cleanup funnel per carrier, ENOENT-
tolerant checked unlinks, exact-path deletion (rm -rf only for an
owned pid-keyed dir or a .sepwork beneath one), and cleanup failure
fails a passing carrier without overwriting its diagnostic. In the
same pass the carriers adapt to the driver contract this branch lands:
--sep and WW_PKGCACHE are gone, -S and the /tmp/ww_run_<pid> scratch
contract are asserted, and rows whose runtime or reject coverage moved
to test/wcc/data fixtures or test/lang @test owners are trimmed to the
byte/artifact/diagnostic observations only they can make.

Repair and adaptation ride together because most files interleave both
in the same hunks; splitting would manufacture intermediate carrier
states that never existed and cannot run against either driver.
This commit is contained in:
2026-08-07 23:02:47 +09:00
parent b2899dd8d3
commit a95a7a316b
132 changed files with 7573 additions and 6172 deletions

View File

@@ -21,11 +21,11 @@
* (mod_collect / mod_lookup_for_fn) stays dormant-but-load-bearing for
* genuinely package-less `//ww:module-reset` deps until #24b/#26.
*
* Asserts (combined `ww build`, BOTH driver stages; `--sep` reproduces
* identically):
* Asserts (default separate compilation, both driver stages):
* 1. Build + run, BOTH stages → exit 9 (the user's `main.run`, return 9),
* never aa.run (return 5).
* 2. cs==ww (rule 10): the combined `.s` is byte-identical between the
* 2. cs==ww (rule 10): the concatenated per-package `.s` is byte-identical
* between the
* two driver stages.
* 3. DISTINCTNESS: the `.s` has EXACTLY ONE `TEXT main.run` (the user's)
* AND EXACTLY ONE `TEXT aa.run` (the import) — distinct symbols, no dup.
@@ -36,6 +36,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
@@ -149,24 +150,38 @@ main(void)
const char *bin = absbin();
if (!bin) { fprintf(stderr, "84 FAIL: getcwd\n"); return 1; }
char td[64], cmd[8192];
int fail = 0;
snprintf(td, sizeof td, "/tmp/wwbare84_%d", getpid());
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd);
mkdir(td, 0755);
char td[] = "/tmp/wwbare84_XXXXXX";
char cmd[8192];
int fail = 0, aaowned = 0, aafile = 0, rootfile = 0;
int stage_started[2] = { 0, 0 };
if (mkdtemp(td) == NULL) {
perror("wwbare84: mkdtemp");
return 1;
}
/* All paths derive from `td` (a small fixed 64-byte buffer) so the
* snprintfs are provably non-truncating (warning-clean). */
char aadir[1024], aaww[1024], rootww[1024];
snprintf(aadir, sizeof aadir, "%s/aa", td);
mkdir(aadir, 0755);
snprintf(aaww, sizeof aaww, "%s/aa/aa.ww", td);
snprintf(rootww, sizeof rootww, "%s/root.ww", td);
if (write_file(aaww, aa_src) || write_file(rootww, root_src)) {
if (mkdir(aadir, 0755) != 0) {
perror(aadir);
fail++;
goto out;
}
aaowned = 1;
if (write_file(aaww, aa_src) != 0) {
fprintf(stderr, "84 FAIL: write fixture\n");
fail++;
goto out;
}
aafile = 1;
if (write_file(rootww, root_src) != 0) {
fprintf(stderr, "84 FAIL: write fixture\n");
fail++;
goto out;
}
rootfile = 1;
struct { const char *drv, *tag; char prog[1024], sfile[1024]; }
stg[] = { { "ww", "cs", {0}, {0} }, { "ww_ww", "ww", {0}, {0} } };
@@ -174,18 +189,20 @@ main(void)
for (int s = 0; s < 2; s++) {
snprintf(stg[s].prog, sizeof stg[s].prog, "%s/prog.%s", td, stg[s].tag);
snprintf(stg[s].sfile, sizeof stg[s].sfile, "%s/prog.%s.s", td, stg[s].tag);
/* #93 sep layout: `--sep -o <prog>` splits the asm across
/* #93 sep layout: `-o <prog>` splits the asm across
* <prog>.sepwork/<pkg>.s — the bare user `TEXT run,` lands in
* __root.s and the imported `TEXT aa.run,` in aa.s. Concat all
* the per-unit .s (sorted glob order is deterministic) into the
* __root.s and the imported `TEXT aa.run,` in aa.s. Concat those
* exact per-unit .s files in a fixed order into the
* flat <prog>.s the label-count + byte-id checks consume. The
* `-I %s` source path is preserved; WW_PKGCACHE is pinned under
* the tmpdir so the shared out/.pkgcache stays untouched. */
* `-I %s` source path is preserved, and all outputs stay under
* the tmpdir. */
snprintf(cmd, sizeof cmd,
"WW_PKGCACHE=%s/pkgc timeout 240 %s/%s build --sep -o %s "
"-I %s %s >/dev/null 2>&1 && cat %s.sepwork/*.s > %s",
td, bin, stg[s].drv, stg[s].prog, td, rootww,
stg[s].prog, stg[s].sfile);
"timeout 240 %s/%s build -o %s "
"-I %s %s >/dev/null 2>&1 && "
"cat %s.sepwork/__root.s %s.sepwork/aa.s > %s",
bin, stg[s].drv, stg[s].prog, td, rootww,
stg[s].prog, stg[s].prog, stg[s].sfile);
stage_started[s] = 1;
if (runwait(cmd) != 0) {
fprintf(stderr, "84 FAIL: %s build\n", stg[s].drv);
fail++;
@@ -210,15 +227,36 @@ main(void)
}
}
/* (2) cs==ww (rule 10): the combined .s is byte-identical. */
/* (2) cs==ww (rule 10): concatenated per-package .s is byte-identical. */
if (files_eq(stg[0].sfile, stg[1].sfile) != 0) {
fprintf(stderr, "84 FAIL: cs .s != ww .s (rule 10)\n");
fail++;
}
out:
snprintf(cmd, sizeof cmd, "rm -rf %s", td);
runwait(cmd);
{
int cleanfail = 0;
char path[1024];
const char *tags[] = { "cs", "ww" };
for (int i = 0; i < 2; i++) {
if (!stage_started[i]) continue;
snprintf(path, sizeof path, "%s/prog.%s.sepwork", td, tags[i]);
snprintf(cmd, sizeof cmd, "rm -rf %s", path);
if (runwait(cmd) != 0) cleanfail = 1;
snprintf(path, sizeof path, "%s/prog.%s", td, tags[i]);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
snprintf(path, sizeof path, "%s/prog.%s.s", td, tags[i]);
if (unlink(path) != 0 && errno != ENOENT) cleanfail = 1;
}
if (rootfile && unlink(rootww) != 0 && errno != ENOENT) cleanfail = 1;
if (aafile && unlink(aaww) != 0 && errno != ENOENT) cleanfail = 1;
if (aaowned && rmdir(aadir) != 0) cleanfail = 1;
if (rmdir(td) != 0) cleanfail = 1;
if (cleanfail) {
fprintf(stderr, "84 FAIL: temporary cleanup failed\n");
fail++;
}
}
if (fail) {
fprintf(stderr, "84: %d check(s) failed\n", fail);
return 1;