use std::time::Duration;
use termlens::{Error, Key, Terminal};
fn numbered(rows: u16, count: usize, scrollback: usize) -> termlens::Result<Terminal> {
Terminal::builder()
.size(40, rows)
.scrollback(scrollback)
.timeout(Duration::from_secs(10))
.args([
"-c",
&format!(
"i=1; while [ $i -le {count} ]; do printf 'line-%d\\n' $i; \
i=$((i+1)); done; printf 'READY'; read guard"
),
])
.spawn("/bin/sh")
}
#[test]
fn content_scrolled_off_the_top_is_still_assertable() -> termlens::Result<()> {
let mut t = numbered(6, 40, 1000)?;
t.wait_until(|s| s.contains("READY"))?;
let s = t.screen();
assert!(
!s.contains("line-1\n"),
"line-1 should have scrolled off:\n{s}"
);
assert!(
s.scrollback_text().contains("line-1\n"),
"history:\n{}",
s.scrollback_text()
);
assert!(s.full_text().contains("line-1\n"));
assert!(s.full_text().contains("line-40"));
assert!(s.scrollback_rows() >= 34, "rows: {}", s.scrollback_rows());
t.send(Key::Enter)?;
assert!(t.wait_exit()?.success());
Ok(())
}
#[test]
fn full_text_answers_without_knowing_which_region_holds_it() -> termlens::Result<()> {
let mut t = numbered(12, 10, 1000)?;
t.wait_until(|s| s.contains("READY"))?;
let s = t.screen();
assert_eq!(s.scrollback_rows(), 0, "nothing has scrolled yet");
assert!(s.full_text().contains("line-1\n"));
t.send(Key::Enter)?;
assert!(t.wait_exit()?.success());
let mut t = numbered(4, 10, 1000)?;
t.wait_until(|s| s.contains("READY"))?;
let s = t.screen();
assert!(s.scrollback_rows() > 0, "some rows must have scrolled");
assert!(s.full_text().contains("line-1\n"));
t.send(Key::Enter)?;
assert!(t.wait_exit()?.success());
Ok(())
}
#[test]
fn the_retention_bound_drops_the_oldest_rows() -> termlens::Result<()> {
let mut t = numbered(4, 60, 10)?;
t.wait_until(|s| s.contains("READY"))?;
let s = t.screen();
assert_eq!(s.scrollback_rows(), 10, "bounded at the configured length");
assert!(
!s.full_text().contains("line-1\n"),
"line-1 is far past the bound:\n{}",
s.full_text()
);
assert!(s.full_text().contains("line-60"));
t.send(Key::Enter)?;
assert!(t.wait_exit()?.success());
Ok(())
}
#[test]
fn retention_can_be_switched_off() -> termlens::Result<()> {
let mut t = numbered(4, 20, 0)?;
t.wait_until(|s| s.contains("READY"))?;
let s = t.screen();
assert_eq!(s.scrollback_rows(), 0);
assert_eq!(s.scrollback_text(), "");
assert_eq!(s.full_text(), s.text(), "full_text is then just the screen");
assert!(!s.full_text().contains("line-1\n"));
t.send(Key::Enter)?;
assert!(t.wait_exit()?.success());
Ok(())
}
#[test]
fn history_is_observable_from_a_predicate() -> termlens::Result<()> {
let mut t = Terminal::builder()
.size(40, 3)
.scrollback(100)
.timeout(Duration::from_secs(10))
.args([
"-c",
r"printf 'committed-block\n'; printf 'a\nb\nc\nd\n'; read guard",
])
.spawn("/bin/sh")?;
t.wait_until(|s| s.scrollback_text().contains("committed-block"))?;
assert!(!t.screen().contains("committed-block"));
t.send(Key::Enter)?;
assert!(t.wait_exit()?.success());
Ok(())
}
#[test]
fn a_wait_on_scrolled_off_text_names_the_history_in_its_error() -> termlens::Result<()> {
let mut t = numbered(3, 10, 100)?;
t.wait_until(|s| s.contains("READY"))?;
let scrolled = t.screen().scrollback_rows();
assert!(scrolled > 1, "the setup must have scrolled: {}", t.screen());
let err = t
.wait_until_for(|s| s.contains("line-1\n"), Duration::from_millis(300))
.expect_err("the text is in history, not on the grid");
assert!(matches!(err, Error::Timeout { .. }), "got: {err}");
let msg = err.to_string();
assert!(
msg.contains(&format!("{scrolled} rows have scrolled off the top")),
"{msg}"
);
assert!(msg.contains("full_text"), "the remedy is named: {msg}");
assert!(err.screen().unwrap().full_text().contains("line-1\n"));
t.send(Key::Enter)?;
let err = t
.wait_until(|s| s.contains("line-1\n"))
.expect_err("the child has exited");
assert!(matches!(err, Error::Eof { .. }), "got: {err}");
assert!(err.to_string().contains("scrolled off the top"), "{err}");
Ok(())
}
#[test]
fn a_wait_with_nothing_scrolled_carries_no_history_note() -> termlens::Result<()> {
let mut t = numbered(12, 3, 100)?;
t.wait_until(|s| s.contains("READY"))?;
assert_eq!(t.screen().scrollback_rows(), 0);
let err = t
.wait_until_for(|s| s.contains("absent"), Duration::from_millis(200))
.expect_err("never printed");
assert!(!err.to_string().contains("scrolled off"), "{err}");
t.send(Key::Enter)?;
assert!(t.wait_exit()?.success());
Ok(())
}