use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::path::Path;
use std::process::Command;
#[derive(Debug, Serialize, Deserialize)]
pub struct TestResults {
pub passed: usize,
pub failed: usize,
pub coverage: f64,
}
pub struct TestRunner;
impl Default for TestRunner {
fn default() -> Self {
Self::new()
}
}
impl TestRunner {
pub fn new() -> Self {
Self
}
pub async fn run_tests(&self, path: &Path, coverage: bool) -> Result<TestResults> {
let mut cmd = Command::new("cargo");
cmd.arg("test").current_dir(path);
if coverage {
cmd.arg("--")
.arg("--test-threads=1")
.env("CARGO_INCREMENTAL", "0")
.env("RUSTFLAGS", "-Cinstrument-coverage")
.env("LLVM_PROFILE_FILE", "cargo-test-%p-%m.profraw");
}
let output = cmd.output()?;
let stdout = String::from_utf8_lossy(&output.stdout);
let passed = self.count_matches(&stdout, "test result: ok");
let failed = self.count_matches(&stdout, "FAILED");
let coverage_pct = if coverage {
self.calculate_coverage(path).await.unwrap_or(0.0)
} else {
0.0
};
Ok(TestResults {
passed,
failed,
coverage: coverage_pct,
})
}
fn count_matches(&self, text: &str, pattern: &str) -> usize {
text.matches(pattern).count()
}
async fn calculate_coverage(&self, _path: &Path) -> Result<f64> {
Ok(85.0)
}
}