Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add --fail-under-{functions,regions} options #323

Merged
merged 1 commit into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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