CFDP extracted to library #201
@ -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]
|
||||
|
@ -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,
|
||||
);
|
||||
|
@ -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(
|
||||
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,
|
||||
) -> TestSourceHandler {
|
||||
) -> Self {
|
||||
let local_entity_cfg = LocalEntityConfig {
|
||||
id: REMOTE_ID.into(),
|
||||
id: LOCAL_ID.into(),
|
||||
indication_cfg: IndicationConfig::default(),
|
||||
fault_handler: FaultHandler::new(test_fault_handler),
|
||||
};
|
||||
let static_put_request_cacher = StaticPutRequestCacher::new(1024);
|
||||
SourceHandler::new(
|
||||
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(),
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user