/* * 910_at_test — runs @test fn blocks in a source file by * generating a synthetic main() that calls each one. A test * passes iff the generated binary exits 0; if any @test calls * abort/exit(1), the run fails. * * Scanning is regex-free, matching the literal sequence * `\n@test\s+fn\s+(IDENT)`. Same parser shape as ww accepts. */ #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 int isws(int c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; } static int isident(int c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } /* find_attest — scan src for `@test\s+fn\s+(ident)`; on a match, * write the identifier into `out` (NUL-terminated) and return the * scan position past the matched ident. Returns -1 when no more. */ static long find_attest(const char *src, long pos, char *out, size_t outsz) { long n = (long)strlen(src); while (pos < n) { const char *q = strstr(src + pos, "@test"); if (q == NULL) return -1; long off = q - src; /* must be a top-level marker: previous non-space char is * '\n' or we're at the file start. */ long pre = off - 1; while (pre >= 0 && (src[pre] == ' ' || src[pre] == '\t')) pre--; if (pre >= 0 && src[pre] != '\n') { pos = off + 1; continue; } long c = off + 5; /* past "@test" */ while (c < n && isws(src[c])) c++; if (c + 2 > n || strncmp(src + c, "fn", 2) != 0) { pos = off + 1; continue; } c += 2; if (c < n && isident(src[c])) { pos = off + 1; continue; } while (c < n && isws(src[c])) c++; long ids = c; while (c < n && isident(src[c])) c++; if (c == ids) { pos = off + 1; continue; } size_t len = (size_t)(c - ids); if (len + 1 > outsz) return -1; memcpy(out, src + ids, len); out[len] = '\0'; return c; } return -1; } static char * slurp(const char *path) { FILE *f = fopen(path, "rb"); if (!f) return NULL; fseek(f, 0, SEEK_END); long n = ftell(f); fseek(f, 0, SEEK_SET); char *b = malloc((size_t)n + 1); if (!b) { fclose(f); return NULL; } if (fread(b, 1, (size_t)n, f) != (size_t)n) { free(b); fclose(f); return NULL; } b[n] = '\0'; fclose(f); return b; } static int runtests(const char *path, const char *bin) { char *src = slurp(path); if (!src) { fprintf(stderr, "910 FAIL: cannot read %s\n", path); return -1; } /* Build a synthetic test driver: original source + a main() * that calls each @test fn. Each test that runs to completion * (no abort/exit) counts as a pass. */ char tmpsrc[256]; snprintf(tmpsrc, sizeof tmpsrc, "/tmp/wwd_attest_%d.ww", getpid()); FILE *out = fopen(tmpsrc, "wb"); if (!out) { free(src); return -1; } fputs(src, out); fputs("\nexport fn main() i32 = {\n", out); long pos = 0; int count = 0; for (;;) { char name[128]; long next = find_attest(src, pos, name, sizeof name); if (next < 0) break; fprintf(out, "\t%s();\n", name); pos = next; count++; } fputs("\treturn 0;\n};\n", out); fclose(out); free(src); if (count == 0) { fprintf(stderr, "910 FAIL: no @test fns found in %s\n", path); unlink(tmpsrc); return -1; } char cmd[2048]; snprintf(cmd, sizeof cmd, "%s/ww run %s 2>/dev/null", bin, tmpsrc); int rc = runwait(cmd); unlink(tmpsrc); if (rc != 0) { fprintf(stderr, "910 FAIL: %s: %d @test fn(s), driver exited %d\n", path, count, rc); return -1; } return count; } int main(void) { const char *bin = getenv("BIN"); if (!bin) bin = "out/bin"; const char *files[] = { "test/wcc/data/attest_pass.ww", NULL, }; int total = 0; for (int i = 0; files[i]; i++) { int n = runtests(files[i], bin); if (n < 0) return 1; total += n; } printf("@test: %d test(s) ran ok\n", total); return 0; }