Add C++/CLI + MSTest acceptance tests (VSTest and MTP)#9509
Conversation
Guards that MSTest test classes authored in a C++/CLI managed assembly are discovered and executed by the MSTest VSTest adapter. Windows-only and skipped (inconclusive) when the MSVC toolset is absent. Builds a tiny C++/CLI test assembly against the freshly-packed MSTest packages with VS MSBuild, then runs it via vstest.console with the just-built adapter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds CppCliMtpTests guarding that MSTest tests authored in a C++/CLI managed assembly can be hosted on Microsoft.Testing.Platform as a self-contained test executable (no vstest.console). Harvests the MTP+MSTest net472 runtime closure via a generated C# EnableMSTestRunner project, builds the C++/CLI exe with VS MSBuild, deploys the closure, and asserts MTP discovers and runs the tests. Extracts shared gate/env helpers into CppCliTestSupport and reuses them from the existing VSTest test. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds two Windows-only MSTest acceptance tests that validate authoring MSTest tests in a managed C++/CLI assembly and running them via both legacy VSTest and Microsoft.Testing.Platform (MTP) hosting paths. It also introduces shared helper utilities to locate a VS install with the VC toolset and to sanitize child-process environments from code-coverage profiler variables.
Changes:
- Add
CppCliVSTestTeststo build a generated C++/CLI test DLL and run it viavstest.console.exeusing the freshly packed MSTest adapter. - Add
CppCliMtpTeststo build a C++/CLI MTP-hosted test EXE and run it as a self-contained MTP test host. - Add
CppCliTestSupporthelper for VS/toolset detection (viavswhere) and code-coverage env-var stripping for child processes.
Show a summary per file
| File | Description |
|---|---|
| test/IntegrationTests/MSTest.Acceptance.IntegrationTests/CppCliVSTestTests.cs | New VSTest-path acceptance test that builds a C++/CLI MSTest DLL and validates discovery/execution via vstest.console.exe. |
| test/IntegrationTests/MSTest.Acceptance.IntegrationTests/CppCliMtpTests.cs | New MTP-path acceptance test that harvests the net472 runtime closure, builds a C++/CLI MTP host EXE, and validates execution. |
| test/IntegrationTests/MSTest.Acceptance.IntegrationTests/CppCliTestSupport.cs | Shared VS discovery + environment sanitization utilities used by both C++/CLI acceptance tests. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 5
- Review effort level: Low
This comment has been minimized.
This comment has been minimized.
Evangelink
left a comment
There was a problem hiding this comment.
Note
🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.
Expert Review — PR #9509 · Add C++/CLI + MSTest acceptance tests (VSTest and MTP)
| # | Dimension | Verdict |
|---|---|---|
| 12 | Flakiness Patterns | 🟡 1 MODERATE |
| 15 | Code Structure & Simplification | 🔵 1 NIT |
✅ 20/22 dimensions clean.
- Flakiness — both vcxproj templates hardcode
PlatformToolset=v143(VS 2022 only), but the vswhere guard inTryFindVsInstallWithCppToolsetAsyncaccepts any VS that shipsVC.Tools.x86.x64(VS 2017+). A VS 2019-only machine passes the availability check but the MSBuild step errors out, reporting FAILED instead of Inconclusive. Fix: add-version "[17.0,)"to the vswhere query inCppCliTestSupport. - Code Structure (NIT) —
CodeCoverageEnvironmentVariablesis astring[]iterated with LINQContains. AHashSet<string>withStringComparer.OrdinalIgnoreCaseis more idiomatic and O(1).
Per-dimension notes
| # | Dimension | Notes |
|---|---|---|
| 1 | Algorithmic Correctness | ✅ Exit-code assertions include the full build/run output in the failure message. Null-guard pattern with Assert.Inconclusive + return is correct (the return is unreachable but defensive and matches codebase convention). |
| 2 | Threading & Concurrency | ✅ Each test class has one test method; no shared mutable state. CommandLine manages its own process semaphore. C++/CLI Task.Result pattern in MtpHost.cpp is safe for a managed-entry-point console app with no SynchronizationContext. |
| 3 | Security & IPC | ✅ All paths constructed via Path.Combine; no user-controlled data fed to shell arguments beyond the asset-generation pipeline that the rest of the acceptance suite already uses. |
| 7 | Resource Management | ✅ All CommandLine, TestAsset, and TempDirectory instances are guarded by using. CancellationTokenRegistration in CommandLine.RunAsyncAndReturnExitCodeAsync is disposed via the using CancellationTokenRegistration statement. |
| 8 | Defensive Coding | ✅ Both test methods assert exit codes with Assert.AreEqual(0, exitCode, $"...{output}..."), providing full diagnostic output on failure. |
| 10 | Test Isolation | ✅ Each method creates its own TestAsset directory. Binlog filenames are unique per method; no shared mutable state across the two new test classes. |
| 11 | Assertion Quality | ✅ Assertions match the repo convention: Assert.Contains for substring matching, Assert.AreEqual for exit codes, Assert.IsTrue(File.Exists(...)) with descriptive messages. |
| 17 | Documentation | ✅ Class-level XML docs clearly explain the scenario, the NuGet-can't-resolve-in-vcxproj limitation, and the harvest workaround. Inline comments explain each numbered step. |
| 21 | Scope & PR Discipline | ✅ Three new files, all test-only, all in the correct acceptance-test project. No production code touched. |
- Derive MSBuild from the validated VS install (TryGetMSBuildPathFromVsInstall) instead of locating it independently, so the C++ targets/toolset are guaranteed available on multi-VS machines. - Use the install's default platform toolset instead of hard-coded v143 so the tests work on any VS version with the VC toolset (not just VS2022). Omitting the toolset entirely defaults to v100 and fails, so the install default is selected explicitly via DefaultPlatformToolset. - Self-skip (Inconclusive) when vstest.console.exe is absent from the located VS install (e.g. Build Tools without the Test Platform), matching the missing-toolset behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
Use a HashSet<string> with OrdinalIgnoreCase instead of a string[] + LINQ linear scan for the code-coverage environment variable filter (per review nit). IDE0028 is suppressed locally because a collection expression cannot carry the custom comparer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🧪 Test quality grade — PR #95092 new integration tests graded across 2 files. Both earn A — they are well-structured end-to-end acceptance tests with numbered steps, meaningful intermediate assertions at each stage, and clean environment hygiene (code-coverage profiler variables stripped before spawning child processes). No issues found; these are good additions.
This advisory comment was generated automatically. Grades are heuristic
|
Why
Some internal teams still rely on the deprecated
Microsoft::VisualStudio::CppUnitTestFramework(CppUnit). It's VSTest-bound and aimed at managed/mixed C++, so MSTest is a plausible migration target for the C++/CLI subset of those projects. These acceptance tests guard that scenario against accidental breakage as we move toward Microsoft.Testing.Platform (MTP).What
Two Windows-only acceptance tests in
MSTest.Acceptance.IntegrationTests, each building a tiny C++/CLI managed test assembly against the freshly-built MSTest packages and asserting the tests are discovered and executed:CppCliVSTestTests— runs the C++/CLI assembly throughvstest.console.exe+ the just-built MSTest adapter (the legacy, VSTest-bound path).CppCliMtpTests— hosts Microsoft.Testing.Platform from a C++/CLIApplicationwith a managedmain(CreateBuilderAsync/AddMSTest/BuildAsync/RunAsync), producing a self-contained MTP test host — novstest.consoleneeded.CppCliTestSupport— shared helpers (locate a VS install with the MSVC toolset viavswhere; build a child-process environment with the code-coverage profiler variables stripped).How they work
A C++/CLI
.vcxprojcannot consume managed assemblies from a NuGetPackageReference, so:MSTest.TestFramework/MSTest.TestAdapterand references the assemblies via<HintPath>;net472runtime closure by building a generated C#EnableMSTestRunnerproject (NuGet resolves the closure into its output), then deploys it next to the C++/CLI exe.Both build the
.vcxprojwith full Visual Studio MSBuild (the dotnet SDK muxer can't build a.vcxproj).Gating
Both are
[OSCondition(OperatingSystems.Windows)]and skip (Assert.Inconclusive) when no Visual Studio install with the MSVC C++ toolset (Microsoft.VisualStudio.Component.VC.Tools.x86.x64) is present, so SDK-only build legs stay green. This is classic/clr(.NET Framework);/clr:netcoreis a separate, untested axis.Validation
.\build.cmd -packthendotnet run --project test/IntegrationTests/MSTest.Acceptance.IntegrationTests -- --filter "FullyQualifiedName~CppCli"→ total: 2, succeeded: 2, skipped: 0 on a VS-equipped Windows machine.Notes for reviewers
MSTest.CppClihelper package (header-only ergonomics layer + props/targets that deploy the closure) if there's appetite.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com