diff --git a/src/dest.rs b/src/dest.rs index 486b924..6662adb 100644 --- a/src/dest.rs +++ b/src/dest.rs @@ -1113,7 +1113,7 @@ mod tests { 2048, test_packet_sender, NativeFilestore::default(), - basic_remote_cfg_table(LOCAL_ID, true), + basic_remote_cfg_table(LOCAL_ID, 1024, true), TestCheckTimerCreator::new(check_timer_expired), ) } diff --git a/src/lib.rs b/src/lib.rs index de0513f..50d0703 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -945,12 +945,13 @@ pub(crate) mod tests { pub fn basic_remote_cfg_table( dest_id: impl Into, + max_packet_len: usize, crc_on_transmission_by_default: bool, ) -> StdRemoteEntityConfigProvider { let mut table = StdRemoteEntityConfigProvider::default(); let remote_entity_cfg = RemoteEntityConfig::new_with_default_values( dest_id.into(), - 1024, + max_packet_len, true, crc_on_transmission_by_default, TransmissionMode::Unacknowledged, diff --git a/src/source.rs b/src/source.rs index d48f2e4..29f8c96 100644 --- a/src/source.rs +++ b/src/source.rs @@ -268,7 +268,6 @@ impl< return Err(PutRequestError::AlreadyBusy); } self.put_request_cacher.set(put_request)?; - self.state_helper.state = super::State::Busy; let remote_cfg = self.remote_cfg_table.get( self.put_request_cacher .static_fields @@ -319,6 +318,7 @@ impl< None, None, )); + self.state_helper.state = super::State::Busy; Ok(()) } @@ -785,9 +785,10 @@ impl< #[cfg(test)] mod tests { - use std::{fs::OpenOptions, io::Write, path::PathBuf, string::ToString}; + use std::{fs::OpenOptions, io::Write, path::PathBuf, vec::Vec}; use alloc::string::String; + use rand::Rng; use spacepackets::{ cfdp::{ pdu::{ @@ -829,8 +830,12 @@ mod tests { struct SourceHandlerTestbench { handler: TestSourceHandler, - srcfile: Option, - destfile: Option, + #[allow(dead_code)] + srcfile_handle: TempPath, + srcfile: String, + destfile: String, + max_packet_len: usize, + check_idle_on_drop: bool, } impl SourceHandlerTestbench { @@ -838,6 +843,7 @@ mod tests { crc_on_transmission_by_default: bool, test_fault_handler: TestFaultHandler, test_packet_sender: TestCfdpSender, + max_packet_len: usize, ) -> Self { let local_entity_cfg = LocalEntityConfig { id: LOCAL_ID.into(), @@ -845,6 +851,8 @@ mod tests { fault_handler: FaultHandler::new(test_fault_handler), }; let static_put_request_cacher = StaticPutRequestCacher::new(2048); + let (srcfile_handle, destfile) = init_full_filepaths_textfile(); + let srcfile = String::from(srcfile_handle.to_path_buf().to_str().unwrap()); Self { handler: SourceHandler::new( local_entity_cfg, @@ -852,22 +860,34 @@ mod tests { NativeFilestore::default(), static_put_request_cacher, 1024, - basic_remote_cfg_table(REMOTE_ID, crc_on_transmission_by_default), + basic_remote_cfg_table( + REMOTE_ID, + max_packet_len, + crc_on_transmission_by_default, + ), SeqCountProviderSimple::default(), ), - srcfile: None, - destfile: None, + srcfile_handle, + srcfile, + destfile: String::from(destfile.to_path_buf().to_str().unwrap()), + max_packet_len, + check_idle_on_drop: true, } } + fn create_user(&self, next_expected_seq_num: u64, filesize: u64) -> TestCfdpUser { + TestCfdpUser::new( + next_expected_seq_num, + self.srcfile.clone(), + self.destfile.clone(), + filesize, + ) + } + fn put_request( &mut self, put_request: &impl ReadablePutRequest, ) -> Result<(), PutRequestError> { - if self.srcfile.is_some() || self.destfile.is_some() { - self.srcfile = Some(put_request.source_file().unwrap().to_string()); - self.destfile = Some(put_request.dest_file().unwrap().to_string()); - } self.handler.put_request(put_request) } @@ -915,16 +935,23 @@ mod tests { } } + impl Drop for SourceHandlerTestbench { + fn drop(&mut self) { + self.all_fault_queues_empty(); + if self.check_idle_on_drop { + assert_eq!(self.handler.state(), State::Idle); + assert_eq!(self.handler.step(), TransactionStep::Idle); + } + } + } + #[test] fn test_basic() { let fault_handler = TestFaultHandler::default(); let test_sender = TestCfdpSender::default(); - let tb = SourceHandlerTestbench::new(false, fault_handler, test_sender); + let tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512); 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); } // Returns a tuple. First parameter: Closure requested. Second parameter: PDU header of @@ -933,9 +960,7 @@ mod tests { tb: &mut SourceHandlerTestbench, cfdp_user: &mut TestCfdpUser, put_request: PutRequestOwned, - srcfile_str: String, - destfile_str: String, - filesize: usize, + filesize: u64, ) -> (bool, PduHeader) { assert_eq!(cfdp_user.transaction_indication_call_count, 0); assert_eq!(cfdp_user.eof_sent_call_count, 0); @@ -967,7 +992,7 @@ mod tests { .value_as_str() .unwrap() .unwrap(), - srcfile_str + tb.srcfile ); assert_eq!( metadata_pdu @@ -975,9 +1000,9 @@ mod tests { .value_as_str() .unwrap() .unwrap(), - destfile_str + tb.destfile ); - assert_eq!(metadata_pdu.metadata_params().file_size, filesize as u64); + assert_eq!(metadata_pdu.metadata_params().file_size, filesize); assert_eq!( metadata_pdu.metadata_params().checksum_type, ChecksumType::Crc32 @@ -1000,7 +1025,7 @@ mod tests { tb: &mut SourceHandlerTestbench, cfdp_user: &mut TestCfdpUser, closure_requested: bool, - filesize: usize, + filesize: u64, checksum: u32, ) { let next_pdu = tb.get_next_sent_pdu().unwrap(); @@ -1012,7 +1037,7 @@ mod tests { let eof_pdu = EofPdu::from_bytes(&next_pdu.raw_pdu).expect("invalid EOF PDU format"); tb.common_pdu_check_for_file_transfer(eof_pdu.pdu_header(), CrcFlag::NoCrc); assert_eq!(eof_pdu.condition_code(), ConditionCode::NoError); - assert_eq!(eof_pdu.file_size(), filesize as u64); + assert_eq!(eof_pdu.file_size(), filesize); assert_eq!(eof_pdu.file_checksum(), checksum); assert_eq!( eof_pdu @@ -1034,124 +1059,206 @@ mod tests { tb.all_fault_queues_empty(); } - #[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 filesize = 0; - 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()); + fn check_next_file_pdu( + tb: &mut SourceHandlerTestbench, + expected_offset: u64, + expected_data: &[u8], + ) { + let next_pdu = tb.get_next_sent_pdu().unwrap(); + assert_eq!(next_pdu.pdu_type, PduType::FileData); + assert!(next_pdu.file_directive_type.is_none()); + let fd_pdu = + FileDataPdu::from_bytes(&next_pdu.raw_pdu).expect("reading file data PDU failed"); + assert_eq!(fd_pdu.offset(), expected_offset); + assert_eq!(fd_pdu.file_data(), expected_data); + assert!(fd_pdu.segment_metadata().is_none()); + } + + fn common_file_transfer( + tb: &mut SourceHandlerTestbench, + cfdp_user: &mut TestCfdpUser, + with_closure: bool, + file_data: Vec, + ) -> (PduHeader, u32) { + let mut digest = CRC_32.digest(); + digest.update(&file_data); + let checksum = digest.finalize(); + cfdp_user.expected_full_src_name = tb.srcfile.clone(); + cfdp_user.expected_full_dest_name = tb.destfile.clone(); + cfdp_user.expected_file_size = file_data.len() as u64; let put_request = PutRequestOwned::new_regular_request( REMOTE_ID.into(), - &srcfile_str, - &destfile_str, + &tb.srcfile, + &tb.destfile, Some(TransmissionMode::Unacknowledged), - Some(false), + Some(with_closure), ) .expect("creating put request failed"); - let mut cfdp_user = TestCfdpUser::new(0, srcfile_str.clone(), destfile_str.clone(), 0); - let (closure_requested, _) = common_no_acked_file_transfer( - &mut tb, - &mut cfdp_user, - put_request, - srcfile_str, - destfile_str, - filesize, + let (closure_requested, pdu_header) = + common_no_acked_file_transfer(tb, cfdp_user, put_request, cfdp_user.expected_file_size); + let mut current_offset = 0; + let chunks = file_data.chunks( + calculate_max_file_seg_len_for_max_packet_len_and_pdu_header( + &pdu_header, + tb.max_packet_len, + None, + ), ); + let mut fd_pdus = 0; + for segment in chunks { + check_next_file_pdu(tb, current_offset, segment); + tb.handler.state_machine(cfdp_user, None).unwrap(); + fd_pdus += 1; + current_offset += segment.len() as u64; + } common_eof_pdu_check( - &mut tb, - &mut cfdp_user, + tb, + cfdp_user, closure_requested, - filesize, - CRC_32.digest().finalize(), - ) + cfdp_user.expected_file_size, + checksum, + ); + (pdu_header, fd_pdus) } - #[test] - fn test_tiny_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(); + fn common_tiny_file_transfer( + tb: &mut SourceHandlerTestbench, + cfdp_user: &mut TestCfdpUser, + with_closure: bool, + ) -> PduHeader { let mut file = OpenOptions::new() .write(true) - .open(&srcfile) + .open(&tb.srcfile) .expect("opening file failed"); let content_str = "Hello World!"; - let filesize = content_str.len(); file.write_all(content_str.as_bytes()) .expect("writing file content failed"); drop(file); - let mut digest = CRC_32.digest(); - digest.update(content_str.as_bytes()); - let checksum = digest.finalize(); - let srcfile_str = String::from(srcfile.to_str().unwrap()); - let destfile_str = String::from(destfile.to_str().unwrap()); + let (pdu_header, fd_pdus) = + common_file_transfer(tb, cfdp_user, with_closure, content_str.as_bytes().to_vec()); + assert_eq!(fd_pdus, 1); + pdu_header + } + + fn common_finish_handling( + tb: &mut SourceHandlerTestbench, + cfdp_user: &mut TestCfdpUser, + pdu_header: PduHeader, + ) { + let finished_pdu = FinishedPduCreator::new_default( + pdu_header, + DeliveryCode::Complete, + FileStatus::Retained, + ); + let finished_pdu_vec = finished_pdu.to_vec().unwrap(); + let packet_info = PacketInfo::new(&finished_pdu_vec).unwrap(); + tb.handler + .state_machine(cfdp_user, Some(&packet_info)) + .unwrap(); + } + + #[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, 512); + let filesize = 0; let put_request = PutRequestOwned::new_regular_request( REMOTE_ID.into(), - &srcfile_str, - &destfile_str, + &tb.srcfile, + &tb.destfile, Some(TransmissionMode::Unacknowledged), Some(false), ) .expect("creating put request failed"); - let mut cfdp_user = TestCfdpUser::new(0, srcfile_str.clone(), destfile_str.clone(), 0); - let (closure_requested, _) = common_no_acked_file_transfer( - &mut tb, - &mut cfdp_user, - put_request, - srcfile_str, - destfile_str, - filesize, - ); - // TODO: File data PDU check. - let next_pdu = tb.get_next_sent_pdu().unwrap(); - assert_eq!(next_pdu.pdu_type, PduType::FileData); - assert!(next_pdu.file_directive_type.is_none()); - let fd_pdu = - FileDataPdu::from_bytes(&next_pdu.raw_pdu).expect("reading file data PDU failed"); - assert_eq!(fd_pdu.offset(), 0); - assert_eq!(fd_pdu.file_data(), content_str.as_bytes()); - assert!(fd_pdu.segment_metadata().is_none()); - tb.handler.state_machine(&mut cfdp_user, None).unwrap(); + let mut cfdp_user = tb.create_user(0, filesize); + let (closure_requested, _) = + common_no_acked_file_transfer(&mut tb, &mut cfdp_user, put_request, filesize); common_eof_pdu_check( &mut tb, &mut cfdp_user, closure_requested, filesize, - checksum, + CRC_32.digest().finalize(), ) } + #[test] + fn test_tiny_file_transfer_not_acked_no_closure() { + let fault_handler = TestFaultHandler::default(); + let test_sender = TestCfdpSender::default(); + let mut cfdp_user = TestCfdpUser::default(); + let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512); + common_tiny_file_transfer(&mut tb, &mut cfdp_user, false); + } + + #[test] + fn test_tiny_file_transfer_not_acked_with_closure() { + let fault_handler = TestFaultHandler::default(); + let test_sender = TestCfdpSender::default(); + let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512); + let mut cfdp_user = TestCfdpUser::default(); + let pdu_header = common_tiny_file_transfer(&mut tb, &mut cfdp_user, true); + common_finish_handling(&mut tb, &mut cfdp_user, pdu_header) + } + + #[test] + fn test_two_segment_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, 128); + let mut cfdp_user = TestCfdpUser::default(); + let mut file = OpenOptions::new() + .write(true) + .open(&tb.srcfile) + .expect("opening file failed"); + let mut rand_data = [0u8; 140]; + rand::thread_rng().fill(&mut rand_data[..]); + file.write_all(&rand_data) + .expect("writing file content failed"); + drop(file); + let (_, fd_pdus) = common_file_transfer(&mut tb, &mut cfdp_user, false, rand_data.to_vec()); + assert_eq!(fd_pdus, 2); + } + + #[test] + fn test_two_segment_file_transfer_not_acked_with_closure() { + let fault_handler = TestFaultHandler::default(); + let test_sender = TestCfdpSender::default(); + let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 128); + let mut cfdp_user = TestCfdpUser::default(); + let mut file = OpenOptions::new() + .write(true) + .open(&tb.srcfile) + .expect("opening file failed"); + let mut rand_data = [0u8; 140]; + rand::thread_rng().fill(&mut rand_data[..]); + file.write_all(&rand_data) + .expect("writing file content failed"); + drop(file); + let (pdu_header, fd_pdus) = + common_file_transfer(&mut tb, &mut cfdp_user, false, rand_data.to_vec()); + assert_eq!(fd_pdus, 2); + common_finish_handling(&mut tb, &mut cfdp_user, pdu_header) + } + #[test] fn test_empty_file_transfer_not_acked_with_closure() { let fault_handler = TestFaultHandler::default(); let test_sender = TestCfdpSender::default(); - let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender); + let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512); let filesize = 0; - 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, + &tb.srcfile, + &tb.destfile, Some(TransmissionMode::Unacknowledged), Some(true), ) .expect("creating put request failed"); - let mut cfdp_user = TestCfdpUser::new(0, srcfile_str.clone(), destfile_str.clone(), 0); - - let (closure_requested, pdu_header) = common_no_acked_file_transfer( - &mut tb, - &mut cfdp_user, - put_request, - srcfile_str, - destfile_str, - filesize, - ); + let mut cfdp_user = tb.create_user(0, filesize); + let (closure_requested, pdu_header) = + common_no_acked_file_transfer(&mut tb, &mut cfdp_user, put_request, filesize); common_eof_pdu_check( &mut tb, &mut cfdp_user, @@ -1159,26 +1266,14 @@ mod tests { filesize, CRC_32.digest().finalize(), ); - let finished_pdu = FinishedPduCreator::new_default( - pdu_header, - DeliveryCode::Complete, - FileStatus::Retained, - ); - let finished_pdu_vec = finished_pdu.to_vec().unwrap(); - let packet_info = PacketInfo::new(&finished_pdu_vec).unwrap(); - tb.handler - .state_machine(&mut cfdp_user, Some(&packet_info)) - .unwrap(); - tb.all_fault_queues_empty(); - assert_eq!(tb.handler.state(), State::Idle); - assert_eq!(tb.handler.step(), TransactionStep::Idle); + common_finish_handling(&mut tb, &mut cfdp_user, pdu_header) } #[test] fn test_put_request_no_remote_cfg() { let fault_handler = TestFaultHandler::default(); let test_sender = TestCfdpSender::default(); - let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender); + let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512); let (srcfile, destfile) = init_full_filepaths_textfile(); let srcfile_str = String::from(srcfile.to_str().unwrap()); @@ -1205,7 +1300,7 @@ mod tests { fn test_put_request_file_does_not_exist() { let fault_handler = TestFaultHandler::default(); let test_sender = TestCfdpSender::default(); - let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender); + let mut tb = SourceHandlerTestbench::new(false, fault_handler, test_sender, 512); let file_which_does_not_exist = "/tmp/this_file_does_not_exist.txt"; let destfile = "/tmp/tmp.txt";