Skip to content

Commit

Permalink
add --fail-under-{functions,regions} options
Browse files Browse the repository at this point in the history
Not including one for branches since Rust itself doesn't support it.
  • Loading branch information
CobaltCause committed Nov 16, 2023
1 parent 57275c3 commit 5e6e03a
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 9 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,15 @@ OPTIONS:
--no-clean
Build without cleaning any old build artifacts

--fail-under-functions <MIN>
Exit with a status of 1 if the total function coverage is less than MIN percent

--fail-under-lines <MIN>
Exit with a status of 1 if the total line coverage is less than MIN percent

--fail-under-regions <MIN>
Exit with a status of 1 if the total region coverage is less than MIN percent

--fail-uncovered-lines <MAX>
Exit with a status of 1 if the uncovered lines are greater than MAX

Expand Down
6 changes: 6 additions & 0 deletions docs/cargo-llvm-cov-report.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,15 @@ OPTIONS:
--hide-instantiations
Hide instantiations from report

--fail-under-functions <MIN>
Exit with a status of 1 if the total function coverage is less than MIN percent

--fail-under-lines <MIN>
Exit with a status of 1 if the total line coverage is less than MIN percent

--fail-under-regions <MIN>
Exit with a status of 1 if the total region coverage is less than MIN percent

--fail-uncovered-lines <MAX>
Exit with a status of 1 if the uncovered lines are greater than MAX

Expand Down
6 changes: 6 additions & 0 deletions docs/cargo-llvm-cov-run.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,15 @@ OPTIONS:
--no-clean
Build without cleaning any old build artifacts

--fail-under-functions <MIN>
Exit with a status of 1 if the total function coverage is less than MIN percent

--fail-under-lines <MIN>
Exit with a status of 1 if the total line coverage is less than MIN percent

--fail-under-regions <MIN>
Exit with a status of 1 if the total region coverage is less than MIN percent

--fail-uncovered-lines <MAX>
Exit with a status of 1 if the uncovered lines are greater than MAX

Expand Down
6 changes: 6 additions & 0 deletions docs/cargo-llvm-cov-test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,15 @@ OPTIONS:
--no-clean
Build without cleaning any old build artifacts

--fail-under-functions <MIN>
Exit with a status of 1 if the total function coverage is less than MIN percent

--fail-under-lines <MIN>
Exit with a status of 1 if the total line coverage is less than MIN percent

--fail-under-regions <MIN>
Exit with a status of 1 if the total region coverage is less than MIN percent

--fail-uncovered-lines <MAX>
Exit with a status of 1 if the uncovered lines are greater than MAX

Expand Down
6 changes: 6 additions & 0 deletions docs/cargo-llvm-cov.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,15 @@ OPTIONS:
--no-clean
Build without cleaning any old build artifacts

--fail-under-functions <MIN>
Exit with a status of 1 if the total function coverage is less than MIN percent

--fail-under-lines <MIN>
Exit with a status of 1 if the total line coverage is less than MIN percent

--fail-under-regions <MIN>
Exit with a status of 1 if the total region coverage is less than MIN percent

--fail-uncovered-lines <MAX>
Exit with a status of 1 if the uncovered lines are greater than MAX

