ww: put test result trailers on a new line

This commit is contained in:
2026-08-21 06:19:26 +09:00
parent 9b6b1100f4
commit b5fb478613
5 changed files with 281 additions and 9 deletions

View File

@@ -7047,6 +7047,149 @@ transaction, stage, workdir, and generated-file cleanup. Existing signal,
timeout, interruption, concurrent-driver, and public-output transaction owners
cover the unchanged mechanisms at those boundaries.
### 11.33 Implemented newline before directory test result trailers
When a directory-owned test product has emitted a nonempty combined capture
whose final byte is not newline, `ww test` now emits exactly one newline before
its existing `ok` or run-status `FAIL` trailer. Empty and already
newline-terminated captures gain no byte. The rule belongs only to the
coordinator boundary between completed test-process output and its result
trailer; it does not rewrite the capture or affect a route with no coordinator
trailer.
#### Pinned Go evidence and classification
The sole authority is official Go 1.26.5 at commit
`c19862e5f8415b4f24b189d065ed739517c548ba`:
- `(*runTestActor).Act` selects one output writer/buffer for a package test
([`cmd/go/internal/test/test.go`, lines 14361499](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1436-L1499)),
then assigns that same writer to the test command's stdout and stderr and
retains the resulting bytes as `out`
([lines 16611708](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1661-L1708)).
- On success, a nonempty `out` without a trailing newline receives one before
the `ok` record
([lines 17121732](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1712-L1732)).
On failure with partial output, the same check inserts one before `FAIL`
([lines 17331769](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1733-L1769)).
- Official `test_fail_newline.txt` asserts that buffered partial failure output
and `FAIL` begin on different lines, and that buffered verbose partial
success output and `ok` begin on different lines. It also records the
deliberate streaming-mode exception
([lines 335](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_fail_newline.txt#L3-L35)).
Those source rules and official script assertions are behavior directly
implemented or asserted by pinned Go. Applying the buffered-package boundary
to WW's directory-product capture is derived from that implementation: WW's
manifest-free directory coordinator likewise owns the completed combined bytes
and immediately appends a package result trailer. Excluding raw single-file
tests and later manual execution of retained binaries is also derived from the
pinned distinction: those WW routes have no coordinator-owned `ok` or `FAIL`
trailer to separate.
#### Fresh four-axis audit and direct pre-fix measurements
The bounded audit considered all four permanent axes and selected only this
test-output gap:
- **Go-like build:** pinned `(*ErrorReporter).errorUnresolved` gives an
undeclared main function its dedicated link error
([`cmd/link/internal/ld/errors.go`, lines 2965](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/errors.go#L29-L65)).
`TestUndefinedRelocErrors` directly requires build failure and that message
([`cmd/link/internal/ld/ld_test.go`, lines 1945](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/ld_test.go#L19-L45)),
using official `issue10978/main.go`, whose main function is absent
([lines 527](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/testdata/issue10978/main.go#L5-L27)).
Direct Cstage and WWstage `ww build -o /dev/null` probes of a selected WW
command package without `main` both exited `1`, produced the identical
`w6l: undefined reference to 'main'` then `ww: w6l failed` diagnostics, and
published nothing. This candidate was aligned.
- **Go-like test:** a selected external `*_test.ww` initializer wrote exactly
`partial-success` without newline and exited `0`. Both stages exited `0`,
wrote no stderr, and emitted 72 stdout bytes beginning
`partial-successok `. The corresponding initializer wrote exactly
`partial-failure` to stderr and exited `7`; both commands exited `1`, wrote
no coordinator stderr, and emitted 86 stdout bytes beginning
`partial-failureFAIL `. Full Cstage and WWstage captures were byte-identical.
These are directly measured pre-fix WW facts and establish the selected
externally observable difference.
- **Go-like package:** `MultiplePackageError` represents conflicting selected
package clauses and formats the two declarations
([`go/build/build.go`, lines 538548](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L538-L548));
the scanner creates it when selected files disagree
([lines 930967](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L930-L967)),
and `TestMultiplePackageImport` asserts the typed result and files
([`go/build/build_test.go`, lines 105124](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build_test.go#L105-L124)).
Direct WW directories declaring `alpha` and `beta` were rejected before any
producer by both stages with the same positioned conflict diagnostic. This
candidate was aligned.
- **Go-like import:** `loadImport` rejects a package declared `main` when it is
imported from another directory
([`cmd/go/internal/load/pkg.go`, lines 787805](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L787-L805));
official `import_main.txt` asserts the rule for builds and internal/external
tests
([lines 335](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/import_main.txt#L3-L35)).
Both WW stages rejected a direct dotted import of a package declared `main`
with the identical `ww: package cmdpkg is a program, not an importable
package` diagnostic. This candidate was aligned.
The direct Go source and official-test statements above are behavior directly
implemented or asserted by pinned Go. The WW command results are directly
measured behavior. The conclusion that the selected change is a runtime
presentation boundary, with no build, package, or import identity effect, is
derived from the pinned placement after command completion and before the
result record.
#### Ownership, final behavior, and preserved boundaries
`internal/wwpackage.pkgemitgroup` is the true owner because it alone has both
the completed product-local combined capture and knowledge that an existing
directory result trailer follows. It first emits the capture unchanged, then
emits one separator only when the capture is nonempty and its last byte is not
newline, then follows the established success or failure branch. The check is
shared by Cstage and WWstage and is independent for every canonically ordered
product, including internal, external, and combined variants; dependency
initialization; filters and list mode; concurrent products; and the private run
of a retained request.
Empty output does not acquire a leading blank line, and output already ending
in newline does not acquire a second one. A nonzero exit or signal retains its
existing process classification and `FAIL` text; only a preceding partial line
is terminated. Test-harness timeout and ordinary assertion output already end
in newline and therefore remain byte-identical. A child that cannot start has
no completed capture/trailer boundary in this function. Parent interruption,
producer failure, and load, compile, assemble, archive, link, or install
failure retain their existing diagnostics and precedence. Raw single-file
tests and later direct execution of retained binaries have no package
coordinator result trailer and retain their exact process bytes.
Loading, graph construction, action construction and scheduling, compiler,
assembler, archiver, linker, generated main, test executable, and retained
artifact bytes are unchanged. The separator is emitted after the private
process completes; it is not written into the capture, executable, interface,
archive, work record, or public destination. Running retention remains private
build, private run, then guarded install. Failed, signalled, interrupted, or
unstartable runs still publish nothing and preserve prior retained bytes.
Concurrent products retain separate capture files and canonical emission;
there is no shared mutable newline state. Existing process-group cleanup,
transaction rollback, temporary-root removal, and staged-file cleanup are
unchanged.
Physical source directories remain runtime/loader metadata only and do not
become package, import, graph, action, artifact, symbol, `.wwi`, publication,
or persistence identity. No persisted byte or key changes, so build workdir
format remains `18`, test workdir format remains `19`, and semantic storage
format remains `3`.
The WW-native `directory_test_trailer_starts_on_new_line` observer proves both
stages for unterminated stdout success, unterminated stderr failure,
unterminated signal output, already terminated output, and empty output. It
also covers concurrent products, a filter, list mode, running retained
publication, retained executable byte identity, and the unchanged raw/manual
routes; complete stdout and stderr from the concurrent Cstage and WWstage runs
must match exactly. Existing package execution, timeout, interruption,
transaction, persistence, byte-identity, and cleanup owners continue to prove
the mechanisms this slice does not alter.
## 12. Candidate architectures and hard-gate decision
Five candidates were developed as coherent systems, not as feature bins.