#![allow(clippy::assertions_on_constants)]
use pokeys_lib::encoders::{EncoderData, EncoderOptions};
use pokeys_lib::io::{PinCapability, PinData, PinFunction};
use pokeys_lib::keyboard_matrix::MatrixKeyboard;
use pokeys_lib::lcd::LcdData;
use pokeys_lib::pulse_engine::PulseEngineV2;
use pokeys_lib::pwm::PwmData;
use pokeys_lib::sensors::EasySensor;
use pokeys_lib::*;
#[cfg(test)]
mod unit_tests {
use super::*;
#[test]
fn test_library_version() {
let version = version();
assert!(!version.is_empty());
assert!(version.contains('.'));
assert_eq!(VERSION_MAJOR, 0);
assert_eq!(VERSION_MINOR, 3);
assert_eq!(VERSION_PATCH, 0);
assert_eq!(version, "0.3.0");
}
#[test]
fn test_device_types() {
use DeviceTypeId::*;
assert_eq!(Device55v1 as u8, 0);
assert_eq!(Device55v2 as u8, 1);
assert_eq!(Device56U as u8, 10);
assert_eq!(Device57U as u8, 30);
assert_eq!(Device58EU as u8, 40);
assert_eq!(Bootloader55 as u8, 3);
assert_eq!(Bootloader56U as u8, 15);
assert_eq!(Bootloader58 as u8, 41);
}
#[test]
fn test_connection_types() {
use ConnectionParam::*;
use DeviceConnectionType::*;
assert_eq!(UsbDevice as u8, 0);
assert_eq!(NetworkDevice as u8, 1);
assert_eq!(FastUsbDevice as u8, 2);
assert_eq!(Tcp as u8, 0);
assert_eq!(Udp as u8, 1);
}
#[test]
fn test_pin_functions() {
#[allow(deprecated)]
let _functions = [
PinFunction::PinRestricted,
PinFunction::Reserved,
PinFunction::DigitalInput,
PinFunction::DigitalOutput,
PinFunction::AnalogInput,
PinFunction::AnalogOutput,
PinFunction::TriggeredInput,
PinFunction::DigitalCounter,
PinFunction::InvertPin,
];
assert_ne!(PinFunction::DigitalInput, PinFunction::DigitalOutput);
assert_eq!(PinFunction::DigitalInput, PinFunction::DigitalInput);
}
#[test]
fn test_from_u8_combined_invert_byte() {
assert_eq!(
PinFunction::from_u8(0x82).unwrap(),
PinFunction::DigitalInput
);
assert_eq!(
PinFunction::from_u8(0x84).unwrap(),
PinFunction::DigitalOutput
);
assert_eq!(
PinFunction::from_u8(0xA0).unwrap(),
PinFunction::TriggeredInput
);
}
#[test]
fn test_pin_data_invert_accessors_public() {
let mut pin_data = PinData::new();
pin_data.pin_function = 0x82; assert!(pin_data.is_inverted());
assert_eq!(pin_data.base_function(), PinFunction::DigitalInput);
assert!(pin_data.is_digital_input());
pin_data.pin_function = 0x02;
assert!(!pin_data.is_inverted());
assert_eq!(pin_data.base_function(), PinFunction::DigitalInput);
}
#[test]
fn test_pin_capabilities() {
let caps = [
PinCapability::DigitalInput,
PinCapability::DigitalOutput,
PinCapability::AnalogInput,
PinCapability::AnalogOutput,
PinCapability::KeyboardMapping,
PinCapability::TriggeredInput,
];
let cap_vec: Vec<_> = caps.iter().collect();
assert_eq!(cap_vec.len(), caps.len());
}
#[test]
fn test_device_info_default() {
let info = DeviceInfo::default();
assert_eq!(info.pin_count, 0);
assert_eq!(info.pwm_count, 0);
assert_eq!(info.analog_inputs, 0);
assert_eq!(info.encoders_count, 0);
assert_eq!(info.fast_encoders, 0);
assert_eq!(info.ultra_fast_encoders, 0);
assert_eq!(info.pwm_internal_frequency, 0);
assert_eq!(info.prot_i2c, 0);
assert_eq!(info.prot_1wire, 0);
assert_eq!(info.additional_options, 0);
}
#[test]
fn test_device_data_default() {
let data = DeviceData::default();
assert_eq!(data.device_type_id, 0);
assert_eq!(data.firmware_version_major, 0);
assert_eq!(data.firmware_version_minor, 0);
assert_eq!(data.serial_number, 0);
assert_eq!(data.device_name(), "");
assert_eq!(data.user_id, 0);
assert!(!data.device_locked());
assert_eq!(data.device_features(), 0);
}
#[test]
fn test_encoder_options() {
let mut options = EncoderOptions::new();
assert!(!options.enabled);
assert!(!options.sampling_4x);
assert!(!options.sampling_2x);
assert!(!options.direct_key_mapping_a);
assert!(!options.macro_mapping_a);
assert!(!options.direct_key_mapping_b);
assert!(!options.macro_mapping_b);
options.enabled = true;
options.sampling_4x = true;
options.sampling_2x = true;
assert!(options.enabled);
assert!(options.sampling_4x);
assert!(options.sampling_2x);
}
#[test]
fn test_pwm_data() {
let pwm_data = PwmData::new();
assert_eq!(pwm_data.pwm_period, 0);
assert_eq!(pwm_data.pwm_values.len(), 6); assert_eq!(pwm_data.enabled_channels, 0);
for i in 0..6 {
assert!(!pwm_data.is_channel_enabled(i));
assert_eq!(pwm_data.get_duty_cycle(i).unwrap(), 0);
}
}
#[test]
fn test_pwm_pin_mapping() {
assert_eq!(PwmData::pin_to_channel(22).unwrap(), 0); assert_eq!(PwmData::pin_to_channel(21).unwrap(), 1); assert_eq!(PwmData::pin_to_channel(20).unwrap(), 2); assert_eq!(PwmData::pin_to_channel(19).unwrap(), 3); assert_eq!(PwmData::pin_to_channel(18).unwrap(), 4); assert_eq!(PwmData::pin_to_channel(17).unwrap(), 5);
assert!(PwmData::pin_to_channel(16).is_err());
assert!(PwmData::pin_to_channel(23).is_err());
assert_eq!(PwmData::channel_to_pin(0).unwrap(), 22);
assert_eq!(PwmData::channel_to_pin(1).unwrap(), 21);
assert_eq!(PwmData::channel_to_pin(2).unwrap(), 20);
assert_eq!(PwmData::channel_to_pin(3).unwrap(), 19);
assert_eq!(PwmData::channel_to_pin(4).unwrap(), 18);
assert_eq!(PwmData::channel_to_pin(5).unwrap(), 17);
assert!(PwmData::channel_to_pin(6).is_err());
}
#[test]
fn test_pwm_channel_operations() {
let mut pwm_data = PwmData::new();
pwm_data.set_channel_enabled(0, true).unwrap();
pwm_data.set_channel_enabled(2, true).unwrap();
assert!(pwm_data.is_channel_enabled(0));
assert!(!pwm_data.is_channel_enabled(1));
assert!(pwm_data.is_channel_enabled(2));
assert_eq!(pwm_data.enabled_channels, 0b00000101);
pwm_data.set_channel_enabled(0, false).unwrap();
assert!(!pwm_data.is_channel_enabled(0));
assert_eq!(pwm_data.enabled_channels, 0b00000100);
pwm_data.set_duty_cycle(1, 1500).unwrap();
assert_eq!(pwm_data.get_duty_cycle(1).unwrap(), 1500);
assert!(pwm_data.set_channel_enabled(6, true).is_err());
assert!(pwm_data.set_duty_cycle(6, 1000).is_err());
assert!(pwm_data.get_duty_cycle(6).is_err());
}
#[test]
fn test_matrix_keyboard() {
let keyboard = MatrixKeyboard::new();
assert_eq!(keyboard.configuration, 0);
assert_eq!(keyboard.width, 0);
assert_eq!(keyboard.height, 0);
assert_eq!(keyboard.scanning_decimation, 0);
assert_eq!(keyboard.column_pins.len(), 8); assert_eq!(keyboard.row_pins.len(), 16); }
#[test]
fn test_lcd_data() {
let lcd = LcdData::new();
assert_eq!(lcd.configuration, 0);
assert_eq!(lcd.rows, 0);
assert_eq!(lcd.columns, 0);
assert_eq!(lcd.row_refresh_flags, 0);
assert_eq!(lcd.line1.len(), 20); assert_eq!(lcd.line2.len(), 20); }
#[test]
fn test_pulse_engine_v2() {
let pe = PulseEngineV2::new();
assert_eq!(pe.axes_state.len(), 8); assert_eq!(pe.axes_config.len(), 8);
assert_eq!(pe.emergency_switch_polarity, 0);
}
#[test]
fn test_real_time_clock() {
let rtc = RealTimeClock::default();
assert_eq!(rtc.year, 0);
assert_eq!(rtc.month, 0);
assert_eq!(rtc.dom, 0); assert_eq!(rtc.hour, 0);
assert_eq!(rtc.min, 0); assert_eq!(rtc.sec, 0); assert_eq!(rtc.dow, 0); }
#[test]
fn test_error_types() {
use PoKeysError::*;
let errors = [
DeviceNotFound,
ConnectionFailed,
InvalidParameter,
CommunicationError,
NotConnected,
UnsupportedOperation,
InternalError("test".to_string()),
];
for error in &errors {
let error_string = format!("{error}");
assert!(!error_string.is_empty());
assert_eq!(error, error);
}
}
#[test]
fn test_error_from_conversions() {
let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "test");
let pokeys_error: PoKeysError = io_error.into();
match pokeys_error {
PoKeysError::Io(_) => {}
_ => panic!("Expected Io error"),
}
}
#[test]
fn test_buffer_constants() {
assert!(REQUEST_BUFFER_SIZE > 0);
assert!(RESPONSE_BUFFER_SIZE > 0);
assert!(REQUEST_BUFFER_SIZE <= 1024);
assert!(RESPONSE_BUFFER_SIZE <= 1024);
assert!(REQUEST_BUFFER_SIZE >= 64);
assert!(RESPONSE_BUFFER_SIZE >= 64);
}
#[test]
fn test_pin_data_validation() {
fn is_valid_pin(pin: u8, max_pins: u8) -> bool {
pin > 0 && pin <= max_pins
}
assert!(!is_valid_pin(0, 55)); assert!(is_valid_pin(1, 55)); assert!(is_valid_pin(55, 55)); assert!(!is_valid_pin(56, 55)); }
#[test]
fn test_pwm_duty_cycle_validation() {
fn is_valid_duty_cycle(duty: f32) -> bool {
(0.0..=100.0).contains(&duty)
}
assert!(is_valid_duty_cycle(0.0));
assert!(is_valid_duty_cycle(50.0));
assert!(is_valid_duty_cycle(100.0));
assert!(!is_valid_duty_cycle(-1.0));
assert!(!is_valid_duty_cycle(101.0));
}
#[test]
fn test_encoder_value_ranges() {
let max_encoder_value = i32::MAX;
let min_encoder_value = i32::MIN;
assert!(max_encoder_value > 0);
assert!(min_encoder_value < 0);
let test_value = max_encoder_value;
let wrapped = test_value.wrapping_add(1);
assert_eq!(wrapped, min_encoder_value);
}
#[test]
fn test_network_device_info() {
let net_info = NetworkDeviceInfo::default();
assert_eq!(net_info.ip_address(), [0, 0, 0, 0]);
assert_eq!(net_info.subnet_mask, [0, 0, 0, 0]);
assert_eq!(net_info.gateway(), [0, 0, 0, 0]);
assert_eq!(net_info.dns_server(), [0, 0, 0, 0]);
assert_eq!(net_info.mac_address(), [0; 6]);
assert_eq!(net_info.device_name(), "");
assert_eq!(net_info.http_port(), 80);
assert_eq!(net_info.tcp_port(), 20055);
assert_eq!(net_info.udp_port(), 20055);
assert!(!net_info.dhcp_enabled());
}
#[test]
fn test_easy_sensor() {
let sensor = EasySensor::new();
assert_eq!(sensor.sensor_id, [0; 8]); assert_eq!(sensor.sensor_type, 0);
assert_eq!(sensor.sensor_value, 0); assert_eq!(sensor.sensor_refresh_period, 0); }
#[test]
fn test_data_structure_sizes() {
use std::mem::size_of;
assert!(size_of::<DeviceInfo>() > 0);
assert!(size_of::<DeviceData>() > 0);
assert!(size_of::<PinData>() > 0);
assert!(size_of::<EncoderData>() > 0);
assert!(size_of::<DeviceInfo>() < 1024);
assert!(size_of::<DeviceData>() < 1024);
}
#[test]
fn test_string_conversions() {
let device_name = "PoKeys57U";
assert!(!device_name.is_empty());
assert!(device_name.len() < 256);
assert!(device_name.chars().all(|c| c.is_alphanumeric()));
}
#[test]
fn test_bit_operations() {
fn set_bit(value: u8, bit: u8) -> u8 {
value | (1 << bit)
}
fn clear_bit(value: u8, bit: u8) -> u8 {
value & !(1 << bit)
}
fn test_bit(value: u8, bit: u8) -> bool {
(value & (1 << bit)) != 0
}
let mut value = 0u8;
value = set_bit(value, 0);
assert!(test_bit(value, 0));
assert_eq!(value, 1);
value = set_bit(value, 7);
assert!(test_bit(value, 7));
assert_eq!(value, 129);
value = clear_bit(value, 0);
assert!(!test_bit(value, 0));
assert_eq!(value, 128); }
#[test]
fn test_checksum_calculation() {
fn calculate_checksum(data: &[u8]) -> u8 {
data.iter().fold(0u8, |acc, &x| acc.wrapping_add(x))
}
let test_data = [0x01, 0x02, 0x03, 0x04];
let checksum = calculate_checksum(&test_data);
assert_eq!(checksum, 10);
let empty_checksum = calculate_checksum(&[]);
assert_eq!(empty_checksum, 0);
let overflow_data = [0xFF, 0xFF];
let overflow_checksum = calculate_checksum(&overflow_data);
assert_eq!(overflow_checksum, 254); }
#[test]
fn test_array_bounds() {
let test_array = [1, 2, 3, 4, 5];
assert_eq!(test_array[0], 1);
assert_eq!(test_array[4], 5);
let slice = &test_array[1..4];
assert_eq!(slice, &[2, 3, 4]);
let sum: i32 = test_array.iter().sum();
assert_eq!(sum, 15);
}
}
#[cfg(test)]
mod network_config_tests {
use pokeys_lib::{NetworkDeviceConfig, NetworkDeviceInfo};
#[test]
fn test_network_device_config_defaults() {
let cfg = NetworkDeviceConfig::new();
assert_eq!(cfg.device_info.subnet_mask, [255, 255, 255, 0]);
assert_eq!(cfg.device_info.tcp_timeout, 1000);
assert_eq!(cfg.device_info.dhcp, 0);
assert_eq!(cfg.device_info.additional_network_options, 0xA0);
assert_eq!(cfg.device_info.ip_address_setup, [0, 0, 0, 0]);
assert_eq!(cfg.device_info.gateway_ip, [0, 0, 0, 0]);
}
#[test]
fn test_network_device_config_builder() {
let mut cfg = NetworkDeviceConfig::new();
cfg.set_ip_address([192, 168, 1, 50]);
cfg.set_subnet_mask([255, 255, 255, 0]);
cfg.set_default_gateway([192, 168, 1, 1]);
cfg.set_dhcp(false);
cfg.set_tcp_timeout(2000);
cfg.set_network_options(false, false, false);
assert_eq!(cfg.device_info.ip_address_setup, [192, 168, 1, 50]);
assert_eq!(cfg.device_info.subnet_mask, [255, 255, 255, 0]);
assert_eq!(cfg.device_info.gateway_ip, [192, 168, 1, 1]);
assert_eq!(cfg.device_info.dhcp, 0);
assert_eq!(cfg.device_info.tcp_timeout, 2000);
}
#[test]
fn test_dhcp_toggle() {
let mut cfg = NetworkDeviceConfig::new();
cfg.set_dhcp(true);
assert_eq!(cfg.device_info.dhcp, 1);
cfg.set_dhcp(false);
assert_eq!(cfg.device_info.dhcp, 0);
}
#[test]
fn test_network_options_flags() {
let mut cfg = NetworkDeviceConfig::new();
cfg.set_network_options(true, false, false);
assert_eq!(cfg.device_info.additional_network_options, 0xA0 | 0x01);
cfg.set_network_options(false, true, false);
assert_eq!(cfg.device_info.additional_network_options, 0xA0 | 0x02);
cfg.set_network_options(false, false, true);
assert_eq!(cfg.device_info.additional_network_options, 0xA0 | 0x04);
cfg.set_network_options(true, true, true);
assert_eq!(cfg.device_info.additional_network_options, 0xA0 | 0x07);
cfg.set_network_options(false, false, false);
assert_eq!(cfg.device_info.additional_network_options, 0xA0);
}
#[test]
fn test_tcp_timeout_unit_conversion() {
let cases: &[(u16, u16)] = &[
(1000, 10),
(100, 1),
(500, 5),
(0, 0), ];
for &(input_ms, expected_units) in cases {
let units = (input_ms / 100).max(if input_ms == 0 { 0 } else { 1 });
assert_eq!(units, expected_units, "input_ms={input_ms}");
}
}
#[test]
fn test_gateway_subnet_set_flag() {
let info = NetworkDeviceInfo {
gateway_ip: [192, 168, 1, 1],
subnet_mask: [0, 0, 0, 0],
..Default::default()
};
let flag = if info.gateway_ip != [0, 0, 0, 0] || info.subnet_mask != [0, 0, 0, 0] {
1u8
} else {
0u8
};
assert_eq!(flag, 1);
let info2 = NetworkDeviceInfo::default();
let flag2 = if info2.gateway_ip != [0, 0, 0, 0] || info2.subnet_mask != [0, 0, 0, 0] {
1u8
} else {
0u8
};
assert_eq!(flag2, 0);
}
#[test]
fn test_additional_options_nibble() {
for lower in 0u8..=0x0F {
let options = (lower & 0x0F) | 0xA0;
assert_eq!(
options >> 4,
0xA,
"upper nibble not 0xA for lower={lower:#x}"
);
assert_eq!(options & 0x0F, lower);
}
}
#[test]
fn test_device_name_truncation() {
let long_name = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; let truncated = if long_name.len() > 20 {
&long_name[..20]
} else {
long_name
};
assert_eq!(truncated.len(), 20);
assert_eq!(truncated, "ABCDEFGHIJKLMNOPQRST");
let short_name = "MyDevice"; let not_truncated = if short_name.len() > 20 {
&short_name[..20]
} else {
short_name
};
assert_eq!(not_truncated, "MyDevice");
}
#[test]
fn test_network_device_info_dhcp_enabled() {
let mut info = NetworkDeviceInfo::default();
assert!(!info.dhcp_enabled());
info.dhcp = 1;
assert!(info.dhcp_enabled());
info.dhcp = 0;
assert!(!info.dhcp_enabled());
}
}
#[cfg(test)]
mod mock_tests {
use super::*;
struct MockDevice {
connected: bool,
pin_states: std::collections::HashMap<u8, bool>,
analog_values: std::collections::HashMap<u8, u16>,
encoder_values: std::collections::HashMap<u8, i32>,
}
impl MockDevice {
fn new() -> Self {
Self {
connected: false,
pin_states: std::collections::HashMap::new(),
analog_values: std::collections::HashMap::new(),
encoder_values: std::collections::HashMap::new(),
}
}
fn connect(&mut self) -> Result<()> {
self.connected = true;
Ok(())
}
fn disconnect(&mut self) {
self.connected = false;
}
fn is_connected(&self) -> bool {
self.connected
}
fn set_digital_output(&mut self, pin: u8, state: bool) -> Result<()> {
if !self.connected {
return Err(PoKeysError::NotConnected);
}
if pin == 0 || pin > 55 {
return Err(PoKeysError::InvalidParameter);
}
self.pin_states.insert(pin, state);
Ok(())
}
fn get_digital_input(&self, pin: u8) -> Result<bool> {
if !self.connected {
return Err(PoKeysError::NotConnected);
}
if pin == 0 || pin > 55 {
return Err(PoKeysError::InvalidParameter);
}
Ok(self.pin_states.get(&pin).copied().unwrap_or(false))
}
fn get_analog_input(&self, pin: u8) -> Result<u16> {
if !self.connected {
return Err(PoKeysError::NotConnected);
}
if pin == 0 || pin > 8 {
return Err(PoKeysError::InvalidParameter);
}
Ok(self.analog_values.get(&pin).copied().unwrap_or(0))
}
fn set_encoder_value(&mut self, encoder: u8, value: i32) {
self.encoder_values.insert(encoder, value);
}
fn get_encoder_value(&self, encoder: u8) -> Result<i32> {
if !self.connected {
return Err(PoKeysError::NotConnected);
}
if encoder >= 25 {
return Err(PoKeysError::InvalidParameter);
}
Ok(self.encoder_values.get(&encoder).copied().unwrap_or(0))
}
}
#[test]
fn test_mock_device_connection() {
let mut device = MockDevice::new();
assert!(!device.is_connected());
device.connect().unwrap();
assert!(device.is_connected());
device.disconnect();
assert!(!device.is_connected());
}
#[test]
fn test_mock_digital_io() {
let mut device = MockDevice::new();
device.connect().unwrap();
device.set_digital_output(1, true).unwrap();
assert!(device.get_digital_input(1).unwrap());
device.set_digital_output(1, false).unwrap();
assert!(!device.get_digital_input(1).unwrap());
device.set_digital_output(5, true).unwrap();
device.set_digital_output(10, false).unwrap();
assert!(device.get_digital_input(5).unwrap());
assert!(!device.get_digital_input(10).unwrap());
}
#[test]
fn test_mock_error_handling() {
let mut device = MockDevice::new();
assert!(device.set_digital_output(1, true).is_err());
assert!(device.get_digital_input(1).is_err());
assert!(device.get_analog_input(1).is_err());
assert!(device.get_encoder_value(0).is_err());
device.connect().unwrap();
assert!(device.set_digital_output(0, true).is_err());
assert!(device.set_digital_output(56, true).is_err());
assert!(device.get_digital_input(0).is_err());
assert!(device.get_digital_input(56).is_err());
assert!(device.get_analog_input(0).is_err());
assert!(device.get_analog_input(9).is_err());
assert!(device.get_encoder_value(25).is_err());
}
#[test]
fn test_mock_analog_inputs() {
let mut device = MockDevice::new();
device.connect().unwrap();
for pin in 1..=8 {
assert_eq!(device.get_analog_input(pin).unwrap(), 0);
}
device.analog_values.insert(1, 2048); device.analog_values.insert(2, 4095);
assert_eq!(device.get_analog_input(1).unwrap(), 2048);
assert_eq!(device.get_analog_input(2).unwrap(), 4095);
}
#[test]
fn test_mock_encoders() {
let mut device = MockDevice::new();
device.connect().unwrap();
for encoder in 0..25 {
assert_eq!(device.get_encoder_value(encoder).unwrap(), 0);
}
device.set_encoder_value(0, 100);
device.set_encoder_value(1, -50);
device.set_encoder_value(2, i32::MAX);
device.set_encoder_value(3, i32::MIN);
assert_eq!(device.get_encoder_value(0).unwrap(), 100);
assert_eq!(device.get_encoder_value(1).unwrap(), -50);
assert_eq!(device.get_encoder_value(2).unwrap(), i32::MAX);
assert_eq!(device.get_encoder_value(3).unwrap(), i32::MIN);
}
#[test]
fn test_mock_state_persistence() {
let mut device = MockDevice::new();
device.connect().unwrap();
device.set_digital_output(1, true).unwrap();
device.set_digital_output(2, false).unwrap();
device.analog_values.insert(1, 1234);
device.set_encoder_value(0, 567);
assert!(device.get_digital_input(1).unwrap());
assert!(!device.get_digital_input(2).unwrap());
assert_eq!(device.get_analog_input(1).unwrap(), 1234);
assert_eq!(device.get_encoder_value(0).unwrap(), 567);
device.disconnect();
device.connect().unwrap();
assert!(device.get_digital_input(1).unwrap());
assert!(!device.get_digital_input(2).unwrap());
assert_eq!(device.get_analog_input(1).unwrap(), 1234);
assert_eq!(device.get_encoder_value(0).unwrap(), 567);
}
#[test]
fn test_servo_types() {
let servo_180 = ServoConfig::one_eighty(22, 25000, 50000);
assert_eq!(servo_180.pin, 22);
match servo_180.servo_type {
ServoType::OneEighty { pos_0, pos_180 } => {
assert_eq!(pos_0, 25000);
assert_eq!(pos_180, 50000);
}
_ => panic!("Expected OneEighty servo type"),
}
let servo_360_pos = ServoConfig::three_sixty_position(21, 30000, 60000);
assert_eq!(servo_360_pos.pin, 21);
match servo_360_pos.servo_type {
ServoType::ThreeSixtyPosition { pos_0, pos_360 } => {
assert_eq!(pos_0, 30000);
assert_eq!(pos_360, 60000);
}
_ => panic!("Expected ThreeSixtyPosition servo type"),
}
let servo_360_speed = ServoConfig::three_sixty_speed(20, 37500, 50000, 25000);
assert_eq!(servo_360_speed.pin, 20);
match servo_360_speed.servo_type {
ServoType::ThreeSixtySpeed {
stop,
clockwise,
anti_clockwise,
} => {
assert_eq!(stop, 37500);
assert_eq!(clockwise, 50000);
assert_eq!(anti_clockwise, 25000);
}
_ => panic!("Expected ThreeSixtySpeed servo type"),
}
}
#[test]
fn test_servo_angle_calculations() {
let servo_180 = ServoConfig::one_eighty(22, 25000, 50000);
match servo_180.servo_type {
ServoType::OneEighty { pos_0, pos_180 } => {
let range = pos_180 as f32 - pos_0 as f32;
let angle_90 = (pos_0 as f32 + (90.0 / 180.0) * range) as u32;
assert_eq!(angle_90, 37500); }
_ => panic!("Expected OneEighty servo type"),
}
let servo_360 = ServoConfig::three_sixty_position(21, 30000, 60000);
match servo_360.servo_type {
ServoType::ThreeSixtyPosition { pos_0, pos_360 } => {
let range = pos_360 as f32 - pos_0 as f32;
let angle_180 = (pos_0 as f32 + (180.0 / 360.0) * range) as u32;
assert_eq!(angle_180, 45000); }
_ => panic!("Expected ThreeSixtyPosition servo type"),
}
}
#[test]
fn test_servo_speed_calculations() {
let servo_speed = ServoConfig::three_sixty_speed(20, 37500, 50000, 25000);
match servo_speed.servo_type {
ServoType::ThreeSixtySpeed {
stop,
clockwise,
anti_clockwise,
} => {
let cw_range = clockwise as f32 - stop as f32;
let acw_range = anti_clockwise as f32 - stop as f32;
let speed_50_cw = (stop as f32 + (50.0 / 100.0) * cw_range) as u32;
assert_eq!(speed_50_cw, 43750);
let speed_50_acw = (stop as f32 + (50.0 / 100.0) * acw_range) as u32;
assert_eq!(speed_50_acw, 31250);
}
_ => panic!("Expected ThreeSixtySpeed servo type"),
}
}
#[test]
fn test_servo_type_validation() {
let servo_180 = ServoConfig::one_eighty(22, 25000, 50000);
let servo_360_pos = ServoConfig::three_sixty_position(21, 30000, 60000);
let servo_360_speed = ServoConfig::three_sixty_speed(20, 37500, 50000, 25000);
assert!(matches!(servo_180.servo_type, ServoType::OneEighty { .. }));
assert!(matches!(
servo_360_pos.servo_type,
ServoType::ThreeSixtyPosition { .. }
));
assert!(matches!(
servo_360_speed.servo_type,
ServoType::ThreeSixtySpeed { .. }
));
assert_eq!(servo_180.pin, 22);
assert_eq!(servo_360_pos.pin, 21);
assert_eq!(servo_360_speed.pin, 20);
}
}