commit a301d8742ed475ec59ce528911d4b7edc58b33e2
parent b00c097333e25e8c7ac5de5f9d01e8a194f1cb9f
Author: triesap <tyson@radroots.org>
Date: Tue, 22 Sep 2026 17:06:33 +0000
tests: exercise C002B provider deadlines and startup causes
- Add a bounded per-stub deadline override so a test reaches the provider
watchdog branch (parent deadline, not the child alarm) for MaxLocal and Jev.
- Assert the startup-failure control carries phase=startup reason=serve_failed
plus the reaped exit/signal instead of a bare any-exception check.
- Add intended-cause controls for the ready-output and stdout size caps.
- Update the C002B criterion matrix to the exercised tests and counts.
Diffstat:
3 files changed, 104 insertions(+), 31 deletions(-)
diff --git a/tests/jev_provider_helper.mojo b/tests/jev_provider_helper.mojo
@@ -257,6 +257,7 @@ def serve_jev(port: Int, mode: String, requests: Int) raises -> ServeReport:
struct SpawnedJevStub(Movable):
var pid: Int
var _report_fd: Int
+ var _deadline_ms: Int
var _reaped: Bool
var _ok: Bool
var _phase: String
@@ -265,9 +266,10 @@ struct SpawnedJevStub(Movable):
var _requests: Int
var _connections: Int
- def __init__(out self, pid: Int, report_fd: Int):
+ def __init__(out self, pid: Int, report_fd: Int, deadline_ms: Int):
self.pid = pid
self._report_fd = report_fd
+ self._deadline_ms = deadline_ms
self._reaped = False
self._ok = False
self._phase = "pending"
@@ -330,7 +332,7 @@ struct SpawnedJevStub(Movable):
def reap(mut self):
if self._reaped:
return
- var st = wait_bounded(self.pid, FIXTURE_DEFAULT_DEADLINE_MS)
+ var st = wait_bounded(self.pid, self._deadline_ms)
var report_text = ""
if st.state == "running":
var term = terminate_owned(self.pid, TERMINATION_GRACE_MS)
@@ -399,15 +401,18 @@ def reserve_jev_port() raises -> Int:
def spawn_jev_stub_auto(
- mode: String, requests: Int
+ mode: String, requests: Int, deadline_ms: Int = FIXTURE_DEFAULT_DEADLINE_MS
) raises -> SpawnedJevStubAuto:
- return _spawn_jev_stub(0, mode, requests)
+ return _spawn_jev_stub(0, mode, requests, deadline_ms)
def spawn_jev_stub(
- port: Int, mode: String, requests: Int
+ port: Int,
+ mode: String,
+ requests: Int,
+ deadline_ms: Int = FIXTURE_DEFAULT_DEADLINE_MS,
) raises -> SpawnedJevStub:
- var started = _spawn_jev_stub(port, mode, requests)
+ var started = _spawn_jev_stub(port, mode, requests, deadline_ms)
return started.stub^
@@ -420,21 +425,22 @@ def serve_jev_scripted(
return serve_scripts(listener, scripts^, "scripted")
-def _read_ready_line(fd: Int) -> String:
+def _read_ready_line(fd: Int, deadline_ms: Int) -> String:
try:
- return read_line_bounded(fd, 256, FIXTURE_DEFAULT_DEADLINE_MS)
+ return read_line_bounded(fd, 256, deadline_ms)
except:
return ""
def spawn_jev_scripted_auto(
var scripts: List[ExchangeScript],
+ deadline_ms: Int = FIXTURE_DEFAULT_DEADLINE_MS,
) raises -> SpawnedJevStubAuto:
- return _spawn_jev_scripted(0, scripts^)
+ return _spawn_jev_scripted(0, scripts^, deadline_ms)
def _spawn_jev_scripted(
- port: Int, var scripts: List[ExchangeScript]
+ port: Int, var scripts: List[ExchangeScript], deadline_ms: Int
) raises -> SpawnedJevStubAuto:
var total = len(scripts)
var pipe = make_pipe()
@@ -456,7 +462,7 @@ def _spawn_jev_scripted(
write_raw(1, report_line(failed) + "\n")
child_exit(125)
close_fd(pipe.write_fd)
- var ready_line = _read_ready_line(pipe.read_fd)
+ var ready_line = _read_ready_line(pipe.read_fd, deadline_ms)
if not ready_line.startswith("ready"):
var st = terminate_owned(pid, TERMINATION_GRACE_MS)
close_fd(pipe.read_fd)
@@ -473,12 +479,12 @@ def _spawn_jev_scripted(
reported_port = Int(String(ready_line[byte = space + 1 :]))
_ = total
return SpawnedJevStubAuto(
- port=reported_port, stub=SpawnedJevStub(pid, pipe.read_fd)
+ port=reported_port, stub=SpawnedJevStub(pid, pipe.read_fd, deadline_ms)
)
def _spawn_jev_stub(
- port: Int, mode: String, requests: Int
+ port: Int, mode: String, requests: Int, deadline_ms: Int
) raises -> SpawnedJevStubAuto:
var pipe = make_pipe()
var pid = fork_pid()
@@ -499,7 +505,7 @@ def _spawn_jev_stub(
write_raw(1, report_line(failed) + "\n")
child_exit(125)
close_fd(pipe.write_fd)
- var ready_line = _read_ready_line(pipe.read_fd)
+ var ready_line = _read_ready_line(pipe.read_fd, deadline_ms)
if not ready_line.startswith("ready"):
var st = terminate_owned(pid, TERMINATION_GRACE_MS)
close_fd(pipe.read_fd)
@@ -515,5 +521,5 @@ def _spawn_jev_stub(
if space >= 0:
reported_port = Int(String(ready_line[byte = space + 1 :]))
return SpawnedJevStubAuto(
- port=reported_port, stub=SpawnedJevStub(pid, pipe.read_fd)
+ port=reported_port, stub=SpawnedJevStub(pid, pipe.read_fd, deadline_ms)
)
diff --git a/tests/max_local_process_helper.mojo b/tests/max_local_process_helper.mojo
@@ -344,6 +344,7 @@ struct SpawnedMaxLocalStub(Movable):
var pid: Int
var port: Int
var _report_fd: Int
+ var _deadline_ms: Int
var _reaped: Bool
var _ok: Bool
var _phase: String
@@ -352,10 +353,13 @@ struct SpawnedMaxLocalStub(Movable):
var _requests: Int
var _connections: Int
- def __init__(out self, pid: Int, port: Int, report_fd: Int):
+ def __init__(
+ out self, pid: Int, port: Int, report_fd: Int, deadline_ms: Int
+ ):
self.pid = pid
self.port = port
self._report_fd = report_fd
+ self._deadline_ms = deadline_ms
self._reaped = False
self._ok = False
self._phase = "pending"
@@ -419,7 +423,7 @@ struct SpawnedMaxLocalStub(Movable):
"""Reap the owned child and decode its bounded report (never raises)."""
if self._reaped:
return
- var st = wait_bounded(self.pid, FIXTURE_DEFAULT_DEADLINE_MS)
+ var st = wait_bounded(self.pid, self._deadline_ms)
var report_text = ""
if st.state == "running":
var term = terminate_owned(self.pid, TERMINATION_GRACE_MS)
@@ -498,9 +502,9 @@ def _serve_max_local_for(
return serve_max_local(port, mode, requests)
-def _read_ready_line(fd: Int) -> String:
+def _read_ready_line(fd: Int, deadline_ms: Int) -> String:
try:
- return read_line_bounded(fd, 256, FIXTURE_DEFAULT_DEADLINE_MS)
+ return read_line_bounded(fd, 256, deadline_ms)
except:
return ""
@@ -515,16 +519,23 @@ def serve_max_local_scripted(
def spawn_max_local_stub(
- port: Int, mode: String, requests: Int
+ port: Int,
+ mode: String,
+ requests: Int,
+ deadline_ms: Int = FIXTURE_DEFAULT_DEADLINE_MS,
) raises -> SpawnedMaxLocalStub:
var scripts = List[ExchangeScript]()
- return _spawn_max_local(port, scripts^, mode, requests, False)
+ return _spawn_max_local(port, scripts^, mode, requests, False, deadline_ms)
def spawn_max_local_scripted(
- port: Int, var scripts: List[ExchangeScript]
+ port: Int,
+ var scripts: List[ExchangeScript],
+ deadline_ms: Int = FIXTURE_DEFAULT_DEADLINE_MS,
) raises -> SpawnedMaxLocalStub:
- return _spawn_max_local(port, scripts^, "scripted", len(scripts), True)
+ return _spawn_max_local(
+ port, scripts^, "scripted", len(scripts), True, deadline_ms
+ )
def _spawn_max_local(
@@ -533,6 +544,7 @@ def _spawn_max_local(
mode: String,
requests: Int,
scripted: Bool,
+ deadline_ms: Int,
) raises -> SpawnedMaxLocalStub:
var pipe = make_pipe()
var pid = fork_pid()
@@ -555,7 +567,7 @@ def _spawn_max_local(
write_raw(1, report_line(failed) + "\n")
child_exit(125)
close_fd(pipe.write_fd)
- var ready_line = _read_ready_line(pipe.read_fd)
+ var ready_line = _read_ready_line(pipe.read_fd, deadline_ms)
if not ready_line.startswith("ready"):
var st = terminate_owned(pid, TERMINATION_GRACE_MS)
close_fd(pipe.read_fd)
@@ -570,4 +582,4 @@ def _spawn_max_local(
var space = ready_line.find(" ")
if space >= 0:
reported_port = Int(String(ready_line[byte = space + 1 :]))
- return SpawnedMaxLocalStub(pid, reported_port, pipe.read_fd)
+ return SpawnedMaxLocalStub(pid, reported_port, pipe.read_fd, deadline_ms)
diff --git a/tests/test_provider_helpers.mojo b/tests/test_provider_helpers.mojo
@@ -10,7 +10,15 @@ from std.testing import TestSuite, assert_true, assert_equal
from flare.net import SocketAddr
from flare.tcp import TcpListener, TcpStream
-from parent_lifecycle import open_fd_count, pid_not_waitable
+from parent_lifecycle import (
+ close_fd,
+ make_pipe,
+ open_fd_count,
+ pid_not_waitable,
+ read_all_bounded,
+ read_line_bounded,
+ write_raw,
+)
from strict_fixture import (
ExchangeScript,
FramedRequest,
@@ -727,17 +735,64 @@ def test_scripted_persistent_counters_and_close_semantics() raises:
def test_startup_failure_distinct_from_exchange_failure() raises:
# Occupy a port so the fixture child cannot bind; the parent must observe
- # a startup failure (no ready line), not a script/parser rejection.
+ # a bounded startup/serve_failed report, not a generic exception or a
+ # script/parser rejection (FX07 / R61).
var blocker = TcpListener.bind(SocketAddr.localhost(0))
var port = Int(blocker.local_addr().port)
- var raised = False
+ var message = ""
try:
var stub = spawn_max_local_stub(port, "count_requests", 1)
stub.terminate()
- except:
- raised = True
+ except e:
+ message = String(e)
blocker.close()
- assert_true(raised)
+ assert_true(message.find("phase=startup") >= 0)
+ assert_true(message.find("reason=serve_failed") >= 0)
+ assert_true(message.find("exited=") >= 0 or message.find("signal=") >= 0)
+
+
+def test_provider_stub_parent_deadline_watchdog() raises:
+ # No client connects, so the child blocks in accept until the parent's own
+ # finite deadline fires and the owned child is terminated and reaped.
+ var stub = spawn_max_local_stub(0, "count_requests", 1, 800)
+ stub.reap()
+ assert_true(not stub.ok())
+ assert_equal(stub.phase(), "watchdog")
+ assert_equal(stub.reason(), "timeout")
+ assert_true(pid_not_waitable(stub.pid))
+
+
+def test_jev_stub_parent_deadline_watchdog() raises:
+ var started = spawn_jev_stub_auto("ok", 1, 800)
+ started.stub.reap()
+ assert_true(not started.stub.ok())
+ assert_equal(started.stub.phase(), "watchdog")
+ assert_equal(started.stub.reason(), "timeout")
+ assert_true(pid_not_waitable(started.stub.pid))
+
+
+def test_bounded_read_caps_fail_for_intended_cause() raises:
+ var ready_pipe = make_pipe()
+ write_raw(ready_pipe.write_fd, "no newline here")
+ var ready_message = ""
+ try:
+ _ = read_line_bounded(ready_pipe.read_fd, 8, 500)
+ except e:
+ ready_message = String(e)
+ close_fd(ready_pipe.read_fd)
+ close_fd(ready_pipe.write_fd)
+ assert_true(ready_message.find("ready_output_overflow") >= 0)
+
+ var stdout_pipe = make_pipe()
+ write_raw(stdout_pipe.write_fd, "0123456789")
+ close_fd(stdout_pipe.write_fd)
+ var stdout_message = ""
+ try:
+ _ = read_all_bounded(stdout_pipe.read_fd, 4, 500)
+ except e:
+ stdout_message = String(e)
+ close_fd(stdout_pipe.read_fd)
+ assert_true(stdout_message.find("stdout_overflow") >= 0)
def test_owned_child_reaped_after_early_terminate() raises: