use justerm_core::{Engine, Key, KeyAction, KeyEvent, ModifiedKeys, Modifiers, decode, encode};
fn enc(t: &Engine, k: Key, mods: Modifiers) -> Vec<u8> {
t.encode_key(KeyEvent {
key: k,
mods,
action: KeyAction::Press,
..Default::default()
})
.expect("every probe key encodes")
}
fn mask(t: &Engine) -> ModifiedKeys {
t.frame().modified_keys
}
fn assert_mask_agrees_with_encoder(t: &Engine) {
let m = mask(t);
for (bit, key, mods) in [
(ModifiedKeys::SHIFT_ENTER, Key::Enter, Modifiers::SHIFT),
(ModifiedKeys::SHIFT_TAB, Key::Tab, Modifiers::SHIFT),
(
ModifiedKeys::SHIFT_BACKSPACE,
Key::Backspace,
Modifiers::SHIFT,
),
(ModifiedKeys::SHIFT_ESCAPE, Key::Escape, Modifiers::SHIFT),
(ModifiedKeys::ALT_ENTER, Key::Enter, Modifiers::ALT),
(ModifiedKeys::ALT_TAB, Key::Tab, Modifiers::ALT),
(ModifiedKeys::ALT_BACKSPACE, Key::Backspace, Modifiers::ALT),
(ModifiedKeys::ALT_ESCAPE, Key::Escape, Modifiers::ALT),
(ModifiedKeys::CTRL_ENTER, Key::Enter, Modifiers::CTRL),
(ModifiedKeys::CTRL_TAB, Key::Tab, Modifiers::CTRL),
(
ModifiedKeys::CTRL_BACKSPACE,
Key::Backspace,
Modifiers::CTRL,
),
(ModifiedKeys::CTRL_ESCAPE, Key::Escape, Modifiers::CTRL),
] {
let distinct = enc(t, key, mods) != enc(t, key, Modifiers::empty());
assert_eq!(
m.contains(bit),
distinct,
"{bit:?} disagrees with encode_key"
);
}
}
#[test]
fn legacy_drops_shift_on_enter() {
let t = Engine::new(80, 24);
assert!(!mask(&t).contains(ModifiedKeys::SHIFT_ENTER));
assert!(mask(&t).contains(ModifiedKeys::SHIFT_TAB));
assert_mask_agrees_with_encoder(&t);
}
#[test]
fn kitty_disambiguate_carries_shift_on_enter() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[>1u"); assert!(mask(&t).contains(ModifiedKeys::SHIFT_ENTER));
assert_mask_agrees_with_encoder(&t);
t.feed(b"\x1b[<u"); assert!(!mask(&t).contains(ModifiedKeys::SHIFT_ENTER));
assert_mask_agrees_with_encoder(&t);
}
#[test]
fn kitty_flags_without_disambiguate_still_drop_shift_on_enter() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[>2u"); assert!(!mask(&t).contains(ModifiedKeys::SHIFT_ENTER));
assert_mask_agrees_with_encoder(&t);
}
#[test]
fn kitty_all_as_escape_carries_every_bit() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[=9u"); assert_eq!(mask(&t), ModifiedKeys::all());
assert_mask_agrees_with_encoder(&t);
}
#[test]
fn modify_other_keys_2_carries_shift_on_enter_and_decstr_clears_it() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[>4;1m"); assert!(!mask(&t).contains(ModifiedKeys::SHIFT_ENTER));
t.feed(b"\x1b[>4;2m");
assert!(mask(&t).contains(ModifiedKeys::SHIFT_ENTER));
assert!(!mask(&t).contains(ModifiedKeys::CTRL_BACKSPACE));
assert_mask_agrees_with_encoder(&t);
t.feed(b"\x1b[!p"); assert!(!mask(&t).contains(ModifiedKeys::SHIFT_ENTER));
assert_mask_agrees_with_encoder(&t);
}
#[test]
fn the_mask_survives_the_wire() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[>4;2m");
let frame = t.frame();
let back = decode(&encode(&frame)).expect("round trip");
assert_eq!(back.modified_keys, frame.modified_keys);
assert!(back.modified_keys.contains(ModifiedKeys::SHIFT_ENTER));
}
#[test]
fn the_mask_follows_the_screen_the_flags_were_pushed_on() {
let mut t = Engine::new(80, 24);
t.feed(b"\x1b[?1049h\x1b[>1u");
assert!(mask(&t).contains(ModifiedKeys::SHIFT_ENTER));
t.feed(b"\x1b[?1049l");
assert!(!mask(&t).contains(ModifiedKeys::SHIFT_ENTER));
assert_mask_agrees_with_encoder(&t);
}