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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user