use crate::{clip_to_width, load_trend, redraw_frame, save_trend};
#[test]
fn redraw_grow_rewrites_from_old_top() {
let mut buf: Vec<u8> = Vec::new();
let frame = (0..14).map(|i| format!("line{i}")).collect::<Vec<_>>().join("\n");
let n = redraw_frame(&mut buf, &frame, 5, None);
assert_eq!(n, 14);
let s = String::from_utf8(buf).unwrap();
assert!(s.starts_with("\x1b[4F"), "must move up prev-1 to old top: {s:?}");
assert_eq!(s.matches("line13").count(), 1);
assert!(!s.contains("\x1b[1B"), "no shrink needed: {s:?}");
}
#[test]
fn redraw_shrink_clears_stale_rows_and_returns_to_new_bottom() {
let mut buf: Vec<u8> = Vec::new();
let frame = (0..5).map(|i| format!("line{i}")).collect::<Vec<_>>().join("\n");
let n = redraw_frame(&mut buf, &frame, 14, None);
assert_eq!(n, 5);
let s = String::from_utf8(buf).unwrap();
assert!(s.starts_with("\x1b[13F"), "must move up prev-1: {s:?}");
assert_eq!(s.matches("\n").count(), 4, "exactly n-1 line feeds: {s:?}");
let shrink = s.split("line4").nth(1).unwrap();
assert!(shrink.starts_with("\x1b[1B"), "down past frame, no clear: {shrink:?}");
assert_eq!(shrink.matches("\x1b[2K\x1b[1B").count(), 8, "stale rows: {shrink:?}");
assert!(shrink.ends_with("\x1b[2K\x1b[9F"), "clear old bottom + up prev-n: {shrink:?}");
}
#[test]
fn redraw_no_previous_frame_just_draws() {
let mut buf: Vec<u8> = Vec::new();
let n = redraw_frame(&mut buf, "a\nb", 0, None);
assert_eq!(n, 2);
let s = String::from_utf8(buf).unwrap();
assert_eq!(s, "\x1b[2K\ra\n\x1b[2K\rb", "no up-move on first frame, no trailing nl: {s:?}");
}
#[test]
fn clip_no_wrap_preserves_short_lines() {
assert_eq!(clip_to_width("abc", Some(10)), "abc");
assert_eq!(clip_to_width("abc", None), "abc");
}
#[test]
fn clip_trims_to_terminal_width_and_resets() {
let out = clip_to_width("█".repeat(20).as_str(), Some(12));
assert_eq!(out.chars().filter(|c| *c == '█').count(), 12);
assert!(out.ends_with("\x1b[0m"));
}
#[test]
fn clip_counts_visible_ignoring_color_codes() {
let styled = format!("\x1b[32m{}\x1b[0m", "x".repeat(30));
let out = clip_to_width(&styled, Some(10));
assert_eq!(out.chars().filter(|c| *c == 'x').count(), 10);
assert!(out.ends_with("\x1b[0m"));
}
#[test]
fn clip_treats_carriage_return_as_zero_width() {
let out = clip_to_width("\r\x1b[2Kxx", Some(2));
assert_eq!(out.matches('x').count(), 2);
}
#[test]
fn trend_roundtrips_across_home() {
let dir = std::env::temp_dir().join(format!("cmduse-trend-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let old = std::env::var_os("HOME");
std::env::set_var("HOME", &dir);
assert!(load_trend().is_none(), "no trend file yet");
save_trend(&[0.0, 0.5, 0.02]).unwrap();
assert_eq!(load_trend(), Some(vec![0.0, 0.5, 0.02]));
std::env::remove_var("HOME");
if let Some(h) = old {
std::env::set_var("HOME", h);
}
let _ = std::fs::remove_dir_all(&dir);
}