Skip to content

Avoid double TestCase construction when filtering during discovery (#9568)#9665

Merged
Evangelink merged 2 commits into
mainfrom
dev/amauryleve/avoid-double-testcase-construction
Jul 7, 2026
Merged

Avoid double TestCase construction when filtering during discovery (#9568)#9665
Evangelink merged 2 commits into
mainfrom
dev/amauryleve/avoid-double-testcase-construction

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes #9568.

Problem

When a test-case filter is active, every matched UnitTestElement was converted to a VSTest TestCase twice during discovery:

  1. inside TestElementFilter.Matches — to evaluate the filter (VSTest's MatchTestCase requires a TestCase), and
  2. inside the discovery sink (HostDiscoverySink / TestCaseDiscoverySink) — to report the matched test.

For large filtered suites this doubled TestCase allocations (including the Id-hashing work) for every matched test. Purely redundant — no behavior/correctness impact.

Fix

Added UnitTestElement.GetOrCreateHostTestCase() — a lazily-cached host TestCase distinct from the always-fresh ToTestCase(). The discovery filter and both discovery sinks now reuse the cached instance, so a matched element is converted to a TestCase exactly once.

  • ToTestCase() stays fresh on every call, so mutation-sensitive callers (e.g. UnitTestElementTests, which mutate → rebuild → assert) are unaffected.
  • The neutral ITestElementFilter / IUnitTestElementSink interfaces keep their UnitTestElement-based signatures — the in-flight platform-agnostic refactor is not perturbed.
  • The cache is reset in Clone / CloneWithUpdatedSource (the latter changes the source, hence the Id) and excluded from serialization via [NonSerialized] under NETFRAMEWORK.

Only 4 files changed (+28 / −3).

Verification

  • Full Debug build: 0 warnings, 0 errors.
  • MSTestAdapter.PlatformServices.UnitTests (net9.0): 893/893 passed (filter, sink, discoverer, and UnitTestElement Id tests).
  • MSTest.IntegrationTests TestCaseFilteringTests: 4/4 passed — the end-to-end regression guard locking FQN + Id filtering across discovery and execution.
  • MSTestAdapter.UnitTests MSTestDiscovererTests: 16/16 passed.

…9568)

Introduce UnitTestElement.GetOrCreateHostTestCase(), a lazily-cached host
TestCase distinct from the always-fresh ToTestCase(). The discovery filter
and the discovery sinks now reuse the cached instance, so a matched test is
converted to a VSTest TestCase exactly once instead of twice (once to
evaluate the filter, once to report it to the sink).

The neutral ITestElementFilter / IUnitTestElementSink interfaces are
unchanged; ToTestCase() still returns a fresh instance so mutation-sensitive
callers are unaffected. The cache is reset on Clone/CloneWithUpdatedSource
and excluded from serialization.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 7, 2026 03:33
…double-testcase-construction

# Conflicts:
#	src/Adapter/MSTestAdapter.PlatformServices/Execution/TestCaseDiscoverySink.cs
#	src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestElement.cs

Copilot AI 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.

Pull request overview

This PR eliminates a redundant TestCase allocation that occurred for every matched test during discovery. Previously, when a test-case filter was active, each UnitTestElement was converted to a VSTest TestCase twice: once inside TestElementFilter.Matches (to evaluate the filter via VSTest's MatchTestCase) and again in the discovery sink (to report the test). It fits into the ongoing platform-agnostic PlatformServices refactor (follow-up to #9567/#9568) without perturbing the neutral ITestElementFilter/IUnitTestElementSink interfaces.

The approach adds a lazily-memoized host TestCase (GetOrCreateHostTestCase()) alongside the always-fresh ToTestCase(). The filter and both discovery sinks now share the cached instance, so a matched element is converted exactly once. The cache is reset on Clone/CloneWithUpdatedSource and marked [NonSerialized] under NETFRAMEWORK, which is important because UnitTestElement[] is marshalled across the AppDomain boundary during execution.

Changes:

  • Added UnitTestElement.GetOrCreateHostTestCase() memoizing a host TestCase, with cache invalidation in both clone paths and [NonSerialized] under NETFRAMEWORK.
  • Updated TestElementFilter.Matches and both discovery sinks (HostDiscoverySink, TestCaseDiscoverySink) to reuse the cached instance.
  • ToTestCase() behavior is unchanged (still fresh per call), so mutation-sensitive callers are unaffected.
Show a summary per file
File Description
src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestElement.cs Adds the _hostTestCase cache field, GetOrCreateHostTestCase(), [NonSerialized] guard, and cache reset in both clone methods.
src/Adapter/MSTestAdapter.PlatformServices/TestMethodFilter.cs TestElementFilter.Matches now reuses the cached host TestCase instead of building a throwaway one.
src/Adapter/MSTestAdapter.PlatformServices/Services/UnitTestElementSinkExtensions.cs HostDiscoverySink.SendTestElement forwards the cached TestCase to the VSTest sink.
src/Adapter/MSTestAdapter.PlatformServices/Execution/TestCaseDiscoverySink.cs Internal execution discovery sink collects the cached TestCase.

Review details

  • Files reviewed: 4/4 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment thread src/Adapter/MSTestAdapter.PlatformServices/ObjectModel/UnitTestElement.cs Outdated
@Evangelink Evangelink added the state/needs-review Awaiting review from the team. label Jul 7, 2026
@Evangelink Evangelink enabled auto-merge (squash) July 7, 2026 03:45
@Evangelink Evangelink merged commit e732301 into main Jul 7, 2026
32 checks passed
@Evangelink Evangelink deleted the dev/amauryleve/avoid-double-testcase-construction branch July 7, 2026 04:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-review Awaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Avoid double TestCase construction when filtering during discovery/execution (platform-agnostic refactor follow-up)

3 participants