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
Everything is OK—the changes have been pushed to branch efficiency/azuredevops-single-pass-min-max-1d374458b554a417. Please review the changes, including any protected files, before creating the pull request.
AzureDevOpsResultIdStore.GetEarliestStartedDate/GetLatestCompletedDate (public AzureDevOpsTestCaseResult overload in AzureDevOpsResultIdStore.cs, plus the internal AzureDevOpsTestSubResult overload in AzureDevOpsResultIdStore.AttemptHistory.cs) used attempts.Where(a => a.X is not null).Min/Max(a => a.X). Each pair of calls is always invoked together from the AzureDevOps live-publishing result-flush hot path, so every invocation double-enumerated the attempts list and allocated a Where iterator per call, for no benefit over a single manual pass.
Focus area
Code-Level Efficiency — replacing a LINQ-based double-enumeration with a single-pass loop.
Approach
Replaced both Where().Min()/Where().Max() chains (4 methods total across the two files) with manual single-pass foreach loops tracking a running min/max, mirroring the existing hand-rolled SumDurations/AddDurations pattern already present in the same files. No public API signature changes.
Energy efficiency evidence
Proxy metrics: execution time (wall clock) and memory allocation (GC.GetAllocatedBytesForCurrentThread()), both mapping directly to reduced CPU cycles and reduced GC pressure — i.e. lower energy draw per call, consistent with the Green Software Foundation's Hardware Efficiency principle (doing the same work with less computation).
Standalone console benchmark (net8.0 Release, Stopwatch + GC.GetAllocatedBytesForCurrentThread(), 2,000,000 call-pairs of GetEarliestStartedDate+GetLatestCompletedDate over a representative 5-element attempts list):
Old (LINQ)
New (single-pass loop)
Delta
Time
581 ms
308 ms
~1.9x faster
Allocated
288,000,040 B
160,000,040 B
~44% less allocation
This is a hot path invoked once per result flush during AzureDevOps live test-result publishing, so the reduction compounds across large test runs.
Trade-offs
Slightly more verbose than the LINQ one-liners, but the same shape as the existing SumDurations helper in the same file, so it's consistent with the surrounding code style rather than introducing a new pattern. No readability concerns beyond that.
Reproducibility
Benchmark: standalone console app (net8.0, -c Release) exercising both implementations with the same synthetic attempts list, measuring Stopwatch.ElapsedMilliseconds and GC.GetAllocatedBytesForCurrentThread() deltas across 2,000,000 call-pairs (warmup of 1,000 iterations before each measured loop, GC.Collect() before each measurement).
Microsoft.Testing.Extensions.UnitTests (net8.0, full suite): 1839/1839 passed (37 pre-existing skips).
Microsoft.Testing.Extensions.UnitTests filtered to FullyQualifiedName~AzureDevOps: 387/387 passed.
dotnet format whitespace TestFx.slnx --verify-no-changes --include <both changed files>: clean, no formatting issues.
Note
GitHub Actions is not permitted to create or approve pull requests in this repository.
The changes have been pushed to branch efficiency/azuredevops-single-pass-min-max-1d374458b554a417 and are ready to review.
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 (32 of 105 lines)
From 9e3ace0ad133c61d5b334fd981c366325772c36b Mon Sep 17 00:00:00 2001
X-GH-AW-Base-Commit: f0fe5a89980d2b29daeb3c949c08d7f61b9cf939
From: "github-actions[bot]" <github-actions[bot]@users.noreply.github.com>
Date: Sun, 20 Sep 2026 21:51:26 +0000
Subject: [PATCH] Use single-pass loops for AzureDevOps result date aggregation
Replace Where().Min()/Where().Max() LINQ chains with manual single-pass
foreach loops tracking running min/max in AzureDevOpsResultIdStore's
GetEarliestStartedDate/GetLatestCompletedDate (both the public
AzureDevOpsTestCaseResult overload and the internal
AzureDevOpsTestSubResult overload). Mirrors the existing hand-rolled
SumDurations/AddDurations pattern already used in the same files.
Both method pairs are invoked together per-attempt from the AzureDevOps
live-publishing result-flush hot path, so each call previously
double-enumerated the attempts list and allocated a Where iterator.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
...AzureDevOpsResultIdStore.AttemptHistory.cs | 26 +++++++++++++++++--
.../AzureDevOpsResultIdStore.cs | 26 +++++++++++++++++--
2 files changed, 48 insertions(+), 4 deletions(-)
diff --git a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsResultIdStore.AttemptHistory.cs b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsResultIdStore.AttemptHistory.cs
index 3ae5eda..a03f7e7 100644
--- a/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsResultIdStore.AttemptHistory.cs+++ b/src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsResultIdStore.AttemptHistory.cs@@ -59,10 +59,32 @@ or AzureDevOpsLivePublishingConstants.NotExecutedTestOutcome
: right.Value > long.MaxValue - left.Value ? long.MaxValue : left.Value + right.Value;
private static DateTimeOffset? GetEarliestStartedDate(IReadOnlyList<AzureDevOpsTestSubResult> attempts)
- => attempts.Where(attempt => attemp
... (truncated)
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:
Tip
Your pull request is ready to create! 🎉 ✅
Everything is OK—the changes have been pushed to branch
efficiency/azuredevops-single-pass-min-max-1d374458b554a417. Please review the changes, including any protected files, before creating the pull request.Create the pull request
The original pull request description is below.
Goal and rationale
AzureDevOpsResultIdStore.GetEarliestStartedDate/GetLatestCompletedDate(publicAzureDevOpsTestCaseResultoverload inAzureDevOpsResultIdStore.cs, plus the internalAzureDevOpsTestSubResultoverload inAzureDevOpsResultIdStore.AttemptHistory.cs) usedattempts.Where(a => a.X is not null).Min/Max(a => a.X). Each pair of calls is always invoked together from the AzureDevOps live-publishing result-flush hot path, so every invocation double-enumerated the attempts list and allocated aWhereiterator per call, for no benefit over a single manual pass.Focus area
Code-Level Efficiency — replacing a LINQ-based double-enumeration with a single-pass loop.
Approach
Replaced both
Where().Min()/Where().Max()chains (4 methods total across the two files) with manual single-passforeachloops tracking a running min/max, mirroring the existing hand-rolledSumDurations/AddDurationspattern already present in the same files. No public API signature changes.Energy efficiency evidence
Proxy metrics: execution time (wall clock) and memory allocation (
GC.GetAllocatedBytesForCurrentThread()), both mapping directly to reduced CPU cycles and reduced GC pressure — i.e. lower energy draw per call, consistent with the Green Software Foundation's Hardware Efficiency principle (doing the same work with less computation).Standalone console benchmark (net8.0 Release,
Stopwatch+GC.GetAllocatedBytesForCurrentThread(), 2,000,000 call-pairs ofGetEarliestStartedDate+GetLatestCompletedDateover a representative 5-element attempts list):This is a hot path invoked once per result flush during AzureDevOps live test-result publishing, so the reduction compounds across large test runs.
Trade-offs
Slightly more verbose than the LINQ one-liners, but the same shape as the existing
SumDurationshelper in the same file, so it's consistent with the surrounding code style rather than introducing a new pattern. No readability concerns beyond that.Reproducibility
Benchmark: standalone console app (net8.0,
-c Release) exercising both implementations with the same synthetic attempts list, measuringStopwatch.ElapsedMillisecondsandGC.GetAllocatedBytesForCurrentThread()deltas across 2,000,000 call-pairs (warmup of 1,000 iterations before each measured loop,GC.Collect()before each measurement).Test status
./build.sh: Build succeeded, 0 warnings, 0 errors.Microsoft.Testing.Extensions.UnitTests(net8.0, full suite): 1839/1839 passed (37 pre-existing skips).Microsoft.Testing.Extensions.UnitTestsfiltered toFullyQualifiedName~AzureDevOps: 387/387 passed.dotnet format whitespace TestFx.slnx --verify-no-changes --include <both changed files>: clean, no formatting issues.Note
GitHub Actions is not permitted to create or approve pull requests in this repository.
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 (32 of 105 lines)
Warning
Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
southcentralus0.in.applicationinsights.azure.comTo allow these domains, add them to the
network.allowedlist in your workflow frontmatter:See Network Configuration for more information.
Add this agentic workflow to your repo
To install this agentic workflow, run