Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1) - #9622

Merged
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges
Jul 5, 2026
Merged

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1)#9622
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Phase 6e-1 of the PlatformServices platform-agnostic initiative

Part of removing the VSTest object model dependency from MSTestAdapter.PlatformServices.

The execution engine used the VSTest IFrameworkHandle (from ObjectModel.Adapter) exclusively to obtain an IAdapterMessageLogger via .ToAdapterMessageLogger(). This phase replaces the IFrameworkHandle parameter with the neutral IAdapterMessageLogger throughout TestExecutionManager (both RunTestsAsync overloads, ExecuteTestsAsync, ExecuteTestsInSourceAsync, Deploy); the boundary (MSTestExecutor) now calls frameworkHandle.ToAdapterMessageLogger() once and injects the result.

This removes the last ObjectModel.Adapter reference from the execution engine (IFrameworkHandle is now entirely absent from PlatformServices).

No behavior change

ToAdapterMessageLogger() returns a stateless HostMessageLogger (single readonly field forwarding to the same underlying VSTest logger), so collapsing the previous per-call-site wrappers into one shared instance is observationally identical — including for the RemotingMessageLogger that marshals it into the child AppDomain.

Verification

  • Full build green across all TFMs (net462, net8.0, net9.0, UWP, WinUI).
  • MSTestAdapter.PlatformServices.UnitTests: 897 (net8.0) / 935 (net462); MSTest.IntegrationTests: 47 pass / 1 skip.
  • Expert MSTest/MTP reviewer: no material findings; confirmed statelessness/instance-equivalence and the child-AppDomain marshaling are byte-for-byte.

Base / stacking

Stacked on Phase 6d-2 (#9621). Base = dev/amauryleve/vstest-decoupling-runcontext.

Remaining work (6e continues)

…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>
@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review July 5, 2026 19:23
@Evangelink
Amaury Levé (Evangelink) merged commit e3dac94 into dev/amauryleve/vstest-decoupling-runcontextJul 5, 2026
27 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/vstest-decoupling-bridges branch July 5, 2026 19:23

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 #9622 · Phase 6e-1: Remove IFrameworkHandle from execution engine

#DimensionVerdict
1Algorithmic Correctness✅ LGTM
2Threading & Concurrency✅ LGTM
3Security & IPC Contract Safety— N/A
4Public API & Binary Compatibility— N/A (all internal)
5Performance & Allocations✅ LGTM (minor improvement: 1 wrapper/run instead of 2–4)
6Cross-TFM Compatibility✅ LGTM
7Resource & IDisposable Management— N/A
8Defensive Coding at Boundaries✅ LGTM
9Localization & Resources— N/A
10Test Isolation✅ LGTM
11Assertion Quality✅ LGTM (AwesomeAssertions, per BannedSymbols.txt)
12Flakiness Patterns— N/A
13Test Completeness & Coverage✅ LGTM
14Data-Driven Test Coverage— N/A
15Code Structure & Simplification🟡 2 NIT (inline)
16Naming & Conventions✅ LGTM
17Documentation Accuracy✅ LGTM
18Analyzer & Code Fix Quality— N/A
19IPC Wire Compatibility— N/A
20Build Infrastructure & Dependencies— N/A
21Scope & PR Discipline✅ LGTM
22PowerShell Scripting Hygiene— N/A

✅ 13/13 applicable dimensions clean — 2 NIT items flagged inline.


Notes

Correctness confirmation:HostMessageLogger is a stateless value-object (single readonly field delegating directly to the underlying IMessageLogger). Collapsing multiple per-call-site ToAdapterMessageLogger() invocations into one shared instance at the adapter boundary is byte-for-byte equivalent. The RemotingMessageLogger cross-AppDomain wrapper correctly wraps any IAdapterMessageLogger, so the shared-instance pattern is safe there too.

Threading: The shared adapterMessageLogger instance is used across parallel workers in ExecuteTestsInSourceAsync. Since HostMessageLogger.SendMessage is a pure delegation and IMessageLogger implementations provided by VSTest hosts are thread-safe, no race condition is introduced.

2 NIT items (inline): Both TestExecutionManager.Parallelization.cs:68 and TestExecutionManager.cs:133 now hold trivial local-variable aliases (adapterMessageLogger = messageLogger and logger = messageLogger). These were meaningful before (each called frameworkHandle.ToAdapterMessageLogger()); now they're no-op assignments. A follow-up cleanup would rename the parameter or remove the local and update downstream uses within the method — both are purely cosmetic.

var tests = new List<UnitTestElement>();

IAdapterMessageLogger logger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger logger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure): Same trivial alias pattern as in ExecuteTestsInSourceAsync: logger is now identically equal to messageLogger. This line was meaningful before (it called frameworkHandle.ToAdapterMessageLogger()) but is now dead.

Follow-up cleanup: rename the parameter or remove the local and update the handful of downstream uses in this method.

#endif

IAdapterMessageLogger adapterMessageLogger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger adapterMessageLogger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure):adapterMessageLogger is now a trivial alias for the incoming parameter messageLogger; the two names exist only because the earlier code called frameworkHandle.ToAdapterMessageLogger() here to produce a new wrapper.

Follow-up cleanup option: rename the parameter from messageLogger to adapterMessageLogger (matching the existing local variable) and drop this assignment, keeping the rest of the method body unchanged. Alternatively, remove the local and replace all downstream uses of adapterMessageLogger with messageLogger. Either way no behavior change.

Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1) - #9622

Merged
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges
Jul 5, 2026
Merged

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1)#9622
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Phase 6e-1 of the PlatformServices platform-agnostic initiative

Part of removing the VSTest object model dependency from MSTestAdapter.PlatformServices.

