test: prove package-source test execution cwd
This commit is contained in:
@@ -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=<dir>` 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=<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.
|
||||
|
||||
21
docs/spec.md
21
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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user