Skip to content

Migrate test suite from xUnit.v3 to MSTest v4 on Microsoft Testing Platform - #14336

Open
jankratochvilcz wants to merge 38 commits into
mainfrom
jankratochvilcz-xunit-to-mstest-migration
Open

Migrate test suite from xUnit.v3 to MSTest v4 on Microsoft Testing Platform#14336
jankratochvilcz wants to merge 38 commits into
mainfrom
jankratochvilcz-xunit-to-mstest-migration

Conversation

@jankratochvilcz

@jankratochvilcz jankratochvilcz commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Migrate MSBuild tests from xUnit.v3 to MSTest v4 / MTP

Summary

This PR migrates the MSBuild test suite from xUnit.v3 to MSTest v4 running on the latest Microsoft Testing Platform (MTP), while preserving the suite's existing serial execution behavior.

Diff scope is intentionally broad because the migration touches test attributes, data rows, assertion APIs, package references, shared test infrastructure, and runner wiring across the test tree. At authoring time the branch diff against origin/main is approximately 531 files changed, 25,565 insertions, 24,462 deletions.

Motivation

  • Move MSBuild onto MSTest v4 and the current MTP runner stack.
  • Align with the .NET ecosystem direction for test execution and runner integration.
  • Retain deterministic, serial test execution so behavior and benchmark comparisons remain apples-to-apples with the previous xUnit.v3 setup.
  • Preserve existing test semantics, platform skips, environment isolation, and CI coverage across .NET Framework and .NET.

Scope

Migrated the 11 primary test runner projects to MSTest/MTP:

  • src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj
  • src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj
  • src/BuildCheck.UnitTests/Microsoft.Build.BuildCheck.UnitTests.csproj
  • src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj
  • src/MSBuild.Coordinator.UnitTests/MSBuild.Coordinator.UnitTests.csproj
  • src/MSBuild.UnitTests/Microsoft.Build.CommandLine.UnitTests.csproj
  • src/MSBuild.EndToEnd.Tests/Microsoft.Build.EndToEnd.Tests.csproj
  • src/StringTools.UnitTests/StringTools.UnitTests.csproj
  • src/TaskAnalyzer.Tests/TaskAnalyzer.Tests.csproj
  • src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj
  • src/Utilities.UnitTests/Microsoft.Build.Utilities.UnitTests.csproj

Also updated shared test infrastructure and CTS/VSTest wrapper projects so the existing CI topology continues to work. StringTools.UnitTests.net35.csproj remains the .NET Framework host path for net35 StringTools coverage because MSTest v4 does not run net35 directly.

Key mechanics

  • Switched primary test projects to Sdk="MSTest.Sdk".
  • Wired Arcade test execution to MSTest/MTP with:
    • TestRunnerName=MSTest
    • EnableMSTestRunner=true
    • UseMSTestSdk=true
    • MicrosoftTestingPlatformVersion=2.3.1
    • MSTestVersion=4.3.0
  • Disabled xUnit runner/package injection for migrated projects. The migration goal is the same as UsingToolXUnit=false; the final Arcade-compatible wiring keeps TestRunnerName=MSTest explicit because a naked UsingToolXUnit=false caused Arcade to omit the RunTests target during CI iteration.
  • Preserved serial execution with MSTestParallelizeScope=None, matching the old xUnit maxParallelThreads=1 behavior.
  • Added MSTest global usings and assembly hooks through shared test infrastructure.
  • Added MSBuildTestMethodAttribute : TestMethodAttribute, which wraps each test in TestEnvironment.Create() and preserves the existing MSBuild test invariants.
  • Replaced xUnit conditional attributes with MSTest-compatible equivalents, including WindowsOnlyFact, UnixOnlyFact, DotNetOnlyFact, WindowsFullFrameworkOnlyFact, ActiveIssue, SkipOnPlatform, long-path, symlink, and net35-specific variants.
  • Converted xUnit data/traits/assertions to MSTest equivalents ([TestMethod], [DataRow], [TestCategory], Assert.Inconclusive, sequence assertions, etc.) while keeping Shouldly where it was already used.
  • Kept Clever Test Selection / .VSTest.csproj wrappers functional and protected wrapper assembly names so existing InternalsVisibleTo grants continue to resolve.

net472 Verify.MSTest / AsyncInterfaces binding resolution

Two net472 test projects use Verify:

  • Microsoft.Build.Engine.UnitTests
  • Microsoft.Build.Tasks.UnitTests

The initial Verify.MSTest version pulled Microsoft.Bcl.AsyncInterfaces 10.0.0.9, but the spawned product MSBuild.exe used by net472 tests binds Microsoft.Bcl.AsyncInterfaces to exactly 10.0.0.8. That mismatch caused net472 runtime failures in CI.

The clean fix is to pin:

  • Verify.MSTest = 31.19.1

Verify.MSTest 31.19.1 is still MSTest v4-compatible, but its Verify dependency floors Microsoft.Bcl.AsyncInterfaces at 10.0.8, matching the product binding redirects. Avoid bumping Verify.MSTest to 31.20.0+ without revalidating net472, because that reintroduces the 10.0.9 floor.

net472 root-cause fixes

