test/package: wire the runtime fixture set into package_test

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.
This commit is contained in:
2026-08-08 00:39:53 +09:00
parent 0b23f9fb31
commit 90af21e132
2 changed files with 180 additions and 42 deletions

View File

@@ -442,3 +442,163 @@ fn packagepath(relative: str) str = {
"/route_test.test.sepwork/__root.s")))); "/route_test.test.sepwork/__root.s"))));
clean(root); 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);
};

View File

@@ -1,45 +1,23 @@
# Native package-runtime fixtures # Package-runtime fixtures
These fixtures exercise the compiled package test binary, rather than a Single-file `@test` sources driven by `test/package/package_test.ww`
subprocess-per-assertion harness. Build the bootstrap tools once with `make all`, (the `make test-package` binary): `singlefile_runtime_outcomes`,
then run the focused cases from the repository root: `published_binary_cli`, and `direct_binary_timeout_and_descendants`.
They exercise the driver's single-file `ww test` leg and the published
`-c` binary's own command surface (repeated globs, `-list`,
`-timeout-ms`); the directory-package legs of the same outcome classes
are owned by `test/package/cases/*`.
```sh Fixture notes:
out/bin/ww test test/package/runtime/success_test.ww
out/bin/ww test -c -o /tmp/ww-runtime-test test/package/runtime/success_test.ww
/tmp/ww-runtime-test 'alpha*' 'delta*'
/tmp/ww-runtime-test -list
/tmp/ww-runtime-test -list 'gamma*'
/tmp/ww-runtime-test -timeout-ms=50 'alpha*'
out/bin/ww test test/package/runtime/fail_test.ww
out/bin/ww test test/package/runtime/multiple_fail_test.ww
out/bin/ww test test/package/runtime/expected_abort_return_test.ww
out/bin/ww test test/package/runtime/premature_exit_test.ww
out/bin/ww test test/package/runtime/signal_test.ww
out/bin/ww test -c -o /tmp/ww-runtime-timeout test/package/runtime/timeout_test.ww
/tmp/ww-runtime-timeout -timeout-ms=50
out/bin/ww test -c -o /tmp/ww-runtime-descendant test/package/runtime/lingering_descendant_test.ww
/tmp/ww-runtime-descendant -timeout-ms=250
out/bin/ww test -c -o /tmp/ww-runtime-escaped test/package/runtime/escaped_descendant_test.ww
/tmp/ww-runtime-escaped -timeout-ms=250
```
The success fixture reports two ordinary passes, one expected-abort pass, and - The runtime recognizes `expectabort(); os.exit(nonzero)` as an
one skip with its reason. The preserved test binary accepts repeated glob expected abort because WW's abort primitive itself terminates with a
arguments directly. List mode prints selected names without starting tests. normal exit status; the ABI cannot distinguish those two paths
Each test has a 30-second default timeout; `-timeout-ms=N` selects a positive without a future abort hook.
per-test timeout up to one hour for a direct test-binary invocation. - The lingering-descendant fixture blocks SIGTERM in its descendant, so
The current runtime recognizes `expectabort(); os.exit(nonzero)` as an expected the runner exercises its grace period and SIGKILL escalation before
abort because WW's abort primitive itself terminates with a normal exit status; draining the record.
the ABI cannot distinguish those two paths without a future abort hook. - The escaped-descendant fixture moves its inherited pipe writer into
The assertion, multiple-failure, unmet-expected-abort, premature-exit, and another process group for one second; the framed nonblocking protocol
signal fixtures fail respectively with a normal exit, two failures whose must return after the test leader and owned group complete, never
process status is still clamped to 1, an expected abort that never happens, an waiting for pipe EOF.
incomplete completion record, and signal 15; none may be mistaken for a pass.
The timeout fixture fails deterministically as `FAIL(timeout)`. The lingering
descendant fixture passes without waiting on the inherited control-pipe writer:
the descendant blocks SIGTERM, so the runner exercises its grace period and
SIGKILL escalation before draining the record.
The escaped-descendant fixture moves its inherited writer into another process
group for one second. The framed nonblocking protocol returns immediately after
the test leader and owned group complete; it never waits for pipe EOF.