test: prove package-source test execution cwd

This commit is contained in:
2026-08-20 16:25:43 +09:00
parent 5f75fcb113
commit 6e9591d4b4
4 changed files with 955 additions and 1 deletions

View File

@@ -560,6 +560,62 @@ fn hexbytes(value: str) str = {
return strings.frombytes(out);
};
fn cwdtestenv(stage: str) []str = {
let inherited: []str = os.getenvs();
let env: []str = alloc([], (inherited.len + 8): u64)!;
let i: i32 = 0;
for (i < inherited.len) {
if (!strings.hasprefix(inherited[i], "PWD=")
&& !strings.hasprefix(inherited[i], "WW_W6C=")
&& !strings.hasprefix(inherited[i], "WW_W6A=")
&& !strings.hasprefix(inherited[i], "WW_W6L=")
&& !strings.hasprefix(inherited[i], "WW_SRCLIB=")
&& !strings.hasprefix(inherited[i], "WW_CWD_")) {
append(env, inherited[i]);
};
i += 1;
};
append(env, "PWD=/ww-cwd-inherited-first");
if (same(stage, "ww")) {
append(env, strings.concat("WW_W6C=", driver("w6c")));
append(env, strings.concat("WW_W6A=", driver("w6a")));
append(env, strings.concat("WW_W6L=", driver("w6l")));
} else {
assert(same(stage, "ww_ww"));
append(env, strings.concat("WW_W6C=", driver("w6c_ww")));
append(env, strings.concat("WW_W6A=", driver("w6a_ww")));
append(env, strings.concat("WW_W6L=", driver("w6l_ww")));
};
append(env, strings.concat("WW_SRCLIB=", repo(), "/lib"));
append(env, "PWD=/ww-cwd-inherited-last");
return env;
};
fn cwdassertrecord(text: str, label: str, cwd: str, pwd: str,
pwdcount: str, pwdlast: str, data: str, testdata: str) void = {
assert(has(text, strings.concat(label, " cwd=", cwd, "\n")));
assert(has(text, strings.concat(label, " PWD=", pwd, "\n")));
assert(has(text, strings.concat(label, " PWD-count=", pwdcount, "\n")));
assert(has(text, strings.concat(label, " PWD-last=", pwdlast, "\n")));
assert(has(text, strings.concat(label, " data=", data, "\n")));
assert(has(text, strings.concat(label, " testdata=", testdata, "\n")));
assert(has(text, strings.concat(label, " create=ok\n")));
};
fn cwdassertpackage(text: str, label: str, dir: str, data: str,
testdata: str) void = {
cwdassertrecord(text, label, dir, dir, "1", "yes", data, testdata);
};
fn cwdwritedata(dir: str, label: str) void = {
let testdata: str = strings.concat(dir, "/testdata");
assert(os.mkdir(testdata, 448i32) == 0);
writefile(strings.concat(dir, "/data.txt"),
strings.concat(label, "-data\n"));
writefile(strings.concat(testdata, "/input.txt"),
strings.concat(label, "-testdata\n"));
};
@test fn deterministic_routing_and_filters() void = {
let root: str = fresh();
let target: str = packagepath("routing");
@@ -618,6 +674,654 @@ fn hexbytes(value: str) str = {
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.
@test fn directory_test_execution_working_directory() void = {
let root: str = fresh();
let caller: str = strings.concat(root, "/caller");
let tree: str = strings.concat(root, "/tree");
let treelink: str = strings.concat(root, "/tree-link");
let failtree: str = strings.concat(root, "/failtree");
mkdirall(caller);
mkdirall(tree);
mkdirall(failtree);
cwdwritedata(caller, "caller");
writefile(strings.concat(caller, "/sentinel"), "parent-sentinel\n");
// Create in reverse lexical order; discovery and product emission may not
// inherit filesystem or request order.
let names: []str = ["probe", "plain", "p6", "p5", "p4", "p3",
"p2", "p1", "testdep", "dep", "cmdno"];
let i: i32 = 0;
for (i < names.len) {
let dir: str = strings.concat(tree, "/", names[i]);
assert(os.mkdir(dir, 448i32) == 0);
cwdwritedata(dir, names[i]);
i += 1;
};
let probe: str = strings.concat(tree, "/probe");
let dep: str = strings.concat(tree, "/dep");
let testdep: str = strings.concat(tree, "/testdep");
let p1: str = strings.concat(tree, "/p1");
let p2: str = strings.concat(tree, "/p2");
let p3: str = strings.concat(tree, "/p3");
let p4: str = strings.concat(tree, "/p4");
let p5: str = strings.concat(tree, "/p5");
let p6: str = strings.concat(tree, "/p6");
let plain: str = strings.concat(tree, "/plain");
let cmdno: str = strings.concat(tree, "/cmdno");
writefile(strings.concat(probe, "/probe.ww"), strings.concat(
"package probe;\n",
"import os;\n",
"import strings;\n",
"fn put(value: str) void = {\n",
" match (os.writeall(os.STDOUT_FILENO, value.ptr, value.len: u64)) {\n",
" case let n: i64 => assert(n == value.len: i64);\n",
" case let e: os.oserror => abort(\"write failed\");\n",
" };\n",
"};\n",
"fn emitfile(label: str, key: str, path: str) void = {\n",
" put(label); put(\" \" ); put(key); put(\"=\");\n",
" let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);\n",
" if (fd < 0) { put(\"<missing>\\n\"); return; };\n",
" let size: i64 = -1i64;\n",
" match (os.filesize(fd)) {\n",
" case let value: i64 => size = value;\n",
" case let e: os.oserror => void;\n",
" };\n",
" if (size < 0i64) { os.close(fd); put(\"<size-error>\\n\"); return; };\n",
" let bytes: []u8 = alloc([], size: u64)!; bytes.len = size: i32;\n",
" match (os.readall(fd, bytes.ptr, size: u64)) {\n",
" case let n: i64 => assert(n == size);\n",
" case let e: os.oserror => abort(\"read failed\");\n",
" }; os.close(fd);\n",
" 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",
" 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",
" 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",
" emitfile(label, \"data\", \"data.txt\");\n",
" emitfile(label, \"testdata\", \"testdata/input.txt\");\n",
" let fd: i32 = os.open(\"created.txt\", os.flag.WRONLY |\n",
" os.flag.CREATE | os.flag.TRUNC, 384i32);\n",
" put(label); put(\" create=\");\n",
" if (fd < 0) { put(\"<error>\\n\"); } else {\n",
" match (os.writeall(fd, label.ptr, label.len: u64)) {\n",
" case let wrote: i64 => assert(wrote == label.len: i64);\n",
" case let e: os.oserror => abort(\"create write failed\");\n",
" }; assert(os.close(fd) == 0); put(\"ok\\n\");\n",
" };\n",
"};\n"));
writefile(strings.concat(dep, "/dep.ww"), strings.concat(
"package dep;\nimport probe;\n",
"export fn value() i32 = { return 7; };\n",
"fn init() void = { probe.run(\"dep-init\"); };\n"));
writefile(strings.concat(dep, "/dep_test.ww"),
"package dep;\nimport probe;\n@test fn cwd() void = { probe.run(\"dep-test\"); };\n");
writefile(strings.concat(testdep, "/testdep.ww"), strings.concat(
"package testdep;\nimport probe;\n",
"export fn value() i32 = { return 8; };\n",
"fn init() void = { probe.run(\"testdep-init\"); };\n"));
writefile(strings.concat(testdep, "/testdep_test.ww"), strings.concat(
"package testdep;\nimport probe;\n",
"@test fn cwd() void = { probe.run(\"testdep-test\"); };\n"));
writefile(strings.concat(p1, "/p1.ww"),
"package p1;\nimport _ dep;\nexport fn value() i32 = { return 1; };\n");
writefile(strings.concat(p1, "/p1_test.ww"),
"package p1;\nimport probe;\n@test fn cwd() void = { probe.run(\"p1-internal\"); };\n");
writefile(strings.concat(p2, "/p2.ww"),
"package p2;\nimport _ dep;\nexport fn value() i32 = { return 2; };\n");
writefile(strings.concat(p2, "/p2_test.ww"), strings.concat(
"package p2_test;\nimport p2;\nimport probe;\n",
"@test fn cwd() void = { assert(p2.value() == 2); ",
"probe.run(\"p2-external\"); };\n"));
writefile(strings.concat(p3, "/p3.ww"),
"package p3;\nimport _ dep;\nexport fn value() i32 = { return 3; };\n");
writefile(strings.concat(p3, "/internal_test.ww"), strings.concat(
"package p3;\nimport _ testdep;\nimport probe;\n",
"export fn helper() i32 = { return 33; };\n",
"@test fn cwd_internal() void = { probe.run(\"p3-internal\"); };\n"));
writefile(strings.concat(p3, "/external_test.ww"), strings.concat(
"package p3_test;\nimport p3;\nimport probe;\n",
"@test fn cwd_external() void = { assert(p3.helper() == 33); ",
"probe.run(\"p3-external\"); };\n"));
writefile(strings.concat(p4, "/internal_test.ww"),
"package p4;\nimport probe;\n@test fn cwd() void = { probe.run(\"p4-internal-only\"); };\n");
writefile(strings.concat(p5, "/external_test.ww"),
"package p5_test;\nimport probe;\n@test fn cwd() void = { probe.run(\"p5-external-only\"); };\n");
writefile(strings.concat(p6, "/internal_test.ww"), strings.concat(
"package p6;\nimport probe;\nexport fn helper() i32 = { return 66; };\n",
"@test fn cwd_internal() void = { probe.run(\"p6-internal-only\"); };\n"));
writefile(strings.concat(p6, "/external_test.ww"), strings.concat(
"package p6_test;\nimport p6;\nimport probe;\n",
"@test fn cwd_external() void = { assert(p6.helper() == 66); ",
"probe.run(\"p6-external-only\"); };\n"));
writefile(strings.concat(plain, "/plain.ww"),
"package plain;\nimport probe;\nfn init() void = { probe.run(\"plain-init\"); };\n");
writefile(strings.concat(cmdno, "/main.ww"), strings.concat(
"package main;\nimport probe;\n",
"fn init() void = { probe.run(\"cmdno-init\"); };\n",
"fn main() void = { probe.run(\"cmdno-main\"); };\n"));
let failnames: []str = ["p8", "p7"];
i = 0;
for (i < failnames.len) {
let dir: str = strings.concat(failtree, "/", failnames[i]);
assert(os.mkdir(dir, 448i32) == 0);
cwdwritedata(dir, failnames[i]);
i += 1;
};
let p7: str = strings.concat(failtree, "/p7");
let p8: str = strings.concat(failtree, "/p8");
writefile(strings.concat(p7, "/p7_test.ww"), strings.concat(
"package p7;\nimport probe;\n@test fn fails() void = {\n",
" probe.run(\"p7-fail\"); assert(false);\n};\n"));
writefile(strings.concat(p8, "/p8_test.ww"), strings.concat(
"package p8;\nimport probe;\nimport time;\n@test fn timesout() void = {\n",
" probe.run(\"p8-timeout\");\n",
" time.sleep((2i64 * (time.second: i64)): time.duration, ",
"time.clock.monotonic);\n};\n"));
let out: commandout;
let lnav: []str = ["/bin/ln", "-s", tree, treelink];
runcommand(root, "cwd-link", lnav, time.second, &out);
expectexit(&out, 0);
// The first environment has hundreds of unrelated entries after two PWD
// entries. The product must dynamically clone it, remove both PWD entries,
// and append exactly one authoritative PWD last.
let cbase: []str = cwdtestenv("ww");
let cenv: []str = alloc([], (cbase.len + 300): u64)!;
i = 0;
for (i < cbase.len) { append(cenv, cbase[i]); i += 1; };
i = 0;
for (i < 300) {
append(cenv, strings.concat("WW_CWD_PADDING_", boundarypkgname(i),
"=abcdefghijklmnopqrstuvwxyz0123456789"));
i += 1;
};
let wenv: []str = cwdtestenv("ww_ww");
let forward: []str = [driver("ww"), "test", "-j", "1", "-I", tree,
dep, testdep, p1, p2, p3, p4, p5, p6];
let reverse: []str = [driver("ww_ww"), "test", "-j", "4", "-I", tree,
p6, p5, p4, p3, p2, p1, testdep, dep];
let outc: commandout;
let outw: commandout;
runcommandenvdir(root, "cwd-multi-c", forward, cenv, caller,
(120i64 * (time.second: i64)): time.duration, &outc);
runcommandenvdir(root, "cwd-multi-ww", reverse, wenv, caller,
(120i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 0);
expectexit(&outw, 0);
assert(same(outc.stdout, outw.stdout));
assert(same(outc.stderr, outw.stderr) && outc.stderr.len == 0);
cwdassertpackage(outc.stdout, "dep-init", dep, "dep-data", "dep-testdata");
cwdassertpackage(outc.stdout, "dep-test", dep, "dep-data", "dep-testdata");
cwdassertpackage(outc.stdout, "testdep-init", testdep,
"testdep-data", "testdep-testdata");
cwdassertpackage(outc.stdout, "testdep-test", testdep,
"testdep-data", "testdep-testdata");
cwdassertpackage(outc.stdout, "dep-init", p1, "p1-data", "p1-testdata");
cwdassertpackage(outc.stdout, "p1-internal", p1,
"p1-data", "p1-testdata");
let multiout: str = strings.dup(outc.stdout);
let ran: []str = [dep, testdep, p1, p2, p3, p4, p5, p6];
i = 0;
for (i < ran.len) {
assert(os.exists(strings.concat(ran[i], "/created.txt")));
i += 1;
};
assert(!os.exists(strings.concat(caller, "/created.txt")));
assert(same(readfile(strings.concat(caller, "/sentinel")),
"parent-sentinel\n"));
// 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.
let filterav: []str = [driver("ww"), "test", "-run", "cwd_external",
"-I", tree, p3];
runcommandenvdir(root, "cwd-filter", filterav, cenv, caller,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata");
cwdassertpackage(out.stdout, "testdep-init", p3,
"p3-data", "p3-testdata");
cwdassertpackage(out.stdout, "p3-external", p3,
"p3-data", "p3-testdata");
assert(!has(out.stdout, "p3-internal cwd="));
let listav: []str = [driver("ww_ww"), "test", "-list", "-I", tree, p3];
runcommandenvdir(root, "cwd-list", listav, wenv, caller,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata");
cwdassertpackage(out.stdout, "testdep-init", p3,
"p3-data", "p3-testdata");
assert(has(out.stdout, "p3.cwd_internal\n"));
assert(has(out.stdout, "p3.cwd_external\n"));
assert(!has(out.stdout, "p3-internal cwd="));
assert(!has(out.stdout, "p3-external cwd="));
let nomatchav: []str = [driver("ww"), "test", "-run", "no-such-test",
"-I", tree, p3];
runcommandenvdir(root, "cwd-no-match", nomatchav, cenv, caller,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata");
cwdassertpackage(out.stdout, "testdep-init", p3,
"p3-data", "p3-testdata");
assert(has(out.stdout, "[no matches]\n"));
assert(!has(out.stdout, "p3-internal cwd="));
assert(!has(out.stdout, "p3-external cwd="));
let failc: []str = [driver("ww"), "test", "-I", tree, "-I", failtree,
p7];
let failw: []str = [driver("ww_ww"), "test", "-I", tree, "-I",
failtree, p7];
runcommandenvdir(root, "cwd-fail-c", failc, cenv, caller,
(60i64 * (time.second: i64)): time.duration, &outc);
runcommandenvdir(root, "cwd-fail-ww", failw, wenv, caller,
(60i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 1);
expectexit(&outw, 1);
assert(same(outc.stdout, outw.stdout));
assert(same(outc.stderr, outw.stderr));
cwdassertpackage(outc.stdout, "p7-fail", p7, "p7-data", "p7-testdata");
assert(same(outc.stderr, strings.concat("FAIL ", p7,
" [p7] (test exit 1)\n")));
let timeoutc: []str = [driver("ww"), "test", "-timeout-ms=50", "-I",
tree, "-I", failtree, p8];
let timeoutw: []str = [driver("ww_ww"), "test", "-timeout-ms=50", "-I",
tree, "-I", failtree, p8];
runcommandenvdir(root, "cwd-timeout-c", timeoutc, cenv, caller,
(60i64 * (time.second: i64)): time.duration, &outc);
runcommandenvdir(root, "cwd-timeout-ww", timeoutw, wenv, caller,
(60i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 1);
expectexit(&outw, 1);
assert(same(outc.stdout, outw.stdout));
assert(same(outc.stderr, outw.stderr));
cwdassertpackage(outc.stdout, "p8-timeout", p8,
"p8-data", "p8-testdata");
assert(has(outc.stdout, "FAIL(timeout)\n"));
assert(same(outc.stderr, strings.concat("FAIL ", p8,
" [p8] (test exit 1)\n")));
// Packages without selected test sources and `ww build` never start a test
// process, even if production initialization would make that visible.
let notestav: []str = [driver("ww_ww"), "test", "-I", tree,
plain, cmdno];
runcommandenvdir(root, "cwd-no-tests", notestav, wenv, caller,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(!has(out.stdout, "plain-init"));
assert(!has(out.stdout, "cmdno-init"));
assert(!has(out.stdout, "cmdno-main"));
assert(!os.exists(strings.concat(plain, "/created.txt")));
assert(!os.exists(strings.concat(cmdno, "/created.txt")));
let commandbin: str = strings.concat(root, "/cmdno.bin");
let buildav: []str = [driver("ww"), "build", "-I", tree, "-o",
commandbin, cmdno];
runcommandenvdir(root, "cwd-build", buildav, cenv, caller,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(os.exists(commandbin));
assert(!has(out.stdout, "cmdno-init") && !has(out.stdout, "cmdno-main"));
assert(!os.exists(strings.concat(cmdno, "/created.txt")));
// Compile-only paths do not run. A published binary has no embedded cwd or
// wrapper: direct invocation keeps the caller's cwd and duplicate PWDs.
assert(os.remove(strings.concat(p2, "/created.txt")) == 0);
let defaultcompile: []str = [driver("ww"), "test", "-c", "-I", tree, p2];
runcommandenvdir(root, "cwd-compile-default", defaultcompile, cenv, caller,
(90i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
let defaultbin: str = strings.concat(p2, "/p2.test");
assert(os.exists(defaultbin));
assert(!os.exists(strings.concat(p2, "/created.txt")));
assert(!has(out.stdout, "dep-init") && !has(out.stdout, "p2-external"));
assert(os.remove(defaultbin) == 0);
clean(strings.concat(defaultbin, ".sepwork"));
assert(os.remove(strings.concat(p1, "/created.txt")) == 0);
let p1cbin: str = strings.concat(root, "/p1-c.test");
let p1wbin: str = strings.concat(root, "/p1-ww.test");
let compilec: []str = [driver("ww"), "test", "-c", "-o", p1cbin,
"-I", tree, p1];
let compilew: []str = [driver("ww_ww"), "test", "-c", "-o", p1wbin,
"-I", tree, p1];
runcommandenvdir(root, "cwd-compile-c", compilec, cenv, caller,
(90i64 * (time.second: i64)): time.duration, &outc);
runcommandenvdir(root, "cwd-compile-ww", compilew, wenv, caller,
(90i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 0);
expectexit(&outw, 0);
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"));
let directbin: []str = [p1cbin, "-package=p1"];
runcommandenvdir(root, "cwd-published-direct", directbin, cenv, caller,
(60i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
cwdassertrecord(out.stdout, "dep-init", caller,
"/ww-cwd-inherited-first", "2", "no", "caller-data",
"caller-testdata");
cwdassertrecord(out.stdout, "p1-internal", caller,
"/ww-cwd-inherited-first", "2", "no", "caller-data",
"caller-testdata");
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.
let rawc: []str = [driver("ww"), "test", "-I", tree,
strings.concat(p4, "/internal_test.ww")];
let raww: []str = [driver("ww_ww"), "test", "-I", tree,
strings.concat(p4, "/internal_test.ww")];
runcommandenvdir(root, "cwd-raw-c", rawc, cbase, caller,
(60i64 * (time.second: i64)): time.duration, &outc);
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
runcommandenvdir(root, "cwd-raw-ww", raww, wenv, caller,
(60i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 0);
expectexit(&outw, 0);
assert(same(outc.stdout, outw.stdout));
assert(same(outc.stderr, outw.stderr));
cwdassertrecord(outc.stdout, "p4-internal-only", caller,
"/ww-cwd-inherited-first", "2", "yes", "caller-data",
"caller-testdata");
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
cwdassertpackage(multiout, "dep-init", p2, "p2-data", "p2-testdata");
cwdassertpackage(multiout, "p2-external", p2,
"p2-data", "p2-testdata");
cwdassertpackage(multiout, "dep-init", p3, "p3-data", "p3-testdata");
cwdassertpackage(multiout, "testdep-init", p3,
"p3-data", "p3-testdata");
cwdassertpackage(multiout, "p3-internal", p3,
"p3-data", "p3-testdata");
cwdassertpackage(multiout, "p3-external", p3,
"p3-data", "p3-testdata");
cwdassertpackage(multiout, "p4-internal-only", p4,
"p4-data", "p4-testdata");
cwdassertpackage(multiout, "p5-external-only", p5,
"p5-data", "p5-testdata");
cwdassertpackage(multiout, "p6-internal-only", p6,
"p6-data", "p6-testdata");
cwdassertpackage(multiout, "p6-external-only", p6,
"p6-data", "p6-testdata");
let realrec: []str = [driver("ww"), "test", "-j", "1", "-I", tree,
strings.concat(tree, "/...")];
let linkrec: []str = [driver("ww_ww"), "test", "-j", "4", "-I",
"../tree-link", "../tree-link/..."];
runcommandenvdir(root, "cwd-recursive-real", realrec, cenv, caller,
(120i64 * (time.second: i64)): time.duration, &outc);
runcommandenvdir(root, "cwd-recursive-link", linkrec, wenv, caller,
(120i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 0);
expectexit(&outw, 0);
assert(same(outc.stdout, outw.stdout));
assert(same(outc.stderr, outw.stderr) && outc.stderr.len == 0);
assert(has(outc.stdout, strings.concat("? ", plain, " [no tests]\n")));
assert(has(outc.stdout, strings.concat("? ", cmdno, " [no tests]\n")));
assert(!has(outc.stdout, "plain-init"));
assert(!has(outc.stdout, "cmdno-init"));
assert(!has(outc.stdout, "cmdno-main"));
cwdassertpackage(outc.stdout, "p6-external-only", p6,
"p6-data", "p6-testdata");
let direct: []str = [driver("ww"), "test", "-I", tree, p1];
let equivalent: []str = [driver("ww_ww"), "test", "-j", "3", "-I",
"../tree-link", "../tree/p1", p1, "../tree/./p1/../p1",
"../tree-link/p1"];
runcommandenvdir(root, "cwd-direct", direct, cenv, caller,
(60i64 * (time.second: i64)): time.duration, &outc);
runcommandenvdir(root, "cwd-equivalent", equivalent, wenv, caller,
(60i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outc, 0);
expectexit(&outw, 0);
assert(same(outc.stdout, outw.stdout));
assert(same(outc.stderr, outw.stderr));
assert(occurrences(outc.stdout,
strings.concat("ok ", p1, " [p1]\n")) == 1);
cwdassertpackage(outc.stdout, "dep-init", p1, "p1-data", "p1-testdata");
cwdassertpackage(outc.stdout, "p1-internal", p1,
"p1-data", "p1-testdata");
// Tool wrappers observe the established build-tool context. Only the
// generated product binary gets the package cwd; compiler, assembler, and
// linker cwd/argv/locale/TMPDIR remain owned by the build plan.
let tooltrace: str = strings.concat(root, "/cwd-tools.trace");
let cw: str = strings.concat(root, "/cwd-w6c.sh");
let aw: str = strings.concat(root, "/cwd-w6a.sh");
let lw: str = strings.concat(root, "/cwd-w6l.sh");
writefile(tooltrace, "");
writeexecutable(cw, strings.concat(
"#!/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 argv' >> \"$WW_CWD_TOOL_TRACE\"\n",
"for arg in \"$@\"; do printf ' <%s>' \"$arg\" >> ",
"\"$WW_CWD_TOOL_TRACE\"; done\n",
"printf '\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
"exec \"$WW_CWD_REAL_W6C\" \"$@\"\n"));
writeexecutable(aw, strings.concat(
"#!/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 argv' >> \"$WW_CWD_TOOL_TRACE\"\n",
"for arg in \"$@\"; do printf ' <%s>' \"$arg\" >> ",
"\"$WW_CWD_TOOL_TRACE\"; done\n",
"printf '\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
"exec \"$WW_CWD_REAL_W6A\" \"$@\"\n"));
writeexecutable(lw, strings.concat(
"#!/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 argv' >> \"$WW_CWD_TOOL_TRACE\"\n",
"for arg in \"$@\"; do printf ' <%s>' \"$arg\" >> ",
"\"$WW_CWD_TOOL_TRACE\"; done\n",
"printf '\\n' >> \"$WW_CWD_TOOL_TRACE\"\n",
"exec \"$WW_CWD_REAL_W6L\" \"$@\"\n"));
let tracebase: []str = cwdtestenv("ww");
let traceenv: []str = alloc([], (tracebase.len + 7): u64)!;
i = 0;
for (i < tracebase.len) {
if (!strings.hasprefix(tracebase[i], "WW_W6C=")
&& !strings.hasprefix(tracebase[i], "WW_W6A=")
&& !strings.hasprefix(tracebase[i], "WW_W6L=")) {
append(traceenv, tracebase[i]);
};
i += 1;
};
append(traceenv, strings.concat("WW_W6C=", cw));
append(traceenv, strings.concat("WW_W6A=", aw));
append(traceenv, strings.concat("WW_W6L=", lw));
append(traceenv, strings.concat("WW_CWD_REAL_W6C=", driver("w6c")));
append(traceenv, strings.concat("WW_CWD_REAL_W6A=", driver("w6a")));
append(traceenv, strings.concat("WW_CWD_REAL_W6L=", driver("w6l")));
append(traceenv, strings.concat("WW_CWD_TOOL_TRACE=", tooltrace));
let work: str = strings.concat(root, "/cwd-work");
assert(os.mkdir(work, 448i32) == 0);
let persistav: []str = [driver("ww"), "test", "-w", work, "-I", tree,
p1];
runcommandenvdir(root, "cwd-persist-cold", persistav, traceenv, caller,
(120i64 * (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 coldtrace: str = readfile(tooltrace);
assert(has(coldtrace, strings.concat("compiler cwd=", caller, "\n")));
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 argv <"));
assert(has(coldtrace, ".unit.new>"));
assert(has(coldtrace, "assembler argv <"));
assert(has(coldtrace, "linker argv <"));
assert(has(coldtrace, ".a.new>"));
let artifactpaths: []str = [
strings.concat(work, "/p1-internal-test.unit.ww"),
strings.concat(work, "/p1-internal-test.wwi"),
strings.concat(work, "/p1-internal-test.s"),
strings.concat(work, "/p1-internal-test.o"),
strings.concat(work, "/p1-internal-test.a"),
strings.concat(work, "/p1-test-main.unit.ww"),
strings.concat(work, "/p1-test-main.init.unit.ww"),
strings.concat(work, "/p1-test-main.wwi"),
strings.concat(work, "/p1-test-main.s"),
strings.concat(work, "/p1-test-main.init.s"),
strings.concat(work, "/p1-test-main.o"),
strings.concat(work, "/p1-test-main.init.o"),
strings.concat(work, "/p1-test-main.a"),
strings.concat(work, "/dep.wwi"),
strings.concat(work, "/dep.a"),
strings.concat(work, "/probe.wwi"),
strings.concat(work, "/probe.a")
];
let artifacts: []str = alloc([], artifactpaths.len: u64)!;
i = 0;
for (i < artifactpaths.len) {
append(artifacts, strings.dup(readfile(artifactpaths[i])));
i += 1;
};
rewritefile(tooltrace, "");
runcommandenvdir(root, "cwd-persist-warm", persistav, traceenv, caller,
(120i64 * (time.second: i64)): time.duration, &outw);
expectexit(&outw, 0);
assert(same(outc.stdout, outw.stdout));
let warmtrace: str = readfile(tooltrace);
assert(!has(warmtrace, "compiler cwd="));
assert(!has(warmtrace, "assembler cwd="));
assert(occurrences(warmtrace, "linker cwd=") == 1);
assert(has(warmtrace, strings.concat("linker cwd=", caller, "\n")));
// Non-source data does not enter graph/action/storage bytes. The established
// warm final relink remains one invocation, while the next uncached runtime
// observes the new relative data immediately.
rewritefile(strings.concat(p1, "/data.txt"), "p1-data-v2\n");
rewritefile(strings.concat(p1, "/testdata/input.txt"),
"p1-testdata-v2\n");
rewritefile(tooltrace, "");
runcommandenvdir(root, "cwd-persist-data", persistav, traceenv, caller,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
cwdassertpackage(out.stdout, "dep-init", p1,
"p1-data-v2", "p1-testdata-v2");
cwdassertpackage(out.stdout, "p1-internal", p1,
"p1-data-v2", "p1-testdata-v2");
let datatrace: str = readfile(tooltrace);
assert(!has(datatrace, "compiler cwd="));
assert(!has(datatrace, "assembler cwd="));
assert(occurrences(datatrace, "linker cwd=") == 1);
assert(has(datatrace, strings.concat("linker cwd=", caller, "\n")));
i = 0;
for (i < artifactpaths.len) {
assert(same(artifacts[i], readfile(artifactpaths[i])));
i += 1;
};
assert(!directoryhasnew(work));
assert(!os.exists(strings.concat(work, "/p1.result")));
assert(!os.exists(strings.concat(work, "/p1.output")));
assert(!os.exists(strings.concat(work, "/p1.status")));
assert(!os.exists(strings.concat(work, "/p1.voucher")));
let p1after: str = strings.concat(root, "/p1-after-data.test");
let afterav: []str = [driver("ww_ww"), "test", "-c", "-o", p1after,
"-I", tree, p1];
runcommandenvdir(root, "cwd-after-data-binary", afterav, wenv, caller,
(90i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 0);
assert(same(readfile(p1cbin), readfile(p1after)));
// The builder wrapper creates a deterministic post-build/pre-run boundary.
// Removing one owned source directory there must be a child setup error;
// the sibling still runs from its own directory and both stages agree.
let hidewrapper: str = strings.concat(root, "/build-then-hide.sh");
writeexecutable(hidewrapper, strings.concat(
"#!/bin/sh\n",
"\"$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",
" printf 'wrapper-hidden=%s\\n' \"$WW_CWD_HIDE_DIR\" >&2\n",
"fi\n",
"exit \"$status\"\n"));
let hidestages: []str = ["ww", "ww_ww"];
let hidestdout: []str = ["", ""];
let hidestderr: []str = ["", ""];
i = 0;
for (i < hidestages.len) {
let base: []str = cwdtestenv(hidestages[i]);
let hideenv: []str = alloc([], (base.len + 2): u64)!;
let j: i32 = 0;
for (j < base.len) { append(hideenv, base[j]); j += 1; };
append(hideenv, strings.concat("WW_CWD_REAL_DRIVER=",
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];
runcommandenvdir(root, strings.concat("cwd-hide-", hidestages[i]),
hideav, hideenv, caller,
(120i64 * (time.second: i64)): time.duration, &out);
expectexit(&out, 1);
assert(has(out.stderr, strings.concat("wrapper-hidden=", p1, "\n")));
assert(has(out.stderr, 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");
assert(os.rename(strings.concat(p1, ".gone"), p1) == 0);
assert(!os.exists(strings.concat(p1, ".gone")));
hidestdout[i] = strings.dup(out.stdout);
hidestderr[i] = strings.dup(out.stderr);
i += 1;
};
assert(same(hidestdout[0], hidestdout[1]));
assert(same(hidestderr[0], hidestderr[1]));
assert(!directoryhasnew(work));
assert(same(readfile(strings.concat(caller, "/sentinel")),
"parent-sentinel\n"));
assert(!os.exists(strings.concat(caller, "/created.txt")));
clean(root);
};
@test fn imported_dependency_tests_do_not_leak() void = {
let root: str = fresh();
let base: str = packagepath("dependency");