use justerm_core::Engine;
fn find(t: &Engine, needle: &str) -> (usize, usize) {
let mut hits = t.search(needle).into_iter();
let m = hits
.next()
.unwrap_or_else(|| panic!("`{needle}` is not in the buffer"));
assert!(
hits.next().is_none(),
"`{needle}` must be unique for this test to mean anything"
);
(m.start_line, m.start_col)
}
fn write_lines(t: &mut Engine, prefix: &str, count: usize) {
for i in 0..count {
t.feed(format!("{prefix}{i:02}\r\n").as_bytes());
}
}
#[test]
fn a_tracked_point_keeps_its_content_across_cap_eviction() {
let mut t = Engine::with_scrollback(20, 2, 10);
write_lines(&mut t, "tag", 12);
let before = find(&t, "tag09");
let id = t.track_point(before.0, before.1);
write_lines(&mut t, "pad", 3);
let now = find(&t, "tag09");
assert!(
now.0 < before.0,
"premise: the absolute index must have moved (before {before:?}, now {now:?})"
);
assert_eq!(
t.tracked_point(id),
Some(now),
"the tracked point must still name tag09 after eviction renumbered it"
);
}
fn filled(cols: usize, rows: usize) -> Engine {
let mut t = Engine::new(cols, rows);
for i in 0..rows {
if i > 0 {
t.feed(b"\r\n");
}
t.feed(&[b'a' + i as u8]);
}
t
}
#[test]
fn a_point_on_the_evicted_line_is_dropped() {
let mut t = Engine::with_scrollback(20, 2, 10);
write_lines(&mut t, "tag", 10);
let at = find(&t, "tag00");
assert_eq!(
at.0, 0,
"premise: tag00 must be the oldest line, i.e. the next to go"
);
let id = t.track_point(at.0, at.1);
assert_eq!(
t.tracked_point(id),
Some(at),
"premise: the point is live before the eviction"
);
write_lines(&mut t, "pad", 5);
assert!(
t.search("tag00").is_empty(),
"premise: tag00 must have left the buffer"
);
assert_eq!(
t.tracked_point(id),
None,
"a point whose line was evicted is gone, not clamped"
);
}
#[test]
fn a_tracked_point_follows_a_below_margin_shift() {
let mut t = filled(10, 5);
t.feed(b"\x1b[1;4r");
let before = find(&t, "e"); let id = t.track_point(before.0, before.1);
t.feed(b"\x1b[4;1H\n");
let now = find(&t, "e");
assert!(
now.0 > before.0,
"premise: the below-margin index must have risen (before {before:?}, now {now:?})"
);
assert_eq!(
t.tracked_point(id),
Some(now),
"the tracked point must still name `e`"
);
}
#[test]
fn a_tracked_point_rotates_with_an_in_screen_region_scroll() {
let mut t = filled(10, 5);
t.feed(b"\x1b[2;4r");
let inside = find(&t, "c");
let edge = find(&t, "b"); let below = find(&t, "e"); let above = find(&t, "a"); let inside_id = t.track_point(inside.0, inside.1);
let edge_id = t.track_point(edge.0, edge.1);
let below_id = t.track_point(below.0, below.1);
let above_id = t.track_point(above.0, above.1);
t.feed(b"\x1b[1S");
let now = find(&t, "c");
assert!(
now.0 < inside.0,
"premise: in-region content must have moved up (before {inside:?}, now {now:?})"
);
assert_eq!(
t.tracked_point(inside_id),
Some(now),
"an in-region point follows its content"
);
assert!(
t.search("b").is_empty(),
"premise: `b` must have left the buffer"
);
assert_eq!(
t.tracked_point(edge_id),
None,
"a point on the dropped edge is gone"
);
assert_eq!(
find(&t, "e"),
below,
"premise: content below the margin does not move"
);
assert_eq!(
t.tracked_point(below_id),
Some(below),
"a point below the region is untouched"
);
assert_eq!(
find(&t, "a"),
above,
"premise: content above the margin does not move"
);
assert_eq!(
t.tracked_point(above_id),
Some(above),
"a point above the region is untouched"
);
}
#[test]
fn a_tracked_point_maps_through_reflow() {
let mut t = Engine::with_scrollback(10, 3, 20);
t.feed(b"0123456789abcdef\r\n"); t.feed(b"needle\r\n");
let before = find(&t, "needle");
let id = t.track_point(before.0, before.1);
t.resize(20, 3);
let now = find(&t, "needle");
assert!(
now.0 < before.0,
"premise: the rewrap must have moved it (before {before:?}, now {now:?})"
);
assert_eq!(
t.tracked_point(id),
Some(now),
"the tracked point must still name `needle`"
);
}
#[test]
fn an_alt_tracked_point_dies_with_the_alt_screen() {
let mut t = Engine::new(20, 3);
t.feed(b"\x1b[?1049h");
t.feed(b"alt-needle");
let at = find(&t, "alt-needle");
let id = t.track_point(at.0, at.1);
assert_eq!(
t.tracked_point(id),
Some(at),
"premise: the point is live on the alt screen"
);
t.feed(b"\x1b[?1049l");
assert_eq!(
t.tracked_point(id),
None,
"leaving alt destroys what the point named"
);
}
#[test]
fn ris_drops_every_tracked_point() {
let mut t = Engine::new(20, 3);
t.feed(b"needle\r\n");
let at = find(&t, "needle");
let id = t.track_point(at.0, at.1);
t.feed(b"\x1bc");
assert_eq!(
t.tracked_point(id),
None,
"RIS emptied the buffer the point named"
);
}
#[test]
fn untrack_point_releases_the_id() {
let mut t = Engine::new(20, 3);
t.feed(b"needle\r\n");
let at = find(&t, "needle");
let id = t.track_point(at.0, at.1);
assert_eq!(
t.tracked_point(id),
Some(at),
"premise: live before release"
);
t.untrack_point(id);
assert_eq!(t.tracked_point(id), None);
t.untrack_point(id); }
#[test]
fn an_out_of_range_point_is_bounded_at_the_read() {
let mut t = Engine::new(10, 3);
t.feed(b"needle\r\n");
let id = t.track_point(9_999, 9_999);
let (line, col) = t
.tracked_point(id)
.expect("an out-of-range point is bounded, not dropped");
assert!(
line < 3,
"the line is bounded into the buffer's own range, got {line}"
);
assert_eq!(
col, 10,
"the column is bounded to the GRID width (ADR-0026 D4), domain [0, cols]"
);
}
#[test]
fn a_tracked_id_is_not_reissued_after_ris() {
let mut t = Engine::new(20, 3);
t.feed(b"needle\r\n");
let old = t.track_point(0, 0);
t.feed(b"\x1bc");
t.feed(b"fresh\r\n");
let new = t.track_point(0, 0);
assert_ne!(old, new, "RIS must not reissue a tracked id");
assert_eq!(t.tracked_point(old), None, "the pre-RIS id stays dead");
}
#[test]
fn an_alt_read_floors_at_the_alt_grid() {
let mut t = Engine::new(20, 3);
write_lines(&mut t, "tag", 6);
t.feed(b"\x1b[?1049h");
let floor = t.scrollback_len();
assert!(
floor > 0,
"premise: there must be primary history below the floor to land in"
);
let id = t.track_point(0, 0); assert_eq!(
t.tracked_point(id).map(|p| p.0),
Some(floor),
"an alt read floors at the alt grid, never at primary history"
);
}
#[test]
fn a_point_of_the_inactive_buffer_does_not_resolve() {
let mut t = Engine::new(20, 4);
write_lines(&mut t, "tag", 6);
let grid_at = find(&t, "tag05"); let history_at = find(&t, "tag02"); let grid_id = t.track_point(grid_at.0, grid_at.1);
let history_id = t.track_point(history_at.0, history_at.1);
assert_eq!(
t.tracked_point(grid_id),
Some(grid_at),
"premise: correct on the primary screen"
);
t.feed(b"\x1b[?1049h");
t.feed(b"X0\r\nX1\r\nX2");
assert!(
grid_at.0 >= t.scrollback_len(),
"premise: tag05 must be on the primary GRID, not in scrollback — that is the ambiguous half"
);
assert!(
grid_at.0 < t.scrollback_len() + 4,
"premise: that index is ALSO an alt-grid row index — the alt screen occupies \
[scrollback_len, scrollback_len + rows), so the stored number is ambiguous rather \
than merely out of range"
);
assert_eq!(
t.tracked_point(grid_id),
None,
"an inactive-buffer point does not resolve into the active frame"
);
assert_eq!(
t.tracked_point(history_id),
None,
"and that holds below the alt floor too — same rule, not a clamp"
);
t.feed(b"\x1b[?1049l");
assert_eq!(
t.tracked_point(grid_id),
Some(grid_at),
"leaving alt makes it resolvable again — it was never gone"
);
assert_eq!(
t.tracked_point(history_id),
Some(history_at),
"and so does the one in scrollback"
);
}
#[test]
fn a_tracked_point_rotates_on_a_linefeed_inside_a_region() {
let mut t = filled(10, 5);
t.feed(b"\x1b[2;4r");
let inside = find(&t, "c");
let edge = find(&t, "b"); let inside_id = t.track_point(inside.0, inside.1);
let edge_id = t.track_point(edge.0, edge.1);
t.feed(b"\x1b[4;1H\n");
let now = find(&t, "c");
assert!(
now.0 < inside.0,
"premise: the linefeed must have moved in-region content up"
);
assert_eq!(
t.tracked_point(inside_id),
Some(now),
"the linefeed site rotates too"
);
assert!(t.search("b").is_empty(), "premise: `b` left the buffer");
assert_eq!(t.tracked_point(edge_id), None);
}
#[test]
fn a_tracked_point_rotates_on_reverse_index() {
let mut t = filled(10, 5);
t.feed(b"\x1b[2;4r");
let inside = find(&t, "c");
let edge = find(&t, "d"); let inside_id = t.track_point(inside.0, inside.1);
let edge_id = t.track_point(edge.0, edge.1);
t.feed(b"\x1b[2;1H\x1bM");
let now = find(&t, "c");
assert!(
now.0 > inside.0,
"premise: RI must have moved in-region content DOWN (before {inside:?}, now {now:?})"
);
assert_eq!(
t.tracked_point(inside_id),
Some(now),
"the direction flag is honoured, not assumed"
);
assert!(
t.search("d").is_empty(),
"premise: the bottom edge left the buffer"
);
assert_eq!(
t.tracked_point(edge_id),
None,
"RI drops the bottom edge, not the top"
);
}
#[test]
fn a_reflow_that_evicts_releases_the_point() {
let mut t = Engine::with_scrollback(20, 2, 4);
for i in 0..3 {
t.feed(format!("tag{i:02}-cdefghijklmnop\r\n").as_bytes()); }
let at = find(&t, "tag00");
let id = t.track_point(at.0, at.1);
assert_eq!(
t.tracked_point(id),
Some(at),
"premise: live before the resize"
);
t.resize(5, 2);
assert!(
t.search("tag00").is_empty(),
"premise: the rewrap must have evicted tag00"
);
assert_eq!(
t.tracked_point(id),
None,
"released, not relocated to line 0"
);
}
#[test]
fn a_primary_point_survives_a_resize_taken_on_the_alt_screen() {
let mut t = Engine::with_scrollback(10, 3, 40);
t.feed(b"0123456789abcdef\r\n"); t.feed(b"needle\r\n");
let before = find(&t, "needle");
let id = t.track_point(before.0, before.1);
t.feed(b"\x1b[?1049h");
t.resize(20, 3); t.feed(b"\x1b[?1049l");
let now = find(&t, "needle");
assert!(
now.0 < before.0,
"premise: the rewrap moved it (before {before:?}, now {now:?})"
);
assert_eq!(
t.tracked_point(id),
Some(now),
"the alt-active branch reflows primary points too"
);
}
#[test]
fn an_alt_point_maps_through_a_resize_on_the_alt_screen() {
let mut t = Engine::with_scrollback(10, 3, 40);
write_lines(&mut t, "tag", 4);
t.feed(b"\x1b[?1049h");
t.feed(b"ALT-NEEDLE");
let before = find(&t, "ALT-NEEDLE");
let id = t.track_point(before.0, before.1);
t.resize(20, 3);
let now = find(&t, "ALT-NEEDLE");
assert_eq!(
t.tracked_point(id),
Some(now),
"an alt point rides the alt pane's own reflow"
);
}
#[test]
fn an_alt_point_on_a_departing_row_is_released_by_a_shrink() {
let mut t = Engine::with_scrollback(20, 3, 40);
write_lines(&mut t, "tag", 4);
t.feed(b"\x1b[?1049h");
t.feed(b"TOPROW\r\nMIDROW\r\nENDROW");
let top = find(&t, "TOPROW");
let mid = find(&t, "MIDROW");
let top_id = t.track_point(top.0, top.1);
let mid_id = t.track_point(mid.0, mid.1);
assert_eq!(
t.tracked_point(top_id),
Some(top),
"premise: live before the shrink"
);
t.resize(20, 2);
assert!(
t.search("TOPROW").is_empty(),
"premise: the shrink must have dropped TOPROW"
);
assert_eq!(
t.tracked_point(top_id),
None,
"a point on a departed alt row is released"
);
let mid_now = find(&t, "MIDROW");
assert_eq!(
t.tracked_point(mid_id),
Some(mid_now),
"and a surviving alt row is re-anchored, not left stale"
);
}
#[test]
fn a_primary_point_evicted_by_a_reflow_on_alt_is_released() {
let mut t = Engine::with_scrollback(20, 2, 4);
for i in 0..3 {
t.feed(format!("tag{i:02}-cdefghijklmnop\r\n").as_bytes());
}
let at = find(&t, "tag00");
let id = t.track_point(at.0, at.1);
t.feed(b"\x1b[?1049h");
t.resize(5, 2); t.feed(b"\x1b[?1049l");
assert!(
t.search("tag00").is_empty(),
"premise: the rewrap must have evicted tag00"
);
assert_eq!(
t.tracked_point(id),
None,
"released by the alt-active branch too, not clamped to 0"
);
}