Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions .github/plans/plan-migrateToMtp.prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Plan: Migrate to Microsoft.Testing.Platform (MTP)

**TL;DR**: You're currently on **xUnit 2.9.3** running under **VSTest** (via `Microsoft.NET.Test.Sdk` + `xunit.runner.visualstudio`). The recommended migration path is to upgrade to **xUnit v3 with native MTP support** via `xunit.v3.runner.mtp`. Your `xunit.runner.json` already contains `_v3_` prefixed settings, so this move was clearly anticipated. The biggest risk is verifying that `Microsoft.DotNet.XUnitExtensions` (from dotnet/arcade) has a v3-compatible release — it's used heavily for `ConditionalFact`/`ConditionalTheory` across ManualTests and FunctionalTests.

---

## Current State

| Component | Current |
|-----------|---------|
| Test framework | xUnit 2.9.3 |
| Test platform | VSTest (`Microsoft.NET.Test.Sdk` 17.14.1) |
| VSTest adapter | `xunit.runner.visualstudio` 2.8.2 |
| Console runner | `xunit.runner.console` 2.9.3 |
| Conditional test infra | `Microsoft.DotNet.XUnitExtensions` 11.0.0-beta |
| Target frameworks | net462, net8.0, net9.0, net10.0 |

5 test projects affected: UnitTests, FunctionalTests, ManualTests, Abstractions.Test, Azure.Test. PerformanceTests/StressTests are out of scope (BenchmarkDotNet/custom harness).

---

## Steps

### Phase 1: Dependency & Compatibility Research *(blocks everything)*

1. **Verify `Microsoft.DotNet.XUnitExtensions` v3 compatibility** — check dotnet/arcade for an xUnit v3-compatible version. This is the single biggest blocker. If none exists, fall back to a bridge approach (VSTestBridge shim with xUnit v2 under MTP).
2. **Audit xUnit v2→v3 breaking changes** — key areas: `IAsyncLifetime` gains `CancellationToken`, `Assert` API changes, `TheoryData` generics, `[Collection]` behavior.
3. **Verify net462 support** — xUnit v3 supports .NET Standard 2.0+ (includes net462), but confirm `xunit.v3.runner.mtp` works on that TFM.

### Phase 2: Package Updates *(parallel with step 5)*

4. **Update `tests/Directory.Packages.props`**: replace `xunit` → `xunit.v3`, remove `xunit.runner.visualstudio` + `xunit.runner.console` + `Microsoft.NET.Test.Sdk`, add `xunit.v3.runner.mtp`, update `Microsoft.DotNet.XUnitExtensions` to v3-compatible version.
5. **Add MTP properties** — set `<UseMicrosoftTestingPlatformRunner>true</UseMicrosoftTestingPlatformRunner>` in test projects or a shared props file if needed.

### Phase 3: Update Test Project Files *(depends on Phase 2)*

6. **Update all 5 test csproj files** — each has package refs duplicated in netfx and netcore `ItemGroup`s. Replace `xunit` → `xunit.v3`, remove `xunit.runner.visualstudio`, `xunit.runner.console`, `Microsoft.NET.Test.Sdk`, add `xunit.v3.runner.mtp`.
7. **Update `xunit.runner.json`** — activate v3 settings (rename `_v3_` keys to actual names), switch `$schema` to v3, remove v2-only `shadowCopy`.

### Phase 4: Fix Test Code *(depends on Phase 3)*

8. **Fix xUnit v3 breaking changes** — `IAsyncLifetime` signature changes, assertion API changes, namespace changes.
9. **Fix `Microsoft.DotNet.XUnitExtensions` API changes** — update all `ConditionalFact`, `ConditionalTheory`, `PlatformSpecific`, `ActiveIssue`, `SkipOnPlatform` usages if needed.

### Phase 5: Build Infrastructure *(depends on Phase 3)*

10. **Update `build2.proj` test targets** — verify `dotnet test` CLI args work under MTP: `--blame-hang`, `--collect "Code coverage"`, `--filter`, `--logger:"trx"`. MTP optionally replaces these with extension packages (`Microsoft.Testing.Extensions.HangDump`, `.TrxReport`, `.CodeCoverage`).
11. **Update `CodeCoverage.runsettings`** — verify MTP compatibility.
12. **Update CI pipelines in `eng/pipelines/`** — current pipelines use `MSBuild@1`/`DotNetCoreCLI@2` which invoke `dotnet test`, so changes should be minimal.