The execution engine used the VSTest IFrameworkHandle (from ObjectModel.Adapter) exclusively to obtain an IAdapterMessageLogger via .ToAdapterMessageLogger(). This phase replaces the IFrameworkHandle parameter with the neutral IAdapterMessageLogger throughout TestExecutionManager (both RunTestsAsync overloads, ExecuteTestsAsync, ExecuteTestsInSourceAsync, Deploy); the boundary (MSTestExecutor) now calls frameworkHandle.ToAdapterMessageLogger() once and injects the result.

This removes the last ObjectModel.Adapter reference from the execution engine (IFrameworkHandle is now entirely absent from PlatformServices).

No behavior change

ToAdapterMessageLogger() returns a stateless HostMessageLogger (single readonly field forwarding to the same underlying VSTest logger), so collapsing the previous per-call-site wrappers into one shared instance is observationally identical — including for the RemotingMessageLogger that marshals it into the child AppDomain.

Verification

  • Full build green across all TFMs (net462, net8.0, net9.0, UWP, WinUI).
  • MSTestAdapter.PlatformServices.UnitTests: 897 (net8.0) / 935 (net462); MSTest.IntegrationTests: 47 pass / 1 skip.
  • Expert MSTest/MTP reviewer: no material findings; confirmed statelessness/instance-equivalence and the child-AppDomain marshaling are byte-for-byte.

Base / stacking

Stacked on Phase 6d-2 (#9621). Base = dev/amauryleve/vstest-decoupling-runcontext.

Remaining work (6e continues)

…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>
@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review July 5, 2026 19:23
@Evangelink
Amaury Levé (Evangelink) merged commit e3dac94 into dev/amauryleve/vstest-decoupling-runcontextJul 5, 2026
27 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/vstest-decoupling-bridges branch July 5, 2026 19:23

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 #9622 · Phase 6e-1: Remove IFrameworkHandle from execution engine

#DimensionVerdict
1Algorithmic Correctness✅ LGTM
2Threading & Concurrency✅ LGTM
3Security & IPC Contract Safety— N/A
4Public API & Binary Compatibility— N/A (all internal)
5Performance & Allocations✅ LGTM (minor improvement: 1 wrapper/run instead of 2–4)
6Cross-TFM Compatibility✅ LGTM
7Resource & IDisposable Management— N/A
8Defensive Coding at Boundaries✅ LGTM
9Localization & Resources— N/A
10Test Isolation✅ LGTM
11Assertion Quality✅ LGTM (AwesomeAssertions, per BannedSymbols.txt)
12Flakiness Patterns— N/A
13Test Completeness & Coverage✅ LGTM
14Data-Driven Test Coverage— N/A
15Code Structure & Simplification🟡 2 NIT (inline)
16Naming & Conventions✅ LGTM
17Documentation Accuracy✅ LGTM
18Analyzer & Code Fix Quality— N/A
19IPC Wire Compatibility— N/A
20Build Infrastructure & Dependencies— N/A
21Scope & PR Discipline✅ LGTM
22PowerShell Scripting Hygiene— N/A

✅ 13/13 applicable dimensions clean — 2 NIT items flagged inline.


Notes

Correctness confirmation:HostMessageLogger is a stateless value-object (single readonly field delegating directly to the underlying IMessageLogger). Collapsing multiple per-call-site ToAdapterMessageLogger() invocations into one shared instance at the adapter boundary is byte-for-byte equivalent. The RemotingMessageLogger cross-AppDomain wrapper correctly wraps any IAdapterMessageLogger, so the shared-instance pattern is safe there too.

Threading: The shared adapterMessageLogger instance is used across parallel workers in ExecuteTestsInSourceAsync. Since HostMessageLogger.SendMessage is a pure delegation and IMessageLogger implementations provided by VSTest hosts are thread-safe, no race condition is introduced.

2 NIT items (inline): Both TestExecutionManager.Parallelization.cs:68 and TestExecutionManager.cs:133 now hold trivial local-variable aliases (adapterMessageLogger = messageLogger and logger = messageLogger). These were meaningful before (each called frameworkHandle.ToAdapterMessageLogger()); now they're no-op assignments. A follow-up cleanup would rename the parameter or remove the local and update downstream uses within the method — both are purely cosmetic.

var tests = new List<UnitTestElement>();

IAdapterMessageLogger logger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger logger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure): Same trivial alias pattern as in ExecuteTestsInSourceAsync: logger is now identically equal to messageLogger. This line was meaningful before (it called frameworkHandle.ToAdapterMessageLogger()) but is now dead.

Follow-up cleanup: rename the parameter or remove the local and update the handful of downstream uses in this method.

#endif

IAdapterMessageLogger adapterMessageLogger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger adapterMessageLogger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure):adapterMessageLogger is now a trivial alias for the incoming parameter messageLogger; the two names exist only because the earlier code called frameworkHandle.ToAdapterMessageLogger() here to produce a new wrapper.

Follow-up cleanup option: rename the parameter from messageLogger to adapterMessageLogger (matching the existing local variable) and drop this assignment, keeping the rest of the method body unchanged. Alternatively, remove the local and replace all downstream uses of adapterMessageLogger with messageLogger. Either way no behavior change.

Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1) - #9622

Merged
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges
Jul 5, 2026
Merged

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1)#9622
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Phase 6e-1 of the PlatformServices platform-agnostic initiative

Part of removing the VSTest object model dependency from MSTestAdapter.PlatformServices.

