lesavka/tests/chaos/input/input_disconnect_chaos_contract.rs

70 lines
2.6 KiB
Rust

// Simulated disconnect safety contracts for input.
//
// Scope: source-level contracts for release-on-disconnect behavior.
// Targets: `client/src/input/keyboard.rs`, `client/src/input/mouse.rs`, and
// `client/src/input/inputs/routing_state.rs`.
// Why: the safest disconnected input is a released input; regressions here can
// leave remote modifiers/buttons stuck or leak typing into the wrong target.
const KEYBOARD_REPORTING_SRC: &str =
include_str!("../../../client/src/input/keyboard/reporting.rs");
const MOUSE_SRC: &str = include_str!(env!("LESAVKA_CLIENT_MOUSE_SRC"));
const INPUT_ROUTING_SRC: &str = include_str!("../../../client/src/input/inputs/routing_state.rs");
fn block_after<'a>(source: &'a str, marker: &str) -> &'a str {
let start = source.find(marker).expect("marker exists");
&source[start..]
}
#[test]
fn keyboard_drop_always_sends_empty_report_to_release_pressed_keys() {
let drop_impl = block_after(KEYBOARD_REPORTING_SRC, "impl Drop for KeyboardAggregator");
assert!(drop_impl.contains("data: [0; 8].into()"));
}
#[test]
fn mouse_drop_uses_mouse_sized_empty_report_to_release_buttons() {
let drop_impl = block_after(MOUSE_SRC, "impl Drop for MouseAggregator");
let drop_body = drop_impl
.split_once("#[cfg(test)]")
.map(|(body, _)| body)
.unwrap_or(drop_impl);
assert!(drop_body.contains("data: [0; 4].into()"));
assert!(
!drop_body.contains("data: [0; 8].into()"),
"mouse disconnect release must not send keyboard-sized reports"
);
}
#[test]
fn local_release_sends_zero_reports_before_disabling_forwarding() {
let release = block_after(INPUT_ROUTING_SRC, "fn begin_local_release");
let keyboard_zero = release
.find("k.send_empty_report()")
.expect("keyboard release");
let keyboard_disable = release.find("k.set_send(false)").expect("keyboard disable");
let mouse_reset = release.find("m.reset_state()").expect("mouse release");
let mouse_disable = release.find("m.set_send(false)").expect("mouse disable");
assert!(
keyboard_zero < keyboard_disable,
"keyboard release report must be sent before forwarding is disabled"
);
assert!(
mouse_reset < mouse_disable,
"mouse release report must be sent before forwarding is disabled"
);
}
#[test]
fn finishing_local_release_resets_state_after_ungrabbing_devices() {
let finish = block_after(INPUT_ROUTING_SRC, "fn finish_local_release");
assert!(finish.contains("k.set_grab(false);"));
assert!(finish.contains("k.reset_state();"));
assert!(finish.contains("m.set_grab(false);"));
assert!(finish.contains("m.reset_state();"));
}