pub mod ecosystem_integration;
pub mod fuzzing;
pub mod integration;
pub mod large_scale;
pub mod propertybased;
pub mod security;
pub mod stress;
use crate::error::CoreResult;
#[cfg(target_os = "linux")]
use crate::error::{CoreError, ErrorContext};
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct TestConfig {
pub timeout: Duration,
pub iterations: usize,
pub memory_limit: usize,
pub verbose: bool,
pub seed: Option<u64>,
}
impl Default for TestConfig {
fn default() -> Self {
Self {
timeout: Duration::from_secs(30),
iterations: 1000,
memory_limit: 1024 * 1024 * 1024, verbose: false,
seed: None,
}
}
}
impl TestConfig {
pub fn new() -> Self {
Self::default()
}
pub fn with_timeout(mut self, timeout: Duration) -> Self {
self.timeout = timeout;
self
}
pub fn with_iterations(mut self, iterations: usize) -> Self {
self.iterations = iterations;
self
}
pub fn with_memory_limit(mut self, limit: usize) -> Self {
self.memory_limit = limit;
self
}
pub fn with_verbose(mut self, verbose: bool) -> Self {
self.verbose = verbose;
self
}
pub fn with_seed(mut self, seed: u64) -> Self {
self.seed = Some(seed);
self
}
}
#[derive(Debug, Clone)]
pub struct TestResult {
pub passed: bool,
pub duration: Duration,
pub cases_executed: usize,
pub memory_used: usize,
pub error: Option<String>,
pub metadata: std::collections::HashMap<String, String>,
}
impl TestResult {
pub fn success(duration: Duration, cases: usize) -> Self {
Self {
passed: true,
duration,
cases_executed: cases,
memory_used: 0,
error: None,
metadata: std::collections::HashMap::new(),
}
}
pub fn failure(duration: Duration, cases: usize, error: String) -> Self {
Self {
passed: false,
duration,
cases_executed: cases,
memory_used: 0,
error: Some(error),
metadata: std::collections::HashMap::new(),
}
}
pub fn with_memory_usage(mut self, memory: usize) -> Self {
self.memory_used = memory;
self
}
pub fn with_metadata(mut self, key: String, value: String) -> Self {
self.metadata.insert(key, value);
self
}
}
pub struct TestRunner {
config: TestConfig,
}
impl TestRunner {
pub fn new(config: TestConfig) -> Self {
Self { config }
}
pub fn execute<F>(&self, test_name: &str, testfn: F) -> CoreResult<TestResult>
where
F: FnOnce() -> CoreResult<()>,
{
if self.config.verbose {
println!("Executing test: {}", test_name);
}
let start_time = Instant::now();
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(testfn));
let duration = start_time.elapsed();
match result {
Ok(Ok(())) => {
if self.config.verbose {
println!("Test {} passed in {:?}", test_name, duration);
}
Ok(TestResult::success(duration, 1))
}
Ok(Err(e)) => {
if self.config.verbose {
println!("Test {} failed: {:?}", test_name, e);
}
Ok(TestResult::failure(duration, 1, format!("{e:?}")))
}
Err(panic) => {
let errormsg = if let Some(s) = panic.downcast_ref::<String>() {
s.clone()
} else if let Some(s) = panic.downcast_ref::<&str>() {
s.to_string()
} else {
"Unknown panic".to_string()
};
if self.config.verbose {
println!("Test {} panicked: {}", test_name, errormsg);
}
Ok(TestResult::failure(duration, 1, errormsg))
}
}
}
pub fn execute_iterations<F>(&self, test_name: &str, testfn: F) -> CoreResult<TestResult>
where
F: Fn(usize) -> CoreResult<()>,
{
if self.config.verbose {
println!(
"Executing {} iterations of test: {}",
self.config.iterations, test_name
);
}
let start_time = Instant::now();
let mut cases_executed = 0;
#[cfg(target_os = "linux")]
let mut max_memory = 0;
#[cfg(not(target_os = "linux"))]
let max_memory = 0;
for i in 0..self.config.iterations {
if start_time.elapsed() > self.config.timeout {
return Ok(TestResult::failure(
start_time.elapsed(),
cases_executed,
format!("Test timed out after {} iterations", cases_executed),
));
}
match testfn(i) {
Ok(()) => {
cases_executed += 1;
#[cfg(target_os = "linux")]
{
if let Ok(memory) = self.get_memory_usage() {
max_memory = max_memory.max(memory);
if memory > self.config.memory_limit {
return Ok(TestResult::failure(
start_time.elapsed(),
cases_executed,
format!("Memory limit exceeded: {} bytes", memory),
)
.with_memory_usage(memory));
}
}
}
}
Err(e) => {
return Ok(TestResult::failure(
start_time.elapsed(),
cases_executed,
format!("Iteration {}: {:?}", i, e),
)
.with_memory_usage(max_memory));
}
}
}
let duration = start_time.elapsed();
if self.config.verbose {
println!(
"Test {} completed {} iterations in {:?}",
test_name, cases_executed, duration
);
}
Ok(TestResult::success(duration, cases_executed).with_memory_usage(max_memory))
}
#[cfg(target_os = "linux")]
#[allow(dead_code)]
fn get_memory_usage(&self) -> CoreResult<usize> {
use std::fs;
let status = fs::read_to_string("/proc/self/status").map_err(|e| {
CoreError::IoError(ErrorContext::new(format!(
"Failed to read /proc/self/status: {}",
e
)))
})?;
for line in status.lines() {
if line.starts_with("VmRSS:") {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() >= 2 {
let kb: usize = parts[1].parse().map_err(|e| {
CoreError::ValidationError(crate::error::ErrorContext::new(format!(
"Failed to parse memory: {}",
e
)))
})?;
return Ok(kb * 1024); }
}
}
Err(CoreError::ComputationError(
crate::error::ErrorContext::new("Could not find VmRSS in /proc/self/status"),
))
}
#[cfg(not(target_os = "linux"))]
#[allow(dead_code)]
fn get_memory_usage(&self) -> CoreResult<usize> {
Ok(0)
}
}
type TestFn = Box<dyn Fn(&TestRunner) -> CoreResult<TestResult> + Send + Sync>;
pub struct TestSuite {
name: String,
tests: Vec<TestFn>,
config: TestConfig,
}
impl TestSuite {
pub fn new(name: &str, config: TestConfig) -> Self {
Self {
name: name.to_string(),
tests: Vec::new(),
config,
}
}
pub fn add_test<F>(&mut self, test_name: &str, testfn: F)
where
F: Fn(&TestRunner) -> CoreResult<TestResult> + Send + Sync + 'static,
{
let name = test_name.to_string();
self.tests.push(Box::new(move |runner| {
println!("Running test: {}", name);
testfn(runner)
}));
}
pub fn run(&self) -> CoreResult<Vec<TestResult>> {
println!("Running test suite: {}", self.name);
let runner = TestRunner::new(self.config.clone());
let mut results = Vec::new();
for (i, test) in self.tests.iter().enumerate() {
println!("Test {}/{}", i + 1, self.tests.len());
match test(&runner) {
Ok(result) => {
results.push(result);
}
Err(e) => {
results.push(TestResult::failure(
Duration::from_secs(0),
0,
format!("{:?}", e),
));
}
}
}
let passed = results.iter().filter(|r| r.passed).count();
let total = results.len();
println!(
"Test suite {} completed: {}/{} tests passed",
self.name, passed, total
);
Ok(results)
}
}