From 44383c10a83190facb27319e102460feff31093b Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sun, 3 Dec 2023 19:45:31 +0100 Subject: [PATCH] added some more tests --- src/ecss/mod.rs | 31 +++++++--- src/ecss/tc.rs | 12 +++- src/ecss/tm.rs | 152 ++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 175 insertions(+), 20 deletions(-) diff --git a/src/ecss/mod.rs b/src/ecss/mod.rs index 7e5f1ab..5f7ddb8 100644 --- a/src/ecss/mod.rs +++ b/src/ecss/mod.rs @@ -152,8 +152,7 @@ pub enum RealPfc { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum PusError { VersionNotSupported(PusVersion), - IncorrectCrc(u16), - NoRawData, + ChecksumFailure(u16), /// CRC16 needs to be calculated first CrcCalculationMissing, ByteConversion(ByteConversionError), @@ -165,11 +164,8 @@ impl Display for PusError { PusError::VersionNotSupported(v) => { write!(f, "PUS version {v:?} not supported") } - PusError::IncorrectCrc(crc) => { - write!(f, "crc16 {crc:#04x} is incorrect") - } - PusError::NoRawData => { - write!(f, "no raw data provided") + PusError::ChecksumFailure(crc) => { + write!(f, "checksum verification for crc16 {crc:#06x} failed") } PusError::CrcCalculationMissing => { write!(f, "crc16 was not calculated") @@ -269,7 +265,7 @@ pub(crate) fn verify_crc16_ccitt_false_from_raw_to_pus_error( ) -> Result<(), PusError> { verify_crc16_ccitt_false_from_raw(raw_data) .then(|| ()) - .ok_or(PusError::IncorrectCrc(crc16)) + .ok_or(PusError::ChecksumFailure(crc16)) } pub(crate) fn verify_crc16_ccitt_false_from_raw(raw_data: &[u8]) -> bool { @@ -391,6 +387,9 @@ mod tests { fn test_enum_u8() { let mut buf = [0, 0, 0]; let my_enum = EcssEnumU8::new(1); + assert_eq!(EcssEnumU8::ptc(), Ptc::Enumerated); + assert_eq!(my_enum.size(), 1); + assert_eq!(my_enum.pfc(), 8); my_enum .write_to_be_bytes(&mut buf[1..2]) .expect("To byte conversion of u8 failed"); @@ -404,6 +403,8 @@ mod tests { my_enum .write_to_be_bytes(&mut buf[1..3]) .expect("To byte conversion of u8 failed"); + assert_eq!(my_enum.size(), 2); + assert_eq!(my_enum.pfc(), 16); assert_eq!(buf[1], 0x1f); assert_eq!(buf[2], 0x2f); } @@ -477,4 +478,18 @@ mod tests { let ptc = Ptc::try_from(ptc_raw).unwrap(); assert_eq!(ptc, Ptc::AbsoluteTime); } + + #[test] + fn test_unsigned_pfc_from_u8() { + let pfc_raw = UnsignedPfc::OneByte as u8; + let pfc = UnsignedPfc::try_from(pfc_raw).unwrap(); + assert_eq!(pfc, UnsignedPfc::OneByte); + } + + #[test] + fn test_real_pfc_from_u8() { + let pfc_raw = RealPfc::Double as u8; + let pfc = RealPfc::try_from(pfc_raw).unwrap(); + assert_eq!(pfc, RealPfc::Double); + } } diff --git a/src/ecss/tc.rs b/src/ecss/tc.rs index 3bfe161..07cb673 100644 --- a/src/ecss/tc.rs +++ b/src/ecss/tc.rs @@ -878,6 +878,7 @@ mod tests { use crate::ecss::{PusError, PusPacket, WritablePusPacket}; use crate::{ByteConversionError, SpHeader}; use crate::{CcsdsPacket, SequenceFlags}; + use alloc::string::ToString; use alloc::vec::Vec; fn base_ping_tc_full_ctor() -> PusTcCreator<'static> { @@ -1013,10 +1014,19 @@ mod tests { .write_to_bytes(test_buf.as_mut_slice()) .expect("Error writing TC to buffer"); test_buf[12] = 0; + test_buf[11] = 0; let res = PusTcReader::new(&test_buf); assert!(res.is_err()); let err = res.unwrap_err(); - assert!(matches!(err, PusError::IncorrectCrc { .. })); + if let PusError::ChecksumFailure(crc) = err { + assert_eq!(crc, 0); + assert_eq!( + err.to_string(), + "checksum verification for crc16 0x0000 failed" + ); + } else { + panic!("unexpected error {err}"); + } } #[test] diff --git a/src/ecss/tm.rs b/src/ecss/tm.rs index 564e3d5..bc831db 100644 --- a/src/ecss/tm.rs +++ b/src/ecss/tm.rs @@ -556,21 +556,21 @@ impl<'raw_data> PusTmCreator<'raw_data> { /// automatically /// * `sec_header` - Information contained in the secondary header, including the service /// and subservice type - /// * `app_data` - Custom application data + /// * `source_data` - Custom application data /// * `set_ccsds_len` - Can be used to automatically update the CCSDS space packet data length /// field. If this is not set to true, [PusTm::update_ccsds_data_len] can be called to set /// the correct value to this field manually pub fn new( sp_header: &mut SpHeader, sec_header: PusTmSecondaryHeader<'raw_data>, - source_data: Option<&'raw_data [u8]>, + source_data: &'raw_data [u8], set_ccsds_len: bool, ) -> Self { sp_header.set_packet_type(PacketType::Tm); sp_header.set_sec_header_flag(); let mut pus_tm = Self { sp_header: *sp_header, - source_data: source_data.unwrap_or(&[]), + source_data, sec_header, calc_crc_on_serialization: true, }; @@ -586,7 +586,7 @@ impl<'raw_data> PusTmCreator<'raw_data> { subservice: u8, time_provider: &impl TimeWriter, stamp_buf: &'raw_data mut [u8], - source_data: Option<&'raw_data [u8]>, + source_data: &'raw_data [u8], set_ccsds_len: bool, ) -> Result { let stamp_size = time_provider.write_to_bytes(stamp_buf)?; @@ -594,6 +594,25 @@ impl<'raw_data> PusTmCreator<'raw_data> { PusTmSecondaryHeader::new_simple(service, subservice, &stamp_buf[0..stamp_size]); Ok(Self::new(sp_header, sec_header, source_data, set_ccsds_len)) } + + pub fn new_no_source_data( + sp_header: &mut SpHeader, + sec_header: PusTmSecondaryHeader<'raw_data>, + set_ccsds_len: bool, + ) -> Self { + sp_header.set_packet_type(PacketType::Tm); + sp_header.set_sec_header_flag(); + let mut pus_tm = Self { + sp_header: *sp_header, + source_data: &[], + sec_header, + calc_crc_on_serialization: true, + }; + if set_ccsds_len { + pus_tm.update_ccsds_data_len(); + } + pus_tm + } pub fn timestamp(&self) -> &[u8] { self.sec_header.timestamp } @@ -973,20 +992,23 @@ impl<'raw> PusTmZeroCopyWriter<'raw> { #[cfg(test)] mod tests { + use alloc::string::ToString; + use super::*; use crate::ecss::PusVersion::PusC; + use crate::time::cds::TimeProvider; use crate::SpHeader; fn base_ping_reply_full_ctor(timestamp: &[u8]) -> PusTmCreator { let mut sph = SpHeader::tm_unseg(0x123, 0x234, 0).unwrap(); let tm_header = PusTmSecondaryHeader::new_simple(17, 2, timestamp); - PusTmCreator::new(&mut sph, tm_header, None, true) + PusTmCreator::new_no_source_data(&mut sph, tm_header, true) } fn base_hk_reply<'a>(timestamp: &'a [u8], src_data: &'a [u8]) -> PusTmCreator<'a> { let mut sph = SpHeader::tm_unseg(0x123, 0x234, 0).unwrap(); let tc_header = PusTmSecondaryHeader::new_simple(3, 5, timestamp); - PusTmCreator::new(&mut sph, tc_header, Some(src_data), true) + PusTmCreator::new(&mut sph, tc_header, src_data, true) } fn dummy_timestamp() -> &'static [u8] { @@ -999,6 +1021,16 @@ mod tests { let pus_tm = base_ping_reply_full_ctor(timestamp); verify_ping_reply(&pus_tm, false, 22, dummy_timestamp()); } + #[test] + fn test_basic_simple_api() { + let mut sph = SpHeader::tm_unseg(0x123, 0x234, 0).unwrap(); + let time_provider = TimeProvider::new_with_u16_days(0, 0); + let mut stamp_buf: [u8; 8] = [0; 8]; + let pus_tm = + PusTmCreator::new_simple(&mut sph, 17, 2, &time_provider, &mut stamp_buf, &[], true) + .unwrap(); + verify_ping_reply(&pus_tm, false, 22, &[64, 0, 0, 0, 0, 0, 0]); + } #[test] fn test_serialization_no_source_data() { @@ -1009,7 +1041,7 @@ mod tests { .write_to_bytes(&mut buf) .expect("Serialization failed"); assert_eq!(ser_len, 22); - verify_raw_ping_reply(&buf); + verify_raw_ping_reply(pus_tm.crc16().unwrap(), &buf); } #[test] @@ -1062,14 +1094,39 @@ mod tests { assert_eq!(ser_len, 22); let (tm_deserialized, size) = PusTmReader::new(&buf, 7).expect("Deserialization failed"); assert_eq!(ser_len, size); + assert_eq!(tm_deserialized.user_data(), tm_deserialized.source_data()); + assert_eq!(tm_deserialized.raw_data(), &buf[..ser_len]); + assert_eq!(tm_deserialized.crc16().unwrap(), pus_tm.crc16().unwrap()); verify_ping_reply_with_reader(&tm_deserialized, false, 22, dummy_timestamp()); } + #[test] + fn test_deserialization_faulty_crc() { + let timestamp = dummy_timestamp(); + let pus_tm = base_ping_reply_full_ctor(timestamp); + let mut buf: [u8; 32] = [0; 32]; + let ser_len = pus_tm + .write_to_bytes(&mut buf) + .expect("Serialization failed"); + assert_eq!(ser_len, 22); + buf[ser_len - 2] = 0; + buf[ser_len - 1] = 0; + let tm_error = PusTmReader::new(&buf, 7); + assert!(tm_error.is_err()); + let tm_error = tm_error.unwrap_err(); + if let PusError::ChecksumFailure(crc) = tm_error { + assert_eq!(crc, 0); + assert_eq!( + tm_error.to_string(), + "checksum verification for crc16 0x0000 failed" + ); + } + } #[test] fn test_manual_field_update() { let mut sph = SpHeader::tm_unseg(0x123, 0x234, 0).unwrap(); let tc_header = PusTmSecondaryHeader::new_simple(17, 2, dummy_timestamp()); - let mut tm = PusTmCreator::new(&mut sph, tc_header, None, false); + let mut tm = PusTmCreator::new_no_source_data(&mut sph, tc_header, false); tm.calc_crc_on_serialization = false; assert_eq!(tm.data_len(), 0x00); let mut buf: [u8; 32] = [0; 32]; @@ -1115,7 +1172,7 @@ mod tests { let res = pus_tm.append_to_vec(&mut vec); assert!(res.is_ok()); assert_eq!(res.unwrap(), 22); - verify_raw_ping_reply(vec.as_slice()); + verify_raw_ping_reply(pus_tm.crc16().unwrap(), vec.as_slice()); } #[test] @@ -1131,7 +1188,7 @@ mod tests { assert_eq!(vec.len(), 26); } - fn verify_raw_ping_reply(buf: &[u8]) { + fn verify_raw_ping_reply(crc16: u16, buf: &[u8]) { // Secondary header is set -> 0b0000_1001 , APID occupies last bit of first byte assert_eq!(buf[0], 0x09); // Rest of APID 0x123 @@ -1154,9 +1211,10 @@ mod tests { assert_eq!(&buf[13..20], dummy_timestamp()); let mut digest = CRC_CCITT_FALSE.digest(); digest.update(&buf[0..20]); - let crc16 = digest.finalize(); + let crc16_calced = digest.finalize(); assert_eq!(((crc16 >> 8) & 0xff) as u8, buf[20]); assert_eq!((crc16 & 0xff) as u8, buf[21]); + assert_eq!(crc16, crc16_calced); } fn verify_ping_reply( @@ -1167,6 +1225,7 @@ mod tests { ) { assert_eq!(tm.len_written(), exp_full_len); assert_eq!(tm.timestamp(), exp_timestamp); + assert_eq!(tm.source_data(), tm.user_data()); verify_ping_reply_generic(tm, has_user_data, exp_full_len); } @@ -1188,7 +1247,9 @@ mod tests { ) { assert!(tm.is_tm()); assert_eq!(PusPacket::service(tm), 17); + assert_eq!(GenericPusTmSecondaryHeader::service(tm), 17); assert_eq!(PusPacket::subservice(tm), 2); + assert_eq!(GenericPusTmSecondaryHeader::subservice(tm), 2); assert!(tm.sec_header_flag()); if has_user_data { assert!(!tm.user_data().is_empty()); @@ -1196,6 +1257,11 @@ mod tests { assert_eq!(PusPacket::pus_version(tm), PusC); assert_eq!(tm.apid(), 0x123); assert_eq!(tm.seq_count(), 0x234); + assert_eq!(PusPacket::pus_version(tm), PusVersion::PusC); + assert_eq!( + GenericPusTmSecondaryHeader::pus_version(tm), + PusVersion::PusC + ); assert_eq!(tm.data_len(), exp_full_len as u16 - 7); assert_eq!(tm.dest_id(), 0x0000); assert_eq!(tm.msg_counter(), 0x0000); @@ -1232,6 +1298,10 @@ mod tests { writer.set_msg_count(100); writer.set_seq_count(MAX_SEQ_COUNT); writer.set_apid(MAX_APID); + assert!(!writer.set_apid(MAX_APID + 1)); + assert!(!writer.set_apid(MAX_SEQ_COUNT + 1)); + assert_eq!(writer.service(), 17); + assert_eq!(writer.subservice(), 2); writer.finish(); // This performs all necessary checks, including the CRC check. let (tm_read_back, tm_size_read_back) = @@ -1242,4 +1312,64 @@ mod tests { assert_eq!(tm_read_back.seq_count(), MAX_SEQ_COUNT); assert_eq!(tm_read_back.apid(), MAX_APID); } + + #[test] + fn test_sec_header_without_stamp() { + let sec_header = PusTmSecondaryHeader::new_simple_no_timestamp(17, 1); + assert_eq!(sec_header.timestamp, &[]); + } + + #[test] + fn test_reader_partial_eq() { + let timestamp = dummy_timestamp(); + let pus_tm = base_ping_reply_full_ctor(timestamp); + let mut buf = [0; 32]; + pus_tm.write_to_bytes(&mut buf).unwrap(); + let (tm_0, _) = PusTmReader::new(&buf, timestamp.len()).unwrap(); + let (tm_1, _) = PusTmReader::new(&buf, timestamp.len()).unwrap(); + assert_eq!(tm_0, tm_1); + } + #[test] + fn test_reader_buf_too_small_2() { + let timestamp = dummy_timestamp(); + let pus_tm = base_ping_reply_full_ctor(timestamp); + let mut buf = [0; 32]; + let written = pus_tm.write_to_bytes(&mut buf).unwrap(); + let tm_error = PusTmReader::new( + &buf[0..PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA + 1], + timestamp.len(), + ); + assert!(tm_error.is_err()); + let tm_error = tm_error.unwrap_err(); + if let PusError::ByteConversion(ByteConversionError::FromSliceTooSmall { + found, + expected, + }) = tm_error + { + assert_eq!(found, PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA + 1); + assert_eq!(expected, written); + } else { + panic!("unexpected error {tm_error}") + } + } + #[test] + fn test_reader_buf_too_small() { + let timestamp = dummy_timestamp(); + let pus_tm = base_ping_reply_full_ctor(timestamp); + let mut buf = [0; 32]; + pus_tm.write_to_bytes(&mut buf).unwrap(); + let tm_error = PusTmReader::new(&buf[0..5], timestamp.len()); + assert!(tm_error.is_err()); + let tm_error = tm_error.unwrap_err(); + if let PusError::ByteConversion(ByteConversionError::FromSliceTooSmall { + found, + expected, + }) = tm_error + { + assert_eq!(found, 5); + assert_eq!(expected, PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA); + } else { + panic!("unexpected error {tm_error}") + } + } }