Skip to content

Migrate Microsoft.DotNet.HotReload.Client.Tests to MSTest.Sdk#54731

Merged
Evangelink merged 9 commits into
dotnet:mainfrom
Evangelink:evangelink/mstest-mtp-hotreload-client-tests
Jun 12, 2026
Merged

Migrate Microsoft.DotNet.HotReload.Client.Tests to MSTest.Sdk#54731
Evangelink merged 9 commits into
dotnet:mainfrom
Evangelink:evangelink/mstest-mtp-hotreload-client-tests

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Summary

  • Migrate Microsoft.DotNet.HotReload.Client.Tests to MSTest.Sdk on Microsoft.Testing.Platform.
  • Replace xUnit attributes/assertions with MSTest equivalents and project-local TestLogger.
  • Preserve net11.0 + net472 multi-targeting.

Validation

  • Q:\src\sdk\.dotnet\dotnet.exe build test\Microsoft.DotNet.HotReload.Client.Tests\Microsoft.DotNet.HotReload.Client.Tests.csproj --nologo — 0 warnings, 0 errors.
  • Q:\src\sdk\.dotnet\dotnet.exe exec artifacts\bin\Microsoft.DotNet.HotReload.Client.Tests\Debug\net11.0\Microsoft.DotNet.HotReload.Client.Tests.dll — 81 passed.
  • artifacts\bin\Microsoft.DotNet.HotReload.Client.Tests\Debug\net472\Microsoft.DotNet.HotReload.Client.Tests.exe — 81 passed.

- Switch SDK to MSTest.Sdk; set UseMSTestSdk=true to opt out of test/Directory.Build.targets xUnit defaults.

- Replace [Fact]/[Theory]/[InlineData] with [TestMethod]/[DataRow].

- Replace Xunit.Combinatorial with Combinatorial.MSTest 2.0.0.

- Replace ITestOutputHelper with TestContext.

- Inline project-local helpers (or replace AssertEx.X with MSTest equivalents).

- Preserve multi-TFM (net11.0 + net472).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Evangelink and others added 2 commits June 12, 2026 10:52
The package was added speculatively during the xUnit -> MSTest.Sdk
migration but is not used by any test in this project. It is also not
available on dnceng's internal NuGet feeds (only on nuget.org), which
breaks CI restore with NU1101.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Helix work-item command was hard-coded to 'dotnet test <dll>' with
VSTest-style options (--filter, --logger trx, --blame-hang*). MSTest.Sdk
projects in this repo are Microsoft.Testing.Platform (MTP) executables
and do not include testhost.dll, so the VSTest invocation aborted with:
    An assembly specified in the application dependencies manifest
    (testhost.deps.json) was not found: package: 'testhost' ...

XUnitPublish.targets now exposes GetIsMTPProject which returns the
project's UseMSTestSdk value, XUnitRunner.targets stamps it onto
SDKCustomXUnitProject items as IsMTPProject metadata, and the
SDKCustomCreateXUnitWorkItemsWithTestExclusion task switches to
'dotnet exec <dll>' with MTP-native CLI when that metadata is set:
  --filter                 (same MSTest syntax)
  --results-directory      (same)
  --report-trx             replaces '--logger trx'
  --diagnostic*            replaces VSTest '-d <log>'

xUnit v3 projects continue to use the existing VSTest command path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink Evangelink marked this pull request as ready for review June 12, 2026 11:13
Copilot AI review requested due to automatic review settings June 12, 2026 11:13

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

Migrates Microsoft.DotNet.HotReload.Client.Tests from xUnit to MSTest.Sdk (Microsoft.Testing.Platform-based), and updates the repo’s Helix/xunit-runner infrastructure to detect and invoke MTP-based test projects appropriately while preserving the project’s net11.0 + net472 multi-targeting.

Changes:

  • Converted HotReload client tests from xUnit attributes/assertions to MSTest equivalents and introduced a project-local TestLogger.
  • Extended test/xunit-runner + Helix work item generation to detect MTP projects (UseMSTestSdk=true) and switch invocation to an MTP-native command line.
  • Added MSTest.Sdk MSBuild SDK pinning in global.json and updated dependabot-tracked test dependencies.