The execution engine used the VSTest IFrameworkHandle (from ObjectModel.Adapter) exclusively to obtain an IAdapterMessageLogger via .ToAdapterMessageLogger(). This phase replaces the IFrameworkHandle parameter with the neutral IAdapterMessageLogger throughout TestExecutionManager (both RunTestsAsync overloads, ExecuteTestsAsync, ExecuteTestsInSourceAsync, Deploy); the boundary (MSTestExecutor) now calls frameworkHandle.ToAdapterMessageLogger() once and injects the result.

This removes the last ObjectModel.Adapter reference from the execution engine (IFrameworkHandle is now entirely absent from PlatformServices).

No behavior change

ToAdapterMessageLogger() returns a stateless HostMessageLogger (single readonly field forwarding to the same underlying VSTest logger), so collapsing the previous per-call-site wrappers into one shared instance is observationally identical — including for the RemotingMessageLogger that marshals it into the child AppDomain.

Verification

  • Full build green across all TFMs (net462, net8.0, net9.0, UWP, WinUI).
  • MSTestAdapter.PlatformServices.UnitTests: 897 (net8.0) / 935 (net462); MSTest.IntegrationTests: 47 pass / 1 skip.
  • Expert MSTest/MTP reviewer: no material findings; confirmed statelessness/instance-equivalence and the child-AppDomain marshaling are byte-for-byte.

Base / stacking

Stacked on Phase 6d-2 (#9621). Base = dev/amauryleve/vstest-decoupling-runcontext.

Remaining work (6e continues)

…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>
@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review July 5, 2026 19:23
@Evangelink
Amaury Levé (Evangelink) merged commit e3dac94 into dev/amauryleve/vstest-decoupling-runcontextJul 5, 2026
27 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/vstest-decoupling-bridges branch July 5, 2026 19:23

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 #9622 · Phase 6e-1: Remove IFrameworkHandle from execution engine

#DimensionVerdict
1Algorithmic Correctness✅ LGTM
2Threading & Concurrency✅ LGTM
3Security & IPC Contract Safety— N/A
4Public API & Binary Compatibility— N/A (all internal)
5Performance & Allocations✅ LGTM (minor improvement: 1 wrapper/run instead of 2–4)
6Cross-TFM Compatibility✅ LGTM
7Resource & IDisposable Management— N/A
8Defensive Coding at Boundaries✅ LGTM
9Localization & Resources— N/A
10Test Isolation✅ LGTM
11Assertion Quality✅ LGTM (AwesomeAssertions, per BannedSymbols.txt)
12Flakiness Patterns— N/A
13Test Completeness & Coverage✅ LGTM
14Data-Driven Test Coverage— N/A
15Code Structure & Simplification🟡 2 NIT (inline)
16Naming & Conventions✅ LGTM
17Documentation Accuracy✅ LGTM
18Analyzer & Code Fix Quality— N/A
19IPC Wire Compatibility— N/A
20Build Infrastructure & Dependencies— N/A
21Scope & PR Discipline✅ LGTM
22PowerShell Scripting Hygiene— N/A

✅ 13/13 applicable dimensions clean — 2 NIT items flagged inline.


Notes

Correctness confirmation:HostMessageLogger is a stateless value-object (single readonly field delegating directly to the underlying IMessageLogger). Collapsing multiple per-call-site ToAdapterMessageLogger() invocations into one shared instance at the adapter boundary is byte-for-byte equivalent. The RemotingMessageLogger cross-AppDomain wrapper correctly wraps any IAdapterMessageLogger, so the shared-instance pattern is safe there too.

Threading: The shared adapterMessageLogger instance is used across parallel workers in ExecuteTestsInSourceAsync. Since HostMessageLogger.SendMessage is a pure delegation and IMessageLogger implementations provided by VSTest hosts are thread-safe, no race condition is introduced.

2 NIT items (inline): Both TestExecutionManager.Parallelization.cs:68 and TestExecutionManager.cs:133 now hold trivial local-variable aliases (adapterMessageLogger = messageLogger and logger = messageLogger). These were meaningful before (each called frameworkHandle.ToAdapterMessageLogger()); now they're no-op assignments. A follow-up cleanup would rename the parameter or remove the local and update downstream uses within the method — both are purely cosmetic.

var tests = new List<UnitTestElement>();

IAdapterMessageLogger logger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger logger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure): Same trivial alias pattern as in ExecuteTestsInSourceAsync: logger is now identically equal to messageLogger. This line was meaningful before (it called frameworkHandle.ToAdapterMessageLogger()) but is now dead.

Follow-up cleanup: rename the parameter or remove the local and update the handful of downstream uses in this method.

#endif

IAdapterMessageLogger adapterMessageLogger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger adapterMessageLogger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure):adapterMessageLogger is now a trivial alias for the incoming parameter messageLogger; the two names exist only because the earlier code called frameworkHandle.ToAdapterMessageLogger() here to produce a new wrapper.

Follow-up cleanup option: rename the parameter from messageLogger to adapterMessageLogger (matching the existing local variable) and drop this assignment, keeping the rest of the method body unchanged. Alternatively, remove the local and replace all downstream uses of adapterMessageLogger with messageLogger. Either way no behavior change.

Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1) - #9622

Merged
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges
Jul 5, 2026
Merged

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1)#9622
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Phase 6e-1 of the PlatformServices platform-agnostic initiative

Part of removing the VSTest object model dependency from MSTestAdapter.PlatformServices.

The execution engine used the VSTest IFrameworkHandle (from ObjectModel.Adapter) exclusively to obtain an IAdapterMessageLogger via .ToAdapterMessageLogger(). This phase replaces the IFrameworkHandle parameter with the neutral IAdapterMessageLogger throughout TestExecutionManager (both RunTestsAsync overloads, ExecuteTestsAsync, ExecuteTestsInSourceAsync, Deploy); the boundary (MSTestExecutor) now calls frameworkHandle.ToAdapterMessageLogger() once and injects the result.

