use justerm_core::{Engine, Match, SelectionSpan};
fn grid() -> Engine {
let mut e = Engine::new(4, 3);
e.feed(b"abcd\r\nefgh\r\nijkl");
e
}
#[test]
fn an_in_range_match_projects_its_row() {
let mut term = grid();
term.set_search_highlights(vec![Match {
start_line: 0,
start_col: 0,
end_line: 0,
end_col: 3,
}]);
assert_eq!(
term.frame().overlay.matches,
vec![SelectionSpan {
row: 0,
left: 0,
right: 3
}]
);
}
#[test]
fn an_out_of_range_start_column_does_not_delete_the_match_row() {
let mut term = grid();
term.set_search_highlights(vec![Match {
start_line: 0,
start_col: 80,
end_line: 0,
end_col: 3,
}]);
assert_eq!(
term.frame().overlay.matches,
vec![SelectionSpan {
row: 0,
left: 3,
right: 3
}],
"the row must still be painted, starting at the last column"
);
}
#[test]
fn the_active_match_intake_is_bounded_too() {
let mut term = grid();
term.set_active_search_match(Some(Match {
start_line: 1,
start_col: 99,
end_line: 1,
end_col: 3,
}));
assert_eq!(
term.frame().overlay.active_match,
vec![SelectionSpan {
row: 1,
left: 3,
right: 3
}]
);
}
#[test]
fn a_multi_row_match_keeps_every_row_including_the_first() {
let mut term = grid();
term.set_search_highlights(vec![Match {
start_line: 0,
start_col: 80,
end_line: 2,
end_col: 3,
}]);
assert_eq!(
term.frame().overlay.matches,
vec![
SelectionSpan {
row: 0,
left: 3,
right: 3
},
SelectionSpan {
row: 1,
left: 0,
right: 3
},
SelectionSpan {
row: 2,
left: 0,
right: 3
},
]
);
}
#[test]
fn the_bound_is_the_grid_width_not_the_printed_text() {
let mut term = Engine::new(4, 3);
term.feed(b"ab\r\ncdef");
term.set_search_highlights(vec![Match {
start_line: 0,
start_col: 80,
end_line: 0,
end_col: 99,
}]);
assert_eq!(
term.frame().overlay.matches,
vec![SelectionSpan {
row: 0,
left: 3,
right: 3
}]
);
}
#[test]
fn an_out_of_range_index_still_designates_nothing() {
let mut term = grid();
term.set_search_highlights(vec![Match {
start_line: 0,
start_col: 0,
end_line: 0,
end_col: 3,
}]);
term.set_active_search_highlight(Some(99));
assert!(term.frame().overlay.active_match.is_empty());
}
#[test]
fn the_row_axis_is_unchanged() {
let mut past_end = grid();
past_end.set_search_highlights(vec![Match {
start_line: 0,
start_col: 0,
end_line: 99,
end_col: 3,
}]);
assert_eq!(
past_end.frame().overlay.matches.len(),
3,
"an end line past the buffer clips to the visible rows"
);
let mut inverted = grid();
inverted.set_search_highlights(vec![Match {
start_line: 99,
start_col: 0,
end_line: 0,
end_col: 3,
}]);
assert!(
inverted.frame().overlay.matches.is_empty(),
"an inverted line pair yields nothing"
);
}
#[test]
fn no_span_escapes_the_grid_through_the_wire() {
let mut term = grid();
term.set_search_highlights(vec![
Match {
start_line: 0,
start_col: 80,
end_line: 0,
end_col: 3,
},
Match {
start_line: 1,
start_col: 0,
end_line: 1,
end_col: 99,
},
]);
term.set_active_search_match(Some(Match {
start_line: 2,
start_col: 77,
end_line: 2,
end_col: 3,
}));
let frame = term.frame();
let bytes = justerm_core::encode(&frame);
let decoded = justerm_core::decode(&bytes).expect("an out-of-range match still round-trips");
for span in decoded
.overlay
.matches
.iter()
.chain(decoded.overlay.active_match.iter())
{
assert!(
span.left < 4 && span.right < 4 && span.row < 3,
"escaped to the wire: {span:?}"
);
}
}
#[test]
fn a_stale_real_match_after_a_narrowing_resize_still_paints() {
let mut term = Engine::new(80, 24);
term.feed(include_bytes!("fixtures/alt_resize_htop.pre.raw"));
let found = term.search("CPU");
let stale = *found.first().expect("fixture: htop's header contains CPU");
assert!(
stale.start_col >= 40,
"fixture: the match must sit past the narrowed width to exercise this ({stale:?})"
);
term.resize(40, 24);
term.set_active_search_match(Some(stale));
let spans = term.frame().overlay.active_match;
assert_eq!(
spans.len(),
1,
"the stale designation must still paint one row, not disappear"
);
assert_eq!(
spans[0].left, 39,
"clamped onto the last column of the narrowed grid"
);
}