commit 951b345c7aff3c88fb5d376302e00538641a1a1a
parent 2b74f41d3a6ea63706609e43fc6cc4098b3c5867
Author: triesap <tyson@radroots.org>
Date: Tue, 22 Sep 2026 22:30:51 +0000
test(hyf): prove stdio fork rollback and simultaneous stream pressure
- Build argv before owning descriptors so no pre-fork exception window can
leak the stdio pipes, and exercise the three-pipe fork rollback with an
injected failure through the same handler as a real fork failure
- Interleave stdout and stderr flooding so concurrent pressure on both
streams is exercised end to end without deadlock
- 68/68 provider-helper and 7/7 repo-local-process tests pass
Diffstat:
4 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/tests/parent_lifecycle.mojo b/tests/parent_lifecycle.mojo
@@ -78,11 +78,16 @@ struct PipeFds(Copyable, Movable):
@fieldwise_init
-struct PipeTriple(Movable):
+struct PipeTriple(Copyable, Movable):
var stdin_pipe: PipeFds
var stdout_pipe: PipeFds
var stderr_pipe: PipeFds
+ def __copyinit__(out self, existing: Self):
+ self.stdin_pipe = existing.stdin_pipe.copy()
+ self.stdout_pipe = existing.stdout_pipe.copy()
+ self.stderr_pipe = existing.stderr_pipe.copy()
+
def close_pipe(pipe: PipeFds):
close_fd(pipe.read_fd)
diff --git a/tests/stdio_process_helper.mojo b/tests/stdio_process_helper.mojo
@@ -152,8 +152,9 @@ def run_stdio_entrypoint_with_deadline(
arg1: String,
deadline_ms: Int,
) raises -> Value:
- var pipes = make_three_pipes()
-
+ # Build argv before owning any descriptors so no exception window can leak
+ # pipes between creation and the fork; fork failure alone is handled by the
+ # rollback helper.
var command = String("mojo")
var include_flag = String("-I")
var include_path = String("src")
@@ -181,6 +182,7 @@ def run_stdio_entrypoint_with_deadline(
process_arg1.as_c_string_slice()
)
+ var pipes = make_three_pipes()
var stdin_read_fd = pipes.stdin_pipe.read_fd
var stdin_write_fd = pipes.stdin_pipe.write_fd
var stdout_read_fd = pipes.stdout_pipe.read_fd
diff --git a/tests/stdio_stderr_flood_entrypoint.mojo b/tests/stdio_stderr_flood_entrypoint.mojo
@@ -1,7 +1,7 @@
-"""Test-only stdio entrypoint that floods stderr before reading stdin.
+"""Test-only stdio entrypoint that floods stdout and stderr interleaved.
-Exercises LC04: the parent must drain stderr concurrently with writing the
-request instead of deadlocking on a full stderr pipe, and must bound the
+Exercises LC04: the parent must drain stdout and stderr concurrently with
+writing the request instead of deadlocking on a full pipe, and must bound the
diagnostics it retains with a distinct overflow cause.
"""
@@ -9,9 +9,13 @@ from std.sys import stderr
def main():
- var chunk = String("")
+ var out_chunk = String("")
for _ in range(1000):
- chunk += "e"
+ out_chunk += "o"
+ var err_chunk = String("")
+ for _ in range(1000):
+ err_chunk += "e"
for _ in range(100):
- print(chunk, file=stderr)
+ print(out_chunk)
+ print(err_chunk, file=stderr)
print('{"ok":true}')
diff --git a/tests/test_provider_helpers.mojo b/tests/test_provider_helpers.mojo
@@ -21,6 +21,7 @@ from parent_lifecycle import (
descriptor_census,
dup2_fd,
fork_owned_or_close,
+ fork_owned_or_close3,
fork_pid,
make_pipe,
make_three_pipes,
@@ -1279,6 +1280,19 @@ def test_fork_failure_closes_owned_pipes() raises:
assert_equal(open_fd_count_checked(), before)
+def test_stdio_fork_failure_closes_all_owned_pipes() raises:
+ # LC01: the stdio helper's fork failure must close all three pipe pairs.
+ var before = open_fd_count_checked()
+ var pipes = make_three_pipes()
+ var message = ""
+ try:
+ _ = fork_owned_or_close3(pipes.copy(), True)
+ except e:
+ message = String(e)
+ assert_true(message.find("injected fork failure") >= 0)
+ assert_equal(open_fd_count_checked(), before)
+
+
def test_result_truth_rejects_duplicate_report_line() raises:
var stub = _owned_report_child(
0,