diff --git a/Cargo.toml b/Cargo.toml index 75f2f21..6b917b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,9 @@ num_enum = "0.7.5" saphyr = "0.0.6" serde = { version = "1.0.228", features = ["derive"] } +[dev-dependencies] +csv = "1.4.0" + [lints.clippy] cloned_instead_of_copied = "warn" copy_iterator = "warn" @@ -45,7 +48,3 @@ unnested_or_patterns = "warn" unused_self = "warn" used_underscore_binding = "warn" unwrap_used = "warn" - -[dev-dependencies] -csv = "1.4.0" - diff --git a/src/lib.rs b/src/lib.rs index 39ea5cb..5f96936 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,4 +3,7 @@ mod file; pub mod raw; pub mod telemetry; +#[cfg(test)] +mod test_utils; + pub use file::IbtFile; diff --git a/src/raw.rs b/src/raw.rs index 5146721..cc98753 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -20,7 +20,7 @@ pub const HEADER_SIZE: usize = std::mem::size_of::
(); pub const SUB_HEADER_SIZE: usize = std::mem::size_of::(); pub const VAR_HEADER_SIZE: usize = std::mem::size_of::(); -#[derive(Clone, Copy, Debug, AnyBitPattern)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, AnyBitPattern)] #[repr(C, align(16))] pub struct Header { // TODO: add assertions on this field @@ -51,7 +51,7 @@ pub struct Header { pub var_bufs: [VarBuf; IRSDK_MAX_BUFS], } -#[derive(Clone, Copy, Debug, Pod, Zeroable)] +#[derive(Clone, Copy, Debug, Eq, Pod, Zeroable)] #[repr(C, align(16))] pub struct VarBuf { /// Which tick this buffer represents @@ -61,7 +61,7 @@ pub struct VarBuf { _pad: [c_int; 2], } -#[derive(Clone, Copy, Debug, AnyBitPattern)] +#[derive(Clone, Copy, Debug, PartialEq, AnyBitPattern)] #[repr(C, align(16))] pub struct DiskSubHeader { /// Timestamp for the start of the session, seconds since epoch @@ -76,14 +76,14 @@ pub struct DiskSubHeader { pub session_record_count: c_int, } -#[derive(Clone, Copy, Debug, AnyBitPattern)] +#[derive(Clone, Copy, Debug, Eq, AnyBitPattern)] #[repr(C, align(16))] pub struct VarHeader { pub ty: c_int, pub offset: c_int, pub count: c_int, - pub count_as_time: c_int, - + pub count_as_time: c_char, + _pad: [c_char; 3], pub name: [c_char; IRSDK_MAX_STRING], pub desc: [c_char; IRSDK_MAX_DESC], pub unit: [c_char; IRSDK_MAX_STRING], @@ -106,3 +106,122 @@ impl VarHeader { bytemuck::cast_slice(bytes) } } + +impl PartialEq for VarBuf { + fn eq(&self, other: &Self) -> bool { + self.tick_count == other.tick_count && self.buf_offset == other.buf_offset + } +} + +impl PartialEq for VarHeader { + fn eq(&self, other: &Self) -> bool { + self.ty == other.ty + && self.offset == other.offset + && self.count == other.count + && self.count_as_time == other.count_as_time + && self.name == other.name + && self.desc == other.desc + && self.unit == other.unit + } +} + +#[cfg(test)] +mod tests { + use crate::{ + include_bytes_aligned, + raw::{DiskSubHeader, Header, VarBuf, VarHeader}, + }; + + #[test] + fn decodes_raw_header() { + // sampled from an IBT file + let raw = include_bytes_aligned!("../test-data/raw_header"); + let header = Header::from_raw_bytes(&raw); + + assert_eq!( + header, + Header { + ver: 2, + status: 1, + tick_rate: 60, + session_info_update: 0, + session_info_len: 15654, + session_info_offset: 40320, + num_vars: 279, + var_header_offset: 144, + num_buf: 1, + buf_len: 1081, + var_bufs: [ + VarBuf { + tick_count: 2122, + buf_offset: 55974, + _pad: [0, 0], + }, + VarBuf { + tick_count: 0, + buf_offset: 0, + _pad: [0, 0], + }, + VarBuf { + tick_count: 0, + buf_offset: 0, + _pad: [0, 0], + }, + VarBuf { + tick_count: 0, + buf_offset: 0, + _pad: [0, 0], + }, + ], + } + ); + } + + #[test] + fn decodes_raw_disk_sub_header() { + // sampled from an IBT file + let raw = include_bytes_aligned!("../test-data/raw_sub_header"); + let disk_sub_header = DiskSubHeader::from_raw_bytes(&raw); + + assert_eq!( + disk_sub_header, + DiskSubHeader { + session_start_date: 1764642265, + session_start_time: 52.116666030881774, + session_end_time: 219.34999949144233, + session_lap_count: 3, + session_record_count: 9759, + } + ); + } + + #[test] + fn decodes_var_header() { + // sampled from an IBT file + let raw = include_bytes_aligned!("../test-data/raw_var_header"); + let var_header = VarHeader::slice_from_fraw_bytes(&raw); + + assert_eq!( + var_header, + [VarHeader { + ty: 5, + offset: 0, + count: 1, + count_as_time: 0, + _pad: [0, 0, 0], + name: test_string(b"SessionTime"), + desc: test_string(b"Seconds since session start"), + unit: test_string(b"s"), + }] + ); + } + + /// Copies the given bytes to an array and fills the leftovers with `b'\0''` + fn test_string(val: &[u8]) -> [i8; N] { + assert!(N > val.len()); + let mut arr = [0; N]; + arr[0..val.len()].clone_from_slice(val); + + arr.map(|b| b as i8) + } +} diff --git a/src/test_utils.rs b/src/test_utils.rs new file mode 100644 index 0000000..9f2cbba --- /dev/null +++ b/src/test_utils.rs @@ -0,0 +1,12 @@ +#[macro_export] +macro_rules! include_bytes_aligned { + ($path:literal, $alignment:expr) => {{ + ::aligned_vec::AVec::<_, ::aligned_vec::ConstAlign<{ $alignment }>>::from_slice( + $alignment, + include_bytes!($path), + ) + }}; + ($path:literal) => { + include_bytes_aligned!($path, $crate::raw::ALIGNMENT) + }; +} diff --git a/test-data/raw_header b/test-data/raw_header new file mode 100644 index 0000000000000000000000000000000000000000..848de349f26b959ccce2fcdaa3a2ff93ce3eca54 GIT binary patch literal 112 xcmZQ#U|?WmU|_IefB-dH28M>Y3=HCo3=9*Xa+WL*IS}c^!N9QW7KlY21^`@Q1b6@d literal 0 HcmV?d00001 diff --git a/test-data/raw_sub_header b/test-data/raw_sub_header new file mode 100644 index 0000000000000000000000000000000000000000..ece691c74dc3812de5213c39a0247d5135383956 GIT binary patch literal 32 ncmccVtCz{Zz`#&9W9G|ud|nO{wUvyFv$GwT85kJk)fgB6s~HG= literal 0 HcmV?d00001 diff --git a/test-data/raw_var_header b/test-data/raw_var_header new file mode 100644 index 0000000000000000000000000000000000000000..2b4d1afb9c7b914a0ab790653993afe198d97777 GIT binary patch literal 144 zcmZQ&fB;4a#SolYT%4Jo7m}Hq3Kc=6f>V?8^HPcxiZk<)Qx%FKsuhY$5{pVu72@C( J;}C}l0|1t~4`2WQ literal 0 HcmV?d00001