ww: put selected toolchain first in test PATH

This commit is contained in:
2026-08-21 05:30:10 +09:00
parent 1b71250ad5
commit 9b6b1100f4
7 changed files with 586 additions and 96 deletions

View File

@@ -633,10 +633,11 @@ fn hexbytes(value: str) str = {
fn cwdtestenv(stage: str) []str = {
let inherited: []str = os.getenvs();
let env: []str = alloc([], (inherited.len + 8): u64)!;
let env: []str = alloc([], (inherited.len + 10): u64)!;
let i: i32 = 0;
for (i < inherited.len) {
if (!strings.hasprefix(inherited[i], "PWD=")
&& !strings.hasprefix(inherited[i], "PATH=")
&& !strings.hasprefix(inherited[i], "WW_W6C=")
&& !strings.hasprefix(inherited[i], "WW_W6A=")
&& !strings.hasprefix(inherited[i], "WW_W6L=")
@@ -646,6 +647,7 @@ fn cwdtestenv(stage: str) []str = {
};
i += 1;
};
append(env, "PATH=/ww-path-inherited-first");
append(env, "PWD=/ww-cwd-inherited-first");
if (same(stage, "ww")) {
append(env, strings.concat("WW_W6C=", driver("w6c")));
@@ -658,10 +660,25 @@ fn cwdtestenv(stage: str) []str = {
append(env, strings.concat("WW_W6L=", driver("w6l_ww")));
};
append(env, strings.concat("WW_SRCLIB=", repo(), "/lib"));
append(env, "PATH=/ww-path-inherited-last");
append(env, "PWD=/ww-cwd-inherited-last");
return env;
};
fn cwdassertpath(text: str, label: str, path: str, count: str) void = {
assert(has(text, strings.concat(label, " PATH=", path, "\n")));
assert(has(text, strings.concat(label, " PATH-count=", count, "\n")));
};
fn cwdasserttestpath(text: str, label: str) void = {
cwdassertpath(text, label, strings.concat(repo(),
"/out/bin:/ww-path-inherited-first"), "1");
};
fn cwdassertcallerpath(text: str, label: str) void = {
cwdassertpath(text, label, "/ww-path-inherited-first", "2");
};
fn cwdassertrecord(text: str, label: str, cwd: str, pwd: str,
pwdcount: str, pwdlast: str, data: str, testdata: str,
input: str) void = {
@@ -707,6 +724,7 @@ fn cwdassertpackage(text: str, label: str, dir: str, data: str,
testdata: str) void = {
cwdassertrecord(text, label, dir, dir, "1", "yes", data, testdata,
"eof");
cwdasserttestpath(text, label);
cwdassertmergedstreams(text, label, dir);
};
@@ -883,27 +901,17 @@ fn cwdwritedata(dir: str, label: str) void = {
" let body: str; body.ptr = bytes.ptr; body.len = bytes.len; put(body);\n",
" if (body.len == 0 || body[body.len - 1] != '\\n') { put(\"\\n\"); };\n",
"};\n",
"export fn run(label: str) void = {\n",
" let buf: [4096]u8;\n",
" let n: i64 = os.getcwd(&buf[0], size([4096]u8)); assert(n > 1i64);\n",
" let cwd: str; cwd.ptr = &buf[0]; cwd.len = (n - 1i64): i32;\n",
" streams(label, cwd);\n",
" put(label); put(\" cwd=\"); put(cwd); put(\"\\n\");\n",
" put(label); put(\" PWD=\");\n",
" match (os.getenv(\"PWD\")) {\n",
" case let value: str => put(value); case void => put(\"<unset>\");\n",
" }; put(\"\\n\");\n",
" let env: []str = os.getenvs(); let count: i32 = 0; let last: i32 = -1;\n",
" let i: i32 = 0; for (i < env.len) {\n",
" if (strings.hasprefix(env[i], \"PWD=\")) { count += 1; last = i; };\n",
" i += 1;\n",
" };\n",
"export fn run(label: str) void = {\n let buf: [4096]u8;\n let n: i64 = os.getcwd(&buf[0], size([4096]u8)); assert(n > 1i64);\n let cwd: str; cwd.ptr = &buf[0]; cwd.len = (n - 1i64): i32;\n streams(label, cwd);\n put(label); put(\" cwd=\"); put(cwd); put(\"\\n\");\n put(label); put(\" PWD=\");\n match (os.getenv(\"PWD\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n put(label); put(\" PATH=\");\n match (os.getenv(\"PATH\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n let env: []str = os.getenvs(); let count: i32 = 0; let pathcount: i32 = 0; let last: i32 = -1;\n let i: i32 = 0; for (i < env.len) {\n if (strings.hasprefix(env[i], \"PWD=\")) { count += 1; last = i; };\n if (strings.hasprefix(env[i], \"PATH=\")) { pathcount += 1; };\n i += 1;\n };\n",
" put(label); put(\" PWD-count=\");\n",
" if (count == 0) { put(\"0\\n\"); } else { if (count == 1) {\n",
" put(\"1\\n\"); } else { if (count == 2) { put(\"2\\n\");\n",
" } else { put(\"many\\n\"); }; }; };\n",
" put(label); put(\" PWD-last=\");\n",
" if (last == env.len - 1) { put(\"yes\\n\"); } else { put(\"no\\n\"); };\n",
" put(label); put(\" PATH-count=\");\n",
" if (pathcount == 0) { put(\"0\\n\"); } else { if (pathcount == 1) {\n",
" put(\"1\\n\"); } else { if (pathcount == 2) { put(\"2\\n\");\n",
" } else { put(\"many\\n\"); }; }; };\n",
" emitfile(label, \"data\", \"data.txt\");\n",
" emitfile(label, \"testdata\", \"testdata/input.txt\");\n",
" let input: [1]u8;\n let inputn: i64 = os.read(os.STDIN_FILENO, &input[0], 1u64);\n put(label); put(\" stdin=\");\n if (inputn == 0i64) { put(\"eof\\n\"); } else {\n if (inputn == 1i64) { put(\"data\\n\"); } else { put(\"<error>\\n\"); };\n };\n",
@@ -1051,6 +1059,46 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(same(readfile(strings.concat(caller, "/sentinel")),
"parent-sentinel\n"));
// The official Go script exercises both an absent and an explicitly empty
// caller PATH. Each produces only the selected toolchain directory, and the
// two stages expose identical process bytes.
assert(os.remove(strings.concat(p4, "/created.txt")) == 0);
let absentbase: []str = cwdtestenv("ww");
let absentenv: []str = alloc([], absentbase.len: u64)!;
i = 0;
for (i < absentbase.len) {
if (!strings.hasprefix(absentbase[i], "PATH=")) {
append(absentenv, absentbase[i]);
};
i += 1;
};
let absentav: []str = [driver("ww"), "test", "-I", tree, p4];
runcommandinputenvdir(root, "path-absent-c", absentav, absentenv, caller,
stdinpath, (60i64 * (time.second: i64)): time.duration, &outc);
expectexit(&outc, 0);
cwdassertrecord(outc.stdout, "p4-internal-only", p4, p4, "1", "yes",
"p4-data", "p4-testdata", "eof");
cwdassertpath(outc.stdout, "p4-internal-only",
strings.concat(repo(), "/out/bin"), "1");
assert(os.remove(strings.concat(p4, "/created.txt")) == 0);
let emptybase: []str = cwdtestenv("ww_ww");
let emptyenv: []str = alloc([], (emptybase.len + 1): u64)!;
i = 0;
for (i < emptybase.len) {
if (!strings.hasprefix(emptybase[i], "PATH=")) {
append(emptyenv, emptybase[i]);
};
i += 1;
};
append(emptyenv, "PATH=");
let emptyav: []str = [driver("ww_ww"), "test", "-I", tree, p4];
runcommandinputenvdir(root, "path-empty-ww", emptyav, emptyenv, caller,
stdinpath, (60i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outw, 0);
assert(same(outc.stdout, outw.stdout));
assert(same(outc.stderr, outw.stderr));
assert(os.remove(strings.concat(p4, "/created.txt")) == 0);
// Filtering, listing, and a no-match filter all execute the same product
// binary. Package initialization therefore has the product cwd even when no
// selected test body runs.
@@ -1172,6 +1220,7 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(same(outc.stderr, outw.stderr));
assert(has(outc.stderr,
"test package must match production package or <package>_test\n"));
assert(!has(outc.stdout, " PATH=") && !has(outw.stdout, " PATH="));
assert(directoryisempty(rejectcwork));
assert(directoryisempty(rejectwwork));
@@ -1186,6 +1235,7 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(!has(out.stdout, "plain-init"));
assert(!has(out.stdout, "cmdno-init"));
assert(!has(out.stdout, "cmdno-main"));
assert(!has(out.stdout, " PATH="));
assert(!os.exists(strings.concat(plain, "/created.txt")));
assert(!os.exists(strings.concat(cmdno, "/created.txt")));
let commandbin: str = strings.concat(root, "/cmdno.bin");
@@ -1197,6 +1247,7 @@ fn cwdwritedata(dir: str, label: str) void = {
expectexit(&out, 0);
assert(os.exists(commandbin));
assert(!has(out.stdout, "cmdno-init") && !has(out.stdout, "cmdno-main"));
assert(!has(out.stdout, " PATH="));
assert(!os.exists(strings.concat(cmdno, "/created.txt")));
// Compile-only paths do not run. A published binary has no embedded cwd or
@@ -1212,6 +1263,7 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(!os.exists(strings.concat(p2, "/p2.test")));
assert(!os.exists(strings.concat(p2, "/created.txt")));
assert(!has(out.stdout, "dep-init") && !has(out.stdout, "p2-external"));
assert(!has(out.stdout, " PATH="));
assert(os.remove(defaultbin) == 0);
assert(os.remove(strings.concat(p1, "/created.txt")) == 0);
@@ -1232,6 +1284,7 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(!os.exists(strings.concat(p1, "/created.txt")));
assert(same(readfile(p1cbin), readfile(p1wbin)));
assert(!has(outc.stdout, "dep-init") && !has(outw.stdout, "dep-init"));
assert(!has(outc.stdout, " PATH=") && !has(outw.stdout, " PATH="));
let directbin: []str = [p1cbin, "-package=p1"];
runcommandinputenvdir(root, "cwd-published-direct", directbin, cenv,
@@ -1244,13 +1297,16 @@ fn cwdwritedata(dir: str, label: str) void = {
cwdassertrecord(out.stdout, "p1-internal", caller,
"/ww-cwd-inherited-first", "2", "no", "caller-data",
"caller-testdata", "data");
cwdassertcallerpath(out.stdout, "dep-init");
cwdassertcallerpath(out.stdout, "p1-internal");
cwdassertsplitstreams(out.stdout, out.stderr, "dep-init", caller);
cwdassertsplitstreams(out.stdout, out.stderr, "p1-internal", caller);
assert(os.exists(strings.concat(caller, "/created.txt")));
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
// Raw single-file compatibility is deliberately outside the directory
// coordinator rule and retains its caller execution context.
// Raw single-file compatibility retains its caller cwd/PWD/stdin context,
// but it is still a test process and therefore receives the selected
// toolchain directory first in PATH.
let rawc: []str = [driver("ww"), "test", "-I", tree,
strings.concat(p4, "/internal_test.ww")];
let raww: []str = [driver("ww_ww"), "test", "-I", tree,
@@ -1269,9 +1325,52 @@ fn cwdwritedata(dir: str, label: str) void = {
cwdassertrecord(outc.stdout, "p4-internal-only", caller,
"/ww-cwd-inherited-first", "2", "yes", "caller-data",
"caller-testdata", "data");
cwdasserttestpath(outc.stdout, "p4-internal-only");
cwdassertsplitstreams(outc.stdout, outc.stderr,
"p4-internal-only", caller);
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
// A retained running test receives the same environment before guarded
// install. A later runtime failure observes PATH but cannot replace the
// successful binary; both stages preserve identical prior bytes.
let retained: str = strings.concat(root, "/path-retained.test");
let retainc: []str = [driver("ww"), "test", "-o", retained,
"-I", tree, p1];
let retainw: []str = [driver("ww_ww"), "test", "-o", retained,
"-I", tree, p1];
runcommandinputenvdir(root, "path-retain-c", retainc, cenv, caller,
stdinpath, (90i64 * (time.second: i64)): time.duration, &outc);
expectexit(&outc, 0);
cwdassertpackage(outc.stdout, "dep-init", p1, "p1-data", "p1-testdata");
cwdassertpackage(outc.stdout, "p1-internal", p1,
"p1-data", "p1-testdata");
let retainedbytes: str = strings.dup(readfile(retained));
runcommandinputenvdir(root, "path-retain-ww", retainw, wenv, caller,
stdinpath, (90i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outw, 0);
assert(same(outc.stdout, outw.stdout));
assert(same(outc.stderr, outw.stderr));
assert(same(retainedbytes, readfile(retained)));
let retainfailc: []str = [driver("ww"), "test", "-o", retained,
"-I", tree, "-I", failtree, p7];
let retainfailw: []str = [driver("ww_ww"), "test", "-o", retained,
"-I", tree, "-I", failtree, p7];
runcommandinputenvdir(root, "path-retain-fail-c", retainfailc, cenv,
caller, stdinpath,
(90i64 * (time.second: i64)): time.duration, &outc);
expectexit(&outc, 1);
cwdassertpackage(outc.stdout, "p7-fail", p7, "p7-data", "p7-testdata");
assert(same(retainedbytes, readfile(retained)));
runcommandinputenvdir(root, "path-retain-fail-ww", retainfailw, wenv,
caller, stdinpath,
(90i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outw, 1);
assert(same(outc.stdout, outw.stdout));
assert(same(outc.stderr, outw.stderr));
assert(same(retainedbytes, readfile(retained)));
assert(!os.exists(strings.concat(retained, ".new")));
assert(!directoryhasfragment(root, "path-retained.test.wwtxn."));
cwdassertpackage(multiout, "dep-init", p2, "p2-data", "p2-testdata");
cwdassertpackage(multiout, "p2-external", p2,
"p2-data", "p2-testdata");
@@ -1344,8 +1443,8 @@ fn cwdwritedata(dir: str, label: str) void = {
"#!/bin/sh\n",
"printf 'compiler cwd=' >> \"$WW_CWD_TOOL_TRACE\"\n",
"/bin/pwd >> \"$WW_CWD_TOOL_TRACE\"\n",
"printf 'compiler env=LC_ALL:%s TMPDIR:%s\\n' \"$LC_ALL\" ",
"\"$TMPDIR\" >> \"$WW_CWD_TOOL_TRACE\"\n",
"printf 'compiler env=LC_ALL:%s TMPDIR:%s PATH:%s\\n' \"$LC_ALL\" ",
"\"$TMPDIR\" \"$PATH\" >> \"$WW_CWD_TOOL_TRACE\"\n",
"if IFS= read -r WW_CWD_STDIN; then\n",
" printf 'compiler stdin=data\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
"else printf 'compiler stdin=eof\\n' >> \"$WW_CWD_TOOL_TRACE\"; fi\n",
@@ -1358,8 +1457,8 @@ fn cwdwritedata(dir: str, label: str) void = {
"#!/bin/sh\n",
"printf 'assembler cwd=' >> \"$WW_CWD_TOOL_TRACE\"\n",
"/bin/pwd >> \"$WW_CWD_TOOL_TRACE\"\n",
"printf 'assembler env=LC_ALL:%s TMPDIR:%s\\n' \"$LC_ALL\" ",
"\"$TMPDIR\" >> \"$WW_CWD_TOOL_TRACE\"\n",
"printf 'assembler env=LC_ALL:%s TMPDIR:%s PATH:%s\\n' \"$LC_ALL\" ",
"\"$TMPDIR\" \"$PATH\" >> \"$WW_CWD_TOOL_TRACE\"\n",
"if IFS= read -r WW_CWD_STDIN; then\n",
" printf 'assembler stdin=data\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
"else printf 'assembler stdin=eof\\n' >> \"$WW_CWD_TOOL_TRACE\"; fi\n",
@@ -1372,8 +1471,8 @@ fn cwdwritedata(dir: str, label: str) void = {
"#!/bin/sh\n",
"printf 'linker cwd=' >> \"$WW_CWD_TOOL_TRACE\"\n",
"/bin/pwd >> \"$WW_CWD_TOOL_TRACE\"\n",
"printf 'linker env=LC_ALL:%s TMPDIR:%s\\n' \"$LC_ALL\" ",
"\"$TMPDIR\" >> \"$WW_CWD_TOOL_TRACE\"\n",
"printf 'linker env=LC_ALL:%s TMPDIR:%s PATH:%s\\n' \"$LC_ALL\" ",
"\"$TMPDIR\" \"$PATH\" >> \"$WW_CWD_TOOL_TRACE\"\n",
"if IFS= read -r WW_CWD_STDIN; then\n",
" printf 'linker stdin=data\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
"else printf 'linker stdin=eof\\n' >> \"$WW_CWD_TOOL_TRACE\"; fi\n",
@@ -1417,9 +1516,14 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(has(coldtrace, strings.concat("assembler cwd=", caller, "\n")));
assert(has(coldtrace, strings.concat("linker cwd=", caller, "\n")));
assert(!has(coldtrace, strings.concat("cwd=", p1, "\n")));
assert(has(coldtrace, "compiler env=LC_ALL:C TMPDIR:/tmp/"));
assert(has(coldtrace, "assembler env=LC_ALL:C TMPDIR:/tmp/"));
assert(has(coldtrace, "linker env=LC_ALL:C TMPDIR:/tmp/"));
assert(has(coldtrace,
"compiler env=LC_ALL:C TMPDIR:/tmp/"));
assert(has(coldtrace,
"assembler env=LC_ALL:C TMPDIR:/tmp/"));
assert(has(coldtrace,
"linker env=LC_ALL:C TMPDIR:/tmp/"));
assert(has(coldtrace, " PATH:/ww-path-inherited-last\n"));
assert(!has(coldtrace, strings.concat(" PATH:", repo(), "/out/bin:")));
assert(has(coldtrace, "compiler stdin=eof\n"));
assert(has(coldtrace, "assembler stdin=eof\n"));
assert(has(coldtrace, "linker stdin=eof\n"));
@@ -1528,7 +1632,7 @@ fn cwdwritedata(dir: str, label: str) void = {
"\"$WW_CWD_REAL_DRIVER\" \"$@\"\n",
"status=$?\n",
"if [ \"$status\" -eq 0 ]; then\n",
" mv -- \"$WW_CWD_HIDE_DIR\" \"$WW_CWD_HIDE_DIR.gone\" || exit 125\n",
" /bin/mv -- \"$WW_CWD_HIDE_DIR\" \"$WW_CWD_HIDE_DIR.gone\" || exit 125\n",
" printf 'wrapper-hidden=%s\\n' \"$WW_CWD_HIDE_DIR\" >&2\n",
"fi\n",
"exit \"$status\"\n"));
@@ -1545,7 +1649,7 @@ fn cwdwritedata(dir: str, label: str) void = {
driver(hidestages[i])));
append(hideenv, strings.concat("WW_CWD_HIDE_DIR=", p1));
let hideav: []str = [driver("wwtest"), "package", "--ww-driver",
hidewrapper, "-j", "2", "-I", tree, p1, p2];
"../build-then-hide.sh", "-j", "2", "-I", tree, p1, p2];
runcommandinputenvdir(root, strings.concat("cwd-hide-", hidestages[i]),
hideav, hideenv, caller, stdinpath,
(120i64 * (time.second: i64)): time.duration, &out);
@@ -1554,10 +1658,16 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(has(out.stdout, strings.concat("FAIL ", p1,
" [p1] (test harness error 2)\n")));
assert(!has(out.stdout, "p1-internal cwd="));
cwdassertpackage(out.stdout, "dep-init", p2,
"p2-data", "p2-testdata");
cwdassertpackage(out.stdout, "p2-external", p2,
"p2-data", "p2-testdata");
cwdassertrecord(out.stdout, "dep-init", p2, p2, "1", "yes",
"p2-data", "p2-testdata", "eof");
cwdassertrecord(out.stdout, "p2-external", p2, p2, "1", "yes",
"p2-data", "p2-testdata", "eof");
cwdassertpath(out.stdout, "dep-init", strings.concat(root,
":/ww-path-inherited-first"), "1");
cwdassertpath(out.stdout, "p2-external", strings.concat(root,
":/ww-path-inherited-first"), "1");
cwdassertmergedstreams(out.stdout, "dep-init", p2);
cwdassertmergedstreams(out.stdout, "p2-external", p2);
assert(os.rename(strings.concat(p1, ".gone"), p1) == 0);
assert(!os.exists(strings.concat(p1, ".gone")));
hidestdout[i] = strings.dup(out.stdout);