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

@@ -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 6475 and 411440](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 16611697](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=<dir>` without
replacing inherited entries
([`cmd/go/internal/base/env.go`, lines 1527](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 12461308](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 6376 and 395402](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 436451, 521525, and 612624](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 5798 and 194305](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 120](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 338](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=<pkggroup.dir>`. 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.