Skip to content

Fix misleading code coverage measurement (#630) - #632

Merged
Nikola Metulev (nmetulev) merged 1 commit into
mainfrom
azchohfi-cli-test-coverage
Jul 15, 2026
Merged

Fix misleading code coverage measurement (#630)#632
Nikola Metulev (nmetulev) merged 1 commit into
mainfrom
azchohfi-cli-test-coverage

Conversation

@azchohfi

Copy link
Copy Markdown
Contributor

Fixes #630.

Problem

The MSTest --coverage run counted auto-generated interop — CsWin32 P/Invoke thunks (NativeMethods.g.cs), ComInterfaceGenerator COM shims (D3D11 / UI Automation), and RegexGenerator state machines — emitted into obj\**. That inflated the denominator to ~101k lines and reported a meaningless ~18% overall. The real hand-written surface is ~21k lines at ~48.8%.

This is the first PR of the test-coverage initiative and changes only the measurement, not any test content, so every later coverage PR is measured against an honest denominator.

Changes

  • src/winapp-CLI/coverage.runsettings (new) — a Microsoft Code Coverage runsettings that excludes generated code only: obj\**, *.g.cs, *.Designer.cs, and the [GeneratedCode] attribute. No hand-written service or logic is excluded — the hardware/COM/GPU interop (UiAutomationService, WgcCapture, keyboard/mouse input) stays in the denominator and will be covered by real tests (unit logic-seams + an in-process UI test) in follow-up PRs.
  • scripts/build-cli.ps1 — wires --coverage-settings into the existing test run so CI and local numbers match.
  • scripts/coverage-report.ps1 (new) — dev/CI tool: runs the suite with the settings, parses the Cobertura report over product source only (de-duping line hits across partial classes / build configs), and prints a per-directory + top-uncovered breakdown and an overall %. Supports -Area, -Filter, -Threshold (gate), -CoberturaPath, -CsvOut. Propagates the test run's exit code so it can't green-light a run whose tests failed.
  • src/winapp-CLI/WinApp.Cli.Tests/README.md — documents the coverage scope and the "generated code is the only exclusion" policy.

Coverage scope policy

  • Exclude only generated code. That single change is what turns the misleading ~18% into the real figure.
  • Do not exclude any service or hand-written logic to grow the number.
  • Meaningful use-case tests first, then unit tests to close the remaining gaps, driving the hand-written surface toward ≥95%.

Baseline (measured with this tooling)

Overall 48.71% — 176 files, 10,286 covered / 21,116 valid lines. Per-directory: Services ~41%, Commands ~63%, Helpers ~59%, others tracked in #630.

Validation

  • coverage-report.ps1 run end-to-end (Release): prints the full breakdown and, on the current suite's pre-existing failures, correctly still emits coverage and exits non-zero (validated: exit code 2 propagated).
  • The pre-existing test failures are the npm dist/cli.js-missing E2E tests (run npm run build in src/winapp-npm), unrelated to this PR.

🤖 Reviewed with the winappcli pr-review skill (8 specialists + an independent GPT-family cross-check) before opening; findings addressed.

The `--coverage` run counted auto-generated interop (CsWin32 P/Invoke thunks,
ComInterfaceGenerator COM shims, RegexGenerator) emitted into obj\**, inflating
the denominator to ~101k lines and reporting a meaningless ~18% overall. The real
hand-written surface is ~21k lines at ~48.8%.

This changes only the measurement, not test content:
- Add src/winapp-CLI/coverage.runsettings excluding generated code only
  (obj\**, *.g.cs, *.Designer.cs, [GeneratedCode]). No hand-written service or
  logic is excluded -- the hardware/COM/GPU interop stays in the denominator and
  will be covered by real tests in follow-up PRs.
- Wire --coverage-settings into scripts/build-cli.ps1 so CI and local runs match.
- Add scripts/coverage-report.ps1: runs the suite with the settings, parses the
  Cobertura report over product source only, prints per-directory + top-uncovered
  breakdown and an overall %, with an optional -Threshold gate.
- Document the coverage scope/policy in the tests README.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 15, 2026 04:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes misleading code coverage measurement for the winapp CLI by excluding only auto-generated code from the coverage denominator, aligning CI and local reporting with the real hand-written product surface (per #630).

Changes:

  • Adds src/winapp-CLI/coverage.runsettings to exclude generated sources (obj\**, *.g.cs, *.Designer.cs) and [GeneratedCode] from coverage.
  • Wires the runsettings into scripts/build-cli.ps1 so CI/local --coverage runs use the same exclusions.
  • Adds scripts/coverage-report.ps1 to run coverage and produce a product-source-focused report (per-directory + top uncovered), and documents the policy in WinApp.Cli.Tests/README.md.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/winapp-CLI/WinApp.Cli.Tests/README.md Documents the “exclude generated only” coverage policy and how to measure coverage consistently.
src/winapp-CLI/coverage.runsettings Introduces a coverage configuration to exclude generated code from the denominator.
scripts/coverage-report.ps1 Adds a helper script to run coverage with the runsettings and report coverage over hand-written product source.
scripts/build-cli.ps1 Updates the existing test+coverage invocation to apply the checked-in runsettings.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions

Copy link
Copy Markdown
Contributor

Build Metrics Report

Binary Sizes

Artifact Baseline Current Delta
CLI (ARM64) 31.90 MB 31.90 MB ✅ 0.0 KB (0.00%)
CLI (x64) 32.22 MB 32.22 MB ✅ 0.0 KB (0.00%)
MSIX (ARM64) 13.39 MB 13.39 MB 📈 +0.1 KB (+0.00%)
MSIX (x64) 14.23 MB 14.23 MB 📈 +0.1 KB (+0.00%)
NPM Package 27.93 MB 27.93 MB 📈 +0.3 KB (+0.00%)
NuGet Package 27.95 MB 27.96 MB 📈 +0.5 KB (+0.00%)

Test Results

1637 passed, 1 skipped out of 1638 tests in 500.5s (+88.6s vs. baseline)

Test Coverage

48% line coverage, 53.5% branch coverage · ✅ +29.9% vs. baseline

CLI Startup Time

32ms median (x64, winapp --version) · 📉 -12ms vs. baseline


Updated 2026-07-15 05:14:46 UTC · commit ac40178 · workflow run

@nmetulev
Nikola Metulev (nmetulev) merged commit 4b50083 into main Jul 15, 2026
23 checks passed
@nmetulev
Nikola Metulev (nmetulev) deleted the azchohfi-cli-test-coverage branch July 15, 2026 05:40
Alexandre Zollinger Chohfi (azchohfi) added a commit that referenced this pull request Jul 15, 2026
…637)

Follow-up to #632. That PR fixed the biggest coverage distortion
(generated code inflating the denominator), but it still collected
coverage on an optimized **Release** build — where the Microsoft
coverage engine reports many standalone block-brace lines (`{`/`}`) as
`hits=0` even when the code executes (it drops or merges their sequence
points). That deflates line coverage and caps control-flow-heavy files
at ~70–80%.

## Fix

Collect coverage on a **Debug** build, which maps lines faithfully (the
standard configuration for .NET coverage). No test content changes.

- `coverage-report.ps1`: default `-Configuration Debug` (+ rationale in
help).
- `build-cli.ps1`: build + run the test/coverage pass in Debug so the
CI-posted cobertura `line-rate` matches local runs. Pass
`-p:TreatWarningsAsErrors=true` on the Debug build to keep the
warning-as-error gate that `Directory.Build.props` otherwise applies
only to Release. The shipped CLI is still the Release `dotnet publish` —
only the test run moves to Debug, and the Release artifact stays
exercised end-to-end by the sample/npm E2E suites.
- `README`: document why Debug and correct the baseline figure.

## Impact on the honest baseline

| | Overall | ManifestService.cs |
|---|---|---|
| Release (understated) | ~48.8% | 62–71% |
| **Debug (accurate)** | **~59.5%** | **81.7%** |

Same tests, ~11 points higher — the gap was pure Release brace
under-counting. This makes the number CI posts on PRs honest and
unblocks the per-file 95% target for the follow-up coverage PRs (the
~70–80% "ceiling" was a measurement artifact, not real code that
couldn't be covered).

## Review

Ran the repo's `pr-review` skill (7 specialists + a GPT multi-model
cross-check) and validated at runtime:
- Full Debug coverage run (1,637 pass / 1 skipped; 3 pre-existing
env-only E2E failures) → 59.5%.
- Debug build with `-p:TreatWarningsAsErrors=true` → 0 warnings, 0
errors.
- Confirmed packaging / MSIX / NuGet / docs-generation consume the
Release `publish` output, not the solution `bin`; the CI metrics
baseline transitions cleanly.

Closes out the Release-measurement half of #630.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Code coverage metric is misleading — generated interop code dominates the denominator

3 participants