Threading/Executable module #1
18
.idea/runConfigurations/Test_All.xml
Normal file
18
.idea/runConfigurations/Test_All.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Test All" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="test -- --include-ignored" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<envs />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
@ -26,6 +26,7 @@ pub trait Executable: Send {
|
||||
}
|
||||
|
||||
/// This function allows executing one task which implements the [Executable][Executable] trait
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `executable`: Executable task
|
||||
@ -74,6 +75,7 @@ pub fn exec_sched_single<
|
||||
|
||||
/// This function allows executing multiple tasks as long as the tasks implement the
|
||||
/// [Executable][Executable] trait
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `executable_vec`: Vector of executable objects
|
||||
@ -134,10 +136,11 @@ pub fn exec_sched_multi<
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{exec_sched_multi, exec_sched_single, Executable, ExecutionType, OpResult};
|
||||
use bus::Bus;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use std::{fmt, thread};
|
||||
|
||||
struct TestInfo {
|
||||
exec_num: u32,
|
||||
@ -154,32 +157,41 @@ mod tests {
|
||||
exec_num: Arc<Mutex<TestInfo>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Clone, Debug)]
|
||||
struct ExampleError {
|
||||
details: String,
|
||||
code: i32,
|
||||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
/// The kind of an error that can occur.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ErrorKind {
|
||||
Generic(String, i32)
|
||||
}
|
||||
|
||||
impl ExampleError {
|
||||
fn new(msg: &str, code: i32) -> ExampleError {
|
||||
ExampleError {
|
||||
details: msg.to_string(),
|
||||
code,
|
||||
kind: ErrorKind::Generic(msg.to_string(), code)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the kind of this error.
|
||||
pub fn kind(&self) -> &ErrorKind {
|
||||
&self.kind
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExampleError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.details)
|
||||
match self.kind() {
|
||||
ErrorKind::Generic(str, code) => {
|
||||
write!(f, "{str} with code {code}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ExampleError {
|
||||
fn description(&self) -> &str {
|
||||
&self.details
|
||||
}
|
||||
}
|
||||
impl Error for ExampleError {}
|
||||
|
||||
const ONE_SHOT_TASK_NAME: &str = "One Shot Task";
|
||||
|
||||
@ -233,6 +245,8 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
const PERIODIC_TASK_NAME: &str = "Periodic Task";
|
||||
|
||||
impl Executable for PeriodicTask {
|
||||
type Error = ExampleError;
|
||||
|
||||
@ -241,7 +255,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn task_name(&self) -> &'static str {
|
||||
"Periodic Task"
|
||||
PERIODIC_TASK_NAME
|
||||
}
|
||||
|
||||
fn periodic_op(&mut self, op_code: i32) -> Result<OpResult, ExampleError> {
|
||||
@ -282,6 +296,41 @@ mod tests {
|
||||
assert_eq!(data.op_code, expected_op_code);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_failed_one_shot() {
|
||||
let op_code_inducing_failure = -1;
|
||||
let shared = Arc::new(Mutex::new(TestInfo {
|
||||
exec_num: 0,
|
||||
op_code: 0,
|
||||
}));
|
||||
let exec_task = OneShotTask {
|
||||
exec_num: shared.clone(),
|
||||
};
|
||||
let task = Box::new(exec_task);
|
||||
let jhandle = exec_sched_single(
|
||||
task,
|
||||
Some(Duration::from_millis(100)),
|
||||
op_code_inducing_failure,
|
||||
None,
|
||||
);
|
||||
let thread_res = jhandle.join().expect("One Shot Task failed");
|
||||
assert!(thread_res.is_err());
|
||||
let error = thread_res.unwrap_err();
|
||||
let err = error.kind();
|
||||
assert!(matches!(err, &ErrorKind::Generic { .. }));
|
||||
match err {
|
||||
ErrorKind::Generic(str, op_code) => {
|
||||
assert_eq!(str, &String::from("One Shot Task Failure"));
|
||||
assert_eq!(op_code, &op_code_inducing_failure);
|
||||
}
|
||||
}
|
||||
let error_display = error.to_string();
|
||||
assert_eq!(error_display, "One Shot Task Failure with code -1");
|
||||
let data = shared.lock().expect("Locking Mutex failed");
|
||||
assert_eq!(data.exec_num, 1);
|
||||
assert_eq!(data.op_code, op_code_inducing_failure);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simple_multi_one_shot() {
|
||||
let expected_op_code = 43;
|
||||
@ -314,7 +363,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cycles() {
|
||||
fn test_cycles_single() {
|
||||
let expected_op_code = 44;
|
||||
let shared = Arc::new(Mutex::new(TestInfo {
|
||||
exec_num: 0,
|
||||
@ -325,7 +374,12 @@ mod tests {
|
||||
cycles: 1,
|
||||
});
|
||||
assert_eq!(cycled_task.task_name(), CYCLE_TASK_NAME);
|
||||
let jh = exec_sched_single(cycled_task, Some(Duration::from_millis(100)), expected_op_code, None);
|
||||
let jh = exec_sched_single(
|
||||
cycled_task,
|
||||
Some(Duration::from_millis(100)),
|
||||
expected_op_code,
|
||||
None,
|
||||
);
|
||||
let thread_res = jh.join().expect("Cycles Task failed");
|
||||
assert!(thread_res.is_ok());
|
||||
let data = shared.lock().expect("Locking Mutex failed");
|
||||
@ -334,22 +388,107 @@ mod tests {
|
||||
assert_eq!(data.op_code, expected_op_code);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_and_cycles() {
|
||||
let expected_op_code = 50;
|
||||
let shared = Arc::new(Mutex::new(TestInfo {
|
||||
exec_num: 0,
|
||||
op_code: 0,
|
||||
}));
|
||||
let one_shot_task = Box::new(OneShotTask {
|
||||
exec_num: shared.clone(),
|
||||
});
|
||||
let cycled_task_0 = Box::new(FixedCyclesTask {
|
||||
exec_num: shared.clone(),
|
||||
cycles: 1,
|
||||
});
|
||||
let cycled_task_1 = Box::new(FixedCyclesTask {
|
||||
exec_num: shared.clone(),
|
||||
cycles: 1,
|
||||
});
|
||||
assert_eq!(cycled_task_0.task_name(), CYCLE_TASK_NAME);
|
||||
assert_eq!(one_shot_task.task_name(), ONE_SHOT_TASK_NAME);
|
||||
let task_vec: Vec<Box<dyn Executable<Error=ExampleError>>> = vec![one_shot_task, cycled_task_0, cycled_task_1];
|
||||
let jh = exec_sched_multi(
|
||||
task_vec,
|
||||
Some(Duration::from_millis(100)),
|
||||
expected_op_code,
|
||||
None,
|
||||
);
|
||||
let thread_res = jh.join().expect("Cycles Task failed");
|
||||
assert!(thread_res.is_ok());
|
||||
let data = shared.lock().expect("Locking Mutex failed");
|
||||
assert_eq!(thread_res.unwrap(), OpResult::Ok);
|
||||
assert_eq!(data.exec_num, 3);
|
||||
assert_eq!(data.op_code, expected_op_code);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_periodic() {
|
||||
fn test_periodic_single() {
|
||||
let mut terminator = Bus::new(5);
|
||||
let expected_op_code = 45;
|
||||
let shared = Arc::new(Mutex::new(TestInfo {
|
||||
exec_num: 0,
|
||||
op_code: 0,
|
||||
}));
|
||||
let periodic_task = Box::new(PeriodicTask {
|
||||
exec_num: shared.clone()
|
||||
exec_num: shared.clone(),
|
||||
});
|
||||
let jh = exec_sched_single(periodic_task, Some(Duration::from_millis(50)), expected_op_code, None);
|
||||
assert_eq!(periodic_task.task_name(), PERIODIC_TASK_NAME);
|
||||
let jh = exec_sched_single(
|
||||
periodic_task,
|
||||
Some(Duration::from_millis(20)),
|
||||
expected_op_code,
|
||||
Some(terminator.add_rx()),
|
||||
);
|
||||
thread::sleep(Duration::from_millis(40));
|
||||
terminator.broadcast(());
|
||||
let thread_res = jh.join().expect("Periodic Task failed");
|
||||
assert!(thread_res.is_ok());
|
||||
let data = shared.lock().expect("Locking Mutex failed");
|
||||
assert_eq!(thread_res.unwrap(), OpResult::Ok);
|
||||
let range = 2..4;
|
||||
assert!(range.contains(&data.exec_num));
|
||||
assert_eq!(data.op_code, expected_op_code);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_periodic_multi() {
|
||||
let mut terminator = Bus::new(5);
|
||||
let expected_op_code = 46;
|
||||
let shared = Arc::new(Mutex::new(TestInfo {
|
||||
exec_num: 0,
|
||||
op_code: 0,
|
||||
}));
|
||||
let cycled_task = Box::new(FixedCyclesTask {
|
||||
exec_num: shared.clone(),
|
||||
cycles: 1,
|
||||
});
|
||||
let periodic_task_0 = Box::new(PeriodicTask {
|
||||
exec_num: shared.clone(),
|
||||
});
|
||||
let periodic_task_1 = Box::new(PeriodicTask {
|
||||
exec_num: shared.clone(),
|
||||
});
|
||||
assert_eq!(periodic_task_0.task_name(), PERIODIC_TASK_NAME);
|
||||
assert_eq!(periodic_task_1.task_name(), PERIODIC_TASK_NAME);
|
||||
let task_vec: Vec<Box<dyn Executable<Error=ExampleError>>> = vec![cycled_task, periodic_task_0, periodic_task_1];
|
||||
let jh = exec_sched_multi(
|
||||
task_vec,
|
||||
Some(Duration::from_millis(20)),
|
||||
expected_op_code,
|
||||
Some(terminator.add_rx()),
|
||||
);
|
||||
thread::sleep(Duration::from_millis(60));
|
||||
terminator.broadcast(());
|
||||
let thread_res = jh.join().expect("Periodic Task failed");
|
||||
assert!(thread_res.is_ok());
|
||||
let data = shared.lock().expect("Locking Mutex failed");
|
||||
assert_eq!(thread_res.unwrap(), OpResult::Ok);
|
||||
let range = 7..11;
|
||||
assert!(range.contains(&data.exec_num));
|
||||
assert_eq!(data.op_code, expected_op_code);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user