// 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();")); }