### Phase 6: Validation *(depends on all above)*

13. Build + run UnitTests (simplest, fastest feedback) on all TFMs and OSes.
14. Build + run FunctionalTests.
15. Build + run ManualTests (CI or with SQL Server).
16. Verify code coverage, TRX output, blame-hang, and test filtering all work.
17. Full CI pipeline pass.

---

## Relevant Files

- `src/Microsoft.Data.SqlClient/tests/Directory.Packages.props` — central test package versions (primary)
- `src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft.Data.SqlClient.UnitTests.csproj` — package refs in netfx + netcore ItemGroups
- `src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj` — same
- `src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj` — same
- `src/Microsoft.Data.SqlClient.Extensions/Abstractions/test/Abstractions.Test.csproj` — extension tests
- `src/Microsoft.Data.SqlClient.Extensions/Azure/test/Azure.Test.csproj` — extension tests
- `src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/xunit.runner.json` — runner config (already v3-prepped)
- `src/Microsoft.Data.SqlClient/tests/tools/Microsoft.Data.SqlClient.TestUtilities/CodeCoverage.runsettings` — coverage config
- `build2.proj` — `TestMdsUnit`, `TestMdsFunctional`, `TestMdsManual` targets (lines 495-590)

## Verification

1. `dotnet build` succeeds for all test csproj files across all TFMs
2. `dotnet test` discovers and runs tests under MTP (not VSTest)
3. `--filter "category!=failing&category!=flaky"` filtering works
4. `--collect "Code coverage"` + runsettings produces coverage
5. `--logger:"trx"` produces TRX output
6. `dotnet msbuild build2.proj -t:TestMdsUnit` works end-to-end
7. CI pipeline passes all stages

## Decisions

- **Recommended**: Direct xUnit v3 + native MTP (Option B) — `xunit.runner.json` already prepped for v3
- **Fallback**: If `Microsoft.DotNet.XUnitExtensions` lacks v3 support → use VSTestBridge shim with xUnit v2 under MTP first, upgrade to v3 later
- **Out of scope**: PerformanceTests, StressTests, legacy `build.proj`

## Further Considerations

1. **`Microsoft.DotNet.XUnitExtensions` v3 compatibility** — The single biggest risk. Check [dotnet/arcade](https://github.com/dotnet/arcade) for xUnit v3 tracking. If blocked, the bridge approach (`Microsoft.Testing.Extensions.VSTestBridge`) lets you adopt MTP immediately without touching xUnit version.
2. **Incremental vs. big-bang** — Since package versions are centralized in `Directory.Packages.props`, all 5 projects move together. You could migrate UnitTests first by decentralizing its packages temporarily, but this adds complexity. Recommend moving all at once since the csproj changes are mechanical.
3. **MTP native extensions vs. CLI compat** — MTP offers packages like `Microsoft.Testing.Extensions.HangDump` and `.TrxReport` as replacements for `--blame-hang` and `--logger:trx`. For initial migration, stick with `dotnet test` CLI compatibility to minimize changes, then optionally adopt native extensions later.
4 changes: 4 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ jobs:
uses: actions/setup-dotnet@v5.0.1
with:
global-json-file: global.json

- name: Restore dotnet tools
shell: bash
run: dotnet tool restore

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
Expand Down
7 changes: 7 additions & 0 deletions dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@
"apicompat"
],
"rollForward": false
},
"powershell": {
"version": "7.6.0",
"commands": [
"pwsh"
],
"rollForward": false
}
}
}
3 changes: 3 additions & 0 deletions eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ jobs:
# Install the .NET SDK.
- template: /eng/pipelines/steps/install-dotnet.yml@self

# Restore dotnet CLI tools (e.g. pwsh, apicompat) before building.
- template: /eng/pipelines/steps/tool-restore.yml@self

# When we're performing a Debug build, we still want to try _compiling_ the
# code in Release mode to ensure downstream pipelines don't encounter
# compilation errors. We won't use the Release artifacts for anything else
Expand Down
3 changes: 3 additions & 0 deletions eng/pipelines/common/templates/jobs/ci-run-tests-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ jobs:
${{ else }}:
runtimes: [8.x, 9.x]

