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:
@@ -627,10 +627,39 @@ fn cwdassertrecord(text: str, label: str, cwd: str, pwd: str,
|
||||
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,
|
||||
testdata: str) void = {
|
||||
cwdassertrecord(text, label, dir, dir, "1", "yes", data, testdata,
|
||||
"eof");
|
||||
cwdassertmergedstreams(text, label, dir);
|
||||
};
|
||||
|
||||
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 cmdno: str = strings.concat(tree, "/cmdno");
|
||||
|
||||
writefile(strings.concat(probe, "/probe.ww"), strings.concat(
|
||||
"package probe;\n",
|
||||
"import os;\n",
|
||||
"import strings;\n",
|
||||
"fn put(value: str) void = {\n",
|
||||
" match (os.writeall(os.STDOUT_FILENO, value.ptr, value.len: u64)) {\n",
|
||||
let streamsource: str = strings.concat(
|
||||
"fn putfd(fd: i32, value: str) void = {\n",
|
||||
" match (os.writeall(fd, value.ptr, value.len: u64)) {\n",
|
||||
" case let n: i64 => assert(n == value.len: i64);\n",
|
||||
" case let e: os.oserror => abort(\"write failed\");\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",
|
||||
" put(label); put(\" \" ); put(key); put(\"=\");\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 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",
|
||||
" streams(label, cwd);\n",
|
||||
" put(label); put(\" cwd=\"); put(cwd); put(\"\\n\");\n",
|
||||
" put(label); put(\" 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 main() void = { probe.run(\"cmdno-main\"); };\n"));
|
||||
|
||||
let failnames: []str = ["reject", "p8", "p7"];
|
||||
let failnames: []str = ["reject", "p9", "p8", "p7"];
|
||||
i = 0;
|
||||
for (i < failnames.len) {
|
||||
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 p8: str = strings.concat(failtree, "/p8");
|
||||
let p9: str = strings.concat(failtree, "/p9");
|
||||
let reject: str = strings.concat(failtree, "/reject");
|
||||
writefile(strings.concat(p7, "/p7_test.ww"), strings.concat(
|
||||
"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",
|
||||
" time.sleep((2i64 * (time.second: i64)): time.duration, ",
|
||||
"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"),
|
||||
"package reject;\nexport fn value() i32 = { return 1; };\n");
|
||||
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.stderr, outw.stderr));
|
||||
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")));
|
||||
|
||||
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,
|
||||
"p8-data", "p8-testdata");
|
||||
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")));
|
||||
|
||||
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
|
||||
// nonempty caller input cannot create a partial persistent generation.
|
||||
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,
|
||||
"/ww-cwd-inherited-first", "2", "no", "caller-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.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,
|
||||
"/ww-cwd-inherited-first", "2", "yes", "caller-data",
|
||||
"caller-testdata", "data");
|
||||
cwdassertsplitstreams(outc.stdout, outc.stderr,
|
||||
"p4-internal-only", caller);
|
||||
assert(os.remove(strings.concat(caller, "/created.txt")) == 0);
|
||||
cwdassertpackage(multiout, "dep-init", p2, "p2-data", "p2-testdata");
|
||||
cwdassertpackage(multiout, "p2-external", p2,
|
||||
@@ -1434,7 +1503,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
(120i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
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")));
|
||||
assert(!has(out.stdout, "p1-internal cwd="));
|
||||
cwdassertpackage(out.stdout, "dep-init", p2,
|
||||
@@ -2007,9 +2076,10 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
runcommand(root, "assert", av,
|
||||
(30i64 * (time.second: i64)): time.duration, &out);
|
||||
expectexit(&out, 1);
|
||||
assert(has(out.stdout,
|
||||
"assert_failure.assertion_failure ... FAIL (exit 1)\n"));
|
||||
assert(!has(out.stderr, "captures:"));
|
||||
assert(has(out.stdout, strings.concat(
|
||||
"assert_failure.assertion_failure ... synthetic assertion failure",
|
||||
"FAIL (exit 1)\n")));
|
||||
assert(out.stderr.len == 0);
|
||||
|
||||
let nonzeroav: []str = [driver("ww"), "test",
|
||||
packagepath("cases/nonzero")];
|
||||
@@ -2018,6 +2088,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
expectexit(&out, 1);
|
||||
assert(has(out.stdout,
|
||||
"nonzero.deliberate_nonzero_exit ... FAIL (exit 7)\n"));
|
||||
assert(out.stderr.len == 0);
|
||||
|
||||
let prematureav: []str = [driver("ww"), "test",
|
||||
packagepath("cases/premature")];
|
||||
@@ -2026,6 +2097,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
expectexit(&out, 1);
|
||||
assert(has(out.stdout,
|
||||
"premature.clean_exit_without_completion ... HARNESS (incomplete result)\n"));
|
||||
assert(out.stderr.len == 0);
|
||||
|
||||
let signalav: []str = [driver("ww"), "test",
|
||||
packagepath("cases/signal")];
|
||||
@@ -2034,6 +2106,7 @@ fn cwdwritedata(dir: str, label: str) void = {
|
||||
expectexit(&out, 1);
|
||||
assert(has(out.stdout,
|
||||
"signal.signal_is_not_exit ... FAIL (signal 15)\n"));
|
||||
assert(out.stderr.len == 0);
|
||||
|
||||
let multiav: []str = [driver("ww"), "test",
|
||||
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"));
|
||||
assert(has(out.stdout,
|
||||
"3 discovered, 3 selected, 3 started, 3 completed\n"));
|
||||
assert(out.stderr.len == 0);
|
||||
clean(root);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user