use super::*;
#[test]
fn hide_cursor_mode() {
let mut screen = make_screen(24, 80);
assert!(!screen.mode(TerminalMode::HideCursor));
screen.set_mode(MODE_HIDE_CURSOR);
assert!(screen.mode(TerminalMode::HideCursor));
screen.clear_mode(MODE_HIDE_CURSOR);
assert!(!screen.mode(TerminalMode::HideCursor));
}
#[test]
fn alternate_screen() {
let mut screen = make_screen(24, 80);
screen.text('A');
screen.enter_alternate_grid();
assert!(screen.mode(TerminalMode::AlternateScreen));
assert!(!screen.cell(0, 0).unwrap().has_contents());
screen.exit_alternate_grid();
assert!(!screen.mode(TerminalMode::AlternateScreen));
assert_eq!(screen.cell(0, 0).unwrap().contents(), "A");
}
#[test]
fn application_cursor_mode() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
assert!(!parser.screen().mode(TerminalMode::ApplicationCursor));
process(&mut parser, b"\x1b[?1h");
assert!(parser.screen().mode(TerminalMode::ApplicationCursor));
process(&mut parser, b"\x1b[?1l");
assert!(!parser.screen().mode(TerminalMode::ApplicationCursor));
}
#[test]
fn bracketed_paste_mode() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
assert!(!parser.screen().mode(TerminalMode::BracketedPaste));
process(&mut parser, b"\x1b[?2004h");
assert!(parser.screen().mode(TerminalMode::BracketedPaste));
process(&mut parser, b"\x1b[?2004l");
assert!(!parser.screen().mode(TerminalMode::BracketedPaste));
}
#[test]
fn ris_clears_modes() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
process(&mut parser, b"\x1b[?1h\x1b[?2004h");
assert!(parser.screen().mode(TerminalMode::ApplicationCursor));
assert!(parser.screen().mode(TerminalMode::BracketedPaste));
process(&mut parser, b"\x1bc");
assert!(!parser.screen().mode(TerminalMode::ApplicationCursor));
assert!(!parser.screen().mode(TerminalMode::BracketedPaste));
}
#[test]
fn focus_and_sync_flags() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
assert!(!parser.screen().mode(TerminalMode::FocusInOut));
assert!(!parser.screen().mode(TerminalMode::SyncUpdate));
process(&mut parser, b"\x1b[?1004h\x1b[?2026h");
assert!(parser.screen().mode(TerminalMode::FocusInOut));
assert!(parser.screen().mode(TerminalMode::SyncUpdate));
process(&mut parser, b"\x1b[?1004l\x1b[?2026l");
assert!(!parser.screen().mode(TerminalMode::FocusInOut));
assert!(!parser.screen().mode(TerminalMode::SyncUpdate));
}
#[test]
fn decscnm_reverse_video_set_and_clear() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
assert!(!parser.screen().mode(TerminalMode::ReverseVideo));
process(&mut parser, b"\x1b[?5h");
assert!(parser.screen().mode(TerminalMode::ReverseVideo));
process(&mut parser, b"\x1b[?5l");
assert!(!parser.screen().mode(TerminalMode::ReverseVideo));
}
#[test]
fn decscnm_reverse_video_emits_mode_changed() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
parser.screen_mut().drain_events();
process(&mut parser, b"\x1b[?5h");
let events = parser.screen_mut().drain_events();
assert!(
events.contains(&ScreenEvent::ModeChanged {
mode: TerminalMode::ReverseVideo,
enabled: true,
}),
"DECSET 5 must emit ModeChanged{{ReverseVideo, true}}; got {events:?}",
);
process(&mut parser, b"\x1b[?5l");
let events = parser.screen_mut().drain_events();
assert!(
events.contains(&ScreenEvent::ModeChanged {
mode: TerminalMode::ReverseVideo,
enabled: false,
}),
"DECRST 5 must emit ModeChanged{{ReverseVideo, false}}; got {events:?}",
);
}
#[test]
fn decscnm_reverse_video_idempotent_no_event() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
process(&mut parser, b"\x1b[?5h");
parser.screen_mut().drain_events();
process(&mut parser, b"\x1b[?5h");
let events = parser.screen_mut().drain_events();
assert!(
!events.iter().any(|e| matches!(
e,
ScreenEvent::ModeChanged {
mode: TerminalMode::ReverseVideo,
..
}
)),
"redundant DECSET 5 must not emit a ModeChanged event; got {events:?}",
);
}
#[test]
fn decset_decbkm_set_and_clear() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
assert!(!parser.screen().mode(TerminalMode::BackspaceBs));
process(&mut parser, b"\x1b[?67h");
assert!(parser.screen().mode(TerminalMode::BackspaceBs));
process(&mut parser, b"\x1b[?67l");
assert!(!parser.screen().mode(TerminalMode::BackspaceBs));
}
#[test]
fn decset_decbkm_emits_mode_changed() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
parser.screen_mut().drain_events();
process(&mut parser, b"\x1b[?67h");
let events = parser.screen_mut().drain_events();
assert!(
events.contains(&ScreenEvent::ModeChanged {
mode: TerminalMode::BackspaceBs,
enabled: true,
}),
"DECSET 67 must emit ModeChanged{{BackspaceBs, true}}; got {events:?}",
);
process(&mut parser, b"\x1b[?67l");
let events = parser.screen_mut().drain_events();
assert!(
events.contains(&ScreenEvent::ModeChanged {
mode: TerminalMode::BackspaceBs,
enabled: false,
}),
"DECRST 67 must emit ModeChanged{{BackspaceBs, false}}; got {events:?}",
);
}
#[test]
fn decset_decbkm_idempotent_no_event() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
process(&mut parser, b"\x1b[?67h");
parser.screen_mut().drain_events();
process(&mut parser, b"\x1b[?67h");
let events = parser.screen_mut().drain_events();
assert!(
!events.iter().any(|e| matches!(
e,
ScreenEvent::ModeChanged {
mode: TerminalMode::BackspaceBs,
..
}
)),
"redundant DECSET 67 must not emit a ModeChanged event; got {events:?}",
);
}
#[test]
fn lnm_set_and_clear() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
assert!(!parser.screen().mode(TerminalMode::LineFeedNewLine));
process(&mut parser, b"\x1b[20h");
assert!(parser.screen().mode(TerminalMode::LineFeedNewLine));
process(&mut parser, b"\x1b[20l");
assert!(!parser.screen().mode(TerminalMode::LineFeedNewLine));
}
#[test]
fn lnm_emits_mode_changed() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
parser.screen_mut().drain_events();
process(&mut parser, b"\x1b[20h");
let events = parser.screen_mut().drain_events();
assert!(
events.contains(&ScreenEvent::ModeChanged {
mode: TerminalMode::LineFeedNewLine,
enabled: true,
}),
"CSI 20 h must emit ModeChanged{{LineFeedNewLine, true}}; got {events:?}",
);
process(&mut parser, b"\x1b[20l");
let events = parser.screen_mut().drain_events();
assert!(
events.contains(&ScreenEvent::ModeChanged {
mode: TerminalMode::LineFeedNewLine,
enabled: false,
}),
"CSI 20 l must emit ModeChanged{{LineFeedNewLine, false}}; got {events:?}",
);
}
#[test]
fn lnm_idempotent_no_event() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
process(&mut parser, b"\x1b[20h");
parser.screen_mut().drain_events();
process(&mut parser, b"\x1b[20h");
let events = parser.screen_mut().drain_events();
assert!(
!events.iter().any(|e| matches!(
e,
ScreenEvent::ModeChanged {
mode: TerminalMode::LineFeedNewLine,
..
}
)),
"redundant CSI 20 h must not emit a ModeChanged event; got {events:?}",
);
}
#[test]
fn lnm_lf_performs_implicit_cr_when_set() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
process(&mut parser, b"\x1b[20h");
process(&mut parser, b"ABCDE\n");
let cursor = parser.screen().cursor();
assert_eq!(
cursor,
Position { row: 1, col: 0 },
"LNM-on LF must reset column to 0 and advance the row; got {cursor:?}",
);
}
#[test]
fn lnm_lf_no_cr_when_unset() {
let mut parser = crate::Parser::new(TerminalSize { rows: 24, cols: 80 }, 0);
assert!(!parser.screen().mode(TerminalMode::LineFeedNewLine));
process(&mut parser, b"ABCDE\n");
let cursor = parser.screen().cursor();
assert_eq!(
cursor,
Position { row: 1, col: 5 },
"LNM-off LF must preserve column and advance the row; got {cursor:?}",
);
}