Skip to content

Add test coverage messages, coverage threshold checks, and summary#9896

Merged
Evangelink merged 30 commits into
microsoft:mainfrom
james-newell-forge:feat/add-coverage-messages
Jul 22, 2026
Merged

Add test coverage messages, coverage threshold checks, and summary#9896
Evangelink merged 30 commits into
microsoft:mainfrom
james-newell-forge:feat/add-coverage-messages

Conversation

@james-newell-forge

@james-newell-forge james-newell-forge commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds the RFC 019 code-coverage message and consumer model to Microsoft.Testing.Platform. Coverage producers can publish counts-based measurements, scope-aware threshold evaluations, and report references through the data-consumer pipeline. The platform correlates them into ITestCoverageResult, renders them in the terminal summary, and returns exit code 14 when a threshold fails.

Note that this PR was assisted with Claude Opus 4.8 and GitHub Copilot.

Public API

  • CoverageMetric provides the seeded append-only metric vocabulary, including Custom.
  • CoverageScope and CoverageScopeLevel identify overall, module, assembly, namespace, type, and file scopes.
  • TestCoverageMessage reports CoveredCount and CoverableCount; Percentage is derived.
  • TestCoverageThresholdMessage reports scope, metric, aggregation, actual/required percentages, no-data policy, and a derived Passed value.
  • TestCoverageReportMessage references a typed rich coverage artifact.
  • ITestCoverageResult exposes correlated scope summaries, thresholds, report references, and HasThresholdFailure for third-party consumers.

Terminal output

Test run summary: Passed!
  total: 22
  failed: 0
  succeeded: 22
  skipped: 0
  duration: 1s 288ms

  Code Coverage Summary:
    Total - Line: 96.6%
    Total - Branch: 77.7%
    Total - Method: 91.7%

  Coverage Threshold Results:
    Total - Line (Total over Module): 96.6% >= 70.0% threshold
    Total - Branch (Total over Module): 77.7% >= 70.0% threshold
    Total - Method (Total over Module): 91.7% >= 70.0% threshold

Scope names and custom metric names are normalized before terminal rendering. No-data policy results are rendered explicitly, and near-threshold comparisons retain enough precision to remain truthful.

Platform behavior

  • A single TestCoverageResult correlates data by session, producer, scope, metric, and custom metric name; duplicate full keys use last-write-wins while preserving stable order.
  • Coverage is registered for both in-process framework and out-of-process controller pipelines.
  • Threshold failures override only an otherwise-successful run with CoverageThresholdFailed (14), while honoring --ignore-exit-code 14.
  • Hot-reload sessions reset accumulated coverage state between runs.

Tests

  • Unit coverage for message/read-model validation, correlation, deduplication, ordering, reset, no-data policy, precision, localization-safe labels, and terminal control-character normalization.
  • Restored all unrelated terminal reporter regression coverage from main.
  • Acceptance coverage for in-process and controller exit-code, terminal rendering, and ignore-exit-code behavior.

Coverage capabilities

  • Extensions can resolve ITestCoverageCapabilities and inspect EnabledProducerUids from a session/data-consumer callback. This lets a report generator detect whether another enabled extension declares coverage messages and emit a warning when none is available.
  • JSON-RPC initialize advertises testing.supportsTestCoverageMessages.
  • Remote capabilities advertise protocol support only; enabled producer UIDs remain application-local because framework and per-session extensions may not exist yet during initialize.

Copilot AI review requested due to automatic review settings July 13, 2026 10:48

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 first-class coverage messages, terminal summaries, threshold tracking, and threshold-specific exit handling to Microsoft.Testing.Platform.

Changes:

  • Introduces public coverage and threshold message contracts.
  • Registers consumers and renders coverage results in terminal summaries.
  • Returns exit code 14 when coverage thresholds fail.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
