use super::{
meta_data::{VerificationMetaData, VerificationMetaDataList},
result::VerificationResult,
VerificationError, VerificationStatus,
};
use crate::{
config::VerifierConfig,
file_structure::{VerificationDirectory, VerificationDirectoryTrait},
verification::VerificationErrorImpl,
};
use std::time::{Duration, SystemTime};
use tracing::{error, info, warn};
#[allow(clippy::type_complexity)]
pub struct Verification<'a, D: VerificationDirectoryTrait> {
meta_data: &'a VerificationMetaData,
status: VerificationStatus,
verification_fn:
Box<dyn Fn(&D, &'static VerifierConfig, &mut VerificationResult) + Send + Sync>,
duration: Option<Duration>,
result: Box<VerificationResult>,
config: &'static VerifierConfig,
}
impl<'a> Verification<'a, VerificationDirectory> {
pub fn new(
id: &str,
name: &str,
verification_fn: impl Fn(&VerificationDirectory, &'static VerifierConfig, &mut VerificationResult)
+ Send
+ Sync
+ 'static,
metadata_list: &'a VerificationMetaDataList,
config: &'static VerifierConfig,
) -> Result<Self, VerificationError> {
let meta_data = match metadata_list.meta_data_from_id(id) {
Some(m) => m,
None => {
return Err(VerificationError::from(
VerificationErrorImpl::MetadataNotFound { id: id.to_string() },
))
}
};
if name != meta_data.name() {
return Err(VerificationError::from(
VerificationErrorImpl::NameMismatch {
name: meta_data.name().to_string(),
id: id.to_string(),
input_name: name.to_string(),
},
));
}
Ok(Verification {
meta_data,
status: VerificationStatus::NotStarted,
verification_fn: Box::new(verification_fn),
duration: None,
result: Box::new(VerificationResult::new()),
config,
})
}
pub fn id(&self) -> &str {
self.meta_data.id()
}
pub fn meta_data(&'a self) -> &'a VerificationMetaData {
self.meta_data
}
pub fn status(&self) -> VerificationStatus {
self.status
}
pub fn verification_result(&self) -> &VerificationResult {
&self.result
}
pub fn is_result_final(&self) -> bool {
!matches!(
self.status,
VerificationStatus::NotStarted | VerificationStatus::Running
)
}
pub fn has_errors(&self) -> Option<bool> {
match self.is_result_final() {
true => Some(self.result.has_errors()),
false => None,
}
}
pub fn has_failures(&self) -> Option<bool> {
match self.is_result_final() {
true => Some(self.result.has_failures()),
false => None,
}
}
pub fn is_ok(&self) -> Option<bool> {
match self.is_result_final() {
true => Some(self.result.is_ok()),
false => None,
}
}
pub fn run(&mut self, directory: &VerificationDirectory) {
self.status = VerificationStatus::Running;
let start_time = SystemTime::now();
info!(
"Verification {} ({}) started",
self.meta_data.name(),
self.meta_data.id()
);
(self.verification_fn)(directory, self.config, self.result.as_mut());
self.duration = Some(start_time.elapsed().unwrap());
self.status = VerificationStatus::calculate_finished(
self.result.has_errors(),
self.result.has_failures(),
);
if self.is_ok().unwrap() {
info!(
"Verification {} ({}) finished successfully. Duration: {}s",
self.meta_data.name(),
self.meta_data.id(),
self.duration.unwrap().as_secs_f32()
);
}
if self.has_errors().unwrap() {
error!(
"Verification {} ({}) finished with errors. Duration: {}s \n{}",
self.meta_data.name(),
self.meta_data.id(),
self.duration.unwrap().as_secs_f32(),
self.result.errors_to_string().join("\n")
);
}
if self.has_failures().unwrap() {
warn!(
"Verification {} ({}) finished with failures. Duration: {}s \n{}",
self.meta_data.name(),
self.meta_data.id(),
self.duration.unwrap().as_secs_f32(),
self.result.failures_to_string().join("\n")
);
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
config::test::CONFIG_TEST,
verification::{result::VerificationEvent, VerificationPeriod},
};
use std::path::Path;
#[test]
fn test_creation() {
fn ok(_: &VerificationDirectory, _: &'static VerifierConfig, _: &mut VerificationResult) {}
let md_list =
VerificationMetaDataList::load(CONFIG_TEST.get_verification_list_str()).unwrap();
assert!(Verification::new(
"01.01",
"VerifySetupCompleteness",
ok,
&md_list,
&CONFIG_TEST,
)
.is_ok());
assert!(Verification::new(
"20.01",
"VerifySetupCompleteness",
ok,
&md_list,
&CONFIG_TEST,
)
.is_err());
assert!(Verification::new("01.01", "Toto", ok, &md_list, &CONFIG_TEST,).is_err());
}
#[test]
fn run_ok() {
fn ok(_: &VerificationDirectory, _: &'static VerifierConfig, _: &mut VerificationResult) {}
let md_list =
VerificationMetaDataList::load(CONFIG_TEST.get_verification_list_str()).unwrap();
let mut verif = Verification::new(
"01.01",
"VerifySetupCompleteness",
ok,
&md_list,
&CONFIG_TEST,
)
.unwrap();
assert_eq!(verif.status, VerificationStatus::NotStarted);
assert!(!verif.is_result_final());
assert!(verif.is_ok().is_none());
assert!(verif.has_errors().is_none());
assert!(verif.has_failures().is_none());
verif.run(&VerificationDirectory::new(
&VerificationPeriod::Setup,
Path::new("."),
));
assert_eq!(verif.status, VerificationStatus::FinishedSuccessfully);
assert!(verif.is_result_final());
assert!(verif.is_ok().unwrap());
assert!(!verif.has_errors().unwrap());
assert!(!verif.has_failures().unwrap());
}
#[test]
fn run_error() {
fn error(
_: &VerificationDirectory,
_: &'static VerifierConfig,
result: &mut VerificationResult,
) {
result.push(VerificationEvent::new_error("toto"));
result.push(VerificationEvent::new_error("toto2"));
result.push(VerificationEvent::new_failure("toto3"));
}
let md_list =
VerificationMetaDataList::load(CONFIG_TEST.get_verification_list_str()).unwrap();
let mut verif = Verification::new(
"01.01",
"VerifySetupCompleteness",
error,
&md_list,
&CONFIG_TEST,
)
.unwrap();
assert_eq!(verif.status, VerificationStatus::NotStarted);
assert!(!verif.is_result_final());
assert!(verif.is_ok().is_none());
assert!(verif.has_errors().is_none());
assert!(verif.has_failures().is_none());
verif.run(&VerificationDirectory::new(
&VerificationPeriod::Setup,
Path::new("."),
));
assert_eq!(
verif.status,
VerificationStatus::FinishedWithFailuresAndErrors
);
assert!(verif.is_result_final());
assert!(!verif.is_ok().unwrap());
assert!(verif.has_errors().unwrap());
assert!(verif.has_failures().unwrap());
assert_eq!(verif.verification_result().errors().len(), 2);
assert_eq!(verif.verification_result().failures().len(), 1);
}
#[test]
fn run_failure() {
fn failure(
_: &VerificationDirectory,
_: &'static VerifierConfig,
result: &mut VerificationResult,
) {
result.push(VerificationEvent::new_failure("toto"));
result.push(VerificationEvent::new_failure("toto2"));
}
let md_list =
VerificationMetaDataList::load(CONFIG_TEST.get_verification_list_str()).unwrap();
let mut verif = Verification::new(
"01.01",
"VerifySetupCompleteness",
failure,
&md_list,
&CONFIG_TEST,
)
.unwrap();
assert_eq!(verif.status, VerificationStatus::NotStarted);
assert!(!verif.is_result_final());
assert!(verif.is_ok().is_none());
assert!(verif.has_errors().is_none());
assert!(verif.has_failures().is_none());
verif.run(&VerificationDirectory::new(
&VerificationPeriod::Setup,
Path::new("."),
));
assert_eq!(verif.status, VerificationStatus::FinishedWithFailures);
assert!(verif.is_result_final());
assert!(!verif.is_ok().unwrap());
assert!(!verif.has_errors().unwrap());
assert!(verif.has_failures().unwrap());
assert_eq!(verif.verification_result().errors().len(), 0);
assert_eq!(verif.verification_result().failures().len(), 2);
}
}