diff --git a/rp2040/src/button_matrix.rs b/rp2040/src/button_matrix.rs index ab22a23..155c732 100644 --- a/rp2040/src/button_matrix.rs +++ b/rp2040/src/button_matrix.rs @@ -112,7 +112,114 @@ impl<'a, const R: usize, const C: usize, const N: usize> ButtonMatrix<'a, R, C, /// /// For small `N` this copy is cheap. If needed, the API could be extended to /// return a reference in the future. - pub fn buttons_pressed(&mut self) -> [bool; N] { +pub fn buttons_pressed(&mut self) -> [bool; N] { self.pressed } } + +#[cfg(all(test, feature = "std"))] +mod tests { +use super::*; +use core::cell::Cell; +use embedded_hal::digital::ErrorType; +use std::rc::Rc; + + struct MockInputPin { + state: Rc>, + } + + impl MockInputPin { + fn new(state: Rc>) -> Self { + Self { state } + } + } + + impl ErrorType for MockInputPin { + type Error = Infallible; + } + + impl InputPin for MockInputPin { + fn is_high(&mut self) -> Result { + Ok(!self.state.get()) + } + + fn is_low(&mut self) -> Result { + Ok(self.state.get()) + } + } + + struct MockOutputPin { + state: Rc>, + } + + impl MockOutputPin { + fn new(state: Rc>) -> Self { + Self { state } + } + } + + impl ErrorType for MockOutputPin { + type Error = Infallible; + } + + impl OutputPin for MockOutputPin { + fn set_high(&mut self) -> Result<(), Self::Error> { + self.state.set(true); + Ok(()) + } + + fn set_low(&mut self) -> Result<(), Self::Error> { + self.state.set(false); + Ok(()) + } + } + + fn matrix_fixture() -> ( + ButtonMatrix<'static, 1, 1, 1>, + Rc>, + Rc>, + ) { + let row_state = Rc::new(Cell::new(false)); + let col_state = Rc::new(Cell::new(false)); + + let row_pin: &'static mut dyn InputPin = + Box::leak(Box::new(MockInputPin::new(row_state.clone()))); + let col_pin: &'static mut dyn OutputPin = + Box::leak(Box::new(MockOutputPin::new(col_state.clone()))); + + let rows: &'static mut [&'static mut dyn InputPin; 1] = + Box::leak(Box::new([row_pin])); + let cols: &'static mut [&'static mut dyn OutputPin; 1] = + Box::leak(Box::new([col_pin])); + + let matrix = ButtonMatrix::new(rows, cols, 2); + + (matrix, row_state, col_state) + } + + #[test] + fn init_pins_sets_columns_high() { + let (mut matrix, _row_state, col_state) = matrix_fixture(); + assert!(!col_state.get()); + matrix.init_pins(); + assert!(col_state.get()); + } + + #[test] + fn process_column_obeys_debounce() { + let (mut matrix, row_state, _col_state) = matrix_fixture(); + let mut states = matrix.buttons_pressed(); + assert!(!states[0]); + row_state.set(true); + matrix.process_column(0); + matrix.process_column(0); + states = matrix.buttons_pressed(); + assert!(states[0]); + + row_state.set(false); + matrix.process_column(0); + matrix.process_column(0); + states = matrix.buttons_pressed(); + assert!(!states[0]); + } +} diff --git a/rp2040/src/mapping.rs b/rp2040/src/mapping.rs index c8adb7c..2403708 100644 --- a/rp2040/src/mapping.rs +++ b/rp2040/src/mapping.rs @@ -163,3 +163,41 @@ pub fn configure_button_mappings(buttons: &mut [Button]) { buttons[BUTTON_FRONT_LEFT_EXTRA].usb_button = USB_BUTTON_30; buttons[BUTTON_FRONT_RIGHT_EXTRA].usb_button = USB_BUTTON_31; } + +#[cfg(all(test, feature = "std"))] +mod tests { + use super::*; + use crate::buttons::{Button, TOTAL_BUTTONS}; + + #[test] + fn front_buttons_have_expected_mappings() { + let mut buttons = [Button::default(); TOTAL_BUTTONS]; + configure_button_mappings(&mut buttons); + + assert_eq!(buttons[BUTTON_FRONT_LEFT_LOWER].usb_button, USB_BUTTON_29); + assert_eq!(buttons[BUTTON_FRONT_RIGHT_UPPER].usb_button, USB_BUTTON_1); + assert_eq!(buttons[BUTTON_FRONT_CONFIG].usb_button_long, USB_BUTTON_3); + } + + #[test] + fn long_press_flags_set_correctly() { + let mut buttons = [Button::default(); TOTAL_BUTTONS]; + configure_button_mappings(&mut buttons); + + assert!(buttons[BUTTON_TOP_LEFT_LOW].enable_long_press); + assert!(buttons[BUTTON_TOP_LEFT_LOW].enable_long_hold); + assert!(buttons[BUTTON_TOP_RIGHT_LOW].enable_long_press); + assert!(!buttons[BUTTON_TOP_RIGHT_LOW].enable_long_hold); + } + + #[test] + fn hat_buttons_map_to_expected_ids() { + let mut buttons = [Button::default(); TOTAL_BUTTONS]; + configure_button_mappings(&mut buttons); + + assert_eq!(buttons[BUTTON_TOP_RIGHT_HAT_UP].usb_button, USB_HAT_UP); + assert_eq!(buttons[BUTTON_TOP_RIGHT_HAT_RIGHT].usb_button, USB_HAT_RIGHT); + assert_eq!(buttons[BUTTON_TOP_RIGHT_HAT_DOWN].usb_button, USB_HAT_DOWN); + assert_eq!(buttons[BUTTON_TOP_RIGHT_HAT_LEFT].usb_button, USB_HAT_LEFT); + } +}