Show a summary per file
File Description
test/xunit-runner/XUnitRunner.targets Adds MSBuild-time detection of MTP projects and flows it as metadata to Helix work item creation.
test/xunit-runner/XUnitPublish.targets Adds GetIsMTPProject target to expose UseMSTestSdk for the runner infrastructure.
test/Microsoft.DotNet.HotReload.Client.Tests/Utilities/TestLogger.cs Adds a project-local logger compatible with MSTest TestContext output.
test/Microsoft.DotNet.HotReload.Client.Tests/StaticWebAssetsManifestTests.cs Migrates tests to MSTest attributes/assert APIs and wires MSTest TestContext.
test/Microsoft.DotNet.HotReload.Client.Tests/SharedSecretProviderTests.cs Migrates to MSTest and adjusts RSA creation per TFM.
test/Microsoft.DotNet.HotReload.Client.Tests/Microsoft.DotNet.HotReload.Client.Tests.csproj Switches project SDK to MSTest.Sdk, opts out of xUnit defaults, and updates dependencies.
test/Microsoft.DotNet.HotReload.Client.Tests/EnvironmentUtilitiesTests.cs Migrates to MSTest and replaces AssertEx with MSTest assertions.
test/Microsoft.DotNet.HotReload.Client.Tests/ArrayBufferWriterTests.cs Migrates a large test suite to MSTest, including data-driven tests and categories.
test/HelixTasks/SDKCustomCreateXUnitWorkItemsWithTestExclusion.cs Adds MTP-aware Helix command generation (switching away from dotnet test).
test/Directory.Build.targets Avoids applying xUnit-specific defaults/global usings when UseMSTestSdk=true.
global.json Pins MSTest.Sdk version under msbuild-sdks.
eng/dependabot/Packages.props Adds dependabot tracking for Combinatorial.MSTest.

Copilot's findings

  • Files reviewed: 12/12 changed files
  • Comments generated: 1

Comment thread test/HelixTasks/SDKCustomCreateXUnitWorkItemsWithTestExclusion.cs Outdated
…ecks

Address PR feedback: replace the Assert.Inconclusive(...) + RuntimeInformation.IsOSPlatform / Environment.Is64BitProcess gates on InvalidAdvance_Large with MSTest declarative ConditionBaseAttributes.

- [OSCondition(OperatingSystems.Windows | OperatingSystems.OSX)] replaces the RuntimeInformation.IsOSPlatform check.
- New Is64BitProcessConditionAttribute (single-purpose ConditionBaseAttribute) replaces the Environment.Is64BitProcess check.

Filed microsoft/testfx#9070 asking testfx to ship a generic ConditionAttribute equivalent of xUnit's [ConditionalFact(typeof(X), nameof(member))] so per-condition shims like this one are no longer needed.

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

Copy link
Copy Markdown
Member Author

Addressed PR feedback in commit 8705ccc:

  • Replaced RuntimeInformation.IsOSPlatform(Windows|OSX) runtime guard with the declarative [OSCondition(OperatingSystems.Windows | OperatingSystems.OSX)] (MSTest 4.3 + the matching OperatingSystems flags enum).
  • Replaced the Environment.Is64BitProcess Assert.Inconclusive guard with a custom [Is64BitProcessCondition] attribute (ConditionBaseAttribute subclass). The test now reads cleanly as [TestMethod] [OSCondition(...)] [Is64BitProcessCondition] [TestCategory(""OuterLoop"")].
  • Filed Add generic ConditionAttribute that evaluates static Type+member like xUnit's [ConditionalFact] microsoft/testfx#9070 asking the testfx team for a generic ConditionAttribute equivalent of xUnit's [ConditionalFact(typeof(X), nameof(member))]. Once that ships, Is64BitProcessConditionAttribute (and every other one-off ConditionBaseAttribute we'd otherwise need to write) can be deleted.

@Evangelink

Copy link
Copy Markdown
Member Author

Replaced the manual <UseMSTestSdk>true</UseMSTestSdk> opt-in with the property MSTest.Sdk already sets for us: UsingMSTestSdk. Since MSTest.Sdk's Sdk.props runs before Microsoft.NET.Sdk.props and test/Directory.Build.targets is imported at the bottom of evaluation, the property is in scope wherever we need it — no per-project knob required.

Also refreshed the related comments in XUnitPublish.targets / XUnitRunner.targets and renamed the intermediate _UseMSTestSdk MSBuild property to _UsingMSTestSdk for clarity. (Thanks @Evangelink for the catch.)

…Sdk)

MSTest.Sdk already sets $(UsingMSTestSdk)=true in its Sdk.props before
Directory.Build.props is evaluated, so the custom <UseMSTestSdk>true</UseMSTestSdk>
opt-in property is redundant. This change:

- Removes <UseMSTestSdk>true</UseMSTestSdk> from MSTest.Sdk csproj(s).
- Renames $(UseMSTestSdk) -> $(UsingMSTestSdk) in test/Directory.Build.targets
  (the xUnit-defaults gating condition) and in xunit-runner/{XUnitPublish,XUnitRunner}.targets
  (Helix MTP dispatcher detection).
@Evangelink

Copy link
Copy Markdown
Member Author

Made --report-trx conditional on Microsoft.Testing.Extensions.TrxReport actually being loaded. MSTest.Sdk enables that extension by default via EnableMicrosoftTestingExtensionsTrxReport=true for the Default / AllMicrosoft profiles, but other MTP hosts (xUnit v3 MTP, MSTest.Sdk with TestingExtensionsProfile=None, etc.) do not — passing --report-trx there fails with 'unknown argument'.