Expand Down
10 changes: 10 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,9 @@ impl Args {
let mut no_cfg_coverage = false;
let mut no_cfg_coverage_nightly = false;
let mut no_report = false;
let mut fail_under_functions = None;
let mut fail_under_lines = None;
let mut fail_under_regions = None;
let mut fail_uncovered_lines = None;
let mut fail_uncovered_regions = None;
let mut fail_uncovered_functions = None;
Expand Down Expand Up @@ -398,7 +400,9 @@ impl Args {
Long("no-cfg-coverage") => parse_flag!(no_cfg_coverage),
Long("no-cfg-coverage-nightly") => parse_flag!(no_cfg_coverage_nightly),
Long("no-report") => parse_flag!(no_report),
Long("fail-under-functions") => parse_opt!(fail_under_functions),
Long("fail-under-lines") => parse_opt!(fail_under_lines),
Long("fail-under-regions") => parse_opt!(fail_under_regions),
Long("fail-uncovered-lines") => parse_opt!(fail_uncovered_lines),
Long("fail-uncovered-regions") => parse_opt!(fail_uncovered_regions),
Long("fail-uncovered-functions") => parse_opt!(fail_uncovered_functions),
Expand Down Expand Up @@ -816,7 +820,9 @@ impl Args {
no_cfg_coverage,
no_cfg_coverage_nightly,
no_report,
fail_under_functions,
fail_under_lines,
fail_under_regions,
fail_uncovered_lines,
fail_uncovered_regions,
fail_uncovered_functions,
Expand Down Expand Up @@ -1025,8 +1031,12 @@ pub(crate) struct LlvmCovOptions {
pub(crate) no_cfg_coverage_nightly: bool,
/// Run tests, but don't generate coverage report
pub(crate) no_report: bool,
/// Exit with a status of 1 if the total function coverage is less than MIN percent.
pub(crate) fail_under_functions: Option<f64>,
/// Exit with a status of 1 if the total line coverage is less than MIN percent.
pub(crate) fail_under_lines: Option<f64>,
/// Exit with a status of 1 if the total region coverage is less than MIN percent.
pub(crate) fail_under_regions: Option<f64>,
/// Exit with a status of 1 if the uncovered lines are greater than MAX.
pub(crate) fail_uncovered_lines: Option<u64>,
/// Exit with a status of 1 if the uncovered regions are greater than MAX.
Expand Down
51 changes: 45 additions & 6 deletions src/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,24 @@ impl CodeCovJsonExport {
/// Files -> list of uncovered lines.
pub(crate) type UncoveredLines = BTreeMap<String, Vec<u64>>;

#[non_exhaustive]
#[derive(Clone, Copy)]
pub enum CoverageKind {
Functions,
Lines,
Regions,
}

impl AsRef<str> for CoverageKind {
fn as_ref(&self) -> &'static str {
match self {
CoverageKind::Functions => "functions",
CoverageKind::Lines => "lines",
CoverageKind::Regions => "regions",
}
}
}

impl LlvmCovJsonExport {
pub fn demangle(&mut self) {
for data in &mut self.data {
Expand All @@ -165,12 +183,13 @@ impl LlvmCovJsonExport {
}

/// Gets the minimal lines coverage of all files.
pub fn get_lines_percent(&self) -> Result<f64> {
pub fn get_coverage_percent(&self, kind: CoverageKind) -> Result<f64> {
let mut count = 0_f64;
let mut covered = 0_f64;
for data in &self.data {
let totals = &data.totals.as_object().context("totals is not an object")?;
let lines = &totals["lines"].as_object().context("no lines")?;
let lines =
&totals[kind.as_ref()].as_object().context(format!("no {}", kind.as_ref()))?;
count += lines["count"].as_f64().context("no count")?;
covered += lines["covered"].as_f64().context("no covered")?;
}
Expand Down Expand Up @@ -555,8 +574,13 @@ mod tests {
}
}

#[test]
fn test_get_lines_percent() {
fn test_get_coverage_percent(kind: CoverageKind) {
let expected = match kind {
CoverageKind::Functions => 100_f64,
CoverageKind::Lines => 68.181_818_181_818_19,
CoverageKind::Regions => 66.666_666_666_666_67,
};

// There are 5 different percentages, make sure we pick the correct one.
let file = format!(
"{}/tests/fixtures/coverage-reports/no_coverage/no_coverage.json",
Expand All @@ -565,10 +589,25 @@ mod tests {
let s = fs::read_to_string(file).unwrap();
let json = serde_json::from_str::<LlvmCovJsonExport>(&s).unwrap();

let percent = json.get_lines_percent().unwrap();
let actual = json.get_coverage_percent(kind).unwrap();

let error_margin = f64::EPSILON;
assert!((percent - 68.181_818_181_818_19).abs() < error_margin, "{percent}");
assert!((actual - expected).abs() < error_margin, "{actual}");
}

#[test]
fn test_get_functions_percent() {
test_get_coverage_percent(CoverageKind::Functions)
}

#[test]
fn test_get_lines_percent() {
test_get_coverage_percent(CoverageKind::Lines)
}

#[test]
fn test_get_regions_percent() {
test_get_coverage_percent(CoverageKind::Regions)
}

#[test]
Expand Down
30 changes: 27 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use std::{
use anyhow::{bail, Context as _, Result};
use camino::{Utf8Path, Utf8PathBuf};
use cargo_config2::Flags;
use cargo_llvm_cov::json::{CodeCovJsonExport, LlvmCovJsonExport};
use cargo_llvm_cov::json::{CodeCovJsonExport, CoverageKind, LlvmCovJsonExport};
use regex::Regex;
use walkdir::WalkDir;

Expand Down Expand Up @@ -485,7 +485,9 @@ fn generate_report(cx: &Context) -> Result<()> {
.generate_report(cx, &object_files, ignore_filename_regex.as_deref())
.context("failed to generate report")?;

if cx.args.cov.fail_under_lines.is_some()
if cx.args.cov.fail_under_functions.is_some()
|| cx.args.cov.fail_under_lines.is_some()
|| cx.args.cov.fail_under_regions.is_some()
|| cx.args.cov.fail_uncovered_functions.is_some()
|| cx.args.cov.fail_uncovered_lines.is_some()
|| cx.args.cov.fail_uncovered_regions.is_some()
Expand All @@ -496,14 +498,36 @@ fn generate_report(cx: &Context) -> Result<()> {
.get_json(cx, &object_files, ignore_filename_regex.as_ref())
.context("failed to get json")?;

if let Some(fail_under_functions) = cx.args.cov.fail_under_functions {
// Handle --fail-under-functions.
let functions_percent = json
.get_coverage_percent(CoverageKind::Functions)
.context("failed to get function coverage")?;
if functions_percent < fail_under_functions {
term::error::set(true);
}
}

if let Some(fail_under_lines) = cx.args.cov.fail_under_lines {
// Handle --fail-under-lines.
let lines_percent = json.get_lines_percent().context("failed to get line coverage")?;
let lines_percent = json
.get_coverage_percent(CoverageKind::Lines)
.context("failed to get line coverage")?;
if lines_percent < fail_under_lines {
term::error::set(true);
}
}

if let Some(fail_under_regions) = cx.args.cov.fail_under_regions {
// Handle --fail-under-regions.
let regions_percent = json
.get_coverage_percent(CoverageKind::Regions)
.context("failed to get region coverage")?;
if regions_percent < fail_under_regions {
term::error::set(true);
}
}

if let Some(fail_uncovered_functions) = cx.args.cov.fail_uncovered_functions {
// Handle --fail-uncovered-functions.
let uncovered =
Expand Down

0 comments on commit 5e6e03a

Please sign in to comment.