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.

View File

@@ -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)

View File

@@ -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