diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 71015b0dc..955b7a780 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -189,3 +189,18 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Build run: cargo +nightly build --target x86_64-unknown-uefi --verbose -p uefi-std-example + coverage: + name: Test Coverage + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + - uses: Swatinem/rust-cache@v2 + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + - name: Generate code coverage + run: cargo xtask cov --lcov + - name: Upload code coverage + uses: codecov/codecov-action@v4 + with: + files: target/lcov diff --git a/xtask/src/cargo.rs b/xtask/src/cargo.rs index c54cc6530..bca199a8c 100644 --- a/xtask/src/cargo.rs +++ b/xtask/src/cargo.rs @@ -186,6 +186,10 @@ impl TargetTypes { pub enum CargoAction { Build, Clippy, + Coverage { + lcov: bool, + open: bool, + }, Doc { open: bool, document_private_items: bool, @@ -251,6 +255,17 @@ impl Cargo { tool_args.extend(["-D", "warnings"]); } } + CargoAction::Coverage { lcov, open } => { + action = "llvm-cov"; + if lcov { + extra_args.extend(["--lcov", "--output-path", "target/lcov"]); + } else { + extra_args.push("--html"); + } + if open { + extra_args.push("--open"); + } + } CargoAction::Doc { open, document_private_items, diff --git a/xtask/src/main.rs b/xtask/src/main.rs index f735486cd..53e46ded0 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -12,12 +12,12 @@ mod tpm; mod util; use crate::opt::{FmtOpt, TestOpt}; -use anyhow::Result; +use anyhow::{bail, Result}; use arch::UefiArch; use cargo::{Cargo, CargoAction, Feature, Package, TargetTypes}; use clap::Parser; use itertools::Itertools; -use opt::{Action, BuildOpt, ClippyOpt, DocOpt, Opt, QemuOpt, TpmVersion}; +use opt::{Action, BuildOpt, ClippyOpt, CovOpt, DocOpt, Opt, QemuOpt, TpmVersion}; use std::process::Command; use util::run_cmd; @@ -85,6 +85,30 @@ fn clippy(opt: &ClippyOpt) -> Result<()> { run_cmd(cargo.command()?) } +/// Generate a code coverage report. +fn code_coverage(opt: &CovOpt) -> Result<()> { + if has_cmd("cargo-llvm-cov") { + let cargo = Cargo { + action: CargoAction::Coverage { + lcov: opt.lcov, + open: opt.open, + }, + features: Feature::more_code(*opt.unstable, false), + // Leave out uefi-macros; the compilation tests will just make + // things slower without contributing anything to the coverage + // report. + packages: vec![Package::UefiRaw, Package::Uefi], + release: false, + target: None, + warnings_as_errors: false, + target_types: TargetTypes::Default, + }; + run_cmd(cargo.command()?) + } else { + bail!("cargo-llvm-cov not found, see https://github.com/taiki-e/cargo-llvm-cov"); + } +} + /// Build docs. fn doc(opt: &DocOpt) -> Result<()> { let cargo = Cargo { @@ -305,6 +329,7 @@ fn main() -> Result<()> { Action::Build(build_opt) => build(build_opt), Action::CheckRaw(_) => check_raw::check_raw(), Action::Clippy(clippy_opt) => clippy(clippy_opt), + Action::Cov(cov_opt) => code_coverage(cov_opt), Action::Doc(doc_opt) => doc(doc_opt), Action::GenCode(gen_opt) => device_path::gen_code(gen_opt), Action::Miri(_) => run_miri(), diff --git a/xtask/src/opt.rs b/xtask/src/opt.rs index af3f06250..aa41bd5d4 100644 --- a/xtask/src/opt.rs +++ b/xtask/src/opt.rs @@ -68,6 +68,7 @@ pub enum Action { Build(BuildOpt), CheckRaw(CheckRawOpt), Clippy(ClippyOpt), + Cov(CovOpt), Doc(DocOpt), GenCode(GenCodeOpt), Miri(MiriOpt), @@ -105,6 +106,21 @@ pub struct ClippyOpt { pub warning: WarningOpt, } +/// Generate a code coverage report. +#[derive(Debug, Parser)] +pub struct CovOpt { + /// Open the output in a browser. + #[clap(long, action)] + pub open: bool, + + /// Output raw lcov data instead of HTML. + #[clap(long, action, conflicts_with = "open")] + pub lcov: bool, + + #[clap(flatten)] + pub unstable: UnstableOpt, +} + /// Build the docs for the uefi packages. #[derive(Debug, Parser)] pub struct DocOpt {