use crate::verify::harnesses::mutation::*;
#[inline]
pub fn mutation_probe(
source_file: &Path,
test_fn_name: &str,
mutations: &[&dyn AppliedMutation],
classes: &[MutationClass],
cargo_test_args: &[&str],
) -> GateReport {
let probe_start = Instant::now();
let mut results = Vec::with_capacity(mutations.len());
let mut feedback = Vec::new();
let original = match fs::read_to_string(source_file) {
Ok(s) => s,
Err(err) => {
return GateReport {
test_name: test_fn_name.to_string(),
source_file: source_file.to_path_buf(),
results: vec![MutationResult {
mutation_id: "probe_init".to_string(),
description: format!(
"failed to read source file {}: {err}",
source_file.display()
),
outcome: MutationOutcome::Skipped {
reason: format!("read error: {err}"),
},
duration: probe_start.elapsed(),
}],
total_duration: probe_start.elapsed(),
feedback: Vec::new(),
};
}
};
for mutation in mutations {
if !classes.is_empty() && !classes.contains(&mutation.class()) {
continue;
}
let start = Instant::now();
let mutated = match mutation.apply(&original) {
Ok(s) => s,
Err(err) => {
results.push(MutationResult {
mutation_id: mutation.id().to_string(),
description: mutation.description().to_string(),
outcome: MutationOutcome::Skipped {
reason: err.to_string(),
},
duration: start.elapsed(),
});
continue;
}
};
if mutated == original {
results.push(MutationResult {
mutation_id: mutation.id().to_string(),
description: mutation.description().to_string(),
outcome: MutationOutcome::Skipped {
reason: "mutation produced identical source".to_string(),
},
duration: start.elapsed(),
});
continue;
}
if let Err(reason) = assert_source_matches_original(source_file, &original) {
results.push(MutationResult {
mutation_id: mutation.id().to_string(),
description: mutation.description().to_string(),
outcome: MutationOutcome::Skipped { reason },
duration: start.elapsed(),
});
break;
}
let mut snapshot = match SourceSnapshot::new(source_file) {
Ok(s) => s,
Err(err) => {
results.push(MutationResult {
mutation_id: mutation.id().to_string(),
description: mutation.description().to_string(),
outcome: MutationOutcome::Skipped {
reason: format!("snapshot failed: {err}"),
},
duration: start.elapsed(),
});
continue;
}
};
if let Err(err) = fs::write(source_file, mutated.as_bytes()) {
if let Err(restore_err) = snapshot.restore_explicit() {
eprintln!(
"vyre-conform H6: write-failed AND restore-failed for {}. \
write: {err}, restore: {restore_err}",
source_file.display()
);
}
results.push(MutationResult {
mutation_id: mutation.id().to_string(),
description: mutation.description().to_string(),
outcome: MutationOutcome::Skipped {
reason: format!("write failed: {err}"),
},
duration: start.elapsed(),
});
continue;
}
let outcome = run_cargo_test(test_fn_name, cargo_test_args);
let killed = !matches!(outcome, MutationOutcome::Survived);
let key = crate::enforce::layers::layer8_feedback_loop::MutationProbeKey {
source_hash: {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
original.hash(&mut hasher);
hasher.finish()
},
test_hash: {
let mut hasher = std::collections::hash_map::DefaultHasher::new();
test_fn_name.hash(&mut hasher);
hasher.finish()
},
mutation: mutation.id().to_string(),
};
let _ = crate::enforce::layers::layer8_feedback_loop::cache_mutation_probe(key, killed);
if let Err(restore_err) = snapshot.restore_explicit() {
eprintln!(
"vyre-conform H6: explicit restore failed for {}: {restore_err}. \
The drop handler will retry on scope exit.",
source_file.display()
);
}
if matches!(outcome, MutationOutcome::Survived) {
feedback.push(StructuredFeedback {
mutation_id: mutation.id().to_string(),
hint: mutation.hint(),
});
}
results.push(MutationResult {
mutation_id: mutation.id().to_string(),
description: mutation.description().to_string(),
outcome,
duration: start.elapsed(),
});
}
GateReport {
test_name: test_fn_name.to_string(),
source_file: source_file.to_path_buf(),
results,
total_duration: probe_start.elapsed(),
feedback,
}
}