ww test: report no tests to run
This commit is contained in:
@@ -7635,8 +7635,9 @@ name per selected descriptor and emits no harness list bytes when its filters
|
|||||||
select zero tests. The test product still starts, package initialization still
|
select zero tests. The test product still starts, package initialization still
|
||||||
runs, the harness returns success without accounting, and the coordinator still
|
runs, the harness returns success without accounting, and the coordinator still
|
||||||
emits the normal package `ok` result. Ordinary non-list execution with zero
|
emits the normal package `ok` result. Ordinary non-list execution with zero
|
||||||
selected tests remains distinct: it keeps `[no matches]` and its
|
selected tests remains distinct: it keeps its
|
||||||
discovered/selected/started/completed accounting.
|
discovered/selected/started/completed accounting and, as implemented later in
|
||||||
|
section 11.39, uses the pinned no-tests warning and package-result suffix.
|
||||||
|
|
||||||
#### Pinned Go evidence and fact classification
|
#### Pinned Go evidence and fact classification
|
||||||
|
|
||||||
@@ -7695,7 +7696,9 @@ runtime difference:
|
|||||||
([`go/build/build.go`, lines 538–549](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L538-L549)
|
([`go/build/build.go`, lines 538–549](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L538-L549)
|
||||||
and [939–967](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L939-L967)),
|
and [939–967](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L939-L967)),
|
||||||
asserted by `TestMultiplePackageImport` and official `testdata/multi`
|
asserted by `TestMultiplePackageImport` and official `testdata/multi`
|
||||||
([`go/build/build_test.go`, lines 105–133](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build_test.go#L105-L133)).
|
([`go/build/build_test.go`, lines 105–133](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build_test.go#L105-L133),
|
||||||
|
[`testdata/multi/file.go`, lines 1–5](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/testdata/multi/file.go#L1-L5),
|
||||||
|
and [`file_appengine.go`, lines 1–5](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/testdata/multi/file_appengine.go#L1-L5)).
|
||||||
Both WW stages rejected an `alpha`/`beta` directory with zero stdout and the
|
Both WW stages rejected an `alpha`/`beta` directory with zero stdout and the
|
||||||
same 159 stderr bytes (SHA-256
|
same 159 stderr bytes (SHA-256
|
||||||
`eaaa0c91f41b5d3deac4caf4299d5c7c650a1330be9b43edd090b8bfea906076`).
|
`eaaa0c91f41b5d3deac4caf4299d5c7c650a1330be9b43edd090b8bfea906076`).
|
||||||
@@ -7742,7 +7745,8 @@ initialization order, symbols, and publication names keep their existing roles.
|
|||||||
The product process and package initialization still run in list mode; no
|
The product process and package initialization still run in list mode; no
|
||||||
per-test child starts. Positive matching, option diagnostics and precedence,
|
per-test child starts. Positive matching, option diagnostics and precedence,
|
||||||
ordinary non-list no-match output, no-test-file results, and raw-file rejection
|
ordinary non-list no-match output, no-test-file results, and raw-file rejection
|
||||||
are unchanged.
|
were unchanged by this list-only slice; section 11.39 subsequently changes only
|
||||||
|
the ordinary no-match warning and successful result annotation.
|
||||||
|
|
||||||
The shared support implementation change legitimately changes its object,
|
The shared support implementation change legitimately changes its object,
|
||||||
archive, and linked test-product bytes. Its exported signature and `.wwi` byte
|
archive, and linked test-product bytes. Its exported signature and `.wwi` byte
|
||||||
@@ -7766,7 +7770,8 @@ initialization, cold and warm persistent work, absence of list/accounting
|
|||||||
sentinels, positive deterministic selection, running `-o` retention, later
|
sentinels, positive deterministic selection, running `-o` retention, later
|
||||||
direct execution, stage stdout/stderr equality, retained executable byte
|
direct execution, stage stdout/stderr equality, retained executable byte
|
||||||
identity, and `.new` cleanup. The existing routing observer separately keeps
|
identity, and `.new` cleanup. The existing routing observer separately keeps
|
||||||
ordinary non-list `[no matches]` plus accounting pinned.
|
the section-11.37 baseline for ordinary non-list reporting; section 11.39
|
||||||
|
supersedes that private marker while retaining the accounting.
|
||||||
|
|
||||||
No persisted-byte contract changed: build workdir format remains `18`, test
|
No persisted-byte contract changed: build workdir format remains `18`, test
|
||||||
workdir format remains `19`, and semantic storage format remains `3`.
|
workdir format remains `19`, and semantic storage format remains `3`.
|
||||||
@@ -7946,6 +7951,174 @@ check introduces no independently interruptible or shared state.
|
|||||||
No valid persisted-byte contract changed. Build workdir format remains `18`,
|
No valid persisted-byte contract changed. Build workdir format remains `18`,
|
||||||
test workdir format remains `19`, and semantic storage format remains `3`.
|
test workdir format remains `19`, and semantic storage format remains `3`.
|
||||||
|
|
||||||
|
### 11.39 Implemented ordinary zero-match test results
|
||||||
|
|
||||||
|
An ordinary successful directory `ww test` whose valid `-run`/`-filter`
|
||||||
|
selection starts no registered test now uses Go's externally visible no-tests
|
||||||
|
protocol. The shared test runtime writes exactly
|
||||||
|
`testing: warning: no tests to run\n` through standard error, retains WW's
|
||||||
|
discovered/selected/started/completed accounting, and returns success. The
|
||||||
|
directory coordinator recognizes that exact line at capture byte zero or after
|
||||||
|
a newline and appends ` [no tests to run]` to the corresponding successful
|
||||||
|
package `ok` result. WW's former `[no matches]` sentinel is no longer emitted.
|
||||||
|
|
||||||
|
This is deliberately distinct from the completed list-mode rule: list mode
|
||||||
|
returns before the warning/accounting branch and retains an unsuffixed package
|
||||||
|
result. A source-bearing directory without test files also retains its separate
|
||||||
|
`? <package> [no test files]` result and starts no runtime product.
|
||||||
|
|
||||||
|
#### Pinned Go evidence and fact classification
|
||||||
|
|
||||||
|
The sole semantic authority is official Go 1.26.5 at commit
|
||||||
|
`c19862e5f8415b4f24b189d065ed739517c548ba`:
|
||||||
|
|
||||||
|
- `testing.(*M).Run` returns directly from list mode at lines 2407–2411. In
|
||||||
|
ordinary execution it gathers whether tests, examples, or fuzz targets ran,
|
||||||
|
writes exactly `testing: warning: no tests to run` to stderr when none did,
|
||||||
|
and keeps the outcome successful when no independent failure occurred
|
||||||
|
([`testing/testing.go`, lines 2432–2485](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/testing/testing.go#L2432-L2485)).
|
||||||
|
- `cmd/go` defines the line-delimited `noTestsToRun` marker
|
||||||
|
([`cmd/go/internal/test/test.go`, line 1385](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1385))
|
||||||
|
and, after a successful test process, recognizes it at capture byte zero or
|
||||||
|
after a newline and appends ` [no tests to run]` to the package result
|
||||||
|
([method `(*runTestActor).Act`, lines 1706–1732](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1706-L1732)).
|
||||||
|
- Official script/testdata `test_match_no_tests.txt` runs one registered test
|
||||||
|
through a nonmatching filter and asserts the suffixed successful package
|
||||||
|
result
|
||||||
|
([lines 1–11](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_match_no_tests.txt#L1-L11)).
|
||||||
|
- Official precedence script/testdata `test_match_no_tests_build_failure.txt`
|
||||||
|
asserts that a build failure under a nonmatching filter produces `FAIL` and
|
||||||
|
does not acquire a successful no-tests result
|
||||||
|
([lines 1–18](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_match_no_tests_build_failure.txt#L1-L18)).
|
||||||
|
|
||||||
|
Those branches, exact bytes, success conditions, delimiter checks, result
|
||||||
|
suffix, and script assertions are **behavior directly implemented or asserted
|
||||||
|
by pinned Go**. That the runtime owns whether a test ran, the coordinator owns
|
||||||
|
the package-result annotation, a build failure precedes both, and an arbitrary
|
||||||
|
mid-line substring is not the marker are **behavior derived from the pinned
|
||||||
|
implementation**.
|
||||||
|
|
||||||
|
WW retains its fnmatch-based local `-run`/`-filter` language rather than
|
||||||
|
adopting Go regular expressions. WW also has an established always-visible
|
||||||
|
harness report rather than Go's quiet/`-v` presentation switch, so this slice
|
||||||
|
does not suppress every successful product capture or replace WW accounting
|
||||||
|
with Go's `PASS` line. Within that honest local presentation boundary, the
|
||||||
|
zero-execution warning, stream owner, success classification, marker delimiter,
|
||||||
|
and package annotation apply directly.
|
||||||
|
|
||||||
|
#### Fresh four-axis audit and direct pre-fix measurements
|
||||||
|
|
||||||
|
The bounded audit examined all four permanent axes before selecting this test
|
||||||
|
runtime/coordinator difference. Commands used Cstage `out/bin/ww` and WWstage
|
||||||
|
`out/bin/ww_ww` against identical sources:
|
||||||
|
|
||||||
|
- **Go-like build:** pinned linker method
|
||||||
|
`(*ErrorReporter).errorUnresolved` gives missing `main.main` a dedicated
|
||||||
|
failure ([`cmd/link/internal/ld/errors.go`, lines 29–67](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/errors.go#L29-L67)),
|
||||||
|
asserted by `TestUndefinedRelocErrors` and official `issue10978`
|
||||||
|
([`cmd/link/internal/ld/ld_test.go`, lines 19–45](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/ld_test.go#L19-L45),
|
||||||
|
[`testdata/issue10978/main.go`, lines 5–27](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/testdata/issue10978/main.go#L5-L27)).
|
||||||
|
Both WW stages rejected a declared-main package with no entry, emitted empty
|
||||||
|
stdout and the same 50 stderr bytes (SHA-256
|
||||||
|
`9ed4d7684412c6d2e615041902072c81e9e09acb3970246d89a2c8bdddd2fcfa`),
|
||||||
|
and published nothing. This applicable build property was aligned.
|
||||||
|
- **Go-like test:** with one registered test, both stages exited 0 for
|
||||||
|
`test -run no-such-*`, emitted empty stderr, and emitted the same 120 stdout
|
||||||
|
bytes (SHA-256
|
||||||
|
`ca12f88ebf1f94d3a2ca63ed1bc1e9b5a4811a660df3af4e70c7b9624ef97c40`):
|
||||||
|
`[no matches]`, zero-selection accounting, and an unsuffixed package `ok`.
|
||||||
|
This private marker and missing result annotation were the selected gap.
|
||||||
|
Empty list selection and positive ordinary selection were already aligned
|
||||||
|
controls and stayed outside the changed branch.
|
||||||
|
- **Go-like package:** pinned `MultiplePackageError` and directory scanning
|
||||||
|
reject conflicting declarations
|
||||||
|
([`go/build/build.go`, lines 538–549](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L538-L549)
|
||||||
|
and [939–967](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L939-L967)),
|
||||||
|
asserted by `TestMultiplePackageImport` and official `testdata/multi`
|
||||||
|
([`go/build/build_test.go`, lines 105–133](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build_test.go#L105-L133)).
|
||||||
|
Both WW stages rejected one `first`/`second` directory with empty stdout and
|
||||||
|
byte-identical 161-byte stderr (SHA-256
|
||||||
|
`a2d95681b8a97d55c26367084cd294632fa015882d1aa53b0df63f24bcf24ced`).
|
||||||
|
This applicable package property was aligned.
|
||||||
|
- **Go-like import:** pinned `unusedImports` and `errorUnusedPkg` reject every
|
||||||
|
nonblank unused import
|
||||||
|
([`cmd/compile/internal/types2/resolver.go`, lines 706–740](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/compile/internal/types2/resolver.go#L706-L740)),
|
||||||
|
asserted by official `importdecl0a.go`
|
||||||
|
([lines 9–26](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/importdecl0/importdecl0a.go#L9-L26)).
|
||||||
|
Both stages rejected an unused dotted `fmt` import with empty stdout and the
|
||||||
|
same semantic diagnostic;
|
||||||
|
raw stderr differed only in the deliberately stage-named private output path.
|
||||||
|
This applicable import property was aligned.
|
||||||
|
|
||||||
|
The WW statuses, streams, lengths, hashes, and diagnostics are **directly
|
||||||
|
measured WW behavior**. The linked official implementation and testdata are
|
||||||
|
**behavior directly implemented or asserted by pinned Go**. Applying the
|
||||||
|
runtime/coordinator split without changing WW's filter syntax or harness report
|
||||||
|
is **behavior derived from the pinned implementation**.
|
||||||
|
|
||||||
|
#### Ownership, final behavior, and preserved boundaries
|
||||||
|
|
||||||
|
`lib/test/run.ww` is the runtime owner. Its existing `selected == 0` branch now
|
||||||
|
writes the pinned warning through the same EINTR-safe fd writer used elsewhere,
|
||||||
|
targeting stderr, then writes the unchanged accounting to stdout and returns 0.
|
||||||
|
Its earlier list return is untouched. A directly invoked retained binary
|
||||||
|
therefore exposes the warning on stderr and accounting on stdout.
|
||||||
|
|
||||||
|
`internal/wwpackage/package.ww` is the directory result owner. It already gives
|
||||||
|
each product one combined stdout/stderr capture and emits that capture in
|
||||||
|
canonical group order. Its new predicate accepts only the exact warning at byte
|
||||||
|
zero or following `\n`; after `pkgrunok` succeeds, `pkgemitgroup` appends the
|
||||||
|
suffix to the result it already owns. Text embedded mid-line in a running test
|
||||||
|
does not match. A failed, signalled, timed-out, interrupted, or unstartable test
|
||||||
|
does not reach the successful result. A producer failure never starts the
|
||||||
|
runtime and cannot synthesize the warning or suffix.
|
||||||
|
|
||||||
|
Both selected driver stages compile the same `lib/test` code into test products
|
||||||
|
and delegate directory execution to the same WW-native coordinator, so no
|
||||||
|
C-only or self-host-only semantic fork was introduced. Direct post-fix probes
|
||||||
|
through both stages exited 0, emitted empty coordinator stderr, and emitted the
|
||||||
|
same 159 stdout bytes (SHA-256
|
||||||
|
`a67242ab79b9bd9bca1a32073c1fccbb9aa4fa9d8ad52ced667b25c54dc1be08`):
|
||||||
|
the warning, unchanged accounting, and suffixed package result.
|
||||||
|
|
||||||
|
Loading and Go-platform source selection are unchanged. Production,
|
||||||
|
internal-test, external-test, recompiled-for-test, support, and generated-main
|
||||||
|
graph nodes and actions are unchanged. Compilers, assemblers, archivers, and
|
||||||
|
linkers retain their diagnostics and scheduling. The support implementation
|
||||||
|
change legitimately changes its object/archive and linked test-product bytes,
|
||||||
|
but its exported signature and `.wwi` contract do not change; valid Cstage and
|
||||||
|
WWstage retained products remain byte-identical.
|
||||||
|
|
||||||
|
Declared package names and canonical dotted import identities remain separate.
|
||||||
|
The coordinator annotates an already-owned result; it derives no identity from
|
||||||
|
the warning, alias, declared name, path leaf, filename, physical directory,
|
||||||
|
output path, artifact name, or linker order. Physical directories remain test
|
||||||
|
cwd and result-label metadata only, never package/import/graph/action/artifact/
|
||||||
|
symbol/`.wwi`/publication/persistence identity.
|
||||||
|
|
||||||
|
Cold and warm persistent work produce identical result bytes and still run the
|
||||||
|
test product because there is no test-result cache. A successful retained run
|
||||||
|
publishes the privately tested executable through the existing guarded install.
|
||||||
|
A later producer failure preserves prior public bytes, and restored valid reuse
|
||||||
|
reproduces the same warning/suffix without rewriting an identical executable.
|
||||||
|
Publication rejection, rollback, existing-output preservation, concurrency,
|
||||||
|
interruption, process-group cancellation, capture separation, final `FAIL`, and
|
||||||
|
cleanup remain with their existing owners. No active `.new`, `.install`,
|
||||||
|
`.wwtxn.*`, adjacent `.sepwork`, process, or capture residue is introduced.
|
||||||
|
|
||||||
|
The WW-native `ordinary_no_match_uses_go_warning_and_result_suffix` observer
|
||||||
|
proves both stages; cold/warm reuse; concurrent reverse-requested packages and
|
||||||
|
ordered per-product markers; exact mid-line rejection; positive, list, and
|
||||||
|
no-test-files controls; retained publication and direct stderr ownership;
|
||||||
|
producer-failure precedence; cold/repeated failure; prior-output preservation;
|
||||||
|
restored reuse; normalized diagnostic parity; retained executable byte identity;
|
||||||
|
and transaction/output-scratch cleanup. Existing signal, timeout, interruption,
|
||||||
|
and process-group observers continue to prove those unchanged mechanisms.
|
||||||
|
|
||||||
|
This is runtime/coordinator presentation, not a persisted-byte contract. Build
|
||||||
|
workdir format remains `18`, test workdir format remains `19`, and semantic
|
||||||
|
storage format remains `3`.
|
||||||
|
|
||||||
## 12. Candidate architectures and hard-gate decision
|
## 12. Candidate architectures and hard-gate decision
|
||||||
|
|
||||||
Five candidates were developed as coherent systems, not as feature bins.
|
Five candidates were developed as coherent systems, not as feature bins.
|
||||||
|
|||||||
18
docs/spec.md
18
docs/spec.md
@@ -828,12 +828,18 @@ List mode starts the same directory-owned product and therefore performs its
|
|||||||
ordinary package initialization, but it does not start any selected test child.
|
ordinary package initialization, but it does not start any selected test child.
|
||||||
The harness emits exactly one newline-terminated qualified name for each test
|
The harness emits exactly one newline-terminated qualified name for each test
|
||||||
selected by the command's filters, in descriptor order. If the selected set is
|
selected by the command's filters, in descriptor order. If the selected set is
|
||||||
empty, the harness emits no list payload, no `[no matches]` sentinel, and no
|
empty, the harness emits no list payload, warning, or accounting and returns
|
||||||
accounting, and returns success; the coordinator still emits the ordinary
|
success; the coordinator still emits the ordinary successful package result
|
||||||
successful package result. This does not alter non-list execution: a valid
|
without a no-tests suffix. Ordinary non-list execution is distinct: a valid
|
||||||
filter selecting no tests continues to emit `[no matches]` and the
|
filter selecting no tests writes exactly
|
||||||
discovered/selected/started/completed accounting. A retained binary invoked
|
`testing: warning: no tests to run\n` through the harness's standard error,
|
||||||
directly has no coordinator package result.
|
preserves the discovered/selected/started/completed accounting and successful
|
||||||
|
exit, and causes a successful directory package result to end in
|
||||||
|
` [no tests to run]`. The coordinator recognizes that exact warning only at
|
||||||
|
capture byte zero or after a newline. Directory execution combines the warning
|
||||||
|
and accounting in its product-local capture; a retained binary invoked directly
|
||||||
|
keeps the warning on standard error, its accounting on standard output, and has
|
||||||
|
no coordinator package result.
|
||||||
|
|
||||||
After those ordered results, an ordinary explicit `ww test` request that records
|
After those ordered results, an ordinary explicit `ww test` request that records
|
||||||
an attributable setup, build, or execution failure emits exactly one standalone
|
an attributable setup, build, or execution failure emits exactly one standalone
|
||||||
|
|||||||
@@ -222,12 +222,18 @@ run loop exactly. Measured on the 31-package `lib/...` walk:
|
|||||||
List mode uses that same product process and initialization boundary but starts
|
List mode uses that same product process and initialization boundary but starts
|
||||||
no per-test child. The shared language harness emits only selected qualified
|
no per-test child. The shared language harness emits only selected qualified
|
||||||
test names, one per line in descriptor order. A valid filter selecting no tests
|
test names, one per line in descriptor order. A valid filter selecting no tests
|
||||||
therefore produces no harness list bytes, no `[no matches]` marker, and no
|
therefore produces no harness list bytes, warning, or accounting while the
|
||||||
accounting while the process returns success; the coordinator continues with
|
process returns success; the coordinator continues with the package's normal
|
||||||
the package's normal `ok` result. Ordinary non-list zero-selection execution
|
unsuffixed `ok` result. Ordinary non-list zero-selection execution instead
|
||||||
retains its `[no matches]` marker and accounting. Concurrent products keep
|
writes the exact line `testing: warning: no tests to run` through harness
|
||||||
independent empty or nonempty captures, and persistent work never caches a test
|
stderr, retains its accounting and successful status, and gives the successful
|
||||||
result.
|
package `ok` result the suffix ` [no tests to run]`. Marker recognition is
|
||||||
|
line-delimited, at capture byte zero or after a newline; an arbitrary substring
|
||||||
|
in user output does not annotate the result. Concurrent products keep
|
||||||
|
independent empty or nonempty captures, markers, and suffixes, and persistent
|
||||||
|
work never caches a test result. A directly invoked retained binary exposes the
|
||||||
|
warning on stderr and accounting on stdout because no coordinator combines its
|
||||||
|
descriptors or emits a package result.
|
||||||
|
|
||||||
Directory test binaries are always linked under the coordinator's temporary
|
Directory test binaries are always linked under the coordinator's temporary
|
||||||
product root. `-c` independently requests a caller-visible executable copy and
|
product root. `-c` independently requests a caller-visible executable copy and
|
||||||
|
|||||||
@@ -1819,6 +1819,15 @@ fn pkgemitinstall(g: *pkggroup) bool = {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Pinned cmd/go recognizes the testing package's exact no-tests warning only
|
||||||
|
// at capture byte zero or after a newline. The harness owns whether a test ran;
|
||||||
|
// this coordinator owns only the corresponding package-result suffix.
|
||||||
|
fn pkgnoteststorun(output: str) bool = {
|
||||||
|
let first: str = "testing: warning: no tests to run\n";
|
||||||
|
let later: str = "\ntesting: warning: no tests to run\n";
|
||||||
|
return strings.hasprefix(output, first) || strings.contains(output, later);
|
||||||
|
};
|
||||||
|
|
||||||
fn pkgemitgroup(g: *pkggroup, compileonly: bool,
|
fn pkgemitgroup(g: *pkggroup, compileonly: bool,
|
||||||
statusfailed: *bool) bool = {
|
statusfailed: *bool) bool = {
|
||||||
if (g.notests) {
|
if (g.notests) {
|
||||||
@@ -1850,6 +1859,9 @@ fn pkgemitgroup(g: *pkggroup, compileonly: bool,
|
|||||||
};
|
};
|
||||||
pkgput(os.STDOUT_FILENO, "ok ");
|
pkgput(os.STDOUT_FILENO, "ok ");
|
||||||
pkglabel(g);
|
pkglabel(g);
|
||||||
|
if (pkgnoteststorun(runoutput)) {
|
||||||
|
pkgput(os.STDOUT_FILENO, " [no tests to run]");
|
||||||
|
};
|
||||||
pkgput(os.STDOUT_FILENO, "\n");
|
pkgput(os.STDOUT_FILENO, "\n");
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -116,8 +116,7 @@ export fn run(tests: [](str, *fn() void)) i32 = {
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
if (selected == 0) {
|
if (selected == 0) {
|
||||||
if (pkgprefix.len != 0) { tstputs("[no matches]\n"); }
|
tsterrputs("testing: warning: no tests to run\n");
|
||||||
else { tstputs("No tests run\n"); };
|
|
||||||
tstputaccounting(tests.len, 0, 0, 0);
|
tstputaccounting(tests.len, 0, 0, 0);
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
@@ -626,6 +625,10 @@ fn tstputs(s: str) void = {
|
|||||||
tstfdwriteall(os.STDOUT_FILENO, s.ptr, s.len: u64);
|
tstfdwriteall(os.STDOUT_FILENO, s.ptr, s.len: u64);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn tsterrputs(s: str) void = {
|
||||||
|
tstfdwriteall(os.STDERR_FILENO, s.ptr, s.len: u64);
|
||||||
|
};
|
||||||
|
|
||||||
fn tstputuint(n: i32) void = {
|
fn tstputuint(n: i32) void = {
|
||||||
let buf: [16]u8;
|
let buf: [16]u8;
|
||||||
let i: i32 = 16;
|
let i: i32 = 16;
|
||||||
|
|||||||
@@ -835,16 +835,21 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
runcommand(root, "nomatch", nav,
|
runcommand(root, "nomatch", nav,
|
||||||
(30i64 * (time.second: i64)): time.duration, &out);
|
(30i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
assert(occurrences(out.stdout, "[no matches]\n") == 1);
|
assert(out.stderr.len == 0);
|
||||||
|
assert(occurrences(out.stdout,
|
||||||
|
"testing: warning: no tests to run\n") == 1);
|
||||||
assert(occurrences(out.stdout,
|
assert(occurrences(out.stdout,
|
||||||
"3 discovered, 0 selected, 0 started, 0 completed\n") == 1);
|
"3 discovered, 0 selected, 0 started, 0 completed\n") == 1);
|
||||||
|
assert(occurrences(out.stdout, strings.concat("ok ", target,
|
||||||
|
" [routing] [no tests to run]\n")) == 1);
|
||||||
|
|
||||||
let lnav: []str = [driver("ww"), "test", "-list", "-run",
|
let lnav: []str = [driver("ww"), "test", "-list", "-run",
|
||||||
"no-such-*", "-I", sourceroot, target];
|
"no-such-*", "-I", sourceroot, target];
|
||||||
runcommand(root, "list-nomatch", lnav,
|
runcommand(root, "list-nomatch", lnav,
|
||||||
(30i64 * (time.second: i64)): time.duration, &out);
|
(30i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 0);
|
expectexit(&out, 0);
|
||||||
assert(occurrences(out.stdout, "[no matches]\n") == 0);
|
assert(!has(out.stdout, "testing: warning: no tests to run"));
|
||||||
|
assert(!has(out.stdout, " [no tests to run]"));
|
||||||
assert(!has(out.stdout, " discovered, "));
|
assert(!has(out.stdout, " discovered, "));
|
||||||
clean(root);
|
clean(root);
|
||||||
};
|
};
|
||||||
@@ -901,7 +906,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
assert(occurrences(out.stdout, "alpha-init\n") == 1);
|
assert(occurrences(out.stdout, "alpha-init\n") == 1);
|
||||||
assert(occurrences(out.stdout, "beta-init\n") == 1);
|
assert(occurrences(out.stdout, "beta-init\n") == 1);
|
||||||
assert(occurrences(out.stdout, "ok ") == 2);
|
assert(occurrences(out.stdout, "ok ") == 2);
|
||||||
assert(!has(out.stdout, "[no matches]"));
|
assert(!has(out.stdout, "testing: warning: no tests to run"));
|
||||||
|
assert(!has(out.stdout, " [no tests to run]"));
|
||||||
assert(!has(out.stdout, " discovered, "));
|
assert(!has(out.stdout, " discovered, "));
|
||||||
assert(!has(out.stdout, "list_alpha.first"));
|
assert(!has(out.stdout, "list_alpha.first"));
|
||||||
assert(!has(out.stdout, "list_alpha.second"));
|
assert(!has(out.stdout, "list_alpha.second"));
|
||||||
@@ -945,7 +951,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
assert(out.stderr.len == 0);
|
assert(out.stderr.len == 0);
|
||||||
assert(occurrences(out.stdout, "alpha-init\n") == 1);
|
assert(occurrences(out.stdout, "alpha-init\n") == 1);
|
||||||
assert(occurrences(out.stdout, "ok ") == 1);
|
assert(occurrences(out.stdout, "ok ") == 1);
|
||||||
assert(!has(out.stdout, "[no matches]"));
|
assert(!has(out.stdout, "testing: warning: no tests to run"));
|
||||||
|
assert(!has(out.stdout, " [no tests to run]"));
|
||||||
assert(!has(out.stdout, "list_alpha."));
|
assert(!has(out.stdout, "list_alpha."));
|
||||||
assert(os.exists(retained[i]));
|
assert(os.exists(retained[i]));
|
||||||
retainedout[i] = strings.dup(out.stdout);
|
retainedout[i] = strings.dup(out.stdout);
|
||||||
@@ -973,6 +980,216 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
clean(root);
|
clean(root);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@test fn ordinary_no_match_uses_go_warning_and_result_suffix() void = {
|
||||||
|
let root: str = fresh();
|
||||||
|
let alpha: str = strings.concat(root, "/alpha");
|
||||||
|
let beta: str = strings.concat(root, "/beta");
|
||||||
|
let none: str = strings.concat(root, "/none");
|
||||||
|
let broken: str = strings.concat(root, "/broken");
|
||||||
|
mkdirall(alpha);
|
||||||
|
mkdirall(beta);
|
||||||
|
mkdirall(none);
|
||||||
|
mkdirall(broken);
|
||||||
|
writefile(strings.concat(alpha, "/alpha.ww"),
|
||||||
|
"package zero_alpha;\nfn value() i32 = { return 1; };\n");
|
||||||
|
writefile(strings.concat(alpha, "/alpha_test.ww"), strings.concat(
|
||||||
|
"package zero_alpha;\nimport os;\n",
|
||||||
|
"@test fn first() void = {};\n",
|
||||||
|
"@test fn marker_text() void = {\n",
|
||||||
|
" let text: str = \"not-a-marker testing: warning: no tests to run\\n\";\n",
|
||||||
|
" os.write(os.STDOUT_FILENO, text.ptr, text.len: u64);\n",
|
||||||
|
"};\n"));
|
||||||
|
writefile(strings.concat(beta, "/beta.ww"),
|
||||||
|
"package zero_beta;\nfn value() i32 = { return 2; };\n");
|
||||||
|
writefile(strings.concat(beta, "/beta_test.ww"),
|
||||||
|
"package zero_beta;\n@test fn only() void = {};\n");
|
||||||
|
writefile(strings.concat(none, "/none.ww"),
|
||||||
|
"package zero_none;\nfn value() i32 = { return 3; };\n");
|
||||||
|
writefile(strings.concat(broken, "/broken.ww"),
|
||||||
|
"package zero_broken;\nfn value() missing_type = { return 4; };\n");
|
||||||
|
writefile(strings.concat(broken, "/broken_test.ww"),
|
||||||
|
"package zero_broken;\n@test fn registered() void = {};\n");
|
||||||
|
|
||||||
|
let warning: str = "testing: warning: no tests to run\n";
|
||||||
|
let suffix: str = " [no tests to run]";
|
||||||
|
let stages: []str = ["ww", "ww_ww"];
|
||||||
|
let tags: []str = ["c", "ww"];
|
||||||
|
let works: []str = [strings.concat(root, "/work-c"),
|
||||||
|
strings.concat(root, "/work-ww")];
|
||||||
|
let retained: []str = [strings.concat(root, "/retained-c.test"),
|
||||||
|
strings.concat(root, "/retained-ww.test")];
|
||||||
|
let coldout: []str = ["", ""];
|
||||||
|
let colderror: []str = ["", ""];
|
||||||
|
let positiveout: []str = ["", ""];
|
||||||
|
let positiveerror: []str = ["", ""];
|
||||||
|
let listout: []str = ["", ""];
|
||||||
|
let listerror: []str = ["", ""];
|
||||||
|
let retainedout: []str = ["", ""];
|
||||||
|
let retainederror: []str = ["", ""];
|
||||||
|
let manualout: []str = ["", ""];
|
||||||
|
let manualerror: []str = ["", ""];
|
||||||
|
let failurediagnostic: []str = ["", ""];
|
||||||
|
let out: commandout;
|
||||||
|
let i: i32 = 0;
|
||||||
|
for (i < stages.len) {
|
||||||
|
// Reverse request order and run two products concurrently. Result bytes
|
||||||
|
// remain directory-sorted and each product owns its own marker/suffix.
|
||||||
|
let emptyav: []str = [driver(stages[i]), "test", "-j", "2",
|
||||||
|
"-w", works[i], "-run", "no-such-*", beta, alpha];
|
||||||
|
runcommand(root, strings.concat("ordinary-empty-cold-", tags[i]),
|
||||||
|
emptyav, (90i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
assert(occurrences(out.stdout, warning) == 2);
|
||||||
|
assert(!has(out.stdout, "[no matches]"));
|
||||||
|
assert(occurrences(out.stdout,
|
||||||
|
"2 discovered, 0 selected, 0 started, 0 completed\n") == 1);
|
||||||
|
assert(occurrences(out.stdout,
|
||||||
|
"1 discovered, 0 selected, 0 started, 0 completed\n") == 1);
|
||||||
|
let alphaok: str = strings.concat("ok ", alpha,
|
||||||
|
" [zero_alpha] [no tests to run]\n");
|
||||||
|
let betaok: str = strings.concat("ok ", beta,
|
||||||
|
" [zero_beta] [no tests to run]\n");
|
||||||
|
let alphacount: i32 = pos(out.stdout,
|
||||||
|
"2 discovered, 0 selected, 0 started, 0 completed\n");
|
||||||
|
let alphareport: i32 = pos(out.stdout, alphaok);
|
||||||
|
let betacount: i32 = pos(out.stdout,
|
||||||
|
"1 discovered, 0 selected, 0 started, 0 completed\n");
|
||||||
|
let betareport: i32 = pos(out.stdout, betaok);
|
||||||
|
assert(alphacount >= 0 && alphacount < alphareport
|
||||||
|
&& alphareport < betacount && betacount < betareport);
|
||||||
|
assert(occurrences(out.stdout, alphaok) == 1);
|
||||||
|
assert(occurrences(out.stdout, betaok) == 1);
|
||||||
|
coldout[i] = strings.dup(out.stdout);
|
||||||
|
colderror[i] = strings.dup(out.stderr);
|
||||||
|
|
||||||
|
runcommand(root, strings.concat("ordinary-empty-warm-", tags[i]),
|
||||||
|
emptyav, (90i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(same(out.stdout, coldout[i]));
|
||||||
|
assert(same(out.stderr, colderror[i]));
|
||||||
|
assert(!directoryhasnew(works[i]));
|
||||||
|
|
||||||
|
// The same bytes inside a running test, but not at a line boundary, are
|
||||||
|
// ordinary user output and must not annotate a successful package result.
|
||||||
|
let positiveav: []str = [driver(stages[i]), "test", "-w",
|
||||||
|
works[i], "-run", "marker_text", alpha];
|
||||||
|
runcommand(root, strings.concat("ordinary-positive-", tags[i]),
|
||||||
|
positiveav, (90i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
assert(occurrences(out.stdout, warning) == 1);
|
||||||
|
assert(has(out.stdout, strings.concat("not-a-marker ", warning)));
|
||||||
|
assert(!has(out.stdout, suffix));
|
||||||
|
assert(has(out.stdout, strings.concat("ok ", alpha,
|
||||||
|
" [zero_alpha]\n")));
|
||||||
|
positiveout[i] = strings.dup(out.stdout);
|
||||||
|
positiveerror[i] = strings.dup(out.stderr);
|
||||||
|
|
||||||
|
// The already-closed list behavior remains an early return: no warning,
|
||||||
|
// accounting, old sentinel, or package suffix.
|
||||||
|
let listav: []str = [driver(stages[i]), "test", "-w", works[i],
|
||||||
|
"-list", "-run", "no-such-*", alpha];
|
||||||
|
runcommand(root, strings.concat("ordinary-list-control-", tags[i]),
|
||||||
|
listav, (90i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(same(out.stdout, strings.concat("ok ", alpha,
|
||||||
|
" [zero_alpha]\n")));
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
assert(!has(out.stdout, warning) && !has(out.stdout, suffix));
|
||||||
|
assert(!has(out.stdout, " discovered, "));
|
||||||
|
listout[i] = strings.dup(out.stdout);
|
||||||
|
listerror[i] = strings.dup(out.stderr);
|
||||||
|
|
||||||
|
let noneav: []str = [driver(stages[i]), "test", "-w", works[i],
|
||||||
|
none];
|
||||||
|
runcommand(root, strings.concat("ordinary-no-files-", tags[i]),
|
||||||
|
noneav, (90i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(same(out.stdout, strings.concat("? ", none,
|
||||||
|
" [no test files]\n")));
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
assert(!has(out.stdout, warning) && !has(out.stdout, suffix));
|
||||||
|
|
||||||
|
// A successful zero-selection run may publish its retained test binary.
|
||||||
|
let retainav: []str = [driver(stages[i]), "test", "-w", works[i],
|
||||||
|
"-o", retained[i], "-run", "no-such-*", alpha];
|
||||||
|
runcommand(root, strings.concat("ordinary-retained-", tags[i]),
|
||||||
|
retainav, (90i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
assert(occurrences(out.stdout, warning) == 1);
|
||||||
|
assert(occurrences(out.stdout, alphaok) == 1);
|
||||||
|
assert(os.exists(retained[i]));
|
||||||
|
assert(!os.exists(strings.concat(retained[i], ".sepwork")));
|
||||||
|
let prior: str = strings.dup(readfile(retained[i]));
|
||||||
|
retainedout[i] = strings.dup(out.stdout);
|
||||||
|
retainederror[i] = strings.dup(out.stderr);
|
||||||
|
|
||||||
|
// Direct execution exposes the harness-owned stream boundary: accounting
|
||||||
|
// remains stdout while the exact pinned warning is stderr.
|
||||||
|
let manualav: []str = [retained[i], "-package=zero_alpha",
|
||||||
|
"no-such-*"];
|
||||||
|
runcommand(root, strings.concat("ordinary-manual-", tags[i]), manualav,
|
||||||
|
(30i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(same(out.stdout,
|
||||||
|
"2 discovered, 0 selected, 0 started, 0 completed\n"));
|
||||||
|
assert(same(out.stderr, warning));
|
||||||
|
manualout[i] = strings.dup(out.stdout);
|
||||||
|
manualerror[i] = strings.dup(out.stderr);
|
||||||
|
|
||||||
|
// A producer failure wins before runtime and preserves a prior public
|
||||||
|
// binary. Cold and repeated failure have no warning, ok line, or suffix.
|
||||||
|
let failav: []str = [driver(stages[i]), "test", "-w", works[i],
|
||||||
|
"-o", retained[i], "-run", "no-such-*", broken];
|
||||||
|
runcommand(root, strings.concat("ordinary-failure-cold-", tags[i]),
|
||||||
|
failav, (90i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(same(out.stdout, "FAIL\n"));
|
||||||
|
assert(out.stderr.len != 0);
|
||||||
|
assert(!has(out.stdout, warning) && !has(out.stderr, warning));
|
||||||
|
assert(!has(out.stdout, suffix) && !has(out.stderr, suffix));
|
||||||
|
assert(!has(out.stdout, "ok ") && !has(out.stderr, "ok "));
|
||||||
|
assert(same(prior, readfile(retained[i])));
|
||||||
|
failurediagnostic[i] = strings.dup(primarydiagnostic(out.stderr));
|
||||||
|
let failstderr: str = strings.dup(out.stderr);
|
||||||
|
|
||||||
|
runcommand(root, strings.concat("ordinary-failure-warm-", tags[i]),
|
||||||
|
failav, (90i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(same(out.stdout, "FAIL\n"));
|
||||||
|
assert(same(out.stderr, failstderr));
|
||||||
|
assert(same(prior, readfile(retained[i])));
|
||||||
|
assert(!has(out.stdout, warning) && !has(out.stderr, warning));
|
||||||
|
|
||||||
|
// Restored valid reuse still emits the success marker/suffix and never
|
||||||
|
// rewrites an already-identical retained binary.
|
||||||
|
runcommand(root, strings.concat("ordinary-restored-", tags[i]), retainav,
|
||||||
|
(90i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(same(out.stdout, retainedout[i]));
|
||||||
|
assert(same(out.stderr, retainederror[i]));
|
||||||
|
assert(same(prior, readfile(retained[i])));
|
||||||
|
assert(!directoryhasnew(works[i]));
|
||||||
|
assert(!os.exists(strings.concat(retained[i], ".sepwork")));
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
assert(same(coldout[0], coldout[1]));
|
||||||
|
assert(same(colderror[0], colderror[1]));
|
||||||
|
assert(same(positiveout[0], positiveout[1]));
|
||||||
|
assert(same(positiveerror[0], positiveerror[1]));
|
||||||
|
assert(same(listout[0], listout[1]));
|
||||||
|
assert(same(listerror[0], listerror[1]));
|
||||||
|
assert(same(retainedout[0], retainedout[1]));
|
||||||
|
assert(same(retainederror[0], retainederror[1]));
|
||||||
|
assert(same(manualout[0], manualout[1]));
|
||||||
|
assert(same(manualerror[0], manualerror[1]));
|
||||||
|
assert(same(failurediagnostic[0], failurediagnostic[1]));
|
||||||
|
assert(same(readfile(retained[0]), readfile(retained[1])));
|
||||||
|
clean(root);
|
||||||
|
};
|
||||||
|
|
||||||
// Directory-package execution context is one product-level runtime property.
|
// Directory-package execution context is one product-level runtime property.
|
||||||
// This fixture keeps every source tree disposable while covering the variant,
|
// This fixture keeps every source tree disposable while covering the variant,
|
||||||
// dependency, environment, routing, concurrency, persistence, and failure
|
// dependency, environment, routing, concurrency, persistence, and failure
|
||||||
@@ -1317,7 +1534,9 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata");
|
cwdassertpackage(out.stdout, "dep-init", p3, "p3-data", "p3-testdata");
|
||||||
cwdassertpackage(out.stdout, "testdep-init", p3,
|
cwdassertpackage(out.stdout, "testdep-init", p3,
|
||||||
"p3-data", "p3-testdata");
|
"p3-data", "p3-testdata");
|
||||||
assert(has(out.stdout, "[no matches]\n"));
|
assert(has(out.stdout, "testing: warning: no tests to run\n"));
|
||||||
|
assert(has(out.stdout, strings.concat("ok ", p3,
|
||||||
|
" [p3] [no tests to run]\n")));
|
||||||
assert(!has(out.stdout, "p3-internal cwd="));
|
assert(!has(out.stdout, "p3-internal cwd="));
|
||||||
assert(!has(out.stdout, "p3-external cwd="));
|
assert(!has(out.stdout, "p3-external cwd="));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user