diff --git a/.idea/tiny4linux.iml b/.idea/tiny4linux.iml index cf84ae4..bbe0a70 100644 --- a/.idea/tiny4linux.iml +++ b/.idea/tiny4linux.iml @@ -3,6 +3,7 @@ + diff --git a/Cargo.lock b/Cargo.lock index a8b19ff..e3882ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -182,6 +182,12 @@ dependencies = [ "libloading 0.7.4", ] +[[package]] +name = "assertables" +version = "9.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59051ec02907378a67b0ba1b8631121f5388c8dbbb3cec8c749d8f93c2c3c211" + [[package]] name = "async-broadcast" version = "0.7.2" @@ -3426,6 +3432,39 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "test-case" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2550dd13afcd286853192af8601920d959b14c401fcece38071d53bf0768a8" +dependencies = [ + "test-case-macros", +] + +[[package]] +name = "test-case-core" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "test-case-macros" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", + "test-case-core", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -3529,6 +3568,7 @@ dependencies = [ name = "tiny4linux" version = "2.1.2" dependencies = [ + "assertables", "bon", "clap", "clap_complete", @@ -3541,6 +3581,7 @@ dependencies = [ "iced", "iced_font_awesome", "nix 0.30.1", + "test-case", "thiserror 2.0.17", "tiny4linux_assets", ] diff --git a/Cargo.toml b/Cargo.toml index a8b52b9..dfc33f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,10 @@ dialoguer = { version = "0.12.0", features = ["fuzzy-select"], optional = true } [build-dependencies] tiny4linux_assets = { git = "https://github.com/OpenFoxes/Tiny4Linux-Icons", rev = "57c10f204b75f1c61087fd9568814ee52d0ec036", optional = true } +[dev-dependencies] +test-case = "3.3.1" +assertables = "9.8.2" + [[bin]] name = "tiny4linux-gui" path = "src/gui/main.rs" diff --git a/src/libs/camera/command02.rs b/src/libs/camera/command02.rs index e70b048..0b7589e 100644 --- a/src/libs/camera/command02.rs +++ b/src/libs/camera/command02.rs @@ -26,3 +26,88 @@ pub fn command02( .try_into() .unwrap() } + +#[cfg(test)] +mod tests { + mod unit { + use crate::libs::camera::command02::command02; + + #[test] + fn full_builder() { + let command = command02() + .function_group([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]) + .sequence_nr([0x10, 0x11]) + .checksum([0x20, 0x21]) + .command([0x30, 0x31, 0x32, 0x33, 0x34, 0x35]) + .appendix([ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, + ]) + .build(); + + assert_eq!( + command[0..2], + [0xaa, 0x25], + "Command should start with frame id" + ); + assert_eq!(command[2..4], [0x10, 0x11], "Sequence number should be set"); + assert_eq!(command[4..6], [0x0c, 0x00], "Segment size should be fix"); + assert_eq!(command[6..8], [0x20, 0x21], "Checksum should be set"); + assert_eq!( + command[8..14], + [0x01, 0x02, 0x03, 0x04, 0x05, 0x06], + "Function group should be set" + ); + assert_eq!( + command[14..20], + [0x30, 0x31, 0x32, 0x33, 0x34, 0x35], + "Command should be set" + ); + assert_eq!( + command[20..36], + [ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f + ], + "Appendix should be set" + ); + } + + #[test] + fn minimal_builder() { + let command = command02() + .function_group([0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5]) + .sequence_nr([0xb0, 0xb1]) + .checksum([0xc0, 0xc1]) + .command([0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5]) + .build(); + + assert_eq!( + command[0..2], + [0xaa, 0x25], + "Command should start with frame id" + ); + assert_eq!(command[2..4], [0xb0, 0xb1], "Sequence number should be set"); + assert_eq!(command[4..6], [0x0c, 0x00], "Segment size should be fix"); + assert_eq!(command[6..8], [0xc0, 0xc1], "Checksum should be set"); + assert_eq!( + command[8..14], + [0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5], + "Function group should be set" + ); + assert_eq!( + command[14..20], + [0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5], + "Command should be set" + ); + assert_eq!( + command[20..36], + [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00 + ], + "Appendix should be empty" + ); + } + } +} diff --git a/src/libs/camera/commands/ai_mode.rs b/src/libs/camera/commands/ai_mode.rs index 7b0a2d8..d9aa7c6 100644 --- a/src/libs/camera/commands/ai_mode.rs +++ b/src/libs/camera/commands/ai_mode.rs @@ -22,3 +22,24 @@ impl AIModeCommand { }) } } + +#[cfg(test)] +mod tests { + use crate::{AIMode, AIModeCommand}; + use test_case::test_case; + + #[test_case(AIMode::NoTracking, [0x16, 0x02, 0x00, 0x00]; "no tracking")] + #[test_case(AIMode::NormalTracking, [0x16, 0x02, 0x02, 0x00]; "normal tracking")] + #[test_case(AIMode::UpperBody, [0x16, 0x02, 0x02, 0x01]; "upper body")] + #[test_case(AIMode::DeskMode, [0x16, 0x02, 0x05, 0x00]; "desk mode")] + #[test_case(AIMode::Whiteboard, [0x16, 0x02, 0x04, 0x00]; "whiteboard")] + #[test_case(AIMode::Group, [0x16, 0x02, 0x01, 0x00]; "group")] + #[test_case(AIMode::Hand, [0x16, 0x02, 0x03, 0x00]; "hand")] + #[test_case(AIMode::CloseUp, [0x16, 0x02, 0x02, 0x02]; "close up")] + #[test_case(AIMode::Headless, [0x16, 0x02, 0x02, 0x03]; "headless")] + #[test_case(AIMode::LowerBody, [0x16, 0x02, 0x02, 0x04]; "lower body")] + #[test_case(AIMode::Unknown, [0x16, 0x02, 0x00, 0x00]; "unknown")] + fn ai_mode(mode: AIMode, expected: [u8; 4]) { + assert_eq!(AIModeCommand::build(mode).unwrap(), expected) + } +} diff --git a/src/libs/camera/commands/exposure_mode.rs b/src/libs/camera/commands/exposure_mode.rs index 556cc1c..abd5e38 100644 --- a/src/libs/camera/commands/exposure_mode.rs +++ b/src/libs/camera/commands/exposure_mode.rs @@ -13,3 +13,38 @@ impl ExposureModeCommand { } } } + +#[cfg(test)] +mod tests { + mod unit { + use crate::{ExposureMode, ExposureModeCommand}; + use assertables::{assert_none, assert_some}; + use test_case::test_case; + + #[test] + fn manual_exposure_mode() { + let exposure_command = ExposureModeCommand::build(ExposureMode::Manual); + + assert_none!( + exposure_command, + "manual exposure mode should not set a exposure mode command" + ) + } + + #[test_case(ExposureMode::Global, [0x03, 0x01, 0x00]; "global automatic exposure should be set")] + #[test_case(ExposureMode::Face, [0x03, 0x01, 0x01]; "face automatic exposure should be set")] + fn automatic_exposure_modes(mode: ExposureMode, expected: [u8; 3]) { + let exposure_command = ExposureModeCommand::build(mode); + + assert_some!( + exposure_command, + "automatic exposure mode should set a exposure mode command" + ); + assert_eq!( + exposure_command.unwrap(), + expected, + "correct command is expected" + ) + } + } +} diff --git a/src/libs/camera/commands/exposure_mode_type.rs b/src/libs/camera/commands/exposure_mode_type.rs index 7f77140..4347da0 100644 --- a/src/libs/camera/commands/exposure_mode_type.rs +++ b/src/libs/camera/commands/exposure_mode_type.rs @@ -30,3 +30,62 @@ impl ExposureModeTypeCommand { .build() } } + +#[cfg(test)] +mod tests { + use crate::{ExposureMode, ExposureModeTypeCommand}; + use test_case::test_case; + + #[test] + fn manual_exposure_mode() { + let exposure_command = ExposureModeTypeCommand::build(ExposureMode::Manual); + + assert_eq!( + exposure_command[8..14], + [0x0a, 0x02, 0x82, 0x29, 0x05, 0x00], + "Function group should be set for exposure mode type" + ); + assert_eq!( + exposure_command[2..4], + [0x16, 0x00], + "Sequence number should be set" + ); + assert_eq!( + exposure_command[6..8], + [0x58, 0x91], + "Checksum should be set" + ); + assert_eq!( + exposure_command[14..20], + [0xb2, 0xaf, 0x02, 0x04, 0x00, 0x00], + "Command should be set" + ); + } + + #[test_case(ExposureMode::Global; "global automatic exposure should be set")] + #[test_case(ExposureMode::Face; "face automatic exposure should be set")] + fn automatic_exposure_modes(mode: ExposureMode) { + let exposure_command = ExposureModeTypeCommand::build(mode); + + assert_eq!( + exposure_command[8..14], + [0x0a, 0x02, 0x82, 0x29, 0x05, 0x00], + "Function group should be set for exposure mode type" + ); + assert_eq!( + exposure_command[2..4], + [0x15, 0x00], + "Sequence number should be set" + ); + assert_eq!( + exposure_command[6..8], + [0xa8, 0x9e], + "Checksum should be set" + ); + assert_eq!( + exposure_command[14..20], + [0xf9, 0x27, 0x01, 0x32, 0x00, 0x00], + "Command should be set" + ); + } +} diff --git a/src/libs/camera/commands/goto_preset_position.rs b/src/libs/camera/commands/goto_preset_position.rs index 6c5041d..4293b39 100644 --- a/src/libs/camera/commands/goto_preset_position.rs +++ b/src/libs/camera/commands/goto_preset_position.rs @@ -46,3 +46,54 @@ impl GotoPresetPositionCommand { .build()) } } + +#[cfg(test)] +mod tests { + use crate::GotoPresetPositionCommand; + use assertables::{assert_err, assert_ok}; + use test_case::test_case; + + #[test_case(0, [0x20, 0x00], [0x6b, 0xdc], [0xd6, 0xfb, 0x00, 0x00, 0x00, 0x00]; "preset position 1 (id 0)")] + #[test_case(1, [0x1a, 0x00], [0x4b, 0x03], [0xeb, 0x2a, 0x01, 0x00, 0x00, 0x00]; "preset position 2 (id 1)")] + #[test_case(2, [0x26, 0x00], [0x8b, 0xc3], [0xaf, 0x19, 0x02, 0x00, 0x00, 0x00]; "preset position 3 (id 2)")] + fn valid_preset_position( + position: i8, + sequence_nr: [u8; 2], + checksum: [u8; 2], + command: [u8; 6], + ) { + let position_command_option = GotoPresetPositionCommand::build(position); + + assert_ok!( + &position_command_option, + "preset position command should be built" + ); + + let position_command = position_command_option.unwrap(); + assert_eq!( + position_command[8..14], + [0x0a, 0x04, 0xc4, 0x39, 0x14, 0x00], + "Function group should be set for preset position" + ); + assert_eq!( + position_command[2..4], + sequence_nr, + "Sequence number should be set" + ); + assert_eq!(position_command[6..8], checksum, "Checksum should be set"); + assert_eq!(position_command[14..20], command, "Command should be set"); + } + + #[test_case(-1; "invalid preset position (lower than 0)")] + #[test_case(3; "invalid preset position (greater than 2)")] + #[test_case(i8::MIN; "invalid preset position (minimal number)")] + #[test_case(i8::MAX - 1; "invalid preset position (maximal number - 1)")] + fn invalid_preset_position(invalid_position: i8) { + let position_command_option = GotoPresetPositionCommand::build(invalid_position); + + assert_err!( + position_command_option, + "invalid position should return an error" + ); + } +} diff --git a/src/libs/camera/commands/hdr_mode.rs b/src/libs/camera/commands/hdr_mode.rs index 497e2a5..405dbf9 100644 --- a/src/libs/camera/commands/hdr_mode.rs +++ b/src/libs/camera/commands/hdr_mode.rs @@ -11,3 +11,15 @@ impl HdrModeCommand { } } } + +#[cfg(test)] +mod tests { + use crate::HdrModeCommand; + use test_case::test_case; + + #[test_case(true, [0x01, 0x01, 0x01]; "HDR on")] + #[test_case(false, [0x01, 0x01, 0x00]; "HDR off")] + fn hdr_mode(mode: bool, expected: [u8; 3]) { + assert_eq!(HdrModeCommand::build(mode), expected) + } +} diff --git a/src/libs/camera/commands/sleep.rs b/src/libs/camera/commands/sleep.rs index 9a58783..28bb873 100644 --- a/src/libs/camera/commands/sleep.rs +++ b/src/libs/camera/commands/sleep.rs @@ -35,3 +35,39 @@ impl SleepCommand { .build()) } } + +#[cfg(test)] +mod tests { + use crate::{SleepCommand, SleepMode}; + use assertables::assert_ok; + use test_case::test_case; + + #[test_case(SleepMode::Awake, [0xa5, 0x00], [0x5f, 0xef], [0xbe, 0x07, 0x00, 0x00, 0x00, 0x00]; "awake command")] + #[test_case(SleepMode::Sleep, [0x42, 0x00], [0xea, 0x63], [0xbf, 0xfb, 0x01, 0x00, 0x00, 0x00]; "sleep command")] + fn sleep_mode(mode: SleepMode, sequence_nr: [u8; 2], checksum: [u8; 2], command: [u8; 6]) { + let sleep_command_option = SleepCommand::build(mode); + + assert_ok!(&sleep_command_option, "sleep mode command should be built"); + + let sleep_command = sleep_command_option.unwrap(); + assert_eq!( + sleep_command[8..14], + [0x0a, 0x02, 0xc2, 0xa0, 0x04, 0x00], + "Function group should be set for sleep mode" + ); + assert_eq!( + sleep_command[2..4], + sequence_nr, + "Sequence number should be set" + ); + assert_eq!(sleep_command[6..8], checksum, "Checksum should be set"); + assert_eq!(sleep_command[14..20], command, "Command should be set"); + } + + #[test] + #[should_panic] + fn unknown_sleep_mode() { + SleepCommand::build(SleepMode::Unknown) + .expect("Should panic because of unknown sleep mode"); + } +} diff --git a/src/libs/camera/commands/tracking_speed.rs b/src/libs/camera/commands/tracking_speed.rs index 2450b00..a794b2c 100644 --- a/src/libs/camera/commands/tracking_speed.rs +++ b/src/libs/camera/commands/tracking_speed.rs @@ -37,3 +37,35 @@ impl TrackingSpeedCommand { .build()) } } + +#[cfg(test)] +mod tests { + use crate::{TrackingSpeed, TrackingSpeedCommand}; + use assertables::assert_ok; + use test_case::test_case; + + #[test_case(TrackingSpeed::Standard, [0x20, 0x00], [0xab, 0xcb], [0xe6, 0x3f, 0x00, 0x00, 0x00, 0x00]; "standard speed")] + #[test_case(TrackingSpeed::Sport, [0x21, 0x00], [0xfa, 0x0e], [0x67, 0xfe, 0x02, 0x00, 0x00, 0x00]; "sport speed")] + fn speed_mode(mode: TrackingSpeed, sequence_nr: [u8; 2], checksum: [u8; 2], command: [u8; 6]) { + let speed_command_option = TrackingSpeedCommand::build(mode); + + assert_ok!( + &speed_command_option, + "tracking speed command should be built" + ); + + let speed_command = speed_command_option.unwrap(); + assert_eq!( + speed_command[8..14], + [0x0a, 0x04, 0xc4, 0x0c, 0x01, 0x00], + "Function group should be set for sleep mode" + ); + assert_eq!( + speed_command[2..4], + sequence_nr, + "Sequence number should be set" + ); + assert_eq!(speed_command[6..8], checksum, "Checksum should be set"); + assert_eq!(speed_command[14..20], command, "Command should be set"); + } +} diff --git a/src/libs/camera/enums.rs b/src/libs/camera/enums.rs index ffee2e2..0ee4e87 100644 --- a/src/libs/camera/enums.rs +++ b/src/libs/camera/enums.rs @@ -89,3 +89,65 @@ pub enum ExposureModeType { Auto, Manual, } + +#[cfg(test)] +mod tests { + mod unit { + mod display { + mod sleep_mode { + use crate::SleepMode; + use test_case::test_case; + + #[test_case(SleepMode::Awake, "Awake"; "Awake")] + #[test_case(SleepMode::Sleep, "Sleeping"; "Sleep")] + #[test_case(SleepMode::Unknown, "Unknown"; "Unknown")] + fn sleep_mode(mode: SleepMode, expected: &str) { + assert_eq!(&mode.to_string(), expected); + } + } + + mod ai_mode { + use crate::AIMode; + use test_case::test_case; + + #[test_case(AIMode::NoTracking, "Static"; "no tracking")] + #[test_case(AIMode::NormalTracking, "Normal Tracking"; "normal tracking")] + #[test_case(AIMode::UpperBody, "Upper Body"; "upper body")] + #[test_case(AIMode::CloseUp, "Close-up"; "close up")] + #[test_case(AIMode::Headless, "Headless"; "headless")] + #[test_case(AIMode::LowerBody, "Lower Body"; "lower body")] + #[test_case(AIMode::DeskMode, "Desk Mode"; "desk mode")] + #[test_case(AIMode::Whiteboard, "Whiteboard"; "whiteboard")] + #[test_case(AIMode::Hand, "Hand"; "hand")] + #[test_case(AIMode::Group, "Group"; "group")] + #[test_case(AIMode::Unknown, "Unknown"; "unknown")] + fn ai_mode(mode: AIMode, expected: &str) { + assert_eq!(&mode.to_string(), expected); + } + } + + mod tracking_speed { + use crate::TrackingSpeed; + use test_case::test_case; + + #[test_case(TrackingSpeed::Standard, "Standard"; "standard")] + #[test_case(TrackingSpeed::Sport, "Sport"; "sport")] + fn tracking_speed(mode: TrackingSpeed, expected: &str) { + assert_eq!(&mode.to_string(), expected); + } + } + + mod exposure_mode { + use crate::ExposureMode; + use test_case::test_case; + + #[test_case(ExposureMode::Manual, "Manual"; "manual")] + #[test_case(ExposureMode::Global, "Global"; "global")] + #[test_case(ExposureMode::Face, "Face"; "face")] + fn exposure_mode(mode: ExposureMode, expected: &str) { + assert_eq!(&mode.to_string(), expected); + } + } + } + } +} diff --git a/src/libs/camera/status.rs b/src/libs/camera/status.rs index 4dd07d1..3a5c9af 100644 --- a/src/libs/camera/status.rs +++ b/src/libs/camera/status.rs @@ -67,3 +67,112 @@ impl CameraStatus { } } } + +#[cfg(test)] +mod tests { + mod unit { + mod decode { + use crate::{AIMode, CameraStatus, SleepMode, TrackingSpeed}; + use test_case::test_case; + + const RAW_TEST_STRING: [u8; 57] = [0; 57]; + + #[test_case(0x00, SleepMode::Awake; "awake")] + #[test_case(0x01, SleepMode::Sleep; "sleep")] + #[test_case(0x02, SleepMode::Unknown; "unknown")] + fn sleep_mode(hex_value: u8, expected: SleepMode) { + let mut test_string = RAW_TEST_STRING; + test_string[0x02] = hex_value; + + let status = CameraStatus::decode(&test_string); + + assert_eq!(expected, status.awake); + } + + #[test_case(0x00, 0x00, AIMode::NoTracking; "none")] + #[test_case(0x02, 0x00, AIMode::NormalTracking; "normal")] + #[test_case(0x02, 0x01, AIMode::UpperBody; "upper body")] + #[test_case(0x02, 0x02, AIMode::CloseUp; "close up")] + #[test_case(0x02, 0x03, AIMode::Headless; "headless")] + #[test_case(0x02, 0x04, AIMode::LowerBody; "lower body")] + #[test_case(0x05, 0x00, AIMode::DeskMode; "desk")] + #[test_case(0x04, 0x00, AIMode::Whiteboard; "whiteboard")] + #[test_case(0x06, 0x00, AIMode::Hand; "hand")] + #[test_case(0x01, 0x00, AIMode::Group; "group")] + #[test_case(0x17, 0x42, AIMode::Unknown; "unknown")] + fn ai_mode(hex_value1: u8, hex_value2: u8, expected: AIMode) { + let mut test_string = RAW_TEST_STRING; + test_string[0x18] = hex_value1; + test_string[0x1c] = hex_value2; + + let status = CameraStatus::decode(&test_string); + + assert_eq!(expected, status.ai_mode); + } + + #[test_case(0x00, TrackingSpeed::Standard; "standard")] + #[test_case(0x02, TrackingSpeed::Sport; "sport")] + #[test_case(0x01, TrackingSpeed::Standard; "unknown defaults to standard")] + fn tracking_speed(hex_value: u8, expected: TrackingSpeed) { + let mut test_string = RAW_TEST_STRING; + test_string[0x21] = hex_value; + + let status = CameraStatus::decode(&test_string); + + assert_eq!(expected, status.speed); + } + + #[test_case(0x01, true; "on")] + #[test_case(0x00, false; "off")] + #[test_case(0x02, true; "unknown defaults to on")] + fn hdr(hex_value: u8, expected: bool) { + let mut test_string = RAW_TEST_STRING; + test_string[0x06] = hex_value; + + let status = CameraStatus::decode(&test_string); + + assert_eq!(expected, status.hdr_on); + } + + #[test] + fn status_defaults() { + let default_status = CameraStatus::default(); + + assert_eq!(default_status.awake, SleepMode::Unknown, "sleep is unknown"); + assert_eq!( + default_status.ai_mode, + AIMode::Unknown, + "tracking is unknown" + ); + assert_eq!( + default_status.speed, + TrackingSpeed::Standard, + "speed is standard" + ); + assert_eq!(default_status.hdr_on, false, "hdr is off"); + } + } + } + + mod integration { + mod camera_status { + use crate::{AIMode, CameraStatus, SleepMode, TrackingSpeed}; + + #[test] + fn decode_status() { + let data = [ + 0x27, 0x00, 0x00, 0x01, 0x42, 0x00, 0x01, 0x01, 0x01, 0x01, 0x88, 0xff, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x00, 0x21, 0x00, 0x02, 0x01, + 0x03, 0x00, 0x01, 0x00, 0x00, 0x1e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + ]; + let status = CameraStatus::decode(&data); + assert_eq!(status.awake, SleepMode::Awake); + assert_eq!(status.hdr_on, true); + assert_eq!(status.ai_mode, AIMode::UpperBody); + assert_eq!(status.speed, TrackingSpeed::Sport); + } + } + } +}