This removes the last ObjectModel.Adapter reference from the execution engine (IFrameworkHandle is now entirely absent from PlatformServices).

No behavior change

ToAdapterMessageLogger() returns a stateless HostMessageLogger (single readonly field forwarding to the same underlying VSTest logger), so collapsing the previous per-call-site wrappers into one shared instance is observationally identical — including for the RemotingMessageLogger that marshals it into the child AppDomain.

Verification

  • Full build green across all TFMs (net462, net8.0, net9.0, UWP, WinUI).
  • MSTestAdapter.PlatformServices.UnitTests: 897 (net8.0) / 935 (net462); MSTest.IntegrationTests: 47 pass / 1 skip.
  • Expert MSTest/MTP reviewer: no material findings; confirmed statelessness/instance-equivalence and the child-AppDomain marshaling are byte-for-byte.

Base / stacking

Stacked on Phase 6d-2 (#9621). Base = dev/amauryleve/vstest-decoupling-runcontext.

Remaining work (6e continues)

…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>
@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review July 5, 2026 19:23
@Evangelink
Amaury Levé (Evangelink) merged commit e3dac94 into dev/amauryleve/vstest-decoupling-runcontextJul 5, 2026
27 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/vstest-decoupling-bridges branch July 5, 2026 19:23

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 #9622 · Phase 6e-1: Remove IFrameworkHandle from execution engine

#DimensionVerdict
1Algorithmic Correctness✅ LGTM
2Threading & Concurrency✅ LGTM
3Security & IPC Contract Safety— N/A
4Public API & Binary Compatibility— N/A (all internal)
5Performance & Allocations✅ LGTM (minor improvement: 1 wrapper/run instead of 2–4)
6Cross-TFM Compatibility✅ LGTM
7Resource & IDisposable Management— N/A
8Defensive Coding at Boundaries✅ LGTM
9Localization & Resources— N/A
10Test Isolation✅ LGTM
11Assertion Quality✅ LGTM (AwesomeAssertions, per BannedSymbols.txt)
12Flakiness Patterns— N/A
13Test Completeness & Coverage✅ LGTM
14Data-Driven Test Coverage— N/A
15Code Structure & Simplification🟡 2 NIT (inline)
16Naming & Conventions✅ LGTM
17Documentation Accuracy✅ LGTM
18Analyzer & Code Fix Quality— N/A
19IPC Wire Compatibility— N/A
20Build Infrastructure & Dependencies— N/A
21Scope & PR Discipline✅ LGTM
22PowerShell Scripting Hygiene— N/A

✅ 13/13 applicable dimensions clean — 2 NIT items flagged inline.


Notes

Correctness confirmation:HostMessageLogger is a stateless value-object (single readonly field delegating directly to the underlying IMessageLogger). Collapsing multiple per-call-site ToAdapterMessageLogger() invocations into one shared instance at the adapter boundary is byte-for-byte equivalent. The RemotingMessageLogger cross-AppDomain wrapper correctly wraps any IAdapterMessageLogger, so the shared-instance pattern is safe there too.

Threading: The shared adapterMessageLogger instance is used across parallel workers in ExecuteTestsInSourceAsync. Since HostMessageLogger.SendMessage is a pure delegation and IMessageLogger implementations provided by VSTest hosts are thread-safe, no race condition is introduced.

2 NIT items (inline): Both TestExecutionManager.Parallelization.cs:68 and TestExecutionManager.cs:133 now hold trivial local-variable aliases (adapterMessageLogger = messageLogger and logger = messageLogger). These were meaningful before (each called frameworkHandle.ToAdapterMessageLogger()); now they're no-op assignments. A follow-up cleanup would rename the parameter or remove the local and update downstream uses within the method — both are purely cosmetic.

var tests = new List<UnitTestElement>();

IAdapterMessageLogger logger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger logger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure): Same trivial alias pattern as in ExecuteTestsInSourceAsync: logger is now identically equal to messageLogger. This line was meaningful before (it called frameworkHandle.ToAdapterMessageLogger()) but is now dead.

Follow-up cleanup: rename the parameter or remove the local and update the handful of downstream uses in this method.

#endif

IAdapterMessageLogger adapterMessageLogger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger adapterMessageLogger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure):adapterMessageLogger is now a trivial alias for the incoming parameter messageLogger; the two names exist only because the earlier code called frameworkHandle.ToAdapterMessageLogger() here to produce a new wrapper.

Follow-up cleanup option: rename the parameter from messageLogger to adapterMessageLogger (matching the existing local variable) and drop this assignment, keeping the rest of the method body unchanged. Alternatively, remove the local and replace all downstream uses of adapterMessageLogger with messageLogger. Either way no behavior change.

Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1) - #9622

Merged
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges
Jul 5, 2026
Merged

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1)#9622
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Phase 6e-1 of the PlatformServices platform-agnostic initiative

Part of removing the VSTest object model dependency from MSTestAdapter.PlatformServices.

The execution engine used the VSTest IFrameworkHandle (from ObjectModel.Adapter) exclusively to obtain an IAdapterMessageLogger via .ToAdapterMessageLogger(). This phase replaces the IFrameworkHandle parameter with the neutral IAdapterMessageLogger throughout TestExecutionManager (both RunTestsAsync overloads, ExecuteTestsAsync, ExecuteTestsInSourceAsync, Deploy); the boundary (MSTestExecutor) now calls frameworkHandle.ToAdapterMessageLogger() once and injects the result.

This removes the last ObjectModel.Adapter reference from the execution engine (IFrameworkHandle is now entirely absent from PlatformServices).

No behavior change

