ww: print final status for explicit test failures
This commit is contained in:
@@ -211,12 +211,12 @@ exec_package_command(int argc, char **argv, const char *target,
|
|||||||
if (fallback == NULL) return 1;
|
if (fallback == NULL) return 1;
|
||||||
prog = fallback;
|
prog = fallback;
|
||||||
}
|
}
|
||||||
if (argc < 0 || argc > INT_MAX - 10) {
|
if (argc < 0 || argc > INT_MAX - 11) {
|
||||||
free(fallback);
|
free(fallback);
|
||||||
sep_fail_size();
|
sep_fail_size();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
char **xargv = calloc((size_t)argc + 10, sizeof *xargv);
|
char **xargv = calloc((size_t)argc + 11, sizeof *xargv);
|
||||||
if (xargv == NULL) {
|
if (xargv == NULL) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"ww %s: cannot allocate package coordinator arguments\n",
|
"ww %s: cannot allocate package coordinator arguments\n",
|
||||||
@@ -233,6 +233,8 @@ exec_package_command(int argc, char **argv, const char *target,
|
|||||||
}
|
}
|
||||||
xargv[n++] = "--ww-driver";
|
xargv[n++] = "--ww-driver";
|
||||||
xargv[n++] = (char *)self_path;
|
xargv[n++] = (char *)self_path;
|
||||||
|
if (!build_only && !add_dot)
|
||||||
|
xargv[n++] = "--ww-explicit-test-target";
|
||||||
if (root_identity != NULL) {
|
if (root_identity != NULL) {
|
||||||
xargv[n++] = "--ww-root-identity";
|
xargv[n++] = "--ww-root-identity";
|
||||||
xargv[n++] = (char *)root_identity;
|
xargv[n++] = (char *)root_identity;
|
||||||
@@ -7985,6 +7987,8 @@ do_test(int argc, char **argv)
|
|||||||
if (!resolve_module(target, incs, resolved, sizeof resolved,
|
if (!resolve_module(target, incs, resolved, sizeof resolved,
|
||||||
&is_dir)) {
|
&is_dir)) {
|
||||||
fprintf(stderr, "ww test: cannot find %s\n", target);
|
fprintf(stderr, "ww test: cannot find %s\n", target);
|
||||||
|
if (src != NULL && !compileonly && !emit_asm)
|
||||||
|
fputs("FAIL\n", stdout);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (is_dir) {
|
if (is_dir) {
|
||||||
@@ -8064,6 +8068,7 @@ do_test(int argc, char **argv)
|
|||||||
fputs("ww: cannot remove temporary output\n", stderr);
|
fputs("ww: cannot remove temporary output\n", stderr);
|
||||||
if (owntmp && rmdir(tmpdir) != 0)
|
if (owntmp && rmdir(tmpdir) != 0)
|
||||||
fputs("ww: cannot remove temporary directory\n", stderr);
|
fputs("ww: cannot remove temporary directory\n", stderr);
|
||||||
|
if (!compileonly && !emit_asm) fputs("FAIL\n", stdout);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (compileonly || emit_asm) {
|
if (compileonly || emit_asm) {
|
||||||
@@ -8079,6 +8084,7 @@ do_test(int argc, char **argv)
|
|||||||
return cleanfail ? 1 : 0;
|
return cleanfail ? 1 : 0;
|
||||||
}
|
}
|
||||||
int rc = run_test_bin(outp, pattern);
|
int rc = run_test_bin(outp, pattern);
|
||||||
|
int test_failed = rc != 0;
|
||||||
if (rc == 0 && deferred_install
|
if (rc == 0 && deferred_install
|
||||||
&& sep_install_test_output(outp, outstem) != 0)
|
&& sep_install_test_output(outp, outstem) != 0)
|
||||||
rc = 1;
|
rc = 1;
|
||||||
@@ -8090,6 +8096,7 @@ do_test(int argc, char **argv)
|
|||||||
fputs("ww: cannot remove temporary directory\n", stderr);
|
fputs("ww: cannot remove temporary directory\n", stderr);
|
||||||
if (rc == 0) rc = 1;
|
if (rc == 0) rc = 1;
|
||||||
}
|
}
|
||||||
|
if (test_failed) fputs("FAIL\n", stdout);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
if (S_ISREG(st.st_mode)) {
|
if (S_ISREG(st.st_mode)) {
|
||||||
@@ -8139,6 +8146,7 @@ do_test(int argc, char **argv)
|
|||||||
fputs("ww: cannot remove temporary output\n", stderr);
|
fputs("ww: cannot remove temporary output\n", stderr);
|
||||||
if (owntmp && rmdir(tmpdir) != 0)
|
if (owntmp && rmdir(tmpdir) != 0)
|
||||||
fputs("ww: cannot remove temporary directory\n", stderr);
|
fputs("ww: cannot remove temporary directory\n", stderr);
|
||||||
|
if (!compileonly && !emit_asm) fputs("FAIL\n", stdout);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (compileonly || emit_asm) {
|
if (compileonly || emit_asm) {
|
||||||
@@ -8154,6 +8162,7 @@ do_test(int argc, char **argv)
|
|||||||
return cleanfail ? 1 : 0;
|
return cleanfail ? 1 : 0;
|
||||||
}
|
}
|
||||||
int rc = run_test_bin(outp, pattern);
|
int rc = run_test_bin(outp, pattern);
|
||||||
|
int test_failed = rc != 0;
|
||||||
if (rc == 0 && deferred_install
|
if (rc == 0 && deferred_install
|
||||||
&& sep_install_test_output(outp, outstem) != 0)
|
&& sep_install_test_output(outp, outstem) != 0)
|
||||||
rc = 1;
|
rc = 1;
|
||||||
@@ -8165,6 +8174,7 @@ do_test(int argc, char **argv)
|
|||||||
fputs("ww: cannot remove temporary directory\n", stderr);
|
fputs("ww: cannot remove temporary directory\n", stderr);
|
||||||
if (rc == 0) rc = 1;
|
if (rc == 0) rc = 1;
|
||||||
}
|
}
|
||||||
|
if (test_failed) fputs("FAIL\n", stdout);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
if (!S_ISDIR(st.st_mode)) {
|
if (!S_ISDIR(st.st_mode)) {
|
||||||
|
|||||||
@@ -7190,6 +7190,159 @@ must match exactly. Existing package execution, timeout, interruption,
|
|||||||
transaction, persistence, byte-identity, and cleanup owners continue to prove
|
transaction, persistence, byte-identity, and cleanup owners continue to prove
|
||||||
the mechanisms this slice does not alter.
|
the mechanisms this slice does not alter.
|
||||||
|
|
||||||
|
### 11.34 Implemented final `FAIL` for explicit ordinary test failures
|
||||||
|
|
||||||
|
An ordinary `ww test` request with an explicit target now ends its ordered
|
||||||
|
standard output with exactly one command-owned `FAIL\n` when test setup, build,
|
||||||
|
or execution fails. The line follows every package result, including successful
|
||||||
|
packages ordered after an earlier failure. It applies to one or many explicit
|
||||||
|
directory, recursive, dotted-directory, or raw-file targets; filters and list
|
||||||
|
mode; and the private execution of a retained test. It does not apply to bare
|
||||||
|
implicit-current-directory `ww test`, `-c`, `-S`, `ww build`, command-line
|
||||||
|
usage/shape or output preflight rejection, publication-only failure,
|
||||||
|
capture-only failure, cleanup-only failure, allocation/systemic coordinator
|
||||||
|
failure, or later direct execution of a retained binary.
|
||||||
|
|
||||||
|
#### Pinned Go evidence and classification
|
||||||
|
|
||||||
|
The sole authority is official Go 1.26.5 at commit
|
||||||
|
`c19862e5f8415b4f24b189d065ed739517c548ba`:
|
||||||
|
|
||||||
|
- `runTest` reports setup errors, writes the per-package setup-failure result,
|
||||||
|
and sets the command exit status
|
||||||
|
([`cmd/go/internal/test/test.go`, lines 1010–1061](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1010-L1061)).
|
||||||
|
It then creates a root `go test` action owned by `printExitStatus`, orders all
|
||||||
|
package print actions, and executes that root
|
||||||
|
([lines 1099–1124](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1099-L1124)).
|
||||||
|
- `builderTest` constructs the ordinary build/run/clean/print chain and gives
|
||||||
|
the run and print boundaries the failure handling needed to reach ordered
|
||||||
|
output; its compile-only branch instead uses a dependency-sensitive nop
|
||||||
|
print action
|
||||||
|
([lines 1133–1169 and 1185–1366](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1133-L1366)).
|
||||||
|
- `(*runTestActor).Act` turns a dependency build failure into a package test
|
||||||
|
result and sets exit status 1
|
||||||
|
([lines 1436–1521](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1436-L1521)).
|
||||||
|
Every non-nil execution error, including nonzero exit or abnormal/start
|
||||||
|
failure, likewise sets the exit status and emits the package failure result
|
||||||
|
([lines 1644–1774](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1644-L1774)).
|
||||||
|
- `builderCleanTest` and `builderPrintTest` put cleanup and captured package
|
||||||
|
output before the root status action
|
||||||
|
([lines 2237–2259](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L2237-L2259)).
|
||||||
|
`printExitStatus` then prints exactly `FAIL\n` when at least one package
|
||||||
|
argument was explicit and the global exit status is nonzero
|
||||||
|
([lines 2262–2284](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L2262-L2284)).
|
||||||
|
- Official `test_status.txt` requires a failing package, a later successful
|
||||||
|
package, and a final standalone `FAIL\n`
|
||||||
|
([lines 3–6](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_status.txt#L3-L6)).
|
||||||
|
Official `test_syntax_error_says_fail.txt` requires `FAIL` for an explicit
|
||||||
|
test build/setup syntax failure
|
||||||
|
([lines 1–13](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_syntax_error_says_fail.txt#L1-L13)).
|
||||||
|
- `testFlags` records explicit file operands in `pkgArgs`
|
||||||
|
([`cmd/go/internal/test/testflag.go`, lines 219–290](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/testflag.go#L219-L290));
|
||||||
|
`PackagesAndErrors` turns those files into the command-line package
|
||||||
|
([`cmd/go/internal/load/pkg.go`, lines 2895–2918](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L2895-L2918)),
|
||||||
|
which `GoFilesPackage` constructs
|
||||||
|
([lines 3244–3315](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/load/pkg.go#L3244-L3315)).
|
||||||
|
|
||||||
|
Those source rules and official assertions are behavior directly implemented
|
||||||
|
or asserted by pinned Go. The single-explicit-package and raw-file cases are
|
||||||
|
derived from `len(pkgArgs) != 0` and the explicit-file loading path. The bare
|
||||||
|
implicit exclusion is derived from empty `pkgArgs`. The compile-only and
|
||||||
|
publication-only exclusions are derived from their uncleared dependency
|
||||||
|
failure preventing the root actor under the pinned work executor
|
||||||
|
([`cmd/go/internal/work/exec.go`, lines 134–205](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/work/exec.go#L134-L205)).
|
||||||
|
|
||||||
|
#### Fresh four-axis audit and direct pre-fix measurements
|
||||||
|
|
||||||
|
The bounded audit considered all four permanent axes and selected only this
|
||||||
|
test-command status gap:
|
||||||
|
|
||||||
|
- **Go-like build:** pinned unresolved-symbol handling and its undeclared-main
|
||||||
|
case are owned by `(*ErrorReporter).errorUnresolved`
|
||||||
|
([`cmd/link/internal/ld/errors.go`, lines 29–65](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/errors.go#L29-L65)),
|
||||||
|
with official assertions in `TestUndefinedRelocErrors`
|
||||||
|
([`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))
|
||||||
|
and `issue10978/main.go`
|
||||||
|
([lines 5–27](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/link/internal/ld/testdata/issue10978/main.go#L5-L27)).
|
||||||
|
Direct Cstage and WWstage no-main builds both exited 1 with identical linker
|
||||||
|
diagnostics and no output. This candidate was aligned.
|
||||||
|
- **Go-like test:** an explicit failing directory made both stages exit 1 with
|
||||||
|
empty stderr and byte-identical 207-byte stdout (SHA-256
|
||||||
|
`3d0d44d5d9c4d4d95446382807ee092a611f19b43dc3013cf8df76f135cb5c46`),
|
||||||
|
ending at its package failure rather than a standalone marker. A failing then
|
||||||
|
successful `-j 2` request had byte-identical 384-byte stdout (SHA-256
|
||||||
|
`4623567924df78375fcca84ff797b7cb89d06d3fd5382704d8b9ebb79916b9d9`)
|
||||||
|
ending at the successful `ok` result. A raw-file failure had byte-identical
|
||||||
|
121-byte stdout (SHA-256
|
||||||
|
`8db356ffc5b3a6d9df3308bfba8301329c5c07ce30c5ccd5cc5ee22a6c43331c`)
|
||||||
|
ending at harness accounting. Explicit missing-import build failure likewise
|
||||||
|
lacked the final marker. These directly measured pre-fix WW facts establish
|
||||||
|
the selected external difference. Bare implicit and `-c` failures already
|
||||||
|
omitted the marker and were aligned exclusions.
|
||||||
|
- **Go-like package:** `MultiplePackageError` and package scanning implement
|
||||||
|
conflicting selected declarations
|
||||||
|
([`go/build/build.go`, lines 538–548 and 931–967](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/go/build/build.go#L538-L548));
|
||||||
|
`TestMultiplePackageImport` asserts the rule
|
||||||
|
([`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 an `alpha`/`beta` directory identically before tools.
|
||||||
|
This candidate was aligned.
|
||||||
|
- **Go-like import:** `unusedImports` and `errorUnusedPkg` implement the unused
|
||||||
|
renamed-import diagnostic
|
||||||
|
([`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));
|
||||||
|
official `importdecl0` asserts ordinary and renamed forms
|
||||||
|
([lines 9–27](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/internal/types/testdata/check/importdecl0/importdecl0a.go#L9-L27)).
|
||||||
|
After normalizing only PID-bearing scratch roots, both WW stages produced the
|
||||||
|
same unused-renamed-import diagnostic and no output. This candidate was
|
||||||
|
aligned.
|
||||||
|
|
||||||
|
The command results in that list are directly measured WW behavior; the linked
|
||||||
|
source and tests are behavior directly implemented or asserted by pinned Go.
|
||||||
|
The conclusion that only test presentation changes while build, package, and
|
||||||
|
import identity remain fixed is behavior derived from the pinned action and
|
||||||
|
final-status placement.
|
||||||
|
|
||||||
|
#### Ownership, final behavior, and preserved boundaries
|
||||||
|
|
||||||
|
The directory owner is `internal/wwpackage.packagecommand`: its private
|
||||||
|
explicit-target bit affects only final status, and `pkgemitplan`/`pkgemitgroup`
|
||||||
|
record attributable build/run failure while preserving canonical result order.
|
||||||
|
After all package captures, package results, install attempts, and temporary-root
|
||||||
|
cleanup, the coordinator emits one final line. Attributable setup/load rejection
|
||||||
|
uses the same status helper. The two public drivers own the raw-file equivalent:
|
||||||
|
they remember the producer or process result, finish their existing cleanup,
|
||||||
|
then emit the line. A successful run followed only by install or cleanup failure
|
||||||
|
does not acquire test-failure status.
|
||||||
|
|
||||||
|
Loading and source selection, canonical package and import identity, graph
|
||||||
|
nodes, action construction, scheduling, compiler/assembler/linker invocation,
|
||||||
|
generated main, child argv/environment/cwd/stdin, capture bytes, package
|
||||||
|
diagnostics, and diagnostic precedence are unchanged. Runtime nonzero exit,
|
||||||
|
signal, timeout, and executable-start failure keep their existing
|
||||||
|
classification; only the command status line follows. One command-global bit
|
||||||
|
is isolated from every product-local capture, so parallel completion order
|
||||||
|
cannot duplicate or reorder it. If the coordinator itself is interrupted before
|
||||||
|
final emission no post-termination output is promised; an observed child
|
||||||
|
interruption is an ordinary run failure.
|
||||||
|
|
||||||
|
Artifact construction and bytes are unchanged. A failing running-retained test
|
||||||
|
still preserves prior public bytes and creates no new executable; successful
|
||||||
|
products in a mixed request retain their existing independent install results.
|
||||||
|
Producer failure, publication rejection, transaction rollback, cleanup, and
|
||||||
|
residue ownership are unchanged, and the marker creates no file. Physical
|
||||||
|
directories and the private explicit-target signal do not enter package,
|
||||||
|
import, graph, action, artifact, symbol, `.wwi`, publication, or persistence
|
||||||
|
identity. No persisted byte or key changed, so build workdir format remains
|
||||||
|
`18`, test workdir format remains `19`, and semantic storage format remains `3`.
|
||||||
|
|
||||||
|
The WW-native `explicit_test_failure_has_final_status` observer covers both
|
||||||
|
stages for single and concurrent failing/succeeding packages, filters, list
|
||||||
|
mode, raw files, setup/build and runtime failure, success, bare implicit and
|
||||||
|
compile-only exclusions, running retained rollback, artifact-byte equality,
|
||||||
|
diagnostic equality, exact cardinality/order, and `.new`/transaction cleanup.
|
||||||
|
Existing directory execution observers cover signals, timeouts, child cleanup,
|
||||||
|
and canonical-order behavior with the new final line, while build-mode controls
|
||||||
|
prove that the other command axis remains silent.
|
||||||
|
|
||||||
## 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.
|
||||||
|
|||||||
13
docs/spec.md
13
docs/spec.md
@@ -790,6 +790,19 @@ linker, and other build failures retain their diagnostic standard-error
|
|||||||
channel; build-action stdout and stderr remain separate. Parallel products own
|
channel; build-action stdout and stderr remain separate. Parallel products own
|
||||||
independent captures and are still emitted in canonical product order.
|
independent captures and are still emitted in canonical product order.
|
||||||
|
|
||||||
|
After those ordered results, an ordinary explicit `ww test` request that records
|
||||||
|
an attributable setup, build, or execution failure emits exactly one standalone
|
||||||
|
`FAIL\n` on standard output. The line is command-owned: it follows even a later
|
||||||
|
successful package result, and a single explicit directory, recursive or dotted
|
||||||
|
directory, or raw source file is sufficient. Runtime nonzero exit, signal,
|
||||||
|
timeout, or executable-start failure all set that status. Filters, list mode,
|
||||||
|
and the private execution of a retained test keep the same rule. Bare implicit
|
||||||
|
current-directory `ww test`, `-c`, `-S`, build requests, CLI usage/shape or
|
||||||
|
output preflight, publication-only failure, capture-only failure, cleanup-only
|
||||||
|
failure, and later direct execution of a retained binary do not emit it. The
|
||||||
|
marker changes no capture, diagnostic, executable, package/import/action
|
||||||
|
identity, publication decision, or persistent byte.
|
||||||
|
|
||||||
The coordinator does not change its own cwd or environment. Parallel products
|
The coordinator does not change its own cwd or environment. Parallel products
|
||||||
receive independent child environments and each uses its own source directory.
|
receive independent child environments and each uses its own source directory.
|
||||||
Relative ordinary files, `testdata`, and writes resolve there for every
|
Relative ordinary files, `testdata`, and writes resolve there for every
|
||||||
|
|||||||
@@ -322,6 +322,18 @@ has a distinct capture, while canonical group-order emission remains
|
|||||||
byte-stable across `-j` values. Build-plan stdout and stderr stay distinct, and
|
byte-stable across `-j` values. Build-plan stdout and stderr stay distinct, and
|
||||||
loader/build diagnostics stay on stderr.
|
loader/build diagnostics stay on stderr.
|
||||||
|
|
||||||
|
After canonical result emission, an ordinary test command with any explicit
|
||||||
|
directory, recursive, dotted-directory, or raw-file target emits exactly one
|
||||||
|
final standalone `FAIL\n` when attributable setup, build, or test execution
|
||||||
|
failed. It follows later successful package output and applies to filters, list
|
||||||
|
mode, signals, timeouts, executable-start failures, and running retained tests.
|
||||||
|
The bare implicit-current-directory form, `-c`, `-S`, build requests, CLI
|
||||||
|
usage/shape or output preflight, publication-only, capture-only, cleanup-only,
|
||||||
|
and direct retained-binary execution do not receive the command marker.
|
||||||
|
Directory coordination and both raw-file drivers own only this final
|
||||||
|
presentation state; test captures, result trailers, diagnostics, scheduling,
|
||||||
|
rollback, artifact bytes, and persisted action identity remain unchanged.
|
||||||
|
|
||||||
The physical directory remains distinct from exact dotted package identity and
|
The physical directory remains distinct from exact dotted package identity and
|
||||||
from production, internal, external, recompiled, support, and generated-main
|
from production, internal, external, recompiled, support, and generated-main
|
||||||
action identity. Only product execution uses it. Compiler, assembler, archiver,
|
action identity. Only product execution uses it. Compiler, assembler, archiver,
|
||||||
|
|||||||
@@ -372,6 +372,17 @@ fn pkgputln(fd: i32, s: str) void = {
|
|||||||
pkgput(fd, "\n");
|
pkgput(fd, "\n");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Go's final test-status action is command-owned: after an explicit ordinary
|
||||||
|
// package request records a setup, build, or run failure, it prints one final
|
||||||
|
// FAIL after the ordered package results. Implicit current-directory testing
|
||||||
|
// and compile-only testing deliberately do not use this action.
|
||||||
|
fn pkgteststatusfail(explicitstatus: bool, compileonly: bool) int = {
|
||||||
|
if (explicitstatus && !compileonly) {
|
||||||
|
pkgputln(os.STDOUT_FILENO, "FAIL");
|
||||||
|
};
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
fn pkgputhexbyte(fd: i32, value: u8) void = {
|
fn pkgputhexbyte(fd: i32, value: u8) void = {
|
||||||
let digits: str = "0123456789abcdef";
|
let digits: str = "0123456789abcdef";
|
||||||
let encoded: [2]u8;
|
let encoded: [2]u8;
|
||||||
@@ -1704,7 +1715,8 @@ fn pkgemitinstall(g: *pkggroup) bool = {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn pkgemitgroup(g: *pkggroup, compileonly: bool) bool = {
|
fn pkgemitgroup(g: *pkggroup, compileonly: bool,
|
||||||
|
statusfailed: *bool) bool = {
|
||||||
if (g.notests) {
|
if (g.notests) {
|
||||||
pkgput(os.STDOUT_FILENO, "? ");
|
pkgput(os.STDOUT_FILENO, "? ");
|
||||||
pkgput(os.STDOUT_FILENO, g.dir);
|
pkgput(os.STDOUT_FILENO, g.dir);
|
||||||
@@ -1728,6 +1740,7 @@ fn pkgemitgroup(g: *pkggroup, compileonly: bool) bool = {
|
|||||||
pkgput(os.STDOUT_FILENO, "\n");
|
pkgput(os.STDOUT_FILENO, "\n");
|
||||||
};
|
};
|
||||||
if (!pkgrunok(g)) {
|
if (!pkgrunok(g)) {
|
||||||
|
*statusfailed = true;
|
||||||
pkgreportcommand(os.STDOUT_FILENO, "test", g, &g.runres);
|
pkgreportcommand(os.STDOUT_FILENO, "test", g, &g.runres);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
@@ -1740,7 +1753,7 @@ fn pkgemitgroup(g: *pkggroup, compileonly: bool) bool = {
|
|||||||
// A directory build capture is emitted once, followed by its independently
|
// A directory build capture is emitted once, followed by its independently
|
||||||
// executed products in their existing byte-sorted group order.
|
// executed products in their existing byte-sorted group order.
|
||||||
fn pkgemitplan(p: *pkgplan, groups: []pkggroup,
|
fn pkgemitplan(p: *pkgplan, groups: []pkggroup,
|
||||||
compileonly: bool, buildonly: bool) i32 = {
|
compileonly: bool, buildonly: bool, statusfailed: *bool) i32 = {
|
||||||
let buildcaptures: bool = pkgemitfile(p.buildout, os.STDOUT_FILENO);
|
let buildcaptures: bool = pkgemitfile(p.buildout, os.STDOUT_FILENO);
|
||||||
buildcaptures = pkgemitfile(p.builderr, os.STDERR_FILENO) && buildcaptures;
|
buildcaptures = pkgemitfile(p.builderr, os.STDERR_FILENO) && buildcaptures;
|
||||||
if (!buildcaptures) {
|
if (!buildcaptures) {
|
||||||
@@ -1755,11 +1768,14 @@ fn pkgemitplan(p: *pkgplan, groups: []pkggroup,
|
|||||||
pkgreportcommand(os.STDERR_FILENO, "build", &groups[i],
|
pkgreportcommand(os.STDERR_FILENO, "build", &groups[i],
|
||||||
&p.buildres);
|
&p.buildres);
|
||||||
};
|
};
|
||||||
|
if (!buildonly && !compileonly) { *statusfailed = true; };
|
||||||
failed += 1;
|
failed += 1;
|
||||||
} else if (buildonly) {
|
} else if (buildonly) {
|
||||||
void;
|
void;
|
||||||
} else {
|
} else {
|
||||||
if (!pkgemitgroup(&groups[i], compileonly)) { failed += 1; };
|
if (!pkgemitgroup(&groups[i], compileonly, statusfailed)) {
|
||||||
|
failed += 1;
|
||||||
|
};
|
||||||
if (!compileonly && !pkgemitinstall(&groups[i])) { failed += 1; };
|
if (!compileonly && !pkgemitinstall(&groups[i])) { failed += 1; };
|
||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
@@ -1774,6 +1790,7 @@ export fn packagecommand(args: []str) int = {
|
|||||||
let jobs: i32 = 1;
|
let jobs: i32 = 1;
|
||||||
let afterdash: bool = false;
|
let afterdash: bool = false;
|
||||||
let buildrootseen: bool = false;
|
let buildrootseen: bool = false;
|
||||||
|
let explicitstatus: bool = false;
|
||||||
if (args.len == PKG_COUNT_MAX) {
|
if (args.len == PKG_COUNT_MAX) {
|
||||||
pkgputln(os.STDERR_FILENO,
|
pkgputln(os.STDERR_FILENO,
|
||||||
"wwtest package: package graph is too large");
|
"wwtest package: package graph is too large");
|
||||||
@@ -1943,6 +1960,12 @@ export fn packagecommand(args: []str) int = {
|
|||||||
i += 2;
|
i += 2;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
if (strings.compare(a, "--ww-explicit-test-target") == 0) {
|
||||||
|
if (explicitstatus || buildonly) { pkgusage(); return 2; };
|
||||||
|
explicitstatus = true;
|
||||||
|
i += 1;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
if (strings.compare(a, "--ww-operation") == 0) {
|
if (strings.compare(a, "--ww-operation") == 0) {
|
||||||
if (i + 1 >= args.len || strings.compare(args[i + 1], "build") != 0
|
if (i + 1 >= args.len || strings.compare(args[i + 1], "build") != 0
|
||||||
|| buildonly) {
|
|| buildonly) {
|
||||||
@@ -2052,7 +2075,10 @@ export fn packagecommand(args: []str) int = {
|
|||||||
};
|
};
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
if (ds.errors != 0) { return 1; };
|
if (ds.errors != 0) {
|
||||||
|
if (ds.fatal) { return 1; };
|
||||||
|
return pkgteststatusfail(explicitstatus, compileonly);
|
||||||
|
};
|
||||||
pkgsortstrings(directroots.paths);
|
pkgsortstrings(directroots.paths);
|
||||||
directroots.paths = pkgdedup(directroots.paths);
|
directroots.paths = pkgdedup(directroots.paths);
|
||||||
i = 0;
|
i = 0;
|
||||||
@@ -2063,7 +2089,7 @@ export fn packagecommand(args: []str) int = {
|
|||||||
&canonicaloom)) {
|
&canonicaloom)) {
|
||||||
if (canonicaloom) { return 1; };
|
if (canonicaloom) { return 1; };
|
||||||
pkgfailpath(ds.paths[i], "cannot canonicalize package directory");
|
pkgfailpath(ds.paths[i], "cannot canonicalize package directory");
|
||||||
return 1;
|
return pkgteststatusfail(explicitstatus, compileonly);
|
||||||
};
|
};
|
||||||
let canonicalsource: str;
|
let canonicalsource: str;
|
||||||
if (!pkgstring(&canonicalsource, canonicaldir, "/",
|
if (!pkgstring(&canonicalsource, canonicaldir, "/",
|
||||||
@@ -2115,7 +2141,7 @@ export fn packagecommand(args: []str) int = {
|
|||||||
let pn: str;
|
let pn: str;
|
||||||
if (!pkgread(ds.paths[i], &body) || !pkgclause(body, &pn)) {
|
if (!pkgread(ds.paths[i], &body) || !pkgclause(body, &pn)) {
|
||||||
pkgfailpath(ds.paths[i], "invalid or missing package clause");
|
pkgfailpath(ds.paths[i], "invalid or missing package clause");
|
||||||
return 1;
|
return pkgteststatusfail(explicitstatus, compileonly);
|
||||||
};
|
};
|
||||||
if (!pkgstring(&s.pkg, pn)) { return 1; };
|
if (!pkgstring(&s.pkg, pn)) { return 1; };
|
||||||
append(srcs, s);
|
append(srcs, s);
|
||||||
@@ -2221,7 +2247,8 @@ export fn packagecommand(args: []str) int = {
|
|||||||
} else {
|
} else {
|
||||||
pkgfailpath(srcs[j].path,
|
pkgfailpath(srcs[j].path,
|
||||||
"test package must match production package or <package>_test");
|
"test package must match production package or <package>_test");
|
||||||
return 1;
|
return pkgteststatusfail(explicitstatus,
|
||||||
|
compileonly);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
j += 1;
|
j += 1;
|
||||||
@@ -2242,7 +2269,8 @@ export fn packagecommand(args: []str) int = {
|
|||||||
&& strings.compare(srcs[j].pkg, secondpkg) != 0) {
|
&& strings.compare(srcs[j].pkg, secondpkg) != 0) {
|
||||||
pkgfailpath(srcs[j].path,
|
pkgfailpath(srcs[j].path,
|
||||||
"test package must match production package or <package>_test");
|
"test package must match production package or <package>_test");
|
||||||
return 1;
|
return pkgteststatusfail(explicitstatus,
|
||||||
|
compileonly);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
j += 1;
|
j += 1;
|
||||||
@@ -2253,7 +2281,8 @@ export fn packagecommand(args: []str) int = {
|
|||||||
if (family.len == 0) {
|
if (family.len == 0) {
|
||||||
pkgfailpath(f.path,
|
pkgfailpath(f.path,
|
||||||
"external test package has empty base name");
|
"external test package has empty base name");
|
||||||
return 1;
|
return pkgteststatusfail(explicitstatus,
|
||||||
|
compileonly);
|
||||||
};
|
};
|
||||||
hasexternal = true;
|
hasexternal = true;
|
||||||
externalpkg = firstpkg;
|
externalpkg = firstpkg;
|
||||||
@@ -2284,7 +2313,7 @@ export fn packagecommand(args: []str) int = {
|
|||||||
} else {
|
} else {
|
||||||
pkgfailpath(f.path,
|
pkgfailpath(f.path,
|
||||||
"test package must match production package or <package>_test");
|
"test package must match production package or <package>_test");
|
||||||
return 1;
|
return pkgteststatusfail(explicitstatus, compileonly);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -2511,6 +2540,7 @@ export fn packagecommand(args: []str) int = {
|
|||||||
let planout: str = outname;
|
let planout: str = outname;
|
||||||
if (buildnull) { planout = ""; };
|
if (buildnull) { planout = ""; };
|
||||||
let failed: i32 = 0;
|
let failed: i32 = 0;
|
||||||
|
let statusfailed: bool = false;
|
||||||
i = 0;
|
i = 0;
|
||||||
for (i < plans.len) {
|
for (i < plans.len) {
|
||||||
if (!pkgsetplanpaths(&plans[i], groups, tmproot, i,
|
if (!pkgsetplanpaths(&plans[i], groups, tmproot, i,
|
||||||
@@ -2675,7 +2705,8 @@ export fn packagecommand(args: []str) int = {
|
|||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
for (i < plans.len) {
|
for (i < plans.len) {
|
||||||
failed += pkgemitplan(&plans[i], groups, compileonly, buildonly);
|
failed += pkgemitplan(&plans[i], groups, compileonly, buildonly,
|
||||||
|
&statusfailed);
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
if (!pkgremoveall(tmproot)) {
|
if (!pkgremoveall(tmproot)) {
|
||||||
@@ -2683,6 +2714,9 @@ export fn packagecommand(args: []str) int = {
|
|||||||
pkgputln(os.STDERR_FILENO, tmproot);
|
pkgputln(os.STDERR_FILENO, tmproot);
|
||||||
failed += 1;
|
failed += 1;
|
||||||
};
|
};
|
||||||
|
if (statusfailed && explicitstatus && !compileonly) {
|
||||||
|
pkgputln(os.STDOUT_FILENO, "FAIL");
|
||||||
|
};
|
||||||
if (failed != 0) { return 1; };
|
if (failed != 0) { return 1; };
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -273,11 +273,11 @@ fn execpackagetests(selfdir: *u8, argv: **u8, argc: i32, start: i32,
|
|||||||
if (prog == nil) { return 1; };
|
if (prog == nil) { return 1; };
|
||||||
};
|
};
|
||||||
|
|
||||||
if (start < 0 || start > argc || argc - start > SEP_COUNT_MAX - 10) {
|
if (start < 0 || start > argc || argc - start > SEP_COUNT_MAX - 11) {
|
||||||
sepfailsize();
|
sepfailsize();
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
let cap: i32 = argc - start + 10;
|
let cap: i32 = argc - start + 11;
|
||||||
let execargv: []*u8;
|
let execargv: []*u8;
|
||||||
let argvallocation: ([]*u8 | nomem) = sepallocptrs(cap);
|
let argvallocation: ([]*u8 | nomem) = sepallocptrs(cap);
|
||||||
match (argvallocation) {
|
match (argvallocation) {
|
||||||
@@ -300,6 +300,9 @@ fn execpackagetests(selfdir: *u8, argv: **u8, argc: i32, start: i32,
|
|||||||
};
|
};
|
||||||
execargv[n] = "--ww-driver".ptr; n += 1;
|
execargv[n] = "--ww-driver".ptr; n += 1;
|
||||||
execargv[n] = selfpath; n += 1;
|
execargv[n] = selfpath; n += 1;
|
||||||
|
if (!buildonly && !adddot) {
|
||||||
|
execargv[n] = "--ww-explicit-test-target".ptr; n += 1;
|
||||||
|
};
|
||||||
if (rootidentity != nil) {
|
if (rootidentity != nil) {
|
||||||
execargv[n] = "--ww-root-identity".ptr; n += 1;
|
execargv[n] = "--ww-root-identity".ptr; n += 1;
|
||||||
execargv[n] = rootidentity; n += 1;
|
execargv[n] = rootidentity; n += 1;
|
||||||
@@ -9243,6 +9246,9 @@ fn runsingletest(selfdir: *u8, src: *u8, incs: *u8, compileonly: i32,
|
|||||||
cerr("ww: cannot remove temporary directory\n");
|
cerr("ww: cannot remove temporary directory\n");
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
if (compileonly == 0 && emitasm == 0) {
|
||||||
|
os.write(1, "FAIL\n".ptr, 5u64);
|
||||||
|
};
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
if (compileonly != 0 || emitasm != 0) {
|
if (compileonly != 0 || emitasm != 0) {
|
||||||
@@ -9292,6 +9298,7 @@ fn runsingletest(selfdir: *u8, src: *u8, incs: *u8, compileonly: i32,
|
|||||||
if (toolbin != nil) {
|
if (toolbin != nil) {
|
||||||
os.free(toolbin: *void, cstrlen(toolbin) + 1u64);
|
os.free(toolbin: *void, cstrlen(toolbin) + 1u64);
|
||||||
};
|
};
|
||||||
|
let testfailed: bool = rc != 0;
|
||||||
if (rc == 0 && deferredinstall
|
if (rc == 0 && deferredinstall
|
||||||
&& sepinstalltestoutput(outp, outstem) != 0) { rc = 1; };
|
&& sepinstalltestoutput(outp, outstem) != 0) { rc = 1; };
|
||||||
if (owntmp) {
|
if (owntmp) {
|
||||||
@@ -9305,6 +9312,7 @@ fn runsingletest(selfdir: *u8, src: *u8, incs: *u8, compileonly: i32,
|
|||||||
if (rc == 0) { rc = 1; };
|
if (rc == 0) { rc = 1; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
if (testfailed) { os.write(1, "FAIL\n".ptr, 5u64); };
|
||||||
return rc;
|
return rc;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -9804,6 +9812,9 @@ fn dotest(selfdir: *u8, argv: **u8, argc: i32, start: i32) i32 = {
|
|||||||
if (resolved == nil) {
|
if (resolved == nil) {
|
||||||
cerr("ww test: cannot find ");
|
cerr("ww test: cannot find ");
|
||||||
os.write(2, target, cstrlen(target)); cerr("\n");
|
os.write(2, target, cstrlen(target)); cerr("\n");
|
||||||
|
if (targetindex >= 0 && compileonly == 0 && emitasm == 0) {
|
||||||
|
os.write(1, "FAIL\n".ptr, 5u64);
|
||||||
|
};
|
||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1159,6 +1159,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
assert(outc.stderr.len == 0);
|
assert(outc.stderr.len == 0);
|
||||||
assert(has(outc.stdout, strings.concat("FAIL ", p7,
|
assert(has(outc.stdout, strings.concat("FAIL ", p7,
|
||||||
" [p7] (test exit 1)\n")));
|
" [p7] (test exit 1)\n")));
|
||||||
|
assert(strings.hassuffix(outc.stdout, "\nFAIL\n"));
|
||||||
|
|
||||||
let timeoutc: []str = [driver("ww"), "test", "-timeout-ms=50", "-I",
|
let timeoutc: []str = [driver("ww"), "test", "-timeout-ms=50", "-I",
|
||||||
tree, "-I", failtree, p8];
|
tree, "-I", failtree, p8];
|
||||||
@@ -1180,6 +1181,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
assert(outc.stderr.len == 0);
|
assert(outc.stderr.len == 0);
|
||||||
assert(has(outc.stdout, strings.concat("FAIL ", p8,
|
assert(has(outc.stdout, strings.concat("FAIL ", p8,
|
||||||
" [p8] (test exit 1)\n")));
|
" [p8] (test exit 1)\n")));
|
||||||
|
assert(strings.hassuffix(outc.stdout, "\nFAIL\n"));
|
||||||
|
|
||||||
let signalc: []str = [driver("ww"), "test", "-I", tree, "-I",
|
let signalc: []str = [driver("ww"), "test", "-I", tree, "-I",
|
||||||
failtree, p9];
|
failtree, p9];
|
||||||
@@ -1199,6 +1201,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
assert(outc.stderr.len == 0);
|
assert(outc.stderr.len == 0);
|
||||||
assert(has(outc.stdout, strings.concat("FAIL ", p9,
|
assert(has(outc.stdout, strings.concat("FAIL ", p9,
|
||||||
" [p9] (test exit 1)\n")));
|
" [p9] (test exit 1)\n")));
|
||||||
|
assert(strings.hassuffix(outc.stdout, "\nFAIL\n"));
|
||||||
|
|
||||||
// Source-class rejection precedes every captured build or test child, so a
|
// Source-class rejection precedes every captured build or test child, so a
|
||||||
// nonempty caller input cannot create a partial persistent generation.
|
// nonempty caller input cannot create a partial persistent generation.
|
||||||
@@ -1216,7 +1219,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
stdinpath, (60i64 * (time.second: i64)): time.duration, &outw);
|
stdinpath, (60i64 * (time.second: i64)): time.duration, &outw);
|
||||||
expectexit(&outc, 1);
|
expectexit(&outc, 1);
|
||||||
expectexit(&outw, 1);
|
expectexit(&outw, 1);
|
||||||
assert(outc.stdout.len == 0 && outw.stdout.len == 0);
|
assert(same(outc.stdout, "FAIL\n") && same(outw.stdout, "FAIL\n"));
|
||||||
assert(same(outc.stderr, outw.stderr));
|
assert(same(outc.stderr, outw.stderr));
|
||||||
assert(has(outc.stderr,
|
assert(has(outc.stderr,
|
||||||
"test package must match production package or <package>_test\n"));
|
"test package must match production package or <package>_test\n"));
|
||||||
@@ -1360,6 +1363,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
(90i64 * (time.second: i64)): time.duration, &outc);
|
(90i64 * (time.second: i64)): time.duration, &outc);
|
||||||
expectexit(&outc, 1);
|
expectexit(&outc, 1);
|
||||||
cwdassertpackage(outc.stdout, "p7-fail", p7, "p7-data", "p7-testdata");
|
cwdassertpackage(outc.stdout, "p7-fail", p7, "p7-data", "p7-testdata");
|
||||||
|
assert(strings.hassuffix(outc.stdout, "\nFAIL\n"));
|
||||||
assert(same(retainedbytes, readfile(retained)));
|
assert(same(retainedbytes, readfile(retained)));
|
||||||
runcommandinputenvdir(root, "path-retain-fail-ww", retainfailw, wenv,
|
runcommandinputenvdir(root, "path-retain-fail-ww", retainfailw, wenv,
|
||||||
caller, stdinpath,
|
caller, stdinpath,
|
||||||
@@ -1735,6 +1739,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
(90i64 * (time.second: i64)): time.duration, &out);
|
(90i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 1);
|
expectexit(&out, 1);
|
||||||
assert(out.stderr.len == 0);
|
assert(out.stderr.len == 0);
|
||||||
|
assert(strings.hassuffix(out.stdout, "\nFAIL\n"));
|
||||||
assert(has(out.stdout, "partial-success\nok "));
|
assert(has(out.stdout, "partial-success\nok "));
|
||||||
assert(!has(out.stdout, "partial-successok "));
|
assert(!has(out.stdout, "partial-successok "));
|
||||||
assert(has(out.stdout, "partial-failure\nFAIL "));
|
assert(has(out.stdout, "partial-failure\nFAIL "));
|
||||||
@@ -1792,6 +1797,133 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
clean(root);
|
clean(root);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@test fn explicit_test_failure_has_final_status() void = {
|
||||||
|
let root: str = fresh();
|
||||||
|
let fail: str = strings.concat(root, "/fail");
|
||||||
|
let pass: str = strings.concat(root, "/pass");
|
||||||
|
let setup: str = strings.concat(root, "/setup");
|
||||||
|
mkdirall(fail); mkdirall(pass); mkdirall(setup);
|
||||||
|
let failfile: str = strings.concat(fail, "/fail_test.ww");
|
||||||
|
writefile(failfile, strings.concat(
|
||||||
|
"package final_fail;\n",
|
||||||
|
"@test fn fails() void = { assert(false); };\n"));
|
||||||
|
writefile(strings.concat(pass, "/pass_test.ww"), strings.concat(
|
||||||
|
"package final_pass;\n",
|
||||||
|
"@test fn passes() void = { assert(true); };\n"));
|
||||||
|
writefile(strings.concat(setup, "/bad_test.ww"), strings.concat(
|
||||||
|
"package final_setup;\nimport nowhere;\n",
|
||||||
|
"@test fn unreachable() void = { void; };\n"));
|
||||||
|
|
||||||
|
let stages: []str = ["ww", "ww_ww"];
|
||||||
|
let tags: []str = ["c", "ww"];
|
||||||
|
let multiout: []str = ["", ""];
|
||||||
|
let multierr: []str = ["", ""];
|
||||||
|
let rawout: []str = ["", ""];
|
||||||
|
let setuperr: []str = ["", ""];
|
||||||
|
let out: commandout;
|
||||||
|
let i: i32 = 0;
|
||||||
|
for (i < stages.len) {
|
||||||
|
let multiav: []str = [driver(stages[i]), "test", "-j", "2",
|
||||||
|
fail, pass];
|
||||||
|
runcommand(root, strings.concat("final-multi-", tags[i]), multiav,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
assert(strings.hassuffix(out.stdout, "\nFAIL\n"));
|
||||||
|
assert(occurrences(out.stdout, "\nFAIL\n") == 1);
|
||||||
|
assert(has(out.stdout, strings.concat("ok ", pass,
|
||||||
|
" [final_pass]\nFAIL\n")));
|
||||||
|
multiout[i] = strings.dup(out.stdout);
|
||||||
|
multierr[i] = strings.dup(out.stderr);
|
||||||
|
|
||||||
|
let filterav: []str = [driver(stages[i]), "test", "-run", "fails",
|
||||||
|
fail];
|
||||||
|
runcommand(root, strings.concat("final-filter-", tags[i]), filterav,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
assert(strings.hassuffix(out.stdout, "\nFAIL\n"));
|
||||||
|
|
||||||
|
let listav: []str = [driver(stages[i]), "test", "-list", setup];
|
||||||
|
runcommand(root, strings.concat("final-list-", tags[i]), listav,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(same(out.stdout, "FAIL\n"));
|
||||||
|
assert(has(out.stderr, "cannot find package nowhere\n"));
|
||||||
|
|
||||||
|
let rawav: []str = [driver(stages[i]), "test", failfile];
|
||||||
|
runcommand(root, strings.concat("final-raw-", tags[i]), rawav,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
assert(strings.hassuffix(out.stdout, "\nFAIL\n"));
|
||||||
|
assert(!has(out.stdout, strings.concat("FAIL ", fail, " [")));
|
||||||
|
rawout[i] = strings.dup(out.stdout);
|
||||||
|
|
||||||
|
let setupav: []str = [driver(stages[i]), "test", setup];
|
||||||
|
runcommand(root, strings.concat("final-setup-", tags[i]), setupav,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(same(out.stdout, "FAIL\n"));
|
||||||
|
assert(has(out.stderr, "cannot find package nowhere\n"));
|
||||||
|
assert(has(out.stderr, strings.concat("FAIL ", setup,
|
||||||
|
" [final_setup] (build exit 1)\n")));
|
||||||
|
setuperr[i] = strings.dup(out.stderr);
|
||||||
|
|
||||||
|
let missing: str = strings.concat(root, "/missing");
|
||||||
|
let missingav: []str = [driver(stages[i]), "test", missing];
|
||||||
|
runcommand(root, strings.concat("final-missing-", tags[i]), missingav,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(same(out.stdout, "FAIL\n"));
|
||||||
|
assert(same(out.stderr, strings.concat("ww test: cannot find ",
|
||||||
|
missing, "\n")));
|
||||||
|
|
||||||
|
let implicitav: []str = [driver(stages[i]), "test"];
|
||||||
|
runcommanddir(root, strings.concat("final-implicit-", tags[i]), fail,
|
||||||
|
implicitav, (60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
assert(!strings.hassuffix(out.stdout, "\nFAIL\n"));
|
||||||
|
|
||||||
|
let compileav: []str = [driver(stages[i]), "test", "-c", setup];
|
||||||
|
runcommand(root, strings.concat("final-compile-", tags[i]), compileav,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(out.stdout.len == 0);
|
||||||
|
assert(has(out.stderr, "cannot find package nowhere\n"));
|
||||||
|
|
||||||
|
let successav: []str = [driver(stages[i]), "test", pass];
|
||||||
|
runcommand(root, strings.concat("final-success-", tags[i]), successav,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 0);
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
assert(!strings.hassuffix(out.stdout, "\nFAIL\n"));
|
||||||
|
|
||||||
|
let retained: str = strings.concat(root, "/retained-", tags[i], ".test");
|
||||||
|
writefile(retained, "prior-test-binary\n");
|
||||||
|
let retainav: []str = [driver(stages[i]), "test", "-o", retained,
|
||||||
|
fail];
|
||||||
|
runcommand(root, strings.concat("final-retained-", tags[i]), retainav,
|
||||||
|
(60i64 * (time.second: i64)): time.duration, &out);
|
||||||
|
expectexit(&out, 1);
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
assert(strings.hassuffix(out.stdout, "\nFAIL\n"));
|
||||||
|
assert(same(readfile(retained), "prior-test-binary\n"));
|
||||||
|
assert(!os.exists(strings.concat(retained, ".new")));
|
||||||
|
assert(!directoryhasfragment(root, ".wwtxn."));
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
assert(same(multiout[0], multiout[1]));
|
||||||
|
assert(same(multierr[0], multierr[1]));
|
||||||
|
assert(same(rawout[0], rawout[1]));
|
||||||
|
assert(same(setuperr[0], setuperr[1]));
|
||||||
|
assert(same(readfile(strings.concat(root, "/retained-c.test")),
|
||||||
|
readfile(strings.concat(root, "/retained-ww.test"))));
|
||||||
|
assert(!directoryhasnew(root));
|
||||||
|
clean(root);
|
||||||
|
};
|
||||||
|
|
||||||
@test fn imported_dependency_tests_do_not_leak() void = {
|
@test fn imported_dependency_tests_do_not_leak() void = {
|
||||||
let root: str = fresh();
|
let root: str = fresh();
|
||||||
let base: str = packagepath("dependency");
|
let base: str = packagepath("dependency");
|
||||||
@@ -4967,7 +5099,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
runcommand(root, strings.concat("multidir-failure-", stages[i]), av,
|
runcommand(root, strings.concat("multidir-failure-", stages[i]), av,
|
||||||
(120i64 * (time.second: i64)): time.duration, &out);
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 1);
|
expectexit(&out, 1);
|
||||||
assert(out.stdout.len == 0);
|
assert(same(out.stdout, "FAIL\n"));
|
||||||
assert(has(out.stderr, "has no exported declaration 'hidden'"));
|
assert(has(out.stderr, "has no exported declaration 'hidden'"));
|
||||||
assert(has(out.stderr, strings.concat("FAIL ", alpha,
|
assert(has(out.stderr, strings.concat("FAIL ", alpha,
|
||||||
" [alpha] (build exit 1)\n")));
|
" [alpha] (build exit 1)\n")));
|
||||||
@@ -5033,7 +5165,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
runcommand(root, strings.concat("isolated-external-", tags[i]),
|
runcommand(root, strings.concat("isolated-external-", tags[i]),
|
||||||
externalav, (120i64 * (time.second: i64)): time.duration, &out);
|
externalav, (120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 1);
|
expectexit(&out, 1);
|
||||||
assert(out.stdout.len == 0);
|
assert(same(out.stdout, "FAIL\n"));
|
||||||
assert(has(out.stderr,
|
assert(has(out.stderr,
|
||||||
"has no exported declaration 'hidden'"));
|
"has no exported declaration 'hidden'"));
|
||||||
assert(has(out.stderr,
|
assert(has(out.stderr,
|
||||||
@@ -5049,7 +5181,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
runcommand(root, strings.concat("isolated-same-", tags[i]), sameav,
|
runcommand(root, strings.concat("isolated-same-", tags[i]), sameav,
|
||||||
(120i64 * (time.second: i64)): time.duration, &out);
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 1);
|
expectexit(&out, 1);
|
||||||
assert(out.stdout.len == 0);
|
assert(same(out.stdout, "FAIL\n"));
|
||||||
assert(has(out.stderr, "undefined: missing"));
|
assert(has(out.stderr, "undefined: missing"));
|
||||||
assert(has(out.stderr, strings.concat("FAIL ", badsame,
|
assert(has(out.stderr, strings.concat("FAIL ", badsame,
|
||||||
" [badsame] (build exit 1)\n")));
|
" [badsame] (build exit 1)\n")));
|
||||||
@@ -8168,12 +8300,15 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
(120i64 * (time.second: i64)): time.duration, &out);
|
(120i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 1);
|
expectexit(&out, 1);
|
||||||
let foldfullwant: str = foldwants[fi];
|
let foldfullwant: str = foldwants[fi];
|
||||||
|
let foldstdoutwant: str = "";
|
||||||
if (same(foldmodes[fi], "test")) {
|
if (same(foldmodes[fi], "test")) {
|
||||||
foldfullwant = strings.concat(foldwants[fi], "FAIL ",
|
foldfullwant = strings.concat(foldwants[fi], "FAIL ",
|
||||||
foldtargets[fi], " [", foldpackages[fi],
|
foldtargets[fi], " [", foldpackages[fi],
|
||||||
"] (build exit 1)\n");
|
"] (build exit 1)\n");
|
||||||
|
foldstdoutwant = "FAIL\n";
|
||||||
};
|
};
|
||||||
assert(out.stdout.len == 0 && same(out.stderr, foldfullwant));
|
assert(same(out.stdout, foldstdoutwant)
|
||||||
|
&& same(out.stderr, foldfullwant));
|
||||||
assert(readfile(compilertrace).len == 0);
|
assert(readfile(compilertrace).len == 0);
|
||||||
assert(readfile(assemblertrace).len == 0);
|
assert(readfile(assemblertrace).len == 0);
|
||||||
assert(readfile(linkertrace).len == 0);
|
assert(readfile(linkertrace).len == 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user