use super::*;
fn dirty_indices(screen: &Screen) -> Vec<u16> {
screen.dirty_rows().map(|(idx, _)| idx).collect()
}
#[test]
fn fresh_screen_has_no_dirty_rows() {
let screen = make_screen(3, 5);
assert!(dirty_indices(&screen).is_empty());
}
#[test]
fn cell_write_marks_only_target_row_dirty() {
let mut screen = make_screen(4, 8);
screen.cup((3, 5));
screen.clear_dirty();
screen.text('A');
assert_eq!(dirty_indices(&screen), vec![2]);
}
#[test]
fn dirty_rows_yields_dirty_state_variant() {
let mut screen = make_screen(2, 3);
screen.text('X');
let collected: Vec<_> = screen.dirty_rows().collect();
assert_eq!(collected, vec![(0, DirtyState::Dirty)]);
}
#[test]
fn erase_row_marks_only_that_row_dirty() {
let mut screen = make_screen(3, 5);
screen.cup((2, 1));
screen.text('A');
screen.clear_dirty();
screen.el(2); assert_eq!(dirty_indices(&screen), vec![1]);
}
#[test]
fn clear_dirty_resets_every_row() {
let mut screen = make_screen(3, 5);
screen.text('A');
screen.cup((2, 1));
screen.text('B');
assert!(!dirty_indices(&screen).is_empty());
screen.clear_dirty();
assert!(dirty_indices(&screen).is_empty());
}
#[test]
fn resize_marks_all_rows_dirty() {
let mut screen = make_screen(2, 4);
screen.clear_dirty();
screen.set_size(TerminalSize { rows: 4, cols: 6 });
assert_eq!(
dirty_indices(&screen),
vec![0, 1, 2, 3],
"after a resize, every row (existing + grown) must be marked dirty for a full redraw",
);
}
#[test]
fn alt_screen_swap_marks_every_row_dirty() {
let mut screen = make_screen(3, 5);
screen.text('A');
screen.clear_dirty();
screen.enter_alternate_grid();
assert_eq!(
dirty_indices(&screen),
vec![0, 1, 2],
"entering the alt grid must mark every row dirty (the visible buffer flipped wholesale)",
);
screen.clear_dirty();
screen.exit_alternate_grid();
assert_eq!(
dirty_indices(&screen),
vec![0, 1, 2],
"exiting the alt grid must mark every row dirty (the visible buffer flipped back)",
);
}
#[test]
fn scroll_marks_scrolled_region_dirty() {
let mut screen = make_screen(3, 5);
screen.cup((1, 1));
screen.text('A');
screen.cup((2, 1));
screen.text('B');
screen.cup((3, 1));
screen.text('C');
screen.clear_dirty();
screen.lf();
assert_eq!(dirty_indices(&screen), vec![0, 1, 2]);
}
#[test]
fn dirty_rows_indices_ascending() {
let mut screen = make_screen(5, 5);
screen.cup((4, 1));
screen.text('B');
screen.cup((2, 1));
screen.text('A');
let indices = dirty_indices(&screen);
assert!(
indices.windows(2).all(|w| w[0] < w[1]),
"dirty_rows() must yield strictly ascending indices, got {indices:?}",
);
assert_eq!(indices, vec![0, 1, 3]);
}
#[test]
fn cursor_move_marks_old_and_new_rows_dirty() {
let mut screen = make_screen(8, 5);
screen.cup((3, 1));
screen.clear_dirty();
screen.cup((5, 1));
assert_eq!(dirty_indices(&screen), vec![2, 4]);
}
#[test]
fn cursor_col_only_move_marks_current_row_dirty() {
let mut screen = make_screen(5, 10);
screen.cup((3, 1));
screen.clear_dirty();
screen.cuf(5);
assert_eq!(dirty_indices(&screen), vec![2]);
}
#[test]
fn cursor_no_op_move_still_marks_row() {
let mut screen = make_screen(3, 5);
screen.cup((1, 1));
screen.clear_dirty();
screen.cha(1);
assert_eq!(dirty_indices(&screen), vec![0]);
}
#[test]
fn linefeed_marks_old_and_new_rows() {
let mut screen = make_screen(5, 5);
screen.cup((1, 1));
screen.clear_dirty();
screen.lf();
let indices = dirty_indices(&screen);
assert!(indices.contains(&0), "expected old row 0 in {indices:?}");
assert!(indices.contains(&1), "expected new row 1 in {indices:?}");
}
#[test]
fn cud_marks_old_and_new_rows_without_full_region() {
let mut screen = make_screen(8, 5);
screen.cup((3, 1));
screen.clear_dirty();
screen.cud(2);
assert_eq!(dirty_indices(&screen), vec![2, 4]);
}
#[test]
fn cup_then_clear_dirty_makes_dirty_rows_empty() {
let mut screen = make_screen(5, 5);
screen.cup((4, 1));
screen.clear_dirty();
assert_eq!(screen.dirty_rows().count(), 0);
}
#[test]
fn cursor_save_restore_marks_target_row() {
let mut screen = make_screen(8, 5);
screen.cup((3, 1));
screen.decsc();
screen.cup((5, 1));
screen.clear_dirty();
screen.decrc();
assert_eq!(dirty_indices(&screen), vec![2, 4]);
}