From 0a7eebdf7e624fb553d36db9161df0260a9e0c7c Mon Sep 17 00:00:00 2001 From: Christian Eltzschig Date: Sat, 27 Jan 2024 03:21:36 +0100 Subject: [PATCH] [#96] Add tests and documentation --- iceoryx2-cal/src/monitoring/file_lock.rs | 2 +- iceoryx2-cal/src/monitoring/mod.rs | 60 ++++++++++++++- iceoryx2-cal/tests/monitoring_tests.rs | 93 ++++++++++++++++++++++++ 3 files changed, 151 insertions(+), 4 deletions(-) diff --git a/iceoryx2-cal/src/monitoring/file_lock.rs b/iceoryx2-cal/src/monitoring/file_lock.rs index 4dfa7fb6c..614a3a70b 100644 --- a/iceoryx2-cal/src/monitoring/file_lock.rs +++ b/iceoryx2-cal/src/monitoring/file_lock.rs @@ -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, diff --git a/iceoryx2-cal/src/monitoring/mod.rs b/iceoryx2-cal/src/monitoring/mod.rs index ca87d4eaf..e779b9d6a 100644 --- a/iceoryx2-cal/src/monitoring/mod.rs +++ b/iceoryx2-cal/src/monitoring/mod.rs @@ -10,16 +10,50 @@ // // 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() { +//! 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() { +//! 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, @@ -27,6 +61,8 @@ pub enum State { 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, @@ -34,6 +70,8 @@ pub enum MonitoringCreateTokenError { 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, @@ -41,28 +79,44 @@ pub enum MonitoringCreateMonitorError { 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; } +/// Creates either a [`MonitoringToken`] or instantiates a [`MonitoringMonitor`] that can monitor +/// the state of a token. pub trait MonitoringBuilder: NamedConceptBuilder { + /// Creates a new [`MonitoringToken`] on success or returns a [`MonitoringCreateTokenError`] + /// on failure. fn create(self) -> Result; + + /// Instantiates a [`MonitoringMonitor`] to monitor a [`MonitoringToken`] fn monitor(self) -> Result; } +/// 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; + /// Returns the default suffix that shall be used for every [`MonitoringToken`]. fn default_suffix() -> FileName { unsafe { FileName::new_unchecked(b".monitor") } } diff --git a/iceoryx2-cal/tests/monitoring_tests.rs b/iceoryx2-cal/tests/monitoring_tests.rs index 941a5e29b..2714755df 100644 --- a/iceoryx2-cal/tests/monitoring_tests.rs +++ b/iceoryx2-cal/tests/monitoring_tests.rs @@ -83,6 +83,99 @@ mod monitoring { assert_that!(sut_monitor.state().unwrap(), eq State::DoesNotExist); } + #[test] + fn list_monitoring_token_works() { + let mut sut_names = vec![]; + const LIMIT: usize = 10; + + assert_that!(::list().unwrap(), len 0); + for i in 0..LIMIT { + sut_names.push(generate_name()); + assert_that!(::does_exist(&sut_names[i]), eq Ok(false)); + std::mem::forget(Sut::Builder::new(&sut_names[i]).create()); + assert_that!(::does_exist(&sut_names[i]), eq Ok(true)); + + let list = ::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{::remove(&sut_names[i])}, eq Ok(true)); + assert_that!(unsafe{::remove(&sut_names[i])}, eq Ok(false)); + } + + assert_that!(::list().unwrap(), len 0); + } + + #[test] + fn custom_suffix_keeps_monitoring_token_separated() { + let config_1 = ::Configuration::default() + .suffix(unsafe { FileName::new_unchecked(b".suffix_1") }); + let config_2 = ::Configuration::default() + .suffix(unsafe { FileName::new_unchecked(b".suffix_2") }); + + let sut_name = generate_name(); + + assert_that!(::does_exist_cfg(&sut_name, &config_1), eq Ok(false)); + assert_that!(::does_exist_cfg(&sut_name, &config_2), eq Ok(false)); + assert_that!(::list_cfg(&config_1).unwrap(), len 0); + assert_that!(::list_cfg(&config_2).unwrap(), len 0); + + let sut_1 = Sut::Builder::new(&sut_name) + .config(&config_1) + .create() + .unwrap(); + + assert_that!(::does_exist_cfg(&sut_name, &config_1), eq Ok(true)); + assert_that!(::does_exist_cfg(&sut_name, &config_2), eq Ok(false)); + assert_that!(::list_cfg(&config_1).unwrap(), len 1); + assert_that!(::list_cfg(&config_2).unwrap(), len 0); + + let sut_2 = Sut::Builder::new(&sut_name) + .config(&config_2) + .create() + .unwrap(); + + assert_that!(::does_exist_cfg(&sut_name, &config_1), eq Ok(true)); + assert_that!(::does_exist_cfg(&sut_name, &config_2), eq Ok(true)); + assert_that!(::list_cfg(&config_1).unwrap(), len 1); + assert_that!(::list_cfg(&config_2).unwrap(), len 1); + + assert_that!(::list_cfg(&config_1).unwrap()[0], eq sut_name); + assert_that!(::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 {::remove_cfg(&sut_name, &config_1)}, eq Ok(true)); + assert_that!(unsafe {::remove_cfg(&sut_name, &config_1)}, eq Ok(false)); + assert_that!(unsafe {::remove_cfg(&sut_name, &config_2)}, eq Ok(true)); + assert_that!(unsafe {::remove_cfg(&sut_name, &config_2)}, eq Ok(false)); + } + + #[test] + fn defaults_for_configuration_are_set_correctly() { + let config = ::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()] mod file_lock {} }