Skip to content

Vendor a neutral SuspendCodeCoverage in PlatformServices (Phase 6e-4c3)#9632

Draft
Evangelink wants to merge 1 commit into
dev/amauryleve/vstest-decoupling-sourcehandlerfrom
dev/amauryleve/vstest-decoupling-suspendcoverage
Draft

Vendor a neutral SuspendCodeCoverage in PlatformServices (Phase 6e-4c3)#9632
Evangelink wants to merge 1 commit into
dev/amauryleve/vstest-decoupling-sourcehandlerfrom
dev/amauryleve/vstest-decoupling-suspendcoverage

Conversation

@Evangelink

@Evangelink Evangelink commented Jul 5, 2026

Copy link
Copy Markdown
Member

Phase 6e-4c3 — vendor a neutral SuspendCodeCoverage

Part of the initiative to make Microsoft.VisualStudio.TestPlatform.MSTestAdapter.PlatformServices platform-agnostic by removing its dependency on Microsoft.TestPlatform.ObjectModel. Strict byte-for-byte, no behavior change.

What this changes

TestDeployment (netfx) wraps the deployment file-copy in using (new SuspendCodeCoverage()) to pause dynamic code-coverage instrumentation of modules loaded while files are copied. That type came from Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities.

This vendors an internal neutral copy at Utilities/SuspendCodeCoverage.cs (namespace ...PlatformServices.Utilities), reproducing the VSTest behavior exactly:

  • On construction: capture the current value of the process environment variable __VANGUARD_SUSPEND_INSTRUMENT__, then set it to TRUE.
  • On dispose: restore the captured previous value (idempotent).

The environment-variable name and value are the collector IPC contract the dynamic code-coverage (Vanguard) engine reads, so they are preserved byte-identical. The wrapper is internal sealed with a straightforward idempotent Dispose — the original's Dispose(bool) / GC.SuppressFinalize plumbing has no finalizer to suppress and is behavior-equivalent to the direct restore.

TestDeployment now resolves SuspendCodeCoverage through its already-present using ...PlatformServices.Utilities;; the VSTest ObjectModel.Utilities using (and its deferral comment) is removed.

Fidelity proof and test-net limitation

The mechanism is a process environment variable (__VANGUARD_SUSPEND_INSTRUMENT__), not a named event / mutex / EventWaitHandle. There is no signal/listener model — the dynamic code-coverage (Vanguard) collector reads this variable from the process environment when deciding whether to instrument a module being loaded. The fidelity of the vendored copy therefore rests entirely on replicating that wire contract byte-identically against the OSS source (vstest v18.4.0 SuspendCodeCoverage.cs).

Source-diff (fidelity proof of record) — OSS original vs vendored copy:

Contract element OSS (Microsoft.TestPlatform.ObjectModel) Vendored copy
env var name "__VANGUARD_SUSPEND_INSTRUMENT__" "__VANGUARD_SUSPEND_INSTRUMENT__"
set value "TRUE" "TRUE"
target (all 3 accesses) EnvironmentVariableTarget.Process EnvironmentVariableTarget.Process
ctor GetEnvironmentVariable(name, Process) → capture; SetEnvironmentVariable(name, "TRUE", Process) identical
dispose SetEnvironmentVariable(name, prev, Process), guarded by _isDisposed identical

The only intentional difference is the collapse of the OSS Dispose() / protected virtual Dispose(bool) / GC.SuppressFinalize into a single idempotent Dispose() — behavior-equivalent because the OSS type declares no finalizer (so GC.SuppressFinalize is a no-op and disposing is always true on the public path). The name/value/target/sequence — the entire wire contract the collector keys off — are verbatim.

Note on the test net: PlatformServices.Desktop.IntegrationTests runs with no coverage collector attached, so its green result proves the vendored code does not crash / the deploy path still works — it does not exercise a real collector reading the variable. That is acceptable here precisely because there is no signaling to get wrong: correctness is fully determined by the (source-identical) variable name/value/target above. There is no clean seam to assert the variable mid-deploy without contorting the copy loop, so no brittle probe was added.

Result: PlatformServices is ObjectModel-type-free

After this change, PlatformServices has zero using/type references to the Microsoft.TestPlatform.ObjectModel package — only string-literal assembly names (used for by-name runtime assembly lookup in the AppDomain/source-host wiring) remain. This clears the way to drop the package reference in the capstone (Phase 7).

Verification

  • Builds 0-warning (net462 and all real TFMs; netfx-guarded change).
  • MSTestAdapter.PlatformServices.UnitTests: 935/935 (net462), 897/897 (net8.0).
  • PlatformServices.Desktop.IntegrationTests: 15/15 (net462) — exercises the deployment path that runs inside the SuspendCodeCoverage scope.
  • Expert-reviewer pass.

Stacking

Stacks on #9631 (Phase 6e-4c2); base branch dev/amauryleve/vstest-decoupling-sourcehandler. Review/merge after the earlier PRs in the chain reach the base. Do not squash-rebase the base.

TestDeployment (netfx) wrapped the deployment file copy in
`using (new SuspendCodeCoverage())` to pause dynamic code-coverage instrumentation
of modules loaded while files are copied. That type came from
Microsoft.VisualStudio.TestPlatform.ObjectModel.Utilities.

Vendor an internal neutral copy at
Utilities/SuspendCodeCoverage.cs (namespace ...PlatformServices.Utilities) that
reproduces the VSTest behavior byte-for-byte:

- On construction: read the current value of the process environment variable
  "__VANGUARD_SUSPEND_INSTRUMENT__" and set it to "TRUE".
- On dispose: restore the previously captured value (idempotent).

The environment-variable name and value are the collector IPC contract the
dynamic code-coverage (Vanguard) engine reads, so they are preserved exactly. The
child-object is internal/sealed with a straightforward idempotent Dispose (the
original's Dispose(bool)/GC.SuppressFinalize plumbing has no finalizer to suppress
and is behavior-equivalent to the direct restore).

TestDeployment now resolves SuspendCodeCoverage via the already-imported
PlatformServices.Utilities namespace; the VSTest ObjectModel.Utilities using is
removed.

With this change PlatformServices has zero `using`/type references to the
Microsoft.TestPlatform.ObjectModel package (only string-literal assembly names
used for by-name runtime lookup remain), clearing the way to drop the package
reference in the capstone.

Verified: PlatformServices builds 0-warning (net462 and all real TFMs;
netfx-guarded change); PlatformServices.UnitTests 935 (net462) / 897 (net8.0);
PlatformServices.Desktop.IntegrationTests 15/15 (exercises the deployment path
that runs inside the SuspendCodeCoverage scope).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to 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