From 4a44cd01513c75e969a752b00d3ab6d4090f6b0a Mon Sep 17 00:00:00 2001 From: Malcolm Daigle Date: Tue, 31 Mar 2026 12:35:21 -0700 Subject: [PATCH 1/7] Trim docs using pwsh via dotnet tool to support cross-platform local development. --- dotnet-tools.json | 7 +++++++ .../ref/Microsoft.Data.SqlClient.csproj | 9 ++++++--- tools/targets/TrimDocsForIntelliSense.targets | 4 ++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/dotnet-tools.json b/dotnet-tools.json index 1f59e06063..11e3494512 100644 --- a/dotnet-tools.json +++ b/dotnet-tools.json @@ -15,6 +15,13 @@ "apicompat" ], "rollForward": false + }, + "powershell": { + "version": "7.6.0", + "commands": [ + "pwsh" + ], + "rollForward": false } } } \ No newline at end of file diff --git a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj index dbdf7e9338..675dd20d5c 100644 --- a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj @@ -36,6 +36,10 @@ $(ArtifactPath)$(AssemblyName).ref/$(ReferenceType)-$(Configuration)/ + + + + - powershell.exe - pwsh - $(PowerShellCommand) + dotnet tool run pwsh -NonInteractive -ExecutionPolicy Unrestricted -Command "$(RepoRoot)tools\intellisense\TrimDocs.ps1 -inputFile '$(DocumentationFile)' -outputFile '$(DocumentationFile)'" diff --git a/tools/targets/TrimDocsForIntelliSense.targets b/tools/targets/TrimDocsForIntelliSense.targets index 05feb26739..fd0ca40ea8 100644 --- a/tools/targets/TrimDocsForIntelliSense.targets +++ b/tools/targets/TrimDocsForIntelliSense.targets @@ -3,7 +3,7 @@ The .NET Foundation licenses this file to you under the MIT license. --> - - + + From 02f53d2f9ec99b7499c828f1882b2e09a8301dda Mon Sep 17 00:00:00 2001 From: Malcolm Daigle Date: Thu, 2 Apr 2026 14:18:13 -0700 Subject: [PATCH 2/7] Manually invoke tool restore before build. --- .../common/templates/jobs/ci-build-nugets-job.yml | 3 +++ .../common/templates/jobs/ci-run-tests-job.yml | 3 +++ .../jobs/build-signed-csproj-package-job.yml | 3 +++ .../jobs/build-signed-sqlclient-package-job.yml | 3 +++ .../build-all-configurations-signed-dlls-step.yml | 3 +++ eng/pipelines/steps/tool-restore.yml | 14 ++++++++++++++ .../ref/Microsoft.Data.SqlClient.csproj | 3 --- tools/targets/CompareMdsRefAssemblies.targets | 7 ------- tools/targets/TrimDocsForIntelliSense.targets | 1 - 9 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 eng/pipelines/steps/tool-restore.yml diff --git a/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml b/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml index 5e17b506c1..1bd403103f 100644 --- a/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml +++ b/eng/pipelines/common/templates/jobs/ci-build-nugets-job.yml @@ -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 diff --git a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml index 76a3bf3c75..a0fb09e787 100644 --- a/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml +++ b/eng/pipelines/common/templates/jobs/ci-run-tests-job.yml @@ -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 diff --git a/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml b/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml index e257386918..d55fcb2089 100644 --- a/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml +++ b/eng/pipelines/onebranch/jobs/build-signed-csproj-package-job.yml @@ -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/code-analyze-step.yml@self parameters: diff --git a/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml b/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml index f3033b07f0..cb10cd2ffe 100644 --- a/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml +++ b/eng/pipelines/onebranch/jobs/build-signed-sqlclient-package-job.yml @@ -63,6 +63,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 + # Build our tooling, which is required by the analysis step below, but # shouldn't be analyzed itself. - task: MSBuild@1 diff --git a/eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml b/eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml index b1429e1fd3..edaa3231dd 100644 --- a/eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml +++ b/eng/pipelines/onebranch/steps/build-all-configurations-signed-dlls-step.yml @@ -40,6 +40,9 @@ steps: # 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 + - task: MSBuild@1 displayName: 'BuildAllConfigurations using build.proj' inputs: diff --git a/eng/pipelines/steps/tool-restore.yml b/eng/pipelines/steps/tool-restore.yml new file mode 100644 index 0000000000..b213583e82 --- /dev/null +++ b/eng/pipelines/steps/tool-restore.yml @@ -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) diff --git a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj index 675dd20d5c..a1e572f494 100644 --- a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj @@ -36,9 +36,6 @@ $(ArtifactPath)$(AssemblyName).ref/$(ReferenceType)-$(Configuration)/ - - - - - - - - - diff --git a/tools/targets/TrimDocsForIntelliSense.targets b/tools/targets/TrimDocsForIntelliSense.targets index fd0ca40ea8..fc94ddbc57 100644 --- a/tools/targets/TrimDocsForIntelliSense.targets +++ b/tools/targets/TrimDocsForIntelliSense.targets @@ -3,7 +3,6 @@ The .NET Foundation licenses this file to you under the MIT license. --> - From 91290d534dd61f94090d1c21faa8efca6a46b9f6 Mon Sep 17 00:00:00 2001 From: Malcolm Daigle Date: Thu, 2 Apr 2026 14:22:09 -0700 Subject: [PATCH 3/7] Add missing tool restore. --- eng/pipelines/onebranch/jobs/build-signed-mds-package-job.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/eng/pipelines/onebranch/jobs/build-signed-mds-package-job.yml b/eng/pipelines/onebranch/jobs/build-signed-mds-package-job.yml index 4464a3a9f1..73dab3f93b 100644 --- a/eng/pipelines/onebranch/jobs/build-signed-mds-package-job.yml +++ b/eng/pipelines/onebranch/jobs/build-signed-mds-package-job.yml @@ -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: From 61ae6def7863c1432a3d9ab57064a149e30ce0ac Mon Sep 17 00:00:00 2001 From: Malcolm Daigle Date: Thu, 2 Apr 2026 14:37:34 -0700 Subject: [PATCH 4/7] Remove dangling targets --- .../ref/Microsoft.Data.SqlClient.csproj | 1 - tools/targets/CompareMdsRefAssemblies.targets | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj index 3e9a39a5d1..7813dee463 100644 --- a/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/ref/Microsoft.Data.SqlClient.csproj @@ -47,7 +47,6 @@ diff --git a/tools/targets/CompareMdsRefAssemblies.targets b/tools/targets/CompareMdsRefAssemblies.targets index 5de3eed5ca..11648a01de 100644 --- a/tools/targets/CompareMdsRefAssemblies.targets +++ b/tools/targets/CompareMdsRefAssemblies.targets @@ -148,7 +148,7 @@ + DependsOnTargets="_DownloadBaselinePackage;_BuildLegacyRefNetFx;_BuildLegacyRefNetCore;_BuildNewRefProject"> - From 4e94e5355197b99ac866f3c9f42769405542242e Mon Sep 17 00:00:00 2001 From: Malcolm Daigle Date: Thu, 2 Apr 2026 16:03:34 -0700 Subject: [PATCH 7/7] Add plan file. --- .github/plans/plan-migrateToMtp.prompt.md | 93 +++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 .github/plans/plan-migrateToMtp.prompt.md diff --git a/.github/plans/plan-migrateToMtp.prompt.md b/.github/plans/plan-migrateToMtp.prompt.md new file mode 100644 index 0000000000..f89d66b4a8 --- /dev/null +++ b/.github/plans/plan-migrateToMtp.prompt.md @@ -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 `true` 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.