test: address review gaps in timing and dynamic targets

This commit is contained in:
Nils Andreas Svee committed 2026-10-02 21:07:10 +07:00
1 parent f4eaba5d70
commit e2356afbcf
4 files changed
+211 -33

No files matched your search

+105
View File
@@ -256,3 +256,108 @@ fn server_command_line_binds_replace_the_environment_bind() {
);
}
}
// Exercise stdin replacement and EOF through the actual application process.
#[cfg(feature = "client")]
#[test]
fn targets_stdin_supersedes_the_active_set_and_stops_on_eof() {
use std::{
io::{BufRead, BufReader, Read, Write},
process::{Child, Stdio},
sync::mpsc,
thread,
time::{Duration, Instant},
};
struct ClientProcess(Child);
impl Drop for ClientProcess {
fn drop(&mut self) {
let _ = self.0.kill();
let _ = self.0.wait();
}
}
let server = in_tree_server::InTreeServer::start(ServerConfig::default());
let mut client = ClientProcess(
Command::new(env!("CARGO_BIN_EXE_irtt-client"))
.env_clear()
.args([
"--duration",
"0",
"--targets-stdin",
"--interval",
"20ms",
"--format",
"jsonl",
"--columns",
"target,event",
])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap(),
);
let stdout = client.0.stdout.take().unwrap();
let (tx, rx) = mpsc::channel();
let reader = thread::spawn(move || {
for line in BufReader::new(stdout).lines() {
if tx.send(line.unwrap()).is_err() {
break;
}
}
});
let mut stdin = client.0.stdin.take().unwrap();
let mut rows = Vec::new();
for target in ["first", "second"] {
writeln!(stdin, "{target}={}", server.addr).unwrap();
stdin.flush().unwrap();
let deadline = Instant::now() + Duration::from_secs(5);
loop {
let row = rx
.recv_timeout(deadline.saturating_duration_since(Instant::now()))
.expect("stdin target set did not produce a reply");
let replied = row.contains(&format!("\"target\":\"{target}\""))
&& row.contains("\"event\":\"echo_reply\"");
rows.push(row);
if replied {
break;
}
}
}
// Retirement can finish after the newer target's first reply.
let deadline = Instant::now() + Duration::from_secs(5);
while !rows.iter().any(|row| {
row.contains("\"target\":\"first\"") && row.contains("\"event\":\"session_closed\"")
}) {
rows.push(
rx.recv_timeout(deadline.saturating_duration_since(Instant::now()))
.expect("superseded stdin target did not retire"),
);
}
drop(stdin);
let deadline = Instant::now() + Duration::from_secs(5);
let status = loop {
if let Some(status) = client.0.try_wait().unwrap() {
break status;
}
assert!(
Instant::now() < deadline,
"client did not terminate on stdin EOF"
);
thread::sleep(Duration::from_millis(10));
};
reader.join().unwrap();
rows.extend(rx.try_iter());
let mut stderr = String::new();
client
.0
.stderr
.take()
.unwrap()
.read_to_string(&mut stderr)
.unwrap();
assert!(status.success(), "{stderr}");
assert!(rows
.iter()
.any(|row| row.contains("\"target\":\"second\"")
&& row.contains("\"event\":\"session_closed\"")));
}