use std::time::{Duration, Instant};
use termlens::{Error, Terminal};
mod common;
fn flood() -> termlens::Result<Terminal> {
let queries = r"\e[6n".repeat(1000);
common::spawn_emit(
Terminal::builder()
.size(80, 24)
.env_clear()
.timeout(Duration::from_secs(10)),
&["--raw-mode", "--raw", &queries, "DONE\n", "--sleep", "3s"],
)
}
#[test]
fn a_child_that_never_reads_its_replies_cannot_wedge_the_drain() {
let start = Instant::now();
let mut t = flood().expect("spawn");
t.wait_until(|s| s.contains("DONE"))
.expect("the drain kept running, so DONE arrived");
drop(t);
assert!(
start.elapsed() < Duration::from_secs(10),
"teardown took {:?} — the drain or the reap is blocking",
start.elapsed()
);
}
#[test]
fn undelivered_replies_are_named_where_the_kernel_makes_them_visible() {
let mut t = flood().expect("spawn");
t.wait_until(|s| s.contains("DONE"))
.expect("the drain kept running");
let err = t
.wait_until_for(|s| s.contains("never-appears"), Duration::from_millis(200))
.expect_err("the predicate can never hold");
let message = err.to_string();
assert!(matches!(err, Error::Timeout { .. }), "got: {err}");
assert!(
err.screen().is_some_and(|s| s.contains("DONE")),
"{message}"
);
#[cfg(target_os = "macos")]
assert!(
message.contains("not reading its input"),
"the backlog should be diagnosed: {message}"
);
}
#[test]
#[cfg_attr(
windows,
ignore = "ConPTY answers or eats the child's queries itself, so they never reach the responder (#149)"
)]
fn replies_still_reach_an_application_that_reads_them() -> termlens::Result<()> {
let mut t = common::spawn_emit(
Terminal::builder().timeout(Duration::from_secs(10)),
&[
"--raw-mode",
"abc",
"--csi",
"6n",
"\nunblocked:",
"--read",
"6",
"--wait",
],
)?;
t.wait_until(|s| s.contains("unblocked:E[1;4R"))?;
t.send(termlens::Key::Enter)?;
assert!(t.wait_exit()?.success());
Ok(())
}