use justerm_core::{Engine, MarkerKind};
fn kinds(t: &Engine) -> Vec<MarkerKind> {
t.command_marks().into_iter().map(|(_, _, k)| k).collect()
}
#[test]
fn full_cycle_records_four_kinded_marks() {
let mut t = Engine::new(40, 24);
t.feed(b"\x1b]133;A\x07user$ \x1b]133;B\x07ls\x1b]133;C\x07out\r\n\x1b]133;D;0\x07");
assert_eq!(
kinds(&t),
vec![
MarkerKind::PromptStart,
MarkerKind::CommandStart,
MarkerKind::OutputStart,
MarkerKind::CommandFinished(Some(0)),
]
);
}
#[test]
fn finished_without_exit_is_none() {
let mut t = Engine::new(40, 24);
t.feed(b"\x1b]133;D\x07");
assert_eq!(kinds(&t), vec![MarkerKind::CommandFinished(None)]);
}
#[test]
fn finished_parses_nonzero_exit() {
let mut t = Engine::new(40, 24);
t.feed(b"\x1b]133;D;130\x07");
assert_eq!(kinds(&t), vec![MarkerKind::CommandFinished(Some(130))]);
}
#[test]
fn finished_empty_or_invalid_exit_is_none() {
let mut t = Engine::new(40, 24);
t.feed(b"\x1b]133;D;\x07");
t.feed(b"\x1b]133;D;abc\x07");
assert_eq!(
kinds(&t),
vec![
MarkerKind::CommandFinished(None),
MarkerKind::CommandFinished(None),
]
);
}
#[test]
fn marks_anchor_at_cursor_line() {
let mut t = Engine::new(40, 24);
t.feed(b"\x1b]133;A\x07"); t.feed(b"cmd\r\n"); t.feed(b"\x1b]133;C\x07");
let marks = t.command_marks();
assert_eq!(marks[0].1, 0, "PromptStart anchors on row 0");
assert_eq!(marks[1].1, 1, "OutputStart anchors on row 1");
}
#[test]
fn alt_screen_marks_are_ignored() {
let mut t = Engine::new(40, 24);
t.feed(b"\x1b[?1049h"); t.feed(b"\x1b]133;A\x07\x1b]133;D;0\x07");
assert!(t.command_marks().is_empty());
}
#[test]
fn command_nav_is_primary_scoped_on_alt() {
let mut t = Engine::new(40, 24);
t.feed(b"\x1b]133;A\x07$ \x1b]133;B\x07ls\x1b]133;C\x07\r\n\x1b]133;D;0\x07");
t.feed(b"\x1b[?1049h");
let _alt = t.add_marker(2);
assert_eq!(
t.command_marks()
.into_iter()
.map(|(_, _, k)| k)
.collect::<Vec<_>>(),
vec![
MarkerKind::PromptStart,
MarkerKind::CommandStart,
MarkerKind::OutputStart,
MarkerKind::CommandFinished(Some(0)),
],
);
let cmds = t.command_lines();
assert_eq!(cmds.len(), 1);
assert_eq!(cmds[0].exit, Some(0));
}
#[test]
fn unknown_subcommand_is_ignored() {
let mut t = Engine::new(40, 24);
t.feed(b"\x1b]133;Z\x07");
t.feed(b"\x1b]133\x07");
assert!(t.command_marks().is_empty());
}
#[test]
fn nested_prompt_starts_each_record() {
let mut t = Engine::new(40, 24);
t.feed(b"\x1b]133;A\x07\x1b]133;A\x07");
assert_eq!(
kinds(&t),
vec![MarkerKind::PromptStart, MarkerKind::PromptStart]
);
}
#[test]
fn command_marks_survive_alt_screen_scroll() {
let mut t = Engine::new(20, 5);
t.feed(b"\x1b]133;A\x07"); t.feed(b"\x1b[?1049h"); t.feed(b"a\r\nb\r\nc\r\nd\r\ne\r\nf\r\ng\r\n"); t.feed(b"\x1b[?1049l");
let marks = t.command_marks();
assert_eq!(marks.len(), 1, "primary mark survives the alt excursion");
assert_eq!(marks[0].1, 0, "still anchored at abs line 0");
assert_eq!(marks[0].2, MarkerKind::PromptStart);
}
#[test]
fn command_marks_survive_alt_screen_reverse_scroll() {
let mut t = Engine::new(20, 5);
t.feed(b"\x1b]133;A\x07"); t.feed(b"\x1b[?1049h"); t.feed(b"\x1b[H"); t.feed(b"\x1bM\x1bM\x1bM\x1bM\x1bM\x1bM"); t.feed(b"\x1b[?1049l");
let marks = t.command_marks();
assert_eq!(marks.len(), 1, "primary mark survives reverse alt scroll");
assert_eq!(marks[0].1, 0, "still anchored at abs line 0");
}
#[test]
fn plain_markers_excluded_command_marks_included() {
let mut t = Engine::new(40, 24);
t.feed(b"hello\r\n");
let _plain = t.add_marker(0);
assert!(
t.command_marks().is_empty(),
"plain marker is not a command mark"
);
t.feed(b"\x1b]133;A\x07");
assert_eq!(t.command_marks().len(), 1, "133 mark is a command mark");
}