From 9f95813f978f6af1a20b23c0ffd1dc60b7d2bcb7 Mon Sep 17 00:00:00 2001 From: Christoffer Martinsson Date: Tue, 16 Sep 2025 08:37:06 +0200 Subject: [PATCH] Added more unittests --- rp2040/src/axis.rs | 18 +++++++++++ rp2040/src/buttons.rs | 65 ++++++++++++++++++++++++++++++--------- rp2040/src/calibration.rs | 18 ++++++++++- 3 files changed, 86 insertions(+), 15 deletions(-) diff --git a/rp2040/src/axis.rs b/rp2040/src/axis.rs index 77c1363..3381e2e 100644 --- a/rp2040/src/axis.rs +++ b/rp2040/src/axis.rs @@ -416,6 +416,10 @@ pub fn calculate_axis_value( ) -> u16 { use crate::hardware::{ADC_MAX, ADC_MIN, AXIS_CENTER}; + if min >= max || center <= min || center >= max { + return center; + } + if value <= min { return ADC_MIN; } @@ -660,6 +664,20 @@ mod tests { assert_eq!(result, AXIS_CENTER); } + #[test] + fn test_calculate_axis_value_degenerate_calibration() { + let expo_lut = ExpoLUT::new(0.0); + + // When calibration collapses to a single point (min=max=center), + // the output should remain at that center rather than clamping low/high. + let result = calculate_axis_value(2100, 2100, 2100, 2100, (0, 0, 0), false, &expo_lut); + assert_eq!(result, 2100); + + // Also handle centers outside the valid window by returning center directly. + let result = calculate_axis_value(1500, 1400, 2000, 1400, (0, 0, 0), false, &expo_lut); + assert_eq!(result, 1400); + } + #[test] fn test_remap_function() { // Test basic remapping diff --git a/rp2040/src/buttons.rs b/rp2040/src/buttons.rs index c9776ec..5cda510 100644 --- a/rp2040/src/buttons.rs +++ b/rp2040/src/buttons.rs @@ -8,9 +8,9 @@ //! - Evaluate special combinations (bootloader, calibration, etc.) //! - Expose a compact state consumed by USB report generation -use crate::mapping::*; use crate::button_matrix::ButtonMatrix; -use crate::hardware::{NUMBER_OF_BUTTONS, AXIS_CENTER, BUTTON_ROWS, BUTTON_COLS}; +use crate::hardware::{AXIS_CENTER, BUTTON_COLS, BUTTON_ROWS, NUMBER_OF_BUTTONS}; +use crate::mapping::*; use embedded_hal::digital::InputPin; use rp2040_hal::timer::Timer; @@ -39,14 +39,13 @@ pub struct Button { // ==================== SPECIAL ACTIONS ==================== /// High‑level actions triggered by dedicated button combinations. - #[derive(Debug, PartialEq)] pub enum SpecialAction { None, Bootloader, StartCalibration, CancelCalibration, - ThrottleHold(u16), // Value to hold + ThrottleHold(u16), // Value to hold VirtualThrottleToggle, CalibrationSetModeM10, CalibrationSetModeM7, @@ -55,7 +54,6 @@ pub enum SpecialAction { // ==================== BUTTON MANAGER ==================== /// Aggregates and processes all buttons, exposing a stable API to the rest of the firmware. - pub struct ButtonManager { pub buttons: [Button; TOTAL_BUTTONS], } @@ -77,7 +75,10 @@ impl ButtonManager { } /// Update button states from the button matrix snapshot. - pub fn update_from_matrix(&mut self, matrix: &mut ButtonMatrix) { + pub fn update_from_matrix( + &mut self, + matrix: &mut ButtonMatrix, + ) { for (index, key) in matrix.buttons_pressed().iter().enumerate() { self.buttons[index].pressed = *key; } @@ -162,7 +163,11 @@ impl ButtonManager { } /// Check for special button combinations (bootloader, calibration state/mode, VT, etc.). - pub fn check_special_combinations(&self, unprocessed_axis_value: u16, calibration_active: bool) -> SpecialAction { + pub fn check_special_combinations( + &self, + unprocessed_axis_value: u16, + calibration_active: bool, + ) -> SpecialAction { // Secondary way to enter bootloader if self.buttons[BUTTON_FRONT_LEFT_LOWER].pressed && self.buttons[BUTTON_TOP_LEFT_MODE].pressed @@ -297,13 +302,14 @@ fn update_button_press_type(button: &mut Button, current_time: u32) { // Auto‑release generated USB press after minimum hold time const USB_MIN_HOLD_MS: u32 = 50; - if button.usb_press_active - && (!button.pressed - && !button.long_press_handled - && current_time - button.usb_press_start_time >= USB_MIN_HOLD_MS) - || (!button.enable_long_hold - && button.long_press_handled - && current_time - button.usb_press_start_time >= USB_MIN_HOLD_MS) + let elapsed = current_time.saturating_sub(button.usb_press_start_time); + let should_release = if button.long_press_handled { + !button.enable_long_hold && elapsed >= USB_MIN_HOLD_MS + } else { + !button.pressed && elapsed >= USB_MIN_HOLD_MS + }; + + if button.usb_press_active && should_release { button.usb_changed = true; button.usb_press_active = false; @@ -483,6 +489,37 @@ mod tests { assert!(button.long_press_handled); } + #[test] + fn test_button_press_type_long_press_auto_release_once() { + let mut button = Button::default(); + button.usb_button_long = 2; + button.enable_long_press = true; + button.enable_long_hold = false; + + // Press the button + button.pressed = true; + update_button_press_type(&mut button, 0); + button.previous_pressed = button.pressed; + + // Hold long enough to trigger the long press path + update_button_press_type(&mut button, 250); + assert!(button.usb_press_active); + assert_eq!(button.active_usb_button, 2); + + // Clear the changed flag to emulate USB stack observing it + button.usb_changed = false; + + // Keep holding and ensure we auto-release exactly once + update_button_press_type(&mut button, 320); + assert!(!button.usb_press_active); + assert!(button.usb_changed); + + button.usb_changed = false; + update_button_press_type(&mut button, 400); + assert!(!button.usb_press_active); + assert!(!button.usb_changed); + } + #[test] fn test_timer_integration_method_exists() { let manager = ButtonManager::new(); diff --git a/rp2040/src/calibration.rs b/rp2040/src/calibration.rs index 29aa610..2472575 100644 --- a/rp2040/src/calibration.rs +++ b/rp2040/src/calibration.rs @@ -135,7 +135,9 @@ impl CalibrationManager { ]; // Save calibration data to storage - let _ = storage::write_calibration_data(write_fn, &axis_data, self.gimbal_mode); + if storage::write_calibration_data(write_fn, &axis_data, self.gimbal_mode).is_err() { + return false; + } // End calibration mode self.active = false; @@ -519,6 +521,20 @@ mod tests { assert!(!manager.is_active()); // Should end calibration } + #[test] + fn test_save_calibration_failure_keeps_active() { + let mut manager = CalibrationManager::new(); + manager.start_calibration(); + let axes = [GimbalAxis::new(); NBR_OF_GIMBAL_AXIS]; + + let mut write_fn = |_page: u32, _data: &[u8]| Err(()); + + let result = manager.save_calibration(&axes, &mut write_fn); + + assert!(!result); + assert!(manager.is_active()); + } + #[test] fn test_save_calibration_inactive() { let mut manager = CalibrationManager::new(); // Note: not starting calibration