/* * 989_coloimport_sep — #98 E3 flip-blocker: a co-located `_test` entry must * not shadow the dir-package it imports under `ww test --sep`. * * The driver builds the sep searchpath srcd-first (srcd = the entry file's * own directory). When the entry is a co-located black-box test * `lib//test.ww`, srcd = `lib/`, so a transitive `import * ` (reached from another package in the closure) hits the sibling * FILE `lib//.ww` (is_dir=0) FIRST — treated as an intra-package * split, folded INLINE into the importer's unit under that importer's * module-reset → w6c rejects `package does not match import path * `. locate_import / locateimport now walk the searchpath in * TWO passes — pass 1 seeks a DIRECTORY on any entry, pass 2 (only if no * directory matches anywhere) seeks a FILE — so the dir-package `/` * on a later entry beats the same-named sibling FILE on the srcd entry, * while a genuine leaf package with no directory still resolves in the * file pass. This gate stands for the 7 lib-run tests * (904/966/967/972/974/976/977) the bug blocked under sep. * * Fixture (test/wcc/data/colo98/, the exact srcd-shadow shape, minimised): * widget/widget.ww package widget — the sibling primary file * that shadows the dir. * widget/widgettest.ww package widget_test, import gadget — the * co-located test ENTRY; * srcd = .../widget. * gadget/gadget.ww package gadget, import widget — the NON-root * importer compiled with -I, * whose self-named `import * widget` triggers the fold. * `-I test/wcc/data/colo98` puts widget's parent on the fallback path, * mirroring how `lib` holds the real `lib//` dirs. * * Asserts (both driver stages, COLD per-stage WW_PKGCACHE): * 1. RUN-EXIT 0 — the @test resolves, builds, links, runs. Pre-fix the * non-root importer's fold makes w6c reject → non-zero; this is the * non-vacuity teeth (verified: revert the two-pass walker → reddens * with `package widget does not match import path gadget`). * 2. REGISTERED — widget.unit.ww + widget.wwi appear in .sepwork: the * import resolved to the DIR-package, not the sibling file. Pre-fix * widget is never registered (folds into the importer), so both are * absent — the "right module tag" signal. * 3. cs==ww (rule 10) — per-package .s/.wwi byte-identical across the * cstage `ww` and wwstage `ww_ww` sep-drivers, proving the `locatein` * port is symmetric with `locate_import_in`. * * Light wwstage-driver test (CLAUDE.md rule 14): every intermediate is * `-o`-redirected to /tmp, so it is phase-1 parallel-safe. Models * 989_septest_run conventions; 989 prefix per the sep-gate precedent. */ #include #include #include #include #include #include #include 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; } static int files_eq(const char *a, const char *b) { char *ba = NULL, *bb = NULL; size_t na = 0, nb = 0; if (slurp(a, &ba, &na) < 0 || slurp(b, &bb, &nb) < 0) { free(ba); free(bb); return -1; } int eq = (na == nb && memcmp(ba, bb, na) == 0); free(ba); free(bb); return eq ? 0 : 1; } /* cs==ww over a sep build's per-package output: every .s/.wwi the cstage * driver produced in `csdir` must be byte-identical to the wwstage * driver's same-named file in `wwdir`. Returns the count of mismatches. */ static int cmp_sepwork(const char *csdir, const char *wwdir, const char *label) { DIR *d = opendir(csdir); if (!d) { fprintf(stderr, "coloimport FAIL: %s — no cs sepwork %s\n", label, csdir); return 1; } int bad = 0, seen = 0; struct dirent *ent; while ((ent = readdir(d)) != NULL) { const char *nm = ent->d_name; size_t nl = strlen(nm); int is_s = (nl > 2 && strcmp(nm + nl - 2, ".s") == 0); int is_wwi = (nl > 4 && strcmp(nm + nl - 4, ".wwi") == 0); if (!is_s && !is_wwi) continue; seen++; char a[2048], b[2048]; snprintf(a, sizeof a, "%s/%s", csdir, nm); snprintf(b, sizeof b, "%s/%s", wwdir, nm); if (files_eq(a, b) != 0) { fprintf(stderr, "coloimport FAIL: %s — cs!=ww for %s (rule 10)\n", label, nm); bad++; } } closedir(d); if (seen == 0) { fprintf(stderr, "coloimport FAIL: %s — no .s/.wwi in %s\n", label, csdir); bad++; } return bad; } struct tcase { const char *label; const char *entry; /* the co-located `_test` entry built under --sep */ const char *incdir; /* -I dir (parent that holds the dir-package) */ const char *regpkg; /* the dir-package that must register its own unit */ }; static struct tcase cases[] = { /* the transitive self-named-import shadow — the exact lib-run shape. */ { "widget", "test/wcc/data/colo98/widget/widgettest.ww", "test/wcc/data/colo98", "widget" }, { NULL, NULL, NULL, NULL }, }; int main(void) { const char *bin = absbin(); if (!bin) return 1; char td[64], cmd[8192]; int fail = 0; snprintf(td, sizeof td, "/tmp/wwcoloimport_%d", getpid()); snprintf(cmd, sizeof cmd, "rm -rf %s", td); runwait(cmd); mkdir(td, 0755); for (int i = 0; cases[i].label; i++) { struct tcase *t = &cases[i]; struct { const char *drv, *tag; int rc; } stg[] = { { "ww", "cs", -1 }, { "ww_ww", "ww", -1 } }; for (int s = 0; s < 2; s++) { char prog[1024]; snprintf(prog, sizeof prog, "%s/%s.%s.bin", td, t->label, stg[s].tag); /* COLD per-(case,stage) cache: every package compiles fresh so * the per-pkg .s/.wwi this gate inspects are produced. */ snprintf(cmd, sizeof cmd, "WW_PKGCACHE='%s/cache.%s.%s' %s/%s test --sep -I %s -o %s %s " ">/dev/null 2>&1", td, t->label, stg[s].tag, bin, stg[s].drv, t->incdir, prog, t->entry); stg[s].rc = runwait(cmd); } /* 1. RUN-EXIT 0 on BOTH stages (pre-fix: w6c rejects the importer). */ if (stg[0].rc != 0 || stg[1].rc != 0) { fprintf(stderr, "coloimport FAIL: %s exits cs=%d ww=%d (expected 0)\n", t->label, stg[0].rc, stg[1].rc); fail++; continue; } char csdir[1024], wwdir[1024]; snprintf(csdir, sizeof csdir, "%s/%s.cs.bin.sepwork", td, t->label); snprintf(wwdir, sizeof wwdir, "%s/%s.ww.bin.sepwork", td, t->label); /* 2. REGISTERED: the dir-package owns its unit + interface (pre-fix * it folds into the importer and these are absent). */ char u[2048], w[2048]; snprintf(u, sizeof u, "%s/%s.unit.ww", csdir, t->regpkg); snprintf(w, sizeof w, "%s/%s.wwi", csdir, t->regpkg); if (access(u, 0) != 0) { fprintf(stderr, "coloimport FAIL: %s — %s missing (import folded " "to sibling file, not the dir-package)\n", t->label, u); fail++; } if (access(w, 0) != 0) { fprintf(stderr, "coloimport FAIL: %s — %s missing\n", t->label, w); fail++; } /* 3. cs==ww (rule 10): per-package .s/.wwi byte-identical. */ fail += cmp_sepwork(csdir, wwdir, t->label); } snprintf(cmd, sizeof cmd, "rm -rf %s", td); runwait(cmd); if (fail) { fprintf(stderr, "coloimport: %d check(s) failed\n", fail); return 1; } printf("coloimport: co-located `_test` entry resolves `import ` to the " "dir-package (not the sibling file) under `ww test --sep` on both driver " "stages + cs==ww per-pkg .s/.wwi\n"); return 0; }