Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 commits
Select commit Hold shift + click to select a range
5bc4f15
Add regression cross-reference script (STEP 0.6)
Copilot May 2, 2026
5df7f7d
Run regression tests from reverted fix PRs
Copilot May 2, 2026
bb20ff9
Run ALL test types in regression verification (UI, device, unit)
Copilot May 2, 2026
3b4b12e
Run regression tests for OVERLAP too (max confidence)
Copilot May 3, 2026
e7bcdf3
Wire regression tests into try-fix candidate validation
Copilot May 3, 2026
6977a60
Renumber pipeline steps to whole numbers (1-8)
Copilot May 3, 2026
a55d4fd
Merge regression test execution into STEP 3, renumber to 7 steps
Copilot May 3, 2026
b78ce53
Filter out fix PRs not merged into PR's base branch
Copilot May 4, 2026
e4d24c5
Add STEP 3: run detected UI test categories
Copilot May 7, 2026
c25c50f
Preserve STEP 3 results when Tier-3 refresh rewrites uitests/content.md
Copilot May 8, 2026
2e65374
STEP 3: enrich UI test failure detail in AI summary comment
Copilot May 8, 2026
9caab18
STEP 3: surface build/deploy errors and avoid misleading ✓ when categ…
Copilot May 8, 2026
f983997
STEP 3: split multi-line dotnet test capture and dedup failure rendering
Copilot May 8, 2026
5d27a5f
STEP 3: detect infrastructure failures and clearly label them in AI s…
Copilot May 8, 2026
24ed4e8
STEP 3: retry on environment errors (same as Gate's verify-tests-fail…
Copilot May 8, 2026
e3f37b7
STEP 3: delegate UI test runs to shared Invoke-UITestWithRetry.ps1
Copilot May 8, 2026
e779ccc
STEP 3: broaden infra-failure detection to 'Build FAILED + 0 passes'
Copilot May 8, 2026
93277d4
STEP 3: align dotnet test invocation with CI pipeline 313 (TRX, TestC…
Copilot May 8, 2026
afe15c6
STEP 3: scope TRX fallback to current run via timestamp filter
Copilot May 8, 2026
5b2d07f
Add Pester tests for Review-PR.ps1 helpers (Get-TrxResults, Get-DotNe…
Copilot May 8, 2026
8d7a6ff
STEP 3: dispatch UI tests to dedicated child pipeline (mirrors CI 313)
Copilot May 9, 2026
5262370
Revert "STEP 3: dispatch UI tests to dedicated child pipeline (mirror…
Copilot May 9, 2026
352ef5e
STEP 3: add inline RunDeepUITests + UpdateAISummaryComment stages
Copilot May 9, 2026
61ddf07
TEMP: skip Gate + Try-Fix to speed up inline-stages validation
Copilot May 9, 2026
e21dbf8
STEP 3: fix cross-stage output variable lookup syntax
Copilot May 9, 2026
66785b8
STEP 3: install workloads + use build.ps1 in RunDeepUITests stage
Copilot May 9, 2026
41b75b8
Fix Deep UI Tests: use -Category param instead of invalid -OutputDir/…
Copilot May 9, 2026
14e19a3
Drop backtick line-continuation in deep UI tests step
Copilot May 9, 2026
b04bb2c
Disable Xcode version validation in RunDeepUITests stage
Copilot May 9, 2026
943cdb9
Install Node.js + Appium in RunDeepUITests stage
Copilot May 9, 2026
435d9af
Prefer iOS 26 simulator like main ui-tests pipeline + allow Update st…
Copilot May 9, 2026
1dd0ff4
Auto-create iPhone 11 Pro on iOS-26 sim if no matching device pre-ins…
Copilot May 9, 2026
1647702
Prefer iOS-26-0 over iOS-26-1 in Start-Emulator.ps1
Copilot May 9, 2026
b80b57d
Re-enable Gate + Try-Fix after inline-stages validated
Copilot May 9, 2026
0e7bd96
Add failed-test names + snapshot-diff PNGs to deep results
Copilot May 9, 2026
ac2e2ed
Wrap deep section in HTML markers to prevent duplicate stacking
Copilot May 9, 2026
76337b2
Enable Android AVD creation in provision for android Platform
Copilot May 9, 2026
94ad8c2
Prefer highest iOS 26.x runtime (26.4) to match CI baselines
Copilot May 9, 2026
bc05ae9
Add Android SDK tools (adb, emulator) to PATH for Deep stage
Copilot May 9, 2026
8caa9f0
Use AcesShared pool for Android Deep UI Tests
Copilot May 9, 2026
71ff790
Re-enable Gate + Try-Fix for full end-to-end validation
Copilot May 9, 2026
81122aa
Install iOS 26.4 simulator explicitly + re-disable Gate/TryFix
Copilot May 10, 2026
c2261fc
Remove iOS 26.4 download step (requires macOS 26.4+, not downloadable)
Copilot May 10, 2026
7d1d0a9
Demand Tahoe image for iOS pool to ensure iOS 26.4 runtime
Copilot May 10, 2026
0f3d6d8
Skip iOS simulator download in Deep stage — use Tahoe pre-installed r…
Copilot May 10, 2026
dbe3e95
Revert skipSimulatorSetup — iOS 26.0 needed for build, not just tests
Copilot May 10, 2026
a74e13f
Explicitly download iOS 26.4 simulator using latest available Xcode
Copilot May 10, 2026
1226ce8
TEMP: Skip ReviewPR, hardcode ViewBaseTests for fast iOS 26.4 testing
Copilot May 10, 2026
c94f1c8
Try multiple iOS 26.4 download approaches on Tahoe agent
Copilot May 10, 2026
5279111
Add Tahoe image demand to androidPool (matches main CI)
Copilot May 10, 2026
c07e50a
Switch Android to ubuntu-22.04 with KVM (matches main CI)
Copilot May 10, 2026
bcdaab0
Free disk space on ubuntu agents for Android emulator
Copilot May 10, 2026
42ebeb3
Force-restart Android app before tests to recover from ANR
Copilot May 10, 2026
3e1bd8b
Wait for Android settings service before tests (API 30 fix)
Copilot May 10, 2026
d68b48a
Add ignoreHiddenApiPolicyError capability for Android API 30
Copilot May 10, 2026
d33964b
Pre-build test project before app restart to avoid ANR on Android
Copilot May 10, 2026
2c9cada
Fix: allow restore during Android test project pre-build
Copilot May 10, 2026
730f363
Restore full pipeline flow (ReviewPR → Deep → Update)
Copilot May 10, 2026
c515443
Switch Android to MAUI-1ESPT pool with 1ESPT-Ubuntu22.04 image
Copilot May 10, 2026
bfcc27d
TEMP: Skip ReviewPR for fast Android MAUI-1ESPT validation
Copilot May 10, 2026
819635b
Add AVD boot step to Deep stage (matches ReviewPR stage)
Copilot May 10, 2026
aa46f12
Move Android app restart to right before dotnet test execution
Copilot May 10, 2026
0ceb447
Remove manual app restart — let Appium manage Android app lifecycle
Copilot May 10, 2026
b60cf62
Pass DEVICE_UDID to BuildAndRunHostApp in Deep stage
Copilot May 10, 2026
a4c4453
Reset LASTEXITCODE at end of Start-Emulator.ps1
Copilot May 11, 2026
2090260
Restore full pipeline + Android 118/119 ViewBaseTests PASS
Copilot May 11, 2026
12f9597
Improve deep test results comment formatting
Copilot May 11, 2026
d07b616
Add <br/> after all </summary> tags + dynamic category title
Copilot May 11, 2026
28cd40a
Wrap each failed test in details/summary for collapsible logs
Copilot May 11, 2026
05776d8
Re-enable Gate + Try-Fix for full end-to-end pipeline
Copilot May 11, 2026
0c31599
Move comment posting + labels to Stage 3
Copilot May 11, 2026
9e31deb
Address all 6 review comments
Copilot May 11, 2026
39cd803
Address 3 critical review findings (C2, C4, C5)
Copilot May 11, 2026
ff48d37
Fully resolve C2: extract all 3 functions + eliminate Invoke-Expression
Copilot May 11, 2026
559bc0e
Fix Get-TrxResults.ps1 path resolution for Copilot CLI context
Copilot May 11, 2026
3fa4608
Restore inline Get-TrxResults in Review-PR.ps1 (Copilot CLI compat)
Copilot May 11, 2026
2d26dbc
Fix Stage 3 parser error: use variable for code fence backticks
Copilot May 11, 2026
0e6dcd4
Fix all backtick/quote escaping in Stage 3 inline PowerShell
Copilot May 11, 2026
f94b87c
Rename Stage 3 from 'Update/Edit' to 'Post AI Summary Comment'
Copilot May 11, 2026
15fbdc9
Replace in-process SKIPPED results with deep results, not append
Copilot May 11, 2026
277f6b5
Fix Stage 3: post review-only comment when Deep stage is skipped
Copilot May 11, 2026
f28ca40
Fix in-process results stripping — emoji char match was broken
Copilot May 11, 2026
042633a
Add retryCountOnTaskFailure:2 to deep UI tests step
Copilot May 11, 2026
a9fc07b
Add per-test retry for Android flaky failures
Copilot May 12, 2026
dad1224
Improve Android ADB broken pipe retry: 3 attempts + PM health check
Copilot May 12, 2026
95932ee
Fix emulator DEVICE_ID capture: use ID from boot detection
Copilot May 12, 2026
7f2bd84
Revert "Fix emulator DEVICE_ID capture: use ID from boot detection"
Copilot May 12, 2026
e7005db
Fix emulator DEVICE_ID: capture during boot, fallback to emulator-5554
Copilot May 12, 2026
b3dc03b
Fix emulator warm-up: increase timeout 3→6m, add retry, boot-wait on …
Copilot May 12, 2026
3346047
Add stale emulator process cleanup to both pipeline stages
Copilot May 13, 2026
a36c5c7
Split STEP 6 into two copilot calls: Expert Review + Try-Fix
Copilot May 13, 2026
83da409
Try-Fix: generate as many candidates as needed, not a fixed count
Copilot May 13, 2026
7f6bb3a
Reorder workflow: Try-Fix first, then Expert Review + Compare
Copilot May 13, 2026
c5cb815
Try-Fix: iterative expert-review-and-test loop per candidate
Copilot May 13, 2026
1370143
Add diagnostic logging between STEP 6a and 6b
Copilot May 13, 2026
583a1d3
Fix inline findings not posted when DEFER_COMMENT_TO_STAGE3=true
Copilot May 19, 2026
ebcc3aa
Fix nil body in inline review comments causing HTTP 422
Copilot May 19, 2026
8900f38
Fix findings array unwrapping in post-inline-review.ps1
Copilot May 19, 2026
3cf66a8
Pipeline improvements: dedup, fail-fast merge, partial-success fix
Copilot May 19, 2026
421d76e
Fix findings JSON wrapper + merge conflict comment token
Copilot May 20, 2026
13294d8
Revert fail-fast merge conflict and dedup checks
Copilot May 20, 2026
5262718
Fix multimodal review findings: TRX, retry, categories, patterns
Copilot May 20, 2026
7a61d79
Fix round 2 review findings: ALL mode, env patterns, fallbacks
Copilot May 20, 2026
e9a1ee3
Fix agentic-labeler truncating labels to 1 per call
Copilot May 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/agents/maui-expert-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ Every bug fix needs a regression test. Modified code must be checked against git
- CHECK: Test covers the specific scenario from the issue report, not a generic case
- CHECK: Shared code changes are tested on all affected platforms
- CHECK: Previously-fixed issue numbers are cross-referenced when modifying the same code area
- CHECK: If `regression-check/risks.json` exists and contains `REVERT` entries, list the affected fix PRs/issues and require author acknowledgment that the reverted fix is intentional. The regression cross-reference script (`Find-RegressionRisks.ps1`) detects when a PR deletes lines that were previously added by a labeled bug-fix PR.
- CHECK: UI tests run on all applicable platforms unless there is a specific technical limitation
- CHECK: Snapshot baselines updated across all platforms when changing background color, font, or layout
- CHECK: Screenshot size matches capture method — a size mismatch means the capture changed, not the rendering
Expand Down
5 changes: 5 additions & 0 deletions .github/aw/actions-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
"version": "v9.0.0",
"sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3"
},
"github/gh-aw-actions/setup@v0.62.5": {
"repo": "github/gh-aw-actions/setup",
"version": "v0.62.5",
"sha": "dc50be57c94373431b49d3d0927f318ac2bb5c4c"
},
"github/gh-aw-actions/setup@v0.72.1": {
"repo": "github/gh-aw-actions/setup",
"version": "v0.72.1",
Expand Down
209 changes: 186 additions & 23 deletions .github/scripts/BuildAndRunHostApp.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,13 @@ Write-Success "Test project: $TestProject"

#region Run Tests

# Determine the filter to use
# Determine the filter to use.
# NOTE: The CI pipeline `maui-pr-uitests` (definition 313) uses `TestCategory=`
# (see eng/pipelines/common/ui-tests-steps.yml lines 116-164). NUnit accepts
# both `Category=` and `TestCategory=` but Cake's RunTestWithLocalDotNet uses
# `TestCategory=` so we mirror that here for byte-for-byte parity with CI.
if ($Category) {
$effectiveFilter = "Category=$Category"
$effectiveFilter = "TestCategory=$Category"
Write-Step "Running UI tests with category: $Category"
} else {
$effectiveFilter = $TestFilter
Expand All @@ -233,27 +237,30 @@ if ($Platform -eq "android") {
Write-Info "Clearing Android logcat buffer before test..."
& adb -s $DeviceUdid logcat -c

# Dismiss any ANR dialogs that may have appeared during build/deploy.
# The emulator can sit idle during long builds, causing SystemUI ANR.
Write-Info "Dismissing any system dialogs before test..."
# Wait for Android settings service to be available.
Write-Info "Waiting for Android settings service..."
$settingsReady = $false
for ($i = 0; $i -lt 30; $i++) {
$settingsCheck = & adb -s $DeviceUdid shell settings get global device_name 2>&1
if ($settingsCheck -and $settingsCheck -notmatch "Can't find service|error") {
$settingsReady = $true
Write-Success "Settings service ready (device_name=$settingsCheck)"
break
}
Write-Info " Settings service not ready yet (attempt $($i+1)/30)..."
Start-Sleep -Seconds 5
}
if (-not $settingsReady) {
Write-Warn "Settings service may not be ready — tests might fail"
}

# Do NOT force-stop or restart the app here. Appium's UiAutomator2
# driver handles app lifecycle via appPackage/appActivity capabilities.
# Manual restart causes double-stop issues and the app ends up in a
# bad state. Just dismiss any system dialogs and let Appium handle it.
& adb -s $DeviceUdid shell am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS 2>$null
& adb -s $DeviceUdid shell input keyevent KEYCODE_ENTER 2>$null
& adb -s $DeviceUdid shell input keyevent KEYCODE_BACK 2>$null
Start-Sleep -Seconds 1
& adb -s $DeviceUdid shell input keyevent KEYCODE_WAKEUP 2>$null
& adb -s $DeviceUdid shell input keyevent KEYCODE_MENU 2>$null
Start-Sleep -Seconds 1

# Check for lingering ANR dialogs via window dump
$windowDump = & adb -s $DeviceUdid shell dumpsys window 2>$null | Select-String "Application Not Responding|ANR"
if ($windowDump) {
Write-Warn "ANR dialog detected — force-dismissing..."
& adb -s $DeviceUdid shell input keyevent KEYCODE_HOME 2>$null
Start-Sleep -Seconds 2
& adb -s $DeviceUdid shell am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS 2>$null
& adb -s $DeviceUdid shell input keyevent KEYCODE_BACK 2>$null
Start-Sleep -Seconds 1
}
}

# Capture test start time for iOS logs
Expand Down Expand Up @@ -306,19 +313,175 @@ $appiumLogFile = Join-Path $HostAppLogsDir "appium.log"
$env:APPIUM_LOG_FILE = $appiumLogFile
Write-Info "Set APPIUM_LOG_FILE: $appiumLogFile (screenshots will be saved here)"

# ── TRX setup (mirrors CI: eng/cake/dotnet.cake `RunTestWithLocalDotNet`) ──
# CI writes one trx per test run via:
# --logger "trx;LogFileName=<sanitized-name>.trx"
# --logger "console;verbosity=normal"
# --results-directory <test-results-dir>
# /p:VStestUseMSBuildOutput=false
# We reproduce that here so STEP 3's renderer can parse authoritative
# pass/fail counts from the TRX (instead of scraping console output, which is
# fragile when many tests run and lines get interleaved or wrapped).
$trxResultsDir = Join-Path $HostAppLogsDir "TestResults"
if (-not (Test-Path $trxResultsDir)) {
New-Item -ItemType Directory -Path $trxResultsDir -Force | Out-Null
}
# Sanitize the trx file name. NUnit/MSTest reject some characters. We keep
# alpha-numeric, dash, underscore and dot — same set Cake's
# SanitizeTestResultsFilename uses.
$trxBaseName = if ($Category) { "$Category-$Platform" } else {
($TestFilter -replace '[^A-Za-z0-9._-]', '_')
}
$trxBaseName = $trxBaseName -replace '[^A-Za-z0-9._-]', '_'
$trxFileName = "$trxBaseName.trx"
$trxFilePath = Join-Path $trxResultsDir $trxFileName
# Pre-clean stale TRX so we never read a previous run's results
if (Test-Path $trxFilePath) { Remove-Item $trxFilePath -Force -ErrorAction SilentlyContinue }

Write-Info "TRX file will be written to: $trxFilePath"

try {
# Run dotnet test and capture output
$testOutput = & dotnet test $TestProject --filter $effectiveFilter --logger "console;verbosity=detailed" 2>&1
# Run dotnet test using the SAME loggers and arguments CI uses in
# `RunTestWithLocalDotNet` (eng/cake/dotnet.cake line 943-981).
$trxRunStart = Get-Date
$testArgs = @($TestProject, "--filter", $effectiveFilter,
"--logger", "trx;LogFileName=$trxFileName",
"--logger", "console;verbosity=normal",
"--results-directory", $trxResultsDir,
"/p:VStestUseMSBuildOutput=false")
Write-Info "Actual dotnet test args: $($testArgs -join ' ')"
$testOutput = & dotnet test @testArgs 2>&1

# Save test output to file
$testOutput | Out-File -FilePath $testOutputFile -Encoding UTF8

# Output test results to the output stream so callers can capture them
# (Write-Host goes to the Information stream which is not captured by 2>&1)
$testOutput | ForEach-Object { Write-Output $_ }


# Surface the TRX path on a marker line so callers (Invoke-UITestWithRetry
# and Review-PR.ps1) can locate the authoritative results file regardless
# of where the working directory was when this script ran.
if (Test-Path $trxFilePath) {
Write-Output ">>> TRX_RESULT_FILE: $trxFilePath"
} else {
# dotnet test may have written the TRX with a slightly different name
# (e.g. LogFileName argument stripped on Windows, or it injected a
# timestamp). Fall back to scanning the results dir for any .trx
# written AFTER this run started — never pick up a stale TRX from a
# previous category that shares the same results directory.
$latestTrx = Get-ChildItem -Path $trxResultsDir -Filter "*.trx" -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -ge $trxRunStart } |
Sort-Object LastWriteTime -Descending | Select-Object -First 1
if ($latestTrx) {
Write-Output ">>> TRX_RESULT_FILE: $($latestTrx.FullName)"
}
}

$testExitCode = $LASTEXITCODE

# ── Per-test retry for flaky failures (Android emulator instability) ──
# Parse the TRX for failed tests and re-run them once. This catches
# emulator-induced timeouts and transient ADB failures that aren't
# real test bugs. Only retry on Android where flake rate is ~5%.
if ($testExitCode -ne 0 -and $Platform -eq 'android' -and (Test-Path $trxFilePath)) {
. "$PSScriptRoot/shared/Get-TrxResults.ps1"
$firstRun = Get-TrxResults -TrxPath $trxFilePath
if ($firstRun -and [int]$firstRun.Failed -gt 0 -and [int]$firstRun.Passed -gt 0) {
$failedNames = @($firstRun.Results | Where-Object { $_.status -eq 'Failed' } | ForEach-Object { $_.name })
Write-Host ""
Write-Warn "🔄 Retrying $($failedNames.Count) failed test(s) on Android..."

# Build a FullyQualifiedName filter for just the failed tests.
# Strip parameter signatures (e.g. TestMethod(arg: "val")) because
# VSTest filter grammar treats ( ) | & ! as operators. Using the
# bare method name with ~ (contains) is safe and sufficient.
$safeNames = @($failedNames | ForEach-Object { $_ -replace '\(.*$', '' } | Select-Object -Unique)
$retryFilter = ($safeNames | ForEach-Object { "FullyQualifiedName~$_" }) -join ' | '
$retryTrx = Join-Path $trxResultsDir "retry-$trxBaseName.trx"
Remove-Item $retryTrx -Force -ErrorAction SilentlyContinue

$retryArgs = @($TestProject, "--filter", $retryFilter,
"--logger", "trx;LogFileName=retry-$trxFileName",
"--logger", "console;verbosity=normal",
"--results-directory", $trxResultsDir,
"/p:VStestUseMSBuildOutput=false", "--no-build")
Write-Info "Retry args: dotnet test --filter '$retryFilter' --no-build"
$retryOutput = & dotnet test @retryArgs 2>&1
$retryOutput | ForEach-Object { Write-Output $_ }
$retryExitCode = $LASTEXITCODE

# Parse retry TRX and count how many passed on retry
$retryTrxPath = Join-Path $trxResultsDir "retry-$trxFileName"
if (Test-Path $retryTrxPath) {
$retryResults = Get-TrxResults -TrxPath $retryTrxPath
if ($retryResults) {
$retryPassed = @($retryResults.Results | Where-Object { $_.status -eq 'Passed' }).Count
$retryFailed = @($retryResults.Results | Where-Object { $_.status -eq 'Failed' }).Count
Write-Host " Retry results: $retryPassed passed, $retryFailed failed (of $($failedNames.Count) retried)" -ForegroundColor Cyan

if ($retryFailed -eq 0) {
Write-Success "All $retryPassed flaky test(s) passed on retry!"
$testExitCode = 0
} else {
Write-Warn "$retryFailed test(s) still failing after retry (real failures)"
}
# Merge retry results into the original TRX: replace only the
# retried test entries in the original with their retry outcomes,
# preserving all tests that passed on the first run. This avoids
# the prior bug where Copy-Item overwrote the full TRX with the
# retry-only TRX, losing the first-run passing tests entirely.
try {
[xml]$origXml = Get-Content -Path $trxFilePath -Raw -Encoding UTF8
[xml]$retryXml = Get-Content -Path $retryTrxPath -Raw -Encoding UTF8
$nsUri = 'http://microsoft.com/schemas/VisualStudio/TeamTest/2010'
$nsMgr = New-Object System.Xml.XmlNamespaceManager($origXml.NameTable)
$nsMgr.AddNamespace('t', $nsUri)
$retryNsMgr = New-Object System.Xml.XmlNamespaceManager($retryXml.NameTable)
$retryNsMgr.AddNamespace('t', $nsUri)

# Build a lookup of retry results by testName
$retryByName = @{}
foreach ($rr in $retryXml.SelectNodes('//t:UnitTestResult', $retryNsMgr)) {
$retryByName[$rr.GetAttribute('testName')] = $rr
}

# Replace matching entries in the original TRX
foreach ($origResult in $origXml.SelectNodes('//t:UnitTestResult', $nsMgr)) {
$tName = $origResult.GetAttribute('testName')
if ($retryByName.ContainsKey($tName)) {
$imported = $origXml.ImportNode($retryByName[$tName], $true)
$origResult.ParentNode.ReplaceChild($imported, $origResult) | Out-Null
}
}

# Update counters to reflect merged results
$allResults = $origXml.SelectNodes('//t:UnitTestResult', $nsMgr)
$mergedTotal = $allResults.Count
$mergedPassed = @($allResults | Where-Object { $_.GetAttribute('outcome') -eq 'Passed' }).Count
$mergedFailed = @($allResults | Where-Object { $_.GetAttribute('outcome') -eq 'Failed' }).Count
$mergedExecuted = $mergedPassed + $mergedFailed
$counters = $origXml.SelectSingleNode('//t:ResultSummary/t:Counters', $nsMgr)
if ($counters) {
$counters.SetAttribute('total', $mergedTotal)
$counters.SetAttribute('executed', $mergedExecuted)
$counters.SetAttribute('passed', $mergedPassed)
$counters.SetAttribute('failed', $mergedFailed)
}

$origXml.Save($trxFilePath)
Write-Info "Merged retry results into original TRX ($mergedTotal total, $mergedPassed passed, $mergedFailed failed)"
} catch {
Write-Warn "Failed to merge TRX — falling back to retry-only TRX: $_"
Copy-Item $retryTrxPath $trxFilePath -Force
}
# Remove the retry TRX to prevent double-counting by downstream aggregators
Remove-Item $retryTrxPath -Force -ErrorAction SilentlyContinue
}
}
}
}

Write-Host ""
Write-Info "Test output saved to: $testOutputFile"

Expand Down
Loading
Loading