add more tests
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good

This commit is contained in:
Robin Müller 2024-03-11 16:28:26 +01:00
parent e7cec1d333
commit 460ce8074f
Signed by: muellerr
GPG Key ID: A649FB78196E3849
3 changed files with 204 additions and 22 deletions

View File

@ -609,6 +609,12 @@ impl From<ParamsHeapless> for Params {
}
}
impl From<ParamsRaw> for Params {
fn from(x: ParamsRaw) -> Self {
Self::Heapless(ParamsHeapless::Raw(x))
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
impl From<Vec<u8>> for Params {

View File

@ -256,16 +256,35 @@ pub mod std_mod {
impl<VerificationReporter: VerificationReportingProvider, UserHook: ActionReplyHandlerHook>
PusService8ReplyHandler<VerificationReporter, UserHook>
{
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn new_with_init_time_now(
verification_reporter: VerificationReporter,
fail_data_buf_size: usize,
user_hook: UserHook,
) -> Result<Self, SystemTimeError> {
let mut reply_handler = Self {
active_requests: HashMap::new(),
verification_reporter,
fail_data_buf: alloc::vec![0; fail_data_buf_size],
current_time: UnixTimestamp::default(),
user_hook,
};
reply_handler.update_time_from_now()?;
Ok(reply_handler)
}
pub fn new(
verification_reporter: VerificationReporter,
fail_data_buf_size: usize,
user_hook: UserHook,
init_time: UnixTimestamp,
) -> Self {
Self {
active_requests: HashMap::new(),
verification_reporter,
fail_data_buf: alloc::vec![0; fail_data_buf_size],
current_time: UnixTimestamp::from_now().unwrap(),
current_time: init_time,
user_hook,
}
}
@ -294,6 +313,9 @@ pub mod std_mod {
Ok(())
}
/// Check for timeouts across all active requests.
///
/// It will call [Self::handle_timeout] for all active requests which have timed out.
pub fn check_for_timeouts(&mut self, time_stamp: &[u8]) -> Result<(), EcssTmtcError> {
for active_req in self.active_requests.values() {
let diff = self.current_time - active_req.start_time;
@ -304,11 +326,22 @@ pub mod std_mod {
Ok(())
}
/// Handle the timeout for a given active request.
///
/// This implementation will report a verification completion failure with a user-provided
/// error code. It supplies the configured request timeout in milliseconds as a [u64]
/// serialized in big-endian format as the failure data.
pub fn handle_timeout(&self, active_request: &ActiveActionRequest, time_stamp: &[u8]) {
let timeout = active_request.timeout.as_millis() as u64;
let timeout_raw = timeout.to_be_bytes();
self.verification_reporter
.completion_failure(
active_request.token,
FailParams::new(time_stamp, &self.user_hook.timeout_error_code(), &[]),
FailParams::new(
time_stamp,
&self.user_hook.timeout_error_code(),
&timeout_raw,
),
)
.unwrap();
self.user_hook.timeout_callback(active_request);
@ -327,11 +360,15 @@ pub mod std_mod {
let active_req = active_req.unwrap();
match action_reply_with_ids.reply {
ActionReplyPus::CompletionFailed { error_code, params } => {
params.write_to_be_bytes(&mut self.fail_data_buf)?;
let fail_data_len = params.write_to_be_bytes(&mut self.fail_data_buf)?;
self.verification_reporter
.completion_failure(
active_req.token,
FailParams::new(time_stamp, &error_code, &self.fail_data_buf),
FailParams::new(
time_stamp,
&error_code,
&self.fail_data_buf[..fail_data_len],
),
)
.map_err(|e| e.0)?;
self.active_requests
@ -342,7 +379,7 @@ pub mod std_mod {
step,
params,
} => {
params.write_to_be_bytes(&mut self.fail_data_buf)?;
let fail_data_len = params.write_to_be_bytes(&mut self.fail_data_buf)?;
self.verification_reporter
.step_failure(
active_req.token,
@ -350,7 +387,7 @@ pub mod std_mod {
time_stamp,
&EcssEnumU16::new(step),
&error_code,
&self.fail_data_buf,
&self.fail_data_buf[..fail_data_len],
),
)
.map_err(|e| e.0)?;
@ -380,6 +417,7 @@ pub mod std_mod {
#[cfg(test)]
mod tests {
use core::{cell::RefCell, time::Duration};
use std::time::SystemTimeError;
use alloc::collections::VecDeque;
use delegate::delegate;
@ -395,6 +433,7 @@ mod tests {
use crate::{
action::ActionRequestVariant,
params::{self, ParamsRaw, WritableToBeBytes},
pus::{
tests::{
PusServiceHandlerWithVecCommon, PusTestHarness, SimplePusPacketHandler,
@ -402,7 +441,7 @@ mod tests {
},
verification::{
self,
tests::{SharedVerificationMap, TestVerificationReporter},
tests::{SharedVerificationMap, TestVerificationReporter, VerificationStatus},
FailParams, TcStateNone, TcStateStarted, VerificationReportingProvider,
},
EcssTcInVecConverter, EcssTmtcError, GenericRoutingError, MpscTcReceiver,
@ -542,6 +581,8 @@ mod tests {
}
const TIMEOUT_ERROR_CODE: ResultU16 = ResultU16::new(1, 2);
const COMPLETION_ERROR_CODE: ResultU16 = ResultU16::new(2, 0);
const COMPLETION_ERROR_CODE_STEP: ResultU16 = ResultU16::new(2, 1);
#[derive(Default)]
pub struct TestReplyHandlerHook {
@ -573,8 +614,12 @@ mod tests {
let reply_handler_hook = TestReplyHandlerHook::default();
let shared_verif_map = SharedVerificationMap::default();
let test_verif_reporter = TestVerificationReporter::new(shared_verif_map.clone());
let reply_handler =
PusService8ReplyHandler::new(test_verif_reporter.clone(), 128, reply_handler_hook);
let reply_handler = PusService8ReplyHandler::new_with_init_time_now(
test_verif_reporter.clone(),
128,
reply_handler_hook,
)
.expect("creating reply handler failed");
Self {
verif_reporter: test_verif_reporter,
handler: reply_handler,
@ -605,17 +650,55 @@ mod tests {
token
}
pub fn assert_request_completion(&self, step: Option<u16>, request_id: RequestId) {
pub fn assert_request_completion_success(&self, step: Option<u16>, request_id: RequestId) {
let verif_info = self
.verif_reporter
.verification_info(&verification::RequestId::from(request_id))
.expect("no verification info found");
self.assert_request_completion_common(&verif_info, step, true)
}
pub fn assert_request_completion_failure(
&self,
step: Option<u16>,
request_id: RequestId,
fail_enum: ResultU16,
fail_data: &[u8],
) {
let verif_info = self
.verif_reporter
.verification_info(&verification::RequestId::from(request_id))
.expect("no verification info found");
self.assert_request_completion_common(&verif_info, step, false);
assert_eq!(verif_info.fail_enum.unwrap(), fail_enum.raw() as u64);
assert_eq!(verif_info.failure_data.unwrap(), fail_data);
}
pub fn assert_request_completion_common(
&self,
verif_info: &VerificationStatus,
step: Option<u16>,
completion_success: bool,
) {
if let Some(step) = step {
assert!(verif_info.step_status.is_some());
assert!(verif_info.step_status.unwrap());
assert_eq!(step, verif_info.step);
}
assert!(verif_info.completed.expect("request is not completed"));
assert_eq!(
verif_info.completed.expect("request is not completed"),
completion_success
);
}
pub fn assert_request_step_failure(&self, step: u16, request_id: RequestId) {
let verif_info = self
.verif_reporter
.verification_info(&verification::RequestId::from(request_id))
.expect("no verification info found");
assert!(verif_info.step_status.is_some());
assert!(!verif_info.step_status.unwrap());
assert_eq!(step, verif_info.step);
}
delegate! {
@ -633,6 +716,10 @@ mod tests {
token: VerificationToken<TcStateStarted>,
timeout: Duration,
);
pub fn update_time_from_now(&mut self) -> Result<(), SystemTimeError>;
pub fn check_for_timeouts(&mut self, time_stamp: &[u8]) -> Result<(), EcssTmtcError>;
}
to self.verif_reporter {
fn add_tc_with_req_id(&mut self, req_id: verification::RequestId) -> VerificationToken<TcStateNone>;
@ -703,7 +790,7 @@ mod tests {
}
#[test]
fn test_reply_handler() {
fn test_reply_handler_completion_success() {
let mut reply_testbench = Pus8ReplyTestbench::new();
let request_id = 0x02;
let action_id = 0x03;
@ -722,7 +809,7 @@ mod tests {
reply_testbench
.handle_action_reply(action_reply, &[])
.expect("reply handling failure");
reply_testbench.assert_request_completion(None, request_id);
reply_testbench.assert_request_completion_success(None, request_id);
}
#[test]
@ -753,6 +840,92 @@ mod tests {
reply_testbench
.handle_action_reply(action_reply, &[])
.expect("reply handling failure");
reply_testbench.assert_request_completion(Some(1), request_id);
reply_testbench.assert_request_completion_success(Some(1), request_id);
}
#[test]
fn test_reply_handler_completion_failure() {
let mut reply_testbench = Pus8ReplyTestbench::new();
let request_id = 0x02;
let action_id = 0x03;
let token = reply_testbench.init_handling_for_request(request_id, action_id);
reply_testbench.add_routed_request(
request_id.into(),
action_id,
token,
Duration::from_millis(1),
);
let params_raw = ParamsRaw::U32(params::U32(5));
let action_reply = ActionReplyPusWithIds {
request_id,
action_id,
reply: ActionReplyPus::CompletionFailed {
error_code: COMPLETION_ERROR_CODE,
params: params_raw.into(),
},
};
reply_testbench
.handle_action_reply(action_reply, &[])
.expect("reply handling failure");
reply_testbench.assert_request_completion_failure(
None,
request_id,
COMPLETION_ERROR_CODE,
&params_raw.to_vec().unwrap(),
);
}
#[test]
fn test_reply_handler_step_failure() {
let mut reply_testbench = Pus8ReplyTestbench::new();
let request_id = 0x02;
let action_id = 0x03;
let token = reply_testbench.init_handling_for_request(request_id, action_id);
reply_testbench.add_routed_request(
request_id.into(),
action_id,
token,
Duration::from_millis(1),
);
let action_reply = ActionReplyPusWithIds {
request_id,
action_id,
reply: ActionReplyPus::StepFailed {
error_code: COMPLETION_ERROR_CODE_STEP,
step: 2,
params: ParamsRaw::U32(crate::params::U32(5)).into(),
},
};
reply_testbench
.handle_action_reply(action_reply, &[])
.expect("reply handling failure");
reply_testbench.assert_request_step_failure(2, request_id);
}
#[test]
fn test_reply_handler_timeout_handling() {
let mut reply_testbench = Pus8ReplyTestbench::new();
let request_id = 0x02;
let action_id = 0x03;
let token = reply_testbench.init_handling_for_request(request_id, action_id);
reply_testbench.add_routed_request(
request_id.into(),
action_id,
token,
Duration::from_millis(1),
);
let timeout_param = Duration::from_millis(1).as_millis() as u64;
let timeout_param_raw = timeout_param.to_be_bytes();
std::thread::sleep(Duration::from_millis(2));
reply_testbench
.update_time_from_now()
.expect("time update failure");
reply_testbench.check_for_timeouts(&[]).unwrap();
reply_testbench.assert_request_completion_failure(
None,
request_id,
TIMEOUT_ERROR_CODE,
&timeout_param_raw,
);
}
}

View File

@ -298,9 +298,9 @@ impl<STATE> VerificationToken<STATE> {
/// Composite helper struct to pass failure parameters to the [VerificationReporter]
pub struct FailParams<'stamp, 'fargs> {
time_stamp: &'stamp [u8],
failure_code: &'fargs dyn EcssEnumeration,
failure_data: &'fargs [u8],
pub time_stamp: &'stamp [u8],
pub failure_code: &'fargs dyn EcssEnumeration,
pub failure_data: &'fargs [u8],
}
impl<'stamp, 'fargs> FailParams<'stamp, 'fargs> {
@ -326,8 +326,8 @@ impl<'stamp, 'fargs> FailParams<'stamp, 'fargs> {
/// Composite helper struct to pass step failure parameters to the [VerificationReporter]
pub struct FailParamsWithStep<'stamp, 'fargs> {
bp: FailParams<'stamp, 'fargs>,
step: &'fargs dyn EcssEnumeration,
pub params: FailParams<'stamp, 'fargs>,
pub step: &'fargs dyn EcssEnumeration,
}
impl<'stamp, 'fargs> FailParamsWithStep<'stamp, 'fargs> {
@ -338,7 +338,7 @@ impl<'stamp, 'fargs> FailParamsWithStep<'stamp, 'fargs> {
failure_data: &'fargs [u8],
) -> Self {
Self {
bp: FailParams::new(time_stamp, failure_code, failure_data),
params: FailParams::new(time_stamp, failure_code, failure_data),
step,
}
}
@ -783,7 +783,7 @@ impl VerificationReporterCore {
msg_count,
&token.req_id,
Some(params.step),
&params.bp,
&params.params,
)
.map_err(|e| VerificationErrorWithToken(e, token))?,
token,
@ -1601,12 +1601,15 @@ pub mod tests {
fn step_failure(
&self,
token: VerificationToken<super::TcStateStarted>,
_params: FailParamsWithStep,
params: FailParamsWithStep,
) -> Result<(), super::VerificationOrSendErrorWithToken<super::TcStateStarted>> {
let verif_map = self.verification_map.lock().unwrap();
match verif_map.borrow_mut().get_mut(&token.req_id) {
Some(entry) => {
entry.step_status = Some(false);
entry.step = params.step.value().try_into().unwrap();
entry.failure_data = Some(params.params.failure_data.to_vec());
entry.fail_enum = Some(params.params.failure_code.value());
}
None => panic!("unexpected start success for request ID {}", token.req_id()),
};