Run wasm test hosts standalone under dotnet test (MTP)#55389
Conversation
Microsoft.Testing.Platform's server mode (`--server dotnettestcli --dotnet-test-pipe`) makes the test host connect back to a named pipe. wasm runtimes (browser/wasi) can't open a named pipe, so requesting server mode makes the host throw PlatformNotSupportedException before any test runs (testfx `DotnetTestConnection` hard-instantiates a `NamedPipeClient`). For wasm modules, skip the server-mode options and the (unused) named pipe listener, and let the host run standalone. Results come from the process exit code and streamed stdout. The existing `MissingTestSessionEnd` guard is already safe for a pipe-less run (its per-session dictionary stays empty). This is step 1 of Blazor `dotnet test` support (#54091): it unblocks running browser-wasm/wasi-wasm MTP test hosts under node / wasmtime today. Rich per-test reporting (TRX-based V1, or the live pipe via a bridge host such as the Blazor Gateway) builds on top. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7a8f2c5b-02e3-4963-926b-69266d1556e2
|
Azure Pipelines: Successfully started running 1 pipeline(s). 2 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Standalone wasm test hosts have no live pipe, so dotnet test needs an on-disk TRX to recover results. For wasm modules, GetArguments now: - always sets --results-directory (defaulting to <cwd>/TestResults when the user didn't pass one) so the host that writes the TRX and the tooling that reads it back agree on the location, and - requests --report-trx --report-trx-filename blazor-wasm.trx. Non-wasm runs are unchanged: --results-directory is still only set when explicitly requested, and the server-mode pipe options are unchanged. This is the launch contract a bridge host (e.g. the Blazor Gateway) relies on to activate its TRX-collection path and copy the artifact back to the results directory. The mode-dependent argument logic is extracted into GetHostModeArguments / GetStandaloneResultsDirectory and unit tested. Assumes the test project includes the Microsoft.Testing.Extensions.TrxReport extension (MSTest.Sdk default). Part of Blazor `dotnet test` support (#54091). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7a8f2c5b-02e3-4963-926b-69266d1556e2
A standalone wasm test host has no live pipe, so its results never reach the terminal reporter — OnTestProcessExited would route even a passing run to HandshakeFailure. This reads the TRX the host wrote and replays it into the reporter as if it had arrived over the pipe. - TrxTestResultParser parses the MTP TRX (Microsoft.Testing.Extensions .TrxReport) into per-test results + run outcome. - TestApplicationHandler.ReportStandaloneResults synthesizes an execution/instance id and drives AssemblyRunStarted -> TestCompleted (per result) -> AssemblyRunCompleted. A missing TRX is treated as a crash (TRX present => ran to completion) and falls back to HandshakeFailure with the process output and exit code. - TestApplication routes wasm modules to this path after the host exits. Outcome mapping follows MTP's TRX (Passed/Failed/NotExecuted; unknown values treated as failures). TRX has no structured expected/actual (only inline in the message), so those are passed as null; the structured diff remains a V2/pipe capability. Adds unit tests for the parser, the outcome mapping, and the handler reporting path (results rendered vs. handshake-failure fallback). Part of Blazor `dotnet test` support (#54091). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7a8f2c5b-02e3-4963-926b-69266d1556e2
End-to-end validation of the wasm path (running a browser-wasm MSTest host under node) showed that the exit-code result works, but requesting `--report-trx` crashes a wasm host: TrxReport's TrxResultStreamingStore starts a background Thread, which throws PlatformNotSupportedException on single-threaded wasm (MSTest 4.4.0-preview / MTP 2.4.0-preview). Add a code note at the `--report-trx` emission site so the dependency on a TrxReport single-threaded-wasm guard is explicit, and a follow-up can gate the emission on that fix so exit-code-passing wasm runs are not regressed. Part of Blazor `dotnet test` support (#54091). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7a8f2c5b-02e3-4963-926b-69266d1556e2
End-to-end testing showed `--report-trx` crashes a standalone wasm host:
TrxReport's TrxResultStreamingStore starts a background thread in its
ctor (SystemTask.RunLongRunning, [UnsupportedOSPlatform("browser")]),
which throws PlatformNotSupportedException on single-threaded wasm. So
emitting `--report-trx` regresses a wasm run that otherwise passes on its
exit code. The MTP owners (testfx) confirmed the bug and recommended
withholding `--report-trx` for wasm until they guard that thread.
Introduce WasmReportTrxSupported (false today) gating:
- whether `--report-trx`/`--report-trx-filename` is emitted for wasm, and
- whether a results directory is defaulted for the wasm host.
ReportStandaloneResults now takes a nullable TRX path: null (TRX not
requested) reports assembly-level pass/fail from the exit code instead of
a handshake failure; a requested-but-missing TRX still means a crash.
The TRX reader (TrxTestResultParser / the per-test replay) stays in place
as forward-looking: flipping WasmReportTrxSupported (once the TrxReport
single-threaded-wasm guard ships in a Microsoft.Testing.Extensions
.TrxReport release) lights up the full per-test TRX path. Exit-code
results work on wasm today.
Tests updated for the new GetHostModeArguments/ReportStandaloneResults
shapes, plus a gated-off (no server, no TRX) case and a no-TRX-requested
exit-code reporting case. 51 tests green.
Part of Blazor `dotnet test` support (#54091).
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7a8f2c5b-02e3-4963-926b-69266d1556e2
…ests Add a regression fixture using a verbatim TRX captured from a real browser-wasm MSTest run (net10.0|browser-wasm, MSTest 4.4.0 with the Microsoft.Testing.Extensions.TrxReport synchronous-on-wasm streaming store), driven end-to-end through headless Chrome. This is the exact schema the SDK reads back from a standalone wasm test host once WasmReportTrxSupported is enabled, so the test exercises the full MTP shape the parser must tolerate: TestDefinitions/TestEntries/TestLists, the extra UnitTestResult attributes (executionId, testType, ...), a UTF-8 BOM, and a NotExecuted result whose duration is present-but-zero. Verified the parser surfaces the three per-test results, their outcomes, and the run outcome, and that ToOutcome maps them to Passed/Passed/Skipped. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7a8f2c5b-02e3-4963-926b-69266d1556e2
|
Note This review comment was AI-assisted and manually requested after validating the findings against the current SDK and testfx code. I found four issues in the current shape:
On moving this into MTP core: detecting wasm in the existing handshake is too late to choose the transport. The SDK must decide whether to create a pipe and append I think the blocking points can be fixed cleanly if testfx exposes the decision before the handshake:
This would also let this PR use a temporary standalone fallback without making RID detection the permanent orchestration contract. |
Summary
Foundational SDK work for Blazor
dotnet testsupport (#54091): run wasm test hosts (browser-wasm/wasi-wasm) underdotnet testand report their results, instead of crashing withPlatformNotSupportedException.The exit-code path works on wasm today (validated end-to-end). The richer per-test TRX path is in place but gated off, pending a Microsoft.Testing.Extensions.TrxReport single-threaded-wasm fix — flipping one flag lights it up. Kept draft until that lands.
What it does
For wasm modules (detected via
RunProperties.RuntimeIdentifierstarting withbrowser/wasi):--server dotnettestcli --dotnet-test-pipe). wasm sandboxes can't open a named pipe, so testfx'sDotnetTestConnectionotherwise throwsPlatformNotSupportedExceptionat host build. The host now runs standalone and reports via process exit code.AssemblyRunStarted+AssemblyRunCompleted) — no per-test detail without a pipe or TRX, but correct assembly-level results.--report-trx(see Known limitation). AWasmReportTrxSupportedflag gates the TRX emission + results-directory defaulting; a nullable TRX path inReportStandaloneResultsdistinguishes "TRX not requested → exit-code result" from "TRX requested but missing → crash".The per-test TRX machinery (
TrxTestResultParser+ the TRX replay inReportStandaloneResults) is retained as forward-looking: flippingWasmReportTrxSupportedonce TrxReport supports wasm enables full per-test reporting with no other change.Validated end-to-end
Booting a real
browser-wasmMSTest host (net10, MSTest 4.4.0-preview) under node:The SDK↔bridge contract was also validated against the real Blazor Gateway (dotnet/aspnetcore) over real sockets + headless Chrome (serve, browser launch, console streaming, exit code).
Known limitation — TRX path gated off on wasm (why this stays draft)
--report-trxcurrently crashes a wasm host: TrxReport'sTrxResultStreamingStorector starts a backgroundThread(SystemTask.RunLongRunning,[UnsupportedOSPlatform("browser")]), which throwsPlatformNotSupportedExceptionon single-threaded wasm (confirmed on MSTest 4.4.0-preview.26367.7 / MTP 2.4.0-preview.26367.7):The MTP owners (testfx) confirmed this is a real, previously-untested bug and are implementing an inline-writer fallback for single-threaded wasm on the browser-enablement track. Until that ships, emitting
--report-trxwould regress a wasm run that passes on its exit code, so this PR gates it off (WasmReportTrxSupported = false) and relies on the exit code. The Blazor Gateway's TRX-POST path is blocked by the same bug — a shared dependency.When the TrxReport fix ships: flip
WasmReportTrxSupported(or gate on a detected TrxReport version / host capability) and the full per-test TRX path activates.Scoping findings for #54091
WasmGenerateAppBundletarget is an empty stub), MSTest 4.4.0-preview+ (browser guards for the MSBuild + RunSettings host-controller extensions; 4.3.2 crashes), and a browser-compatible test registration. That configuration belongs in the Blazor/wasm test SDK or template, not thedotnet testCLI.Tests (all passing)
TestApplicationTests— wasm RID detection; host-mode arg emission for standalone-with-TRX, standalone-without-TRX (neither server nor TRX), and non-standalone (server); results-directory defaulting.TrxTestResultParserTests— TRX parse, missing/malformed handling, outcome mapping.StandaloneTestResultReportingTests— TRX replay; missing-requested-TRX → handshake-failure (crash); no-TRX-requested → exit-code reporting (not a crash).TestApplicationHandlerTestsunaffected.Notes
dotnet test(exit-code results today; per-test TRX results once TrxReport supports wasm) instead of erroring withPlatformNotSupportedException.StreamMessageClientseam over a duplexStream) are tracked separately under Blazor support fordotnet test#54091.