ToAdapterMessageLogger() returns a stateless HostMessageLogger (single readonly field forwarding to the same underlying VSTest logger), so collapsing the previous per-call-site wrappers into one shared instance is observationally identical — including for the RemotingMessageLogger that marshals it into the child AppDomain.

Verification

  • Full build green across all TFMs (net462, net8.0, net9.0, UWP, WinUI).
  • MSTestAdapter.PlatformServices.UnitTests: 897 (net8.0) / 935 (net462); MSTest.IntegrationTests: 47 pass / 1 skip.
  • Expert MSTest/MTP reviewer: no material findings; confirmed statelessness/instance-equivalence and the child-AppDomain marshaling are byte-for-byte.

Base / stacking

Stacked on Phase 6d-2 (#9621). Base = dev/amauryleve/vstest-decoupling-runcontext.

Remaining work (6e continues)

…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>
@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review July 5, 2026 19:23
@Evangelink
Amaury Levé (Evangelink) merged commit e3dac94 into dev/amauryleve/vstest-decoupling-runcontextJul 5, 2026
27 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/vstest-decoupling-bridges branch July 5, 2026 19:23

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 #9622 · Phase 6e-1: Remove IFrameworkHandle from execution engine

#DimensionVerdict
1Algorithmic Correctness✅ LGTM
2Threading & Concurrency✅ LGTM
3Security & IPC Contract Safety— N/A
4Public API & Binary Compatibility— N/A (all internal)
5Performance & Allocations✅ LGTM (minor improvement: 1 wrapper/run instead of 2–4)
6Cross-TFM Compatibility✅ LGTM
7Resource & IDisposable Management— N/A
8Defensive Coding at Boundaries✅ LGTM
9Localization & Resources— N/A
10Test Isolation✅ LGTM
11Assertion Quality✅ LGTM (AwesomeAssertions, per BannedSymbols.txt)
12Flakiness Patterns— N/A
13Test Completeness & Coverage✅ LGTM
14Data-Driven Test Coverage— N/A
15Code Structure & Simplification🟡 2 NIT (inline)
16Naming & Conventions✅ LGTM
17Documentation Accuracy✅ LGTM
18Analyzer & Code Fix Quality— N/A
19IPC Wire Compatibility— N/A
20Build Infrastructure & Dependencies— N/A
21Scope & PR Discipline✅ LGTM
22PowerShell Scripting Hygiene— N/A

✅ 13/13 applicable dimensions clean — 2 NIT items flagged inline.


Notes

Correctness confirmation:HostMessageLogger is a stateless value-object (single readonly field delegating directly to the underlying IMessageLogger). Collapsing multiple per-call-site ToAdapterMessageLogger() invocations into one shared instance at the adapter boundary is byte-for-byte equivalent. The RemotingMessageLogger cross-AppDomain wrapper correctly wraps any IAdapterMessageLogger, so the shared-instance pattern is safe there too.

Threading: The shared adapterMessageLogger instance is used across parallel workers in ExecuteTestsInSourceAsync. Since HostMessageLogger.SendMessage is a pure delegation and IMessageLogger implementations provided by VSTest hosts are thread-safe, no race condition is introduced.

2 NIT items (inline): Both TestExecutionManager.Parallelization.cs:68 and TestExecutionManager.cs:133 now hold trivial local-variable aliases (adapterMessageLogger = messageLogger and logger = messageLogger). These were meaningful before (each called frameworkHandle.ToAdapterMessageLogger()); now they're no-op assignments. A follow-up cleanup would rename the parameter or remove the local and update downstream uses within the method — both are purely cosmetic.

var tests = new List<UnitTestElement>();

IAdapterMessageLogger logger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger logger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure): Same trivial alias pattern as in ExecuteTestsInSourceAsync: logger is now identically equal to messageLogger. This line was meaningful before (it called frameworkHandle.ToAdapterMessageLogger()) but is now dead.

Follow-up cleanup: rename the parameter or remove the local and update the handful of downstream uses in this method.

#endif

IAdapterMessageLogger adapterMessageLogger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger adapterMessageLogger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure):adapterMessageLogger is now a trivial alias for the incoming parameter messageLogger; the two names exist only because the earlier code called frameworkHandle.ToAdapterMessageLogger() here to produce a new wrapper.

Follow-up cleanup option: rename the parameter from messageLogger to adapterMessageLogger (matching the existing local variable) and drop this assignment, keeping the rest of the method body unchanged. Alternatively, remove the local and replace all downstream uses of adapterMessageLogger with messageLogger. Either way no behavior change.

Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1) - #9622

Merged
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges
Jul 5, 2026
Merged

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1)#9622
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Phase 6e-1 of the PlatformServices platform-agnostic initiative

Part of removing the VSTest object model dependency from MSTestAdapter.PlatformServices.

The execution engine used the VSTest IFrameworkHandle (from ObjectModel.Adapter) exclusively to obtain an IAdapterMessageLogger via .ToAdapterMessageLogger(). This phase replaces the IFrameworkHandle parameter with the neutral IAdapterMessageLogger throughout TestExecutionManager (both RunTestsAsync overloads, ExecuteTestsAsync, ExecuteTestsInSourceAsync, Deploy); the boundary (MSTestExecutor) now calls frameworkHandle.ToAdapterMessageLogger() once and injects the result.

This removes the last ObjectModel.Adapter reference from the execution engine (IFrameworkHandle is now entirely absent from PlatformServices).

No behavior change

ToAdapterMessageLogger() returns a stateless HostMessageLogger (single readonly field forwarding to the same underlying VSTest logger), so collapsing the previous per-call-site wrappers into one shared instance is observationally identical — including for the RemotingMessageLogger that marshals it into the child AppDomain.

