96 lines
3.1 KiB
Rust
96 lines
3.1 KiB
Rust
|
|
//! 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<evdev::Device> {
|
||
|
|
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::<evdev::KeyCode>::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"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|