test/.../TerminalTestReporterTests.cs Tests coverage-summary rendering.
.../Services/TestCoverageResult.cs Tracks coverage and failed thresholds.
.../Services/ITestCoverageResult.cs Defines the internal result contract.
.../PublicAPI/PublicAPI.Unshipped.txt Declares new public APIs.
.../TerminalOutputDevice.SessionLifecycle.cs Renders coverage after sessions.
.../TerminalOutputDevice.DataConsumption.cs Consumes coverage messages.
.../TerminalOutputDevice.cs Registers message types and buffers.
.../TerminalTestReporter.Summary.cs Formats coverage summaries.
.../Messages/TestCoverageMessages.cs Adds coverage message contracts.
.../InternalAPI/InternalAPI.Unshipped.txt Records new internal APIs.
.../Hosts/TestHostControllersTestHost.cs Handles controller threshold exits.
.../Hosts/TestHostBuilder.Framework.cs Registers the framework consumer.
.../Hosts/TestHostBuilder.CommonServices.cs Registers the coverage service.
.../Hosts/ConsoleTestHost.cs Handles in-process threshold exits.
.../Helpers/ExitCodes.cs Adds exit code 14.
...Extensions.TrxReport/...Unshipped.txt Updates internal API baseline.
...Extensions.Retry/...Unshipped.txt Updates internal API baseline.
...Extensions.MSBuild/InternalAPI.Unshipped.txt Updates internal API baseline.
...Extensions.JUnitReport/...Unshipped.txt Updates internal API baseline.
...Extensions.HtmlReport/...Unshipped.txt Updates internal API baseline.
...Extensions.HangDump/...Unshipped.txt Updates internal API baseline.
...Extensions.GitHubActionsReport/...Unshipped.txt Updates internal API baseline.
...Extensions.CtrfReport/...Unshipped.txt Updates internal API baseline.
...Extensions.AzureDevOpsReport/...Unshipped.txt Updates internal API baseline.

Comment thread src/Platform/Microsoft.Testing.Platform/Hosts/ConsoleTestHost.cs Outdated
Comment thread src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControllersTestHost.cs Outdated
@Evangelink

Copy link
Copy Markdown
Member

Hi @james-newell-forge — thanks for the contribution! I did a security pass and a fairly deep code review. Sharing findings below.

Prompt-injection / security check

No prompt injection found. The only AI-related text is the benign "this PR was assisted with Claude Opus 4.8" note in the description. Nothing in the diff attempts to manipulate reviewers or automation. 👍

Before going further: goal + tracking issue

A couple of process points before we dig into the code:

  1. What's the concrete goal here? Could you describe the end-to-end scenario you're trying to enable? Right now the PR adds message contracts, a consumer, terminal rendering, and a new exit code — but nothing in the codebase actually produces TestCoverageMessage / TestCoverageThresholdMessage. Your own note ("Changes would need to be made to mimic the coverage output in the SDK via IPC") confirms the producer side is missing, so this is effectively a public API + dead consumption path with no way to exercise it today. Knowing the real target scenario will help us judge the shape of the API.

  2. Please get an approved tracking issue first. This introduces a brand-new public API surface to Microsoft.Testing.Platform (two message types, three enums, a new exit code, a PublicAPI.Unshipped.txt contract). Per our contributing guidelines we want new features — especially new public API — to have an approved design/issue before implementation, since public API is essentially permanent. Could you open a Feature issue (use the native GitHub Feature issue type, not a type/feature label) describing the design so the team can sign off on the surface? That avoids you investing more effort in an API shape we might want to change.

I'll hold off requesting detailed changes until we align on the above, but here's the technical review so it's all in one place.

Design-level concerns

  • No producer / can't be tested end-to-end. Because nothing emits these messages, there are no acceptance/integration tests exercising the real path (terminal output + exit code 14). The added unit tests only call AppendCoverageSummary directly. This makes it hard to validate the feature actually works as a whole.
  • Duplicated state. Coverage entries are buffered twice: once in TerminalOutputDevice (_coverageEntries / _coverageThresholdEntries) and once in TestCoverageResult. Two independent consumers accumulating the same data is easy to get out of sync. Consider having the terminal device read from the single ITestCoverageResult service rather than maintaining its own lists.
  • Public API naming. TestCoverageThresholdMessage.Stat (of type CoverageThresholdStat) is an unclear abbreviation for a permanent public property. Something like Statistic / AggregationKind reads better. Same for the enum name.
  • PropertyBagData with an empty bag. Both messages derive from PropertyBagData but never populate the property bag — consumers that inspect the bag generically will see nothing. Is PropertyBagData the right base, or should these carry their data as bag properties?

Correctness / consistency issues

  • Missing localization (guideline violation). The user-facing terminal strings — "Code Coverage Summary:", "Coverage Threshold Results:", and the "Test coverage" / "Reports code coverage..." display names — are hardcoded English. Every other terminal summary string in TerminalTestReporter goes through TerminalResources.resx (e.g. TerminalResources.OutOfProcessArtifactsProduced). New strings must be added to the .resx and the .xlf files regenerated via /t:UpdateXlf.
  • Culture-sensitive number formatting. {entry.Value:F1} uses the current culture, so on some locales you'll get 96,6%. Terminal diagnostics elsewhere generally format with CultureInfo.InvariantCulture.
  • Thread safety. ConsumeAsync does an unguarded List.Add. Please confirm the data-consumer pump guarantees serialized calls per consumer; if not, these lists need synchronization.

