// Extra pure HID encoder edge coverage. // // Scope: verify balanced modifier/key release sequences without touching // evdev, uinput, or gadget device nodes. // Targets: `common/src/hid.rs`. // Why: a leaked shift/modifier or missing release is exactly how a safe // synthetic input path turns into foreground typing like `aAaasa`. use lesavka_common::hid::{KeyboardHidReport, append_char_reports, char_to_usage}; fn reports_for(text: &str) -> Vec { let mut reports = Vec::new(); for ch in text.chars() { assert!(append_char_reports(&mut reports, ch), "unsupported {ch:?}"); } reports } #[test] fn shifted_character_releases_modifier_before_following_unshifted_character() { let reports = reports_for("Aa"); assert_eq!(reports.len(), 6); assert_eq!(reports[0], [0x02, 0, 0, 0, 0, 0, 0, 0]); assert_eq!(reports[1], [0x02, 0, 0x04, 0, 0, 0, 0, 0]); assert_eq!(reports[2], [0x02, 0, 0, 0, 0, 0, 0, 0]); assert_eq!(reports[3], [0; 8]); assert_eq!(reports[4], [0, 0, 0x04, 0, 0, 0, 0, 0]); assert_eq!(reports[5], [0; 8]); } #[test] fn whitespace_characters_emit_press_release_pairs_without_sticky_keys() { let reports = reports_for("\n\r\t "); let expected_usages = [0x28, 0x28, 0x2B, 0x2C]; assert_eq!(reports.len(), expected_usages.len() * 2); for (chunk, usage) in reports.chunks_exact(2).zip(expected_usages) { assert_eq!(chunk[0], [0, 0, usage, 0, 0, 0, 0, 0]); assert_eq!(chunk[1], [0; 8]); } } #[test] fn shifted_symbols_are_balanced_modifier_key_release_sequences() { for ch in [ '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '{', '}', '|', ':', '"', '~', '<', '>', '?', ] { let mut reports = Vec::new(); let (usage, modifiers) = char_to_usage(ch).expect("supported shifted symbol"); assert_eq!(modifiers, 0x02, "{ch:?} should require shift"); assert!(append_char_reports(&mut reports, ch)); assert_eq!( reports, vec![ [0x02, 0, 0, 0, 0, 0, 0, 0], [0x02, 0, usage, 0, 0, 0, 0, 0], [0x02, 0, 0, 0, 0, 0, 0, 0], [0; 8], ] ); } }