//! Extra include-based coverage for input aggregator edge cases. //! //! Scope: keep additional quick-toggle regression checks in a separate file so //! each testing module stays under the 500 LOC contract. //! Targets: `client/src/input/inputs.rs`. //! Why: quick swap-key taps can otherwise disappear inside one poll cycle and //! make local/remote handoff feel flaky in the live launcher path. mod layout { pub use lesavka_client::layout::*; } mod keyboard { pub use lesavka_client::input::keyboard::*; } mod mouse { pub use lesavka_client::input::mouse::*; } #[allow(warnings)] mod inputs_contract_extra { include!(env!("LESAVKA_CLIENT_INPUTS_SRC")); use evdev::AttributeSet; use evdev::uinput::VirtualDevice; use serial_test::serial; use std::thread; fn open_virtual_device(vdev: &mut VirtualDevice) -> Option { for _ in 0..40 { if let Ok(mut nodes) = vdev.enumerate_dev_nodes_blocking() { if let Some(Ok(path)) = nodes.next() { if let Ok(dev) = evdev::Device::open(path) { let _ = dev.set_nonblocking(true); return Some(dev); } } } thread::sleep(std::time::Duration::from_millis(10)); } None } fn build_keyboard_pair(name: &str) -> Option<(VirtualDevice, evdev::Device)> { let mut keys = AttributeSet::::new(); keys.insert(evdev::KeyCode::KEY_A); keys.insert(evdev::KeyCode::KEY_ENTER); let mut vdev = VirtualDevice::builder() .ok()? .name(name) .with_keys(&keys) .ok()? .build() .ok()?; let dev = open_virtual_device(&mut vdev)?; Some((vdev, dev)) } #[test] #[serial] fn quick_toggle_detects_tap_when_press_and_release_land_in_same_poll_cycle() { let Some((mut vdev, dev)) = build_keyboard_pair("lesavka-input-quick-toggle-tap") else { return; }; let (kbd_tx, _) = tokio::sync::broadcast::channel(16); let (agg_kbd_tx, _) = tokio::sync::broadcast::channel(16); let (agg_mou_tx, _) = tokio::sync::broadcast::channel(16); let mut keyboard = KeyboardAggregator::new(dev, false, kbd_tx, None); vdev.emit(&[ evdev::InputEvent::new(evdev::EventType::KEY.0, evdev::KeyCode::KEY_A.0, 1), evdev::InputEvent::new(evdev::EventType::KEY.0, evdev::KeyCode::KEY_A.0, 0), ]) .expect("emit quick-toggle tap"); thread::sleep(std::time::Duration::from_millis(20)); keyboard.process_events(); let mut agg = InputAggregator::new(false, agg_kbd_tx, agg_mou_tx, None); agg.quick_toggle_key = Some(evdev::KeyCode::KEY_A); agg.keyboards.push(keyboard); assert!( agg.quick_toggle_active(), "quick-toggle should fire even when a tap starts and ends inside one poll batch" ); assert!( !agg.quick_toggle_active(), "tap activation should be consumed after one observation" ); } }