Verification

  • Full build green across all TFMs (net462, net8.0, net9.0, UWP, WinUI).
  • MSTestAdapter.PlatformServices.UnitTests: 897 (net8.0) / 935 (net462); MSTest.IntegrationTests: 47 pass / 1 skip.
  • Expert MSTest/MTP reviewer: no material findings; confirmed statelessness/instance-equivalence and the child-AppDomain marshaling are byte-for-byte.

Base / stacking

Stacked on Phase 6d-2 (#9621). Base = dev/amauryleve/vstest-decoupling-runcontext.

Remaining work (6e continues)

…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>
@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review July 5, 2026 19:23
@Evangelink
Amaury Levé (Evangelink) merged commit e3dac94 into dev/amauryleve/vstest-decoupling-runcontextJul 5, 2026
27 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/vstest-decoupling-bridges branch July 5, 2026 19:23

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 #9622 · Phase 6e-1: Remove IFrameworkHandle from execution engine

#DimensionVerdict
1Algorithmic Correctness✅ LGTM
2Threading & Concurrency✅ LGTM
3Security & IPC Contract Safety— N/A
4Public API & Binary Compatibility— N/A (all internal)
5Performance & Allocations✅ LGTM (minor improvement: 1 wrapper/run instead of 2–4)
6Cross-TFM Compatibility✅ LGTM
7Resource & IDisposable Management— N/A
8Defensive Coding at Boundaries✅ LGTM
9Localization & Resources— N/A
10Test Isolation✅ LGTM
11Assertion Quality✅ LGTM (AwesomeAssertions, per BannedSymbols.txt)
12Flakiness Patterns— N/A
13Test Completeness & Coverage✅ LGTM
14Data-Driven Test Coverage— N/A
15Code Structure & Simplification🟡 2 NIT (inline)
16Naming & Conventions✅ LGTM
17Documentation Accuracy✅ LGTM
18Analyzer & Code Fix Quality— N/A
19IPC Wire Compatibility— N/A
20Build Infrastructure & Dependencies— N/A
21Scope & PR Discipline✅ LGTM
22PowerShell Scripting Hygiene— N/A

✅ 13/13 applicable dimensions clean — 2 NIT items flagged inline.


Notes

Correctness confirmation:HostMessageLogger is a stateless value-object (single readonly field delegating directly to the underlying IMessageLogger). Collapsing multiple per-call-site ToAdapterMessageLogger() invocations into one shared instance at the adapter boundary is byte-for-byte equivalent. The RemotingMessageLogger cross-AppDomain wrapper correctly wraps any IAdapterMessageLogger, so the shared-instance pattern is safe there too.

Threading: The shared adapterMessageLogger instance is used across parallel workers in ExecuteTestsInSourceAsync. Since HostMessageLogger.SendMessage is a pure delegation and IMessageLogger implementations provided by VSTest hosts are thread-safe, no race condition is introduced.

2 NIT items (inline): Both TestExecutionManager.Parallelization.cs:68 and TestExecutionManager.cs:133 now hold trivial local-variable aliases (adapterMessageLogger = messageLogger and logger = messageLogger). These were meaningful before (each called frameworkHandle.ToAdapterMessageLogger()); now they're no-op assignments. A follow-up cleanup would rename the parameter or remove the local and update downstream uses within the method — both are purely cosmetic.

var tests = new List<UnitTestElement>();

IAdapterMessageLogger logger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger logger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure): Same trivial alias pattern as in ExecuteTestsInSourceAsync: logger is now identically equal to messageLogger. This line was meaningful before (it called frameworkHandle.ToAdapterMessageLogger()) but is now dead.

Follow-up cleanup: rename the parameter or remove the local and update the handful of downstream uses in this method.

#endif

IAdapterMessageLogger adapterMessageLogger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger adapterMessageLogger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure):adapterMessageLogger is now a trivial alias for the incoming parameter messageLogger; the two names exist only because the earlier code called frameworkHandle.ToAdapterMessageLogger() here to produce a new wrapper.

Follow-up cleanup option: rename the parameter from messageLogger to adapterMessageLogger (matching the existing local variable) and drop this assignment, keeping the rest of the method body unchanged. Alternatively, remove the local and replace all downstream uses of adapterMessageLogger with messageLogger. Either way no behavior change.

Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1) - #9622

Merged
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges
Jul 5, 2026
Merged

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1)#9622
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Phase 6e-1 of the PlatformServices platform-agnostic initiative

Part of removing the VSTest object model dependency from MSTestAdapter.PlatformServices.

The execution engine used the VSTest IFrameworkHandle (from ObjectModel.Adapter) exclusively to obtain an IAdapterMessageLogger via .ToAdapterMessageLogger(). This phase replaces the IFrameworkHandle parameter with the neutral IAdapterMessageLogger throughout TestExecutionManager (both RunTestsAsync overloads, ExecuteTestsAsync, ExecuteTestsInSourceAsync, Deploy); the boundary (MSTestExecutor) now calls frameworkHandle.ToAdapterMessageLogger() once and injects the result.

This removes the last ObjectModel.Adapter reference from the execution engine (IFrameworkHandle is now entirely absent from PlatformServices).

No behavior change

ToAdapterMessageLogger() returns a stateless HostMessageLogger (single readonly field forwarding to the same underlying VSTest logger), so collapsing the previous per-call-site wrappers into one shared instance is observationally identical — including for the RemotingMessageLogger that marshals it into the child AppDomain.

Verification

  • Full build green across all TFMs (net462, net8.0, net9.0, UWP, WinUI).
  • MSTestAdapter.PlatformServices.UnitTests: 897 (net8.0) / 935 (net462); MSTest.IntegrationTests: 47 pass / 1 skip.
  • Expert MSTest/MTP reviewer: no material findings; confirmed statelessness/instance-equivalence and the child-AppDomain marshaling are byte-for-byte.

