test/package/runtime/ was a manual README-driven corpus with no target. Its unique surface — the driver's single-file ww test leg, the unmet-expected-abort outcome, and the published -c binary's own CLI (repeated globs, -list, -timeout-ms, direct-binary timeout and descendant handling) — is now asserted by three new @test fns in package_test.ww; the README drops its manual-run instructions.
605 lines
21 KiB
Plaintext
605 lines
21 KiB
Plaintext
package package_test;
|
|
|
|
// Native end-to-end checks for the first single-directory package route.
|
|
// Make builds and invokes this one binary; discovery, subprocess ownership,
|
|
// output interpretation, and assertions remain WW code.
|
|
|
|
import os;
|
|
import os.exec;
|
|
import strings;
|
|
import temp;
|
|
import time;
|
|
|
|
type commandout = struct {
|
|
termination: exec.termination,
|
|
code: i32,
|
|
stdout: str,
|
|
stderr: str,
|
|
};
|
|
|
|
fn envrequired(name: str) str = {
|
|
match (os.getenv(name)) {
|
|
case let value: str => {
|
|
assert(value.len != 0);
|
|
return strings.dup(value);
|
|
};
|
|
case void => abort("missing package-test environment");
|
|
};
|
|
};
|
|
|
|
fn repo() str = { return envrequired("WW_PACKAGE_REPO"); };
|
|
|
|
fn driver(name: str) str = {
|
|
return strings.concat(repo(), "/out/bin/", name);
|
|
};
|
|
|
|
fn readfile(path: str) str = {
|
|
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
|
|
assert(fd >= 0);
|
|
let sr: (i64 | os.oserror) = os.filesize(fd);
|
|
let n: i64 = -1i64;
|
|
match (sr) {
|
|
case let v: i64 => n = v;
|
|
case let e: os.oserror => abort("filesize failed");
|
|
};
|
|
assert(n >= 0i64);
|
|
let b: []u8 = alloc([], (n + 1i64): u64)!;
|
|
b.len = (n + 1i64): i32;
|
|
let rr: (i64 | os.oserror) = os.readall(fd, b.ptr, n: u64);
|
|
os.close(fd);
|
|
let got: i64 = -1i64;
|
|
match (rr) {
|
|
case let v: i64 => got = v;
|
|
case let e: os.oserror => abort("read failed");
|
|
};
|
|
assert(got == n);
|
|
let ni: i32 = n: i32;
|
|
b[ni] = 0u8;
|
|
let out: str;
|
|
out.ptr = b.ptr;
|
|
out.len = n: i32;
|
|
return out;
|
|
};
|
|
|
|
fn writefile(path: str, content: str) void = {
|
|
let fd: i32 = os.open(path,
|
|
os.flag.WRONLY | os.flag.CREATE | os.flag.EXCL, 384i32);
|
|
assert(fd >= 0);
|
|
match (os.writeall(fd, content.ptr, content.len: u64)) {
|
|
case let n: i64 => assert(n == content.len: i64);
|
|
case let e: os.oserror => abort("write failed");
|
|
};
|
|
assert(os.close(fd) == 0);
|
|
};
|
|
|
|
fn runcommand(root: str, name: str, argv: []str,
|
|
lifetime: time.duration, out: *commandout) void = {
|
|
let c: exec.command;
|
|
c.path = argv[0];
|
|
c.argv = argv;
|
|
c.env = os.getenvs();
|
|
c.dir = repo();
|
|
c.stdoutpath = strings.concat(root, "/", name, ".stdout");
|
|
c.stderrpath = strings.concat(root, "/", name, ".stderr");
|
|
c.deadline = time.add(time.now(time.clock.monotonic), lifetime);
|
|
c.grace = (100i64 * (time.millisecond: i64)): time.duration;
|
|
let r: exec.result;
|
|
exec.run(&c, &r);
|
|
assert(r.errno == 0 && r.cleanuperrno == 0);
|
|
out.termination = r.termination;
|
|
out.code = r.code;
|
|
out.stdout = readfile(c.stdoutpath);
|
|
out.stderr = readfile(c.stderrpath);
|
|
};
|
|
|
|
fn clean(root: str) void = {
|
|
let av: []str = ["/bin/rm", "-rf", "--", root];
|
|
let c: exec.command;
|
|
c.path = av[0];
|
|
c.argv = av;
|
|
c.env = os.getenvs();
|
|
c.dir = "/";
|
|
c.stdoutpath = strings.concat(root, ".cleanup.stdout");
|
|
c.stderrpath = strings.concat(root, ".cleanup.stderr");
|
|
c.deadline = time.add(time.now(time.clock.monotonic), time.second);
|
|
c.grace = (50i64 * (time.millisecond: i64)): time.duration;
|
|
let r: exec.result;
|
|
exec.run(&c, &r);
|
|
assert(r.termination == exec.termination.EXIT && r.code == 0);
|
|
assert(os.remove(c.stdoutpath) == 0);
|
|
assert(os.remove(c.stderrpath) == 0);
|
|
};
|
|
|
|
fn fresh() str = { return strings.dup(temp.dir()); };
|
|
|
|
fn pos(haystack: str, needle: str) i32 = {
|
|
match (strings.index(haystack, needle)) {
|
|
case let n: i32 => return n;
|
|
case void => return -1;
|
|
};
|
|
};
|
|
|
|
fn has(haystack: str, needle: str) bool = {
|
|
return pos(haystack, needle) >= 0;
|
|
};
|
|
|
|
fn same(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 occurrences(haystack: str, needle: str) i32 = {
|
|
if (needle.len == 0 || haystack.len < needle.len) { return 0; };
|
|
let count: i32 = 0;
|
|
let i: i32 = 0;
|
|
for (i + needle.len <= haystack.len) {
|
|
let matches: bool = true;
|
|
let j: i32 = 0;
|
|
for (j < needle.len) {
|
|
if (haystack[i + j] != needle[j]) { matches = false; break; };
|
|
j += 1;
|
|
};
|
|
if (matches) { count += 1; i += needle.len; }
|
|
else { i += 1; };
|
|
};
|
|
return count;
|
|
};
|
|
|
|
fn expectexit(out: *commandout, code: i32) void = {
|
|
assert(out.termination == exec.termination.EXIT);
|
|
assert(out.code == code);
|
|
};
|
|
|
|
fn packagepath(relative: str) str = {
|
|
return strings.concat(repo(), "/test/package/", relative);
|
|
};
|
|
|
|
@test fn deterministic_routing_and_filters() void = {
|
|
let root: str = fresh();
|
|
let target: str = packagepath("routing");
|
|
let av: []str = [driver("ww"), "test", "-list", target];
|
|
let out: commandout;
|
|
runcommand(root, "list", av, (30i64 * (time.second: i64)): time.duration,
|
|
&out);
|
|
expectexit(&out, 0);
|
|
let first: i32 = pos(out.stdout, "routing.private_helper_first\n");
|
|
let second: i32 = pos(out.stdout, "routing.same_package\n");
|
|
let third: i32 = pos(out.stdout, "routing_test.external_package\n");
|
|
assert(first >= 0 && first < second && second < third);
|
|
assert(occurrences(out.stdout, "routing.private_helper_first\n") == 1);
|
|
assert(occurrences(out.stdout, "routing.same_package\n") == 1);
|
|
assert(occurrences(out.stdout, "routing_test.external_package\n") == 1);
|
|
|
|
let rav: []str = [driver("ww"), "test", target];
|
|
runcommand(root, "run", rav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(has(out.stdout, "routing.private_helper_first ... ok\n"));
|
|
assert(has(out.stdout, "routing.same_package ... ok\n"));
|
|
assert(has(out.stdout, "routing_test.external_package ... ok\n"));
|
|
assert(has(out.stdout,
|
|
"2 passed, 0 failed, 0 skipped, 0 harness errors\n"));
|
|
assert(has(out.stdout,
|
|
"1 passed, 0 failed, 0 skipped, 0 harness errors\n"));
|
|
|
|
let fav: []str = [driver("ww"), "test", "-run",
|
|
"routing.private_helper_first", "-filter", "external_package",
|
|
target];
|
|
runcommand(root, "filters", fav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(has(out.stdout, "routing.private_helper_first ... ok\n"));
|
|
assert(!has(out.stdout, "routing.same_package ..."));
|
|
assert(has(out.stdout, "routing_test.external_package ... ok\n"));
|
|
|
|
let nav: []str = [driver("ww"), "test", "-run", "no-such-*", target];
|
|
runcommand(root, "nomatch", nav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(occurrences(out.stdout, "[no matches]\n") == 2);
|
|
assert(occurrences(out.stdout,
|
|
"1 discovered, 0 selected, 0 started, 0 completed\n") == 1);
|
|
assert(occurrences(out.stdout,
|
|
"2 discovered, 0 selected, 0 started, 0 completed\n") == 1);
|
|
|
|
let lnav: []str = [driver("ww"), "test", "-list", "-run",
|
|
"no-such-*", target];
|
|
runcommand(root, "list-nomatch", lnav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(occurrences(out.stdout, "[no matches]\n") == 2);
|
|
clean(root);
|
|
};
|
|
|
|
@test fn imported_dependency_tests_do_not_leak() void = {
|
|
let root: str = fresh();
|
|
let base: str = packagepath("dependency");
|
|
let av: []str = [driver("ww"), "test", "-I", base,
|
|
strings.concat(base, "/root")];
|
|
let out: commandout;
|
|
runcommand(root, "dependency", av,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(has(out.stdout, "root.imported_production_only ... ok\n"));
|
|
assert(!has(out.stdout, "imported_dependency_test_must_not_leak"));
|
|
assert(has(out.stdout,
|
|
"1 discovered, 1 selected, 1 started, 1 completed\n"));
|
|
clean(root);
|
|
};
|
|
|
|
@test fn empty_and_invalid_package_classes() void = {
|
|
let root: str = fresh();
|
|
let av: []str = [driver("ww"), "test", packagepath("no_tests")];
|
|
let out: commandout;
|
|
runcommand(root, "notests", av,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(has(out.stdout, " [no tests]\n"));
|
|
assert(!has(out.stdout, " ... ok"));
|
|
|
|
let bad: []str = [driver("ww"), "test", packagepath("bad_external")];
|
|
runcommand(root, "badexternal", bad,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stderr,
|
|
"test package must match production package or <package>_test"));
|
|
|
|
let empty: str = strings.concat(root, "/empty");
|
|
assert(os.mkdir(empty, 448i32) == 0);
|
|
let emptyav: []str = [driver("ww"), "test", empty];
|
|
runcommand(root, "emptydir", emptyav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stderr, "directory contains no WW package sources"));
|
|
clean(root);
|
|
};
|
|
|
|
@test fn symlink_package_is_rejected() void = {
|
|
let root: str = fresh();
|
|
let real: str = strings.concat(root, "/real");
|
|
let link: str = strings.concat(root, "/link");
|
|
assert(os.mkdir(real, 448i32) == 0);
|
|
writefile(strings.concat(real, "/real.ww"),
|
|
"package real;\nexport fn value() int = { return 1; };\n");
|
|
let lav: []str = ["/bin/ln", "-s", real, link];
|
|
let out: commandout;
|
|
runcommand(root, "link", lav, time.second, &out);
|
|
expectexit(&out, 0);
|
|
let av: []str = [driver("ww"), "test", link];
|
|
runcommand(root, "reject", av,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stderr, "symlink traversal is not allowed"));
|
|
clean(root);
|
|
};
|
|
|
|
@test fn pass_skip_and_expected_abort() void = {
|
|
let root: str = fresh();
|
|
let av: []str = [driver("ww"), "test", packagepath("cases/pass")];
|
|
let out: commandout;
|
|
runcommand(root, "pass", av,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(has(out.stdout, "pass_test.alpha_pass ... ok\n"));
|
|
assert(has(out.stdout,
|
|
"pass_test.beta_skip ... SKIP: synthetic unavailable feature\n"));
|
|
assert(has(out.stdout, "pass_test.gamma_expected_abort ... ok\n"));
|
|
assert(has(out.stdout, "pass_test.delta_pass ... ok\n"));
|
|
assert(has(out.stdout,
|
|
"3 passed, 0 failed, 1 skipped, 0 harness errors\n"));
|
|
assert(has(out.stdout,
|
|
"4 discovered, 4 selected, 4 started, 4 completed\n"));
|
|
clean(root);
|
|
};
|
|
|
|
@test fn failures_are_distinct_and_clamped() void = {
|
|
let root: str = fresh();
|
|
let out: commandout;
|
|
let av: []str = [driver("ww"), "test",
|
|
packagepath("cases/assert_failure")];
|
|
runcommand(root, "assert", av,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stdout,
|
|
"assert_failure_test.assertion_failure ... FAIL (exit 1)\n"));
|
|
assert(!has(out.stderr, "captures:"));
|
|
|
|
let nonzeroav: []str = [driver("ww"), "test",
|
|
packagepath("cases/nonzero")];
|
|
runcommand(root, "nonzero", nonzeroav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stdout,
|
|
"nonzero_test.deliberate_nonzero_exit ... FAIL (exit 7)\n"));
|
|
|
|
let prematureav: []str = [driver("ww"), "test",
|
|
packagepath("cases/premature")];
|
|
runcommand(root, "premature", prematureav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stdout,
|
|
"premature_test.clean_exit_without_completion ... HARNESS (incomplete result)\n"));
|
|
|
|
let signalav: []str = [driver("ww"), "test",
|
|
packagepath("cases/signal")];
|
|
runcommand(root, "signal", signalav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stdout,
|
|
"signal_test.signal_is_not_exit ... FAIL (signal 15)\n"));
|
|
|
|
let multiav: []str = [driver("ww"), "test",
|
|
packagepath("cases/multi_fail")];
|
|
runcommand(root, "multifail", multiav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stdout,
|
|
"1 passed, 2 failed, 0 skipped, 0 harness errors\n"));
|
|
assert(has(out.stdout,
|
|
"3 discovered, 3 selected, 3 started, 3 completed\n"));
|
|
clean(root);
|
|
};
|
|
|
|
@test fn timeout_and_descendant_cleanup() void = {
|
|
let root: str = fresh();
|
|
let av: []str = [driver("ww"), "test", "-timeout-ms=100",
|
|
packagepath("cases/timeout")];
|
|
let out: commandout;
|
|
runcommand(root, "timeout", av,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stdout,
|
|
"timeout_test.timeout_clears_term_resistant_descendant ... FAIL(timeout)\n"));
|
|
assert(has(out.stdout,
|
|
"1 discovered, 1 selected, 1 started, 1 completed\n"));
|
|
|
|
let descendantav: []str = [driver("ww"), "test",
|
|
packagepath("cases/descendant")];
|
|
runcommand(root, "descendant", descendantav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(has(out.stdout,
|
|
"descendant_test.normal_return_clears_descendant ... ok\n"));
|
|
assert(!has(out.stdout, "HARNESS"));
|
|
clean(root);
|
|
};
|
|
|
|
@test fn cstage_wwstage_behavior_and_assembly_match() void = {
|
|
let root: str = fresh();
|
|
writefile(strings.concat(root, "/route.ww"),
|
|
"package route;\nexport fn value() int = { return 7; };\n");
|
|
writefile(strings.concat(root, "/a_test.ww"),
|
|
"package route;\n@test fn white() void = { assert(value() == 7); };\n");
|
|
writefile(strings.concat(root, "/z_test.ww"),
|
|
"package route_test;\nimport route;\n@test fn external() void = { assert(route.value() == 7); };\n");
|
|
|
|
let outc: commandout;
|
|
let outw: commandout;
|
|
let listc: []str = [driver("ww"), "test", "-list", root];
|
|
let listw: []str = [driver("ww_ww"), "test", "-list", root];
|
|
runcommand(root, "list-c", listc,
|
|
(30i64 * (time.second: i64)): time.duration, &outc);
|
|
runcommand(root, "list-ww", listw,
|
|
(30i64 * (time.second: i64)): time.duration, &outw);
|
|
expectexit(&outc, 0);
|
|
expectexit(&outw, 0);
|
|
assert(same(outc.stdout, outw.stdout));
|
|
assert(same(outc.stderr, outw.stderr));
|
|
|
|
let misusec: []str = [driver("ww"), "test", "-run"];
|
|
let misusew: []str = [driver("ww_ww"), "test", "-run"];
|
|
runcommand(root, "misuse-c", misusec, time.second, &outc);
|
|
runcommand(root, "misuse-ww", misusew, time.second, &outw);
|
|
expectexit(&outc, 2);
|
|
expectexit(&outw, 2);
|
|
assert(same(outc.stdout, outw.stdout));
|
|
assert(same(outc.stderr, outw.stderr));
|
|
assert(has(outc.stderr, "ww test: -run needs an argument\n"));
|
|
|
|
let timeoutc: []str = [driver("ww"), "test",
|
|
"-timeout-ms=4000000", root];
|
|
let timeoutw: []str = [driver("ww_ww"), "test",
|
|
"-timeout-ms=4000000", root];
|
|
runcommand(root, "timeout-misuse-c", timeoutc, time.second, &outc);
|
|
runcommand(root, "timeout-misuse-ww", timeoutw, time.second, &outw);
|
|
expectexit(&outc, 2);
|
|
expectexit(&outw, 2);
|
|
assert(same(outc.stdout, outw.stdout));
|
|
assert(same(outc.stderr, outw.stderr));
|
|
assert(has(outc.stderr, "usage: wwtest package"));
|
|
|
|
let cc: []str = [driver("ww"), "test", "-c", root];
|
|
runcommand(root, "compile-c", cc,
|
|
(30i64 * (time.second: i64)): time.duration, &outc);
|
|
expectexit(&outc, 0);
|
|
assert(has(outc.stdout, strings.concat(" -> ", root, "/route.test\n")));
|
|
assert(has(outc.stdout,
|
|
strings.concat(" -> ", root, "/route_test.test\n")));
|
|
let cwhite: str = readfile(strings.concat(root,
|
|
"/route.test.sepwork/__root.s"));
|
|
let cexternal: str = readfile(strings.concat(root,
|
|
"/route_test.test.sepwork/__root.s"));
|
|
// The explicit package `-c` outputs are caller-owned artifacts. Release
|
|
// the two exact C-stage trees before asking the WW driver to acquire the
|
|
// same stems; the driver never deletes a pre-existing `.sepwork` path.
|
|
clean(strings.concat(root, "/route.test.sepwork"));
|
|
clean(strings.concat(root, "/route_test.test.sepwork"));
|
|
|
|
let wc: []str = [driver("ww_ww"), "test", "-c", root];
|
|
runcommand(root, "compile-ww", wc,
|
|
(30i64 * (time.second: i64)): time.duration, &outw);
|
|
expectexit(&outw, 0);
|
|
assert(same(outc.stdout, outw.stdout));
|
|
assert(same(outc.stderr, outw.stderr));
|
|
assert(same(cwhite, readfile(strings.concat(root,
|
|
"/route.test.sepwork/__root.s"))));
|
|
assert(same(cexternal, readfile(strings.concat(root,
|
|
"/route_test.test.sepwork/__root.s"))));
|
|
clean(root);
|
|
};
|
|
|
|
fn runtimepath(relative: str) str = {
|
|
return strings.concat(repo(), "/test/package/runtime/", relative);
|
|
};
|
|
|
|
// The single-FILE `ww test <file>` route wraps the same in-binary test
|
|
// runtime as the directory-package route (cases/*), but through the
|
|
// driver's single-file leg. One row per outcome class keeps that leg's
|
|
// wiring honest; the runtime-internal classifications themselves are
|
|
// owned by the cases/* tests above.
|
|
@test fn singlefile_runtime_outcomes() void = {
|
|
let root: str = fresh();
|
|
let out: commandout;
|
|
let successav: []str = [driver("ww"), "test",
|
|
runtimepath("success_test.ww")];
|
|
runcommand(root, "sf-success", successav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(has(out.stdout, "alpha_pass ... ok\n"));
|
|
assert(has(out.stdout,
|
|
"beta_skip ... SKIP: synthetic unavailable feature\n"));
|
|
assert(has(out.stdout, "gamma_expected_abort ... ok\n"));
|
|
assert(has(out.stdout, "delta_pass ... ok\n"));
|
|
assert(has(out.stdout,
|
|
"3 passed, 0 failed, 1 skipped, 0 harness errors\n"));
|
|
assert(has(out.stdout,
|
|
"4 discovered, 4 selected, 4 started, 4 completed\n"));
|
|
|
|
let failav: []str = [driver("ww"), "test",
|
|
runtimepath("fail_test.ww")];
|
|
runcommand(root, "sf-fail", failav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stdout, "assertion_failure ... FAIL (exit 1)\n"));
|
|
|
|
let multiav: []str = [driver("ww"), "test",
|
|
runtimepath("multiple_fail_test.ww")];
|
|
runcommand(root, "sf-multi", multiav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stdout,
|
|
"0 passed, 2 failed, 0 skipped, 0 harness errors\n"));
|
|
|
|
let unmetav: []str = [driver("ww"), "test",
|
|
runtimepath("expected_abort_return_test.ww")];
|
|
runcommand(root, "sf-unmet", unmetav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stdout,
|
|
"expected_abort_must_happen ... FAIL (expected abort)\n"));
|
|
|
|
let premav: []str = [driver("ww"), "test",
|
|
runtimepath("premature_exit_test.ww")];
|
|
runcommand(root, "sf-premature", premav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stdout,
|
|
"clean_exit_without_completion ... HARNESS (incomplete result)\n"));
|
|
|
|
let sigav: []str = [driver("ww"), "test",
|
|
runtimepath("signal_test.ww")];
|
|
runcommand(root, "sf-signal", sigav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stdout, "signal_is_not_exit ... FAIL (signal 15)\n"));
|
|
clean(root);
|
|
};
|
|
|
|
// The published test binary's OWN command surface: repeated glob args,
|
|
// -list (with and without a glob), and -timeout-ms. Only a direct
|
|
// invocation of the `-c` artifact proves these; every driver route
|
|
// parses its flags before the binary sees them.
|
|
@test fn published_binary_cli() void = {
|
|
let root: str = fresh();
|
|
let out: commandout;
|
|
let stem: str = strings.concat(root, "/rtsuccess");
|
|
let cav: []str = [driver("ww"), "test", "-c", "-o", stem,
|
|
runtimepath("success_test.ww")];
|
|
runcommand(root, "publish", cav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
|
|
let gav: []str = [stem, "alpha*", "delta*"];
|
|
runcommand(root, "globs", gav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(has(out.stdout, "alpha_pass ... ok\n"));
|
|
assert(has(out.stdout, "delta_pass ... ok\n"));
|
|
assert(has(out.stdout,
|
|
"2 passed, 0 failed, 0 skipped, 0 harness errors\n"));
|
|
assert(has(out.stdout,
|
|
"4 discovered, 2 selected, 2 started, 2 completed\n"));
|
|
|
|
let lav: []str = [stem, "-list"];
|
|
runcommand(root, "list", lav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(same(out.stdout,
|
|
"alpha_pass\nbeta_skip\ngamma_expected_abort\ndelta_pass\n"));
|
|
|
|
let lgav: []str = [stem, "-list", "gamma*"];
|
|
runcommand(root, "list-glob", lgav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(same(out.stdout, "gamma_expected_abort\n"));
|
|
|
|
let tav: []str = [stem, "-timeout-ms=50", "alpha*"];
|
|
runcommand(root, "cli-timeout", tav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(has(out.stdout,
|
|
"1 passed, 0 failed, 0 skipped, 0 harness errors\n"));
|
|
clean(root);
|
|
};
|
|
|
|
// Direct-binary timeout classification and descendant handling. The
|
|
// runcommand deadline bounds the no-hang claims: a runner waiting on a
|
|
// SIGTERM-blocking or process-group-escaped pipe writer would trip it.
|
|
@test fn direct_binary_timeout_and_descendants() void = {
|
|
let root: str = fresh();
|
|
let out: commandout;
|
|
let tstem: str = strings.concat(root, "/rttimeout");
|
|
let tcav: []str = [driver("ww"), "test", "-c", "-o", tstem,
|
|
runtimepath("timeout_test.ww")];
|
|
runcommand(root, "publish-timeout", tcav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
let trun: []str = [tstem, "-timeout-ms=50"];
|
|
runcommand(root, "run-timeout", trun,
|
|
(20i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 1);
|
|
assert(has(out.stdout, "hang_times_out ... FAIL(timeout)\n"));
|
|
|
|
let lstem: str = strings.concat(root, "/rtlinger");
|
|
let lcav: []str = [driver("ww"), "test", "-c", "-o", lstem,
|
|
runtimepath("lingering_descendant_test.ww")];
|
|
runcommand(root, "publish-linger", lcav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
let lrun: []str = [lstem, "-timeout-ms=250"];
|
|
runcommand(root, "run-linger", lrun,
|
|
(20i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(has(out.stdout,
|
|
"returning_test_clears_descendant ... ok\n"));
|
|
|
|
let estem: str = strings.concat(root, "/rtescaped");
|
|
let ecav: []str = [driver("ww"), "test", "-c", "-o", estem,
|
|
runtimepath("escaped_descendant_test.ww")];
|
|
runcommand(root, "publish-escaped", ecav,
|
|
(30i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
let erun: []str = [estem, "-timeout-ms=250"];
|
|
runcommand(root, "run-escaped", erun,
|
|
(20i64 * (time.second: i64)): time.duration, &out);
|
|
expectexit(&out, 0);
|
|
assert(has(out.stdout,
|
|
"escaped_writer_does_not_hold_runner ... ok\n"));
|
|
clean(root);
|
|
};
|