diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2034b0..1477917 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,3 +36,30 @@ jobs: grep -E '^test result:|^running' test-output.txt >> "$GITHUB_STEP_SUMMARY" || true echo '```' >> "$GITHUB_STEP_SUMMARY" fi + + coverage: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@stable + with: + components: llvm-tools-preview + - uses: Swatinem/rust-cache@v2 + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + - name: Generate coverage report + run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info + - name: Upload to Codecov + uses: codecov/codecov-action@v5 + with: + files: lcov.info + fail_ci_if_error: true + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + - name: Job summary + if: always() + run: | + echo "## Coverage" >> "$GITHUB_STEP_SUMMARY" + echo '```' >> "$GITHUB_STEP_SUMMARY" + cargo llvm-cov report --summary-only 2>/dev/null >> "$GITHUB_STEP_SUMMARY" || true + echo '```' >> "$GITHUB_STEP_SUMMARY" diff --git a/.gitignore b/.gitignore index 96ef6c0..32a86fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target Cargo.lock +lcov.info diff --git a/Makefile b/Makefile index 479dcff..55f62ea 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,17 @@ -.PHONY: help check test fmt lint doc ci +.PHONY: help check test fmt lint doc ci cov cov-report cov-regions install-dev help: @echo "Available targets:" - @echo " check Check workspace compiles" - @echo " test Run all tests" - @echo " fmt Format code" - @echo " lint Run clippy with warnings as errors" - @echo " doc Build and open docs in browser" - @echo " ci Run all CI checks locally (fmt, clippy, test, doc)" + @echo " check Check workspace compiles" + @echo " test Run all tests" + @echo " fmt Format code" + @echo " lint Run clippy with warnings as errors" + @echo " doc Build and open docs in browser" + @echo " cov Generate HTML coverage report and open it" + @echo " cov-report Print uncovered lines" + @echo " cov-regions Print uncovered regions with context" + @echo " ci Run all CI checks locally (fmt, clippy, test, doc)" + @echo " install-dev Install local dev tools (cargo-llvm-cov)" # Check workspace compiles check: @@ -29,6 +33,23 @@ lint: doc: cargo doc --no-deps -p wavekat-core --all-features --open +# Install local dev tools (run once after cloning) +install-dev: + rustup component add llvm-tools-preview + cargo install cargo-llvm-cov + +# Generate HTML coverage report and open it in the browser +cov: + cargo llvm-cov --all-features --open + +# Print uncovered lines only (paste into Claude Code to write tests) +cov-report: + @cargo llvm-cov --all-features --text 2>/dev/null | grep -E '\|[[:space:]]+0\|' || echo "No uncovered lines." + +# Print uncovered regions with source context (paste into Claude Code to write tests) +cov-regions: + @cargo llvm-cov --all-features --text 2>/dev/null | grep -B1 '\^0' || echo "No uncovered regions." + # Run all CI checks locally (mirrors .github/workflows/ci.yml) ci: cargo fmt --all -- --check diff --git a/README.md b/README.md index f0d881b..3251d56 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![Crates.io](https://img.shields.io/crates/v/wavekat-core.svg)](https://crates.io/crates/wavekat-core) [![docs.rs](https://docs.rs/wavekat-core/badge.svg)](https://docs.rs/wavekat-core) +[![codecov](https://codecov.io/gh/wavekat/wavekat-core/branch/main/graph/badge.svg)](https://codecov.io/gh/wavekat/wavekat-core) Shared types for the WaveKat audio processing ecosystem. diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..6e99a73 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,13 @@ +coverage: + status: + project: + default: + target: auto # must not drop below current coverage + threshold: 1% # allow up to 1% drop before failing + patch: + default: + target: 80% # new code in PRs must be at least 80% covered + +comment: + layout: "reach,diff,flags,tree" + behavior: default # post once, update on new commits diff --git a/crates/wavekat-core/src/audio.rs b/crates/wavekat-core/src/audio.rs index 2b180b7..d521573 100644 --- a/crates/wavekat-core/src/audio.rs +++ b/crates/wavekat-core/src/audio.rs @@ -311,4 +311,42 @@ mod tests { assert_eq!(frame.samples().as_ptr(), ptr); assert_eq!(frame.sample_rate(), 24000); } + + #[test] + fn into_samples_vec_f32() { + let samples = vec![0.1f32, -0.2, 0.3]; + let frame = AudioFrame::new(&samples, 16000); + assert!(matches!(frame.samples, Cow::Borrowed(_))); + assert_eq!(frame.samples(), &[0.1, -0.2, 0.3]); + } + + #[test] + fn into_samples_array_f32() { + let samples = [0.1f32, -0.2, 0.3]; + let frame = AudioFrame::new(&samples, 16000); + assert!(matches!(frame.samples, Cow::Borrowed(_))); + assert_eq!(frame.samples(), &[0.1, -0.2, 0.3]); + } + + #[test] + fn into_samples_vec_i16() { + let samples: Vec = vec![0, 16384, i16::MIN]; + let frame = AudioFrame::new(&samples, 16000); + assert!(matches!(frame.samples, Cow::Owned(_))); + let s = frame.samples(); + assert!((s[0] - 0.0).abs() < f32::EPSILON); + assert!((s[1] - 0.5).abs() < 0.001); + assert!((s[2] - -1.0).abs() < f32::EPSILON); + } + + #[test] + fn into_samples_array_i16() { + let samples: [i16; 3] = [0, 16384, i16::MIN]; + let frame = AudioFrame::new(&samples, 16000); + assert!(matches!(frame.samples, Cow::Owned(_))); + let s = frame.samples(); + assert!((s[0] - 0.0).abs() < f32::EPSILON); + assert!((s[1] - 0.5).abs() < 0.001); + assert!((s[2] - -1.0).abs() < f32::EPSILON); + } } diff --git a/docs/02-test-coverage.md b/docs/02-test-coverage.md new file mode 100644 index 0000000..342b26e --- /dev/null +++ b/docs/02-test-coverage.md @@ -0,0 +1,37 @@ +# 02 — Test Coverage + +## Overview + +Code coverage is measured on every CI run using [`cargo-llvm-cov`](https://github.com/taiki-e/cargo-llvm-cov) and reported to [Codecov](https://codecov.io/gh/wavekat/wavekat-core). + +## How It Works + +The `coverage` job in `.github/workflows/ci.yml`: + +1. Installs `cargo-llvm-cov` via [`taiki-e/install-action`](https://github.com/taiki-e/install-action). +2. Runs the full test suite with LLVM instrumentation across all features: + ```sh + cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info + ``` +3. Uploads the LCOV report to Codecov. + +## Viewing Coverage + +- **Badge** — shown in `README.md`, reflects the latest `main` branch coverage. +- **Codecov dashboard** — `https://codecov.io/gh/wavekat/wavekat-core` for line-by-line detail and PR diff coverage. + +## Setup (one-time) + +1. Sign in to [codecov.io](https://codecov.io) with your GitHub account. +2. Enable the `wavekat/wavekat-core` repository. +3. Add the `CODECOV_TOKEN` secret to the GitHub repository settings. + +## Running Locally + +```sh +# Install dev tools once after cloning +make install-dev + +# Generate an HTML report and open it +make cov +```