use super::*;
#[test]
fn wrap_count_matches_wrap_runs_for_any_run_split() {
let red = Color::Red;
let blue = Color::Blue;
let long = "x".repeat(50);
let cases = [
"",
"abc",
"hello world this is a longer sentence to wrap",
"日本語のテキストを折り返す", "a日b本c語d",
long.as_str(),
];
for text in cases {
for budget in [1usize, 2, 3, 4, 7, 10, 13, 80] {
let mut splits = vec![0usize, text.len()];
for (i, _) in text.char_indices() {
splits.push(i);
}
for &cut in &splits {
if !text.is_char_boundary(cut) {
continue;
}
let runs = vec![
(red, text[..cut].to_string()),
(blue, text[cut..].to_string()),
];
let got = wrap_runs(&runs, budget, None).len();
assert_eq!(
got,
wrap_count(text, budget),
"text={text:?} budget={budget} cut={cut}"
);
}
}
}
}
#[test]
fn wrap_off_keeps_one_display_line_per_row() {
let mut app = app_with(LONG_DIFF);
app.view = View::Unified;
app.rebuild_file_spans();
app.recompute_file_span();
render(&mut app, 40, 20);
assert!(!app.wrap);
let (s, e) = app.file_range();
assert_eq!(app.display_lines(s, e), e - s);
assert!((s..e).all(|i| app.row_h(i) == 1));
}
#[test]
fn long_line_wraps_into_several_display_lines() {
let mut app = app_with(LONG_DIFF);
app.view = View::Unified;
app.rebuild_file_spans();
app.recompute_file_span();
app.toggle_wrap();
assert!(app.wrap);
render(&mut app, 40, 20);
let (s, e) = app.file_range();
let tall = (s..e).filter(|&i| app.row_h(i) > 1).count();
assert_eq!(tall, 1, "exactly the long code line wraps");
assert!(
app.display_lines(s, e) > e - s,
"wrapping adds display lines over the row count"
);
}
#[test]
fn click_below_a_wrapped_line_maps_to_the_next_row() {
let mut app = app_with(LONG_DIFF);
app.view = View::Unified;
app.rebuild_file_spans();
app.recompute_file_span();
app.toggle_wrap();
render(&mut app, 40, 20);
let (s, e) = app.file_range();
let tall = (s..e).find(|&i| app.row_h(i) > 1).expect("a wrapped row");
let next = (tall + 1..e)
.find(|&i| app.is_selectable_at(i))
.expect("a row after the wrapped one");
app.scroll = s;
let mut off = 0usize;
for i in s..tall {
off += app.row_h(i);
}
off += app.row_h(tall); let area = app.diff_area;
let row = area.y + off as u16;
click(
&mut app,
Rect {
x: area.x + 8,
y: row,
width: 1,
height: 1,
},
);
assert_eq!(app.selected, next, "click maps through the wrapped row");
}
#[test]
fn wrapping_a_real_patch_keeps_heights_and_render_in_sync() {
use crate::loader::load_patch;
let cs = load_patch(Some(Path::new("examples/rust-long-en.patch"))).unwrap();
let mut app = App::with_comments(cs, CommentStore::default());
app.wrap = true;
app.geom.dirty = true;
for view in [View::Unified, View::Split] {
if app.view != view {
app.toggle_view();
}
for w in [50u16, 80, 120, 200] {
render(&mut app, w, 30);
for _ in 0..40 {
app.move_by(1, app.height.max(1));
render(&mut app, w, 30);
}
}
}
}
#[test]
fn display_lines_prefix_sum_matches_a_direct_walk() {
let mut app = app_with(LONG_DIFF);
app.view = View::Unified;
app.rebuild_file_spans();
app.recompute_file_span();
app.toggle_wrap();
render(&mut app, 40, 20);
let (s, e) = app.file_range();
assert_eq!(app.geom.offsets.len(), app.active_len() + 1);
for a in s..=e {
for b in a..=e {
let walk: usize = (a..b).map(|i| app.row_h(i)).sum();
assert_eq!(app.display_lines(a, b), walk, "range {a}..{b}");
}
}
}
#[test]
fn widening_reclamps_scroll_off_the_end() {
let mut app = app_with(LONG_DIFF);
app.view = View::Unified;
app.rebuild_file_spans();
app.recompute_file_span();
app.toggle_wrap();
render(&mut app, 30, 4);
app.scroll_view(100);
render(&mut app, 30, 4);
render(&mut app, 400, 4);
let (s, e) = app.file_range();
assert!(
app.scroll <= app.max_scroll_row(s, e, app.height.max(1)),
"scroll {} must be clamped to max {}",
app.scroll,
app.max_scroll_row(s, e, app.height.max(1))
);
}
#[test]
fn toggling_wrap_recomputes_heights_and_holds_the_cursor() {
let mut app = app_with(LONG_DIFF);
app.view = View::Unified;
app.rebuild_file_spans();
app.recompute_file_span();
render(&mut app, 40, 6);
let before = app.selected;
app.toggle_wrap();
render(&mut app, 40, 6);
assert!(!app.geom.heights.is_empty());
assert_eq!(app.selected, before);
let (s, _) = app.file_range();
assert!(app.scroll >= s);
}