use super::*;
const CLEAR: &str = "\u{001B}[2J\u{001B}[3J\u{001B}[H";
const BSU: &str = "\u{001B}[?2026h";
const ESU: &str = "\u{001B}[?2026l";
fn count(haystack: &[u8], needle: &str) -> usize {
let n = needle.as_bytes();
if n.is_empty() {
return 0;
}
let mut hits = 0;
let mut i = 0;
while i + n.len() <= haystack.len() {
if &haystack[i..i + n.len()] == n {
hits += 1;
i += n.len();
} else {
i += 1;
}
}
hits
}
fn rerender_output(frame: usize, height: usize) -> String {
let mut lines = vec!["#450 top".to_owned(), format!("frame {frame}")];
while lines.len() < height.saturating_sub(1) {
lines.push(String::new());
}
lines.push("#450 bottom".to_owned());
lines.join("\n")
}
fn run_rerender(viewport: usize, heights: &[usize]) -> Vec<u8> {
let mut w = FrameWriter::new();
let mut all = Vec::new();
for (frame, &h) in heights.iter().enumerate() {
let output = rerender_output(frame, h);
let params = FrameParams {
is_tty: true,
viewport_rows: viewport,
output: &output,
output_height: h,
static_output: "",
is_unmounting: false,
cursor_dirty: false,
cursor: None,
interactive: Some(true),
is_in_ci: false,
debug: false,
};
all.extend_from_slice(&w.write_frame(¶ms));
}
all
}
#[test]
fn initial_overflow_clears_zero() {
let bytes = run_rerender(3, &[4]);
assert_eq!(count(&bytes, CLEAR), 0);
}
#[test]
fn initial_fullscreen_clears_zero() {
let bytes = run_rerender(3, &[3]);
assert_eq!(count(&bytes, CLEAR), 0);
}
#[test]
fn height_minus_one_control_clears_zero() {
let bytes = run_rerender(6, &[5, 5, 5, 5, 5, 5]);
assert_eq!(count(&bytes, CLEAR), 0);
}
#[test]
fn shrink_from_fullscreen_clears_once() {
let bytes = run_rerender(6, &[6, 6, 5, 5, 5, 5]);
assert_eq!(count(&bytes, CLEAR), 1);
}
#[test]
fn shrink_from_overflow_clears_once() {
let bytes = run_rerender(6, &[7, 5, 5, 5]);
assert_eq!(count(&bytes, CLEAR), 1);
}
#[test]
fn full_height_rerender_clears_at_most_once() {
let bytes = run_rerender(6, &[6, 6, 6, 6, 6, 6]);
assert!(count(&bytes, CLEAR) <= 1);
assert_eq!(count(&bytes, CLEAR), 0);
}
#[test]
fn full_height_with_marker_clears_zero_before_unmount() {
let bytes = run_rerender(6, &[6, 6, 6, 6]);
assert_eq!(count(&bytes, CLEAR), 0);
}
#[test]
fn grow_to_fullscreen_clears_zero() {
let bytes = run_rerender(6, &[5, 5, 6, 6]);
assert_eq!(count(&bytes, CLEAR), 0);
}
#[test]
fn non_tty_grow_to_overflow_clears_zero() {
let mut w = FrameWriter::new();
let mut all = Vec::new();
for (frame, &h) in [2usize, 4].iter().enumerate() {
let output = rerender_output(frame, h);
let params = FrameParams {
is_tty: false,
viewport_rows: 3,
output: &output,
output_height: h,
static_output: "",
is_unmounting: false,
cursor_dirty: false,
cursor: None,
interactive: Some(true),
is_in_ci: false,
debug: false,
};
all.extend_from_slice(&w.write_frame(¶ms));
}
assert_eq!(count(&all, CLEAR), 0);
}
#[test]
fn non_tty_full_height_clears_zero() {
let mut w = FrameWriter::new();
let mut all = Vec::new();
for (frame, &h) in [6usize, 6, 6].iter().enumerate() {
let output = rerender_output(frame, h);
let params = FrameParams {
is_tty: false,
viewport_rows: 6,
output: &output,
output_height: h,
static_output: "",
is_unmounting: false,
cursor_dirty: false,
cursor: None,
interactive: Some(true),
is_in_ci: false,
debug: false,
};
all.extend_from_slice(&w.write_frame(¶ms));
}
assert_eq!(count(&all, CLEAR), 0);
}
#[test]
fn static_shrink_from_fullscreen_clears_once() {
let mut w = FrameWriter::new();
let mut all = Vec::new();
let heights = [6usize, 6, 5, 5, 5, 5];
for (frame, &h) in heights.iter().enumerate() {
let output = rerender_output(frame, h);
let static_output = if frame == 0 { "#450 static line\n" } else { "" };
let params = FrameParams {
is_tty: true,
viewport_rows: 6,
output: &output,
output_height: h,
static_output,
is_unmounting: false,
cursor_dirty: false,
cursor: None,
interactive: Some(true),
is_in_ci: false,
debug: false,
};
all.extend_from_slice(&w.write_frame(¶ms));
}
assert_eq!(count(&all, CLEAR), 1);
assert!(count(&all, "#450 static line") >= 1);
}
#[test]
fn viewport_shrink_into_overflow_clears_once() {
let mut w = FrameWriter::new();
let mut all = Vec::new();
let frames: [(usize, usize); 2] = [(6, 6), (5, 6)]; for (frame, &(viewport, h)) in frames.iter().enumerate() {
let output = rerender_output(frame, h);
let params = FrameParams {
is_tty: true,
viewport_rows: viewport,
output: &output,
output_height: h,
static_output: "",
is_unmounting: false,
cursor_dirty: false,
cursor: None,
interactive: Some(true),
is_in_ci: false,
debug: false,
};
all.extend_from_slice(&w.write_frame(¶ms));
}
assert_eq!(count(&all, CLEAR), 1);
}
fn steady_params<'a>(
output: &'a str,
interactive: Option<bool>,
is_in_ci: bool,
debug: bool,
) -> FrameParams<'a> {
FrameParams {
is_tty: true,
viewport_rows: 100, output,
output_height: 1,
static_output: "",
is_unmounting: false,
cursor_dirty: false,
cursor: None,
interactive,
is_in_ci,
debug,
}
}
#[test]
fn sync_and_will_render_wraps_in_bsu_esu() {
let mut w = FrameWriter::new();
let bytes = w.write_frame(&steady_params("hello", Some(true), false, false));
assert_eq!(count(&bytes, BSU), 1);
assert_eq!(count(&bytes, ESU), 1);
let s = String::from_utf8(bytes).unwrap();
assert!(s.find(BSU).unwrap() < s.find(ESU).unwrap());
}
#[test]
fn debug_never_wraps() {
let mut w = FrameWriter::new();
let bytes = w.write_frame(&steady_params("hello", Some(true), false, true));
assert_eq!(count(&bytes, BSU), 0);
assert_eq!(count(&bytes, ESU), 0);
assert_eq!(count(&bytes, CLEAR), 0);
assert_eq!(bytes, b"hello");
}
#[test]
fn non_interactive_never_wraps() {
let mut w = FrameWriter::new();
let bytes = w.write_frame(&steady_params("hello", Some(false), false, false));
assert_eq!(count(&bytes, BSU), 0);
assert_eq!(count(&bytes, ESU), 0);
assert!(!bytes.is_empty(), "frame still renders, just unwrapped");
}
#[test]
fn noop_frame_emits_zero_bytes() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&steady_params("hello", Some(true), false, false));
let second = w.write_frame(&steady_params("hello", Some(true), false, false));
assert!(second.is_empty(), "unchanged + clean cursor emits nothing");
}
#[test]
fn cursor_dirty_noop_diff_emits_zero_bytes() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&steady_params("hello", Some(true), false, false));
let mut p = steady_params("hello", Some(true), false, false);
p.cursor_dirty = true;
let second = w.write_frame(&p);
assert!(
second.is_empty(),
"cursor_dirty gate opens but no-op diff must still emit nothing"
);
}
fn pos(x: usize, y: usize) -> CursorPos {
CursorPos { x, y }
}
fn cursor_params<'a>(output: &'a str, cursor: Option<CursorPos>) -> FrameParams<'a> {
let mut p = steady_params(output, Some(true), false, false);
p.cursor = cursor;
p.cursor_dirty = cursor.is_some();
p
}
#[test]
fn cursor_only_change_emits_oracle_sequence_and_returns_some() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&cursor_params("hi", None));
let bytes = w.write_frame(&cursor_params("hi", Some(pos(3, 0))));
let expected = format!("{BSU}\u{001B}[1A\u{001B}[4G\u{001B}[?25h{ESU}");
assert_eq!(
String::from_utf8(bytes.clone()).unwrap(),
expected,
"cursor-only change must emit the oracle buildCursorOnlySequence, sync-wrapped"
);
assert!(
!bytes.is_empty(),
"a cursor-only change makes write_frame return Some (non-empty) on byte-identical output"
);
}
#[test]
fn same_position_reset_emits_zero_bytes() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&cursor_params("hi", None));
let first = w.write_frame(&cursor_params("hi", Some(pos(3, 0))));
assert!(!first.is_empty(), "establishing the cursor emits a frame");
let second = w.write_frame(&cursor_params("hi", Some(pos(3, 0))));
assert!(
second.is_empty(),
"same-position re-set on unchanged output must emit nothing (position-change gate)"
);
}
#[test]
fn cursor_clear_emits_oracle_hide_sequence() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&cursor_params("hi", None));
let _ = w.write_frame(&cursor_params("hi", Some(pos(3, 0))));
let bytes = w.write_frame(&cursor_params("hi", None));
let expected = format!("{BSU}\u{001B}[?25l\u{001B}[1B\u{001B}[1G{ESU}");
assert_eq!(
String::from_utf8(bytes).unwrap(),
expected,
"clearing a shown cursor must emit the oracle hide-and-return sequence"
);
}
#[test]
fn output_change_with_active_cursor_wraps_diff_in_cursor_transport() {
let mut bare = FrameWriter::new();
let _ = bare.write_frame(&cursor_params("hi", None));
let bare_diff = String::from_utf8(bare.write_frame(&cursor_params("ho", None))).unwrap();
assert!(bare_diff.starts_with(BSU) && bare_diff.ends_with(ESU));
let inner_diff = &bare_diff[BSU.len()..bare_diff.len() - ESU.len()];
let mut w = FrameWriter::new();
let _ = w.write_frame(&cursor_params("hi", Some(pos(1, 0))));
let bytes = String::from_utf8(w.write_frame(&cursor_params("ho", Some(pos(2, 0))))).unwrap();
let return_prefix = "\u{001B}[?25l\u{001B}[1B\u{001B}[1G";
let cursor_suffix = "\u{001B}[1A\u{001B}[3G\u{001B}[?25h";
let expected = format!("{BSU}{return_prefix}{inner_diff}{cursor_suffix}{ESU}");
assert_eq!(
bytes, expected,
"an output change with an active cursor must be returnPrefix + diff + cursorSuffix, wrapped"
);
}
#[test]
fn debug_bare_newline_static_is_ignored() {
let mut w = FrameWriter::new();
let mut p = steady_params("hello", Some(true), false, true);
p.static_output = "\n";
let first = w.write_frame(&p);
assert_eq!(first, b"hello");
let mut p2 = steady_params("world", Some(true), false, true);
p2.static_output = "";
let second = w.write_frame(&p2);
assert_eq!(second, b"world");
}
#[test]
fn should_synchronize_matches_oracle() {
assert!(should_synchronize(true, Some(true), false));
assert!(!should_synchronize(true, Some(false), false));
assert!(!should_synchronize(false, Some(true), false));
assert!(should_synchronize(true, None, false));
assert!(!should_synchronize(true, None, true));
}
#[test]
fn should_clear_predicate_disjuncts() {
assert!(!should_clear_terminal_for_frame(false, 6, 7, 7, false));
assert!(should_clear_terminal_for_frame(true, 6, 7, 5, false));
assert!(should_clear_terminal_for_frame(true, 6, 5, 7, false));
assert!(!should_clear_terminal_for_frame(true, 3, 0, 4, false));
assert!(should_clear_terminal_for_frame(true, 6, 6, 5, false));
assert!(should_clear_terminal_for_frame(true, 6, 6, 6, true));
assert!(!should_clear_terminal_for_frame(true, 6, 6, 6, false));
}
#[test]
fn decset_literals_pinned() {
assert_eq!(bsu, "\u{001B}[?2026h");
assert_eq!(esu, "\u{001B}[?2026l");
assert_eq!(bsu.as_bytes(), &[27, 91, 63, 50, 48, 50, 54, 104]);
assert_eq!(esu.as_bytes(), &[27, 91, 63, 50, 48, 50, 54, 108]);
}
fn frame_params<'a>(
output: &'a str,
output_height: usize,
viewport_rows: usize,
static_output: &'a str,
) -> FrameParams<'a> {
FrameParams {
is_tty: true,
viewport_rows,
output,
output_height,
static_output,
is_unmounting: false,
cursor_dirty: false,
cursor: None,
interactive: Some(true),
is_in_ci: false,
debug: false,
}
}
#[test]
fn reset_diff_state_equals_fresh_writer() {
let mut dirty = FrameWriter::new();
let _ = dirty.write_frame(&frame_params("line-a\nline-b", 2, 6, "STATIC-CHUNK\n"));
let _ = dirty.write_frame(&frame_params("a\nb\nc\nd\ne\nf\ng", 7, 6, ""));
dirty.reset_diff_state();
let schedule = |w: &mut FrameWriter| -> Vec<u8> {
let mut all = Vec::new();
all.extend_from_slice(&w.write_frame(&frame_params("x\ny\nz\nw", 4, 6, "")));
all.extend_from_slice(&w.write_frame(&frame_params("p\nq\nr\ns\nt\nu\nv", 7, 6, "")));
all
};
let after_reset = schedule(&mut dirty);
let mut fresh = FrameWriter::new();
let from_fresh = schedule(&mut fresh);
assert_eq!(
after_reset, from_fresh,
"post-reset bytes must equal a fresh writer's for the same schedule"
);
}
#[test]
fn reset_diff_state_clears_all_fields() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&frame_params("hello\nworld", 2, 6, "STATIC\n"));
let _ = w.write_frame(&frame_params("a\nb\nc\nd\ne\nf\ng", 7, 6, "MORE-STATIC\n"));
assert!(!w.last_output.is_empty(), "precondition: last_output dirty");
assert!(
w.last_output_height != 0,
"precondition: last_output_height dirty"
);
assert!(
!w.full_static_output.is_empty(),
"precondition: full_static_output dirty"
);
w.reset_diff_state();
assert_eq!(w.last_output, "", "last_output not cleared");
assert_eq!(w.last_output_height, 0, "last_output_height not cleared");
assert_eq!(
w.full_static_output, "",
"full_static_output (static state) not cleared"
);
assert_eq!(w, FrameWriter::new(), "writer not equal to as-constructed");
}
#[test]
fn reset_diff_state_is_pure_state() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&frame_params("a\nb\nc", 3, 6, ""));
w.reset_diff_state();
let bytes = w.write_frame(&frame_params("a\nb\nc", 3, 6, ""));
assert!(
!bytes.is_empty(),
"post-reset identical frame must re-bootstrap, not skip as no-op"
);
}
fn expected_erase_lines(count: usize) -> String {
if count == 0 {
return String::new();
}
let mut out = String::new();
for i in 0..count {
out.push_str("\u{001B}[2K");
if i < count - 1 {
out.push_str("\u{001B}[1A");
}
}
out.push_str("\u{001B}[G");
out
}
#[test]
fn clear_emits_erase_lines_and_zeros_baseline() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&frame_params("a\nb\nc", 3, 100, ""));
let bytes = w.clear();
assert_eq!(
String::from_utf8(bytes).unwrap(),
expected_erase_lines(4),
"clear() must emit exactly eraseLines(previous_lines.len)"
);
let restore = String::from_utf8(w.restore_last_output()).unwrap();
assert_eq!(
restore,
format!("{}{}", expected_erase_lines(0), "a\nb\nc\n"),
"after clear(), restore diffs against an EMPTY baseline (full bootstrap)"
);
}
#[test]
fn clear_preserves_last_output_state() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&frame_params("hi\nthere", 2, 100, "STATIC\n"));
let before_last_output = w.last_output.clone();
let before_render = w.last_output_to_render.clone();
let before_height = w.last_output_height;
let before_static = w.full_static_output.clone();
let _ = w.clear();
assert_eq!(w.last_output, before_last_output, "last_output preserved");
assert_eq!(
w.last_output_to_render, before_render,
"last_output_to_render preserved"
);
assert_eq!(
w.last_output_height, before_height,
"last_output_height preserved"
);
assert_eq!(
w.full_static_output, before_static,
"full_static_output preserved"
);
assert_eq!(w.last_output_to_render, "hi\nthere\n");
}
#[test]
fn sync_baseline_repins_to_incremental_diff() {
let mut with = FrameWriter::new();
let _ = with.write_frame(&frame_params("alpha\nbeta", 2, 100, ""));
let _ = with.clear();
with.sync_baseline();
let with_changed =
String::from_utf8(with.write_frame(&frame_params("alpha\nGAMMA", 2, 100, ""))).unwrap();
let mut without = FrameWriter::new();
let _ = without.write_frame(&frame_params("alpha\nbeta", 2, 100, ""));
let _ = without.clear();
let without_changed =
String::from_utf8(without.write_frame(&frame_params("alpha\nGAMMA", 2, 100, ""))).unwrap();
assert!(
!with_changed.is_empty(),
"the changed render after sync still emits an incremental diff"
);
assert!(
without_changed.contains("alpha\n"),
"the no-sync bootstrap re-emits the unchanged 'alpha' line verbatim"
);
assert!(
!with_changed.contains("alpha"),
"sync_baseline() re-pins the baseline -> the changed render SKIPS the unchanged \
'alpha' line entirely (incremental, not bootstrap)"
);
assert!(
with_changed.contains("GAMMA") && without_changed.contains("GAMMA"),
"both renders write the changed line"
);
}
#[test]
fn clear_without_sync_repaints_but_with_sync_noops() {
let mut without = FrameWriter::new();
let _ = without.write_frame(&frame_params("alpha\nbeta", 2, 100, ""));
let _ = without.clear();
assert!(
!without.restore_last_output().is_empty(),
"clear() without sync_baseline() leaves an empty baseline -> restore repaints"
);
let mut with = FrameWriter::new();
let _ = with.write_frame(&frame_params("alpha\nbeta", 2, 100, ""));
let _ = with.clear();
with.sync_baseline();
assert!(
with.restore_last_output().is_empty(),
"clear()+sync_baseline() re-pins the baseline -> restore is a no-op"
);
}
#[test]
fn restore_last_output_repaints_full_frame() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&frame_params("one\ntwo", 2, 100, ""));
let _ = w.clear();
let restore = String::from_utf8(w.restore_last_output()).unwrap();
let expected = format!("{}{}", expected_erase_lines(0), "one\ntwo\n");
assert_eq!(
restore, expected,
"restore_last_output() must repaint the full padded last frame"
);
assert!(
restore.contains("one") && restore.contains("two"),
"the restored frame carries the last frame's content"
);
}
#[test]
fn restore_last_output_is_non_empty_after_clear() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&frame_params("payload", 1, 100, ""));
let _ = w.clear();
assert!(
!w.restore_last_output().is_empty(),
"restore_last_output() must emit the repaint bytes, not nothing"
);
}
#[test]
fn clear_then_restore_leaves_baseline_in_sync() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&frame_params("live\nframe", 2, 100, ""));
let _ = w.clear();
let _ = w.restore_last_output();
let after_restore =
String::from_utf8(w.write_frame(&frame_params("live\nCHANGED", 2, 100, ""))).unwrap();
let mut bare = FrameWriter::new();
let _ = bare.write_frame(&frame_params("live\nframe", 2, 100, ""));
let _ = bare.clear();
let bootstrap =
String::from_utf8(bare.write_frame(&frame_params("live\nCHANGED", 2, 100, ""))).unwrap();
assert!(
bootstrap.contains("live\n"),
"the from-empty bootstrap re-emits the unchanged 'live' line verbatim"
);
assert!(
!after_restore.contains("live"),
"after restore the baseline is re-pinned, so the changed render SKIPS the \
unchanged 'live' line entirely (incremental, not bootstrap)"
);
assert!(
after_restore.contains("CHANGED") && bootstrap.contains("CHANGED"),
"both renders write the changed line"
);
}
#[test]
fn last_output_to_render_padding_matches_fullscreen_policy() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&frame_params("x\ny", 2, 100, ""));
assert_eq!(
w.last_output_to_render, "x\ny\n",
"non-fullscreen frame records the newline-padded output_to_render"
);
let mut fs = FrameWriter::new();
let _ = fs.write_frame(&frame_params("a\nb\nc", 3, 3, ""));
assert_eq!(
fs.last_output_to_render, "a\nb\nc",
"fullscreen frame records the raw output as output_to_render"
);
}
#[test]
fn forget_last_output_zeroes_only_last_output_fields() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&frame_params("hi\nthere", 2, 100, "STATIC\n"));
assert!(!w.last_output.is_empty(), "precondition: last_output dirty");
assert!(
!w.last_output_to_render.is_empty(),
"precondition: last_output_to_render dirty"
);
let before_height = w.last_output_height;
let before_static = w.full_static_output.clone();
assert!(
before_height != 0 && !before_static.is_empty(),
"preconditions"
);
w.forget_last_output();
assert_eq!(w.last_output, "", "last_output zeroed");
assert_eq!(w.last_output_to_render, "", "last_output_to_render zeroed");
assert_eq!(
w.last_output_height, before_height,
"last_output_height PRESERVED (narrower than reset_diff_state)"
);
assert_eq!(
w.full_static_output, before_static,
"full_static_output PRESERVED (narrower than reset_diff_state)"
);
}
#[test]
fn forget_last_output_forces_repaint_on_identical_reflow() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&frame_params("aa\nbb", 2, 100, ""));
let _ = w.clear();
w.forget_last_output();
let repaint = w.write_frame(&frame_params("aa\nbb", 2, 100, ""));
assert!(
!repaint.is_empty(),
"after clear()+forget_last_output(), an identical reflow must repaint, not no-op"
);
let mut control = FrameWriter::new();
let _ = control.write_frame(&frame_params("aa\nbb", 2, 100, ""));
let _ = control.clear();
let control_repaint = control.write_frame(&frame_params("aa\nbb", 2, 100, ""));
assert!(
control_repaint.is_empty(),
"without forget_last_output, the identical re-render no-ops (last_output unchanged)"
);
}
#[test]
fn reset_static_output_drops_dead_static_only() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&frame_params("live-1", 1, 6, "S:dead\n"));
assert_eq!(
w.full_static_output, "S:dead\n",
"precondition: the dead instance's chunk accumulated"
);
let before_last = w.last_output.clone();
let before_to_render = w.last_output_to_render.clone();
let before_height = w.last_output_height;
w.reset_static_output();
assert_eq!(w.full_static_output, "", "full_static_output zeroed");
assert_eq!(w.last_output, before_last, "last_output PRESERVED");
assert_eq!(
w.last_output_to_render, before_to_render,
"last_output_to_render PRESERVED"
);
assert_eq!(
w.last_output_height, before_height,
"last_output_height PRESERVED"
);
let _ = w.write_frame(&frame_params("live-2", 1, 6, "S:fresh\n"));
assert_eq!(
w.full_static_output, "S:fresh\n",
"post-reset accumulation restarts from the new instance only"
);
let bytes = w.write_frame(&frame_params("a\nb\nc\nd\ne\nf\ng", 7, 6, ""));
let s = String::from_utf8(bytes).expect("frame bytes are utf8");
assert!(
s.contains("\u{001B}[2J"),
"the overflow frame engages the clear branch: {s:?}"
);
assert!(
!s.contains("S:dead"),
"the dead <Static> instance's chunk must NOT replay: {s:?}"
);
assert!(
s.contains("S:fresh"),
"the new <Static> instance's chunk MUST replay: {s:?}"
);
}
#[test]
fn clear_zeroes_cursor_state() {
let mut w = FrameWriter::new();
let _ = w.write_frame(&cursor_params("hi", None));
let _ = w.write_frame(&cursor_params("hi", Some(pos(1, 0))));
assert_eq!(
w.previous_cursor_position,
Some(pos(1, 0)),
"precondition: a cursor is recorded as shown"
);
assert!(w.cursor_was_shown, "precondition: cursor_was_shown is true");
let _ = w.clear();
assert_eq!(
w.previous_cursor_position, None,
"clear() zeroes previous_cursor_position (no stale re-home after resize)"
);
assert!(
!w.cursor_was_shown,
"clear() zeroes cursor_was_shown (no stale hide-prefix after resize)"
);
}
fn rendered_writer(text: &str) -> FrameWriter {
let mut w = FrameWriter::new();
let params = FrameParams {
is_tty: true,
viewport_rows: 24,
output: text,
output_height: 1,
static_output: "",
is_unmounting: false,
cursor_dirty: false,
cursor: None,
interactive: Some(true),
is_in_ci: false,
debug: false,
};
let _ = w.write_frame(¶ms);
w
}
#[test]
fn compose_console_write_rendered_sync() {
let mut w = rendered_writer("hello");
let data = b"APP DATA";
let composed = w.compose_console_write(data, true);
let mut w2 = rendered_writer("hello");
let clear = w2.clear();
let restore = w2.restore_last_output();
let mut expected = Vec::new();
expected.extend_from_slice(bsu.as_bytes());
expected.extend_from_slice(&clear);
expected.extend_from_slice(data);
expected.extend_from_slice(&restore);
expected.extend_from_slice(esu.as_bytes());
assert_eq!(
composed, expected,
"compose_console_write(sync=true) is byte-identical to manual bsu+clear+data+restore+esu"
);
}
#[test]
fn compose_console_write_rendered_nosync() {
let mut w = rendered_writer("hello");
let data = b"APP DATA";
let composed = w.compose_console_write(data, false);
let mut w2 = rendered_writer("hello");
let clear = w2.clear();
let restore = w2.restore_last_output();
let mut expected = Vec::new();
expected.extend_from_slice(&clear);
expected.extend_from_slice(data);
expected.extend_from_slice(&restore);
assert_eq!(
composed, expected,
"compose_console_write(sync=false) is byte-identical to manual clear+data+restore (no BSU/ESU)"
);
}
#[test]
fn compose_console_write_nothing_rendered_sync() {
let mut w = FrameWriter::new();
let data = b"EARLY DATA";
let composed = w.compose_console_write(data, true);
let mut expected = Vec::new();
expected.extend_from_slice(bsu.as_bytes());
expected.extend_from_slice(data);
expected.extend_from_slice(esu.as_bytes());
assert_eq!(
composed, expected,
"nothing-rendered sync: bsu+data+esu (clear/restore empty)"
);
}
#[test]
fn compose_console_write_nothing_rendered_nosync() {
let mut w = FrameWriter::new();
let data = b"EARLY DATA";
let composed = w.compose_console_write(data, false);
assert_eq!(
composed,
data.to_vec(),
"nothing-rendered nosync: just data"
);
}
#[test]
fn compose_console_prefix_rendered() {
let mut w = rendered_writer("hello");
let prefix = w.compose_console_prefix(true);
let mut w2 = rendered_writer("hello");
let clear = w2.clear();
let mut expected = Vec::new();
expected.extend_from_slice(bsu.as_bytes());
expected.extend_from_slice(&clear);
assert_eq!(
prefix, expected,
"compose_console_prefix(sync=true) is bsu+clear"
);
}
#[test]
fn compose_console_prefix_nosync() {
let mut w = rendered_writer("hello");
let prefix = w.compose_console_prefix(false);
let mut w2 = rendered_writer("hello");
let clear = w2.clear();
assert_eq!(
prefix, clear,
"compose_console_prefix(sync=false) is just clear"
);
}
#[test]
fn compose_console_suffix_rendered() {
let mut w = rendered_writer("hello");
let _ = w.compose_console_prefix(true);
let suffix = w.compose_console_suffix(true);
let mut w2 = rendered_writer("hello");
let _ = w2.clear();
let restore = w2.restore_last_output();
let mut expected = Vec::new();
expected.extend_from_slice(&restore);
expected.extend_from_slice(esu.as_bytes());
assert_eq!(
suffix, expected,
"compose_console_suffix(sync=true) is restore+esu"
);
}
#[test]
fn compose_console_suffix_nosync() {
let mut w = rendered_writer("hello");
let _ = w.compose_console_prefix(false);
let suffix = w.compose_console_suffix(false);
let mut w2 = rendered_writer("hello");
let _ = w2.clear();
let restore = w2.restore_last_output();
assert_eq!(
suffix, restore,
"compose_console_suffix(sync=false) is just restore"
);
}
#[test]
fn prefix_data_suffix_concatenation_matches_fused() {
let data = b"STDERR DATA";
let mut w1 = rendered_writer("world");
let fused = w1.compose_console_write(data, true);
let mut w2 = rendered_writer("world");
let prefix = w2.compose_console_prefix(true);
let suffix = w2.compose_console_suffix(true);
let mut three_write = Vec::new();
three_write.extend_from_slice(&prefix);
three_write.extend_from_slice(data);
three_write.extend_from_slice(&suffix);
assert_eq!(
fused, three_write,
"prefix+data+suffix byte-identical to fused compose_console_write"
);
}