#![cfg_attr(docsrs, feature(doc_cfg))]
#![warn(missing_docs)]
#![warn(rust_2018_idioms)]
use dev_report::{CheckResult, Severity};
#[derive(Debug, Clone)]
pub struct CoverageRun {
name: String,
version: String,
}
impl CoverageRun {
pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
Self {
name: name.into(),
version: version.into(),
}
}
pub fn execute(&self) -> Result<CoverageResult, CoverageError> {
Ok(CoverageResult {
name: self.name.clone(),
version: self.version.clone(),
line_pct: 0.0,
function_pct: 0.0,
region_pct: 0.0,
total_lines: 0,
covered_lines: 0,
})
}
}
#[derive(Debug, Clone)]
pub struct CoverageResult {
pub name: String,
pub version: String,
pub line_pct: f64,
pub function_pct: f64,
pub region_pct: f64,
pub total_lines: u64,
pub covered_lines: u64,
}
#[derive(Debug, Clone, Copy)]
pub enum CoverageThreshold {
MinLinePct(f64),
MinFunctionPct(f64),
MinRegionPct(f64),
}
impl CoverageThreshold {
pub fn min_line_pct(pct: f64) -> Self {
Self::MinLinePct(pct)
}
pub fn min_function_pct(pct: f64) -> Self {
Self::MinFunctionPct(pct)
}
pub fn min_region_pct(pct: f64) -> Self {
Self::MinRegionPct(pct)
}
}
impl CoverageResult {
pub fn into_check_result(self, threshold: CoverageThreshold) -> CheckResult {
let name = format!("coverage::{}", self.name);
let (actual, target, label) = match threshold {
CoverageThreshold::MinLinePct(p) => (self.line_pct, p, "line"),
CoverageThreshold::MinFunctionPct(p) => (self.function_pct, p, "function"),
CoverageThreshold::MinRegionPct(p) => (self.region_pct, p, "region"),
};
let detail = format!("{label} coverage {actual:.2}% (threshold {target:.2}%)");
if actual < target {
CheckResult::fail(name, Severity::Warning).with_detail(detail)
} else {
CheckResult::pass(name).with_detail(detail)
}
}
}
#[derive(Debug)]
pub enum CoverageError {
ToolNotInstalled,
SubprocessFailed(String),
ParseError(String),
}
impl std::fmt::Display for CoverageError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::ToolNotInstalled => write!(f, "cargo-llvm-cov is not installed"),
Self::SubprocessFailed(s) => write!(f, "subprocess failed: {s}"),
Self::ParseError(s) => write!(f, "parse error: {s}"),
}
}
}
impl std::error::Error for CoverageError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn run_returns_a_result() {
let run = CoverageRun::new("x", "0.1.0");
let r = run.execute().unwrap();
assert_eq!(r.name, "x");
}
#[test]
fn threshold_pass() {
let r = CoverageResult {
name: "x".into(),
version: "0.1.0".into(),
line_pct: 90.0,
function_pct: 85.0,
region_pct: 80.0,
total_lines: 100,
covered_lines: 90,
};
let c = r.into_check_result(CoverageThreshold::min_line_pct(80.0));
assert!(matches!(c.verdict, dev_report::Verdict::Pass));
}
#[test]
fn threshold_fail() {
let r = CoverageResult {
name: "x".into(),
version: "0.1.0".into(),
line_pct: 50.0,
function_pct: 60.0,
region_pct: 40.0,
total_lines: 100,
covered_lines: 50,
};
let c = r.into_check_result(CoverageThreshold::min_line_pct(80.0));
assert!(matches!(c.verdict, dev_report::Verdict::Fail));
}
}