use std::collections::HashMap;
use anyhow::anyhow;
use clap::Parser;
use scan_core::{Oracle, Scan, Time, TransitionSystemGenerator};
use super::report::Report;
const NO_PROPS_ERR: &str = "missing properties to verify.
Pass a (space-separated) list of one or more properties specified in the model to be verified.
Alternatively, use the flag --all to verify all properties at once.\n
Examples:
'scan PATH/TO/MODEL verify PROPERTY' verifies the property PROPERTY over the model
'scan PATH/TO/MODEL verify PROPERTY_1 PROPERTY_2' verifies the properties PROPERTY_1 and PROPERTY_2 together over the model
'scan PATH/TO/MODEL verify --all' verifies all specified properties together over the model";
const ALL_PROPS_ERR: &str =
"the --all flag is incompatible with individually-specified properties.\n
Examples:
'scan PATH/TO/MODEL verify PROPERTY' verifies the property PROPERTY over the model
'scan PATH/TO/MODEL verify PROPERTY_1 PROPERTY_2' verifies the properties PROPERTY_1 and PROPERTY_2 together over the model
'scan PATH/TO/MODEL verify --all' verifies all specified properties together over the model";
const BAD_CONFIDENCE: &str =
"confidence has to be a value strictly greater than 0 and strictly less than 1.\n
Example:
'scan PATH/TO/MODEL verify --confidence 0.98 PROPERTY' verifies the property PROPERTY with confidence 0.98 (i.e., 98%)";
const BAD_PRECISION: &str =
"precision has to be a value strictly greater than 0 and strictly less than 1.\n
Example:
'scan PATH/TO/MODEL verify --precision 0.005 PROPERTY' verifies the property PROPERTY with precision 0.005 (i.e., 0.5%)";
#[derive(Debug, Clone, Parser)]
#[deny(missing_docs)]
pub(crate) struct VerifyArgs {
pub(crate) properties: Vec<String>,
#[arg(short, long)]
pub(crate) all: bool,
#[arg(short, long, default_value_t = 0.95)]
pub(crate) confidence: f64,
#[arg(short, long, default_value_t = 0.01)]
pub(crate) precision: f64,
#[arg(short, long, default_value_t = 10000)]
pub(crate) duration: Time,
#[arg(long)]
pub(crate) single_thread: bool,
}
impl VerifyArgs {
pub(crate) fn validate(&self) -> anyhow::Result<()> {
if self.properties.is_empty() && !self.all {
Err(anyhow!(NO_PROPS_ERR))
} else if !self.properties.is_empty() && self.all {
Err(anyhow!(ALL_PROPS_ERR))
} else if 0f64 >= self.confidence || self.confidence >= 1f64 {
Err(anyhow!(BAD_CONFIDENCE))
} else if 0f64 >= self.precision || self.precision >= 1f64 {
Err(anyhow!(BAD_PRECISION))
} else {
Ok(())
}
}
pub(crate) fn verify<'a, Ts, O>(&self, model: String, scan: &'a Scan<Ts, O>) -> Report
where
Ts: TransitionSystemGenerator + Sync + 'a,
O: Oracle + Sync + 'a,
{
if self.single_thread {
scan.adaptive(self.confidence, self.precision, self.duration);
} else {
scan.par_adaptive(self.confidence, self.precision, self.duration);
}
let successes = scan.successes();
let failures = scan.failures();
let runs = successes + failures;
let rate = successes as f64 / runs as f64;
let property_failures = self
.properties
.iter()
.cloned()
.zip(scan.violations().into_iter().chain([0].into_iter().cycle()))
.collect::<HashMap<String, u32>>();
Report {
model,
precision: self.precision,
confidence: self.confidence,
duration: self.duration,
rate,
runs,
successes,
failures,
property_failures,
}
}
}