The new GetTrxReportEnabled target queries that property; XUnitRunner.targets propagates it as EnableTrxReport metadata, and SDKCustomCreateXUnitWorkItemsWithTestExclusion only emits the flag when it's true. No behavioural change for the current PRs since all migrated projects are MSTest.Sdk-based, but the dispatcher is now forward-safe.

(Applied to all 9 stacked PRs since they share these plumbing files.)

@Evangelink

Copy link
Copy Markdown
Member Author

Replaced the custom <UseMSTestSdk> opt-in property with the built-in UsingMSTestSdk property that MSTest.Sdk already sets in its Sdk.props before Directory.Build.props is evaluated. This drops a redundant declaration in every migrated csproj and just reads what the SDK already publishes.

The original xUnit v3 test used TestContext.Current.CancellationToken to propagate
test cancellation into the WriteAsync call. The MSTest migration swapped that for
CancellationToken.None, dropping cancellation propagation.

Restore the original behavior by adding the standard MSTest `public TestContext TestContext { get; set; }`
property on the test class and routing the cancellation token through it.
@Evangelink

Copy link
Copy Markdown
Member Author

Switched migration-introduced CancellationToken.None back to a cancellable token by adding the standard MSTest public TestContext TestContext { get; set; } property on the test class and using TestContext.CancellationToken — this preserves the original xUnit v3 behavior (TestContext.Current.CancellationToken) and keeps tests cancellable. The TestContext.Current static would also work but is currently marked experimental (MSTESTEXP).

@Evangelink

Copy link
Copy Markdown
Member Author

Round 3: moved FluentAssertions Using and AwesomeAssertions PackageReference to test/Directory.Build.targets.

The Using is now global for any test project ('$(IsTestProject)' == 'true' OR '$(UsingMSTestSdk)' == 'true'). The PackageReference is added for MSTest.Sdk projects only — xUnit projects already get it transitively via the Microsoft.NET.TestFramework project reference.

Deviation from your literal suggestion: I left the Microsoft.NET.TestFramework.* Usings and Xunit Using gated on UsingMSTestSdk != true. Moving them unconditional would also require a global <ProjectReference Include="Microsoft.NET.TestFramework" /> (otherwise MSTest projects would fail with CS0246), which would drag xunit.v3.assert and other xUnit pieces into the MSTest projects. Happy to apply the broader change (with a global ProjectRef) if you'd prefer.

BagOfMandMs and others added 3 commits June 12, 2026 14:20
--report-trx is an MTP CLI argument provided only when the
Microsoft.Testing.Extensions.TrxReport extension is loaded on the test host.
MSTest.Sdk's Default/AllMicrosoft profiles enable it by default, but other MTP
runners (e.g. xUnit v3 MTP) do not bundle the extension, so passing --report-trx
to those hosts fails with 'unknown argument'.

XUnitPublish.targets now exposes the GetTrxReportEnabled target which returns
the value of EnableMicrosoftTestingExtensionsTrxReport. XUnitRunner.targets
calls that target and propagates the value as the EnableTrxReport metadata of
SDKCustomXUnitProject items. SDKCustomCreateXUnitWorkItemsWithTestExclusion
reads that metadata and only appends --report-trx to the MTP command line when
it is true.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…est/Directory.Build.targets

Per @Evangelink: keep per-csproj boilerplate minimal. FluentAssertions is now a
global using for any test project (gated on IsTestProject OR UsingMSTestSdk),
and AwesomeAssertions is added as a PackageReference for MSTest.Sdk projects.
xUnit projects continue to pick it up transitively via Microsoft.NET.TestFramework.

The Microsoft.NET.TestFramework.* and Xunit usings remain gated on the xUnit
branch (UsingMSTestSdk != true) because MSTest projects in this repo do not
reference Microsoft.NET.TestFramework; making those usings global would fail
with CS0246.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The recent `Helix dispatcher: gate --report-trx on TrxReport extension
being loaded` commit added XML comments in XUnitRunner.targets and
XUnitPublish.targets that included the literal `--report-trx` switch
(and a `--` dash separator). XML 1.0 disallows `--` inside comment
contents, so MSBuild aborts loading test/UnitTests.proj with:

    error MSB4024: The imported project file
    "test/xunit-runner/XUnitRunner.targets" could not be loaded.
    An XML comment cannot contain '--', and '-' cannot be the last
    character.

That breaks the Queue Tests stage on every PR that picked up these
files. Rephrase the comments so they no longer contain `--`; the
actual MSBuild logic is unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink Evangelink merged commit f4ba7b5 into dotnet:main Jun 12, 2026
25 checks passed
@Evangelink Evangelink deleted the evangelink/mstest-mtp-hotreload-client-tests branch June 12, 2026 16:14
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview6 milestone Jun 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants