Skip to content

Add RFC 019: Code coverage messages & consumer model for MTP#9906

Merged
Evangelink merged 5 commits into
mainfrom
dev/amauryleve/rfc-code-coverage-messages
Jul 13, 2026
Merged

Add RFC 019: Code coverage messages & consumer model for MTP#9906
Evangelink merged 5 commits into
mainfrom
dev/amauryleve/rfc-code-coverage-messages

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Summary

Adds RFC 019 (docs/RFCs/019-Code-Coverage-Messages.md), a design proposal for a first-class code coverage contract in Microsoft.Testing.Platform.

This RFC provides the design context for #9896, which introduces permanent public coverage API while the producer side does not yet exist. Since public API is effectively forever, the shape is captured here for team sign-off before implementation continues.

What it proposes

  • Counts, not percentages — measurements carry covered/coverable and derive %, matching every surveyed ecosystem (lcov, Cobertura, JaCoCo, Istanbul, coverage.py, llvm-cov, SonarQube generic, Codecov) and enabling honest cross-scope aggregation.
  • A closed, seeded, append-only CoverageMetric enum (Line/Statement/Branch/Method/Function/Block/Instruction/Region/Class/Condition/Complexity) with a single Custom escape hatch — instead of the prototype's 3-value Line/Branch/Method enum (which can't even name block, the primary metric of Microsoft's own dotnet-coverage).
  • Scope-aware measurements and thresholds (Overall → Module → … → File), so a UI can build a tree and thresholds can be per-scope.
  • A typed pointer to the rich artifact (TestCoverageReportMessage / CoverageReportReference) — per-line fidelity stays in the standardized report file rather than being duplicated over IPC.
  • An ITestCoverageResult consumer read model that correlates data once, so third-party extensions (HTML/UI report generators, PR-comment bots, dashboards, badges) — and the terminal itself — consume a single source of truth without re-aggregating.

Evidence

The design is grounded in a cross-language survey of how real coverage tools model their data (metric vocabularies and raw file formats), plus the precedent of purpose-built language-agnostic contracts (SonarQube generic coverage, Codecov), which both chose a small closed model and derive the rest.

Notes

This RFC was drafted with AI assistance during a review of #9896.

Proposes a cross-language-informed coverage contract for Microsoft.Testing.Platform: counts-based measurements, a seeded append-only metric enum, scope-aware threshold results, a typed report-artifact reference, and an ITestCoverageResult consumer read model. Provides design context for PR #9896.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5cec9403-3bea-4115-bd84-b02d37197b94
Copilot AI review requested due to automatic review settings July 13, 2026 14:18

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

Adds RFC 019, proposing a first-class code coverage contract for Microsoft.Testing.Platform.

Changes:

  • Defines count-based, scope-aware coverage messages and thresholds.
  • Proposes a correlated consumer read model and artifact references.
  • Documents platform wiring, exit-code behavior, and open design questions.
Show a summary per file
File Description
docs/RFCs/019-Code-Coverage-Messages.md Introduces the code coverage messages and consumer-model RFC.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 6
  • Review effort level: Medium

Comment thread docs/RFCs/019-Code-Coverage-Messages.md Outdated
Comment thread docs/RFCs/019-Code-Coverage-Messages.md Outdated
Comment thread docs/RFCs/019-Code-Coverage-Messages.md
Comment thread docs/RFCs/019-Code-Coverage-Messages.md
Comment thread docs/RFCs/019-Code-Coverage-Messages.md Outdated
Comment thread docs/RFCs/019-Code-Coverage-Messages.md Outdated
@Evangelink Evangelink changed the title Add RFC 019: Code coverage messages &amp; consumer model for MTP Add RFC 019: Code coverage messages & consumer model for MTP Jul 13, 2026
- Scope levels are overlapping dimensions, not a strict tree (type<->file is many-to-many); replace ParentPath with a non-authoritative ContainerHint and point exact containment at the report artifact.
- Threshold aggregation now names its population via AggregatedOver so (scope, metric, aggregation) is unambiguous; add CoverageAggregation.None.
- Add ProducerId to measurements/thresholds/results so multiple collectors are not silently merged.
- Validate threshold percentages (reject NaN/inf/out-of-range) and enforce the Custom metric-name invariant.
- Fix the Custom-metric indexer ambiguity with a GetCustom(name) lookup.
- Make the consumer example compile against the real ITestSessionLifetimeHandler / IDataConsumer contracts.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5cec9403-3bea-4115-bd84-b02d37197b94
Copilot AI review requested due to automatic review settings July 13, 2026 17:49

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.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 8
  • Review effort level: Medium

Comment thread docs/RFCs/019-Code-Coverage-Messages.md Outdated
Comment thread docs/RFCs/019-Code-Coverage-Messages.md
Comment thread docs/RFCs/019-Code-Coverage-Messages.md Outdated
Comment thread docs/RFCs/019-Code-Coverage-Messages.md Outdated
Comment thread docs/RFCs/019-Code-Coverage-Messages.md
Comment thread docs/RFCs/019-Code-Coverage-Messages.md Outdated
Comment thread docs/RFCs/019-Code-Coverage-Messages.md
Comment thread docs/RFCs/019-Code-Coverage-Messages.md Outdated
- CoverageScope: enforce name invariant (only Overall is unnamed); exclude the non-authoritative ContainerHint from equality/hashing so it never splits scope identity.
- Require a non-empty ProducerId on measurements, thresholds, reports and CoverageMetricResult so multi-collector data is never silently merged.
- Validate coverableCount and coveredCount separately for correct parameter attribution.
- Threshold: carry HasCoverableData + TreatNoDataAsFailure so no-data is distinct from genuine 0% and Passed applies an explicit policy.
- Report message: enforce the Custom<->CustomFormatName discriminated-pair invariant both ways; require a report path.
- Read-model classes: add explicit (internal) constructors so they compile under nullable/warnings-as-errors and aren't publicly constructible in an invalid state.
- Consumer example: use a stable literal Uid instead of nameof(...).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5cec9403-3bea-4115-bd84-b02d37197b94
Copilot AI review requested due to automatic review settings July 13, 2026 18:04

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.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 3
  • Review effort level: Low

Comment thread docs/RFCs/019-Code-Coverage-Messages.md
Comment thread docs/RFCs/019-Code-Coverage-Messages.md Outdated
Comment thread docs/RFCs/019-Code-Coverage-Messages.md
@Evangelink Evangelink added the state/needs-review Awaiting review from the team. label Jul 13, 2026
@Evangelink Evangelink enabled auto-merge (squash) July 13, 2026 19:26
- Enforce the Custom<->CustomMetricName discriminated pair both ways on TestCoverageMessage and TestCoverageThresholdMessage (reject a custom name when metric is not Custom).
- Normalize ActualPercentage to a stable 0 in the no-data case so NaN/Infinity can never leak into consumers that read it without checking HasCoverableData.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5cec9403-3bea-4115-bd84-b02d37197b94
Copilot AI review requested due to automatic review settings July 13, 2026 19:28

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.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 5
  • Review effort level: Medium

Comment thread docs/RFCs/019-Code-Coverage-Messages.md Outdated
Comment thread docs/RFCs/019-Code-Coverage-Messages.md
Comment thread docs/RFCs/019-Code-Coverage-Messages.md Outdated
Comment thread docs/RFCs/019-Code-Coverage-Messages.md Outdated
Comment thread docs/RFCs/019-Code-Coverage-Messages.md
- Reframe the raw layer as a deliberately reduced line/branch common denominator, not full-fidelity primitives (statement/instruction/block/region can't be reconstructed from line+branch); authoritative detail lives in the artifact.
- Derive all three messages from DataWithSessionUid instead of PropertyBagData and add SessionUid to the correlation key, so multi-session hosts attribute coverage correctly.
- Give CoverageScopeSummary compilable indexer/GetCustom implementations backed by Metrics (incl. the Custom-rejection and ordinal name match).
- Make the read-model DTO constructors public (validated, get-only props) so third-party consumers can construct snapshots and fakes in tests without reflection.
- Define the full correlation key (SessionUid, ProducerId, Scope, Metric, CustomMetricName-when-Custom) and specify last-write-wins on duplicate keys.
- Update the property-bag open question to reflect the new base.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 5cec9403-3bea-4115-bd84-b02d37197b94
Copilot AI review requested due to automatic review settings July 13, 2026 19:56
@Evangelink Evangelink merged commit bced1e4 into main Jul 13, 2026
50 checks passed
@Evangelink Evangelink deleted the dev/amauryleve/rfc-code-coverage-messages branch July 13, 2026 19:57

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.

Review details

  • Files reviewed: 1/1 changed files
  • Comments generated: 5
  • Review effort level: Medium


public override bool Equals(object? obj) => obj is CoverageScope s && Equals(s);

public override int GetHashCode() => HashCode.Combine(Level, Name);
Comment on lines +596 to +603
// Public so third-party report generators can construct snapshots in their own unit tests
// and build fakes of ITestCoverageResult without reflection. Get-only properties still
// prevent mutation after construction.
public CoverageScopeSummary(CoverageScope scope, IReadOnlyList<CoverageMetricResult> metrics)
{
Scope = scope;
Metrics = metrics ?? throw new ArgumentNullException(nameof(metrics));
}
Comment on lines +663 to +667
Metric = metric;
CoveredCount = coveredCount;
CoverableCount = coverableCount;
ProducerId = producerId;
CustomMetricName = customMetricName;
Comment on lines +694 to +697
Path = path;
Format = format;
ProducerId = producerId;
CustomFormatName = customFormatName;
Comment on lines +761 to +766
The read model correlates messages into `CoverageScopeSummary` / threshold / report entries
using the **full** key, not just `(scope, metric)`:

```text
(SessionUid, ProducerId, Scope, Metric, CustomMetricName when Metric == Custom)
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-review Awaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants