1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// include!()'d from executor.rs
// Parallel mutant execution methods
impl MutantExecutor {
/// Execute tests on multiple mutants in parallel using thread pool
///
/// Uses tokio tasks for parallel execution with temporary file isolation
/// to avoid file conflicts. Each mutant test runs independently.
pub async fn execute_mutants_parallel(
&self,
mutants: &[Mutant],
workers: usize,
) -> Result<Vec<MutationResult>> {
use std::sync::Arc;
use tokio::sync::Semaphore;
println!(" 🚀 Parallel execution with {} workers", workers);
// Create semaphore to limit concurrent executions
let semaphore = Arc::new(Semaphore::new(workers));
let mut tasks = Vec::new();
let total_mutants = mutants.len();
for (i, mutant) in mutants.iter().enumerate() {
let sem = semaphore.clone();
let mutant = mutant.clone();
let executor = self.clone();
let index = i + 1;
// Spawn task for each mutant
let task = tokio::spawn(async move {
// Acquire permit (blocks if all workers busy)
let _permit = sem.acquire().await.expect("semaphore not closed");
println!(
" [{}/{}] Testing mutant {}...",
index, total_mutants, mutant.id
);
match executor.execute_mutant_isolated(&mutant).await {
Ok(result) => {
let status_symbol = match result.status {
MutantStatus::Killed => "✅",
MutantStatus::Survived => "❌",
MutantStatus::CompileError => "🔧",
MutantStatus::Timeout => "⏱️",
_ => "❓",
};
println!(
" {} {:?} ({}ms)",
status_symbol, result.status, result.execution_time_ms
);
Ok(result)
}
Err(e) => {
eprintln!(" ⚠️ Error executing mutant {}: {}", mutant.id, e);
Ok(MutationResult {
mutant: mutant.clone(),
status: MutantStatus::CompileError,
test_failures: vec![],
execution_time_ms: 0,
error_message: Some(e.to_string()),
})
}
}
});
tasks.push(task);
}
// Wait for all tasks to complete
let mut results = Vec::new();
for task in tasks {
match task.await {
Ok(Ok(result)) => results.push(result),
Ok(Err(e)) => return Err(e),
Err(e) => return Err(anyhow::anyhow!("Task join error: {}", e)),
}
}
Ok(results)
}
/// Execute a single mutant in isolation (for parallel execution)
///
/// Uses a unique temporary file for this mutant to avoid conflicts
async fn execute_mutant_isolated(&self, mutant: &Mutant) -> Result<MutationResult> {
use std::time::Instant;
let start_time = Instant::now();
// Create unique scratch file for this mutant (no conflicts!)
let temp_dir = std::env::temp_dir();
let unique_file = temp_dir.join(format!("pmat_{}_{}.rs", std::process::id(), mutant.id));
// Write mutated source to unique scratch file
fs::write(&unique_file, &mutant.mutated_source)
.await
.context("Failed to write isolated mutant")?;
// Run tests with timeout (smart filtering)
let test_result = timeout(self.timeout, self.run_cargo_test_for_mutant(mutant)).await;
// Cleanup scratch file
let _ = fs::remove_file(&unique_file).await;
// Parse results
let execution_time_ms = start_time.elapsed().as_millis() as u64;
let (status, test_failures, error_message) = match test_result {
Ok(Ok(output)) => self.parse_test_output(&output),
Ok(Err(e)) => (MutantStatus::CompileError, vec![], Some(e.to_string())),
Err(_) => (
MutantStatus::Timeout,
vec![],
Some("Test execution timed out".to_string()),
),
};
Ok(MutationResult {
mutant: mutant.clone(),
status,
test_failures,
execution_time_ms,
error_message,
})
}
}