#![cfg(feature = "mutation-testing")]
use pmat::services::mutation::{
Mutant, MutantExecutor, MutantStatus, MutationOperatorType, MutationResult, SourceLocation,
};
use std::path::PathBuf;
use std::time::Instant;
#[tokio::test]
#[ignore] async fn red_parallel_execution_must_be_faster_than_sequential() {
let mutants = create_test_mutants(4);
let work_dir = PathBuf::from(".");
let executor = MutantExecutor::new(work_dir.clone());
let start = Instant::now();
let seq_results = executor.execute_mutants(&mutants).await.unwrap();
let seq_time = start.elapsed();
let start = Instant::now();
let par_results: Vec<MutationResult> = executor
.execute_mutants_parallel(&mutants, 4)
.await
.unwrap();
let par_time = start.elapsed();
assert!(
par_time < seq_time / 2,
"Parallel ({:?}) should be at least 2× faster than sequential ({:?})",
par_time,
seq_time
);
assert_eq!(seq_results.len(), par_results.len());
}
#[tokio::test]
#[ignore] async fn red_parallel_execution_must_handle_file_conflicts_safely() {
let mutants = vec![
create_mutant("test.rs", "fn test() { 1 + 2 }"),
create_mutant("test.rs", "fn test() { 1 - 2 }"),
create_mutant("test.rs", "fn test() { 1 * 2 }"),
];
let work_dir = PathBuf::from(".");
let executor = MutantExecutor::new(work_dir);
let results: Vec<MutationResult> = executor
.execute_mutants_parallel(&mutants, 3)
.await
.unwrap();
assert_eq!(results.len(), 3);
for result in &results {
assert!(matches!(
result.status,
MutantStatus::Killed | MutantStatus::Survived | MutantStatus::CompileError
));
}
}
#[tokio::test]
#[ignore] async fn red_parallel_execution_must_respect_worker_count() {
let mutants = create_test_mutants(10);
let work_dir = PathBuf::from(".");
let executor = MutantExecutor::new(work_dir);
let results: Vec<MutationResult> = executor
.execute_mutants_parallel(&mutants, 2)
.await
.unwrap();
assert_eq!(results.len(), 10);
}
#[tokio::test]
#[ignore = "TDD RED phase - Feature not fully implemented yet"]
async fn red_parallel_execution_must_preserve_original_files() {
let test_file = PathBuf::from("/tmp/pmat_parallel_test.rs");
let original_content = "fn original() { 42 }";
std::fs::write(&test_file, original_content).unwrap();
let mutants = vec![
create_mutant_for_file(&test_file, "fn mutated1() { 1 }"),
create_mutant_for_file(&test_file, "fn mutated2() { 2 }"),
];
let work_dir = PathBuf::from("/tmp");
let executor = MutantExecutor::new(work_dir);
let _: Vec<MutationResult> = executor
.execute_mutants_parallel(&mutants, 2)
.await
.unwrap();
let final_content = std::fs::read_to_string(&test_file).unwrap();
assert_eq!(
final_content, original_content,
"Original file was corrupted during parallel execution!"
);
let _ = std::fs::remove_file(&test_file);
}
#[tokio::test]
#[ignore] async fn red_parallel_execution_must_not_deadlock() {
let mutants = create_test_mutants(100);
let work_dir = PathBuf::from(".");
let executor = MutantExecutor::new(work_dir);
let start = Instant::now();
let results: Vec<MutationResult> = executor
.execute_mutants_parallel(&mutants, 8)
.await
.unwrap();
let elapsed = start.elapsed();
assert_eq!(results.len(), 100);
assert!(elapsed.as_secs() < 300, "Took too long, possible deadlock");
}
fn create_test_mutants(count: usize) -> Vec<Mutant> {
(0..count)
.map(|i| create_mutant(&format!("test_{}.rs", i), &format!("fn test() {{ {} }}", i)))
.collect()
}
fn create_mutant(file: &str, source: &str) -> Mutant {
create_mutant_for_file(&PathBuf::from(file), source)
}
fn create_mutant_for_file(file: &PathBuf, source: &str) -> Mutant {
use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hasher};
let mut hasher = RandomState::new().build_hasher();
hasher.write(source.as_bytes());
let id = hasher.finish();
Mutant {
id: format!("TEST_{:x}", id),
original_file: file.clone(),
mutated_source: source.to_string(),
location: SourceLocation {
line: 1,
column: 1,
end_line: 1,
end_column: 10,
},
operator: MutationOperatorType::ArithmeticReplacement,
hash: "test_hash".to_string(),
status: MutantStatus::Pending,
}
}