Skip to content

[perf-improver] perf: eliminate eager testFullName allocation in MSTestTestNodeConverter #9823

Description

@github-actions

Goal and Rationale

MSTestTestNodeConverter.CreateBaseTestNode eagerly computed:

string testFullName = $"{testMethod.FullClassName}.{testMethod.Name}";

This string was only used as a final null-coalescing fallback for DisplayName. Because TestMethod.DisplayName is always non-null (the constructor sets it to displayName ?? name), this allocation was thrown away on every call — 3× per test per run (discovery, in-progress, result).

AddTrxResultProperties had the same variable for two additional uses:

  1. TrxTestDefinitionName(testMethod.DisplayName ?? testFullName)DisplayName is never null, so the fallback never fired.
  2. TryParseFullyQualifiedType(testFullName, ...) — re-parsed FullClassName from a string that already had FullClassName concatenated into it. testMethod.FullClassName is directly available.

Changes

  • CreateBaseTestNode: inline testFullName into the null-coalescing chain; the compiler only evaluates the interpolation if both prior operands are null (they never are).
  • AddTrxResultProperties: use testMethod.DisplayName directly for TrxTestDefinitionName; use testMethod.FullClassName directly in the testMethodIdentifierProperty-absent fallback path; remove the now-dead TryParseFullyQualifiedType private method.

Performance Evidence

For a 10 000-test suite:

  • Eliminated: ~30 000 eager string allocations in CreateBaseTestNode (3 calls × 10 000 tests; each string ≈ 50–100 chars of Namespace.ClassName.MethodName)
  • Eliminated: 1 string allocation per TRX-result test node in the testMethodIdentifierProperty-absent path
  • Eliminated: string parse work in TryParseFullyQualifiedType for that fallback path (2× LastIndexOf, substring slice)

These are transient allocation savings; GC collection and throughput impact is proportional to test-suite size.

Trade-offs

None. DisplayName is documented as always initialized; FullClassName is the source of truth for the type name. Behavior is identical.

Test Status

MSTest.TestAdapter (net9.0) builds cleanly with 0 warnings and 0 errors. MSTestTestNodeConverter unit tests (24 tests) could not be run locally due to framework restore failures in this environment (unrelated pre-existing issue: NU1201 for net48/net462 targets); CI will verify.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • southcentralus0.in.applicationinsights.azure.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "southcentralus0.in.applicationinsights.azure.com"

See Network Configuration for more information.

🤖 Automated content by GitHub Copilot. Generated by the Perf Improver workflow. · 301.7 AIC · ⌖ 15.9 AIC · ⊞ 12.5K · [◷]( · )

Add this agentic workflows to your repo

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/perf-improver.md@main

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/lazy-testfullname-allocation-7012a5b3d6ba4693.

Click here to create the pull request

To fix the permissions issue, go to SettingsActionsGeneral and enable Allow GitHub Actions to create and approve pull requests. See also: gh-aw FAQ

Show patch preview (106 of 106 lines)
From d82c12d7e7da1153824753b87198613ccfd940ce Mon Sep 17 00:00:00 2001
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Fri, 10 Jul 2026 14:32:08 +0000
Subject: [PATCH] perf: eliminate eager testFullName allocation in
 MSTestTestNodeConverter
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

In CreateBaseTestNode, the string `testFullName` was always computed
eagerly even though it is only needed as a final fallback when both
`displayNameOverride` and `testMethod.DisplayName` are null. Since
`TestMethod.DisplayName` is always initialized (constructor sets it
to `displayName ?? name`), the allocation is never used. Inline the
interpolation so the compiler only evaluates it if the earlier operands
are null.

In AddTrxResultProperties, `testFullName` was used in three places:
- TrxTestDefinitionName fallback: `testMethod.DisplayName` is always
  non-null, so the fallback never fired; use `testMethod.DisplayName`
  directly.
- TrxFullyQualifiedTypeNameProperty fallback: the only use was to parse
  back the class name that is already present as `testMethod.FullClassName`;
  use it directly and remove the now-dead `TryParseFullyQualifiedType`
  private method.

For a 10 000-test suite this eliminates ~30 000 transient string
allocations per run in CreateBaseTestNode (3 calls per test ×
FullClassName + '.' + MethodName string), plus one additional string
allocation per TRX-result test node in the fallback path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
 .../MSTestTestNodeConverter.cs                | 29 ++-----------------
 1 file changed, 3 insertions(+), 26 deletions(-)

diff --git a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestTestNodeConverter.cs b/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestTestNodeConverter.cs
index 001a3d9..8e2486f 100644
--- a/src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestTestNodeConverter.cs
+++ b/sr
... (truncated)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions