#![cfg_attr(coverage_nightly, coverage(off))]
pub mod ast_parser_properties;
pub mod state_machine_properties;
pub mod cache_consistency_properties;
pub mod dag_construction_properties;
pub mod satd_parser_properties;
pub mod test_generators;
pub mod shrinking_strategies;
use proptest::prelude::*;
use std::panic;
#[derive(Debug, Clone)]
pub struct PropertyTestConfig {
pub cases: u32,
pub max_shrink_iters: u32,
pub fork: bool,
pub timeout: u32,
pub parallel: bool,
}
impl Default for PropertyTestConfig {
fn default() -> Self {
Self {
cases: 1000,
max_shrink_iters: 50,
fork: true,
timeout: 30_000,
parallel: true,
}
}
}
pub fn run_property_suite<F>(config: PropertyTestConfig, test_fn: F) -> Result<(), TestError>
where
F: Fn() -> Result<(), TestCaseError> + Send + Sync + 'static,
{
let proptest_config = ProptestConfig {
cases: config.cases,
max_shrink_iters: config.max_shrink_iters,
fork: config.fork,
timeout: config.timeout,
..Default::default()
};
if config.parallel {
use rayon::prelude::*;
let pool = rayon::ThreadPoolBuilder::new()
.num_threads(num_cpus::get())
.panic_handler(|_| {}) .build()
.map_err(|e| TestError::Setup(e.to_string()))?;
pool.install(|| {
(0..config.cases)
.into_par_iter()
.try_for_each(|_| test_fn())
.map_err(|e| TestError::Property(format!("{:?}", e)))
})
} else {
for _ in 0..config.cases {
test_fn().map_err(|e| TestError::Property(format!("{:?}", e)))?;
}
Ok(())
}
}
#[derive(Debug)]
pub enum TestError {
Setup(String),
Property(String),
Shrinking(String),
Timeout,
}
impl std::fmt::Display for TestError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TestError::Setup(msg) => write!(f, "Test setup error: {}", msg),
TestError::Property(msg) => write!(f, "Property test failed: {}", msg),
TestError::Shrinking(msg) => write!(f, "Shrinking failed: {}", msg),
TestError::Timeout => write!(f, "Test timed out"),
}
}
}
impl std::error::Error for TestError {}
#[derive(Debug, Default)]
pub struct PropertyTestMetrics {
pub total_cases: u32,
pub passed: u32,
pub failed: u32,
pub shrink_steps: u32,
pub panics_caught: u32,
pub timeouts: u32,
}
impl PropertyTestMetrics {
pub fn success_rate(&self) -> f64 {
if self.total_cases == 0 {
0.0
} else {
self.passed as f64 / self.total_cases as f64
}
}
pub fn defect_detection_rate(&self) -> f64 {
if self.total_cases == 0 {
0.0
} else {
self.failed as f64 / self.total_cases as f64
}
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_property_config_defaults() {
let config = PropertyTestConfig::default();
assert_eq!(config.cases, 1000);
assert_eq!(config.max_shrink_iters, 50);
assert!(config.fork);
assert_eq!(config.timeout, 30_000);
assert!(config.parallel);
}
#[test]
fn test_metrics_calculation() {
let mut metrics = PropertyTestMetrics::default();
metrics.total_cases = 100;
metrics.passed = 95;
metrics.failed = 5;
assert_eq!(metrics.success_rate(), 0.95);
assert_eq!(metrics.defect_detection_rate(), 0.05);
}
}