diff --git a/Makefile b/Makefile index 39ade31e..fc7c4c3f 100644 --- a/Makefile +++ b/Makefile @@ -421,7 +421,7 @@ MISC_WW_TARGETS = $(MISC_WW_TESTS:%=wwtest/%) # test/sep: they run under test-compiler. TOOL_WW_TESTS = test/tool/rejects_test.ww test/tool/driver_test.ww \ test/tool/wwdump_test.ww test/tool/ffi_test.ww \ - test/tool/attest_test.ww + test/tool/attest_test.ww test/tool/wwirune_test.ww TOOL_WW_TARGETS = $(TOOL_WW_TESTS:%=wwtest/%) BOOTSTRAP_WRAPPER_SOURCES = test/wcc/950_selfcheck.c \ test/wcc/991_w6a_ww.c \ diff --git a/test/tool/wwirune_test.ww b/test/tool/wwirune_test.ww new file mode 100644 index 00000000..3f2fc930 --- /dev/null +++ b/test/tool/wwirune_test.ww @@ -0,0 +1,104 @@ +package wwirune_test; + +// .wwi round-trip observer for \u / \U Unicode escapes in exported +// rune defs. Port of the retired native carrier +// test/wcc/110_uniesc_run.c; every assertion preserved, the .wwi +// inspection added (the audit's optional strengthen). +// +// An exported wide-rune `def` re-serialized by the .wwi producer must +// re-parse into the same codepoint on the importer side (the +// write-twin of the lexer read-fix — cmd/w6c/wwi.c wwi_rune / +// selfhost/cmd/wcc/wwi.ww wwirune emit \u/\U above 0xFF). One def per +// serializer branch: \x (<= 0xFF), \u (<= 0xFFFF), \U. The claim +// REQUIRES a real multi-file module tree: a single-file two-package +// fixture builds through __root.unit.ww and never produces a wr.wwi +// (probed in residual-carrier-audit.json, 110 entry), so no corpus +// fixture can exercise the serializer. +// +// Both driver stages run and their wr.wwi must be byte-identical +// (rule 10); the serialized escape text itself is pinned per branch. +// Dropped C machinery, not assertions: the ww_ww-absent skip gate +// (the Make target declares both drivers) and the unlink/rmdir +// accounting (testenv.clean asserts the removal). + +import os; +import os.exec; +import strings; +import testenv; +import time; + +fn fail(label: str, why: str) void = { + let m: str = strings.concat("wwirune FAIL: ", label, " -- ", why, + "\n"); + os.write(2, m.ptr, m.len: u64); + assert(false); +}; + +fn tmo() time.duration = { + return (240i64 * (time.second: i64)): time.duration; +}; + +fn runcode(dir: str, name: str, argv: []str) i32 = { + let co: testenv.commandout; + testenv.runcommand(dir, dir, name, argv, tmo(), &co); + if (co.termination != exec.termination.EXIT) { return -1; }; + return co.code; +}; + +@test fn roundtrip() void = { + let drvs: []str = ["ww", "ww_ww"]; + let wwis: []str = ["", ""]; + let d: i32 = 0; + for (d < 2) { + let td: str = testenv.fresh(); + assert(os.mkdir(strings.concat(td, "/wr"), 493) == 0); + testenv.writefile(strings.concat(td, "/wr/wr.ww"), strings.concat( + "package wr;\n", + "export def EACUTE: rune = '\\u00e9';\n", + "export def EURO: rune = '\\u20ac';\n", + "export def SMILEY: rune = '\\U0001F600';\n")); + testenv.writefile(strings.concat(td, "/main.ww"), strings.concat( + "package main;\n", + "import wr;\n", + "fn main() i32 = {\n", + " let a: rune = wr.EACUTE;\n", + " let b: rune = wr.EURO;\n", + " let c: rune = wr.SMILEY;\n", + " if ((a: u32) != 233u32) { return 1; };\n", + " if ((b: u32) != 8364u32) { return 2; };\n", + " if ((c: u32) != 128512u32) { return 3; };\n", + " return 42;\n", + "};\n")); + let av: []str = [testenv.driver(drvs[d]), "build", "main.ww"]; + if (runcode(td, strings.concat("build_", drvs[d]), av) != 0) { + fail("roundtrip", strings.concat("build via ", drvs[d], + " failed")); + }; + let rav: []str = [strings.concat(td, "/main")]; + if (runcode(td, strings.concat("run_", drvs[d]), rav) != 42) { + fail("roundtrip", strings.concat(drvs[d], " built program ", + "exit != 42 (a wide-rune def re-parsed wrong)")); + }; + wwis[d] = testenv.readfile(strings.concat(td, + "/main.sepwork/wr.wwi")); + testenv.clean(td); + d += 1; + }; + + // one needle per serializer branch, exact re-serialized text + let needles: []str = [ + "export def EACUTE: rune = '\\xe9';\n", + "export def EURO: rune = '\\u20ac';\n", + "export def SMILEY: rune = '\\U0001f600';\n"]; + let i: i32 = 0; + for (i < needles.len) { + if (!testenv.has(wwis[0], needles[i])) { + fail("roundtrip", strings.concat("wr.wwi lacks `", needles[i], + "` (serializer branch drifted)")); + }; + i += 1; + }; + if (!testenv.same(wwis[0], wwis[1])) { + fail("roundtrip", "cs wr.wwi != ww wr.wwi (rule 10)"); + }; +}; diff --git a/test/wcc/110_uniesc_run.c b/test/wcc/110_uniesc_run.c deleted file mode 100644 index 814d803a..00000000 --- a/test/wcc/110_uniesc_run.c +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 110_uniesc_run — .wwi round-trip carrier for \u / \U Unicode escapes. - * - * The value-row half (rune/string \u \U \x decode asserts) migrated to the - * in-language row-table test/lang/uniesc_test.ww (fold-6, task #4); this - * file retains ONLY the .wwi round-trip case, which is a two-package - * sep-build observer with no in-language @test home (it asserts the - * PRODUCER re-serializes a wide-rune `def` byte-exact, observed across a - * package boundary — not expressible as a single-compile @test). - * - * An exported wide-rune `def` re-serialized by the producer must re-parse - * into the same codepoint (the write-twin of the lexer read-fix — - * cmd/w6c/wwi.c wwi_rune / selfhost/cmd/wcc/wwi.ww wwirune emit \u/\U above - * 0xFF). Runs through both the cstage `ww` and wwstage `ww_ww` drivers - * (task #50); the wwstage arm is skipped when ww_ww is absent (a - * cstage-only build), mirroring 640_int_cast_signed. - */ -#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; -} - -/* run_roundtrip — sep-build a two-package program where the imported - * package exports wide-rune defs; the producer must serialize them as - * \u/\U so the importer re-parses the same codepoint. Returns the built - * program's exit code (42 on a correct round-trip). */ -static int -run_roundtrip(const char *driver, int id) -{ - char dir[80], wrdir[112], wrp[176], mainp[160], cmd[1024]; - char outbin[160], scratch[176]; - snprintf(dir, sizeof dir, "/tmp/wwue_rt_%d_%d", getpid(), id); - snprintf(wrdir, sizeof wrdir, "%s/wr", dir); - snprintf(wrp, sizeof wrp, "%s/wr.ww", wrdir); - snprintf(mainp, sizeof mainp, "%s/main.ww", dir); - snprintf(outbin, sizeof outbin, "%s/main", dir); - snprintf(scratch, sizeof scratch, "%s/main.sepwork", dir); - - if (mkdir(dir, 0755) != 0) { - perror(dir); - return -1; - } - int got = -1; - int wr_owned = 0; - if (mkdir(wrdir, 0755) != 0) { - perror(wrdir); - goto cleanup; - } - wr_owned = 1; - - FILE *f = fopen(wrp, "wb"); - if (!f) { - perror(wrp); - goto cleanup; - } - /* one def per serializer branch: \x (<=0xFF), \u (<=0xFFFF), \U. */ - fputs("package wr;\n" - "export def EACUTE: rune = '\\u00e9';\n" - "export def EURO: rune = '\\u20ac';\n" - "export def SMILEY: rune = '\\U0001F600';\n", f); - if (fclose(f) != 0) { - perror(wrp); - goto cleanup; - } - f = fopen(mainp, "wb"); - if (!f) { - perror(mainp); - goto cleanup; - } - fputs("package main;\n" - "import wr;\n" - "fn main() i32 = {\n" - " let a: rune = wr.EACUTE;\n" - " let b: rune = wr.EURO;\n" - " let c: rune = wr.SMILEY;\n" - " if ((a: u32) != 233u32) { return 1; };\n" - " if ((b: u32) != 8364u32) { return 2; };\n" - " if ((c: u32) != 128512u32) { return 3; };\n" - " return 42;\n" - "};\n", f); - if (fclose(f) != 0) { - perror(mainp); - goto cleanup; - } - - snprintf(cmd, sizeof cmd, "cd %s && %s build main.ww", dir, driver); - if (runwait(cmd) != 0) { - fprintf(stderr, "roundtrip: build via %s failed\n", driver); - goto cleanup; - } - got = runwait(outbin); - -cleanup: - /* `ww build` retains this exact caller-owned scratch tree. */ - snprintf(cmd, sizeof cmd, "rm -rf %s", scratch); - int cleanfail = runwait(cmd) != 0; - if (unlink(outbin) != 0 && errno != ENOENT) { - perror(outbin); - cleanfail = 1; - } - if (unlink(mainp) != 0 && errno != ENOENT) { - perror(mainp); - cleanfail = 1; - } - if (wr_owned) { - if (unlink(wrp) != 0 && errno != ENOENT) { - perror(wrp); - cleanfail = 1; - } - if (rmdir(wrdir) != 0) { - perror(wrdir); - cleanfail = 1; - } - } - if (rmdir(dir) != 0) { - perror(dir); - cleanfail = 1; - } - if (cleanfail && got == 42) - got = -1; - return got; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[1024]; - if (bin[0] != '/') { - char cwd[1024]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - char wdrv[1024]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - drivers[] = { - { "cstage", cdrv, 0 }, - { "wwstage", wdrv, 1 }, - { NULL, NULL, 0 }, - }; - - int total = 0, fail = 0; - for (int d = 0; drivers[d].name; d++) { - if (drivers[d].gated_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, "uniesc_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - int rt = run_roundtrip(drivers[d].path, d); - total++; - if (rt != 42) { - fprintf(stderr, - "uniesc_run[%s][wwi_roundtrip]: exit=%d want=42\n", - drivers[d].name, rt); - fail++; - } - } - if (fail) { - fprintf(stderr, "uniesc_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("uniesc_run: %d/%d ok\n", total, total); - return 0; -}