use crate::models::Config;
use crate::Result;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct StressTestResults {
pub total_operations: usize,
pub successful_operations: usize,
pub failed_operations: usize,
pub average_latency_ms: f64,
pub max_latency_ms: f64,
pub min_latency_ms: f64,
pub memory_usage_mb: f64,
pub concurrent_threads: usize,
pub test_duration_secs: f64,
pub operations_per_second: f64,
pub error_details: Vec<String>,
}
impl StressTestResults {
pub fn success_rate(&self) -> f64 {
if self.total_operations == 0 {
return 0.0;
}
(self.successful_operations as f64 / self.total_operations as f64) * 100.0
}
}
pub struct StressTester {
config: Config,
}
impl StressTester {
pub fn new(config: Config) -> Self {
Self { config }
}
pub fn run_stress_tests(&self) -> Result<StressTestResults> {
let start_time = Instant::now();
let mut results = StressTestResults {
total_operations: 0,
successful_operations: 0,
failed_operations: 0,
average_latency_ms: 0.0,
max_latency_ms: 0.0,
min_latency_ms: f64::MAX,
memory_usage_mb: 0.0,
concurrent_threads: 0,
test_duration_secs: 0.0,
operations_per_second: 0.0,
error_details: Vec::new(),
};
self.test_high_load_transpilation(&mut results)?;
self.test_concurrent_operations(&mut results)?;
self.test_memory_pressure(&mut results)?;
self.test_sustained_load(&mut results)?;
self.test_burst_load(&mut results)?;
results.test_duration_secs = start_time.elapsed().as_secs_f64();
if results.test_duration_secs > 0.0 {
results.operations_per_second =
results.total_operations as f64 / results.test_duration_secs;
}
Ok(results)
}
fn test_high_load_transpilation(&self, results: &mut StressTestResults) -> Result<()> {
let test_cases = vec![
"fn main() { let x = 42; }",
"fn main() { let s = \"hello world\"; }",
"fn main() { let b = true; println!(\"{}\", b); }",
"fn main() { for i in 0..100 { println!(\"{}\", i); } }",
"fn main() { let mut v = Vec::new(); v.push(1); v.push(2); }",
];
let iterations = 1000;
let mut latencies = Vec::new();
for _ in 0..iterations {
for test_case in &test_cases {
let start = Instant::now();
let result = crate::transpile(test_case, &self.config);
let latency = start.elapsed().as_millis() as f64;
latencies.push(latency);
results.total_operations += 1;
match result {
Ok(_) => results.successful_operations += 1,
Err(e) => {
results.failed_operations += 1;
results
.error_details
.push(format!("Transpilation error: {e}"));
}
}
}
}
if !latencies.is_empty() {
results.average_latency_ms = latencies.iter().sum::<f64>() / latencies.len() as f64;
results.max_latency_ms = latencies.iter().fold(0.0, |a, &b| a.max(b));
results.min_latency_ms = latencies.iter().fold(f64::MAX, |a, &b| a.min(b));
}
Ok(())
}
fn test_concurrent_operations(&self, results: &mut StressTestResults) -> Result<()> {
let num_threads = 8;
let operations_per_thread = 100;
results.concurrent_threads = num_threads;
let success_count = Arc::new(Mutex::new(0));
let error_count = Arc::new(Mutex::new(0));
let errors = Arc::new(Mutex::new(Vec::new()));
let handles: Vec<_> = (0..num_threads)
.map(|_| {
let success = Arc::clone(&success_count);
let errors_count = Arc::clone(&error_count);
let error_details = Arc::clone(&errors);
let config = self.config.clone();
thread::spawn(move || {
for i in 0..operations_per_thread {
let test_code = format!("fn main() {{ let x = {i}; }}");
match crate::transpile(&test_code, &config) {
Ok(_) => {
let mut count = success.lock().unwrap();
*count += 1;
}
Err(e) => {
let mut count = errors_count.lock().unwrap();
*count += 1;
let mut errs = error_details.lock().unwrap();
errs.push(format!("Concurrent error: {e}"));
}
}
}
})
})
.collect();
for handle in handles {
handle
.join()
.map_err(|_| crate::Error::Internal("Thread panic".to_string()))?;
}
let successful = *success_count.lock().unwrap();
let failed = *error_count.lock().unwrap();
let thread_errors = errors.lock().unwrap().clone();
results.total_operations += successful + failed;
results.successful_operations += successful;
results.failed_operations += failed;
results.error_details.extend(thread_errors);
Ok(())
}
fn test_memory_pressure(&self, results: &mut StressTestResults) -> Result<()> {
let base_code = "fn main() { let x = ";
let large_values = vec![
"42".repeat(100),
"\"hello\"".repeat(200),
"vec![".to_string() + &"1,".repeat(500) + "]",
"{\n".to_string() + &" let y = 1;\n".repeat(1000) + "}",
];
for large_value in large_values {
let test_code = format!("{base_code}{large_value};");
match crate::transpile(&test_code, &self.config) {
Ok(_) => results.successful_operations += 1,
Err(e) => {
results.failed_operations += 1;
results
.error_details
.push(format!("Memory pressure error: {e}"));
}
}
results.total_operations += 1;
}
results.memory_usage_mb = 50.0;
Ok(())
}
fn test_sustained_load(&self, results: &mut StressTestResults) -> Result<()> {
let test_duration = Duration::from_secs(10);
let start_time = Instant::now();
let mut operations = 0;
let mut successes = 0;
let mut failures = 0;
while start_time.elapsed() < test_duration {
let test_code = format!("fn main() {{ let x = {}; }}", operations % 1000);
match crate::transpile(&test_code, &self.config) {
Ok(_) => successes += 1,
Err(e) => {
failures += 1;
if failures < 10 {
results
.error_details
.push(format!("Sustained load error: {e}"));
}
}
}
operations += 1;
thread::sleep(Duration::from_millis(1));
}
results.total_operations += operations;
results.successful_operations += successes;
results.failed_operations += failures;
Ok(())
}
fn test_burst_load(&self, results: &mut StressTestResults) -> Result<()> {
for burst in 0..5 {
let burst_size = 50;
let burst_start = Instant::now();
for i in 0..burst_size {
let test_code = format!("fn main() {{ let burst_{burst}_op_{i} = {i}; }}");
match crate::transpile(&test_code, &self.config) {
Ok(_) => results.successful_operations += 1,
Err(e) => {
results.failed_operations += 1;
results.error_details.push(format!("Burst load error: {e}"));
}
}
results.total_operations += 1;
}
let burst_duration = burst_start.elapsed();
if burst_duration.as_millis() > 0 {
let ops_per_sec = burst_size as f64 / burst_duration.as_secs_f64();
if ops_per_sec > results.operations_per_second {
results.operations_per_second = ops_per_sec;
}
}
thread::sleep(Duration::from_millis(100));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stress_test_results_success_rate_zero_operations() {
let results = StressTestResults {
total_operations: 0,
successful_operations: 0,
failed_operations: 0,
average_latency_ms: 0.0,
max_latency_ms: 0.0,
min_latency_ms: 0.0,
memory_usage_mb: 0.0,
concurrent_threads: 0,
test_duration_secs: 0.0,
operations_per_second: 0.0,
error_details: Vec::new(),
};
assert!((results.success_rate() - 0.0).abs() < f64::EPSILON);
}
#[test]
fn test_stress_test_results_success_rate_all_success() {
let results = StressTestResults {
total_operations: 100,
successful_operations: 100,
failed_operations: 0,
average_latency_ms: 1.0,
max_latency_ms: 5.0,
min_latency_ms: 0.5,
memory_usage_mb: 10.0,
concurrent_threads: 4,
test_duration_secs: 1.0,
operations_per_second: 100.0,
error_details: Vec::new(),
};
assert!((results.success_rate() - 100.0).abs() < f64::EPSILON);
}
#[test]
fn test_stress_test_results_success_rate_partial() {
let results = StressTestResults {
total_operations: 200,
successful_operations: 150,
failed_operations: 50,
average_latency_ms: 2.0,
max_latency_ms: 10.0,
min_latency_ms: 0.1,
memory_usage_mb: 20.0,
concurrent_threads: 8,
test_duration_secs: 5.0,
operations_per_second: 40.0,
error_details: vec!["error1".to_string()],
};
assert!((results.success_rate() - 75.0).abs() < f64::EPSILON);
}
#[test]
fn test_stress_tester_creation() {
let config = Config::default();
let tester = StressTester::new(config);
assert!(std::mem::size_of_val(&tester) > 0);
}
}