From fc8483da0780aa748a40c69f7964e97917da968e Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 22 Oct 2026 17:58:40 +0200 Subject: [PATCH] mind-body tests: the fake claude is bounded, so a broken rule fails fast instead of hanging The real-child tests use bash read -t, and the whole-run test fails on a quiet stream instead of waiting out the poll. Found by cargo-mutants: three read_usage mutants timed out at 180 s. Co-Authored-By: Claude Sonnet 5.5 --- crates/mind-body/src/self_run/tests.rs | 29 +++++++++++++++++++++++--- crates/mind-body/tests/self_run.rs | 2 +- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/crates/mind-body/src/self_run/tests.rs b/crates/mind-body/src/self_run/tests.rs index a385e27..c9ce971 100644 --- a/crates/mind-body/src/self_run/tests.rs +++ b/crates/mind-body/src/self_run/tests.rs @@ -613,7 +613,7 @@ mod process { /// A fake `claude`: the shell script `body`, in a directory that outlives the child. fn script(dir: &tempfile::TempDir, body: &str) -> PathBuf { let path = dir.path().join("claude"); - std::fs::write(&path, format!("#!/bin/sh\n{body}\n")).unwrap(); + std::fs::write(&path, format!("#!/bin/bash\n{body}\n")).unwrap(); std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o755)).unwrap(); path } @@ -694,13 +694,36 @@ mod process { let lines = [at("m1", 10), at("m2", CEILING_TOKENS)].join("\n"); let result = result_line(); // Prints two requests, waits for the flush on stdin, then the result, as a stand-in for a Self that obeys. - let body = format!("cat <<'EOF'\n{lines}\nEOF\nread start\nread flush\ncat <<'EOF'\n{result}\nEOF\nwhile read more; do :; done\nexit 0"); + let body = format!("cat <<'EOF'\n{lines}\nEOF\nread -t 5 start\nread -t 5 flush\ncat <<'EOF'\n{result}\nEOF\nwhile read more; do :; done\nexit 0"); let program = script(&dir, &body); let mind = with_run(); + // A read that times out here means the stream stopped, never a wait worth 60 s: + // fail at once instead of hanging the suite. + struct Prompt(Box); + impl Child for Prompt { + fn write_line(&mut self, line: &str) { + self.0.write_line(line) + } + fn close_input(&mut self) { + self.0.close_input() + } + fn next_line(&mut self, timeout: StdDuration) -> Read { + match self.0.next_line(StdDuration::from_secs(20).min(timeout)) { + Read::Timeout => panic!("the fake claude went quiet"), + read => read, + } + } + fn interrupt(&mut self) { + self.0.interrupt() + } + fn wait(&mut self) -> Option { + self.0.wait() + } + } struct Spawn(PathBuf); impl Spawner for Spawn { fn spawn(&self, args: &[String]) -> Result> { - Ok(start(&self.0, args)) + Ok(Box::new(Prompt(start(&self.0, args)))) } } let mut out = Vec::new(); diff --git a/crates/mind-body/tests/self_run.rs b/crates/mind-body/tests/self_run.rs index a1e9c46..b4a8773 100644 --- a/crates/mind-body/tests/self_run.rs +++ b/crates/mind-body/tests/self_run.rs @@ -182,7 +182,7 @@ mod lane { ) }; let script = format!( - "#!/bin/sh\necho \"$@\" > {record}\necho '{first}'\necho '{second}'\nread start\nread flush\necho \"$start\" >> {record}\necho \"$flush\" >> {record}\necho '{{\"type\":\"result\"}}'\nwhile read more; do :; done\nexit 0\n", + "#!/bin/bash\necho \"$@\" > {record}\necho '{first}'\necho '{second}'\nread -t 5 start\nread -t 5 flush\necho \"$start\" >> {record}\necho \"$flush\" >> {record}\necho '{{\"type\":\"result\"}}'\nwhile read more; do :; done\nexit 0\n", record = record.display(), first = request("msg_1", 100), second = request("msg_2", 299_999),