ww test: preserve combined product output order
Go 1.26.5 maps each test binary's stdout and stderr to the same product writer. Teach the captured executor to share one open file description for byte-equal output paths, then make directory test products emit that one ordered capture on coordinator stdout. Build captures and inherited-stdio routes remain unchanged.\n\nKeep the executor mechanism, package policy, native Cstage/WWstage proof, and normative contract together so every commit preserves the observable test-output behavior.
This commit is contained in:
@@ -6039,6 +6039,125 @@ source-class rejection creates no persistent state, Cstage/WWstage diagnostics
|
|||||||
and output match, compile-only binaries are equal, persisted artifact bytes do
|
and output match, compile-only binaries are equal, persisted artifact bytes do
|
||||||
not change, and no `.new` residue survives.
|
not change, and no `.new` residue survives.
|
||||||
|
|
||||||
|
### 11.26 Implemented combined ordered test-product output
|
||||||
|
|
||||||
|
Every coordinator-executed directory-package test product now maps its standard
|
||||||
|
output and standard error to one product-local open capture. The coordinator
|
||||||
|
emits that capture on stdout after the product completes, preserving the order
|
||||||
|
in which writes from either descriptor reach the shared output. Runtime and
|
||||||
|
child-setup status lines are stdout product diagnostics. WW no longer drains
|
||||||
|
two captures and emits all stdout before all stderr.
|
||||||
|
|
||||||
|
#### Pinned Go evidence and pre-fix WW behavior
|
||||||
|
|
||||||
|
The authority is official Go 1.26.5 at commit
|
||||||
|
`c19862e5f8415b4f24b189d065ed739517c548ba`:
|
||||||
|
|
||||||
|
- `runTestActor.Act` selects one output writer for the test action: direct
|
||||||
|
stdout, JSON conversion, stdout plus a buffer, or a private buffer
|
||||||
|
([`cmd/go/internal/test/test.go`, lines 1436–1499](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1436-L1499)).
|
||||||
|
- It assigns that same writer to both `cmd.Stdout` and `cmd.Stderr`, then runs
|
||||||
|
the test binary
|
||||||
|
([`test.go`, lines 1661–1697](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1661-L1697)).
|
||||||
|
Success and runtime-failure status text is written through `cmd.Stdout`
|
||||||
|
([`test.go`, lines 1712–1769](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/internal/test/test.go#L1712-L1769)).
|
||||||
|
- `os/exec.Cmd` documents its shared-writer rule; `childStderr` returns the
|
||||||
|
already prepared stdout child file when the writers compare equal; and
|
||||||
|
`Start` installs the returned files as descriptors 1 and 2
|
||||||
|
([`os/exec/exec.go`, lines 208–225, 565–606, and 710–738](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/os/exec/exec.go#L565-L606)).
|
||||||
|
- Official command testdata has test mains write only to `os.Stderr`, requires
|
||||||
|
those bytes on `go test` stdout in buffered and streaming forms, and requires
|
||||||
|
command stderr to remain empty
|
||||||
|
([`cmd/go/testdata/script/test_fail_newline.txt`, lines 3–35 and 42–65](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_fail_newline.txt#L3-L65)).
|
||||||
|
The adjacent orphan-I/O test forwards a descendant's stderr through the test
|
||||||
|
process and likewise requires the bytes on command stdout
|
||||||
|
([`test_timeout_stdin.txt`, lines 9–21 and 39–82](https://github.com/golang/go/blob/c19862e5f8415b4f24b189d065ed739517c548ba/src/cmd/go/testdata/script/test_timeout_stdin.txt#L9-L82)).
|
||||||
|
|
||||||
|
The output destination is explicit in the pinned implementation and official
|
||||||
|
testdata. The ordering conclusion is source-derived: equal writers reuse one
|
||||||
|
child file or pipe instead of two independently drained pipes. No installed
|
||||||
|
host Go behavior is authority.
|
||||||
|
|
||||||
|
Before this slice, `pkgstartrun` supplied distinct `test.stdout` and
|
||||||
|
`test.stderr` paths. `exec.start` opened independent files, and
|
||||||
|
`pkgemitgroup` later emitted the entire stdout file followed by the entire
|
||||||
|
stderr file on different coordinator descriptors. A direct both-stage probe
|
||||||
|
that wrote `OUT-1`, `ERR-1`, `OUT-2`, `ERR-2` by alternating syscalls therefore
|
||||||
|
reported `OUT-1`, `OUT-2` on stdout and `ERR-1`, `ERR-2` on stderr. Cstage and
|
||||||
|
WWstage had byte-identical pre-fix behavior.
|
||||||
|
|
||||||
|
#### Descriptor and product ownership
|
||||||
|
|
||||||
|
The reusable captured executor owns only the descriptor mechanism. When
|
||||||
|
`stdoutpath` and `stderrpath` are byte-equal, it opens the path once with the
|
||||||
|
existing exclusive mode and obtains a close-on-exec duplicate from the same
|
||||||
|
open file description. The child maps those owned descriptors to fd 1 and fd 2.
|
||||||
|
Distinct paths retain independent exclusive opens and their prior behavior.
|
||||||
|
Path equality here selects an executor resource; it creates no filesystem,
|
||||||
|
package, import, action, symbol, artifact, or persistence identity.
|
||||||
|
|
||||||
|
The package coordinator owns the policy. One `pkggroup` now allocates one
|
||||||
|
`runoutput`, supplies it for both child paths, reads it once, and writes it to
|
||||||
|
coordinator stdout. Production, internal, external, recompiled-for-test,
|
||||||
|
support, generated-main, and test-only actions retain their exact topology and
|
||||||
|
one directory product still owns one process. Dependency initialization and
|
||||||
|
test bodies share the product descriptors naturally; no source rewriting or
|
||||||
|
manual stream forwarding exists.
|
||||||
|
|
||||||
|
Individual writes by one process retain syscall order. Descendants inheriting
|
||||||
|
the descriptors share the same open output, with ordinary kernel scheduling for
|
||||||
|
concurrent writers. Different products never share a capture. `-j N` may run
|
||||||
|
products concurrently, but the coordinator still waits for completion and
|
||||||
|
emits complete captures in canonical group order, so serial and parallel
|
||||||
|
command byte streams remain identical.
|
||||||
|
|
||||||
|
#### Diagnostics, nonexecution, and inherited routes
|
||||||
|
|
||||||
|
Successful and failing test-binary bytes, including bytes written to fd 2, are
|
||||||
|
emitted on stdout. A nonzero product, signal-classified test, timeout, or child
|
||||||
|
setup failure appends the existing `FAIL DIR [package] (test ...)` status on
|
||||||
|
stdout. Loader, source, compiler, assembler, linker, build-action, allocation,
|
||||||
|
capture-read, and cleanup diagnostics keep their established stderr channel;
|
||||||
|
captured build-plan stdout and stderr remain separate.
|
||||||
|
|
||||||
|
`ww build`, directory `ww test -c` (including `-c -o`), and a directory with no
|
||||||
|
selected test source start no product and allocate no run capture. A published
|
||||||
|
test binary invoked directly and the raw single-file compatibility route bypass
|
||||||
|
the coordinator, inherit fd 1 and fd 2 independently, and retain the caller's
|
||||||
|
stream destinations. `exec.runstdio` is unchanged. An arbitrary
|
||||||
|
`exec.command` with distinct output paths is also unchanged.
|
||||||
|
|
||||||
|
#### Failure, persistence, cleanup, and proof
|
||||||
|
|
||||||
|
Input still opens before any output. A merged output open failure creates no
|
||||||
|
child and no second capture. Duplicate, fork, descriptor-map, `chdir`, and
|
||||||
|
`execve` failures use the existing checked setup marker and close every owned
|
||||||
|
descriptor. Product failure does not erase successfully committed compilation;
|
||||||
|
sibling products retain independent output, process groups, and cleanup. The
|
||||||
|
coordinator removes its product captures with the existing temporary root, and
|
||||||
|
rejection or rollback publishes no partial result or `.new` state.
|
||||||
|
|
||||||
|
Output routing is request-time process metadata. It changes no unit, export,
|
||||||
|
assembly, object, archive, generated main, binary, action/storage key, tool
|
||||||
|
record, stamp, or persistent byte. There is still no test-result cache. Build
|
||||||
|
workdir format remains `18`, test workdir format remains `19`, and semantic
|
||||||
|
storage remains `3`.
|
||||||
|
|
||||||
|
The executor-level native proof in `test/wwfixture/process/main.ww` requires
|
||||||
|
distinct captures to stay distinct and equal paths to preserve alternating
|
||||||
|
fd-1/fd-2 bytes through one file in both compiler stages, including when the
|
||||||
|
caller closed stdout and stderr. The package owner
|
||||||
|
`directory_test_execution_working_directory` alternates real writes through
|
||||||
|
production and test-only dependency initialization; production/internal,
|
||||||
|
external, combined, recompiled, and test-only products; filters, list, and
|
||||||
|
no-match execution; success, assertion failure, signal, timeout, and post-build
|
||||||
|
`chdir` failure; serial/parallel and equivalent-root requests; cold/warm/data-
|
||||||
|
only persistence; and direct/raw boundaries. It requires Cstage/WWstage output
|
||||||
|
and diagnostics to match, failure/setup trailers to use stdout, successful
|
||||||
|
outer stderr to be empty, direct/raw stderr to remain separate, artifacts and
|
||||||
|
binaries to remain byte-identical, and every temporary or staged path to be
|
||||||
|
cleaned.
|
||||||
|
|
||||||
## 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.
|
||||||
|
|||||||
14
docs/spec.md
14
docs/spec.md
@@ -665,6 +665,16 @@ listing, no-match execution, failure, and timeout all retain that boundary.
|
|||||||
Standard input is runtime process metadata and contributes no package, action,
|
Standard input is runtime process metadata and contributes no package, action,
|
||||||
artifact, or persistence identity.
|
artifact, or persistence identity.
|
||||||
|
|
||||||
|
The product's standard output and standard error refer to one product-local
|
||||||
|
capture. Bytes from either descriptor retain the order in which their writes
|
||||||
|
reach that shared open output, and the coordinator emits the completed capture
|
||||||
|
on its standard output. A runtime failure or child-setup failure appends the
|
||||||
|
product status diagnostic to standard output as well. Loader, source,
|
||||||
|
compiler, assembler, linker, and other build failures retain their diagnostic
|
||||||
|
standard-error channel; build-action stdout and stderr remain separate.
|
||||||
|
Parallel products own independent captures and are still emitted in canonical
|
||||||
|
product order.
|
||||||
|
|
||||||
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
|
||||||
@@ -672,8 +682,8 @@ executing filter or list path. `ww build`, directory `ww test -c` (including
|
|||||||
`-c -o`), and a no-selected-test directory execute no test child and receive no
|
`-c -o`), and a no-selected-test directory execute no test child and receive no
|
||||||
execution-directory effect. A published test binary invoked directly, and the
|
execution-directory effect. A published test binary invoked directly, and the
|
||||||
raw single-file compatibility route, inherit the user's invocation cwd,
|
raw single-file compatibility route, inherit the user's invocation cwd,
|
||||||
environment, and standard input; no package directory or input policy is
|
environment, and three standard descriptors; no package directory, input, or
|
||||||
embedded or forced by the binary.
|
output policy is embedded or forced by the binary.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -248,6 +248,14 @@ parallel products observe immediate EOF rather than consuming a shared input
|
|||||||
offset. Production and test-only dependency initialization, filters, list mode,
|
offset. Production and test-only dependency initialization, filters, list mode,
|
||||||
no-match execution, failure, and timeout use that same process boundary.
|
no-match execution, failure, and timeout use that same process boundary.
|
||||||
|
|
||||||
|
The same product maps fd 1 and fd 2 to one open product-local capture. This
|
||||||
|
matches Go's test-command use of one writer for `exec.Cmd.Stdout` and
|
||||||
|
`exec.Cmd.Stderr`: writes are not drained into two files and regrouped later.
|
||||||
|
The coordinator emits the completed combined bytes and any product run-status
|
||||||
|
line on stdout. Each parallel product has a distinct capture, while canonical
|
||||||
|
group-order emission remains byte-stable across `-j` values. Build-plan stdout
|
||||||
|
and stderr stay distinct, and loader/build diagnostics stay on stderr.
|
||||||
|
|
||||||
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,
|
||||||
@@ -337,7 +345,11 @@ creation; source-class rejection with empty workdirs; and deterministic
|
|||||||
post-build child-`chdir` failure isolated from a successful sibling. It pads the
|
post-build child-`chdir` failure isolated from a successful sibling. It pads the
|
||||||
inherited environment beyond former fixed observer sizes, requires one appended
|
inherited environment beyond former fixed observer sizes, requires one appended
|
||||||
product `PWD`, requires EOF for captured actions and caller data for inherited-
|
product `PWD`, requires EOF for captured actions and caller data for inherited-
|
||||||
stdio routes, and sweeps the persistent workdir for staged residue.
|
stdio routes, alternates fd-1/fd-2 write syscalls through every executing
|
||||||
|
variant and initializer, requires one ordered stdout stream on success,
|
||||||
|
assertion failure, signal, timeout, and setup failure, requires direct/raw
|
||||||
|
inherited routes to keep their streams separate, and sweeps the persistent
|
||||||
|
workdir for staged residue.
|
||||||
Command-global bounded-memory failure remains independently
|
Command-global bounded-memory failure remains independently
|
||||||
owned by `allocation_failure_is_command_global`.
|
owned by `allocation_failure_is_command_global`.
|
||||||
|
|
||||||
@@ -513,6 +525,10 @@ invocations are not safe to run concurrently and remain mutually exclusive.
|
|||||||
coordinators use its captured asynchronous path. An empty captured-command
|
coordinators use its captured asynchronous path. An empty captured-command
|
||||||
`stdinpath` opens the null device; an explicit path supplies controlled input,
|
`stdinpath` opens the null device; an explicit path supplies controlled input,
|
||||||
and either descriptor is installed before exec with checked setup reporting.
|
and either descriptor is installed before exec with checked setup reporting.
|
||||||
|
Distinct output paths create independent exclusive captures; byte-equal output
|
||||||
|
paths open once and duplicate that descriptor so fd 1 and fd 2 share the same
|
||||||
|
open file description. The executor process proof covers both modes, including
|
||||||
|
closed caller standard descriptors.
|
||||||
The WW driver directly uses `os.exec.runstdio` for inherited-stdio,
|
The WW driver directly uses `os.exec.runstdio` for inherited-stdio,
|
||||||
inherited-environment, leader-only compiler, assembler, linker, cleanup, run,
|
inherited-environment, leader-only compiler, assembler, linker, cleanup, run,
|
||||||
and single-file-test calls. The local WW `procrun` implementation is deleted.
|
and single-file-test calls. The local WW `procrun` implementation is deleted.
|
||||||
|
|||||||
@@ -35,8 +35,7 @@ type pkggroup = struct {
|
|||||||
root: str,
|
root: str,
|
||||||
bin: str,
|
bin: str,
|
||||||
buildok: str,
|
buildok: str,
|
||||||
runout: str,
|
runoutput: str,
|
||||||
runerr: str,
|
|
||||||
state: i32,
|
state: i32,
|
||||||
runstartfailed: bool,
|
runstartfailed: bool,
|
||||||
runres: exec.result,
|
runres: exec.result,
|
||||||
@@ -1359,8 +1358,7 @@ fn pkgsetplanpaths(p: *pkgplan, groups: []pkggroup, root: str, index: i32,
|
|||||||
} else {
|
} else {
|
||||||
if (!pkgstring(&g.bin, g.root, "/package.test")) { return false; };
|
if (!pkgstring(&g.bin, g.root, "/package.test")) { return false; };
|
||||||
};
|
};
|
||||||
if (!pkgstring(&g.runout, g.root, "/test.stdout")
|
if (!pkgstring(&g.runoutput, g.root, "/test.output")
|
||||||
|| !pkgstring(&g.runerr, g.root, "/test.stderr")
|
|
||||||
|| !pkgstring(&g.buildok, g.root, "/build.ok")) { return false; };
|
|| !pkgstring(&g.buildok, g.root, "/build.ok")) { return false; };
|
||||||
i += 1;
|
i += 1;
|
||||||
};
|
};
|
||||||
@@ -1374,27 +1372,28 @@ fn pkglabel(g: *pkggroup) void = {
|
|||||||
pkgput(os.STDOUT_FILENO, "]");
|
pkgput(os.STDOUT_FILENO, "]");
|
||||||
};
|
};
|
||||||
|
|
||||||
fn pkgreportcommand(kind: str, g: *pkggroup, r: *exec.result) void = {
|
fn pkgreportcommand(fd: i32, kind: str, g: *pkggroup,
|
||||||
pkgput(os.STDERR_FILENO, "FAIL ");
|
r: *exec.result) void = {
|
||||||
pkgput(os.STDERR_FILENO, g.dir);
|
pkgput(fd, "FAIL ");
|
||||||
pkgput(os.STDERR_FILENO, " [");
|
pkgput(fd, g.dir);
|
||||||
pkgput(os.STDERR_FILENO, g.pkg);
|
pkgput(fd, " [");
|
||||||
pkgput(os.STDERR_FILENO, "] (");
|
pkgput(fd, g.pkg);
|
||||||
pkgput(os.STDERR_FILENO, kind);
|
pkgput(fd, "] (");
|
||||||
|
pkgput(fd, kind);
|
||||||
if (r.errno != 0 || r.cleanuperrno != 0
|
if (r.errno != 0 || r.cleanuperrno != 0
|
||||||
|| r.termination == exec.termination.ERROR) {
|
|| r.termination == exec.termination.ERROR) {
|
||||||
pkgput(os.STDERR_FILENO, " harness error ");
|
pkgput(fd, " harness error ");
|
||||||
let code: i32 = r.errno;
|
let code: i32 = r.errno;
|
||||||
if (code == 0) { code = r.cleanuperrno; };
|
if (code == 0) { code = r.cleanuperrno; };
|
||||||
pkgput(os.STDERR_FILENO, strconv.i32tos(code, strconv.base.DEC));
|
pkgput(fd, strconv.i32tos(code, strconv.base.DEC));
|
||||||
} else if (r.termination == exec.termination.SIGNAL) {
|
} else if (r.termination == exec.termination.SIGNAL) {
|
||||||
pkgput(os.STDERR_FILENO, " signal ");
|
pkgput(fd, " signal ");
|
||||||
pkgput(os.STDERR_FILENO, strconv.i32tos(r.code, strconv.base.DEC));
|
pkgput(fd, strconv.i32tos(r.code, strconv.base.DEC));
|
||||||
} else {
|
} else {
|
||||||
pkgput(os.STDERR_FILENO, " exit ");
|
pkgput(fd, " exit ");
|
||||||
pkgput(os.STDERR_FILENO, strconv.i32tos(r.code, strconv.base.DEC));
|
pkgput(fd, strconv.i32tos(r.code, strconv.base.DEC));
|
||||||
};
|
};
|
||||||
pkgputln(os.STDERR_FILENO, ")");
|
pkgputln(fd, ")");
|
||||||
};
|
};
|
||||||
|
|
||||||
fn pkgstartbuild(p: *pkgplan, groups: []pkggroup, builder: str, includes: []str,
|
fn pkgstartbuild(p: *pkgplan, groups: []pkggroup, builder: str, includes: []str,
|
||||||
@@ -1542,8 +1541,8 @@ fn pkgstartrun(g: *pkggroup, filters: []str, timeoutarg: str,
|
|||||||
rcmd.argv = ra;
|
rcmd.argv = ra;
|
||||||
rcmd.env = env;
|
rcmd.env = env;
|
||||||
rcmd.dir = g.dir;
|
rcmd.dir = g.dir;
|
||||||
rcmd.stdoutpath = g.runout;
|
rcmd.stdoutpath = g.runoutput;
|
||||||
rcmd.stderrpath = g.runerr;
|
rcmd.stderrpath = g.runoutput;
|
||||||
rcmd.deadline.sec = 0i64;
|
rcmd.deadline.sec = 0i64;
|
||||||
rcmd.deadline.nsec = 0i64;
|
rcmd.deadline.nsec = 0i64;
|
||||||
rcmd.grace = 0i64: time.duration;
|
rcmd.grace = 0i64: time.duration;
|
||||||
@@ -1580,16 +1579,14 @@ fn pkgemitgroup(g: *pkggroup, compileonly: bool) bool = {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
if (g.runstartfailed) { return false; };
|
if (g.runstartfailed) { return false; };
|
||||||
let runstdout: str;
|
let runoutput: str;
|
||||||
let runstderr: str;
|
if (!pkgread(g.runoutput, &runoutput)) {
|
||||||
if (!pkgread(g.runout, &runstdout) || !pkgread(g.runerr, &runstderr)) {
|
|
||||||
pkgfailpath(g.root, "cannot read test capture");
|
pkgfailpath(g.root, "cannot read test capture");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
pkgput(os.STDOUT_FILENO, runstdout);
|
pkgput(os.STDOUT_FILENO, runoutput);
|
||||||
pkgput(os.STDERR_FILENO, runstderr);
|
|
||||||
if (!pkgrunok(g)) {
|
if (!pkgrunok(g)) {
|
||||||
pkgreportcommand("test", g, &g.runres);
|
pkgreportcommand(os.STDOUT_FILENO, "test", g, &g.runres);
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
pkgput(os.STDOUT_FILENO, "ok ");
|
pkgput(os.STDOUT_FILENO, "ok ");
|
||||||
@@ -1612,7 +1609,8 @@ fn pkgemitplan(p: *pkgplan, groups: []pkggroup,
|
|||||||
let i: i32 = p.start;
|
let i: i32 = p.start;
|
||||||
for (i < p.end) {
|
for (i < p.end) {
|
||||||
if (!pkgproductbuilt(&groups[i], buildonly)) {
|
if (!pkgproductbuilt(&groups[i], buildonly)) {
|
||||||
pkgreportcommand("build", &groups[i], &p.buildres);
|
pkgreportcommand(os.STDERR_FILENO, "build", &groups[i],
|
||||||
|
&p.buildres);
|
||||||
failed += 1;
|
failed += 1;
|
||||||
} else if (buildonly) {
|
} else if (buildonly) {
|
||||||
void;
|
void;
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ export type result = struct {
|
|||||||
// command is deliberately concrete. argv includes argv[0], env is the full
|
// command is deliberately concrete. argv includes argv[0], env is the full
|
||||||
// environment, and an empty dir inherits the caller's working directory.
|
// environment, and an empty dir inherits the caller's working directory.
|
||||||
// An empty stdinpath selects the null device; stdoutpath and stderrpath are
|
// An empty stdinpath selects the null device; stdoutpath and stderrpath are
|
||||||
// created exclusively with mode 0600. A zero deadline means no timeout.
|
// created exclusively with mode 0600. Equal output paths share one open
|
||||||
|
// capture. A zero deadline means no timeout.
|
||||||
export type command = struct {
|
export type command = struct {
|
||||||
path: str,
|
path: str,
|
||||||
argv: []str,
|
argv: []str,
|
||||||
@@ -120,6 +121,16 @@ fn hasnul(s: str) bool = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn samepath(a: str, b: str) bool = {
|
||||||
|
if (a.len != b.len) { return false; };
|
||||||
|
let i: i32 = 0;
|
||||||
|
for (i < a.len) {
|
||||||
|
if (a[i] != b[i]) { return false; };
|
||||||
|
i += 1;
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
fn valid(c: *command) bool = {
|
fn valid(c: *command) bool = {
|
||||||
if (c.path.len == 0 || c.argv.len == 0 || c.argv[0].len == 0) {
|
if (c.path.len == 0 || c.argv.len == 0 || c.argv[0].len == 0) {
|
||||||
return false;
|
return false;
|
||||||
@@ -262,8 +273,14 @@ export fn start(p: *process, c: *command) void = {
|
|||||||
closefd(&p.result, infd);
|
closefd(&p.result, infd);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let errfd: i32 = os.open(c.stderrpath,
|
let errfd: i32;
|
||||||
os.flag.WRONLY | os.flag.CREATE | os.flag.EXCL, 384);
|
if (samepath(c.stdoutpath, c.stderrpath)) {
|
||||||
|
errfd = syscall3(SYS_FCNTL, outfd: i64,
|
||||||
|
F_DUPFD_CLOEXEC: i64, (os.STDERR_FILENO + 1): i64): i32;
|
||||||
|
} else {
|
||||||
|
errfd = os.open(c.stderrpath,
|
||||||
|
os.flag.WRONLY | os.flag.CREATE | os.flag.EXCL, 384);
|
||||||
|
};
|
||||||
if (errfd < 0) {
|
if (errfd < 0) {
|
||||||
fail(p, errfd);
|
fail(p, errfd);
|
||||||
closefd(&p.result, infd);
|
closefd(&p.result, infd);
|
||||||
|
|||||||
@@ -627,10 +627,39 @@ fn cwdassertrecord(text: str, label: str, cwd: str, pwd: str,
|
|||||||
assert(has(text, strings.concat(label, " create=ok\n")));
|
assert(has(text, strings.concat(label, " create=ok\n")));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn cwdassertmergedstreams(text: str, label: str, dir: str) void = {
|
||||||
|
let out1: str = strings.concat("STREAM ", label, " out-1 cwd=", dir, "\n");
|
||||||
|
let err1: str = strings.concat("STREAM ", label, " err-1 cwd=", dir, "\n");
|
||||||
|
let out2: str = strings.concat("STREAM ", label, " out-2 cwd=", dir, "\n");
|
||||||
|
let err2: str = strings.concat("STREAM ", label, " err-2 cwd=", dir, "\n");
|
||||||
|
let p0: i32 = pos(text, out1);
|
||||||
|
let p1: i32 = pos(text, err1);
|
||||||
|
let p2: i32 = pos(text, out2);
|
||||||
|
let p3: i32 = pos(text, err2);
|
||||||
|
assert(p0 >= 0 && p0 < p1 && p1 < p2 && p2 < p3);
|
||||||
|
assert(occurrences(text, out1) == 1 && occurrences(text, err1) == 1);
|
||||||
|
assert(occurrences(text, out2) == 1 && occurrences(text, err2) == 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
fn cwdassertsplitstreams(stdout: str, stderr: str, label: str, dir: str) void = {
|
||||||
|
let out1: str = strings.concat("STREAM ", label, " out-1 cwd=", dir, "\n");
|
||||||
|
let err1: str = strings.concat("STREAM ", label, " err-1 cwd=", dir, "\n");
|
||||||
|
let out2: str = strings.concat("STREAM ", label, " out-2 cwd=", dir, "\n");
|
||||||
|
let err2: str = strings.concat("STREAM ", label, " err-2 cwd=", dir, "\n");
|
||||||
|
let p0: i32 = pos(stdout, out1);
|
||||||
|
let p2: i32 = pos(stdout, out2);
|
||||||
|
let p1: i32 = pos(stderr, err1);
|
||||||
|
let p3: i32 = pos(stderr, err2);
|
||||||
|
assert(p0 >= 0 && p0 < p2 && p1 >= 0 && p1 < p3);
|
||||||
|
assert(!has(stdout, err1) && !has(stdout, err2));
|
||||||
|
assert(!has(stderr, out1) && !has(stderr, out2));
|
||||||
|
};
|
||||||
|
|
||||||
fn cwdassertpackage(text: str, label: str, dir: str, data: str,
|
fn cwdassertpackage(text: str, label: str, dir: str, data: str,
|
||||||
testdata: str) void = {
|
testdata: str) void = {
|
||||||
cwdassertrecord(text, label, dir, dir, "1", "yes", data, testdata,
|
cwdassertrecord(text, label, dir, dir, "1", "yes", data, testdata,
|
||||||
"eof");
|
"eof");
|
||||||
|
cwdassertmergedstreams(text, label, dir);
|
||||||
};
|
};
|
||||||
|
|
||||||
fn cwdwritedata(dir: str, label: str) void = {
|
fn cwdwritedata(dir: str, label: str) void = {
|
||||||
@@ -768,16 +797,26 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
let plain: str = strings.concat(tree, "/plain");
|
let plain: str = strings.concat(tree, "/plain");
|
||||||
let cmdno: str = strings.concat(tree, "/cmdno");
|
let cmdno: str = strings.concat(tree, "/cmdno");
|
||||||
|
|
||||||
writefile(strings.concat(probe, "/probe.ww"), strings.concat(
|
let streamsource: str = strings.concat(
|
||||||
"package probe;\n",
|
"fn putfd(fd: i32, value: str) void = {\n",
|
||||||
"import os;\n",
|
" match (os.writeall(fd, value.ptr, value.len: u64)) {\n",
|
||||||
"import strings;\n",
|
|
||||||
"fn put(value: str) void = {\n",
|
|
||||||
" match (os.writeall(os.STDOUT_FILENO, value.ptr, value.len: u64)) {\n",
|
|
||||||
" case let n: i64 => assert(n == value.len: i64);\n",
|
" case let n: i64 => assert(n == value.len: i64);\n",
|
||||||
" case let e: os.oserror => abort(\"write failed\");\n",
|
" case let e: os.oserror => abort(\"write failed\");\n",
|
||||||
" };\n",
|
" };\n",
|
||||||
"};\n",
|
"};\n",
|
||||||
|
"fn put(value: str) void = { putfd(os.STDOUT_FILENO, value); };\n",
|
||||||
|
"fn puterr(value: str) void = { putfd(os.STDERR_FILENO, value); };\n",
|
||||||
|
"export fn streams(label: str, context: str) void = {\n",
|
||||||
|
" put(strings.concat(\"STREAM \", label, \" out-1 cwd=\", context, \"\\n\"));\n",
|
||||||
|
" puterr(strings.concat(\"STREAM \", label, \" err-1 cwd=\", context, \"\\n\"));\n",
|
||||||
|
" put(strings.concat(\"STREAM \", label, \" out-2 cwd=\", context, \"\\n\"));\n",
|
||||||
|
" puterr(strings.concat(\"STREAM \", label, \" err-2 cwd=\", context, \"\\n\"));\n",
|
||||||
|
"};\n");
|
||||||
|
writefile(strings.concat(probe, "/probe.ww"), strings.concat(
|
||||||
|
"package probe;\n",
|
||||||
|
"import os;\n",
|
||||||
|
"import strings;\n",
|
||||||
|
streamsource,
|
||||||
"fn emitfile(label: str, key: str, path: str) void = {\n",
|
"fn emitfile(label: str, key: str, path: str) void = {\n",
|
||||||
" put(label); put(\" \" ); put(key); put(\"=\");\n",
|
" put(label); put(\" \" ); put(key); put(\"=\");\n",
|
||||||
" let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);\n",
|
" let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);\n",
|
||||||
@@ -800,6 +839,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
" let buf: [4096]u8;\n",
|
" let buf: [4096]u8;\n",
|
||||||
" let n: i64 = os.getcwd(&buf[0], size([4096]u8)); assert(n > 1i64);\n",
|
" let n: i64 = os.getcwd(&buf[0], size([4096]u8)); assert(n > 1i64);\n",
|
||||||
" let cwd: str; cwd.ptr = &buf[0]; cwd.len = (n - 1i64): i32;\n",
|
" let cwd: str; cwd.ptr = &buf[0]; cwd.len = (n - 1i64): i32;\n",
|
||||||
|
" streams(label, cwd);\n",
|
||||||
" put(label); put(\" cwd=\"); put(cwd); put(\"\\n\");\n",
|
" put(label); put(\" cwd=\"); put(cwd); put(\"\\n\");\n",
|
||||||
" put(label); put(\" PWD=\");\n",
|
" put(label); put(\" PWD=\");\n",
|
||||||
" match (os.getenv(\"PWD\")) {\n",
|
" match (os.getenv(\"PWD\")) {\n",
|
||||||
@@ -880,7 +920,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
"fn init() void = { probe.run(\"cmdno-init\"); };\n",
|
"fn init() void = { probe.run(\"cmdno-init\"); };\n",
|
||||||
"fn main() void = { probe.run(\"cmdno-main\"); };\n"));
|
"fn main() void = { probe.run(\"cmdno-main\"); };\n"));
|
||||||
|
|
||||||
let failnames: []str = ["reject", "p8", "p7"];
|
let failnames: []str = ["reject", "p9", "p8", "p7"];
|
||||||
i = 0;
|
i = 0;
|
||||||
for (i < failnames.len) {
|
for (i < failnames.len) {
|
||||||
let dir: str = strings.concat(failtree, "/", failnames[i]);
|
let dir: str = strings.concat(failtree, "/", failnames[i]);
|
||||||
@@ -890,6 +930,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
};
|
};
|
||||||
let p7: str = strings.concat(failtree, "/p7");
|
let p7: str = strings.concat(failtree, "/p7");
|
||||||
let p8: str = strings.concat(failtree, "/p8");
|
let p8: str = strings.concat(failtree, "/p8");
|
||||||
|
let p9: str = strings.concat(failtree, "/p9");
|
||||||
let reject: str = strings.concat(failtree, "/reject");
|
let reject: str = strings.concat(failtree, "/reject");
|
||||||
writefile(strings.concat(p7, "/p7_test.ww"), strings.concat(
|
writefile(strings.concat(p7, "/p7_test.ww"), strings.concat(
|
||||||
"package p7;\nimport probe;\n@test fn fails() void = {\n",
|
"package p7;\nimport probe;\n@test fn fails() void = {\n",
|
||||||
@@ -899,6 +940,9 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
" probe.run(\"p8-timeout\");\n",
|
" probe.run(\"p8-timeout\");\n",
|
||||||
" time.sleep((2i64 * (time.second: i64)): time.duration, ",
|
" time.sleep((2i64 * (time.second: i64)): time.duration, ",
|
||||||
"time.clock.monotonic);\n};\n"));
|
"time.clock.monotonic);\n};\n"));
|
||||||
|
writefile(strings.concat(p9, "/p9_test.ww"), strings.concat(
|
||||||
|
"package p9;\nimport os;\nimport probe;\n@test fn signals() void = {\n",
|
||||||
|
" probe.run(\"p9-signal\"); os.kill(os.getpid(), os.SIGTERM);\n};\n"));
|
||||||
writefile(strings.concat(reject, "/reject.ww"),
|
writefile(strings.concat(reject, "/reject.ww"),
|
||||||
"package reject;\nexport fn value() i32 = { return 1; };\n");
|
"package reject;\nexport fn value() i32 = { return 1; };\n");
|
||||||
writefile(strings.concat(reject, "/bad_test.ww"),
|
writefile(strings.concat(reject, "/bad_test.ww"),
|
||||||
@@ -1016,7 +1060,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
assert(same(outc.stdout, outw.stdout));
|
assert(same(outc.stdout, outw.stdout));
|
||||||
assert(same(outc.stderr, outw.stderr));
|
assert(same(outc.stderr, outw.stderr));
|
||||||
cwdassertpackage(outc.stdout, "p7-fail", p7, "p7-data", "p7-testdata");
|
cwdassertpackage(outc.stdout, "p7-fail", p7, "p7-data", "p7-testdata");
|
||||||
assert(same(outc.stderr, strings.concat("FAIL ", p7,
|
assert(outc.stderr.len == 0);
|
||||||
|
assert(has(outc.stdout, strings.concat("FAIL ", p7,
|
||||||
" [p7] (test exit 1)\n")));
|
" [p7] (test exit 1)\n")));
|
||||||
|
|
||||||
let timeoutc: []str = [driver("ww"), "test", "-timeout-ms=50", "-I",
|
let timeoutc: []str = [driver("ww"), "test", "-timeout-ms=50", "-I",
|
||||||
@@ -1036,9 +1081,29 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
cwdassertpackage(outc.stdout, "p8-timeout", p8,
|
cwdassertpackage(outc.stdout, "p8-timeout", p8,
|
||||||
"p8-data", "p8-testdata");
|
"p8-data", "p8-testdata");
|
||||||
assert(has(outc.stdout, "FAIL(timeout)\n"));
|
assert(has(outc.stdout, "FAIL(timeout)\n"));
|
||||||
assert(same(outc.stderr, strings.concat("FAIL ", p8,
|
assert(outc.stderr.len == 0);
|
||||||
|
assert(has(outc.stdout, strings.concat("FAIL ", p8,
|
||||||
" [p8] (test exit 1)\n")));
|
" [p8] (test exit 1)\n")));
|
||||||
|
|
||||||
|
let signalc: []str = [driver("ww"), "test", "-I", tree, "-I",
|
||||||
|
failtree, p9];
|
||||||
|
let signalw: []str = [driver("ww_ww"), "test", "-I", tree, "-I",
|
||||||
|
failtree, p9];
|
||||||
|
runcommandinputenvdir(root, "cwd-signal-c", signalc, cenv, caller,
|
||||||
|
stdinpath, (60i64 * (time.second: i64)): time.duration, &outc);
|
||||||
|
runcommandinputenvdir(root, "cwd-signal-ww", signalw, wenv, caller,
|
||||||
|
stdinpath, (60i64 * (time.second: i64)): time.duration, &outw);
|
||||||
|
expectexit(&outc, 1);
|
||||||
|
expectexit(&outw, 1);
|
||||||
|
assert(same(outc.stdout, outw.stdout));
|
||||||
|
assert(same(outc.stderr, outw.stderr));
|
||||||
|
cwdassertpackage(outc.stdout, "p9-signal", p9,
|
||||||
|
"p9-data", "p9-testdata");
|
||||||
|
assert(has(outc.stdout, "FAIL (signal 15)\n"));
|
||||||
|
assert(outc.stderr.len == 0);
|
||||||
|
assert(has(outc.stdout, strings.concat("FAIL ", p9,
|
||||||
|
" [p9] (test exit 1)\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.
|
||||||
let rejectcwork: str = strings.concat(root, "/stdin-reject-c");
|
let rejectcwork: str = strings.concat(root, "/stdin-reject-c");
|
||||||
@@ -1131,6 +1196,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
cwdassertrecord(out.stdout, "p1-internal", caller,
|
cwdassertrecord(out.stdout, "p1-internal", caller,
|
||||||
"/ww-cwd-inherited-first", "2", "no", "caller-data",
|
"/ww-cwd-inherited-first", "2", "no", "caller-data",
|
||||||
"caller-testdata", "data");
|
"caller-testdata", "data");
|
||||||
|
cwdassertsplitstreams(out.stdout, out.stderr, "dep-init", caller);
|
||||||
|
cwdassertsplitstreams(out.stdout, out.stderr, "p1-internal", caller);
|
||||||
assert(os.exists(strings.concat(caller, "/created.txt")));
|
assert(os.exists(strings.concat(caller, "/created.txt")));
|
||||||
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
|
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
|
||||||
|
|
||||||
@@ -1154,6 +1221,8 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
cwdassertrecord(outc.stdout, "p4-internal-only", caller,
|
cwdassertrecord(outc.stdout, "p4-internal-only", caller,
|
||||||
"/ww-cwd-inherited-first", "2", "yes", "caller-data",
|
"/ww-cwd-inherited-first", "2", "yes", "caller-data",
|
||||||
"caller-testdata", "data");
|
"caller-testdata", "data");
|
||||||
|
cwdassertsplitstreams(outc.stdout, outc.stderr,
|
||||||
|
"p4-internal-only", caller);
|
||||||
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
|
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
|
||||||
cwdassertpackage(multiout, "dep-init", p2, "p2-data", "p2-testdata");
|
cwdassertpackage(multiout, "dep-init", p2, "p2-data", "p2-testdata");
|
||||||
cwdassertpackage(multiout, "p2-external", p2,
|
cwdassertpackage(multiout, "p2-external", p2,
|
||||||
@@ -1434,7 +1503,7 @@ 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);
|
||||||
assert(has(out.stderr, strings.concat("wrapper-hidden=", p1, "\n")));
|
assert(has(out.stderr, strings.concat("wrapper-hidden=", p1, "\n")));
|
||||||
assert(has(out.stderr, strings.concat("FAIL ", p1,
|
assert(has(out.stdout, strings.concat("FAIL ", p1,
|
||||||
" [p1] (test harness error 2)\n")));
|
" [p1] (test harness error 2)\n")));
|
||||||
assert(!has(out.stdout, "p1-internal cwd="));
|
assert(!has(out.stdout, "p1-internal cwd="));
|
||||||
cwdassertpackage(out.stdout, "dep-init", p2,
|
cwdassertpackage(out.stdout, "dep-init", p2,
|
||||||
@@ -2007,9 +2076,10 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
runcommand(root, "assert", av,
|
runcommand(root, "assert", av,
|
||||||
(30i64 * (time.second: i64)): time.duration, &out);
|
(30i64 * (time.second: i64)): time.duration, &out);
|
||||||
expectexit(&out, 1);
|
expectexit(&out, 1);
|
||||||
assert(has(out.stdout,
|
assert(has(out.stdout, strings.concat(
|
||||||
"assert_failure.assertion_failure ... FAIL (exit 1)\n"));
|
"assert_failure.assertion_failure ... synthetic assertion failure",
|
||||||
assert(!has(out.stderr, "captures:"));
|
"FAIL (exit 1)\n")));
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
|
||||||
let nonzeroav: []str = [driver("ww"), "test",
|
let nonzeroav: []str = [driver("ww"), "test",
|
||||||
packagepath("cases/nonzero")];
|
packagepath("cases/nonzero")];
|
||||||
@@ -2018,6 +2088,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
expectexit(&out, 1);
|
expectexit(&out, 1);
|
||||||
assert(has(out.stdout,
|
assert(has(out.stdout,
|
||||||
"nonzero.deliberate_nonzero_exit ... FAIL (exit 7)\n"));
|
"nonzero.deliberate_nonzero_exit ... FAIL (exit 7)\n"));
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
|
||||||
let prematureav: []str = [driver("ww"), "test",
|
let prematureav: []str = [driver("ww"), "test",
|
||||||
packagepath("cases/premature")];
|
packagepath("cases/premature")];
|
||||||
@@ -2026,6 +2097,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
expectexit(&out, 1);
|
expectexit(&out, 1);
|
||||||
assert(has(out.stdout,
|
assert(has(out.stdout,
|
||||||
"premature.clean_exit_without_completion ... HARNESS (incomplete result)\n"));
|
"premature.clean_exit_without_completion ... HARNESS (incomplete result)\n"));
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
|
||||||
let signalav: []str = [driver("ww"), "test",
|
let signalav: []str = [driver("ww"), "test",
|
||||||
packagepath("cases/signal")];
|
packagepath("cases/signal")];
|
||||||
@@ -2034,6 +2106,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
expectexit(&out, 1);
|
expectexit(&out, 1);
|
||||||
assert(has(out.stdout,
|
assert(has(out.stdout,
|
||||||
"signal.signal_is_not_exit ... FAIL (signal 15)\n"));
|
"signal.signal_is_not_exit ... FAIL (signal 15)\n"));
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
|
|
||||||
let multiav: []str = [driver("ww"), "test",
|
let multiav: []str = [driver("ww"), "test",
|
||||||
packagepath("cases/multi_fail")];
|
packagepath("cases/multi_fail")];
|
||||||
@@ -2044,6 +2117,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
|||||||
"1 passed, 2 failed, 0 skipped, 0 harness errors\n"));
|
"1 passed, 2 failed, 0 skipped, 0 harness errors\n"));
|
||||||
assert(has(out.stdout,
|
assert(has(out.stdout,
|
||||||
"3 discovered, 3 selected, 3 started, 3 completed\n"));
|
"3 discovered, 3 selected, 3 started, 3 completed\n"));
|
||||||
|
assert(out.stderr.len == 0);
|
||||||
clean(root);
|
clean(root);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -83,6 +83,13 @@ fn childmode(args: []str) void = {
|
|||||||
if (!writeexact(os.STDERR_FILENO, "route stderr\n")) { os.exit(135); };
|
if (!writeexact(os.STDERR_FILENO, "route stderr\n")) { os.exit(135); };
|
||||||
os.exit(0);
|
os.exit(0);
|
||||||
};
|
};
|
||||||
|
if (eq(mode, "merge")) {
|
||||||
|
if (!writeexact(os.STDOUT_FILENO, "out 1\n")) { os.exit(146); };
|
||||||
|
if (!writeexact(os.STDERR_FILENO, "err 1\n")) { os.exit(147); };
|
||||||
|
if (!writeexact(os.STDOUT_FILENO, "out 2\n")) { os.exit(148); };
|
||||||
|
if (!writeexact(os.STDERR_FILENO, "err 2\n")) { os.exit(149); };
|
||||||
|
os.exit(0);
|
||||||
|
};
|
||||||
if (eq(mode, "signal")) {
|
if (eq(mode, "signal")) {
|
||||||
os.kill(os.getpid(), os.SIGKILL);
|
os.kill(os.getpid(), os.SIGKILL);
|
||||||
os.exit(120);
|
os.exit(120);
|
||||||
@@ -251,6 +258,16 @@ fn main() void = {
|
|||||||
assert(eq(readfile(c.stdoutpath), "route stdout\n"));
|
assert(eq(readfile(c.stdoutpath), "route stdout\n"));
|
||||||
assert(eq(readfile(c.stderrpath), "route stderr\n"));
|
assert(eq(readfile(c.stderrpath), "route stderr\n"));
|
||||||
|
|
||||||
|
fillcommand(&c, self, "merge", root, "merge",
|
||||||
|
time.second, grace);
|
||||||
|
let mergeerr: str = c.stderrpath;
|
||||||
|
c.stderrpath = c.stdoutpath;
|
||||||
|
exec.run(&c, &r);
|
||||||
|
assert(r.termination == exec.termination.EXIT && r.code == 0);
|
||||||
|
assert(r.errno == 0 && r.cleanuperrno == 0);
|
||||||
|
assert(eq(readfile(c.stdoutpath), "out 1\nerr 1\nout 2\nerr 2\n"));
|
||||||
|
assert(!os.exists(mergeerr));
|
||||||
|
|
||||||
fillcommand(&c, self, "signal", root, "signal",
|
fillcommand(&c, self, "signal", root, "signal",
|
||||||
time.second, grace);
|
time.second, grace);
|
||||||
exec.run(&c, &r);
|
exec.run(&c, &r);
|
||||||
@@ -353,6 +370,16 @@ fn main() void = {
|
|||||||
|| cr.errno != 0 || cr.cleanuperrno != 0) { os.exit(139); };
|
|| cr.errno != 0 || cr.cleanuperrno != 0) { os.exit(139); };
|
||||||
if (!eq(readfile(cc.stdoutpath), "route stdout\n")) { os.exit(140); };
|
if (!eq(readfile(cc.stdoutpath), "route stdout\n")) { os.exit(140); };
|
||||||
if (!eq(readfile(cc.stderrpath), "route stderr\n")) { os.exit(141); };
|
if (!eq(readfile(cc.stderrpath), "route stderr\n")) { os.exit(141); };
|
||||||
|
fillcommand(&cc, self, "merge", root, "closedmerge",
|
||||||
|
time.second, grace);
|
||||||
|
let closedmergeerr: str = cc.stderrpath;
|
||||||
|
cc.stderrpath = cc.stdoutpath;
|
||||||
|
exec.run(&cc, &cr);
|
||||||
|
if (cr.termination != exec.termination.EXIT || cr.code != 0
|
||||||
|
|| cr.errno != 0 || cr.cleanuperrno != 0) { os.exit(150); };
|
||||||
|
if (!eq(readfile(cc.stdoutpath),
|
||||||
|
"out 1\nerr 1\nout 2\nerr 2\n")) { os.exit(151); };
|
||||||
|
if (os.exists(closedmergeerr)) { os.exit(152); };
|
||||||
os.exit(0);
|
os.exit(0);
|
||||||
};
|
};
|
||||||
let closedstatus: i32 = waitchild(closedprobe);
|
let closedstatus: i32 = waitchild(closedprobe);
|
||||||
@@ -382,12 +409,13 @@ fn main() void = {
|
|||||||
let captures: []str = [
|
let captures: []str = [
|
||||||
"exit0.out", "exit0.err", "exitdesc.out", "exitdesc.err",
|
"exit0.out", "exit0.err", "exitdesc.out", "exitdesc.err",
|
||||||
"exit7.out", "exit7.err", "exit127.out", "exit127.err",
|
"exit7.out", "exit7.err", "exit127.out", "exit127.err",
|
||||||
"route.out", "route.err", "signal.out", "signal.err",
|
"route.out", "route.err", "merge.out", "signal.out", "signal.err",
|
||||||
"execfail.out", "execfail.err", "pastfail.out", "pastfail.err",
|
"execfail.out", "execfail.err", "pastfail.out", "pastfail.err",
|
||||||
"chdirfail.out", "chdirfail.err", "timeout.out", "timeout.err",
|
"chdirfail.out", "chdirfail.err", "timeout.out", "timeout.err",
|
||||||
"exists.out", "runinterrupt.out", "runinterrupt.err",
|
"exists.out", "runinterrupt.out", "runinterrupt.err",
|
||||||
"runinterrupt.ready",
|
"runinterrupt.ready",
|
||||||
"closed.out", "closed.err", "interrupt.out", "interrupt.err",
|
"closed.out", "closed.err", "closedmerge.out",
|
||||||
|
"interrupt.out", "interrupt.err",
|
||||||
];
|
];
|
||||||
let i: i32 = 0;
|
let i: i32 = 0;
|
||||||
for (i < captures.len) {
|
for (i < captures.len) {
|
||||||
|
|||||||
Reference in New Issue
Block a user