use crate::services::coverage_improvement::{
CoverageImprovementConfig, CoverageImprovementReport, CoverageImprovementService,
};
use anyhow::Result;
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum CoverageImproveOutputFormat {
Text,
Json,
Markdown,
}
#[allow(clippy::too_many_arguments)]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "path_exists")]
pub async fn handle_coverage_improve(
project_path: PathBuf,
target: f64,
max_iterations: usize,
fast: bool,
mutation_threshold: f64,
focus: Vec<String>,
exclude: Vec<String>,
output: Option<PathBuf>,
format: CoverageImproveOutputFormat,
) -> Result<()> {
eprintln!("📊 PMAT Coverage Improvement");
eprintln!("🎯 Target: {:.1}%", target);
eprintln!("📁 Project: {}", project_path.display());
let config = CoverageImprovementConfig {
project_path,
target_coverage: target,
max_iterations,
fast_mode: fast,
mutation_threshold,
focus_patterns: focus,
exclude_patterns: exclude,
};
let service = CoverageImprovementService::new(config);
let report = service.improve_coverage().await?;
let formatted = match format {
CoverageImproveOutputFormat::Text => format_text(&report),
CoverageImproveOutputFormat::Json => format_json(&report)?,
CoverageImproveOutputFormat::Markdown => format_markdown(&report),
};
if let Some(output_path) = output {
std::fs::write(&output_path, &formatted)?;
eprintln!("📝 Report written to: {}", output_path.display());
} else {
println!("{}", formatted);
}
print_summary(&report);
if report.success {
Ok(())
} else {
anyhow::bail!("Failed to reach target coverage");
}
}
fn format_text(report: &CoverageImprovementReport) -> String {
let mut output = String::new();
output.push_str("Coverage Improvement Report\n");
output.push_str("===========================\n\n");
output.push_str(&format!("Baseline: {:.2}%\n", report.baseline_coverage));
output.push_str(&format!("Target: {:.2}%\n", report.target_coverage));
output.push_str(&format!("Final: {:.2}%\n", report.final_coverage));
output.push_str(&format!(
"Gain: +{:.2}%\n\n",
report.final_coverage - report.baseline_coverage
));
for iteration in &report.iterations {
output.push_str(&format!("Iteration {}:\n", iteration.iteration));
output.push_str(&format!(
" Files targeted: {}\n",
iteration.files_targeted.len()
));
output.push_str(&format!(
" Tests generated: {}\n",
iteration.tests_generated
));
output.push_str(&format!(
" Coverage gain: +{:.2}%\n",
iteration.coverage_gain
));
output.push_str(&format!(
" Mutation score: {:.1}%\n\n",
iteration.mutation_score
));
}
output.push_str(&format!(
"Status: {}\n",
if report.success {
"SUCCESS"
} else {
"INCOMPLETE"
}
));
output.push_str(&format!("Reason: {}\n", report.stop_reason));
output
}
fn format_json(report: &CoverageImprovementReport) -> Result<String> {
serde_json::to_string_pretty(report).map_err(Into::into)
}
fn format_markdown(report: &CoverageImprovementReport) -> String {
let mut output = String::new();
output.push_str("# Coverage Improvement Report\n\n");
output.push_str("## Summary\n\n");
output.push_str(&format!(
"- **Baseline Coverage**: {:.2}%\n",
report.baseline_coverage
));
output.push_str(&format!(
"- **Target Coverage**: {:.2}%\n",
report.target_coverage
));
output.push_str(&format!(
"- **Final Coverage**: {:.2}%\n",
report.final_coverage
));
output.push_str(&format!(
"- **Total Gain**: +{:.2}%\n\n",
report.final_coverage - report.baseline_coverage
));
output.push_str("## Iterations\n\n");
output.push_str("| Iteration | Files | Tests | Coverage Gain | Mutation Score |\n");
output.push_str("|-----------|-------|-------|---------------|----------------|\n");
for iteration in &report.iterations {
output.push_str(&format!(
"| {} | {} | {} | +{:.2}% | {:.1}% |\n",
iteration.iteration,
iteration.files_targeted.len(),
iteration.tests_generated,
iteration.coverage_gain,
iteration.mutation_score
));
}
output.push_str(&format!(
"\n## Result\n\n**Status**: {}\n\n",
if report.success {
"✅ SUCCESS"
} else {
"⚠️ INCOMPLETE"
}
));
output.push_str(&format!("**Reason**: {}\n", report.stop_reason));
output
}
fn print_summary(report: &CoverageImprovementReport) {
eprintln!("\n📊 Summary:");
eprintln!(" Baseline: {:.2}%", report.baseline_coverage);
eprintln!(" Final: {:.2}%", report.final_coverage);
eprintln!(
" Gain: +{:.2}%",
report.final_coverage - report.baseline_coverage
);
eprintln!(" Iterations: {}", report.iterations.len());
if report.success {
eprintln!("✅ Target coverage reached!");
} else {
eprintln!("⚠️ {}", report.stop_reason);
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
use crate::services::coverage_improvement::IterationReport;
#[test]
fn test_format_text() {
let report = CoverageImprovementReport {
baseline_coverage: 50.0,
target_coverage: 95.0,
final_coverage: 65.0,
iterations: vec![IterationReport {
iteration: 1,
files_targeted: vec![PathBuf::from("test.rs")],
tests_generated: 5,
coverage_gain: 15.0,
mutation_score: 85.0,
}],
success: false,
stop_reason: "Max iterations reached".to_string(),
};
let text = format_text(&report);
assert!(text.contains("50.00%"));
assert!(text.contains("65.00%"));
assert!(text.contains("Iteration 1"));
}
#[test]
fn test_format_json() {
let report = CoverageImprovementReport {
baseline_coverage: 50.0,
target_coverage: 95.0,
final_coverage: 95.0,
iterations: vec![],
success: true,
stop_reason: "Target reached".to_string(),
};
let json = format_json(&report).unwrap();
assert!(json.contains("baseline_coverage"));
assert!(json.contains("95.0"));
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod property_tests {
use proptest::prelude::*;
proptest! {
#[test]
fn basic_property_stability(_input in ".*") {
prop_assert!(true);
}
#[test]
fn module_consistency_check(_x in 0u32..1000) {
prop_assert!(_x < 1001);
}
}
}