# Restore dotnet CLI tools (e.g. pwsh, apicompat) before building.
- template: /eng/pipelines/steps/tool-restore.yml@self

- ${{ if ne(parameters.prebuildSteps, '') }}:
- ${{ parameters.prebuildSteps }} # extra steps to run before the build like downloading sni and the required configuration

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ jobs:
# Install the .NET SDK.
- template: /eng/pipelines/steps/install-dotnet.yml@self

# Restore dotnet CLI tools (e.g. pwsh, apicompat) before building.
- template: /eng/pipelines/steps/tool-restore.yml@self

# Perform Roslyn analysis before building, since this step will clobber build output.
- template: /eng/pipelines/onebranch/steps/roslyn-analyzers-csproj-step.yml@self
parameters:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ jobs:
# Install the .NET SDK
- template: /eng/pipelines/steps/install-dotnet.yml@self

# Restore dotnet CLI tools (e.g. pwsh, apicompat) before building.
- template: /eng/pipelines/steps/tool-restore.yml@self

# Perform analysis before building, since this step will clobber build output
- template: /eng/pipelines/onebranch/steps/roslyn-analyzers-mds-step.yml@self
parameters:
Expand Down
14 changes: 14 additions & 0 deletions eng/pipelines/steps/tool-restore.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#################################################################################
# Licensed to the .NET Foundation under one or more agreements. #
# The .NET Foundation licenses this file to you under the MIT license. #
# See the LICENSE file in the project root for more information. #
#################################################################################

# Restores dotnet CLI tools defined in dotnet-tools.json.
# This step should be invoked after install-dotnet.yml and before any build
# steps that depend on the restored tools (e.g. pwsh, apicompat).

steps:
- script: dotnet tool restore
displayName: Restore .NET Tools
workingDirectory: $(Build.SourcesDirectory)
Comment on lines +7 to +14
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The PR description is still the default template and doesn’t describe the intent of these changes (tooling/pipeline updates, test package changes, flaky test quarantine). Please update the PR description with a short summary and testing notes so reviewers can validate scope and impact.

Copilot uses AI. Check for mistakes.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<OutputPath>$(ArtifactPath)$(AssemblyName).ref/$(ReferenceType)-$(Configuration)/</OutputPath>
</PropertyGroup>


<!-- Trim Docs for IntelliSense ====================================== -->
<!--
It has been determined that including the remarks section in IntelliSense docs looks bad in
Expand All @@ -48,10 +49,8 @@
AfterTargets="Build"
Condition="'$(IsCrossTargetingBuild)' != 'true' AND '$(GenerateDocumentationFile)' == 'true'">
<PropertyGroup>
<PowerShellCommand Condition="'$(OS)' == 'Windows_NT'">powershell.exe</PowerShellCommand>
<PowerShellCommand Condition="'$(OS)' != 'Windows_NT'">pwsh</PowerShellCommand>
<PowerShellCommand>
$(PowerShellCommand)
dotnet tool run pwsh
-NonInteractive
-ExecutionPolicy Unrestricted
-Command "$(RepoRoot)tools\intellisense\TrimDocs.ps1 -inputFile '$(DocumentationFile)' -outputFile '$(DocumentationFile)'"
Comment on lines 51 to 56
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

The ref build’s TrimDocs step now shells out via dotnet tool run pwsh. If the local tool manifest hasn’t been restored, building the ref project will fail late in the build (after compilation) with a missing-tool error. Consider adding a precondition/error message (or performing tool restore earlier in the repo build) to make this failure mode clearer and avoid surprising local builds.

Copilot uses AI. Check for mistakes.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ public void SimpleFillTest()

