Skip to content

Commit

Permalink
[#96] Add tests and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
elfenpiff committed Jan 27, 2024
1 parent 7fc4b36 commit 0a7eebd
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 4 deletions.
2 changes: 1 addition & 1 deletion iceoryx2-cal/src/monitoring/file_lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ impl NamedConceptMgmt for FileLockMonitoring {
"Unable to list all FileLockMonitoring instances in \"{}\"",
path
);
let directory = match Directory::new(&path) {
let directory = match Directory::new(path) {
Ok(directory) => directory,
Err(DirectoryOpenError::InsufficientPermissions) => {
fail!(from origin, with NamedConceptListError::InsufficientPermissions,
Expand Down
60 changes: 57 additions & 3 deletions iceoryx2-cal/src/monitoring/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,59 +10,113 @@
//
// SPDX-License-Identifier: Apache-2.0 OR MIT

use iceoryx2_bb_container::semantic_string::SemanticString;
use iceoryx2_bb_system_types::file_name::FileName;
//! Allows one process to monitor the state of another process. Can detect if the process is
//! [`State::Alive`], [`State::Dead`] or the existance with [`State::DoesNotExist`]. To activate
//! monitoring the process that shall be monitored must instantiate a [`MonitoringToken`]. As long
//! as the [`MonitoringToken`] is in scope the [`MonitoringMonitor`] will detect the process as
//! [`State::Alive`]. When the process crashes it will be detected as [`State::Dead`]. If the
//! process does not yet have instantiated a [`MonitoringMonitor`] the process is identified as
//! [`State::DoesNotExist`].
//!
//! # Example
//!
//! ```
//! use iceoryx2_cal::monitoring::*;
//!
//! fn monitored_process<M: Monitoring>() {
//! let token = M::Builder::new(FileName::new(b"unique_process_identifier").unwrap()).unwrap();
//!
//! // keep the token in scope and do what a process shall do
//!
//! // process can no longer be monitored
//! drop(token);
//! }
//!
//! fn watching_process<M: Monitoring>() {
//! let monitor = M::Builder::new(FileName::new(b"unique_process_identifier").unwrap()).unwrap();
//!
//! match monitor.state().unwrap() {
//! State::Alive => println!("process is alive"),
//! State::Dead => println!("process is dead"),
//! State::DoesNotExist => println!("process does not exist"),
//! }
//! }
//! ```
use crate::{
pub use iceoryx2_bb_container::semantic_string::SemanticString;
pub use iceoryx2_bb_system_types::file_name::FileName;

pub use crate::{
named_concept::NamedConcept, named_concept::NamedConceptBuilder,
named_concept::NamedConceptMgmt,
};

pub mod file_lock;

/// Represents the state of a monitored process.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum State {
Alive,
Dead,
DoesNotExist,
}

/// Represents the possible errors that can occur when a new [`MonitoringToken`] is created with
/// [`MonitoringBuilder::create()`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MonitoringCreateTokenError {
InsufficientPermissions,
AlreadyExists,
InternalError,
}

/// Represents the possible errors that can occur when a new [`MonitoringMonitor`] is created with
/// [`MonitoringBuilder::monitor()`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MonitoringCreateMonitorError {
InsufficientPermissions,
Interrupt,
InternalError,
}

/// Represents the possible errors that can occur when the [`State`] is acquired via
/// [`MonitoringMonitor::state()`].
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MonitoringStateError {
Interrupt,
InternalError,
}

/// The token enables a process to be monitored by another process.
pub trait MonitoringToken: NamedConcept {}

/// The monitor allows to monitor another process that has instantiated a [`MonitoringToken`]
pub trait MonitoringMonitor: NamedConcept {
/// Returns the current [`State`] of the monitored process. On failure it returns
/// [`MonitoringStateError`].
fn state(&self) -> Result<State, MonitoringStateError>;
}

/// Creates either a [`MonitoringToken`] or instantiates a [`MonitoringMonitor`] that can monitor
/// the state of a token.
pub trait MonitoringBuilder<T: Monitoring>: NamedConceptBuilder<T> {
/// Creates a new [`MonitoringToken`] on success or returns a [`MonitoringCreateTokenError`]
/// on failure.
fn create(self) -> Result<T::Token, MonitoringCreateTokenError>;

/// Instantiates a [`MonitoringMonitor`] to monitor a [`MonitoringToken`]
fn monitor(self) -> Result<T::Monitor, MonitoringCreateMonitorError>;
}

/// Concept that allows to monitor a process from within another process. The process must hereby
/// instantiate a [`MonitoringToken`] with [`MonitoringBuilder`] so that it can be monitored with
/// the [`MonitoringMonitor`].
pub trait Monitoring: NamedConceptMgmt + Sized {
type Token: MonitoringToken;
type Monitor: MonitoringMonitor;
type Builder: MonitoringBuilder<Self>;

/// Returns the default suffix that shall be used for every [`MonitoringToken`].
fn default_suffix() -> FileName {
unsafe { FileName::new_unchecked(b".monitor") }
}
Expand Down
93 changes: 93 additions & 0 deletions iceoryx2-cal/tests/monitoring_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,99 @@ mod monitoring {
assert_that!(sut_monitor.state().unwrap(), eq State::DoesNotExist);
}

#[test]
fn list_monitoring_token_works<Sut: Monitoring>() {
let mut sut_names = vec![];
const LIMIT: usize = 10;

assert_that!(<Sut as NamedConceptMgmt>::list().unwrap(), len 0);
for i in 0..LIMIT {
sut_names.push(generate_name());
assert_that!(<Sut as NamedConceptMgmt>::does_exist(&sut_names[i]), eq Ok(false));
std::mem::forget(Sut::Builder::new(&sut_names[i]).create());
assert_that!(<Sut as NamedConceptMgmt>::does_exist(&sut_names[i]), eq Ok(true));

let list = <Sut as NamedConceptMgmt>::list().unwrap();
assert_that!(list, len i + 1);
let does_exist_in_list = |value| {
for e in &list {
if e == value {
return true;
}
}
false
};

for name in &sut_names {
assert_that!(does_exist_in_list(name), eq true);
}
}

for i in 0..LIMIT {
assert_that!(unsafe{<Sut as NamedConceptMgmt>::remove(&sut_names[i])}, eq Ok(true));
assert_that!(unsafe{<Sut as NamedConceptMgmt>::remove(&sut_names[i])}, eq Ok(false));
}

assert_that!(<Sut as NamedConceptMgmt>::list().unwrap(), len 0);
}

#[test]
fn custom_suffix_keeps_monitoring_token_separated<Sut: Monitoring>() {
let config_1 = <Sut as NamedConceptMgmt>::Configuration::default()
.suffix(unsafe { FileName::new_unchecked(b".suffix_1") });
let config_2 = <Sut as NamedConceptMgmt>::Configuration::default()
.suffix(unsafe { FileName::new_unchecked(b".suffix_2") });

let sut_name = generate_name();

assert_that!(<Sut as NamedConceptMgmt>::does_exist_cfg(&sut_name, &config_1), eq Ok(false));
assert_that!(<Sut as NamedConceptMgmt>::does_exist_cfg(&sut_name, &config_2), eq Ok(false));
assert_that!(<Sut as NamedConceptMgmt>::list_cfg(&config_1).unwrap(), len 0);
assert_that!(<Sut as NamedConceptMgmt>::list_cfg(&config_2).unwrap(), len 0);

let sut_1 = Sut::Builder::new(&sut_name)
.config(&config_1)
.create()
.unwrap();

assert_that!(<Sut as NamedConceptMgmt>::does_exist_cfg(&sut_name, &config_1), eq Ok(true));
assert_that!(<Sut as NamedConceptMgmt>::does_exist_cfg(&sut_name, &config_2), eq Ok(false));
assert_that!(<Sut as NamedConceptMgmt>::list_cfg(&config_1).unwrap(), len 1);
assert_that!(<Sut as NamedConceptMgmt>::list_cfg(&config_2).unwrap(), len 0);

let sut_2 = Sut::Builder::new(&sut_name)
.config(&config_2)
.create()
.unwrap();

assert_that!(<Sut as NamedConceptMgmt>::does_exist_cfg(&sut_name, &config_1), eq Ok(true));
assert_that!(<Sut as NamedConceptMgmt>::does_exist_cfg(&sut_name, &config_2), eq Ok(true));
assert_that!(<Sut as NamedConceptMgmt>::list_cfg(&config_1).unwrap(), len 1);
assert_that!(<Sut as NamedConceptMgmt>::list_cfg(&config_2).unwrap(), len 1);

assert_that!(<Sut as NamedConceptMgmt>::list_cfg(&config_1).unwrap()[0], eq sut_name);
assert_that!(<Sut as NamedConceptMgmt>::list_cfg(&config_2).unwrap()[0], eq sut_name);

assert_that!(*sut_1.name(), eq sut_name);
assert_that!(*sut_2.name(), eq sut_name);

std::mem::forget(sut_1);
std::mem::forget(sut_2);

assert_that!(unsafe {<Sut as NamedConceptMgmt>::remove_cfg(&sut_name, &config_1)}, eq Ok(true));
assert_that!(unsafe {<Sut as NamedConceptMgmt>::remove_cfg(&sut_name, &config_1)}, eq Ok(false));
assert_that!(unsafe {<Sut as NamedConceptMgmt>::remove_cfg(&sut_name, &config_2)}, eq Ok(true));
assert_that!(unsafe {<Sut as NamedConceptMgmt>::remove_cfg(&sut_name, &config_2)}, eq Ok(false));
}

#[test]
fn defaults_for_configuration_are_set_correctly<Sut: Monitoring>() {
let config = <Sut as NamedConceptMgmt>::Configuration::default();
assert_that!(*config.get_suffix(), eq Sut::default_suffix());
assert_that!(*config.get_path_hint(), eq Sut::default_path_hint());
assert_that!(*config.get_prefix(), eq Sut::default_prefix());
}

#[instantiate_tests(<iceoryx2_cal::monitoring::file_lock::FileLockMonitoring>)]
mod file_lock {}
}

0 comments on commit 0a7eebd

Please sign in to comment.