test: port the wwi wide-rune round-trip to ww; retire 110_uniesc_run
Adds the audit's optional strengthen: the re-serialized escape text is pinned per producer branch and wr.wwi is byte-id across stages.
This commit is contained in:
104
test/tool/wwirune_test.ww
Normal file
104
test/tool/wwirune_test.ww
Normal file
@@ -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)");
|
||||
};
|
||||
};
|
||||
@@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user