Minor

  • The exit-code check is duplicated verbatim in ConsoleTestHost and TestHostControllersTestHost; a shared helper would avoid drift.
  • The 9 InternalAPI.Unshipped.txt edits across extension projects are just re-baselining the new ExitCode value — expected, but worth a sanity check that they were regenerated rather than hand-edited.

Happy to help once there's an approved issue and we know the target scenario. Thanks again!

Copilot AI review requested due to automatic review settings July 13, 2026 11:25

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

Copilot reviewed 41 out of 41 changed files in this pull request and generated 2 comments.

Comment thread src/Platform/Microsoft.Testing.Platform/Hosts/ConsoleTestHost.cs Outdated
Comment thread src/Platform/Microsoft.Testing.Platform/Hosts/TestHostControllersTestHost.cs Outdated

@Evangelink Evangelink left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking PR until my previous points are addressed

@microsoft-github-policy-service microsoft-github-policy-service Bot added the needs/author-feedback Waiting on the original author. label Jul 13, 2026
Copilot AI review requested due to automatic review settings July 13, 2026 12:08
@microsoft-github-policy-service microsoft-github-policy-service Bot added needs/attention Needs maintainer attention. and removed needs/author-feedback Waiting on the original author. labels Jul 13, 2026

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

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

Comment thread src/Platform/Microsoft.Testing.Platform/Services/TestApplicationResult.cs Outdated
@james-newell-forge

Copy link
Copy Markdown
Contributor Author

The goal on this change is to allow coverage extensions like Microsoft's coverage or coverlet to add threshold coverage checks (which would address microsoft/codecoverage#160). In addition, coverage summaries can be displayed as a first-class citizen. Will add a tracking issue.

@james-newell-forge

Copy link
Copy Markdown
Contributor Author

Thread safety. ConsumeAsync does an unguarded List.Add. Please confirm the data-consumer pump guarantees serialized calls per consumer; if not, these lists need synchronization.

TestCoverageResult.ConsumeAsync thread-safety

Verdict: List<T>.Add isn't thread-safe, but the usage here is — the message bus serializes ConsumeAsync per consumer.

Why it's safe

  • No concurrent Add. Each IDataConsumer gets one AsyncConsumerDataProcessor with a single-reader channel (SingleReader = true) and one background loop that awaits each ConsumeAsync before reading the next. Many threads may PublishAsync, but dispatch is serialized → two .Adds never overlap.
  • Safe reads. All readers run only after DrainDataAsync() (+ DisableAsync()):
    • the hosts read HasCoverageThresholdFailure for the exit code, and
    • TerminalOutputDevice.DisplayAfterSessionEndRunAsync reads CoverageEntries / ThresholdEntries to render the summary.
      The drain marker (awaited TaskCompletionSource) is a happens-before barrier, so all writes are visible and no read races an in-flight Add.

Caveats

  • Relies on the contract that only the bus calls ConsumeAsync (true here).
  • Would race if any code read the collections before the drain (none does today — both the host exit-code check and the terminal summary run post-drain).

Recommendation

No change needed — consistent with the platform's other built-in consumers.

Copilot AI review requested due to automatic review settings July 13, 2026 12:33

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

Copilot reviewed 44 out of 44 changed files in this pull request and generated 2 comments.

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

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

Copilot AI review requested due to automatic review settings July 14, 2026 14:31
@james-newell-forge
james-newell-forge force-pushed the feat/add-coverage-messages branch from 427501c to 7ddfc77 Compare July 14, 2026 14:31

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

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

Comment thread src/Platform/Microsoft.Testing.Platform/PublicAPI/PublicAPI.Unshipped.txt Outdated
@james-newell-forge

Copy link
Copy Markdown
Contributor Author

Please get an approved tracking issue first. This introduces a brand-new public API surface to Microsoft.Testing.Platform (two message types, three enums, a new exit code, a PublicAPI.Unshipped.txt contract). Per our contributing guidelines we want new features — especially new public API — to have an approved design/issue before implementation, since public API is essentially permanent. Could you open a Feature issue (use the native GitHub Feature issue type, not a type/feature label) describing the design so the team can sign off on the surface? That avoids you investing more effort in an API shape we might want to change.