Base / stacking

Stacked on Phase 6d-2 (#9621). Base = dev/amauryleve/vstest-decoupling-runcontext.

Remaining work (6e continues)

…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>
@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review July 5, 2026 19:23
@Evangelink
Amaury Levé (Evangelink) merged commit e3dac94 into dev/amauryleve/vstest-decoupling-runcontextJul 5, 2026
27 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/vstest-decoupling-bridges branch July 5, 2026 19:23

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 #9622 · Phase 6e-1: Remove IFrameworkHandle from execution engine

#DimensionVerdict
1Algorithmic Correctness✅ LGTM
2Threading & Concurrency✅ LGTM
3Security & IPC Contract Safety— N/A
4Public API & Binary Compatibility— N/A (all internal)
5Performance & Allocations✅ LGTM (minor improvement: 1 wrapper/run instead of 2–4)
6Cross-TFM Compatibility✅ LGTM
7Resource & IDisposable Management— N/A
8Defensive Coding at Boundaries✅ LGTM
9Localization & Resources— N/A
10Test Isolation✅ LGTM
11Assertion Quality✅ LGTM (AwesomeAssertions, per BannedSymbols.txt)
12Flakiness Patterns— N/A
13Test Completeness & Coverage✅ LGTM
14Data-Driven Test Coverage— N/A
15Code Structure & Simplification🟡 2 NIT (inline)
16Naming & Conventions✅ LGTM
17Documentation Accuracy✅ LGTM
18Analyzer & Code Fix Quality— N/A
19IPC Wire Compatibility— N/A
20Build Infrastructure & Dependencies— N/A
21Scope & PR Discipline✅ LGTM
22PowerShell Scripting Hygiene— N/A

✅ 13/13 applicable dimensions clean — 2 NIT items flagged inline.


Notes

Correctness confirmation:HostMessageLogger is a stateless value-object (single readonly field delegating directly to the underlying IMessageLogger). Collapsing multiple per-call-site ToAdapterMessageLogger() invocations into one shared instance at the adapter boundary is byte-for-byte equivalent. The RemotingMessageLogger cross-AppDomain wrapper correctly wraps any IAdapterMessageLogger, so the shared-instance pattern is safe there too.

Threading: The shared adapterMessageLogger instance is used across parallel workers in ExecuteTestsInSourceAsync. Since HostMessageLogger.SendMessage is a pure delegation and IMessageLogger implementations provided by VSTest hosts are thread-safe, no race condition is introduced.

2 NIT items (inline): Both TestExecutionManager.Parallelization.cs:68 and TestExecutionManager.cs:133 now hold trivial local-variable aliases (adapterMessageLogger = messageLogger and logger = messageLogger). These were meaningful before (each called frameworkHandle.ToAdapterMessageLogger()); now they're no-op assignments. A follow-up cleanup would rename the parameter or remove the local and update downstream uses within the method — both are purely cosmetic.

var tests = new List<UnitTestElement>();

IAdapterMessageLogger logger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger logger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure): Same trivial alias pattern as in ExecuteTestsInSourceAsync: logger is now identically equal to messageLogger. This line was meaningful before (it called frameworkHandle.ToAdapterMessageLogger()) but is now dead.

Follow-up cleanup: rename the parameter or remove the local and update the handful of downstream uses in this method.

#endif

IAdapterMessageLogger adapterMessageLogger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger adapterMessageLogger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure):adapterMessageLogger is now a trivial alias for the incoming parameter messageLogger; the two names exist only because the earlier code called frameworkHandle.ToAdapterMessageLogger() here to produce a new wrapper.

Follow-up cleanup option: rename the parameter from messageLogger to adapterMessageLogger (matching the existing local variable) and drop this assignment, keeping the rest of the method body unchanged. Alternatively, remove the local and replace all downstream uses of adapterMessageLogger with messageLogger. Either way no behavior change.

Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Evangelink
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1) - #9622

Merged
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges
Jul 5, 2026
Merged

Remove IFrameworkHandle from the PlatformServices execution engine (Phase 6e-1)#9622
Amaury Levé (Evangelink) merged 1 commit into
dev/amauryleve/vstest-decoupling-runcontextfrom
dev/amauryleve/vstest-decoupling-bridges

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Phase 6e-1 of the PlatformServices platform-agnostic initiative

Part of removing the VSTest object model dependency from MSTestAdapter.PlatformServices.

The execution engine used the VSTest IFrameworkHandle (from ObjectModel.Adapter) exclusively to obtain an IAdapterMessageLogger via .ToAdapterMessageLogger(). This phase replaces the IFrameworkHandle parameter with the neutral IAdapterMessageLogger throughout TestExecutionManager (both RunTestsAsync overloads, ExecuteTestsAsync, ExecuteTestsInSourceAsync, Deploy); the boundary (MSTestExecutor) now calls frameworkHandle.ToAdapterMessageLogger() once and injects the result.

This removes the last ObjectModel.Adapter reference from the execution engine (IFrameworkHandle is now entirely absent from PlatformServices).

No behavior change

ToAdapterMessageLogger() returns a stateless HostMessageLogger (single readonly field forwarding to the same underlying VSTest logger), so collapsing the previous per-call-site wrappers into one shared instance is observationally identical — including for the RemotingMessageLogger that marshals it into the child AppDomain.

Verification

  • Full build green across all TFMs (net462, net8.0, net9.0, UWP, WinUI).
  • MSTestAdapter.PlatformServices.UnitTests: 897 (net8.0) / 935 (net462); MSTest.IntegrationTests: 47 pass / 1 skip.
  • Expert MSTest/MTP reviewer: no material findings; confirmed statelessness/instance-equivalence and the child-AppDomain marshaling are byte-for-byte.

