Skip to content

Forward generic warning/error host messages over the dotnet test pipe (protocol 1.3.0)#9521

Merged
Evangelink merged 3 commits into
mainfrom
dev/amauryleve/dotnet-test-feature-parity
Jun 30, 2026
Merged

Forward generic warning/error host messages over the dotnet test pipe (protocol 1.3.0)#9521
Evangelink merged 3 commits into
mainfrom
dev/amauryleve/dotnet-test-feature-parity

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Context

Following up on #9463 (Azure DevOps logging-command forwarding), this continues closing the single-assembly ↔ dotnet test feature-parity gap.

Under the dotnet test pipe protocol the host installs a no-op output device (the SDK's TerminalTestReporter owns user-facing output), so the only host output that crossed the wire was test results, artifacts, session events, and — since #9463 — Azure DevOps logging commands. Every other host UI message was swallowed. That meant host-side diagnostics produced outside test results were lost in multi-assembly runs:

  • HangDump diagnostics (timeout message, process tree, list of tests still running, disk info)
  • CrashDump diagnostics ("process crashed", tests running at crash)
  • Retry orchestrator summary ("failed attempt N/N+1", "completed after N attempts", explanations)
  • Generic extension / framework warnings & errors (VSTestBridge MessageLoggerAdapter, adapter banners, custom extensions) — the broadest gap

What this does

Rather than special-casing each producer, this adds a generic pass-through channel and lets the client decide what to do with the messages.

Wire contract (shared with dotnet/sdk via ObjectFieldIds.cs + Constants.cs):

  • Bumps ProtocolConstants.SupportedVersions1.0.0;1.1.0;1.2.0;1.3.0.
  • New DisplayMessageFieldsId (serializer id 12) and DisplayMessageLevels (Information/Warning/Error).

Host side (this repo only):

  • New DisplayMessage model + DisplayMessageSerializer (mirrors AzureDevOpsLogMessage).
  • DotnetTestConnection: new IsDisplayMessageForwardingSupported (negotiated ≥ 1.3.0) + a SendMessageAsync case.
  • DotnetTestPassthroughOutputDevice now forwards WarningMessageOutputDeviceData / ErrorMessageOutputDeviceData as DisplayMessage (gated on 1.3.0), alongside the existing Azure DevOps path. Informational text is still discarded (severity floor = Warning/Error).
  • OutputDeviceManager now installs the forwarding device under the pipe protocol everywhere, not just on Azure DevOps agents (generic messages aren't AzDO-specific; the AzDO branch stays naturally inert off-agent).

The SDK-side sink already exists (TerminalTestReporter.WriteWarningMessage / WriteErrorMessage); only the transport was missing.

Design decisions

  • Version-gated only — an older SDK that negotiates ≤ 1.2.0 never receives the new message id (host gates on the negotiated version). A finer-grained, handshake-negotiated type/severity filter is intentionally not added now (YAGNI for the current low message volume); the handshake stays an open dictionary so a WantedMessageSeverity knob can be added later without a protocol break.
  • Severity-only, no color field to keep the wire minimal — forward-compatible to add later if needed.
  • Kept AzureDevOpsLogMessage as its own path — it has a verbatim ##vso[...] pipeline-log requirement and is gated on the agent; folding it into DisplayMessage would change its semantics.

SDK follow-up (separate, like #9463)

dotnet/sdk needs its own DisplayMessage model/serializer copy and routing to the shared reporter's WriteWarningMessage/WriteErrorMessage. Not done here by design — the message models/serializers aren't shared source yet (only the zero-dependency ObjectFieldIds.cs + Constants.cs are).

Tests

  • Unit (ProtocolTests): DisplayMessage round-trip (with and without null optional fields), serializer-id stability (id 12), protocol-version string. Mirrored in the standalone DotnetTestProtocolContract.UnitTests.
  • Unit (DotnetTestPassthroughOutputDeviceTests): warning/error look up the connection; informational text is swallowed without touching the service provider.
  • Acceptance (DotnetTestPipeDisplayMessageForwardingTests): at 1.3.0 a framework's warning + error are forwarded as DisplayMessage with the correct severity levels and instance-id correlation; at 1.2.0 nothing is forwarded.
  • Black-box wire reader (DotnetTestPipeProtocol) extended with DisplayMessage decoding; baseline test updated to the new advertised version string.

All green locally: platform builds clean, 26 platform unit tests + 6 contract tests + 2 new acceptance tests pass, and the 7 existing AzDO-forwarding + baseline acceptance tests still pass (no regression).

… (protocol 1.3.0)

Under the dotnet test pipe protocol the host installed a no-op (or AzDO-only)
output device, so host-side diagnostics produced outside test results
(hang/crash dump diagnostics, retry summaries, generic extension/framework
warnings and errors) were swallowed in multi-assembly runs.

This adds a generic DisplayMessage (serializer id 12, protocol 1.3.0) carrying a
severity level + text. The host's forwarding output device now relays
WarningMessageOutputDeviceData / ErrorMessageOutputDeviceData to the SDK as a
DisplayMessage (gated on the SDK negotiating 1.3.0), alongside the existing
AzureDevOpsLogMessage path. Informational output is still discarded (severity
floor = Warning/Error). The forwarding device is now installed under the pipe
protocol everywhere, not just on Azure DevOps agents.

Forwarding is version-gated only; a finer-grained handshake type filter is left
as a future, additively-introducible knob. The dotnet/sdk consumer side (its own
DisplayMessage copy + routing to WriteWarningMessage/WriteErrorMessage) is a
separate follow-up, mirroring the AzureDevOpsLogMessage work.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 30, 2026 05:09
@Evangelink
Evangelink enabled auto-merge (squash) June 30, 2026 05:10
@Evangelink Evangelink added the state/needs-review Awaiting review from the team. label Jun 30, 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

This PR extends the dotnet test pipe protocol in Microsoft.Testing.Platform (MTP) to forward generic host warning/error diagnostics (not just test results or Azure DevOps logging commands) across multi-assembly runs, by introducing a new DisplayMessage wire frame and negotiating it via protocol 1.3.0.

Changes:

  • Add DisplayMessage model/levels + serializer (serializer id 12) and bump ProtocolConstants.SupportedVersions to include 1.3.0.
  • Update the pipe connection + output device plumbing so Warning/Error output device messages are forwarded over the protocol when the negotiated version is >= 1.3.0.
  • Add/extend unit + acceptance tests, and extend the black-box acceptance wire decoder to recognize DisplayMessage.
Show a summary per file
File Description
test/UnitTests/Microsoft.Testing.Platform.UnitTests/OutputDevice/DotnetTestPassthroughOutputDeviceTests.cs Adds unit coverage for warning/error forwarding lookup vs informational suppression behavior.
test/UnitTests/Microsoft.Testing.Platform.UnitTests/IPC/ProtocolTests.cs Adds DisplayMessage round-trip tests, serializer-id stability check (id 12), and version string update.
test/UnitTests/Microsoft.Testing.Platform.DotnetTestProtocolContract.UnitTests/DotnetTestProtocolContractTests.cs Mirrors serializer-id stability + protocol version bump in the contract test project.
test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DotnetTestPipe/DotnetTestPipeProtocol.cs Extends acceptance wire reader with serializer id 12 + DisplayMessage body decoding.
test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DotnetTestPipe/DotnetTestPipeDisplayMessageForwardingTests.cs New acceptance tests validating forwarding at 1.3.0 and absence at 1.2.0.
test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/DotnetTestPipe/DotnetTestPipeBaselineTests.cs Updates baseline advertised-protocol expectations to include 1.3.0.
src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Serializers/DisplayMessageSerializer.cs New serializer for DisplayMessage (id 12) using the shared field ids/levels.
src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/ObjectFieldIds.cs Adds embedded DisplayMessageFieldsId with stable serializer + field ids.
src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Models/DisplayMessage.cs New IPC model representing forwarded host diagnostics with severity.
src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/IPC/Constants.cs Adds embedded DisplayMessageLevels + bumps supported protocol versions to 1.3.0.
src/Platform/Microsoft.Testing.Platform/ServerMode/DotnetTest/DotnetTestConnection.cs Adds negotiation flag IsDisplayMessageForwardingSupported and sends DisplayMessage requests.
src/Platform/Microsoft.Testing.Platform/OutputDevice/OutputDeviceManager.cs Installs DotnetTestPassthroughOutputDevice under pipe protocol unconditionally (not just on AzDO).
src/Platform/Microsoft.Testing.Platform/OutputDevice/DotnetTestPassthroughOutputDevice.cs Forwards warning/error messages as DisplayMessage (1.3.0+) while preserving informational suppression.
src/Platform/Microsoft.Testing.Platform/IPC/Serializers/RegisterSerializers.cs Registers DisplayMessageSerializer so frames can be sent/received under the pipe protocol.

Review details

  • Files reviewed: 14/14 changed files
  • Comments generated: 2
  • Review effort level: Low

@Evangelink

This comment has been minimized.

Address PR review: the host serializer now skips any extra bytes beyond the
single Level byte when the declared field size is larger (forward-compat for a
widened Level / corrupted frame), and the black-box acceptance decoder respects
the field size and handles end-of-stream explicitly instead of casting a -1
ReadByte() result to a byte.

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

This comment has been minimized.

@Evangelink

Copy link
Copy Markdown
Member Author

/azp run microsoft.testfx

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

Copilot AI review requested due to automatic review settings June 30, 2026 10:07

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

@Evangelink

Copy link
Copy Markdown
Member Author

🧪 Test quality grade — PR #9521

12 tests graded across 5 files (7 new, 5 modified). 10 grade A, 2 grade B. Both B-grade tests are SerializerIds_AreStable in two sibling projects — their bodies slightly exceed the 30-line threshold because of the 12 serializer IDs being pinned, which is inherent to the contract-test pattern and carries no functional risk. All other tests are well-structured with meaningful assertions and descriptive names.

ΔTestGradeBandNotes
mod DotnetTestProtocolContractTests.
SerializerIds_
AreStable
B 80–89 Body just exceeds 30 lines (12 serializer pin entries + assertions); inherent to the contract-test pattern, no functional issues.
mod ProtocolTests.
SerializerIds_
AreStable
B 80–89 Body ~36 lines including a preamble comment block and 12 serializer pins; minor length only, logic is clean.
new DotnetTestPassthroughOutputDeviceTests.
DisplayAsync_
WithErrorButNoConnection_
IsSwallowedWithoutThrowing
A 90–100 No issues found.
new DotnetTestPassthroughOutputDeviceTests.
DisplayAsync_
WithInformationalText_
IsSwallowedWithoutResolvingTheConnection
A 90–100 No issues found.
new DotnetTestPassthroughOutputDeviceTests.
DisplayAsync_
WithWarningButNoConnection_
IsSwallowedWithoutThrowing
A 90–100 No issues found.
mod DotnetTestPipeBaselineTests.
DotnetTestPipe_
TestAppAdvertisesAllSupportedVersions_
NegotiatesDownToV100WithOldSdk
A 90–100 No issues found.
new DotnetTestPipeDisplayMessageForwardingTests.
DotnetTestPipe_
WhenSdkOnlySupports120_
DoesNotForwardDisplayMessages
A 90–100 No issues found.
new DotnetTestPipeDisplayMessageForwardingTests.
DotnetTestPipe_
WhenSdkSupports130_
ForwardsWarningAndErrorAsDisplayMessages
A 90–100 No issues found.
mod DotnetTestProtocolContractTests.
ProtocolVersion_
IsStable
A 90–100 No issues found.
new ProtocolTests.
DisplayMessageSerializeDeserialize
A 90–100 No issues found.
new ProtocolTests.
DisplayMessageSerializeDeserialize_
WithNullOptionalFields
A 90–100 No issues found.
mod ProtocolTests.
ProtocolVersion_
IsStable
A 90–100 No issues found.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 405.5 AIC · ⌖ 25.4 AIC · ⊞ 43.8K · [◷]( · )

@Evangelink
Evangelink merged commit 3090fe6 into main Jun 30, 2026
33 checks passed
@Evangelink
Evangelink deleted the dev/amauryleve/dotnet-test-feature-parity branch June 30, 2026 11:51
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