From d85c1e39ce80d7832d38f5c1ae07b2b41909da29 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Tue, 3 Nov 2026 11:45:43 +0100 Subject: [PATCH] ci: gate Core coverage and exclude test tooling from the report Coverage settings exclude the fuzz harness, server probe and test assembly; CI fails if Core drops below 95% lines or 88% branches. The gitignore no longer swallows tests/coverage.runsettings. --- .github/workflows/ci.yml | 10 ++++++++++ .gitignore | 1 + tests/check-coverage.py | 37 +++++++++++++++++++++++++++++++++++++ tests/coverage.runsettings | 18 ++++++++++++++++++ 4 files changed, 66 insertions(+) create mode 100755 tests/check-coverage.py create mode 100644 tests/coverage.runsettings diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7986e4..ee27c5e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,10 +47,20 @@ jobs: - name: Test run: > dotnet test tests/PalladiumWallet.Tests --no-build -c Release + --settings tests/coverage.runsettings --logger "trx;LogFileName=test-results.trx" --collect:"XPlat Code Coverage" --results-directory TestResults + # Coverage gate (Ubuntu only, one report is enough): fails the job if Core drops below the + # thresholds. App and Cli are reported but not gated yet. + - name: Coverage gate + if: matrix.os == 'ubuntu-latest' + shell: bash + run: | + report=$(ls TestResults/*/coverage.cobertura.xml | head -1) + python3 tests/check-coverage.py "$report" PalladiumWallet.Core:95:88 PalladiumWallet.App PalladiumWallet.Cli + - name: Upload test results if: always() uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 19a2b91..27c605d 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ packages/ TestResults/ coverage/ coverage.* +!tests/coverage.runsettings *.coverage *.coveragexml *.trx diff --git a/tests/check-coverage.py b/tests/check-coverage.py new file mode 100755 index 0000000..e483831 --- /dev/null +++ b/tests/check-coverage.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +"""Fail if Cobertura line/branch coverage of an assembly drops below a threshold. + +usage: check-coverage.py COVERAGE.xml PACKAGE:MIN_LINE[:MIN_BRANCH] [...] +example: check-coverage.py coverage.cobertura.xml PalladiumWallet.Core:90:80 +Prints a per-package summary either way. Stdlib only (runs on any CI runner).""" +import sys +import xml.etree.ElementTree as ET + + +def main() -> int: + if len(sys.argv) < 3: + print(__doc__) + return 2 + root = ET.parse(sys.argv[1]).getroot() + packages = {p.get("name"): p for p in root.iter("package")} + failed = False + for spec in sys.argv[2:]: + name, *limits = spec.split(":") + min_line = float(limits[0]) if limits else 0.0 + min_branch = float(limits[1]) if len(limits) > 1 else 0.0 + pkg = packages.get(name) + if pkg is None: + print(f"FAIL {name}: not present in the report") + failed = True + continue + line = float(pkg.get("line-rate")) * 100 + branch = float(pkg.get("branch-rate")) * 100 + ok = line >= min_line and branch >= min_branch + failed |= not ok + print(f"{'ok ' if ok else 'FAIL'} {name}: lines {line:.1f}% (min {min_line:g}), " + f"branches {branch:.1f}% (min {min_branch:g})") + return 1 if failed else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/coverage.runsettings b/tests/coverage.runsettings new file mode 100644 index 0000000..eed398c --- /dev/null +++ b/tests/coverage.runsettings @@ -0,0 +1,18 @@ + + + + + + + + cobertura + [PalladiumWallet.Fuzz]*,[PalladiumWallet.ServerProbe]*,[PalladiumWallet.Tests]*,[Avalonia*]* + GeneratedCodeAttribute,CompilerGeneratedAttribute,ExcludeFromCodeCoverageAttribute + true + + + + +