Base / stacking

Stacked on Phase 6d-2 (#9621). Base = dev/amauryleve/vstest-decoupling-runcontext.

Remaining work (6e continues)

…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>
@Evangelink
Amaury Levé (Evangelink) marked this pull request as ready for review July 5, 2026 19:23
@Evangelink
Amaury Levé (Evangelink) merged commit e3dac94 into dev/amauryleve/vstest-decoupling-runcontextJul 5, 2026
27 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/vstest-decoupling-bridges branch July 5, 2026 19:23

@github-actionsgithub-actionsBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 #9622 · Phase 6e-1: Remove IFrameworkHandle from execution engine

#DimensionVerdict
1Algorithmic Correctness✅ LGTM
2Threading & Concurrency✅ LGTM
3Security & IPC Contract Safety— N/A
4Public API & Binary Compatibility— N/A (all internal)
5Performance & Allocations✅ LGTM (minor improvement: 1 wrapper/run instead of 2–4)
6Cross-TFM Compatibility✅ LGTM
7Resource & IDisposable Management— N/A
8Defensive Coding at Boundaries✅ LGTM
9Localization & Resources— N/A
10Test Isolation✅ LGTM
11Assertion Quality✅ LGTM (AwesomeAssertions, per BannedSymbols.txt)
12Flakiness Patterns— N/A
13Test Completeness & Coverage✅ LGTM
14Data-Driven Test Coverage— N/A
15Code Structure & Simplification🟡 2 NIT (inline)
16Naming & Conventions✅ LGTM
17Documentation Accuracy✅ LGTM
18Analyzer & Code Fix Quality— N/A
19IPC Wire Compatibility— N/A
20Build Infrastructure & Dependencies— N/A
21Scope & PR Discipline✅ LGTM
22PowerShell Scripting Hygiene— N/A

✅ 13/13 applicable dimensions clean — 2 NIT items flagged inline.


Notes

Correctness confirmation:HostMessageLogger is a stateless value-object (single readonly field delegating directly to the underlying IMessageLogger). Collapsing multiple per-call-site ToAdapterMessageLogger() invocations into one shared instance at the adapter boundary is byte-for-byte equivalent. The RemotingMessageLogger cross-AppDomain wrapper correctly wraps any IAdapterMessageLogger, so the shared-instance pattern is safe there too.

Threading: The shared adapterMessageLogger instance is used across parallel workers in ExecuteTestsInSourceAsync. Since HostMessageLogger.SendMessage is a pure delegation and IMessageLogger implementations provided by VSTest hosts are thread-safe, no race condition is introduced.

2 NIT items (inline): Both TestExecutionManager.Parallelization.cs:68 and TestExecutionManager.cs:133 now hold trivial local-variable aliases (adapterMessageLogger = messageLogger and logger = messageLogger). These were meaningful before (each called frameworkHandle.ToAdapterMessageLogger()); now they're no-op assignments. A follow-up cleanup would rename the parameter or remove the local and update downstream uses within the method — both are purely cosmetic.

var tests = new List<UnitTestElement>();

IAdapterMessageLogger logger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger logger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure): Same trivial alias pattern as in ExecuteTestsInSourceAsync: logger is now identically equal to messageLogger. This line was meaningful before (it called frameworkHandle.ToAdapterMessageLogger()) but is now dead.

Follow-up cleanup: rename the parameter or remove the local and update the handful of downstream uses in this method.

#endif

IAdapterMessageLogger adapterMessageLogger = frameworkHandle.ToAdapterMessageLogger();
IAdapterMessageLogger adapterMessageLogger = messageLogger;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

NIT (Dim 15 — Code Structure):adapterMessageLogger is now a trivial alias for the incoming parameter messageLogger; the two names exist only because the earlier code called frameworkHandle.ToAdapterMessageLogger() here to produce a new wrapper.

Follow-up cleanup option: rename the parameter from messageLogger to adapterMessageLogger (matching the existing local variable) and drop this assignment, keeping the rest of the method body unchanged. Alternatively, remove the local and replace all downstream uses of adapterMessageLogger with messageLogger. Either way no behavior change.

Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Amaury Levé (Evangelink) added a commit that referenced this pull request Jul 5, 2026
…ervices
Squashed rebase of the vstest-decoupling PlatformServices stack onto main.
Phases 1, 2 and 5 already landed on main via #9548/#9567/#9550; this commit
carries the remaining net-new work:
- Phase 3 (#9566): abstract the VSTest discovery sink (IUnitTestElementSink).
- Phase 4 (#9572): abstract VSTest execution input.
- Phase 6a (#9576): neutralize deployment input (DeploymentContext).
- Phase 6b (#9579): neutralize test result recording (ITestResultRecorder).
- Phase 6c (#9585): neutralize test message logging.
- Phase 6c2: neutralize run-settings input in the host layer (settingsXml).
- Phase 6d-1: move test-case filter parsing to the adapter boundary
(ITestElementFilterProvider / TestElementFilterProvider).
- Phase 6d-2: remove IRunContext/IDiscoveryContext from PlatformServices.
- Phase 6e-1 (#9622): remove IFrameworkHandle from the execution engine.
- Phase 6e-2 (#9623): relocate VSTest logger/sink bridges to the adapter.
- Phase 6e-3a (#9624): neutralize the trait type on UnitTestElement.
Result: MSTestAdapter.PlatformServices no longer references the VSTest
run/discovery context or result object model; those types live only at the
MSTest.TestAdapter boundary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@Evangelink