Relocate the UnitTestElement<->TestCase conversion to the adapter (Phase 6e-3b)#9626
Merged
Merged
Conversation
Base automatically changed from
dev/amauryleve/vstest-decoupling-traits
to
dev/amauryleve/vstest-decoupling-runcontext
July 5, 2026 19:24
…orm-agnostic effort) (#9572) Co-authored-by: Amaury Leveque <amauryleve@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…-agnostic effort) (#9576) Co-authored-by: Amaury Leveque <amauryleve@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Amaury Leveque <amauryleve@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…(Phase 6c) (#9585) Co-authored-by: Amaury Leveque <amauryleve@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace the VSTest IRunSettings with a neutral settings-XML string through the isolation-host layer, removing IRunSettings from MSTestAdapter.PlatformServices. - IPlatformServiceProvider.CreateTestSourceHost, TestSourceHost (both ctors) and AssemblyEnumeratorWrapper.GetTests/GetTestsInIsolation now take string? settingsXml. - TestExecutionManager.CacheSessionParameters takes the settings-XML string directly. - Callers extract runContext?.RunSettings?.SettingsXml / discoveryContext?.RunSettings?.SettingsXml at the point they already had the (still VSTest) run/discovery context; only .SettingsXml (a string) was ever read off IRunSettings, so this is byte-for-byte. The remaining IRunContext/IDiscoveryContext usage is the test-case filter (deferred to the filter sub-phase). No behavior change: the appdomain DisableAppDomain decision and the run-parameter caching read the same settings XML as before. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Move TestMethodFilter (and its nested TestElementFilter) out of MSTestAdapter.PlatformServices up into MSTest.TestAdapter, and inject the neutral ITestElementFilter into the engine and discoverer via a new ITestElementFilterProvider abstraction. - New neutral ITestElementFilterProvider (PlatformServices.Interface): the boundary builds it (TestElementFilterProvider, closing over the VSTest IRunContext/IDiscoveryContext) and passes it into TestExecutionManager.RunTestsAsync/ExecuteTestsAsync and UnitTestDiscoverer.DiscoverTests. - The engine/discoverer invoke the provider at the EXACT points they previously built the filter (per source), so filter parse-error reporting keeps the same timing and per-source semantics; TestElementFilter.Matches still does element.ToTestCase() (byte-for-byte; #9568 deferred). - This removes ITestCaseFilterExpression / GetTestCaseFilter / MatchTestCase / the VSTest TestProperty filter set from PlatformServices code. IRunContext/IDiscoveryContext remain only for deployment + settings extraction (removed in a follow-up). No behavior change: filtered set/order and the discovery/execution filterHasError bail-out are identical; TestCaseFilteringTests (out-of-proc filter regression net) stays green. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace the VSTest IRunContext/IDiscoveryContext with neutral primitives extracted at the adapter boundary, so MSTestAdapter.PlatformServices no longer references either type. - Execution: MSTestExecutor builds the neutral DeploymentContext (test-run directory + run settings XML) from the host run context and injects it into TestExecutionManager.RunTestsAsync/ ExecuteTestsAsync/ExecuteTestsInSourceAsync/Deploy (DeploymentContext un-guarded so it is the single execution-inputs carrier on all TFMs). - Discovery: MSTestDiscoverer passes the run settings XML string into UnitTestDiscoverer. DiscoverTests/DiscoverTestsInSource; MSTestDiscovererHelpers.InitializeDiscovery and MSTestSettings.PopulateSettings take string? settingsXml. - Only .SettingsXml + .TestRunDirectory were ever read off the contexts, so this is byte-for-byte. IRunContext/IDiscoveryContext are now absent from PlatformServices code (doc comments only); the remaining ObjectModel.Adapter surface is the IFrameworkHandle-backed deploy/recorder/logger handles. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…hase 6e-1) The execution engine used the VSTest IFrameworkHandle exclusively to obtain an IAdapterMessageLogger via ToAdapterMessageLogger(). Replace the IFrameworkHandle parameter with the neutral IAdapterMessageLogger throughout TestExecutionManager (RunTestsAsync both overloads, ExecuteTestsAsync, ExecuteTestsInSourceAsync, Deploy); the adapter boundary (MSTestExecutor) now calls frameworkHandle.ToAdapterMessageLogger() once and injects the result. This removes the last VSTest ObjectModel.Adapter reference from the execution engine. No behavior change: the logger wrapper is stateless, so injecting one instance is identical to building one per call site. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Move the three remaining VSTest-object-model bridge helpers out of MSTestAdapter.PlatformServices and into MSTest.TestAdapter: AdapterMessageLoggerExtensions, MessageLevel (ToTestMessageLevel), and UnitTestElementSinkExtensions. These are the last code references to Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging / ITestCaseDiscoverySink in PlatformServices; only doc comments now mention the VSTest types. The logical namespace is unchanged so callers at the adapter boundary and the integration harness are unaffected. PlatformServices.UnitTests calls the ToAdapterMessageLogger bridge, which now lives in MSTest.TestAdapter; touching that module runs its [ModuleInitializer] (MSTestExecutor.SetPlatformLogger), which assigns PlatformServiceProvider.Instance.AdapterTraceLogger. Make the test double's setter tolerate the assignment (as the real PlatformServiceProvider does) instead of throwing. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Replace the VSTest object-model Trait type carried on UnitTestElement.Traits
with a neutral, platform-agnostic TestTrait { Name, Value } struct. The
engine-side producers and consumers (ReflectHelper/ReflectionHelper
GetTestPropertiesAsTraits, TypeEnumerator, TestExecutionManager TestContext
building, TestRunInfo, the test-filter context) now operate on TestTrait, so
five files stop referencing Microsoft.VisualStudio.TestPlatform.ObjectModel.
The VSTest Trait only survives at the adapter conversion boundary
(TestCaseExtensions and UnitTestElement.ToTestCase), which convert between
TestTrait and the host trait type.
TestTrait is [Serializable] on .NET Framework because UnitTestElement is
serialized across app domains during isolated discovery/execution; order and
Name/Value are preserved, so trait -> TestContext reporting and the produced
host test case are byte-for-byte identical.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
…ase 6e-3b) Move the deep VSTest-object-model conversion out of MSTestAdapter.PlatformServices and into MSTest.TestAdapter, as a pure relocation (no behavior change): - UnitTestElement.ToTestCase()/GetOrCreateHostTestCase() and the test-case Id hashing (GenerateSerializedDataStrategyTestId / VersionedGuidFromHash) become UnitTestElementExtensions in the adapter. The Id hashing moves byte-identical (VersionedGuidFromHash verbatim), preserving cross-version discovery->execution test-id correlation. - The EngineConstants '#region Test Property registration' (every TestProperty id/label/valueType/attribute, plus the TCM/TFS label constants) moves verbatim into a new adapter AdapterTestProperties class. EngineConstants keeps only its neutral members (extensions, fixture traits, executor uri) and no longer references the VSTest object model. - TestCaseExtensions and TcmTestPropertiesProvider (already adapter-namespaced) move physically into MSTest.TestAdapter. UnitTestElement, EngineConstants, TestCaseExtensions and TcmTestPropertiesProvider all stop referencing Microsoft.VisualStudio.TestPlatform.ObjectModel, dropping the PlatformServices coupling from 13 to 10 files. The conversion is still invoked only at the adapter boundary (executor/discoverer/recorder/filter). The single ToTestCase in the test-case filter and CloneWithUpdatedSource are left as-is to keep this change byte-for-byte (#9568 and #9573 remain open). 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>
bb13e25 to
8832952
Compare
0101
approved these changes
Jul 5, 2026
This was referenced Jul 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 6e-3b — Relocate the
UnitTestElement⇄TestCaseconversion to the adapterPart of the initiative to make
MSTestAdapter.PlatformServicesplatform-agnostic by removing its dependency on the VSTest object model (Microsoft.TestPlatform.ObjectModel). VSTest coupling moves up intoMSTest.TestAdapter; the platform-services engine becomes neutral. Pure relocation — no behavior change.What moves (all invoked only at the adapter boundary today)
UnitTestElement.ToTestCase()/GetOrCreateHostTestCase()+ the test-case Id hashing (GenerateSerializedDataStrategyTestId/VersionedGuidFromHash) → newUnitTestElementExtensionsinMSTest.TestAdapter/Extensions.ToTestCase/GetOrCreateHostTestCasewere already called only from the adapter (executor, discoverer, recorder, filter, sink).EngineConstants#region Test Property registration— everyTestPropertyid/label/valueType/attribute, plus the TCM/TFS label constants — → newAdapterTestPropertiesclass in the adapter.EngineConstantskeeps only its neutral members (extensions, fixture traits, executor URI) and no longer references the VSTest object model.TestCaseExtensionsandTcmTestPropertiesProvider(already adapter-namespaced) → physically moved intoMSTest.TestAdapter.Coupling impact
UnitTestElement,EngineConstants,TestCaseExtensions, andTcmTestPropertiesProviderall stop referencingMicrosoft.VisualStudio.TestPlatform.ObjectModel, dropping the PlatformServices coupling from 13 → 10 files. The remaining 10 are the settings/utility residuals (6e-4).Fidelity — the ID invariant (byte-identical proof)
Per the review guardrail, the correctness-critical bodies moved byte-for-byte:
AdapterTestPropertiesTestProperty region:Compare-Objectof the oldEngineConstantsregion vs the new class body reports IDENTICAL (everyTestProperty.Registerid/label/valueType/attribute unchanged).VersionedGuidFromHash:Compare-Objectof old vs new body reports IDENTICAL.ToTestCase/GenerateSerializedDataStrategyTestIddiffer only by the mechanicalthis.→element./ localtestMethodandEngineConstants.X→AdapterTestProperties.Xprefix rewrites; the algorithm, order, and hash inputs are unchanged.Empirically:
UnitTestElementTests' exact test-Id GUID assertions pass, and the cross-process discovery→execution integration suite is green — proving the serialized-test-case caches and id correlation are preserved.Deliberately deferred to stay byte-for-byte (both remain open): the single
ToTestCasein the test-case filter (#9568) andCloneWithUpdatedSource's mutate-this behavior (#9573). Naming:AdapterTestPropertiesavoids clashing with the VSTestTestCasePropertiestype.Verification
build.cmd -c Debuggreen across all TFMs (net462/net8.0/net9.0/UWP/WinUI), IDE0005 clean.MSTestAdapter.PlatformServices.UnitTests: 897 (net8.0) / 935 (net462), 0 failed.MSTestAdapter.UnitTests: 21, 0 failed.MSTest.IntegrationTests(cross-proc, net462): 48 total, 0 failed, 1 skipped (OutputIsNotMixedWhenTestsRunInParallel, pre-existing known flaky).Stacking
Stacked chain onto
dev/amauryleve/vstest-decoupling-base:6c2 #9590→6d-1 #9591→6d-2 #9621→6e-1 #9622→6e-2 #9623→6e-3a #9624→ 6e-3b (this).Base for this PR is the 6e-3a branch (
dev/amauryleve/vstest-decoupling-traits). Review/merge after the predecessors reach the base. Do not rebase/reset the base.