CFDP extracted to library #201

Closed
muellerr wants to merge 18 commits from continue-cfsp-source-handler into main
3 changed files with 381 additions and 177 deletions
Showing only changes of commit ed4b3ee858 - Show all commits

View File

@ -776,6 +776,7 @@ impl<
fn notice_of_suspension(&mut self) {
// TODO: Implement suspension handling.
}
fn abandon_transaction(&mut self) {
self.reset();
}
@ -815,6 +816,10 @@ impl<
Ok(1)
}
pub fn local_cfg(&self) -> &LocalEntityConfig<UserFaultHook> {
&self.local_cfg
}
fn tstate(&self) -> &TransferState<CheckTimerProvider> {
&self.tparams.tstate
}
@ -831,7 +836,7 @@ mod tests {
#[allow(unused_imports)]
use std::println;
use alloc::{collections::VecDeque, string::String, sync::Arc, vec::Vec};
use alloc::{sync::Arc, vec::Vec};
use rand::Rng;
use spacepackets::{
cfdp::{
@ -844,148 +849,18 @@ mod tests {
use crate::cfdp::{
filestore::NativeFilestore,
tests::{basic_remote_cfg_table, SentPdu, TestCfdpSender, TestFaultHandler},
user::OwnedMetadataRecvdParams,
tests::{
basic_remote_cfg_table, SentPdu, TestCfdpSender, TestCfdpUser, TestFaultHandler,
LOCAL_ID,
},
CheckTimerProviderCreator, CountdownProvider, FaultHandler, IndicationConfig,
StdRemoteEntityConfigProvider, CRC_32,
};
use super::*;
const LOCAL_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(1);
const REMOTE_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(2);
pub struct FileSegmentRecvdParamsNoSegMetadata {
pub id: TransactionId,
pub offset: u64,
pub length: usize,
}
#[derive(Default)]
struct TestCfdpUser {
next_expected_seq_num: u64,
expected_full_src_name: String,
expected_full_dest_name: String,
expected_file_size: u64,
transaction_indication_call_count: u32,
eof_recvd_call_count: u32,
finished_indic_queue: VecDeque<TransactionFinishedParams>,
metadata_recv_queue: VecDeque<OwnedMetadataRecvdParams>,
file_seg_recvd_queue: VecDeque<FileSegmentRecvdParamsNoSegMetadata>,
}
impl TestCfdpUser {
fn new(
next_expected_seq_num: u64,
expected_full_src_name: String,
expected_full_dest_name: String,
expected_file_size: u64,
) -> Self {
Self {
next_expected_seq_num,
expected_full_src_name,
expected_full_dest_name,
expected_file_size,
transaction_indication_call_count: 0,
eof_recvd_call_count: 0,
finished_indic_queue: VecDeque::new(),
metadata_recv_queue: VecDeque::new(),
file_seg_recvd_queue: VecDeque::new(),
}
}
fn generic_id_check(&self, id: &crate::cfdp::TransactionId) {
assert_eq!(id.source_id, LOCAL_ID.into());
assert_eq!(id.seq_num().value(), self.next_expected_seq_num);
}
}
impl CfdpUser for TestCfdpUser {
fn transaction_indication(&mut self, id: &crate::cfdp::TransactionId) {
self.generic_id_check(id);
self.transaction_indication_call_count += 1;
}
fn eof_sent_indication(&mut self, id: &crate::cfdp::TransactionId) {
self.generic_id_check(id);
}
fn transaction_finished_indication(
&mut self,
finished_params: &crate::cfdp::user::TransactionFinishedParams,
) {
self.generic_id_check(&finished_params.id);
self.finished_indic_queue.push_back(*finished_params);
}
fn metadata_recvd_indication(
&mut self,
md_recvd_params: &crate::cfdp::user::MetadataReceivedParams,
) {
self.generic_id_check(&md_recvd_params.id);
assert_eq!(
String::from(md_recvd_params.src_file_name),
self.expected_full_src_name
);
assert_eq!(
String::from(md_recvd_params.dest_file_name),
self.expected_full_dest_name
);
assert_eq!(md_recvd_params.msgs_to_user.len(), 0);
assert_eq!(md_recvd_params.source_id, LOCAL_ID.into());
assert_eq!(md_recvd_params.file_size, self.expected_file_size);
self.metadata_recv_queue.push_back(md_recvd_params.into());
}
fn file_segment_recvd_indication(
&mut self,
segment_recvd_params: &crate::cfdp::user::FileSegmentRecvdParams,
) {
self.generic_id_check(&segment_recvd_params.id);
self.file_seg_recvd_queue
.push_back(FileSegmentRecvdParamsNoSegMetadata {
id: segment_recvd_params.id,
offset: segment_recvd_params.offset,
length: segment_recvd_params.length,
})
}
fn report_indication(&mut self, _id: &crate::cfdp::TransactionId) {}
fn suspended_indication(
&mut self,
_id: &crate::cfdp::TransactionId,
_condition_code: ConditionCode,
) {
panic!("unexpected suspended indication");
}
fn resumed_indication(&mut self, _id: &crate::cfdp::TransactionId, _progresss: u64) {}
fn fault_indication(
&mut self,
_id: &crate::cfdp::TransactionId,
_condition_code: ConditionCode,
_progress: u64,
) {
panic!("unexpected fault indication");
}
fn abandoned_indication(
&mut self,
_id: &crate::cfdp::TransactionId,
_condition_code: ConditionCode,
_progress: u64,
) {
panic!("unexpected abandoned indication");
}
fn eof_recvd_indication(&mut self, id: &crate::cfdp::TransactionId) {
self.generic_id_check(id);
self.eof_recvd_call_count += 1;
}
}
#[derive(Debug)]
struct TestCheckTimer {
counter: Cell<u32>,
@ -1066,7 +941,7 @@ mod tests {
let test_sender = TestCfdpSender::default();
let dest_handler =
default_dest_handler(fault_handler, test_sender, check_timer_expired.clone());
let (src_path, dest_path) = init_full_filenames();
let (src_path, dest_path) = init_full_filepaths_textfile();
assert!(!Path::exists(&dest_path));
let handler = Self {
check_timer_expired,
@ -1205,7 +1080,7 @@ mod tests {
}
}
fn init_full_filenames() -> (PathBuf, PathBuf) {
fn init_full_filepaths_textfile() -> (PathBuf, PathBuf) {
(
tempfile::TempPath::from_path("/tmp/test.txt").to_path_buf(),
tempfile::NamedTempFile::new()
@ -1230,7 +1105,7 @@ mod tests {
2048,
test_packet_sender,
NativeFilestore::default(),
basic_remote_cfg_table(),
basic_remote_cfg_table(LOCAL_ID, true),
TestCheckTimerCreator::new(check_timer_expired),
)
}
@ -1297,6 +1172,9 @@ mod tests {
.user_hook
.borrow()
.all_queues_empty());
assert!(dest_handler.pdu_sender.queue_empty());
assert_eq!(dest_handler.state(), State::Idle);
assert_eq!(dest_handler.step(), TransactionStep::Idle);
}
#[test]

View File

@ -667,7 +667,7 @@ impl<'raw> PacketInfo<'raw> {
pub(crate) mod tests {
use core::cell::RefCell;
use alloc::{collections::VecDeque, vec::Vec};
use alloc::{collections::VecDeque, string::String, vec::Vec};
use spacepackets::{
cfdp::{
lv::Lv,
@ -679,16 +679,150 @@ pub(crate) mod tests {
},
ChecksumType, ConditionCode, PduType, TransmissionMode,
},
util::UnsignedByteFieldU16,
util::{UnsignedByteField, UnsignedByteFieldU16, UnsignedEnum},
};
use crate::cfdp::PacketTarget;
use super::{
user::{CfdpUser, OwnedMetadataRecvdParams, TransactionFinishedParams},
PacketInfo, PduSendProvider, RemoteEntityConfig, RemoteEntityConfigProvider,
StdRemoteEntityConfigProvider, TransactionId, UserFaultHookProvider,
};
pub const LOCAL_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(1);
pub struct FileSegmentRecvdParamsNoSegMetadata {
pub id: TransactionId,
pub offset: u64,
pub length: usize,
}
#[derive(Default)]
pub struct TestCfdpUser {
pub next_expected_seq_num: u64,
pub expected_full_src_name: String,
pub expected_full_dest_name: String,
pub expected_file_size: u64,
pub transaction_indication_call_count: u32,
pub eof_recvd_call_count: u32,
pub finished_indic_queue: VecDeque<TransactionFinishedParams>,
pub metadata_recv_queue: VecDeque<OwnedMetadataRecvdParams>,
pub file_seg_recvd_queue: VecDeque<FileSegmentRecvdParamsNoSegMetadata>,
}
impl TestCfdpUser {
pub fn new(
next_expected_seq_num: u64,
expected_full_src_name: String,
expected_full_dest_name: String,
expected_file_size: u64,
) -> Self {
Self {
next_expected_seq_num,
expected_full_src_name,
expected_full_dest_name,
expected_file_size,
transaction_indication_call_count: 0,
eof_recvd_call_count: 0,
finished_indic_queue: VecDeque::new(),
metadata_recv_queue: VecDeque::new(),
file_seg_recvd_queue: VecDeque::new(),
}
}
pub fn generic_id_check(&self, id: &crate::cfdp::TransactionId) {
assert_eq!(id.source_id, LOCAL_ID.into());
assert_eq!(id.seq_num().value(), self.next_expected_seq_num);
}
}
impl CfdpUser for TestCfdpUser {
fn transaction_indication(&mut self, id: &crate::cfdp::TransactionId) {
self.generic_id_check(id);
self.transaction_indication_call_count += 1;
}
fn eof_sent_indication(&mut self, id: &crate::cfdp::TransactionId) {
self.generic_id_check(id);
}
fn transaction_finished_indication(
&mut self,
finished_params: &crate::cfdp::user::TransactionFinishedParams,
) {
self.generic_id_check(&finished_params.id);
self.finished_indic_queue.push_back(*finished_params);
}
fn metadata_recvd_indication(
&mut self,
md_recvd_params: &crate::cfdp::user::MetadataReceivedParams,
) {
self.generic_id_check(&md_recvd_params.id);
assert_eq!(
String::from(md_recvd_params.src_file_name),
self.expected_full_src_name
);
assert_eq!(
String::from(md_recvd_params.dest_file_name),
self.expected_full_dest_name
);
assert_eq!(md_recvd_params.msgs_to_user.len(), 0);
assert_eq!(md_recvd_params.source_id, LOCAL_ID.into());
assert_eq!(md_recvd_params.file_size, self.expected_file_size);
self.metadata_recv_queue.push_back(md_recvd_params.into());
}
fn file_segment_recvd_indication(
&mut self,
segment_recvd_params: &crate::cfdp::user::FileSegmentRecvdParams,
) {
self.generic_id_check(&segment_recvd_params.id);
self.file_seg_recvd_queue
.push_back(FileSegmentRecvdParamsNoSegMetadata {
id: segment_recvd_params.id,
offset: segment_recvd_params.offset,
length: segment_recvd_params.length,
})
}
fn report_indication(&mut self, _id: &crate::cfdp::TransactionId) {}
fn suspended_indication(
&mut self,
_id: &crate::cfdp::TransactionId,
_condition_code: ConditionCode,
) {
panic!("unexpected suspended indication");
}
fn resumed_indication(&mut self, _id: &crate::cfdp::TransactionId, _progresss: u64) {}
fn fault_indication(
&mut self,
_id: &crate::cfdp::TransactionId,
_condition_code: ConditionCode,
_progress: u64,
) {
panic!("unexpected fault indication");
}
fn abandoned_indication(
&mut self,
_id: &crate::cfdp::TransactionId,
_condition_code: ConditionCode,
_progress: u64,
) {
panic!("unexpected abandoned indication");
}
fn eof_recvd_indication(&mut self, id: &crate::cfdp::TransactionId) {
self.generic_id_check(id);
self.eof_recvd_call_count += 1;
}
}
#[derive(Default)]
pub(crate) struct TestFaultHandler {
pub notice_of_suspension_queue: VecDeque<(TransactionId, ConditionCode, u64)>,
@ -791,14 +925,17 @@ pub(crate) mod tests {
}
}
pub fn basic_remote_cfg_table() -> StdRemoteEntityConfigProvider {
pub fn basic_remote_cfg_table(
dest_id: impl Into<UnsignedByteField>,
crc_on_transmission_by_default: bool,
) -> StdRemoteEntityConfigProvider {
let mut table = StdRemoteEntityConfigProvider::default();
let remote_entity_cfg = RemoteEntityConfig::new_with_default_values(
UnsignedByteFieldU16::new(1).into(),
dest_id.into(),
1024,
1024,
true,
true,
crc_on_transmission_by_default,
TransmissionMode::Unacknowledged,
ChecksumType::Crc32,
);

View File

@ -1,4 +1,5 @@
use core::{cell::RefCell, ops::ControlFlow, str::Utf8Error};
use std::println;
use spacepackets::{
cfdp::{
@ -118,6 +119,8 @@ pub enum PutRequestError {
Storage(#[from] ByteConversionError),
#[error("already busy with put request")]
AlreadyBusy,
#[error("no remote entity configuration found for {0:?}")]
NoRemoteCfgFound(UnsignedByteField),
}
pub struct SourceHandler<
@ -258,6 +261,9 @@ impl<
);
if remote_cfg.is_none() {
// TODO: Specific error.
return Err(PutRequestError::NoRemoteCfgFound(
self.put_request_cacher.static_fields.destination_id,
));
}
let remote_cfg = remote_cfg.unwrap();
self.state_helper.num_packets_ready = 0;
@ -281,7 +287,7 @@ impl<
};
self.tstate = Some(TransferState::new(
TransactionId::new(
self.put_request_cacher.static_fields.destination_id,
self.local_cfg().id,
UnsignedByteField::new(
SeqCountProvider::MAX_BIT_WIDTH / 8,
self.seq_count_provider.get_and_increment().into(),
@ -302,6 +308,7 @@ impl<
}
fn fsm_busy(&mut self, cfdp_user: &mut impl CfdpUser) -> Result<u32, SourceError> {
let mut sent_packets = 0;
if self.state_helper.step == TransactionStep::Idle {
self.state_helper.step = TransactionStep::TransactionStart;
}
@ -312,7 +319,8 @@ impl<
if self.state_helper.step == TransactionStep::SendingMetadata {
self.prepare_and_send_metadata_pdu()?;
self.state_helper.step = TransactionStep::SendingFileData;
return Ok(1);
sent_packets += 1;
// return Ok(1);
}
if self.state_helper.step == TransactionStep::SendingFileData {
if let ControlFlow::Break(result) = self.file_data_fsm()? {
@ -321,7 +329,8 @@ impl<
}
if self.state_helper.step == TransactionStep::SendingEof {
self.eof_fsm(cfdp_user)?;
return Ok(1);
sent_packets += 1;
// return Ok(1);
}
if self.state_helper.step == TransactionStep::WaitingForFinished {
/*
@ -373,7 +382,7 @@ impl<
self.reset()
*/
}
Ok(0)
Ok(sent_packets)
}
fn eof_fsm(&mut self, cfdp_user: &mut impl CfdpUser) -> Result<(), SourceError> {
@ -431,7 +440,6 @@ impl<
.expect("transfer state unexpectedly empty");
if !self.put_request_cacher.has_source_file() {
self.fparams.metadata_only = true;
self.fparams.empty_file = true;
} else {
let source_file = self
.put_request_cacher
@ -446,9 +454,13 @@ impl<
self.put_request_cacher
.dest_file()
.map_err(SourceError::DestFileNotValidUtf8)?;
if self.vfs.file_size(source_file)? > u32::MAX as u64 {
let file_size = self.vfs.file_size(source_file)?;
if file_size > u32::MAX as u64 {
self.pdu_conf.file_flag = LargeFileFlag::Large
} else {
if file_size == 0 {
self.fparams.empty_file = true;
}
self.pdu_conf.file_flag = LargeFileFlag::Normal
}
}
@ -463,7 +475,7 @@ impl<
if larger_entity_width != cached_id.size() {
UnsignedByteField::new(larger_entity_width, cached_id.value_const())
} else {
self.local_cfg.id
*cached_id
}
};
self.pdu_conf
@ -662,8 +674,8 @@ impl<
let mut pdu_buffer_mut = self.pdu_and_cksum_buffer.borrow_mut();
let written_len = pdu.write_to_bytes(&mut pdu_buffer_mut)?;
self.pdu_sender.send_pdu(
PduType::FileDirective,
Some(FileDirectiveType::MetadataPdu),
pdu.pdu_type(),
pdu.file_directive_type(),
&pdu_buffer_mut[0..written_len],
)?;
Ok(())
@ -708,6 +720,21 @@ impl<
fn handle_keep_alive_pdu(&mut self) {}
/// Get the step, which denotes the exact step of a pending CFDP transaction when applicable.
pub fn step(&self) -> TransactionStep {
self.state_helper.step
}
/// Get the step, which denotes whether the CFDP handler is active, and which CFDP class
/// is used if it is active.
pub fn state(&self) -> State {
self.state_helper.state
}
pub fn local_cfg(&self) -> &LocalEntityConfig<UserFaultHook> {
&self.local_cfg
}
/// This function is public to allow completely resetting the handler, but it is explicitely
/// discouraged to do this. CFDP has mechanism to detect issues and errors on itself.
/// Resetting the handler might interfere with these mechanisms and lead to unexpected
@ -721,14 +748,24 @@ impl<
#[cfg(test)]
mod tests {
use spacepackets::util::UnsignedByteFieldU16;
use std::path::PathBuf;
use alloc::string::String;
use spacepackets::{
cfdp::{pdu::metadata::MetadataPduReader, ChecksumType, CrcFlag},
util::UnsignedByteFieldU16,
};
use tempfile::TempPath;
use super::*;
use crate::{
cfdp::{
filestore::NativeFilestore,
tests::{basic_remote_cfg_table, TestCfdpSender, TestFaultHandler},
FaultHandler, IndicationConfig, StdRemoteEntityConfigProvider,
request::PutRequestOwned,
tests::{
basic_remote_cfg_table, SentPdu, TestCfdpSender, TestCfdpUser, TestFaultHandler,
},
FaultHandler, IndicationConfig, StdRemoteEntityConfigProvider, CRC_32,
},
seq_count::SeqCountProviderSimple,
};
@ -736,6 +773,13 @@ mod tests {
const LOCAL_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(1);
const REMOTE_ID: UnsignedByteFieldU16 = UnsignedByteFieldU16::new(2);
fn init_full_filepaths_textfile() -> (TempPath, PathBuf) {
(
tempfile::NamedTempFile::new().unwrap().into_temp_path(),
tempfile::TempPath::from_path("/tmp/test.txt").to_path_buf(),
)
}
type TestSourceHandler = SourceHandler<
TestCfdpSender,
TestFaultHandler,
@ -744,33 +788,178 @@ mod tests {
SeqCountProviderSimple<u16>,
>;
fn default_source_handler(
test_fault_handler: TestFaultHandler,
test_packet_sender: TestCfdpSender,
) -> TestSourceHandler {
let local_entity_cfg = LocalEntityConfig {
id: REMOTE_ID.into(),
indication_cfg: IndicationConfig::default(),
fault_handler: FaultHandler::new(test_fault_handler),
};
let static_put_request_cacher = StaticPutRequestCacher::new(1024);
SourceHandler::new(
local_entity_cfg,
test_packet_sender,
NativeFilestore::default(),
static_put_request_cacher,
1024,
basic_remote_cfg_table(),
SeqCountProviderSimple::default(),
)
struct SourceHandlerTestbench {
handler: TestSourceHandler,
// src_path: PathBuf,
// dest_path: PathBuf,
}
impl SourceHandlerTestbench {
fn new(
crc_on_transmission_by_default: bool,
test_fault_handler: TestFaultHandler,
test_packet_sender: TestCfdpSender,
) -> Self {
let local_entity_cfg = LocalEntityConfig {
id: LOCAL_ID.into(),
indication_cfg: IndicationConfig::default(),
fault_handler: FaultHandler::new(test_fault_handler),
};
let static_put_request_cacher = StaticPutRequestCacher::new(2048);
Self {
handler: SourceHandler::new(
local_entity_cfg,
test_packet_sender,
NativeFilestore::default(),
static_put_request_cacher,
1024,
basic_remote_cfg_table(REMOTE_ID, crc_on_transmission_by_default),
SeqCountProviderSimple::default(),
),
}
}
fn all_fault_queues_empty(&self) -> bool {
self.handler
.local_cfg
.user_fault_hook()
.borrow()
.all_queues_empty()
}
fn pdu_queue_empty(&self) -> bool {
self.handler.pdu_sender.queue_empty()
}
fn get_next_sent_pdu(&self) -> Option<SentPdu> {
self.handler.pdu_sender.retrieve_next_pdu()
}
}
#[test]
fn test_basic() {
let fault_handler = TestFaultHandler::default();
let test_sender = TestCfdpSender::default();
let source_handler = default_source_handler(fault_handler, test_sender);
assert!(source_handler.transmission_mode().is_none());
// assert!(fault_handler.all_queues_empty());
let tb = SourceHandlerTestbench::new(false, fault_handler, test_sender);
assert!(tb.handler.transmission_mode().is_none());
assert!(tb.all_fault_queues_empty());
assert!(tb.pdu_queue_empty());
assert_eq!(tb.handler.state(), State::Idle);
assert_eq!(tb.handler.step(), TransactionStep::Idle);
}
#[test]
fn test_empty_file_transfer_not_acked_no_closure() {
let fault_handler = TestFaultHandler::default();
let test_sender = TestCfdpSender::default();
let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender);
let (srcfile, destfile) = init_full_filepaths_textfile();
let srcfile_str = String::from(srcfile.to_str().unwrap());
let destfile_str = String::from(destfile.to_str().unwrap());
let put_request = PutRequestOwned::new_regular_request(
REMOTE_ID.into(),
&srcfile_str,
&destfile_str,
Some(TransmissionMode::Unacknowledged),
Some(false),
)
.expect("creating put request failed");
tb.handler
.put_request(&put_request)
.expect("put_request call failed");
assert_eq!(tb.handler.state(), State::Busy);
assert_eq!(tb.handler.step(), TransactionStep::Idle);
let mut cfdp_user = TestCfdpUser::new(0, srcfile_str.clone(), destfile_str.clone(), 0);
let sent_packets = tb
.handler
.state_machine(&mut cfdp_user, None)
.expect("source handler FSM failure");
assert_eq!(sent_packets, 2);
assert!(!tb.pdu_queue_empty());
let next_pdu = tb.get_next_sent_pdu().unwrap();
assert!(!tb.pdu_queue_empty());
assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
assert_eq!(
next_pdu.file_directive_type,
Some(FileDirectiveType::MetadataPdu)
);
let metadata_pdu =
MetadataPduReader::new(&next_pdu.raw_pdu).expect("invalid metadata PDU format");
assert_eq!(
metadata_pdu
.src_file_name()
.value_as_str()
.unwrap()
.unwrap(),
srcfile_str
);
assert_eq!(
metadata_pdu
.dest_file_name()
.value_as_str()
.unwrap()
.unwrap(),
destfile_str
);
assert_eq!(metadata_pdu.metadata_params().file_size, 0);
assert_eq!(
metadata_pdu.metadata_params().checksum_type,
ChecksumType::Crc32
);
assert!(!metadata_pdu.metadata_params().closure_requested);
assert_eq!(metadata_pdu.options(), &[]);
let next_pdu = tb.get_next_sent_pdu().unwrap();
assert_eq!(next_pdu.pdu_type, PduType::FileDirective);
assert_eq!(
next_pdu.file_directive_type,
Some(FileDirectiveType::EofPdu)
);
let eof_pdu = EofPdu::from_bytes(&next_pdu.raw_pdu).expect("invalid metadata PDU format");
assert_eq!(eof_pdu.condition_code(), ConditionCode::NoError);
assert_eq!(eof_pdu.file_size(), 0);
assert_eq!(eof_pdu.file_checksum(), CRC_32.digest().finalize());
assert_eq!(
eof_pdu.pdu_header().common_pdu_conf().source_id(),
LOCAL_ID.into()
);
assert_eq!(
eof_pdu.pdu_header().common_pdu_conf().dest_id(),
REMOTE_ID.into()
);
assert_eq!(
eof_pdu.pdu_header().common_pdu_conf().crc_flag,
CrcFlag::NoCrc
);
assert_eq!(
eof_pdu.pdu_header().common_pdu_conf().direction,
Direction::TowardsReceiver
);
assert_eq!(
eof_pdu.pdu_header().common_pdu_conf().file_flag,
LargeFileFlag::Normal
);
assert_eq!(
eof_pdu.pdu_header().common_pdu_conf().trans_mode,
TransmissionMode::Unacknowledged
);
assert_eq!(
eof_pdu
.pdu_header()
.common_pdu_conf()
.transaction_seq_num
.value_const(),
0
);
assert_eq!(
eof_pdu
.pdu_header()
.common_pdu_conf()
.transaction_seq_num
.size(),
2
);
assert_eq!(tb.handler.state(), State::Idle);
assert_eq!(tb.handler.step(), TransactionStep::Idle);
}
}