[perf-improver] perf: single-pass message iteration in VSTestBridge ObjectModelConverters#8823
Conversation
…ters
When converting a VSTest TestResult to a TestNode, testResult.Messages was
iterated twice when TRX reporting was enabled:
1. A LINQ Select pass to build TrxMessage[] (creates SelectIterator state
machine + intermediate array via collection spread)
2. A separate foreach to collect standard-error/standard-output strings
Replace with a single foreach that collects all three outputs in one pass.
Also removes a dead addVSTestProviderProperties variable whose value was
never read after the usage was previously removed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR optimizes the VSTestBridge adapter’s conversion from VSTest TestResult to MTP TestNode by consolidating testResult.Messages processing into a single pass, reducing iterator/collection allocations when TRX reporting is enabled.
Changes:
- Replaces the prior
Select(...)+[..]TRX message projection plus a separateforeachwith a singleforeachthat collects TRX messages, stdout, and stderr together. - Removes an unused
addVSTestProviderPropertieslocal that was computed but never read.
Show a summary per file
| File | Description |
|---|---|
| src/Platform/Microsoft.Testing.Extensions.VSTestBridge/ObjectModel/ObjectModelConverters.cs | Consolidates message processing into one loop and removes a dead local to reduce allocations in the VSTestBridge conversion path. |
Copilot's findings
- Files reviewed: 1/1 changed files
- Comments generated: 1
Pre-allocate the trxMessages list with the exact message count to avoid List<T> growth reallocations, and switch the loop guard from isTrxEnabled to a direct null check on trxMessages so the null-forgiveness operator is no longer needed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🔍 Build Failure Analysis
❌ Root Cause:
|
Evangelink
left a comment
There was a problem hiding this comment.
Generated by Build Failure Analysis for issue #8823 · sonnet46 2.6M
The new List<TrxMessage>(testResult.Messages.Count) constructor call triggers IDE0028 (warn-as-error in CI) which suggests a collection expression. Capacity hints are not expressible in collection-expression form, so wrap the line with the same pragma pattern other call sites in this repo already use (TestContextImplementation.cs, TelemetryCollector.cs). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ception ordering Addresses review comment on PR #8823: the original pre-PR code threw InvalidOperationException from FullyQualifiedName parsing before iterating testResult.Messages, so an UnreachableException from an unexpected msg.Category could not fire in the malformed-FQN case. The previous version of the single-pass refactor moved the foreach above the FQN validation, which changed the observable exception order for error paths. This commit hoists the TRX-only property additions (TrxExceptionProperty, TrxTestDefinitionName, TrxFullyQualifiedTypeNameProperty) above the message loop while keeping TrxMessagesProperty after the loop so it can consume the collected list. Behavior is now identical to the pre-PR code for both happy and error paths, and the single-pass perf win is preserved. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🤖 This is an automated contribution from Perf Improver.
Goal and Rationale
When converting a VSTest
TestResultto aTestNodein the VSTestBridge adapter,testResult.Messageswas iterated twice when TRX reporting was enabled:Selectpass to build aTrxMessage[]forTrxMessagesProperty— creates aSelectIterator<>state machine and an intermediate array via[.. ...]collection spreadforeachto collectstandardErrorMessages/standardOutputMessagesforStandardErrorProperty/StandardOutputPropertyThis also removes a dead
addVSTestProviderPropertiesvariable that was declared but its value was never read (the call-site was removed at some earlier point).Approach
Replace the two separate iterations with a single
foreachthat builds all three collections in one pass:isTrxEnabled, transform each message to aTrxMessageand collect it in a pre-allocatedList<TrxMessage>List<TrxMessage>to createTrxMessagesPropertyPerformance Evidence
testResult.Messagesiterations (TRX enabled)SelectIterator<>+ intermediateTrxMessage[]+ deadbool+List<string>?× 2List<TrxMessage>(pre-allocated) +List<string>?× 2bool+List<string>?× 2List<string>?× 2For a typical CI run with 1 000 tests and TRX enabled, this eliminates ≥1 000
SelectIteratorstate machines and ≥1 000 intermediateTrxMessage[]allocations.Methodology: code inspection + diff review.
ToTestNode(TestResult)is called once per test result in the VSTestBridge execution path.Trade-offs
TrxMessage trxMsg = switch { ... }), but the logic is clearer than splitting across two separate iterations.Test Status
Microsoft.Testing.Platform.UnitTests(net8.0): 1079 passed, 0 failed, 3 skipped ✅Microsoft.Testing.Extensions.UnitTests(net8.0): 392 passed, 0 failed, 7 skipped ✅Reproducibility
Add this agentic workflows to your repo
To install this agentic workflow, run