Uh oh!
There was an error while loading. Please reload this page.
Native MTP integration for MSTest — RFC 018 + Phases 1–5 (opt-in native path) - #9706
Conversation
…bridge) Proposes a staged roadmap to plug MSTest directly into Microsoft.Testing.Platform as a native ITestFramework, replacing the Microsoft.Testing.Extensions.VSTestBridge indirection on the MTP code path. Documents the existing neutral seams (IUnitTestElementSink, ITestResultRecorder, IAdapterMessageLogger, ITestElementFilterProvider, DeploymentContext), a 6-phase plan, capability parity checklist, testing strategy, and risks. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds RFC 018, a design/roadmap document proposing that MSTest plug directly into Microsoft.Testing.Platform (MTP) as a first-class ITestFramework, retiring the Microsoft.Testing.Extensions.VSTestBridge indirection on the MTP code path. It is a documentation-only artifact (status "Under discussion") with no production code changes, intended to let maintainers steer the approach before implementation. The RFC documents the current double-conversion architecture, the existing neutral seams the engine already uses, a 6-phase independently-shippable roadmap, a capability/feature-parity checklist, and the testing strategy, risks, and open questions.
Changes:
- Documents current vs. target MTP architecture, highlighting the wasteful
neutral model → VSTest TestCase/TestResult → TestNodedouble conversion and the resulting information loss (emptyAssemblyFullName/ReturnTypeFullName). - Lays out a 6-phase roadmap that keeps the bridge as default until the native path reaches parity, with a feature-parity checklist and dual-run testing strategy.
- Follows the established
docs/RFCs/convention; the bridge package is not removed (NUnit/Expecto/third-party adapters still use it) — only MSTest stops depending on it.
Show a summary per file
| File | Description |
|---|---|
| docs/RFCs/018-Native-MTP-Integration-For-MSTest.md | New RFC describing the native MTP integration design, phasing, parity checklist, testing strategy, risks, and open questions. All referenced types, seams, file paths, and the quoted in-code comment were verified as accurate against the codebase. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 0
- Review effort level: Medium
There was a problem hiding this comment.
Note
🤖 Automated review by GitHub Copilot. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.
| # | Dimension | Verdict |
|---|---|---|
| 17 | Documentation Accuracy | 🟢 2 NIT |
| 21 | Scope & PR Discipline | 🟢 1 NIT |
✅ 20/22 dimensions N/A (no code changes). 2 dimensions reviewed — only minor nits found.
Summary: This is a well-structured, thoroughly researched RFC. The claims about existing neutral seams (IUnitTestElementSink, ITestResultRecorder, IAdapterMessageLogger, ITestElementFilterProvider) are verified against the codebase, the MSTestBridgedTestFramework information-loss quote is accurate (line 100–106 in that file), and the 6-phase roadmap with the capability parity checklist covers all bridge features I can identify. The design principle of "boundary replacement, not engine rewrite" is well-supported by the architecture diagrams.
- Path convention: use forward slashes for cross-platform consistency (line 84)
- Consider linking a tracking issue for the 6-phase roadmap
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Adds the first, unwired building block for plugging MSTest directly into Microsoft.Testing.Platform instead of routing through the VSTest bridge: - MSTestTestNodeConverter: builds MTP TestNodes directly from MSTest's neutral models (UnitTestElement + framework TestResult), faithfully porting the mapping the bridge does today via ObjectModelConverters/TestResultExtensions/UnitTestElementExtensions (outcome, timing, std out/err, TRX categories/messages/exception/type-name, traits, file location, TestMethodIdentifierProperty). - MtpUnitTestElementSink / MtpTestResultRecorder: MTP-native IUnitTestElementSink / ITestResultRecorder that publish TestNodeUpdateMessage on the message bus. - MSTestTestNodeException: native message+stacktrace carrier (replaces the bridge's VSTestException on the MSTest path). - UnitTestElementExtensions.GetTestId: exposes the stable test id neutrally, without materializing a VSTest TestCase. The bridge remains the default; these seams are covered by 24 unit tests and are wired in a later phase. No behavior change. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
When the MSTEST_EXPERIMENTAL_NATIVE_MTP opt-in is set, MSTest publishes discovery and result TestNodes directly via the Phase 1 native converter+seams, bypassing the bridge's ObjectModelConverters / FrameworkHandlerAdapter / TestCaseDiscoverySinkAdapter. The bridge remains the default; only context/filter/config/command-line parsing still flows through it. Changes: - VSTestBridge base: expose the session UID as an internal auto-property (bridge already has InternalsVisibleTo MSTest.TestAdapter; no public API change). - MSTestDiscoverer: add an IUnitTestElementSink overload and share a discovery core so the native sink is used directly instead of discoverySink.ToUnitTestElementSink(). - MSTestExecutor: add a Func<MSTestSettings,ITestResultRecorder> overload and share a run core so a native recorder is used instead of frameworkHandle.ToTestResultRecorder(); the framework handle is still used for message logging and apartment-state handling. - MSTestBridgedTestFramework: flag-gated native wiring. Validated: full build 0/0; MSTestAdapter.UnitTests 45/45; native-path acceptance OutputTests(6)+TrxReportTests(3)+Inconclusive/Ignore/DataSource(38) all green; default bridge-path OutputTests(6) unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
🧪 Test quality grade — PR #970624 new test methods in
This advisory comment was generated automatically. Grades are heuristic
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Make the neutral discovery/result seams asynchronous end-to-end (per review): ITestResultRecorder (RecordStartAsync/RecordEmptyResultAsync/RecordResultAsync) and IUnitTestElementSink (SendTestElementAsync). Thread async through UnitTestDiscoverer (DiscoverTestsAsync/DiscoverTestsInSourceAsync/SendTestCasesAsync) and TestExecutionManager.SendTestResultsAsync. The MTP implementations now await the message-bus publish instead of blocking with GetAwaiter().GetResult(); the VSTest implementations return completed tasks (sync fast-path preserves STA behavior). - Add the UTF-8 BOM required by .editorconfig to the five new .cs files. - Use forward slashes for the seam path in RFC 018. Validated: full build 0/0; unit tests MSTestAdapter.UnitTests 45/45 and MSTestAdapter.PlatformServices.UnitTests 898/898; acceptance default-path threading/output/trx 41 green; native-path threading/output/trx/outcome/data-driven 75 green. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
Apply two project-convention improvements to files added in recent PRs: - MSTestTestNodeConverter.cs (added in #9706): replace Substring calls with range operators per csharp_style_prefer_range_operator = true - TestMethodRunner.DataSource.cs (added in #9729): replace == null with is null per the project's 'always use is null / is not null' rule No behavioral change; builds verified to succeed with 0 errors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- AzureFoundry: add DefaultAzureCredential/managed identity auth details and required environment variables (PR #9707) - MSTestTestFramework: new entry documenting the native MTP ITestFramework for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755) - VSTestBridge: note MSTest no longer depends on it on the MTP path as of MSTest 4.3 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- AzureFoundry: add DefaultAzureCredential/managed identity auth details and required environment variables (PR #9707) - MSTestTestFramework: new entry documenting the native MTP ITestFramework for MSTest introduced by RFC 018 (PRs #9706, #9743, #9748, #9755) - VSTestBridge: note MSTest no longer depends on it on the MTP path as of MSTest 4.3 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
What
Works toward removing the
Microsoft.Testing.Extensions.VSTestBridgedependency from MSTest's Microsoft.Testing.Platform (MTP) code path, so MSTest plugs into MTP natively. Delivered as a reviewable, phased series — all behind the opt-inMSTEST_EXPERIMENTAL_NATIVE_MTPswitch, so the shipping default (the bridge) is unchanged.docs/RFCs/018-...md).TestNodeproduction seam (MSTestTestNodeConverter,MtpUnitTestElementSink,MtpTestResultRecorder).MSTestTestFrameworkthat handles the MTP request directly: native filtering, runsettings/config, message logging, and session lifecycle. No VSTest bridge object-model adapters on the native request path.Why
On the MTP path, MSTest round-trips through the VSTest object model (context/filter/runsettings/handle/sink adapters) even though its engine already runs on neutral models. Going native removes that plumbing.
Phases 3–5 in the latest commit
A native
MSTestTestFramework : ITestFramework, IDataProducerreplaces the bridge framework on the flag path and reuses MSTest's existingMSTestDiscoverer/MSTestExecutorengine. New (all#if !WINDOWS_UWP):MSTestFilterContext(MSTestRunContext/MSTestDiscoveryContext) — nativeIRunContext/IDiscoveryContextthat builds the VSTestITestCaseFilterExpressionfrom the MTPITestExecutionFilter+--filter+ runsettings<TestCaseFilter>(reusingMicrosoft.TestPlatform.Filter.Source, now referenced directly). MSTest's existingTestMethodFiltermatching is unchanged.MSTestRunSettings— nativeIRunSettings: reads runsettings, patches MTP defaults (DesignMode/ResultsDirectory/--test-parameter), and warns on unsupported entries (reusing the bridge's already-localizedExtensionResources).MSTestFrameworkHandle— anIFrameworkHandlethat only forwards messages toIOutputDevice(results flow through the native recorder).AddMSTestbranches the framework factory on the flag; the bridge's shared command-line/config/env-var registrations are kept for now (retired with the dependency in the final step).Validation
.\build.cmd— 0 warnings, 0 errors across all TFMs.MSTestAdapter.UnitTests) + 898 (MSTestAdapter.PlatformServices.UnitTests), all green.FilterTests,RunsettingsTests(incl. localization),OutputTests,TrxReportTests,ThreadingTests(STA),ServerModeTests,DeploymentItem,DataSourceTests,Inconclusive/Ignore,TestRunParameters,TimeoutTests.FilterTests+RunsettingsTests(45) — unchanged.Remaining — Phase 6 (separate, gated)
Flip the default to native and drop the
VSTestBridgedependency from MSTest. This is gated on:--filter/--settings/--test-parameteroption providers and the runsettings config/env-var providers), so the package reference can be removed.The bridge package itself is not removed — NUnit/Expecto/3rd-party adapters still use it. Only MSTest stops depending on it.