use std::assert_matches;
use super::*;
use hidpp::feature::smartshift::WheelMode;
use crate::SmartShiftMode;
use crate::SmartShiftStatus;
use crate::write::smartshift::{
is_missing_enhanced, is_transient_smartshift_error, smartshift_to_wheel,
status_matches_desired, wheel_mode_to_smartshift,
};
use crate::write::{HidppFeatureErrorKind, HidppOperation};
#[test]
fn capabilities_sort_and_deduplicate_values() -> Result<(), WriteError> {
let caps = DpiCapabilities::new(vec![1600, 400, 800, 800])?;
assert_eq!(caps.values(), [400, 800, 1600]);
assert_eq!(caps.min(), 400);
assert_eq!(caps.max(), 1600);
Ok(())
}
#[test]
fn capabilities_reject_empty_list() {
assert_matches!(
DpiCapabilities::new(Vec::new()),
Err(WriteError::EmptyDpiList)
);
}
#[test]
fn nearest_returns_closest_supported_value() -> Result<(), WriteError> {
let caps = DpiCapabilities::new(vec![400, 800, 1600])?;
assert_eq!(caps.nearest(390), 400);
assert_eq!(caps.nearest(1000), 800);
assert_eq!(caps.nearest(2000), 1600);
Ok(())
}
#[test]
fn step_hint_returns_smallest_positive_gap() -> Result<(), WriteError> {
let caps = DpiCapabilities::new(vec![400, 800, 1200, 2000])?;
assert_eq!(caps.step_hint(), 400);
Ok(())
}
#[test]
fn adjacent_test_target_prefers_next_then_previous_value() -> Result<(), WriteError> {
let caps = DpiCapabilities::new(vec![400, 800, 1600])?;
assert_eq!(caps.adjacent_test_target(400), Some(800));
assert_eq!(caps.adjacent_test_target(800), Some(1600));
assert_eq!(caps.adjacent_test_target(1600), Some(800));
Ok(())
}
#[test]
fn adjacent_test_target_handles_current_outside_list() -> Result<(), WriteError> {
let caps = DpiCapabilities::new(vec![400, 800, 1600])?;
assert_eq!(caps.adjacent_test_target(1000), Some(1600));
assert_eq!(caps.adjacent_test_target(2000), Some(1600));
Ok(())
}
#[test]
fn smartshift_and_wheel_mode_byte_encodings_match() {
assert_eq!(
u8::from(SmartShiftMode::Free),
u8::from(WheelMode::Freespin)
);
assert_eq!(
u8::from(SmartShiftMode::Ratchet),
u8::from(WheelMode::Ratchet)
);
}
#[test]
fn wheel_mode_maps_to_smartshift_mode() {
assert_eq!(
wheel_mode_to_smartshift(WheelMode::Freespin),
SmartShiftMode::Free
);
assert_eq!(
wheel_mode_to_smartshift(WheelMode::Ratchet),
SmartShiftMode::Ratchet
);
}
#[test]
fn smartshift_to_wheel_round_trips() {
for mode in [SmartShiftMode::Free, SmartShiftMode::Ratchet] {
assert_eq!(wheel_mode_to_smartshift(smartshift_to_wheel(mode)), mode);
}
}
#[test]
fn missing_enhanced_triggers_fallback() {
assert!(is_missing_enhanced(&WriteError::FeatureUnsupported {
feature_hex: 0x2111,
}));
}
#[test]
fn missing_legacy_does_not_trigger_fallback() {
assert!(!is_missing_enhanced(&WriteError::FeatureUnsupported {
feature_hex: 0x2110,
}));
}
#[test]
fn transport_errors_do_not_trigger_fallback() {
assert!(!is_missing_enhanced(&WriteError::DeviceUnreachable {
index: 0xff,
}));
assert!(!is_missing_enhanced(&WriteError::Hidpp("boom".into())));
}
#[test]
fn transient_smartshift_errors_are_retryable() {
assert!(is_transient_smartshift_error(&WriteError::HidppFeature {
operation: HidppOperation::WriteSmartShift,
feature_hex: 0x2111,
kind: HidppFeatureErrorKind::InvalidArgument,
}));
assert!(is_transient_smartshift_error(&WriteError::HidppFeature {
operation: HidppOperation::WriteSmartShift,
feature_hex: 0x2110,
kind: HidppFeatureErrorKind::Busy,
}));
assert!(is_transient_smartshift_error(
&WriteError::UnsupportedResponse {
operation: HidppOperation::ReadSmartShift,
feature_hex: 0x2110,
}
));
}
#[test]
fn permanent_smartshift_errors_are_not_retryable() {
assert!(!is_transient_smartshift_error(
&WriteError::FeatureUnsupported {
feature_hex: 0x2111,
}
));
assert!(!is_transient_smartshift_error(&WriteError::HidppFeature {
operation: HidppOperation::WriteSmartShift,
feature_hex: 0x2111,
kind: HidppFeatureErrorKind::InvalidFunctionId,
}));
}
#[test]
fn status_match_ignores_zero_preserve_fields() {
let current = SmartShiftStatus {
mode: SmartShiftMode::Ratchet,
auto_disengage: 10,
tunable_torque: 33,
};
let desired = SmartShiftStatus {
mode: SmartShiftMode::Ratchet,
auto_disengage: 10,
tunable_torque: 0,
};
assert!(status_matches_desired(current, desired));
assert!(!status_matches_desired(
current,
SmartShiftStatus {
mode: SmartShiftMode::Free,
..desired
}
));
}