spacepackets/src/cfdp/pdu/mod.rs

657 lines
25 KiB
Rust
Raw Normal View History

2023-05-14 20:10:34 +02:00
use crate::cfdp::*;
use crate::util::{UnsignedByteField, UnsignedEnum};
use crate::{ByteConversionError, SizeMissmatch};
use core::fmt::{Display, Formatter};
#[cfg(feature = "std")]
use std::error::Error;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum PduError {
ByteConversionError(ByteConversionError),
/// Found version ID invalid, not equal to [CFDP_VERSION_2].
CfdpVersionMissmatch(u8),
/// Invalid length for the entity ID detected. Only the values 1, 2, 4 and 8 are supported.
InvalidEntityLen(u8),
/// Invalid length for the entity ID detected. Only the values 1, 2, 4 and 8 are supported.
InvalidTransactionSeqNumLen(u8),
/// The first entry will be the source entity ID length, the second one the destination entity
/// ID length.
SourceDestIdLenMissmatch((usize, usize)),
}
impl Display for PduError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
PduError::InvalidEntityLen(raw_id) => {
write!(
f,
2023-05-18 13:32:45 +02:00
"Invalid PDU entity ID length {raw_id}, only [1, 2, 4, 8] are allowed"
2023-05-14 20:10:34 +02:00
)
}
PduError::InvalidTransactionSeqNumLen(raw_id) => {
write!(
f,
2023-05-18 13:32:45 +02:00
"invalid PDUtransaction seq num length {raw_id}, only [1, 2, 4, 8] are allowed"
2023-05-14 20:10:34 +02:00
)
}
PduError::CfdpVersionMissmatch(raw) => {
write!(
f,
"cfdp version missmatch, found {raw}, expected {CFDP_VERSION_2}"
)
}
PduError::SourceDestIdLenMissmatch((src_len, dest_len)) => {
write!(
f,
2023-05-18 13:32:45 +02:00
"missmatch of PDU source length {src_len} and destination length {dest_len}"
2023-05-14 20:10:34 +02:00
)
}
PduError::ByteConversionError(e) => {
write!(f, "low level byte conversion error: {e}")
}
}
}
}
#[cfg(feature = "std")]
impl Error for PduError {}
impl From<ByteConversionError> for PduError {
fn from(value: ByteConversionError) -> Self {
Self::ByteConversionError(value)
}
}
/// Common configuration fields for a PDU.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct CommonPduConfig {
source_entity_id: UnsignedByteField,
dest_entity_id: UnsignedByteField,
2023-05-15 01:01:46 +02:00
pub transaction_seq_num: UnsignedByteField,
pub trans_mode: TransmissionMode,
pub file_flag: LargeFileFlag,
pub crc_flag: CrcFlag,
pub direction: Direction,
2023-05-14 20:10:34 +02:00
}
2023-05-18 11:08:46 +02:00
// TODO: Builder pattern might be applicable here..
2023-05-14 20:10:34 +02:00
impl CommonPduConfig {
pub fn new(
2023-05-15 01:01:46 +02:00
source_id: impl Into<UnsignedByteField>,
dest_id: impl Into<UnsignedByteField>,
transaction_seq_num: impl Into<UnsignedByteField>,
2023-05-14 20:10:34 +02:00
trans_mode: TransmissionMode,
file_flag: LargeFileFlag,
crc_flag: CrcFlag,
direction: Direction,
) -> Result<Self, PduError> {
2023-05-15 01:01:46 +02:00
let source_id = source_id.into();
let dest_id = dest_id.into();
let transaction_seq_num = transaction_seq_num.into();
2023-05-14 20:10:34 +02:00
if source_id.len() != dest_id.len() {
return Err(PduError::SourceDestIdLenMissmatch((
source_id.len(),
dest_id.len(),
)));
}
2023-05-15 01:01:46 +02:00
if source_id.len() != 1
&& source_id.len() != 2
&& source_id.len() != 4
&& source_id.len() != 8
{
return Err(PduError::InvalidEntityLen(source_id.len() as u8));
}
if transaction_seq_num.len() != 1
&& transaction_seq_num.len() != 2
&& transaction_seq_num.len() != 4
&& transaction_seq_num.len() != 8
{
return Err(PduError::InvalidTransactionSeqNumLen(
transaction_seq_num.len() as u8,
));
}
2023-05-14 20:10:34 +02:00
Ok(Self {
source_entity_id: source_id,
dest_entity_id: dest_id,
transaction_seq_num,
trans_mode,
file_flag,
crc_flag,
direction,
})
}
2023-05-15 01:01:46 +02:00
pub fn new_with_defaults(
source_id: impl Into<UnsignedByteField>,
dest_id: impl Into<UnsignedByteField>,
transaction_seq_num: impl Into<UnsignedByteField>,
) -> Result<Self, PduError> {
Self::new(
source_id,
dest_id,
transaction_seq_num,
TransmissionMode::Acknowledged,
LargeFileFlag::Normal,
CrcFlag::NoCrc,
Direction::TowardsReceiver,
)
}
pub fn source_id(&self) -> UnsignedByteField {
self.source_entity_id
}
pub fn dest_id(&self) -> UnsignedByteField {
self.dest_entity_id
}
2023-05-14 20:10:34 +02:00
}
2023-05-15 01:01:46 +02:00
2023-05-18 11:08:46 +02:00
const FIXED_HEADER_LEN: usize = 4;
2023-05-15 01:01:46 +02:00
2023-05-14 20:10:34 +02:00
/// Abstraction for the PDU header common to all CFDP PDUs
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PduHeader {
pdu_type: PduType,
pdu_conf: CommonPduConfig,
seg_metadata_flag: SegmentMetadataFlag,
seg_ctrl: SegmentationControl,
pdu_datafield_len: u16,
}
impl PduHeader {
pub fn new_for_file_data(
pdu_conf: CommonPduConfig,
2023-05-18 00:46:58 +02:00
pdu_datafield_len: u16,
2023-05-14 20:10:34 +02:00
seg_metadata_flag: SegmentMetadataFlag,
seg_ctrl: SegmentationControl,
) -> Self {
PduHeader {
pdu_type: PduType::FileData,
pdu_conf,
seg_metadata_flag,
seg_ctrl,
pdu_datafield_len,
}
}
pub fn new_no_file_data(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self {
PduHeader {
2023-05-15 01:01:46 +02:00
pdu_type: PduType::FileDirective,
2023-05-14 20:10:34 +02:00
pdu_conf,
seg_metadata_flag: SegmentMetadataFlag::NotPresent,
seg_ctrl: SegmentationControl::NoRecordBoundaryPreservation,
pdu_datafield_len,
}
}
2023-05-18 00:46:58 +02:00
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
2023-05-18 13:32:45 +02:00
// Internal note: There is currently no way to pass a PDU configuration like this, but
// this check is still kept for defensive programming.
2023-05-14 20:10:34 +02:00
if self.pdu_conf.source_entity_id.len() != self.pdu_conf.dest_entity_id.len() {
return Err(PduError::SourceDestIdLenMissmatch((
self.pdu_conf.source_entity_id.len(),
self.pdu_conf.dest_entity_id.len(),
)));
}
if buf.len()
2023-05-18 11:08:46 +02:00
< FIXED_HEADER_LEN
2023-05-15 01:01:46 +02:00
+ self.pdu_conf.source_entity_id.len()
+ self.pdu_conf.transaction_seq_num.len()
2023-05-14 20:10:34 +02:00
{
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
found: buf.len(),
2023-05-18 11:08:46 +02:00
expected: FIXED_HEADER_LEN,
2023-05-14 20:10:34 +02:00
})
.into());
}
let mut current_idx = 0;
buf[current_idx] = (CFDP_VERSION_2 << 5)
| ((self.pdu_type as u8) << 4)
| ((self.pdu_conf.direction as u8) << 3)
| ((self.pdu_conf.trans_mode as u8) << 2)
| ((self.pdu_conf.crc_flag as u8) << 1)
| (self.pdu_conf.file_flag as u8);
current_idx += 1;
buf[current_idx..current_idx + 2].copy_from_slice(&self.pdu_datafield_len.to_be_bytes());
current_idx += 2;
buf[current_idx] = ((self.seg_ctrl as u8) << 7)
2023-05-15 01:01:46 +02:00
| (((self.pdu_conf.source_entity_id.len() - 1) as u8) << 4)
2023-05-14 20:10:34 +02:00
| ((self.seg_metadata_flag as u8) << 3)
2023-05-15 01:01:46 +02:00
| ((self.pdu_conf.transaction_seq_num.len() - 1) as u8);
current_idx += 1;
2023-05-14 20:10:34 +02:00
self.pdu_conf.source_entity_id.write_to_be_bytes(
&mut buf[current_idx..current_idx + self.pdu_conf.source_entity_id.len()],
)?;
current_idx += self.pdu_conf.source_entity_id.len();
self.pdu_conf.transaction_seq_num.write_to_be_bytes(
&mut buf[current_idx..current_idx + self.pdu_conf.transaction_seq_num.len()],
)?;
current_idx += self.pdu_conf.transaction_seq_num.len();
self.pdu_conf.dest_entity_id.write_to_be_bytes(
&mut buf[current_idx..current_idx + self.pdu_conf.dest_entity_id.len()],
)?;
2023-05-18 00:46:58 +02:00
current_idx += self.pdu_conf.dest_entity_id.len();
Ok(current_idx)
2023-05-14 20:10:34 +02:00
}
2023-05-18 11:08:46 +02:00
pub fn from_be_bytes(buf: &[u8]) -> Result<(Self, usize), PduError> {
if buf.len() < FIXED_HEADER_LEN {
2023-05-14 20:10:34 +02:00
return Err(PduError::ByteConversionError(
ByteConversionError::FromSliceTooSmall(SizeMissmatch {
found: buf.len(),
2023-05-18 11:08:46 +02:00
expected: FIXED_HEADER_LEN,
2023-05-14 20:10:34 +02:00
}),
));
}
2023-05-15 01:01:46 +02:00
let cfdp_version_raw = (buf[0] >> 5) & 0b111;
2023-05-14 20:10:34 +02:00
if cfdp_version_raw != CFDP_VERSION_2 {
return Err(PduError::CfdpVersionMissmatch(cfdp_version_raw));
}
2023-05-15 01:01:46 +02:00
// unwrap for single bit fields: This operation will always succeed.
2023-05-14 20:10:34 +02:00
let pdu_type = PduType::try_from((buf[0] >> 4) & 0b1).unwrap();
let direction = Direction::try_from((buf[0] >> 3) & 0b1).unwrap();
let trans_mode = TransmissionMode::try_from((buf[0] >> 2) & 0b1).unwrap();
let crc_flag = CrcFlag::try_from((buf[0] >> 1) & 0b1).unwrap();
let file_flag = LargeFileFlag::try_from(buf[0] & 0b1).unwrap();
let pdu_datafield_len = u16::from_be_bytes(buf[1..3].try_into().unwrap());
let seg_ctrl = SegmentationControl::try_from((buf[3] >> 7) & 0b1).unwrap();
2023-05-15 01:03:15 +02:00
let expected_len_entity_ids = (((buf[3] >> 4) & 0b111) + 1) as usize;
2023-05-14 20:10:34 +02:00
if (expected_len_entity_ids != 1)
&& (expected_len_entity_ids != 2)
&& (expected_len_entity_ids != 4)
&& (expected_len_entity_ids != 8)
{
return Err(PduError::InvalidEntityLen(expected_len_entity_ids as u8));
}
let seg_metadata_flag = SegmentMetadataFlag::try_from((buf[3] >> 3) & 0b1).unwrap();
2023-05-15 01:03:15 +02:00
let expected_len_seq_num = ((buf[3] & 0b111) + 1) as usize;
2023-05-14 20:10:34 +02:00
if (expected_len_seq_num != 1)
&& (expected_len_seq_num != 2)
&& (expected_len_seq_num != 4)
&& (expected_len_seq_num != 8)
{
return Err(PduError::InvalidTransactionSeqNumLen(
expected_len_seq_num as u8,
));
}
if buf.len() < (4 + 2 * expected_len_entity_ids + expected_len_seq_num) {
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
found: buf.len(),
expected: 4 + 2 * expected_len_entity_ids + expected_len_seq_num,
})
.into());
}
let mut current_idx = 4;
2023-05-15 01:01:46 +02:00
// It is okay to unwrap here because we checked the validity of the expected length and of
// the remaining buffer length.
2023-05-14 20:10:34 +02:00
let source_id =
UnsignedByteField::new_from_be_bytes(expected_len_entity_ids, &buf[current_idx..])
.unwrap();
current_idx += expected_len_entity_ids;
let transaction_seq_num =
UnsignedByteField::new_from_be_bytes(expected_len_seq_num, &buf[current_idx..])
.unwrap();
current_idx += expected_len_seq_num;
let dest_id =
UnsignedByteField::new_from_be_bytes(expected_len_entity_ids, &buf[current_idx..])
.unwrap();
2023-05-18 11:08:46 +02:00
current_idx += expected_len_entity_ids;
2023-05-14 20:10:34 +02:00
let common_pdu_conf = CommonPduConfig::new(
source_id,
dest_id,
transaction_seq_num,
trans_mode,
file_flag,
crc_flag,
direction,
)
.unwrap();
2023-05-18 11:08:46 +02:00
Ok((
PduHeader {
pdu_type,
pdu_conf: common_pdu_conf,
seg_metadata_flag,
seg_ctrl,
pdu_datafield_len,
},
current_idx,
))
2023-05-14 20:10:34 +02:00
}
pub fn pdu_type(&self) -> PduType {
self.pdu_type
}
pub fn common_pdu_conf(&self) -> &CommonPduConfig {
&self.pdu_conf
}
2023-05-15 01:01:46 +02:00
pub fn seg_metadata_flag(&self) -> SegmentMetadataFlag {
self.seg_metadata_flag
}
pub fn seg_ctrl(&self) -> SegmentationControl {
self.seg_ctrl
}
}
#[cfg(test)]
mod tests {
2023-05-18 11:08:46 +02:00
use crate::cfdp::pdu::{CommonPduConfig, PduError, PduHeader, FIXED_HEADER_LEN};
2023-05-15 01:01:46 +02:00
use crate::cfdp::{
2023-05-18 00:46:58 +02:00
CrcFlag, Direction, LargeFileFlag, PduType, SegmentMetadataFlag, SegmentationControl,
TransmissionMode, CFDP_VERSION_2,
2023-05-15 01:01:46 +02:00
};
2023-05-18 13:32:45 +02:00
use crate::util::{UbfU8, UnsignedByteField, UnsignedByteFieldU16, UnsignedByteFieldU8};
2023-05-18 11:08:46 +02:00
use crate::ByteConversionError;
2023-05-18 00:46:58 +02:00
use std::format;
2023-05-15 01:01:46 +02:00
#[test]
fn test_basic_state() {
2023-05-18 13:32:45 +02:00
let src_id = UnsignedByteFieldU8::new(1);
let dest_id = UnsignedByteFieldU8::new(2);
let transaction_id = UnsignedByteFieldU8::new(3);
2023-05-15 01:01:46 +02:00
let common_pdu_cfg = CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_id)
.expect("common config creation failed");
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
assert_eq!(pdu_header.pdu_type(), PduType::FileDirective);
let common_conf_ref = pdu_header.common_pdu_conf();
assert_eq!(*common_conf_ref, common_pdu_cfg);
// These should be 0 and ignored for non-filedata PDUs
assert_eq!(
pdu_header.seg_metadata_flag(),
SegmentMetadataFlag::NotPresent
);
assert_eq!(
pdu_header.seg_ctrl(),
SegmentationControl::NoRecordBoundaryPreservation
);
2023-05-18 00:46:58 +02:00
assert_eq!(pdu_header.pdu_datafield_len, 5);
}
#[test]
fn test_serialization_1() {
2023-05-18 13:32:45 +02:00
let src_id = UnsignedByteFieldU8::new(1);
let dest_id = UnsignedByteFieldU8::new(2);
let transaction_id = UnsignedByteFieldU8::new(3);
2023-05-15 01:01:46 +02:00
let common_pdu_cfg = CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_id)
.expect("common config creation failed");
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
let mut buf: [u8; 7] = [0; 7];
let res = pdu_header.write_to_be_bytes(&mut buf);
assert!(res.is_ok());
2023-05-18 00:46:58 +02:00
// 4 byte fixed header plus three bytes src, dest ID and transaction ID
assert_eq!(res.unwrap(), 7);
2023-05-15 01:01:46 +02:00
assert_eq!((buf[0] >> 5) & 0b111, CFDP_VERSION_2);
// File directive
assert_eq!((buf[0] >> 4) & 1, 0);
// Towards receiver
assert_eq!((buf[0] >> 3) & 1, 0);
// Acknowledged
assert_eq!((buf[0] >> 2) & 1, 0);
// No CRC
assert_eq!((buf[0] >> 1) & 1, 0);
// Regular file size
assert_eq!(buf[0] & 1, 0);
let pdu_datafield_len = u16::from_be_bytes(buf[1..3].try_into().unwrap());
assert_eq!(pdu_datafield_len, 5);
// No record boundary preservation
assert_eq!((buf[3] >> 7) & 1, 0);
// Entity ID length raw value is actual number of octets - 1 => 0
assert_eq!((buf[3] >> 4) & 0b111, 0);
// No segment metadata
assert_eq!((buf[3] >> 3) & 0b1, 0);
// Transaction Sequence ID length raw value is actual number of octets - 1 => 0
assert_eq!(buf[3] & 0b111, 0);
2023-05-18 00:46:58 +02:00
assert_eq!(buf[4], 1);
assert_eq!(buf[5], 3);
assert_eq!(buf[6], 2);
}
2023-05-18 11:08:46 +02:00
#[test]
fn test_deserialization_1() {
2023-05-18 13:32:45 +02:00
let src_id = UnsignedByteFieldU8::new(1);
let dest_id = UnsignedByteFieldU8::new(2);
let transaction_id = UnsignedByteFieldU8::new(3);
2023-05-18 11:08:46 +02:00
let common_pdu_cfg = CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_id)
.expect("common config creation failed");
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
let mut buf: [u8; 7] = [0; 7];
let res = pdu_header.write_to_be_bytes(&mut buf);
assert!(res.is_ok());
let deser_res = PduHeader::from_be_bytes(&buf);
assert!(deser_res.is_ok());
let (header_read_back, read_size) = deser_res.unwrap();
assert_eq!(read_size, 7);
assert_eq!(header_read_back, pdu_header);
}
2023-05-18 00:46:58 +02:00
#[test]
fn test_serialization_2() {
2023-05-18 13:32:45 +02:00
let src_id = UnsignedByteFieldU16::new(0x0001);
let dest_id = UnsignedByteFieldU16::new(0x0203);
let transaction_id = UnsignedByteFieldU16::new(0x0405);
2023-05-18 00:46:58 +02:00
let mut common_pdu_cfg =
CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_id)
.expect("common config creation failed");
common_pdu_cfg.crc_flag = CrcFlag::WithCrc;
common_pdu_cfg.direction = Direction::TowardsSender;
common_pdu_cfg.trans_mode = TransmissionMode::Unacknowledged;
common_pdu_cfg.file_flag = LargeFileFlag::Large;
let pdu_header = PduHeader::new_for_file_data(
common_pdu_cfg,
5,
SegmentMetadataFlag::Present,
SegmentationControl::WithRecordBoundaryPreservation,
);
let mut buf: [u8; 16] = [0; 16];
let res = pdu_header.write_to_be_bytes(&mut buf);
assert!(res.is_ok(), "{}", format!("Result {res:?} not okay"));
// 4 byte fixed header, 6 bytes additional fields
assert_eq!(res.unwrap(), 10);
assert_eq!((buf[0] >> 5) & 0b111, CFDP_VERSION_2);
// File directive
assert_eq!((buf[0] >> 4) & 1, 1);
// Towards sender
assert_eq!((buf[0] >> 3) & 1, 1);
// Unacknowledged
assert_eq!((buf[0] >> 2) & 1, 1);
// With CRC
assert_eq!((buf[0] >> 1) & 1, 1);
// Large file size
assert_eq!(buf[0] & 1, 1);
let pdu_datafield_len = u16::from_be_bytes(buf[1..3].try_into().unwrap());
assert_eq!(pdu_datafield_len, 5);
// With record boundary preservation
assert_eq!((buf[3] >> 7) & 1, 1);
// Entity ID length raw value is actual number of octets - 1 => 1
assert_eq!((buf[3] >> 4) & 0b111, 1);
// With segment metadata
assert_eq!((buf[3] >> 3) & 0b1, 1);
// Transaction Sequence ID length raw value is actual number of octets - 1 => 1
assert_eq!(buf[3] & 0b111, 1);
assert_eq!(u16::from_be_bytes(buf[4..6].try_into().unwrap()), 0x0001);
assert_eq!(u16::from_be_bytes(buf[6..8].try_into().unwrap()), 0x0405);
assert_eq!(u16::from_be_bytes(buf[8..10].try_into().unwrap()), 0x0203);
2023-05-15 01:01:46 +02:00
}
2023-05-18 00:46:58 +02:00
#[test]
2023-05-18 11:08:46 +02:00
fn test_deserialization_2() {
2023-05-18 13:32:45 +02:00
let src_id = UnsignedByteFieldU16::new(0x0001);
let dest_id = UnsignedByteFieldU16::new(0x0203);
let transaction_id = UnsignedByteFieldU16::new(0x0405);
2023-05-18 11:08:46 +02:00
let mut common_pdu_cfg =
CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_id)
.expect("common config creation failed");
common_pdu_cfg.crc_flag = CrcFlag::WithCrc;
common_pdu_cfg.direction = Direction::TowardsSender;
common_pdu_cfg.trans_mode = TransmissionMode::Unacknowledged;
common_pdu_cfg.file_flag = LargeFileFlag::Large;
let pdu_header = PduHeader::new_for_file_data(
common_pdu_cfg,
5,
SegmentMetadataFlag::Present,
SegmentationControl::WithRecordBoundaryPreservation,
);
let mut buf: [u8; 16] = [0; 16];
let res = pdu_header.write_to_be_bytes(&mut buf);
assert!(res.is_ok());
let deser_res = PduHeader::from_be_bytes(&buf);
assert!(deser_res.is_ok());
let (header_read_back, read_size) = deser_res.unwrap();
assert_eq!(read_size, 10);
assert_eq!(header_read_back, pdu_header);
}
#[test]
fn test_invalid_raw_version() {
2023-05-18 13:32:45 +02:00
let src_id = UnsignedByteFieldU8::new(1);
let dest_id = UnsignedByteFieldU8::new(2);
let transaction_id = UnsignedByteFieldU8::new(3);
2023-05-18 11:08:46 +02:00
let common_pdu_cfg = CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_id)
.expect("common config creation failed");
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
let mut buf: [u8; 7] = [0; 7];
let res = pdu_header.write_to_be_bytes(&mut buf);
assert!(res.is_ok());
buf[0] &= !0b1110_0000;
buf[0] |= (CFDP_VERSION_2 + 1) << 5;
let res = PduHeader::from_be_bytes(&buf);
assert!(res.is_err());
let error = res.unwrap_err();
if let PduError::CfdpVersionMissmatch(raw_version) = error {
assert_eq!(raw_version, CFDP_VERSION_2 + 1);
} else {
panic!("invalid exception: {}", error);
}
}
#[test]
fn test_buf_too_small_1() {
let buf: [u8; 3] = [0; 3];
let res = PduHeader::from_be_bytes(&buf);
assert!(res.is_err());
let error = res.unwrap_err();
if let PduError::ByteConversionError(ByteConversionError::FromSliceTooSmall(missmatch)) =
error
{
assert_eq!(missmatch.found, 3);
assert_eq!(missmatch.expected, FIXED_HEADER_LEN);
} else {
panic!("invalid exception: {}", error);
}
}
#[test]
fn test_buf_too_small_2() {
2023-05-18 13:32:45 +02:00
let src_id = UnsignedByteFieldU8::new(1);
let dest_id = UnsignedByteFieldU8::new(2);
let transaction_id = UnsignedByteFieldU8::new(3);
2023-05-18 11:08:46 +02:00
let common_pdu_cfg = CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_id)
.expect("common config creation failed");
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
let mut buf: [u8; 7] = [0; 7];
let res = pdu_header.write_to_be_bytes(&mut buf);
assert!(res.is_ok());
let header = PduHeader::from_be_bytes(&buf[0..6]);
assert!(header.is_err());
let error = header.unwrap_err();
if let PduError::ByteConversionError(ByteConversionError::FromSliceTooSmall(missmatch)) =
error
{
assert_eq!(missmatch.found, 6);
assert_eq!(missmatch.expected, 7);
}
}
2023-05-18 13:32:45 +02:00
#[test]
fn test_invalid_seq_len() {
let src_id = UbfU8::new(1);
let dest_id = UbfU8::new(2);
let transaction_seq_id = UbfU8::new(3);
let invalid_byte_field = UnsignedByteField::new(3, 5);
let pdu_conf_res = CommonPduConfig::new_with_defaults(src_id, dest_id, invalid_byte_field);
assert!(pdu_conf_res.is_err());
let error = pdu_conf_res.unwrap_err();
if let PduError::InvalidTransactionSeqNumLen(len) = error {
assert_eq!(len, 3);
} else {
panic!("Invalid exception: {}", error)
}
let pdu_conf_res = CommonPduConfig::new_with_defaults(
invalid_byte_field,
invalid_byte_field,
transaction_seq_id,
);
assert!(pdu_conf_res.is_err());
let error = pdu_conf_res.unwrap_err();
if let PduError::InvalidEntityLen(len) = error {
assert_eq!(len, 3);
} else {
panic!("Invalid exception: {}", error)
}
}
#[test]
fn test_missmatch_src_dest_id() {
let src_id = UnsignedByteField::new(1, 5);
let dest_id = UnsignedByteField::new(2, 5);
let transaction_seq_id = UbfU8::new(3);
let pdu_conf_res = CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_seq_id);
assert!(pdu_conf_res.is_err());
let error = pdu_conf_res.unwrap_err();
if let PduError::SourceDestIdLenMissmatch((src_len, dest_len)) = error {
assert_eq!(src_len, 1);
assert_eq!(dest_len, 2);
}
}
#[test]
fn test_invalid_raw_src_id_len() {
let src_id = UnsignedByteFieldU8::new(1);
let dest_id = UnsignedByteFieldU8::new(2);
let transaction_id = UnsignedByteFieldU8::new(3);
let common_pdu_cfg = CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_id)
.expect("common config creation failed");
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
let mut buf: [u8; 7] = [0; 7];
let res = pdu_header.write_to_be_bytes(&mut buf);
assert!(res.is_ok());
buf[3] &= !0b0111_0000;
// Equivalent to the length of three
buf[3] |= 0b10 << 4;
let header_res = PduHeader::from_be_bytes(&buf);
assert!(header_res.is_err());
let error = header_res.unwrap_err();
if let PduError::InvalidEntityLen(len) = error {
assert_eq!(len, 3);
} else {
panic!("invalid exception {:?}", error)
}
}
#[test]
fn test_invalid_transaction_seq_id_len() {
let src_id = UnsignedByteFieldU8::new(1);
let dest_id = UnsignedByteFieldU8::new(2);
let transaction_id = UnsignedByteFieldU8::new(3);
let common_pdu_cfg = CommonPduConfig::new_with_defaults(src_id, dest_id, transaction_id)
.expect("common config creation failed");
let pdu_header = PduHeader::new_no_file_data(common_pdu_cfg, 5);
let mut buf: [u8; 7] = [0; 7];
let res = pdu_header.write_to_be_bytes(&mut buf);
assert!(res.is_ok());
buf[3] &= !0b0000_0111;
// Equivalent to the length of three
buf[3] |= 0b10;
let header_res = PduHeader::from_be_bytes(&buf);
assert!(header_res.is_err());
let error = header_res.unwrap_err();
if let PduError::InvalidTransactionSeqNumLen(len) = error {
assert_eq!(len, 3);
} else {
panic!("invalid exception {:?}", error)
}
}
2023-05-14 20:10:34 +02:00
}