Skip to content

Target NetCurrent for Microsoft.DotNet.XUnitConsoleRunner test host#16914

Open
steveisok wants to merge 2 commits into
dotnet:mainfrom
steveisok:fix-xunit-console-runner-tfm
Open

Target NetCurrent for Microsoft.DotNet.XUnitConsoleRunner test host#16914
steveisok wants to merge 2 commits into
dotnet:mainfrom
steveisok:fix-xunit-console-runner-tfm

Conversation

@steveisok
Copy link
Copy Markdown
Member

Note

This pull request was drafted with assistance from GitHub Copilot (AI-generated content).

Summary

Change Microsoft.DotNet.XUnitConsoleRunner (xunit.console.dll) to target $(NetCurrent) instead of $(NetMinimum). The XUnitConsoleRunner is a test host that runs test assemblies built against the current .NET TFM, so its host runtime must also be the current TFM.

Background

#16795 ("Downgrade TFM to NetMinimum") changed several Arcade projects from $(BundledNETCoreAppTargetFramework) to $(NetMinimum) to decouple Arcade from the installed .NET SDK. That change is appropriate for build-time MSBuild tasks, but the Microsoft.DotNet.XUnitConsoleRunner project produces xunit.console.dll, the test host executable shipped via the Microsoft.DotNet.XUnitConsoleRunner package and consumed by Microsoft.DotNet.XHarness.TestRunners.Xunit, RemoteExecutor, etc.

For RemoteExecutor, #16795 also added <RollForward>Major</RollForward> as mitigation. No equivalent mitigation was added for XUnitConsoleRunner, and RollForward=Major would not actually have helped here — once the .NET 10 host is selected, even forward-rolling can't load a test assembly that references members only present in net11's BCL.

Repro / impact

Consumer: dotnet/runtime#128648 (a routine VMR backflow PR). The Wasm.Build.Tests Helix work items fail with:

TypeLoadException: Could not load type 'Wasm.Build.Tests.WasmTemplateTestsBase'
from assembly 'Wasm.Build.Tests, Version=11.0.0.0, ...' because the format is invalid.

23 of 24 work items fail per CLR-on-WASM job. Failing build: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1438863. Baseline passing main build: https://dev.azure.com/dnceng-public/public/_build/results?buildId=1438570.

Smoking-gun evidence

I diffed the entire Helix payload between a passing main build and the failing PR build (210 DLLs). Only six differ; five are inert (MVID-only or attribute-blob deltas). The substantive change is in xunit.console.dll:

-.assembly extern System.Runtime { .ver 11:0:0:0 }
+.assembly extern System.Runtime { .ver 10:0:0:0 }
...
-.custom instance void [System.Runtime]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string)
-   = ( ... ".NETCoreApp,Version=v11.0" ... )
+.custom instance void [System.Runtime]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string)
+   = ( ... ".NETCoreApp,Version=v10.0" ... )

And the host runtimeconfig:

tfm framework.version
main (passing) net11.0 11.0.0-preview.5.26227.104
PR (failing) net10.0 10.0.0

The .NET 10 host is then asked to load a test assembly that references System.Runtime, Version=11.0.0.0 and types/members only present in net11 → TypeLoadException.

Fix

-    <TargetFramework>$(NetMinimum)</TargetFramework>
+    <!-- Target NetCurrent so the test host can load test assemblies built against
+         the current .NET TFM. Using NetMinimum here causes TypeLoadException
+         "format is invalid" when loading newer-TFM test assemblies because the
+         minimum .NET runtime is selected and cannot resolve newer BCL members. -->
+    <TargetFramework>$(NetCurrent)</TargetFramework>

This preserves #16795's "decouple from the SDK" intent — NetCurrent is just an Arcade property, not the SDK's bundled TFM, and ref packs are NuGet-restored.

Notes / open questions

  • Validation against dotnet/runtime (Wasm.Build.Tests) will confirm the fix; happy to chase that with an arcade insertion once this is in.
  • Did not touch RemoteExecutor — its RollForward=Major mitigation may also be insufficient for runtime/test-assembly cases, but that's a separate investigation.
  • Should this be considered for a NetMinimum-targeted release branch too, if any consumer depends on it for test execution?

Refs

PR dotnet#16795 changed the XUnitConsoleRunner TFM from
$(BundledNETCoreAppTargetFramework) to $(NetMinimum) to decouple
Arcade from the installed .NET SDK. While that is correct for build-
time MSBuild tasks, it is incorrect for xunit.console.dll, which is
a test host that loads and executes test assemblies built against
$(NetCurrent).

With NetMinimum (net10.0) selected, the runtimeconfig pins the host
to .NET 10 and consumers (e.g. dotnet/runtime) get:

  TypeLoadException: Could not load type ... because the format is invalid

when the host tries to load a test assembly that references members
only present in NetCurrent's BCL. Wasm.Build.Tests on dotnet/runtime
PR #128648 reproduced this on 23/24 work items.

Targeting NetCurrent keeps PR dotnet#16795's 'decouple from installed SDK'
goal (NetCurrent is a property, not the SDK's bundled TFM) while
restoring the correct host TFM for test execution.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 28, 2026 23:26
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Reverts Microsoft.DotNet.XUnitConsoleRunner.csproj from $(NetMinimum) back to $(NetCurrent) so the xUnit console test host runs on the current .NET TFM and can load test assemblies built against newer BCL members. This addresses a regression from #16795 where the NetMinimum (net10.0) host could not load net11.0 test assemblies, causing TypeLoadException in consumers such as dotnet/runtime Wasm.Build.Tests.

Changes:

  • Change TargetFramework from $(NetMinimum) to $(NetCurrent) for the xUnit console runner test host.
  • Add an explanatory comment noting why NetMinimum is unsuitable for this test-host project.

Copy link
Copy Markdown
Member

@ViktorHofer ViktorHofer left a comment

Choose a reason for hiding this comment

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

Requiring XUnitConsoleRunner to target and run on NetCurrent is a dotnet/runtime specific requirement, other repos run with older versions of .NET. This is fine to take if you add to NetMinimum, instead of replacing it. Other test host runner also multi-target (i.e. xunit.console or vstest).

@ViktorHofer
Copy link
Copy Markdown
Member

ViktorHofer commented May 29, 2026

FWIW this whole project would ideally get deleted after most consumers switched to xunit.v3 which has in-built support (or via MTP) to run the tests and doesn't need a separate runner assembly.

Build the xunit.console test host for both NetMinimum and NetCurrent instead
of a single TFM. The NetCurrent host is packed to the historical tools/net
folder (preserving existing consumers and the props default); the NetMinimum
host is packed to a TFM-specific folder (e.g. tools/net10.0).

A companion build .targets selects the host matching the consuming project's
.NET version when one is shipped, otherwise the tools/net (NetCurrent) host is
used. This addresses review feedback to keep a NetMinimum host for repos that
consume the latest Arcade on an older .NET SDK, while restoring a NetCurrent
host so repos building against the in-development TFM (e.g. dotnet/runtime
WasmBuildTests) no longer hit TypeLoadException "format is invalid".

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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 3 out of 3 changed files in this pull request and generated no new comments.

@steveisok steveisok enabled auto-merge (squash) June 1, 2026 00:39
@ViktorHofer ViktorHofer disabled auto-merge June 1, 2026 07:04
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.

4 participants