The migration surfaced three net472-only defects that only reproduced under the MSTest runner's
test-ordering and its generated entry point. None are test-logic changes — they restore the exact
runtime behavior the xUnit host provided.

  • Config-cache toolset pollution (7 tests). On .NET Framework, ToolsetConfigurationReader
    caches process-wide (a static Lazy<Configuration>) which app.config it reads for toolset
    definitions, based on BuildEnvironmentHelper.Instance.RunningTests at the first read. The xUnit
    host set RunningTests via an ITestPipelineStartup before discovery; MSTest's [AssemblyInitialize]
    can run after the cache is first populated (during MTP discovery / dynamic-data providers / a static
    ctor), so the cache was built with RunningTests == false and read MSBuild.exe.config, baking a
    MSBuildExtensionsPath = <testbin> toolset property that poisoned every later evaluation. Fixed with
    a [ModuleInitializer] in the shared TestAssemblyInfo.cs that sets the RunningTests flags at
    assembly-load time, before anything can read the config cache — mirroring the old xUnit pipeline
    startup. This resolved the 5 MSBuildExtensionsPath* and 2 VerifyPropertyTrackingLogging* failures.
  • Long-path PathTooLongException (1 test). ProjectItemSpecTooLong builds a ~750-char relative
    path and asserts MSBuild normalizes it under MAX_PATH without throwing. On .NET Framework whether
    Path.GetDirectoryName enforces the 260 limit is governed by the AppContext switch
    Switch.System.IO.BlockLongPaths, whose default is derived from the process entry-point's target
    framework. The MSTest-generated entry point fell back to the shared App.config's stale
    sku=v4.5.1, defaulting BlockLongPaths to true. Fixed by explicitly setting
    Switch.System.IO.BlockLongPaths=false in src/Shared/UnitTests/App.config, matching main's
    effective behavior. .NET (net10.0) ignores app.config and is unaffected.
  • MTP diagnostic-log file lock (flaky, 1 leg). ToolTaskDoesNotHangWhenGrandchildInheritsPipeHandles
    intentionally spawns a ~40s background ping/sleep grandchild that inherits the tool's pipe handles
    (regression test for Process.WaitForExit() deadlocks when waiting on "dotnet.exe build /m /nr:true" #2981). Under MTP that grandchild also inherits the diagnostic-log file handle and,
    by outliving the test, intermittently blocked Arcade's post-run WriteLinesToFile with MSB3491. The test
    now terminates the grandchild it spawned after its assertions, releasing the inherited handle before log
    finalization while still exercising the >30s EOF-timeout path.
  • Removed a stray [TestClass] attribute that the bulk migration inserted into an embedded C# task
    source string used by STA task tests.

These fixes keep environment-sensitive net472 tests order-independent under MSTest.

Testing

  • Full PR validation matrix green on all legs except the pre-existing-flaky macOS Core leg (see note below).
  • Linux / .NET (net10.0) green.
  • macOS / .NET (net10.0) green (achieved on build 1509023; see the known-flaky note below for later intermittent macOS-only failures).
  • Windows-on-Core / .NET (net10.0) green.
  • Windows-on-full / .NET Framework (net472) green.
  • Windows Full Release / .NET Framework (net472) green.
  • Source-build legs green.
  • Both target families covered: net472 and net10.0.

Known-flaky macOS Core leg (pre-existing, not migration-related)

A fully-green run across all 8 legs including macOS Core was achieved on this branch (build 1509023).
After a subsequent main merge (which pulled in PR #14224's new process-spawning coordinator tests), the
macOS Core leg has intermittently failed while every other leg — all net472, all net10.0, Linux,
Windows, Source-Build — stays green
. The macOS failures hit different tests on each run and are all
environmental: spawned-MSBuild 30s timeouts, OutOfMemoryException-dump write races, coordinator
process-spawn output truncation, and the shared BuildFailureLogInvariant global-temp scan race
(TestEnvironment.cs) — none of which the migration touched.

This is pre-existing pipeline flakiness, not a migration regression: main's own latest build
(1509637, pure xUnit) failed with macOS Core as the only failed leg, and main's msbuild-pr
history is red in ~half of recent builds. macOS Core is not a required status check.

Flagged / ignored tests

Current status: none disabled by the migration.

No tests are intentionally ignored or disabled because of MSTest v4. The net35 StringTools compatibility project is not an ignored test; it remains the existing .NET Framework host path because MSTest v4 does not run net35 directly. The intermittent macOS Core failures described above are pre-existing infra flakiness (they also fail on main's xUnit builds), so no test is quarantined for them. If a late validation pass finds an unavoidable skipped case, add it here before merge with:

  • test name / project
  • platform + TFM affected
  • exact skip condition
  • tracking issue
  • reason it cannot run under MSTest v4 yet

Performance: MSTest vs xUnit — both on Microsoft Testing Platform (MTP)

This is a like-for-like MTP comparison. The pre-migration suite was already running under Microsoft
Testing Platform — xunit.v3 self-hosted as an Exe via TestRunnerName=XUnitV3 + a pinned
MicrosoftTestingPlatformVersion, not VSTest. This PR keeps MTP as the constant and swaps only the
runner framework: xunit.v3's MTP runner → MSTest v4's MTP runner. So any delta below reflects the
framework/runner engine, not a testhost change.

Measured on the same msbuild-pr pipeline (definition 75, same agent pool and legs), averaging the
per-leg wall-clock (cibuild.cmd, build+test) of recent green xUnit-on-MTP main builds vs the MSTest
runs on this branch:

Leg (build+test, min) xUnit/MTP avg MSTest/MTP avg Δ
Windows Core 36.9 31.1 −5.7
Linux Core 24.2 21.5 −2.7
Windows Full (net472) 41.2 38.7 −2.5
Windows Full Release (net472) 33.8 32.1 −1.7
macOS Core 34.8 33.9 −0.9
Linux Core Multithreaded 13.1 12.3 −0.8
Source-Build (Managed) 5.0 4.2 −0.8

Verdict: no regression. The leg-level numbers are within CI noise, not a real speedup — and at the
isolated test-execution level MSTest is marginally (~+2.4%) slower.
The table above (xUnit n=4 baselines,
MSTest n=3 runs) does show every MSTest leg landing at or below the xUnit average, but leg wall-clock is
dominated by restore + build + shared-agent variance: the xUnit-on-MTP baselines vary among themselves by
more than most of these deltas (e.g. Windows Full 35.1–43.8 min), and MSTest sits inside that band every
time. Treat the leg table as "no measurable regression," not as a win.

Isolated test-execution time (the honest number). To remove build + agent variance, I compared two CI
builds of the exact same source — main HEAD 596a1379 — one before this branch's runner swap and one
after (1509637 xUnit vs 1509649 MSTest, Linux net10), summing per-test durations from the published TRX:

Metric (Linux net10, same source) xUnit/MTP MSTest/MTP Δ
Σ per-test execution time 1797 s 1841 s +2.4%

So the pure runner-framework delta is a small ~2.4% slowdown in test execution, not a speedup. It is
swamped by build/agent variance at leg granularity, which is why the leg table reads flat-to-faster. This is
consistent with MSTest v4's per-test data-driven dispatch vs xUnit.v3's; it is not caused by parallelism
(serial is preserved on both sides).

Test-count parity — no coverage lost

Using the same same-source pair (so ordering/flake can't confound it), comparing distinct executed test
methods
(data-row args collapsed):

  • True migration regressions (a test that executed under xUnit but is now skipped under MSTest): 0.
  • Global coverage: xUnit 6253 distinct executed methods vs MSTest 6249 on Linux net10.
  • MSTest reports more total and more skipped rows only because MSTest enumerates every [DataRow] of a
    conditionally-skipped (e.g. Windows-only) test as its own NotExecuted row, whereas xUnit reports one. The
    passed counts match for every major project (Framework 777, Engine 4964, OM 1619, CommandLine 450).
  • The only genuine gap found — 4 shared NativeMethodsShared_Tests that had been dropped from all projects
    during the file consolidation — has been fixed in this PR by re-adding the file to Engine.UnitTests
    (they now run and pass). After that fix there is no known executed-test coverage loss.

Migration factors that could affect timing (all intentionally timing-neutral):

  • Serial execution preserved by design — xUnit's maxParallelThreads=1 is mapped to MSTest's
    MSTestParallelizeScope=None, so there is no parallelism-driven swing in either direction. This keeps
    the comparison apples-to-apples and avoids introducing new ordering flakiness.
  • MTP host unchanged — both before and after use the same Microsoft Testing Platform self-hosted
    executable, so there is no VSTest→MTP startup effect in play; only the runner framework differs.
  • The [ModuleInitializer] RunningTests fix runs once at assembly load, and the ToolTask flake fix adds a
    single Process.GetProcessesByName + Kill in one test — both negligible.

For an isolated per-runner number (discovery + execution only, excluding build), a reproducible harness is
included as a follow-up artifact (run-benchmark.sh + benchmark-mstest-vs-xunit.md): it compares
xUnit.v3 (on MTP) at the merge-base with MSTest v4 (on MTP) at this PR head, one cold plus two warm runs per
project.

Reviewer notes

  • Product code changes are intentionally avoided except where required by test infrastructure integration; the substantive migration is in test projects and shared test infrastructure.
  • Serial execution is preserved, so any performance deltas should be attributable to runner/framework behavior rather than changed parallelism.
  • Keep Verify.MSTest pinned at 31.19.1 unless net472 spawned-MSBuild binding behavior is revalidated.

jankratochvilcz and others added 14 commits July 11, 2026 21:38
Add MSTest SDK infrastructure and shared test hooks, then migrate StringTools and Framework unit tests while keeping remaining xUnit projects building.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Convert Microsoft.Build.Tasks.UnitTests from xUnit.v3 to MSTest v4 on the
Microsoft Testing Platform (MSTest.Sdk). All 1205 tests run with 0 failures
on net10.0.

Key changes:
- SDK switched to MSTest.Sdk; Verify.MSTest bumped to 31.24.0 (30.x referenced
  an assembly removed in MSTest 4).
- IClassFixture patterns converted to static-fixture + ClassInitialize/Cleanup.
- Collection Assert.AreEqual (reference compare) converted to Shouldly ShouldBe
  or Assert.AreSame where a same-reference check was intended.
- RoslynCodeTaskFactory Verify tests: class made partial with [UsesVerify];
  UseProjectRelativeDirectory moved to a [ModuleInitializer].
- Added [TestClass] to the concrete Directory.Build/ProjectExtensions import
  test classes so inherited [MSBuildTestMethod]s are discovered.
- Cleaned MSTEST0017/0030/0032/0055/0065 analyzer warnings.
- Custom_COM test-data .cs excluded from Compile (they carry assembly attrs).
- Shared src/Shared/UnitTests/*_Tests.cs remain deferred until Build.UnitTests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Convert Build.UnitTests and shared src/Shared/UnitTests/*_Tests.cs from
xUnit.v3 to MSTest v4 on MTP: MSTest.Sdk csproj, Verify.MSTest pattern for
the 3 snapshot-test files, and fix xUnit-specific Assert overloads
(ContainsSingle/Contains/DoesNotContain predicate forms, Assert.Collection,
IEqualityComparer collection equality, Throws paramName, IsNotType/IsAssignableFrom).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
MSTest Assert.AreEqual does reference equality on collections (unlike xUnit
Assert.Equal which does element-wise). Convert array/dictionary/set comparisons
to CollectionAssert / Helpers.AssertDictionariesEqual / IEquatable, resolving
~25 test failures and clearing MSTEST0065 analyzer warnings. Also remove stray
[TestClass] from non-test helper classes (MSTEST0063) and fix MSTEST0032/SA style warnings.

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

These 2 .OSX.verified.txt baselines (from PR #11318) were the only 2 of 113
snapshots ending in a double trailing newline; Verify.MSTest flags the mismatch.
Align them with actual deterministic output.

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

MSTest requires a derived test class to carry its own [TestClass] to run
inherited test methods; xUnit did this implicitly.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Three migration defects surfaced by CI (build 1505701) that local macOS/net10
runs could not catch:

1. RunTests target missing (MSB4057). Setting UsingToolXUnit=false blanks
   Arcade's TestRunnerName (Tests.targets line 16), so the MSTest runner targets
   that define RunTests were never imported and every test project failed the
   Test target. Select the MSTest-on-MTP runner explicitly instead:
   TestRunnerName=MSTest + EnableMSTestRunner=true + UseMSTestSdk=true, and drop
   UsingToolXUnit=false.

2. Wrong category filter for MSTest. The excluded-category filter used the
   xUnit MTP not-trait extension, which the MSTest runner rejects (it printed
   help and exited non-zero). Rewrite the ';'-separated exclusion list into a
   VSTest-style --filter "TestCategory!=a&TestCategory!=b..." expression, quoted
   so '&' survives /bin/sh under Exec.

3. net472 compile breaks. OSPlatform.FreeBSD does not exist on .NET Framework
   (use OSPlatform.Create("FREEBSD")), and Assert.Skip is an xUnit v3 API the
   converter left in two files (map to Assert.Inconclusive).

Validated locally: Framework.UnitTests now runs via Arcade MTP,
775/775 passed on net10.0 with the category filter correctly applied.

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

@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.

I have done only a first pass review

<ItemGroup>
<Compile Remove="MSBuildTestMethodAttribute.cs" />
</ItemGroup>

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.

Nit

Suggested change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — removed the extra blank line in Microsoft.Build.UnitTests.Shared.csproj.

Comment on lines +10 to +12
<PackageReference Include="xunit.v3.assert" VersionOverride="$(XUnitV3Version)" />
<PackageReference Include="xunit.v3.extensibility.core" VersionOverride="$(XUnitV3Version)" />
<PackageReference Include="MSTest.TestFramework" VersionOverride="4.3.0" PrivateAssets="all" />

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.

This is mixing MSTest and xUnit

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — removed the leftover xunit.v3.* package references and the Xunit.NetCore.Extensions project reference from UnitTests.Shared; the project now only keeps the MSTest/Shouldly references it uses.

<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<Reference Include="System.Net.Http" Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'" />

<PackageReference Include="Shouldly" />
<PackageReference Include="Verify.XunitV3" />

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.

Shall it use Verify.MSTest instead of just dropping?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional — this project no longer has any Verify usages, so there is no Verify.MSTest reference to add here. The projects that still use Verify keep Verify.MSTest.


<IsPackable>false</IsPackable>
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>Microsoft.Build.Coordinator.UnitTests</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Shouldly" />

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.

Why is this added?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional — MSBuild.Coordinator.UnitTests uses Shouldly throughout the migrated tests, so the package reference is required after dropping the xUnit assertion package.

Comment thread Directory.Packages.props Outdated
Comment on lines +54 to +58
<PackageVersion Update="Microsoft.Testing.Platform" Version="2.3.1" />
<PackageVersion Update="Microsoft.Testing.Platform.MSBuild" Version="2.3.1" />
<PackageVersion Update="MSTest.Analyzers" Version="4.3.0" />
<PackageVersion Update="MSTest.TestAdapter" Version="4.3.0" />
<PackageVersion Update="MSTest.TestFramework" Version="4.3.0" />

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.

All of that should not be needed as you are using MSTest.Sdk

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — removed the explicit MSTest/MTP PackageVersion Update entries. With the remaining MSTest.Sdk version properties in place, the affected net10 test projects still build cleanly.

Comment thread Directory.Build.props
<TestRunnerName>MSTest</TestRunnerName>
<EnableMSTestRunner>true</EnableMSTestRunner>
<UseMSTestSdk>true</UseMSTestSdk>
<MSTestParallelizeScope>None</MSTestParallelizeScope>

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.

Is it expected to have no parallelization?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional — yes. This preserves the old xUnit serial execution model; MSBuild tests share process-wide state (environment, temp paths, BuildEnvironment, node/trait settings), and enabling method/class parallelization would reintroduce flakiness. I added 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.

xUnit by default is running parallel on class level not serial.

Comment thread Directory.Build.props
Comment on lines +43 to +44
<MicrosoftTestingPlatformVersion>2.3.1</MicrosoftTestingPlatformVersion>
<MSTestVersion>4.3.0</MSTestVersion>

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.

Why is this set?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional — these properties keep the MSTest.Sdk-added components aligned. Removing them let older defaults back in and reproduced an MSB3277 MSTest.TestFramework conflict locally; I added a comment to make that explicit.

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.

It's not clear. You should only refernce MSTest.Sdk and nothing else.

};
}

public abstract class ConditionalMSBuildTestMethodAttribute : MSBuildTestMethodAttribute

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.

I would suggest to use a MSTest conditional attribute here instead of TestMethod

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional for this migration — these attributes intentionally remain one-attribute xUnit-compatible test markers such as [WindowsOnlyFact]. Moving them to MSTest conditional-only attributes would require adding a separate test-method attribute to many tests and would change the migration shape. The MSTEST0057 suppression is now scoped to this file.

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.

You should not use custom TestMethod for conditions, instead you should inherit from ConditionBaseAttribute. For WindowsOnlyFact it should basically be [TestMethod] + [OSCondition(Windows)]

/// This is really a test of our XmlDocumentWithLocation.
/// </summary>
[Fact]
[MSBuildTestMethod]

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.

Posting only once. This is pretty strange, you are not doing straight replacement (this comment applies to most test methods)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional — [MSBuildTestMethod] is the migration's replacement for [Fact]/[Theory], not a straight [TestMethod], because it preserves the existing per-test MSBuild isolation wrapper and the xUnit-style conditional aliases.

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.

You were using Fact and not a custom attribute so it's not clear how this is preserving any kind of behavior

Assert.StartsWith("EmptyProjectName", solution.ProjectsInOrder[0].ProjectName);
Assert.Equal("src\\.proj", solution.ProjectsInOrder[0].RelativePath);
Assert.Equal("{0ABED153-9451-483C-8140-9E8D7306B216}", solution.ProjectsInOrder[0].ProjectGuid);
solution.ProjectsInOrder[0].ProjectName.ShouldStartWith("EmptyProjectName");

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.

Why are you using Shoudly instead of default assertion here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional — the suite already uses Shouldly broadly, and this project has a Shouldly reference. ShouldStartWith keeps the assertion intent clear and consistent with nearby migrated assertions.

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.

I would then recommend to use banned api analyzer and use only shoudly instead of mixing both styles (it's a lot less clear for reviewers IMO)

Resolve analyzer-as-error failures that only surface when the Windows-only
test files compile on CI:
- MSTEST0065: collection asserts use Assert.AreSequenceEqual/AreNotSequenceEqual
  (ResGen, WinMDExp, TrackedDependencies).
- MSTEST0017: swap Assert.AreEqual(actual, expected) to (expected, actual)
  (AssignProjectConfiguration, ResGen).
- MSTEST0032: remove always-true Assert.IsTrue(true) no-ops in skip branches
  (FileTracker, GenerateResourceOutOfProc).
- MSTEST0030: add [TestClass] to Xaml GeneratedTask fixture.
- MSTEST0036: rename shadowing derived DefaultPaths to TestSpecificDefaultPaths
  in RAR strong-name fixtures (behavior preserved; base member no longer hidden).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment thread Directory.Build.props Outdated
<MicrosoftTestingPlatformVersion>2.3.1</MicrosoftTestingPlatformVersion>
<MSTestVersion>4.3.0</MSTestVersion>
<!-- Custom test method attributes intentionally preserve existing xUnit-style constructors during incremental migration. -->
<NoWarn>$(NoWarn);MSTEST0057</NoWarn>

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.

Suppressing MSTEST0057 repo-wide should not be done. A global NoWarn silences the analyzer for the entire test suite, so it will also hide genuine violations in test code unrelated to the custom attributes. If the suppression is only required for the MSBuildTestMethodAttribute hierarchy, scope it narrowly instead (e.g. #pragma warning disable MSTEST0057 around those declarations, or an .editorconfig entry limited to MSBuildTestMethodAttribute.cs) so the rule keeps protecting the rest of the code.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — removed the repo-wide MSTEST0057 NoWarn and replaced it with a #pragma scoped to MSBuildTestMethodAttribute.cs, around the custom test-method attribute hierarchy only.

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.

You haven't fixed the issue with pragma, only moved the suppression. You should flow the arguments otherwise some features like line redirection in VS won't work properly.

{
}

public class MSBuildTestMethodAttribute : TestMethodAttribute

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.

Why introduce a new MSBuildTestMethodAttribute instead of reusing [TestMethod]? The per-test wrapping (TestEnvironment creation, invariant-culture handling, conditional skip) that this provides could largely be achieved without a custom TestMethodAttribute subclass — e.g. via [TestInitialize]/[TestCleanup] (or a base test class / assembly-level hooks) for the environment and culture setup, and MSTest's built-in conditional attributes for skips. A custom TestMethodAttribute is the heaviest extension point and forces every test and derived conditional attribute onto it (and is what's driving the MSTEST0057 suppression above). Could you explain what specifically requires overriding ExecuteAsync here rather than the lighter-weight hooks?

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.

GlobalTestInitialize/GlobalTestCleanup is what should be used here, I think.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional after re-evaluating it. GlobalTestInitialize/GlobalTestCleanup could cover some environment setup, but not the full current behavior without a much larger and riskier rewrite: exact before/after wrapping of every test body, native + managed MSBuildExtensionsPath* scrubbing and CommunicationsUtilities cache reset for net472, capture/restore of BuildEnvironmentHelper.Instance, invariant-culture selection from method/class attributes, existing category/platform skip behavior for direct runs, and final trait invariant checks. MSTest conditional attributes would also force changing the one-attribute [WindowsOnlyFact]/similar aliases across the suite. I kept the custom wrapper but narrowed MSTEST0057 to that file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Intentional — I considered global hooks, but they don't give the same attribute-driven skip/culture behavior or the exact ExecuteAsync try/finally wrapper around the test body that protects net472 from leaked native environment/static MSBuild state. I kept the wrapper and scoped the analyzer suppression narrowly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Second pass: I re-checked whether this can safely move to MSTest hooks. I’m keeping ExecuteAsync here because GlobalTestInitialize/GlobalTestCleanup are assembly-lifetime hooks, while the regression this fixes is per-test leakage: every test needs to start and end with managed+native MSBuildExtensionsPath* scrubbing plus CommunicationsUtilities cache reset, BuildEnvironmentHelper.Instance restore, culture restore, and trait invariant checks around the test body. [TestInitialize]/[TestCleanup] could only replicate that by forcing the whole suite through a new shared base class, and it still would not preserve the one-attribute conditional aliases/dynamic skip checks without a broad rewrite. Given the net472-only native environment-block behavior and the fact that I can only validate net10 locally, the custom method attribute is the heavier extension point but the lowest-risk containment. MSTEST0057 remains scoped to this file only.

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.

GlobalTestInitialize and GlobalTestCleanup are NOT assembly-lifetime hooks. They are per-test.

jankratochvilcz and others added 12 commits July 13, 2026 14:17
Verify.MSTest 31.x floors Microsoft.Bcl.AsyncInterfaces at [10.0.9,), landing 10.0.9
in the net472 test output for Engine.UnitTests and Tasks.UnitTests. The MSBuild.exe
those tests spawn binds Microsoft.Bcl.AsyncInterfaces to exactly 10.0.0.8 (app.config),
so the mismatched DLL caused a FileLoadException at startup (40 Engine + 13 Tasks
net472 failures). 30.6.0 floors at [9.0.8,), unifying to the 10.0.8 already provided
by System.Text.Json 10.0.8, matching the product redirect. MSTest.TestFramework floor
[3.10.1,) is satisfied by our 4.3.0; all Verify APIs used are stable core APIs.

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

Verify.MSTest 31.24.0 (required for MSTest v4 source-generator compatibility) floors
Microsoft.Bcl.AsyncInterfaces at 10.0.9 on .NETFramework. The product MSBuild.exe that
Engine/Tasks tests spawn binds that assembly to exactly 10.0.0.8 (src/MSBuild/app.config),
so co-locating any other version crashes MSBuild.exe at startup with a FileLoadException
(40 Engine + 13 Tasks net472 failures).

Force Microsoft.Bcl.AsyncInterfaces to 10.0.8 on .NETFramework in the two affected test
projects (VersionOverride, NU1605 suppressed for the intentional downgrade) so the DLL
next to the spawned MSBuild.exe matches its config. Add an explicit binding redirect
(0-10.0.0.9 -> 10.0.0.8) to the shared test App.config so the test host also loads the
10.0.8 that is present. net10.0 is unaffected (override is .NETFramework-only).

Reverts the earlier Verify.MSTest 30.6.0 pin, whose source generator emits code requiring
the MSTest v3 TestFramework.Extensions assembly (CS0012 under MSTest v4).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Forcing Microsoft.Bcl.AsyncInterfaces to 10.0.8 at compile time (previous
attempt) triggered CS1705 because Verify.MSTest 31.x references 10.0.9 and a
referenced assembly may not require a higher version than the direct reference.

Instead compile against the Verify-resolved 10.0.9 (no CS1705) but overwrite the
net472 output copy with 10.0.8 via a post-build target so the co-located product
MSBuild.exe (which binds AsyncInterfaces to exactly 10.0.0.8) loads a matching
assembly. The test host redirects 10.0.9 -> 10.0.8 through the shared App.config.
Opt in per project via VerifyMSTestForcesAsyncInterfaces1008.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The net472 test crash was caused by Verify.MSTest 31.20+ bumping its
Microsoft.Bcl.AsyncInterfaces floor from 10.0.8 to 10.0.9, while the product
MSBuild.exe these two projects spawn (co-located in the test output) binds that
assembly to exactly 10.0.0.8. Any other version on disk throws a FileLoadException
at MSBuild.exe startup; compiling against a lower version fails with CS1705; and an
explicit App.config downgrade redirect collides with AutoGenerateBindingRedirects
(MSB3836). All of these are avoided by simply using the newest Verify.MSTest that
still floors AsyncInterfaces at 10.0.8 -- 31.19.1 -- which also targets MSTest v4
(MSTest.TestFramework 4.2.3), so the source generator compiles cleanly. This mirrors
how main kept AsyncInterfaces at 10.0.8 via Verify.XunitV3.

Reverts the post-build DLL-swap target, the VerifyMSTestForcesAsyncInterfaces1008
opt-in properties, and the shared App.config binding redirect added while chasing
the version conflict.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The migration to MSTest changed test execution order, exposing latent
BuildEnvironmentHelper singleton-pollution bugs and a bulk-migration
artifact that xUnit's ordering had masked:

- MSBuildTestMethodAttribute now captures the clean BuildEnvironment
  established by AssemblyInitialize and restores it after every test, so
  tests that reset the singleton via ResetInstance_ForUnitTestsOnly()
  without restoring (BinaryLogger_Tests, ChangeWaves_Tests, XMake_Tests,
  Exec_Tests, RAR, etc.) no longer leak a re-detected environment into
  environment-sensitive tests (e.g. Evaluator MSBuildExtensionsPath* on
  .NET Framework). Order-independent, covers all current/future polluters.

- BuildEnvironmentHelper_Tests.EmptyStandaloneEnviroment now captures and
  restores the pre-test instance in its Dispose (matching the existing
  SdkResolverLoader_Tests / ProjectCacheTests pattern).

- Removed a stray [TestClass] attribute that the bulk migration script
  injected into the embedded C# task source string in TaskBuilder_Tests
  (GenerateSTATask). That source compiles into a runtime task assembly
  that does not reference MSTest, so the attribute broke compilation and
  caused the TestSTAThread* tests to fail with InvalidProjectFileException.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The 9 remaining net472-only failures in Microsoft.Build.Engine.UnitTests all
derive from a single cause: the process environment leaks
MSBuildExtensionsPath[32|64] pointing at the test host output directory into
later tests. These variables are not reserved, so Utilities.GetEnvironmentProperties
lets them override the computed default. MSBuild persists a build's environment
into the native process block via a raw kernel32 P/Invoke on .NET Framework and
does not always restore it; a managed Environment.SetEnvironmentVariable(null)
from an unrelated test does not reliably clear what MSBuild reads back through
GetEnvironmentStringsW. xUnit's ordering hid this; MSTest's ordering exposes it.

Fix (order-independent, analogous to the BuildEnvironment restore): capture the
clean values of these variables at first run and restore them after every test
via both the managed and the native (kernel32) APIs on .NET Framework.

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

# Conflicts:
#	eng/dependabot/Directory.Packages.props
#	global.json
#	src/Build.UnitTests/Evaluation/PartialEvaluation_Tests.cs
#	src/Framework.UnitTests/FileMatcher_Tests.cs
#	src/MSBuild.UnitTests/XMake_Tests.cs
#	src/Shared/UnitTests/TestAssemblyInfo.cs
The merge with origin/main brought in three new analyzer test files that were
authored against xUnit ([Fact]/[Theory]/[InlineData], using Xunit) but land in
the already-migrated MSTest.Sdk TaskAnalyzer.Tests project. Convert them to
MSTest ([TestClass]/[TestMethod]/[DataRow]) to match the rest of the project.

TaskAnalyzer.Tests net10.0: 207/207 pass.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Build.UnitTests and Tasks.UnitTests still applied the xUnit-FactAttribute-derived
LongPathSupportDisabledFact / RequiresSymbolicLinksFact / WindowsNet35OnlyFact
attributes. Under the MSTest runner those are not discovered as tests, so ~10
tests were compiled but silently never executed. The MSTest equivalents
(LongPathSupportDisabledTestMethod / RequiresSymbolicLinksTestMethod /
WindowsNet35OnlyTestMethod, deriving from MSBuildTestMethodAttribute) already
existed and Framework.UnitTests already used them; point Build/Tasks usages at
them too and delete the now-unused xUnit-derived attribute files.

Verified net10: Build.UnitTests + Tasks.UnitTests compile clean;
BinaryLoggerShouldEmbedSymlinkFilesViaTaskOutput (RequiresSymbolicLinks) now
discovered and passes (previously silently skipped).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The iteration-8 extension-path scrub short-circuited on the managed
Environment.GetEnvironmentVariable read before issuing the native kernel32
SetEnvironmentVariable. On .NET Framework the managed and native environment
blocks diverge: a leaked MSBuildExtensionsPath (written to the native block by a
prior in-proc build, or injected by the dotnet muxer at host launch) reads back
as null through the managed API, so the guard skipped the native scrub and the
leak persisted. Also, the scrub only ran after each test, so the very first
environment-sensitive test still saw the muxer's process-launch value.

Fix: rename to ScrubExtensionPathEnvironmentVariables, drop the managed-read
short-circuit so the native scrub always runs on netfx, and call it BEFORE each
test (in ExecuteAsync) as well as after (in RestoreCleanBuildEnvironment). This
neutralizes the leak that made the 9 net472 MSBuildExtensionsPath* /
VerifyPropertyTrackingLogging* / ProjectItemSpecTooLong failures order-dependent.

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

# Conflicts:
#	src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs
… env cache

The prior test-isolation scrub captured a baseline value for the
MSBuildExtensionsPath[32|64] environment variables on the first test and
restored it after every test. On .NET Framework the very first capture can
already observe a leaked value (the test-host output directory injected into
the native environment block by the dotnet muxer or a prior in-proc build),
so the scrub re-applied the leaked value on every test instead of clearing
it. That defeated isolation and left the same 9 net472 failures
(MSBuildExtensionsPath*, VerifyPropertyTrackingLogging*, ProjectItemSpecTooLong).

The clean state on .NET Framework is *unset*: when the variables are absent,
MSBuild computes the default from Program Files, which is exactly what these
tests expect. Unconditionally unset the three variables (managed + native
kernel32) before and after every test, and invalidate the
CommunicationsUtilities native-environment-block cache via a new test-only
ResetEnvironmentStateForUnitTestsOnly() so the next evaluation re-reads the
freshly-scrubbed block rather than serving a stale snapshot.

net10 unaffected (37/37 MSBuildExtensionsPath*/VerifyPropertyTrackingLogging*/
ProjectItemSpecTooLong pass, no regression).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
jankratochvilcz and others added 7 commits July 14, 2026 17:48
The 9 net472-only test failures (MSBuildExtensionsPath*, VerifyPropertyTracking*,
ProjectItemSpecTooLong) were caused by a static config-cache timing bug, not an
environment-variable leak.

On .NET Framework, ToolsetConfigurationReader caches (process-wide) which app
config it reads for toolset definitions. When BuildEnvironmentHelper.Instance
.RunningTests is false at the first read, it reads MSBuild.exe.config, which
defines a toolset property MSBuildExtensionsPath = $([MSBuild]::
GetMSBuildExtensionsPath()) resolving to the test host's own output directory.
That poisons MSBuildExtensionsPath resolution for the whole run.

xUnit set RunningTests=true from an ITestPipelineStartup before discovery. MSTest
[AssemblyInitialize] can run after MTP discovery has already populated the config
cache with RunningTests=false. Set the running-tests flags from a
[ModuleInitializer] (assembly load, before discovery/data-providers/static ctors)
so the first config read always observes RunningTests=true.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 49f1ea15-de87-41f9-b4c0-62ddf5ecd47f
The last net472-only failure, ProjectItemSpecTooLong, throws PathTooLongException
in Path.GetDirectoryName on a deliberately long (~750 char) relative path that is
supposed to normalize below MAX_PATH.

On .NET Framework, whether GetDirectoryName enforces the 260 limit is governed by
the AppContext switch Switch.System.IO.BlockLongPaths, whose default is derived
from the process target-framework quirk. On main the xUnit-MTP entry exe carries
[TargetFramework(net472)], so BlockLongPaths defaults to false. Under the
MSTest-MTP generated entry point the process falls back to the shared App.config's
stale <supportedRuntime sku="v4.5.1"> (pre-4.6.2), so BlockLongPaths defaults to
true and GetDirectoryName throws.

Set Switch.System.IO.BlockLongPaths=false explicitly in the shared test App.config
so long-path handling matches main's effective behavior regardless of entry-point
quirk detection. Only affects .NET Framework test hosts; net10 ignores app.config.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 49f1ea15-de87-41f9-b4c0-62ddf5ecd47f
…og-file lock

ToolTaskDoesNotHangWhenGrandchildInheritsPipeHandles spawns a ~40s background
ping/sleep that inherits this process's handles, including the Microsoft.Testing.Platform
diagnostic-log file handle. When it outlived the test it kept that log locked, causing
Arcade's post-run WriteLinesToFile to intermittently fail with MSB3491 on one CI leg.
Snapshot the grandchild processes before launch and terminate only the one this test
spawned in a finally, releasing the inherited handle before log finalization. The
grandchild still outlives the 30s EOF timeout during Execute(), preserving the test's intent.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 49f1ea15-de87-41f9-b4c0-62ddf5ecd47f
Merging main brought in PR #13973 (unsupported ITaskItem<T> analyzer), which added
UnsupportedTaskItemTypeAnalyzerTests.cs (xUnit) and a new [Fact] in
PreferTypedParameterAnalyzerTests.cs. Convert them to MSTest ([TestClass]/[TestMethod]/
[DataRow]) to match the rest of the migrated suite. net10 TaskAnalyzer.Tests: 27/27 pass.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 49f1ea15-de87-41f9-b4c0-62ddf5ecd47f
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 49f1ea15-de87-41f9-b4c0-62ddf5ecd47f
Remove redundant xUnit references from UnitTests.Shared, rely on MSTest.Sdk package versions where possible, and scope MSTEST0057 suppression to the custom test method attributes.

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

Copilot-Session: 49f1ea15-de87-41f9-b4c0-62ddf5ecd47f
@jankratochvilcz

jankratochvilcz commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

@Evangelink thanks for the thorough review! Note I'm mostly experimenting to see what the transition looks like and most importantly what are the tangible benefits. The responses in this PR are mostly AI generated. I'm mostly trying to speedrun to a runnable state so I can have an apples to apples comparison, not too focused on polish at the moment yet.

jankratochvilcz and others added 3 commits July 15, 2026 08:36
…MSTest

PR #14224 (coordinator deadlock fix) landed on main adding new xUnit [Fact] tests
in MSBuild.Coordinator.UnitTests. Merged main and converted the new/conflicting tests
to MSTest ([Fact]->[MSBuildTestMethod]), and fixed the CoordinatorTestHelper ctor param
from ITestOutputHelper to TestContext. All 73 coordinator tests pass on net10.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 49f1ea15-de87-41f9-b4c0-62ddf5ecd47f
Build 1509649: 7/8 legs fully green (all net472 + Linux + Source-Build).
Only macOS Core failed on flaky shared-infra invariant BuildFailureLogInvariant
(TestEnvironment.cs debug/failure-log file-count race, extended by main PR #14224 for
coordinator traces) in Tasks CopyWithDuplicatesUsingFiles, plus a macOS coordinator
test-host crash (no TRX). Both pass on Linux + Windows; classic macOS process/pipe timing
flakiness, not a migration defect. Re-running to confirm.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 49f1ea15-de87-41f9-b4c0-62ddf5ecd47f
macOS Core leg is pre-existing-flaky (fails on main's own xUnit builds too,
e.g. build 1509637 macOS-only failure); all other legs green.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 49f1ea15-de87-41f9-b4c0-62ddf5ecd47f
@jankratochvilcz
jankratochvilcz marked this pull request as ready for review July 15, 2026 08:13
Copilot AI review requested due to automatic review settings July 15, 2026 08:13
@jankratochvilcz
jankratochvilcz requested a review from a team as a code owner July 15, 2026 08: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.

Copilot wasn't able to review this pull request because it exceeds the maximum number of files (300). Try reducing the number of changed files and requesting a review from Copilot again.

During the xUnit->MSTest migration, NativeMethodsShared_Tests.cs was removed
from Tasks.UnitTests and Utilities.UnitTests (where it had duplicate coverage)
but, unlike its shared siblings (AssemblyNameEx_Tests, PrintLineDebugger_Tests,
ResourceUtilities_Tests) which were consolidated into Engine.UnitTests, it was
never re-added anywhere. That silently dropped 4 cross-platform tests
(GetLastWriteDirectoryUtcTimeReturnsMinValueForFile,
GetLastWriteFileUtcTimeReturnsMinValueForDirectory,
GetLastWriteFileUtcTimeReturnsMinValueForMissingFile,
SetCurrentDirectoryDoesNotSetNonexistentFolder) plus one Windows-only test.

Add the file to Engine.UnitTests alongside its siblings. The class already has
[TestClass]/[MSBuildTestMethod], so it is discovered and the 4 tests pass on
net10 (Windows-only TestGetProcAddress skips off-Windows as expected).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 49f1ea15-de87-41f9-b4c0-62ddf5ecd47f
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