You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
π€ This PR was created by Perf Improver, an automated AI assistant focused on performance improvements.
Goal and Rationale
Eliminate unnecessary heap allocations in two code paths:
ValidSourceExtensions (TestSourceHandler) β The property previously allocated a new List<string> on every access. It is called in AreValidSources() during test discovery for each source file. Since the list contents are compile-time constants, it can be safely cached as a static readonly field.
ReflectionTestMethodInfo (TestMethodRunner) β ExecuteTestWithDataSourceAsync could allocate up to two ReflectionTestMethodInfo instances per data row when testDataSource is not null and GetDisplayName returns null (requiring fallback to ComputeDefaultDisplayName). The instance is now created once and reused.
Approach
Cache ValidSourceExtensions as a private static readonly field. All preprocessor branches (NETFRAMEWORK, WINDOWS_UWP/WIN_UI, default) are compile-time-constant, so the collection is always fixed for a given build target.
Refactor the null-coalescing display name resolution in ExecuteTestWithDataSourceAsync to a short-circuit if/else block that creates ReflectionTestMethodInfo at most once.
Performance Evidence
Change 1 β ValidSourceExtensions:
Before: every call to testSourceHandler.ValidSourceExtensions allocates a new List<string> (2β3 items).
After: zero allocations per call; returns the same cached collection.
Impact: called once per source file in AreValidSources() during discovery init.
Change 2 β ReflectionTestMethodInfo in data-driven tests:
Before: up to 2 ReflectionTestMethodInfo allocations per data row when GetDisplayName returns null.
After: at most 1 allocation per data row (only when testDataSource is not null). Zero allocations when testDataSource is null or displayNameFromTestDataRow is set.
Impact: proportional to number of data rows in data-driven tests.
Trade-offs
The cached ValidSourceExtensions collection is an IEnumerable<string> backed by an array literal; it is read-only by nature. No risk of mutation.
The refactored display name logic is semantically equivalent to the original null-coalescing chain; the precedence (displayNameFromTestDataRow β GetDisplayName β ComputeDefaultDisplayName β fallback) is preserved.
For allocation profiling, use dotnet-trace or BenchmarkDotNet with MemoryDiagnoser on a data-driven test suite.
Test Status
MSTestAdapter.PlatformServices builds cleanly with 0 warnings and 0 errors on net8.0.
The MSTest.TestAdapter project has a pre-existing infrastructure build failure (missing ApplicationInsights NuGet) unrelated to these changes.
Note
π Integrity filter blocked 3 items
The following items were blocked because they don't meet the GitHub integrity level.
#2340search_issues: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
#757search_issues: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
#3759search_issues: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
To allow these resources, lower min-integrity in your GitHub frontmatter:
This was originally intended as a pull request, but GitHub Actions is not permitted to create or approve pull requests in this repository.
The changes have been pushed to branch perf-assist/reduce-allocations-discovery-execution-906516a4f3ae9159.
To fix the permissions issue, go to Settings β Actions β General and enable Allow GitHub Actions to create and approve pull requests. See also: gh-aw FAQ
Show patch preview (92 of 92 lines)
From d3ffe3262dee683d79a02a68112bb423aa43ac4b Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 24 Apr 2026 13:30:28 +0000
Subject: [PATCH] perf: reduce allocations in test discovery and data-driven
test execution
- ValidSourceExtensions in TestSourceHandler now returns a cached static
readonly collection instead of allocating a new List<string> on every
property access. This eliminates unnecessary heap allocations during
test source validation.
- ExecuteTestWithDataSourceAsync in TestMethodRunner no longer creates
two ReflectionTestMethodInfo instances when testDataSource is not null
and GetDisplayName returns null (requiring fallback to
ComputeDefaultDisplayName). The instance is now created once and reused,
halving the allocations for this code path in data-driven tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../Execution/TestMethodRunner.cs | 17 ++++++++++----
.../Services/TestSourceHandler.cs | 23 +++++++++++--------
2 files changed, 26 insertions(+), 14 deletions(-)
diff --git a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestMethodRunner.cs b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestMethodRunner.cs
index da74d5b..1438962 100644
--- a/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestMethodRunner.cs+++ b/src/Adapter/MSTestAdapter.PlatformServices/Execution/TestMethodRunner.cs@@ -321,10 +321,19 @@ private async Task<TestResult[]> ExecuteTestWithDataSourceAsync(UTF.ITestDataSou
data = tupleExpandedToArray;
}
- displayName = displayNameFromTestDataRow- ?? testDataSource?.GetDisplayName(new ReflectionTestMethodInfo(_testMethodInfo.MethodInfo, _test.DisplayName), data)- ?? (testDataSource is null ? displayName : TestDataSourceUtilities.ComputeDefaultDisplayName(new ReflectionTestMethodInfo(_testMethodInfo.MethodInfo, _test.DisplayName), d
... (truncated)
π€ This PR was created by Perf Improver, an automated AI assistant focused on performance improvements.
Goal and Rationale
Eliminate unnecessary heap allocations in two code paths:
ValidSourceExtensions(TestSourceHandler) β The property previously allocated a newList<string>on every access. It is called inAreValidSources()during test discovery for each source file. Since the list contents are compile-time constants, it can be safely cached as a static readonly field.ReflectionTestMethodInfo(TestMethodRunner) βExecuteTestWithDataSourceAsynccould allocate up to twoReflectionTestMethodInfoinstances per data row whentestDataSourceis not null andGetDisplayNamereturns null (requiring fallback toComputeDefaultDisplayName). The instance is now created once and reused.Approach
ValidSourceExtensionsas aprivate static readonlyfield. All preprocessor branches (NETFRAMEWORK,WINDOWS_UWP/WIN_UI, default) are compile-time-constant, so the collection is always fixed for a given build target.ExecuteTestWithDataSourceAsyncto a short-circuitif/elseblock that createsReflectionTestMethodInfoat most once.Performance Evidence
Change 1 β
ValidSourceExtensions:Before: every call to
testSourceHandler.ValidSourceExtensionsallocates a newList<string>(2β3 items).After: zero allocations per call; returns the same cached collection.
Impact: called once per source file in
AreValidSources()during discovery init.Change 2 β
ReflectionTestMethodInfoin data-driven tests:Before: up to 2
ReflectionTestMethodInfoallocations per data row whenGetDisplayNamereturns null.After: at most 1 allocation per data row (only when
testDataSourceis not null). Zero allocations whentestDataSourceis null ordisplayNameFromTestDataRowis set.Impact: proportional to number of data rows in data-driven tests.
Trade-offs
ValidSourceExtensionscollection is anIEnumerable<string>backed by an array literal; it is read-only by nature. No risk of mutation.displayNameFromTestDataRowβGetDisplayNameβComputeDefaultDisplayNameβ fallback) is preserved.Reproducibility
Build the affected project and run unit tests:
For allocation profiling, use dotnet-trace or BenchmarkDotNet with
MemoryDiagnoseron a data-driven test suite.Test Status
MSTestAdapter.PlatformServicesbuilds cleanly with 0 warnings and 0 errors onnet8.0.The
MSTest.TestAdapterproject has a pre-existing infrastructure build failure (missingApplicationInsightsNuGet) unrelated to these changes.Note
π Integrity filter blocked 3 items
The following items were blocked because they don't meet the GitHub integrity level.
search_issues: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".search_issues: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".search_issues: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".To allow these resources, lower
min-integrityin your GitHub frontmatter:Note
This was originally intended as a pull request, but GitHub Actions is not permitted to create or approve pull requests in this repository.
The changes have been pushed to branch
perf-assist/reduce-allocations-discovery-execution-906516a4f3ae9159.Click here to create the pull request
To fix the permissions issue, go to Settings β Actions β General and enable Allow GitHub Actions to create and approve pull requests. See also: gh-aw FAQ
Show patch preview (92 of 92 lines)