// TODO Synapse: Remove Northwind dependency by creating required tables in setup.
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))]
// https://github.com/dotnet/SqlClient/issues/4135
[Trait("Category", "flaky")]
public void FillShouldAllowRetryLogicProviderToBeInvoked()
{
int maxRetries = 3;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
<Reference Include="System.Transactions" />

<PackageReference Include="Microsoft.Data.SqlClient.SNI" />
<PackageReference Include="Microsoft.DotNet.XUnitExtensions" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Microsoft.SqlServer.Server" />
<PackageReference Include="System.Configuration.ConfigurationManager" />
Expand All @@ -73,7 +72,6 @@
<!-- References for netcore -->
<ItemGroup Condition="'$(TargetFramework)' != 'net462'">
<PackageReference Include="Microsoft.Data.SqlClient.SNI.runtime" />
<PackageReference Include="Microsoft.DotNet.XUnitExtensions" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Microsoft.SqlServer.Server" />
<PackageReference Include="System.Configuration.ConfigurationManager" />
Expand Down
9 changes: 1 addition & 8 deletions tools/targets/CompareMdsRefAssemblies.targets
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,6 @@
Text="Baseline ref assemblies available at $(BaselineExtractDir)ref\" />
</Target>

<!-- ================================================================== -->
<!-- _RestoreTools -->
<!-- ================================================================== -->
<Target Name="_RestoreTools" DependsOnTargets="_SetApiCompatProperties">
<Exec Command="dotnet tool restore" WorkingDirectory="$(RepoRoot)" />
</Target>

<!-- ================================================================== -->
<!-- _BuildLegacyRefNetFx -->
<!-- ================================================================== -->
Expand Down Expand Up @@ -155,7 +148,7 @@
<!-- _RunRefApiCompat -->
<!-- ================================================================== -->
<Target Name="_RunRefApiCompat"
DependsOnTargets="_DownloadBaselinePackage;_RestoreTools;_BuildLegacyRefNetFx;_BuildLegacyRefNetCore;_BuildNewRefProject">
DependsOnTargets="_DownloadBaselinePackage;_BuildLegacyRefNetFx;_BuildLegacyRefNetCore;_BuildNewRefProject">

Comment on lines 148 to 152
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

Removing the _RestoreTools dependency means CompareMdsRefAssemblies now assumes the local tool manifest has already been restored (for apicompat). This is a behavior change that can break the documented one-liner usage unless callers remember to run dotnet tool restore first. Consider keeping a tool-restore dependency (optionally gated behind a property) or adding a clear error/message when the tool isn’t available.

Copilot uses AI. Check for mistakes.
<!--
For each TFM, compare:
Expand Down
3 changes: 1 addition & 2 deletions tools/targets/TrimDocsForIntelliSense.targets
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ The .NET Foundation licenses this file to you under the MIT license. -->
<Project>
<!-- This target runs after Build, and trims XML documentation generated in the $(OutputPath) of the project where this target is included.-->
<Target Name="TrimDocsForIntelliSense" AfterTargets="Build" Condition="'$(GenerateDocumentationFile)' == 'true'">
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

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

This target now relies on dotnet tool run pwsh, which will fail if local tools haven’t been restored yet (common on first build after clone). Consider either (1) restoring tools as part of the build flow before this target runs, or (2) falling back to system-installed pwsh/powershell.exe when available and emit a clear error instructing dotnet tool restore otherwise.

Suggested change
<Target Name="TrimDocsForIntelliSense" AfterTargets="Build" Condition="'$(GenerateDocumentationFile)' == 'true'">
<Target Name="TrimDocsForIntelliSense" AfterTargets="Build" Condition="'$(GenerateDocumentationFile)' == 'true'">
<Exec Command="dotnet tool restore" />

Copilot uses AI. Check for mistakes.
<Exec Command="powershell.exe -NonInteractive -ExecutionPolicy Unrestricted -Command &quot;$(ToolsDir)intellisense\TrimDocs.ps1 -inputFile &apos;$(OutputPath)\Microsoft.Data.SqlClient.xml&apos; -outputFile &apos;$(OutputPath)\Microsoft.Data.SqlClient.xml&apos;&quot;" Condition=" '$(OS)' == 'Windows_NT' " />
<Exec Command="pwsh -NonInteractive -ExecutionPolicy Unrestricted -Command &quot;$(ToolsDir)intellisense\TrimDocs.ps1 -inputFile &apos;$(OutputPath)\Microsoft.Data.SqlClient.xml&apos; -outputFile &apos;$(OutputPath)\Microsoft.Data.SqlClient.xml&apos;&quot;" Condition=" '$(OS)' != 'Windows_NT' " />
<Exec Command="dotnet tool run pwsh -NonInteractive -ExecutionPolicy Unrestricted -Command &quot;$(ToolsDir)intellisense\TrimDocs.ps1 -inputFile &apos;$(OutputPath)\Microsoft.Data.SqlClient.xml&apos; -outputFile &apos;$(OutputPath)\Microsoft.Data.SqlClient.xml&apos;&quot;" />
</Target>
</Project>
Loading