//! Coverage for keyboard paste debounce and RPC branches. //! //! Scope: include keyboard input source and cover paste debounce/RPC paths. //! Targets: `client/src/input/keyboard.rs`. //! Why: paste routing has operator-visible side effects, and keeping it in a //! focused contract keeps keyboard coverage under the 500 LOC hygiene cap. mod keymap { pub use lesavka_client::input::keymap::*; } #[allow(warnings)] mod keyboard_paste_rpc_contract { include!(env!("LESAVKA_CLIENT_KEYBOARD_SRC")); use serial_test::serial; use temp_env::with_var; fn open_any_keyboard_device() -> Option { let entries = std::fs::read_dir("/dev/input").ok()?; for entry in entries.flatten() { let path = entry.path(); let name = path.file_name()?.to_string_lossy(); if !name.starts_with("event") { continue; } let dev = evdev::Device::open(path).ok()?; let _ = dev.set_nonblocking(true); let looks_like_keyboard = dev .supported_keys() .map(|keys| { keys.contains(evdev::KeyCode::KEY_A) && keys.contains(evdev::KeyCode::KEY_ENTER) && keys.contains(evdev::KeyCode::KEY_LEFTCTRL) }) .unwrap_or(false); if looks_like_keyboard { return Some(dev); } } None } fn build_keyboard(name: &str) -> Option { let mut keys = evdev::AttributeSet::::new(); for key in [ evdev::KeyCode::KEY_A, evdev::KeyCode::KEY_B, evdev::KeyCode::KEY_V, evdev::KeyCode::KEY_LEFTCTRL, evdev::KeyCode::KEY_LEFTALT, ] { keys.insert(key); } let mut vdev = evdev::uinput::VirtualDevice::builder() .ok()? .name(name) .with_keys(&keys) .ok()? .build() .ok()?; for _ in 0..40 { if let Ok(mut nodes) = vdev.enumerate_dev_nodes_blocking() { if let Some(Ok(path)) = nodes.next() { let dev = evdev::Device::open(path).ok()?; dev.set_nonblocking(true).ok()?; return Some(dev); } } std::thread::sleep(std::time::Duration::from_millis(10)); } None } #[test] #[serial] fn live_modifier_report_honors_configured_staging_delay() { let (tx, mut rx) = tokio::sync::broadcast::channel(4); with_var("LESAVKA_LIVE_MODIFIER_DELAY_MS", Some("1"), || { emit_live_keyboard_report( &tx, evdev::KeyCode::KEY_A, 1, [0x01, 0, 0x04, 0, 0, 0, 0, 0], ); }); let staged = rx.try_recv().expect("modifier-only report"); assert_eq!(staged.data, vec![0x01, 0, 0, 0, 0, 0, 0, 0]); let final_report = rx.try_recv().expect("final key report"); assert_eq!(final_report.data, vec![0x01, 0, 0x04, 0, 0, 0, 0, 0]); } #[test] #[cfg(coverage)] #[serial] fn try_handle_paste_event_coverage_path_respects_debounce_fallthrough() { let Some(dev) = open_any_keyboard_device() .or_else(|| build_keyboard("lesavka-include-kbd-coverage-debounce")) else { return; }; let (tx, mut rx) = tokio::sync::broadcast::channel(32); let mut agg = KeyboardAggregator::new(dev, false, tx, None); agg.paste_enabled = true; agg.paste_rpc_enabled = false; agg.pressed_keys.insert(evdev::KeyCode::KEY_LEFTCTRL); agg.pressed_keys.insert(evdev::KeyCode::KEY_LEFTALT); agg.pressed_keys.insert(evdev::KeyCode::KEY_V); let now_ms = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_millis() as u64; LAST_PASTE_MS.store(now_ms, Ordering::Relaxed); with_var("LESAVKA_CLIPBOARD_CMD", Some("printf 'a'"), || { with_var("LESAVKA_CLIPBOARD_CHORD", Some("ctrl+alt+v"), || { with_var("LESAVKA_CLIPBOARD_DEBOUNCE_MS", Some("999999"), || { assert!(agg.try_handle_paste_event(evdev::KeyCode::KEY_V, 1)); }); }); }); let pkt = rx .try_recv() .expect("debounced paste should still emit a swallowed empty report"); assert_eq!(pkt.data, vec![0; 8]); assert!( rx.try_recv().is_err(), "debounced paste should not emit HID reports" ); LAST_PASTE_MS.store(0, Ordering::Relaxed); } #[test] #[cfg(coverage)] #[serial] fn try_handle_paste_event_coverage_path_invokes_rpc_when_enabled() { let Some(dev) = open_any_keyboard_device() .or_else(|| build_keyboard("lesavka-include-kbd-coverage-rpc")) else { return; }; let (paste_tx, mut paste_rx) = tokio::sync::mpsc::unbounded_channel::(); let (tx, _rx) = tokio::sync::broadcast::channel(32); let mut agg = KeyboardAggregator::new(dev, false, tx, Some(paste_tx)); agg.paste_enabled = true; agg.paste_rpc_enabled = true; agg.pressed_keys.insert(evdev::KeyCode::KEY_LEFTCTRL); agg.pressed_keys.insert(evdev::KeyCode::KEY_LEFTALT); agg.pressed_keys.insert(evdev::KeyCode::KEY_V); with_var( "LESAVKA_CLIPBOARD_CMD", Some("printf 'rpc-coverage'"), || { with_var("LESAVKA_CLIPBOARD_CHORD", Some("ctrl+alt+v"), || { with_var("LESAVKA_CLIPBOARD_DEBOUNCE_MS", Some("0"), || { assert!(agg.try_handle_paste_event(evdev::KeyCode::KEY_V, 1)); }); }); }, ); let payload = paste_rx.try_recv().expect("rpc payload"); assert_eq!(payload, "rpc-coverage"); } }