diff --git a/docs/build-system.md b/docs/build-system.md
index a5efa78f..ee6459b0 100644
--- a/docs/build-system.md
+++ b/docs/build-system.md
@@ -5746,6 +5746,188 @@ invalid UTF-8, ignored files, and absence of normalization; zero-tool
rejection; multi-product publication isolation; cold/warm add-remove reuse;
exact artifact preservation; and Cstage/WWstage diagnostic and byte identity.
+### 11.24 Implemented package-source test execution directory
+
+Every coordinator-executed directory-package test product now runs its one
+generated binary from the canonical physical source directory of the selected
+package. The child also receives the corresponding effective `PWD`. This is
+runtime metadata for the directory product, not canonical package or action
+identity.
+
+#### Pinned Go evidence and pre-fix WW behavior
+
+The authority is official Go 1.26.5 at commit
+`c19862e5f8415b4f24b189d065ed739517c548ba`:
+
+- `go test` documents that each listed package is tested by a separate binary,
+ that `testdata` is ignored by package discovery so it can hold ancillary
+ files, and that a command-run binary executes in the corresponding package
+ source directory
+ ([`cmd/go/internal/test/test.go`, lines 64–75 and 411–440](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L411-L440)).
+ The same text says that a generated test binary invoked directly may require
+ the user to enter that directory first; the source directory is not embedded
+ in the executable.
+- `runTestActor.Act` creates the command, assigns
+ `cmd.Dir = a.Package.Dir`, clips the original environment, appends `PATH`,
+ calls `base.AppendPWD(env, cmd.Dir)`, assigns the environment, attaches
+ output, and runs the command
+ ([`test.go`, lines 1661–1697](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1661-L1697)).
+ The run action retains the original package rather than deriving a directory
+ from `ptest`, `pxtest`, or `pmain`.
+- `AppendPWD` requires an absolute directory and appends `PWD=
` without
+ replacing inherited entries
+ ([`cmd/go/internal/base/env.go`, lines 15–27](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/base/env.go#L15-L27)).
+ Go's `os/exec` applies last-value-wins duplicate elimination to an explicit
+ environment by scanning backward, retaining the last key, and restoring the
+ surviving order
+ ([`os/exec/exec.go`, lines 1246–1308](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/os/exec/exec.go#L1246-L1308)).
+ On the pinned Linux boundary, all inherited exact uppercase `PWD=` entries
+ are therefore superseded by the appended package value. Keys remain
+ case-sensitive; `pwd=` and malformed non-key entries are not `PWD`.
+- Loader `Package.Dir` is the source directory and remains a separate field
+ from `ImportPath`; the command loader copies it from `go/build.Package.Dir`
+ ([`cmd/go/internal/load/pkg.go`, lines 63–76 and 395–402](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L63-L76)).
+ `go/build` likewise owns source location separately from import identity
+ ([`go/build/build.go`, lines 436–451, 521–525, and 612–624](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L436-L451)).
+- Official scripts use files, directories, and executable fixtures relative to
+ the tested package and mutate ordinary data between test invocations
+ ([`test_cache_inputs.txt`, lines 57–98 and 194–305](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_cache_inputs.txt#L57-L98));
+ exercise recursive discovery from a symlink root without following nested
+ directory symlinks
+ ([`list_symlink_dotdotdot.txt`, lines 1–20](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/list_symlink_dotdotdot.txt#L1-L20));
+ and keep multi-package compile-only output separate from execution
+ ([`test_compile_multi_pkg.txt`, lines 3–38](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_compile_multi_pkg.txt#L3-L38)).
+
+Before this slice, both WW stages ran directory products from the coordinator's
+invocation directory. With deliberately duplicated inherited entries,
+`getcwd` returned that caller directory while WW's first-match `os.getenv`
+returned the first unrelated `PWD`. `data.txt` and `testdata/input.txt` were
+therefore read from the caller, and relative writes from parallel products
+collided there. Production and test-only dependency initializers inherited the
+same incorrect process context. Direct, recursive, redundant, absolute, and
+root-symlink spellings already converged on one product but did not use its
+stored directory for execution. Cstage and WWstage had identical pre-fix
+output and binaries.
+
+#### Directory-product ownership and identity separation
+
+The package coordinator already canonicalizes each selected source directory
+to one absolute, symlink-free physical spelling, rebuilds its selected source
+paths below that directory, sorts and deduplicates them, and stores the result
+as `pkggroup.dir`. Relative, absolute, redundant-component, direct, recursive,
+reversed-root, filesystem-order, and root-symlink requests that reach one
+package therefore retain the same physical product directory. WW's deliberate
+applicability boundary differs from Go only where Go preserves a loader-owned
+symlink spelling: WW uses its already specified canonical physical spelling.
+
+That directory is an execution-context field. Exact case-sensitive dotted
+package identity still owns graph interning, import bindings, mangled symbols,
+`.wwi` ownership, action and storage keys, archives, diagnostics, and link
+closure. Declared package name, source alias, path leaf, filename, artifact
+basename, output path, test action name, and physical-directory equality do
+not become canonical identity. No physical path was added to a unit, export,
+symbol, archive, generated main, action digest, product name, status, voucher,
+stamp, or persistence key.
+
+One `pkggroup` owns the process for production plus internal tests,
+production plus external tests, the combined shape, internal-only,
+external-only, and mixed test-only directories. Production `p`, augmented
+`ptest`, external `pxtest`, product-scoped recompiled actions, support, and
+generated main remain separate actions and derive no independent cwd. The one
+directory product supplies its `dir` to the one executed binary.
+
+#### Child cwd, environment, and concurrency
+
+Only `pkgstartrun` sets the existing `exec.command.dir` to `pkggroup.dir`.
+`lib/os/exec` opens the absolute stdout/stderr captures in the parent, copies
+argv and environment, forks, and calls `chdir` only in the child immediately
+before `execve`. The executable, `argv[0]`, captures, product scratch, and
+coordinator publication paths are absolute, so the child directory cannot
+reinterpret them. No runtime coordinator-global `chdir` was added; its cwd and
+`PWD` remain unchanged.
+
+Each started product also receives a newly allocated run environment. It
+retains inherited entries in order except exact `TMPDIR=`, `LC_ALL=`, and
+uppercase `PWD=` entries, then appends `LC_ALL=C`, the product-local absolute
+`TMPDIR`, and `PWD=`. Removing prior `PWD` entries reproduces
+Go's observable last-wins result because WW's executor intentionally preserves
+duplicates and WW `os.getenv` returns the first one. Case-distinct and malformed
+entries remain untouched. The product observes exactly one uppercase `PWD`, at
+the appended position.
+
+The vector and both generated strings are product-local, dynamically sized,
+and published only after every checked allocation succeeds. Partial failure
+frees only initialized owned storage and never frees borrowed inherited
+strings. `exec.start` synchronously deep-copies the command before returning,
+after which the coordinator frees its run vector, generated `TMPDIR`, generated
+`PWD`, generated `-package` argument, and argv vector. Concurrent children
+therefore hold independent fork snapshots; no shared environment vector or
+process-global state is mutated.
+
+All dependency initialization occurs inside that product process. A production
+or test-only dependency reached by package `p` sees `p`'s directory. If the
+dependency is separately selected as its own test product, that second process
+sees the dependency's directory. Filters, no-match filters, and list mode use
+the same binary and context whenever they execute. With multiple products and
+`-j N`, each child independently observes its own directory and fixture names;
+emission remains byte-sorted and identical to `-j 1`.
+
+#### Nonexecution paths, tools, and direct binaries
+
+`ww build` starts no test process. Directory `ww test -c`, including
+`-c -o`, builds or publishes but never enters `pkgstartrun`; no execution cwd
+or run environment is allocated. A published binary subsequently invoked by
+the user bypasses the coordinator and inherits the user's cwd and environment.
+The raw single-file test compatibility route is likewise unchanged. A
+directory with no selected test source still creates no support, generated
+main, link, run, result, or execution-context state.
+
+Build-plan commands retain `dir=""` and their existing tool environment.
+Compiler, assembler, in-driver archiver, linker, support generation, generated
+main construction, the Cstage driver, and the WWstage driver therefore retain
+their exact prior cwd, argv, and environment. The runtime rule required no
+`lib/os/exec`, compiler, checker, writer, assembler, linker, or driver change.
+Independent Cstage/WWstage compile-only products remain byte-identical, and
+changing only `data.txt` or `testdata` changes no unit, `.wwi`, assembly,
+object, archive, generated-main, or binary bytes.
+
+#### Failure, cleanup, persistence, and proof
+
+A missing product directory after a successful build fails the child's
+`chdir`. The executor reports the positive errno through its setup marker as
+`termination.ERROR`, distinct from a program exit 127. The coordinator emits
+`FAIL DIR [package] (test harness error ERRNO)`, treats it as execution/setup
+failure rather than loader failure, retains successful compilation, continues
+independently schedulable siblings, and removes its owned captures and scratch
+under the existing execution-failure contract. It never changes the parent or
+a sibling's cwd/environment.
+
+Execution cwd and `PWD` are request-time process metadata. There is no test
+result cache, and runtime failure does not invalidate already committed build
+artifacts. A warm persistent request still performs the established final
+relink, but no compiler or assembler work; changing only fixture data causes no
+additional producer work or persistent-byte change and the next always-run
+test immediately observes the new data. Build workdir format remains `18`,
+test workdir format remains `19`, and semantic storage remains `3`.
+
+The focused native owner is
+`directory_test_execution_working_directory` in
+`test/package/package_test.ww`. It creates only disposable source trees and
+compares Cstage and WWstage across duplicate and large environments; exact
+`getcwd`, effective/count/position of `PWD`; ordinary data, `testdata`, and
+relative writes; all production/internal/external/test-only shapes;
+production and test-only dependency initialization; recompiled external
+self-import; direct/recursive/redundant/absolute/symlink roots; reversed roots
+and creation order; `-j 1`/parallel execution; filters/list/no-match; failure
+and timeout; no-test and build paths; `-c`, `-c -o`, direct binaries, and raw
+single files; exact tool cwd/argv/locale/TMPDIR; persistent data-only reuse and
+artifact/binary identity; and a deterministic post-build directory removal
+where the affected product reports `ENOENT` while its sibling succeeds.
+Checked command-global allocation-failure parity remains owned by
+`allocation_failure_is_command_global`; the focused observer additionally
+crosses the former fixed environment-size boundary and verifies that no
+partial execution environment or staged `.new` state is published.
+
## 12. Candidate architectures and hard-gate decision
Five candidates were developed as coherent systems, not as feature bins.
diff --git a/docs/spec.md b/docs/spec.md
index 122e2ebc..9bc42234 100644
--- a/docs/spec.md
+++ b/docs/spec.md
@@ -645,6 +645,27 @@ mixed directories are accepted from their selected test files. A directory
with no selected test file validates ordinary production but creates no test
support, generated main, link, binary, result, or process.
+When `ww test` executes a directory-owned product, the child process working
+directory is that product's canonical absolute physical package source
+directory. Its per-run environment has exactly one effective uppercase `PWD`,
+with that same value. The physical source directory is execution context, not
+canonical package identity: it does not enter dotted identity, import binding,
+action identity, mangled symbols, `.wwi`, artifacts, or persistence keys.
+Production, internal-test, external-test, recompiled-for-test, support, and
+generated-main actions in the product share the one process context while
+remaining separate actions. Reachable dependency initialization consequently
+observes the tested product's directory; a dependency tested as its own product
+observes its own directory.
+
+The coordinator does not change its own cwd or environment. Parallel products
+receive independent child environments and each uses its own source directory.
+Relative ordinary files, `testdata`, and writes resolve there for every
+executing filter or list path. `ww build`, directory `ww test -c` (including
+`-c -o`), and a no-selected-test directory execute no test child and receive no
+execution-directory effect. A published test binary invoked directly, and the
+raw single-file compatibility route, inherit the user's invocation cwd and
+environment; no package directory is embedded or forced by the binary.
+
---
## 11. Concurrency (reserved)
diff --git a/docs/test-system-v2.md b/docs/test-system-v2.md
index 0a39f2c8..c80159ef 100644
--- a/docs/test-system-v2.md
+++ b/docs/test-system-v2.md
@@ -225,10 +225,32 @@ including a combined internal/external directory:
the coordinator rejects a multi-package fan-out ("cannot use -o with
multiple packages", Go's `go test -o` rule), and `-o` without `-c` is
rejected at the driver ("needs -c for a package target") because a plain
-run always executes from the temp root. Without `-c`, it removes the
+run leaves no caller-owned artifact for `-o` to name. Without `-c`, it removes the
temporary binary and scratch with its workspace. The language runtime
owns individual `@test` functions.
+Every actually executed directory product gives its single generated binary
+the product's canonical absolute physical source directory as child cwd. A
+fresh per-run environment removes inherited exact uppercase `PWD` entries and
+appends one `PWD` with that same directory, matching Go 1.26.5's observable
+`AppendPWD` plus last-value-wins `os/exec` behavior. The coordinator's cwd and
+environment do not change, and overlapping `-j N` children share no writable
+environment vector. Relative ordinary data, `testdata`, and writes resolve in
+the tested directory. Production and test-only dependency initializers run in
+that product process and see its directory; separately testing the dependency
+creates a separate process using the dependency directory. Equivalent direct,
+recursive, redundant, absolute, and root-symlink spellings converge before
+this runtime field is assigned.
+
+The physical directory remains distinct from exact dotted package identity and
+from production, internal, external, recompiled, support, and generated-main
+action identity. Only product execution uses it. Compiler, assembler, archiver,
+linker, support, and generated-main commands retain their build-plan cwd and
+environment. `ww build`, `ww test -c`, `-c -o`, and no-selected-test products
+start no test child. A published test binary run directly and the raw
+single-file compatibility path inherit the user's cwd/environment and contain
+no forced package-directory behavior.
+
Before package grouping, both directory drivers and the shared recursive
coordinator apply Go 1.26.5's filename OS/architecture rule for WW's fixed
`linux/amd64` target. The basename stem ends at its first dot; a final `_test`
@@ -291,6 +313,23 @@ exact-argv, command, and persistent-workdir observers, the package suite proves
archive-only link argv and exact warm/rejection-state behavior without
duplicating those broader mechanisms in this observer.
+The same package owner contains the focused
+`directory_test_execution_working_directory` observer. It constructs
+independent temporary directories from an unrelated caller cwd and compares
+Cstage/WWstage output for production/internal/external/combined and every
+test-only shape; production and test-only dependency initialization;
+recompiled external self-import; duplicate inherited `PWD`; ordinary data,
+`testdata`, and relative writes; direct/recursive/redundant/absolute/symlink
+roots; reversed request and creation order; serial and parallel products;
+filters, list, and no-match execution; failure and timeout; build/no-test and
+compile-only paths; direct published binaries and raw single-file execution;
+build-tool cwd/argv/environment; persistent data-only reuse and artifact bytes;
+and deterministic post-build child-`chdir` failure isolated from a successful
+sibling. It pads the inherited environment beyond former fixed observer sizes,
+requires one appended product `PWD`, and sweeps the persistent workdir for
+staged residue. Command-global bounded-memory failure remains independently
+owned by `allocation_failure_is_command_global`.
+
The same package owner contains the focused
`platform_filename_source_selection` observer. It independently generates the
suffix matrix, production/internal/external sources, wrong-target import and
@@ -387,6 +426,14 @@ real directory test products always run, while no-selected-test directories do
not create a process. The byte-identity and bootstrap gates keep building on
fresh scratch. `make clean` reclaims every workdir under `out/`.
+Directory-product cwd and `PWD` are request-time child-process metadata, not
+persisted action inputs or results. A runtime directory-entry failure therefore
+does not erase a successfully committed compilation generation. Changing only
+ordinary fixture data causes no unit/export/assembly/object/archive/main/binary
+change and no producer work beyond the same established warm final relink,
+while the next always-run product observes the new bytes. Build workdir format
+remains 18, test workdir format remains 19, and semantic storage remains 3.
+
Fold keys and first-spelling tables are request-only loader state and are never
stored in a workdir. They do not alter successful unit bytes, action/storage
keys, or tool records, so build format 18, test format 19, and semantic storage
diff --git a/test/package/package_test.ww b/test/package/package_test.ww
index cfc46b48..91e66773 100644
--- a/test/package/package_test.ww
+++ b/test/package/package_test.ww
@@ -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(\"\\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(\"\\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(\"\");\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(\"\\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");