ww: preserve original environment for directory tests

This commit is contained in:
2026-08-21 12:57:55 +09:00
parent bcc3aa9336
commit 8a5fc8977f
5 changed files with 424 additions and 67 deletions

View File

@@ -633,11 +633,16 @@ fn hexbytes(value: str) str = {
fn cwdtestenv(stage: str) []str = {
let inherited: []str = os.getenvs();
let env: []str = alloc([], (inherited.len + 10): u64)!;
let env: []str = alloc([], (inherited.len + 24): u64)!;
let i: i32 = 0;
for (i < inherited.len) {
if (!strings.hasprefix(inherited[i], "PWD=")
&& !strings.hasprefix(inherited[i], "PATH=")
&& !strings.hasprefix(inherited[i], "LC_ALL=")
&& !strings.hasprefix(inherited[i], "TMPDIR=")
&& !strings.hasprefix(inherited[i], "WW_CWD_ORIGINAL=")
&& !strings.hasprefix(inherited[i], "WW_CWD_EMPTY=")
&& !strings.hasprefix(inherited[i], "lc_all=")
&& !strings.hasprefix(inherited[i], "WW_W6C=")
&& !strings.hasprefix(inherited[i], "WW_W6A=")
&& !strings.hasprefix(inherited[i], "WW_W6L=")
@@ -649,6 +654,14 @@ fn cwdtestenv(stage: str) []str = {
};
append(env, "PATH=/ww-path-inherited-first");
append(env, "PWD=/ww-cwd-inherited-first");
append(env, "LC_ALL=C.UTF-8");
append(env, "TMPDIR=/ww-caller-tmp-first");
append(env, "WW_CWD_ORIGINAL=first");
append(env, "WW_CWD_EMPTY=");
append(env, "lc_all=case-kept");
append(env, "WW_CWD_MALFORMED");
append(env, "");
append(env, "WW_CWD_MALFORMED");
if (same(stage, "ww")) {
append(env, strings.concat("WW_W6C=", driver("w6c")));
append(env, strings.concat("WW_W6A=", driver("w6a")));
@@ -660,6 +673,9 @@ fn cwdtestenv(stage: str) []str = {
append(env, strings.concat("WW_W6L=", driver("w6l_ww")));
};
append(env, strings.concat("WW_SRCLIB=", repo(), "/lib"));
append(env, "LC_ALL=C");
append(env, "TMPDIR=/ww-caller-tmp-last");
append(env, "WW_CWD_ORIGINAL=last");
append(env, "PATH=/ww-path-inherited-last");
append(env, "PWD=/ww-cwd-inherited-last");
return env;
@@ -679,6 +695,42 @@ fn cwdassertcallerpath(text: str, label: str) void = {
cwdassertpath(text, label, "/ww-path-inherited-first", "2");
};
fn cwdassertenvvalues(text: str, label: str) void = {
assert(has(text, strings.concat(label, " LC_ALL=C.UTF-8\n")));
assert(has(text, strings.concat(label,
" TMPDIR=/ww-caller-tmp-first\n")));
assert(has(text, strings.concat(label,
" WW_CWD_ORIGINAL=first\n")));
assert(has(text, strings.concat(label, " WW_CWD_EMPTY=\n")));
assert(has(text, strings.concat(label, " lc_all=case-kept\n")));
};
fn cwdassertdirectoryenv(text: str, label: str) void = {
cwdassertenvvalues(text, label);
assert(has(text, strings.concat(label, " LC_ALL-count=1\n")));
assert(has(text, strings.concat(label, " TMPDIR-count=1\n")));
assert(has(text, strings.concat(label,
" WW_CWD_ORIGINAL-count=1\n")));
assert(has(text, strings.concat(label,
" WW_CWD_EMPTY-count=1\n")));
assert(has(text, strings.concat(label, " lc_all-count=1\n")));
assert(has(text, strings.concat(label, " malformed-count=2\n")));
assert(has(text, strings.concat(label, " empty-entry-count=0\n")));
};
fn cwdassertcallerenv(text: str, label: str) void = {
cwdassertenvvalues(text, label);
assert(has(text, strings.concat(label, " LC_ALL-count=2\n")));
assert(has(text, strings.concat(label, " TMPDIR-count=2\n")));
assert(has(text, strings.concat(label,
" WW_CWD_ORIGINAL-count=2\n")));
assert(has(text, strings.concat(label,
" WW_CWD_EMPTY-count=1\n")));
assert(has(text, strings.concat(label, " lc_all-count=1\n")));
assert(has(text, strings.concat(label, " malformed-count=2\n")));
assert(has(text, strings.concat(label, " empty-entry-count=1\n")));
};
fn cwdassertrecord(text: str, label: str, cwd: str, pwd: str,
pwdcount: str, pwdlast: str, data: str, testdata: str,
input: str) void = {
@@ -724,6 +776,7 @@ fn cwdassertpackage(text: str, label: str, dir: str, data: str,
testdata: str) void = {
cwdassertrecord(text, label, dir, dir, "1", "yes", data, testdata,
"eof");
cwdassertdirectoryenv(text, label);
cwdasserttestpath(text, label);
cwdassertmergedstreams(text, label, dir);
};
@@ -795,10 +848,10 @@ fn cwdwritedata(dir: str, label: str) void = {
clean(root);
};
// Directory-package execution is one product-level runtime property. This
// fixture keeps every source tree disposable while covering the variant,
// dependency, routing, concurrency, persistence, and failure boundaries that
// could otherwise accidentally derive a cwd from an action or request name.
// Directory-package execution context is one product-level runtime property.
// This fixture keeps every source tree disposable while covering the variant,
// dependency, environment, routing, concurrency, persistence, and failure
// boundaries that could otherwise leak build-plan or caller process state.
@test fn directory_test_execution_working_directory() void = {
let root: str = fresh();
let caller: str = strings.concat(root, "/caller");
@@ -872,12 +925,22 @@ fn cwdwritedata(dir: str, label: str) void = {
"};\n",
"fn put(value: str) void = { putfd(os.STDOUT_FILENO, value); };\n",
"fn puterr(value: str) void = { putfd(os.STDERR_FILENO, value); };\n",
"fn putcount(value: i32) void = {\n",
" if (value == 0) { put(\"0\\n\"); } else { if (value == 1) {\n",
" put(\"1\\n\"); } else { if (value == 2) { put(\"2\\n\");\n",
" } else { put(\"many\\n\"); }; }; };\n",
"};\n",
"export fn streams(label: str, context: str) void = {\n",
" put(strings.concat(\"STREAM \", label, \" out-1 cwd=\", context, \"\\n\"));\n",
" puterr(strings.concat(\"STREAM \", label, \" err-1 cwd=\", context, \"\\n\"));\n",
" put(strings.concat(\"STREAM \", label, \" out-2 cwd=\", context, \"\\n\"));\n",
" puterr(strings.concat(\"STREAM \", label, \" err-2 cwd=\", context, \"\\n\"));\n",
"};\n");
let envsource: str = strings.concat(
" put(label); put(\" LC_ALL=\");\n match (os.getenv(\"LC_ALL\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n put(label); put(\" TMPDIR=\");\n match (os.getenv(\"TMPDIR\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n put(label); put(\" WW_CWD_ORIGINAL=\");\n match (os.getenv(\"WW_CWD_ORIGINAL\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n put(label); put(\" WW_CWD_EMPTY=\");\n match (os.getenv(\"WW_CWD_EMPTY\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n put(label); put(\" lc_all=\");\n match (os.getenv(\"lc_all\")) {\n case let value: str => put(value); case void => put(\"<unset>\");\n }; put(\"\\n\");\n",
" let lccount: i32 = 0; let tmpcount: i32 = 0;\n let originalcount: i32 = 0; let emptyvaluecount: i32 = 0;\n let lowercount: i32 = 0; let malformedcount: i32 = 0;\n let emptyentrycount: i32 = 0;\n i = 0; for (i < env.len) {\n if (strings.hasprefix(env[i], \"LC_ALL=\")) { lccount += 1; };\n if (strings.hasprefix(env[i], \"TMPDIR=\")) { tmpcount += 1; };\n if (strings.hasprefix(env[i], \"WW_CWD_ORIGINAL=\")) { originalcount += 1; };\n if (strings.hasprefix(env[i], \"WW_CWD_EMPTY=\")) { emptyvaluecount += 1; };\n if (strings.hasprefix(env[i], \"lc_all=\")) { lowercount += 1; };\n if (strings.compare(env[i], \"WW_CWD_MALFORMED\") == 0) { malformedcount += 1; };\n if (env[i].len == 0) { emptyentrycount += 1; };\n i += 1;\n };\n",
" put(label); put(\" PWD-count=\"); putcount(count);\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=\"); putcount(pathcount);\n",
" put(label); put(\" LC_ALL-count=\"); putcount(lccount);\n put(label); put(\" TMPDIR-count=\"); putcount(tmpcount);\n put(label); put(\" WW_CWD_ORIGINAL-count=\"); putcount(originalcount);\n put(label); put(\" WW_CWD_EMPTY-count=\"); putcount(emptyvaluecount);\n put(label); put(\" lc_all-count=\"); putcount(lowercount);\n put(label); put(\" malformed-count=\"); putcount(malformedcount);\n put(label); put(\" empty-entry-count=\"); putcount(emptyentrycount);\n");
writefile(strings.concat(probe, "/probe.ww"), strings.concat(
"package probe;\n",
"import os;\n",
@@ -902,16 +965,7 @@ fn cwdwritedata(dir: str, label: str) void = {
" 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 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",
envsource,
" 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",
@@ -1078,6 +1132,7 @@ fn cwdwritedata(dir: str, label: str) void = {
expectexit(&outc, 0);
cwdassertrecord(outc.stdout, "p4-internal-only", p4, p4, "1", "yes",
"p4-data", "p4-testdata", "eof");
cwdassertdirectoryenv(outc.stdout, "p4-internal-only");
cwdassertpath(outc.stdout, "p4-internal-only",
strings.concat(repo(), "/out/bin"), "1");
assert(os.remove(strings.concat(p4, "/created.txt")) == 0);
@@ -1224,6 +1279,8 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(has(outc.stderr,
"test package must match production package or <package>_test\n"));
assert(!has(outc.stdout, " PATH=") && !has(outw.stdout, " PATH="));
assert(!has(outc.stdout, " LC_ALL=")
&& !has(outw.stdout, " LC_ALL="));
assert(directoryisempty(rejectcwork));
assert(directoryisempty(rejectwwork));
@@ -1239,6 +1296,8 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(!has(out.stdout, "cmdno-init"));
assert(!has(out.stdout, "cmdno-main"));
assert(!has(out.stdout, " PATH="));
assert(!has(out.stdout, " LC_ALL=")
&& !has(out.stdout, " TMPDIR="));
assert(!os.exists(strings.concat(plain, "/created.txt")));
assert(!os.exists(strings.concat(cmdno, "/created.txt")));
let commandbin: str = strings.concat(root, "/cmdno.bin");
@@ -1251,6 +1310,8 @@ fn cwdwritedata(dir: str, label: str) void = {
assert(os.exists(commandbin));
assert(!has(out.stdout, "cmdno-init") && !has(out.stdout, "cmdno-main"));
assert(!has(out.stdout, " PATH="));
assert(!has(out.stdout, " LC_ALL=")
&& !has(out.stdout, " TMPDIR="));
assert(!os.exists(strings.concat(cmdno, "/created.txt")));
// Compile-only paths do not run. A published binary has no embedded cwd or
@@ -1267,6 +1328,8 @@ fn cwdwritedata(dir: str, label: str) void = {
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(!has(out.stdout, " LC_ALL=")
&& !has(out.stdout, " TMPDIR="));
assert(os.remove(defaultbin) == 0);
assert(os.remove(strings.concat(p1, "/created.txt")) == 0);
@@ -1288,6 +1351,8 @@ fn cwdwritedata(dir: str, label: str) void = {
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="));
assert(!has(outc.stdout, " LC_ALL=")
&& !has(outw.stdout, " LC_ALL="));
let directbin: []str = [p1cbin, "-package=p1"];
runcommandinputenvdir(root, "cwd-published-direct", directbin, cenv,
@@ -1300,6 +1365,8 @@ fn cwdwritedata(dir: str, label: str) void = {
cwdassertrecord(out.stdout, "p1-internal", caller,
"/ww-cwd-inherited-first", "2", "no", "caller-data",
"caller-testdata", "data");
cwdassertcallerenv(out.stdout, "dep-init");
cwdassertcallerenv(out.stdout, "p1-internal");
cwdassertcallerpath(out.stdout, "dep-init");
cwdassertcallerpath(out.stdout, "p1-internal");
cwdassertsplitstreams(out.stdout, out.stderr, "dep-init", caller);
@@ -1328,6 +1395,7 @@ 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");
cwdassertcallerenv(outc.stdout, "p4-internal-only");
cwdasserttestpath(outc.stdout, "p4-internal-only");
cwdassertsplitstreams(outc.stdout, outc.stderr,
"p4-internal-only", caller);
@@ -1666,6 +1734,8 @@ fn cwdwritedata(dir: str, label: str) void = {
"p2-data", "p2-testdata", "eof");
cwdassertrecord(out.stdout, "p2-external", p2, p2, "1", "yes",
"p2-data", "p2-testdata", "eof");
cwdassertdirectoryenv(out.stdout, "dep-init");
cwdassertdirectoryenv(out.stdout, "p2-external");
cwdassertpath(out.stdout, "dep-init", strings.concat(root,
":/ww-path-inherited-first"), "1");
cwdassertpath(out.stdout, "p2-external", strings.concat(root,