Tracking Issue created: #9943

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: 92/92 changed files
  • Comments generated: 1
  • Review effort level: Medium

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

Copilot-Session: a480e161-c94a-4b69-aa3b-43e05e1552cb
Copilot AI review requested due to automatic review settings July 22, 2026 13:58

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: 93/93 changed files
  • Comments generated: 1
  • Review effort level: Medium

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

Copilot-Session: a480e161-c94a-4b69-aa3b-43e05e1552cb
Copilot AI review requested due to automatic review settings July 22, 2026 14:09

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: 95/95 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@Evangelink Evangelink removed the needs/attention Needs maintainer attention. label Jul 22, 2026
Preserve the transport handshake wire ID and move the coverage capability to the next available ID.

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

Copilot-Session: a480e161-c94a-4b69-aa3b-43e05e1552cb

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: 95/95 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment thread src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Constants.cs Outdated
Advertise protocol 1.6.0 for the additive coverage handshake property and pin the updated contract in documentation and tests.

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

Copilot-Session: a480e161-c94a-4b69-aa3b-43e05e1552cb

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

Comments suppressed due to low confidence (1)

src/Platform/Microsoft.Testing.Platform/IPC/Serializers/TestHostCompletedRequestSerializer.cs:21

  • Catching EndOfStreamException also treats a payload containing 1–3 bytes of a truncated second field as a valid legacy payload, silently accepting a corrupt frame. Legacy payloads have exactly zero bytes after ExitCode; check for that case explicitly and let ReadInt reject partial fields.
  • Files reviewed: 95/95 changed files
  • Comments generated: 11
  • Review effort level: Medium

Comment thread src/Platform/Microsoft.Testing.Platform/Services/TestCoverageCapabilities.cs Outdated
Comment thread src/Platform/Microsoft.Testing.Platform/Services/ITestCoverageResult.cs Outdated
Comment thread src/Platform/Microsoft.Testing.Platform/Services/ExitCodeIgnorePolicy.cs Outdated
Comment thread src/Platform/Microsoft.Testing.Platform/Services/CoverageScopeSummary.cs Outdated
Comment thread src/Platform/Microsoft.Testing.Platform/Services/CoverageReportReference.cs Outdated
Comment thread src/Platform/Microsoft.Testing.Platform/Services/CoverageMetricResult.cs Outdated
Save the newly added C# sources as UTF-8 with BOM per the repository editor configuration.

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

Copilot-Session: a480e161-c94a-4b69-aa3b-43e05e1552cb
Accept only an exact legacy completion frame and reject partial unfiltered exit-code fields as corrupt payloads.

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

Copilot-Session: a480e161-c94a-4b69-aa3b-43e05e1552cb

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: 95/95 changed files
  • Comments generated: 2
  • Review effort level: Medium

Comment thread docs/RFCs/019-Code-Coverage-Messages.md
Clone coverage producer discovery into a fresh per-request snapshot and document the distinct correlation keys used by each coverage entry type.

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

Copilot-Session: a480e161-c94a-4b69-aa3b-43e05e1552cb

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

Copilot reviewed 95 out of 95 changed files in this pull request and generated 1 comment.

Do not advertise first-class coverage over the dotnet-test pipe until the protocol has coordinated serialization and SDK rendering support.

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

Copilot-Session: a480e161-c94a-4b69-aa3b-43e05e1552cb

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

Copilot reviewed 90 out of 90 changed files in this pull request and generated 1 comment.

Comment thread docs/RFCs/019-Code-Coverage-Messages.md
Declare the session UID property assigned by the sample constructor so the documented API matches the implementation.

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

Copilot-Session: a480e161-c94a-4b69-aa3b-43e05e1552cb

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

Copilot reviewed 90 out of 90 changed files in this pull request and generated 1 comment.

Comment thread test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/IPCTests.cs Outdated
Exercise the current completion serializer against the legacy deserializer and retain the subsequent-frame checks for mixed-version compatibility.

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

Copilot-Session: a480e161-c94a-4b69-aa3b-43e05e1552cb

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

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

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

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

Use the offset/count MemoryStream.Write overload so the protocol truncation tests compile on .NET Framework.

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

Copilot-Session: a480e161-c94a-4b69-aa3b-43e05e1552cb

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: 90/90 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

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.

3 participants