From f7ca3a5d411268dc43b8963952e2a4c561ef1acb Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Mon, 12 Jan 2026 17:17:35 -0600 Subject: [PATCH 1/9] Improve verify-tests-fail skill: add fetch, fix fork detection, require full verification --- .../verify-tests-fail-without-fix/SKILL.md | 67 +++----- .../scripts/verify-tests-fail.ps1 | 162 ++++++++---------- 2 files changed, 98 insertions(+), 131 deletions(-) diff --git a/.github/skills/verify-tests-fail-without-fix/SKILL.md b/.github/skills/verify-tests-fail-without-fix/SKILL.md index f0708157e4c5..5aa853bea8e0 100644 --- a/.github/skills/verify-tests-fail-without-fix/SKILL.md +++ b/.github/skills/verify-tests-fail-without-fix/SKILL.md @@ -1,37 +1,34 @@ --- name: verify-tests-fail-without-fix -description: Verifies UI tests catch the bug. Auto-detects mode based on git diff - if fix files exist, verifies FAIL without fix and PASS with fix. If only test files, verifies tests FAIL. +description: Verifies UI tests catch the bug by checking tests FAIL without the fix and PASS with the fix. Requires fix files to be detected in the PR. --- # Verify Tests Fail Without Fix -Verifies UI tests actually catch the issue. **Mode is auto-detected based on git diff.** +Verifies UI tests actually catch the issue by running tests in two states: +1. **Without fix** - tests should FAIL (bug is present) +2. **With fix** - tests should PASS (bug is fixed) ## Usage ```bash -# Auto-detects everything - just specify platform -pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform android +# Auto-detects everything - just specify platform (recommended for skill invocation) +pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform android -RequireFullVerification # With explicit test filter -pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform ios -TestFilter "Issue33356" +pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform ios -TestFilter "Issue33356" -RequireFullVerification ``` -## Auto-Detection +## Requirements -The script automatically determines the mode: +This skill requires: +- **Fix files** in the PR (non-test code changes) +- **Test files** in the PR (to verify against) -| Changed Files | Mode | Behavior | -|---------------|------|----------| -| Fix files + test files | Full verification | FAIL without fix, PASS with fix | -| Only test files | Verify failure only | Tests must FAIL (reproduce bug) | - -**Fix files** = any changed file NOT in test directories -**Test files** = files in `TestCases.*` directories +If no fix files are detected, the skill will error out with troubleshooting guidance. ## Expected Output -**Full mode (fix files detected):** ``` ╔═══════════════════════════════════════════════════════════╗ ║ VERIFICATION PASSED ✅ ║ @@ -41,49 +38,39 @@ The script automatically determines the mode: ╚═══════════════════════════════════════════════════════════╝ ``` -**Verify failure only (no fix files):** -``` -╔═══════════════════════════════════════════════════════════╗ -║ VERIFICATION PASSED ✅ ║ -╠═══════════════════════════════════════════════════════════╣ -║ Tests FAILED as expected (bug is reproduced) ║ -╚═══════════════════════════════════════════════════════════╝ -``` - ## Troubleshooting | Problem | Cause | Solution | |---------|-------|----------| +| No fix files detected | Base branch detection failed or no non-test files changed | Use `-FixFiles` or `-BaseBranch` explicitly | | Tests pass without fix | Tests don't detect the bug | Review test assertions, update test | -| Tests pass (no fix files) | **Test is wrong** | Review test vs issue description, fix test | +| Tests fail with fix | Fix doesn't work or test is wrong | Review fix implementation | | App crashes | Duplicate issue numbers, XAML error | Check device logs | | Element not found | Wrong AutomationId, app crashed | Verify IDs match | ## What It Does -**Full mode:** -1. Auto-detects fix files (non-test code) from git diff -2. Auto-detects test classes from `TestCases.Shared.Tests/*.cs` -3. Reverts fix files to base branch -4. Runs tests (should FAIL without fix) -5. Restores fix files -6. Runs tests (should PASS with fix) -7. Reports result - -**Verify Failure Only mode:** -1. Runs tests once -2. Verifies they FAIL (bug reproduced) -3. Reports result +1. Fetches base branch from origin to ensure accurate diff +2. Auto-detects fix files (non-test code) from git diff +3. Auto-detects test classes from `TestCases.Shared.Tests/*.cs` +4. Reverts fix files to base branch +5. Runs tests (should FAIL without fix) +6. Restores fix files +7. Runs tests (should PASS with fix) +8. Reports result ## Optional Parameters ```bash +# Require full verification (fail if no fix files detected) - recommended +-RequireFullVerification + # Explicit test filter -TestFilter "Issue32030|ButtonUITests" # Explicit fix files -FixFiles @("src/Core/src/File.cs") -# Verify failure only (no fix exists yet) --VerifyFailureOnly +# Explicit base branch +-BaseBranch "main" ``` diff --git a/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 b/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 index 5aa8b570ab8e..622e57fb53c4 100644 --- a/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 +++ b/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 @@ -1,22 +1,16 @@ #!/usr/bin/env pwsh <# .SYNOPSIS - Verifies that UI tests catch the bug. Auto-detects mode based on whether fix files exist. + Verifies that UI tests catch the bug by running tests without and with the fix. .DESCRIPTION - This script verifies that tests actually catch the issue. It auto-detects the mode: + This script verifies that tests actually catch the issue by: + 1. Reverting fix files to base branch + 2. Running tests WITHOUT fix (should FAIL) + 3. Restoring fix files + 4. Running tests WITH fix (should PASS) - **If fix files exist (non-test code changed):** - - Full verification mode - - Reverts fix files to base branch - - Runs tests WITHOUT fix (should FAIL) - - Restores fix files - - Runs tests WITH fix (should PASS) - - **If only test files changed (no fix files):** - - Verify failure only mode - - Runs tests once expecting them to FAIL - - Confirms tests reproduce the bug + Fix files are auto-detected from the git diff (non-test files that changed). .PARAMETER Platform Target platform: "android" or "ios" @@ -35,6 +29,10 @@ .PARAMETER OutputDir Directory to store results (default: "CustomAgentLogsTmp/TestValidation") +.PARAMETER RequireFullVerification + If set, the script will fail if it cannot run full verification mode + (i.e., if no fix files are detected). Used by the skill to ensure proper validation. + .EXAMPLE # Auto-detect everything - simplest usage ./verify-tests-fail.ps1 -Platform android @@ -43,6 +41,10 @@ # Specify test filter, auto-detect mode and fix files ./verify-tests-fail.ps1 -Platform android -TestFilter "Issue32030" +.EXAMPLE + # Require full verification (fail if no fix files detected) + ./verify-tests-fail.ps1 -Platform android -RequireFullVerification + .EXAMPLE # Specify everything explicitly ./verify-tests-fail.ps1 -Platform ios -TestFilter "Issue12345" ` @@ -64,7 +66,10 @@ param( [string]$BaseBranch, [Parameter(Mandatory = $false)] - [string]$OutputDir = "CustomAgentLogsTmp/TestValidation" + [string]$OutputDir = "CustomAgentLogsTmp/TestValidation", + + [Parameter(Mandatory = $false)] + [switch]$RequireFullVerification ) $ErrorActionPreference = "Stop" @@ -105,19 +110,32 @@ function Test-IsTestFile { $BaseBranchDetected = $BaseBranch if (-not $BaseBranchDetected) { $currentBranch = git rev-parse --abbrev-ref HEAD 2>$null - $remote = git config "branch.$currentBranch.remote" 2>$null - if (-not $remote) { $remote = "origin" } - $remoteUrl = git remote get-url $remote 2>$null - $repo = $null - if ($remoteUrl -match "github\.com[:/]([^/]+/[^/]+?)(\.git)?$") { - $repo = $matches[1] - } + # Try gh pr view without --repo first (works for PRs from forks to upstream) + $BaseBranchDetected = gh pr view --json baseRefName --jq '.baseRefName' 2>$null - if ($repo) { - $BaseBranchDetected = gh pr view $currentBranch --repo $repo --json baseRefName --jq '.baseRefName' 2>$null - } else { - $BaseBranchDetected = gh pr view --json baseRefName --jq '.baseRefName' 2>$null + # If that fails, try with the origin remote's repo + if (-not $BaseBranchDetected) { + $remoteUrl = git remote get-url origin 2>$null + $repo = $null + if ($remoteUrl -match "github\.com[:/]([^/]+/[^/]+?)(\.git)?$") { + $repo = $matches[1] + } + + if ($repo) { + $BaseBranchDetected = gh pr view $currentBranch --repo $repo --json baseRefName --jq '.baseRefName' 2>$null + } + } +} + +# Fetch the base branch from origin to ensure we have the latest +# This ensures accurate diff detection even if local branch is stale +if ($BaseBranchDetected) { + Write-Host "ℹ️ Fetching latest $BaseBranchDetected from origin..." -ForegroundColor Cyan + git fetch origin "${BaseBranchDetected}:${BaseBranchDetected}" 2>$null + if ($LASTEXITCODE -ne 0) { + # If direct fetch fails (e.g., currently on that branch), just fetch the ref + git fetch origin $BaseBranchDetected 2>$null } } @@ -125,9 +143,6 @@ if (-not $BaseBranchDetected) { $DetectedFixFiles = @() if ($BaseBranchDetected) { $changedFiles = git diff $BaseBranchDetected HEAD --name-only 2>$null - if ($LASTEXITCODE -ne 0) { - $changedFiles = git diff "origin/$BaseBranchDetected" HEAD --name-only 2>$null - } if ($changedFiles) { foreach ($file in $changedFiles) { @@ -143,73 +158,38 @@ if ($FixFiles -and $FixFiles.Count -gt 0) { $DetectedFixFiles = $FixFiles } -# Determine mode based on whether we have fix files -$VerifyFailureOnlyMode = ($DetectedFixFiles.Count -eq 0) - -# ============================================================ -# VERIFY FAILURE ONLY MODE (no fix files detected) -# ============================================================ -if ($VerifyFailureOnlyMode) { +# Error if no fix files detected and RequireFullVerification is set +if ($DetectedFixFiles.Count -eq 0 -and $RequireFullVerification) { Write-Host "" - Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan - Write-Host "║ VERIFY FAILURE ONLY MODE ║" -ForegroundColor Cyan - Write-Host "╠═══════════════════════════════════════════════════════════╣" -ForegroundColor Cyan - Write-Host "║ No fix files detected - verifying tests FAIL ║" -ForegroundColor Cyan - Write-Host "║ (Only test files changed, or new tests created) ║" -ForegroundColor Cyan - Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Cyan + Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Red + Write-Host "║ ERROR: NO FIX FILES DETECTED ║" -ForegroundColor Red + Write-Host "╠═══════════════════════════════════════════════════════════╣" -ForegroundColor Red + Write-Host "║ Full verification mode required but no fix files found. ║" -ForegroundColor Red + Write-Host "║ ║" -ForegroundColor Red + Write-Host "║ Possible causes: ║" -ForegroundColor Red + Write-Host "║ - Base branch detection failed ║" -ForegroundColor Red + Write-Host "║ - No non-test files changed in this PR ║" -ForegroundColor Red + Write-Host "║ - Git fetch failed to update local branch ║" -ForegroundColor Red + Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Red Write-Host "" - - if (-not $TestFilter) { - Write-Host "❌ -TestFilter is required when no fix files are detected" -ForegroundColor Red - Write-Host " Example: -TestFilter 'Issue33356'" -ForegroundColor Yellow - exit 1 - } - - # Create output directory - $OutputPath = Join-Path $RepoRoot $OutputDir - New-Item -ItemType Directory -Force -Path $OutputPath | Out-Null - $FailureOnlyLog = Join-Path $OutputPath "verify-failure-only.log" - - Write-Host "Platform: $Platform" -ForegroundColor White - Write-Host "TestFilter: $TestFilter" -ForegroundColor White + Write-Host "Debug info:" -ForegroundColor Yellow + Write-Host " Base branch detected: '$BaseBranchDetected'" -ForegroundColor Yellow + Write-Host " Current branch: $(git rev-parse --abbrev-ref HEAD)" -ForegroundColor Yellow Write-Host "" - Write-Host "Running tests (expecting FAILURE)..." -ForegroundColor Yellow - - # Run the test - $buildScript = Join-Path $RepoRoot ".github/scripts/BuildAndRunHostApp.ps1" - & $buildScript -Platform $Platform -TestFilter $TestFilter -Rebuild 2>&1 | Tee-Object -FilePath $FailureOnlyLog - - # Check test result - $testOutputLog = Join-Path $RepoRoot "CustomAgentLogsTmp/UITests/test-output.log" - $testFailed = $false - - if (Test-Path $testOutputLog) { - $content = Get-Content $testOutputLog -Raw - if ($content -match "Failed:\s*(\d+)" -and [int]$matches[1] -gt 0) { - $testFailed = $true - } - } - + Write-Host "To fix, try one of:" -ForegroundColor Cyan + Write-Host " 1. Specify fix files explicitly: -FixFiles @('path/to/fix.cs')" -ForegroundColor White + Write-Host " 2. Specify base branch: -BaseBranch 'main'" -ForegroundColor White + Write-Host " 3. Ensure you're on a PR branch with non-test file changes" -ForegroundColor White + exit 1 +} + +# If no fix files and not requiring full verification, warn but continue +if ($DetectedFixFiles.Count -eq 0) { Write-Host "" - if ($testFailed) { - Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Green - Write-Host "║ VERIFICATION PASSED ✅ ║" -ForegroundColor Green - Write-Host "╠═══════════════════════════════════════════════════════════╣" -ForegroundColor Green - Write-Host "║ Tests FAILED as expected (bug is reproduced) ║" -ForegroundColor Green - Write-Host "║ ║" -ForegroundColor Green - Write-Host "║ Next: Implement a fix, then rerun to verify tests pass. ║" -ForegroundColor Green - Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Green - exit 0 - } else { - Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Red - Write-Host "║ VERIFICATION FAILED ❌ ║" -ForegroundColor Red - Write-Host "╠═══════════════════════════════════════════════════════════╣" -ForegroundColor Red - Write-Host "║ Tests PASSED (unexpected - bug not reproduced) ║" -ForegroundColor Red - Write-Host "║ ║" -ForegroundColor Red - Write-Host "║ Your test is wrong. Fix it and rerun. ║" -ForegroundColor Red - Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Red - exit 1 - } + Write-Host "⚠️ Warning: No fix files detected. Cannot run full verification." -ForegroundColor Yellow + Write-Host " Use -RequireFullVerification to enforce full verification mode." -ForegroundColor Yellow + Write-Host " Use -FixFiles to specify fix files explicitly." -ForegroundColor Yellow + exit 0 } # ============================================================ From ed5309b79aee5203f79ae2afe442d54baa628b75 Mon Sep 17 00:00:00 2001 From: Jakub Florkowski Date: Tue, 13 Jan 2026 23:52:41 +0100 Subject: [PATCH 2/9] Add 'verify failure only' mode to test verification script Enhanced verify-tests-fail.ps1 to support a mode where, if no fix files are detected, it auto-detects changed test files, runs only the relevant tests, and expects them to fail. This mode helps validate that new or updated tests correctly reproduce a bug before a fix is implemented, providing detailed output and guidance for users. --- .../scripts/verify-tests-fail.ps1 | 191 +++++++++++++++++- 1 file changed, 186 insertions(+), 5 deletions(-) diff --git a/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 b/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 index 622e57fb53c4..44ed2d28c328 100644 --- a/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 +++ b/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 @@ -183,13 +183,194 @@ if ($DetectedFixFiles.Count -eq 0 -and $RequireFullVerification) { exit 1 } -# If no fix files and not requiring full verification, warn but continue +# If no fix files and not requiring full verification, run in "verify failure only" mode if ($DetectedFixFiles.Count -eq 0) { Write-Host "" - Write-Host "⚠️ Warning: No fix files detected. Cannot run full verification." -ForegroundColor Yellow - Write-Host " Use -RequireFullVerification to enforce full verification mode." -ForegroundColor Yellow - Write-Host " Use -FixFiles to specify fix files explicitly." -ForegroundColor Yellow - exit 0 + Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan + Write-Host "║ VERIFY FAILURE ONLY MODE ║" -ForegroundColor Cyan + Write-Host "╠═══════════════════════════════════════════════════════════╣" -ForegroundColor Cyan + Write-Host "║ No fix files detected - will only verify: ║" -ForegroundColor Cyan + Write-Host "║ 1. Tests FAIL (proving they catch the bug) ║" -ForegroundColor Cyan + Write-Host "║ ║" -ForegroundColor Cyan + Write-Host "║ Use this mode when creating tests before writing a fix. ║" -ForegroundColor Cyan + Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Cyan + Write-Host "" + + # Auto-detect test filter if not provided + if (-not $TestFilter) { + Write-Host "🔍 Auto-detecting test filter from changed test files..." -ForegroundColor Cyan + + $changedFiles = @() + if ($BaseBranchDetected) { + $changedFiles = git diff $BaseBranchDetected HEAD --name-only 2>$null + if ($LASTEXITCODE -ne 0) { + $changedFiles = git diff "origin/$BaseBranchDetected" HEAD --name-only 2>$null + } + } + + # If no base branch, use git status to find unstaged/staged files + if (-not $changedFiles -or $changedFiles.Count -eq 0) { + $changedFiles = git diff --name-only 2>$null + if ($changedFiles -and $changedFiles.Count -gt 0) { + $changedFiles = @($changedFiles) + } else { + $changedFiles = git diff --cached --name-only 2>$null + } + } + + # Find test files (files in test directories that are .cs files) + $testFiles = @() + foreach ($file in $changedFiles) { + if ($file -match "TestCases\.(Shared\.Tests|HostApp).*\.cs$" -and $file -notmatch "^_") { + $testFiles += $file + } + } + + if ($testFiles.Count -eq 0) { + Write-Host "❌ Could not auto-detect test filter. No test files found in changed files." -ForegroundColor Red + Write-Host " Looking for files matching: TestCases.(Shared.Tests|HostApp)/*.cs" -ForegroundColor Yellow + Write-Host " Please provide -TestFilter parameter explicitly." -ForegroundColor Yellow + exit 1 + } + + # Extract class names from test files + $testClassNames = @() + foreach ($file in $testFiles) { + if ($file -match "TestCases\.Shared\.Tests.*\.cs$") { + $fullPath = Join-Path $RepoRoot $file + if (Test-Path $fullPath) { + $content = Get-Content $fullPath -Raw + if ($content -match "public\s+(partial\s+)?class\s+(\w+)") { + $className = $matches[2] + if ($className -notmatch "^_" -and $testClassNames -notcontains $className) { + $testClassNames += $className + } + } + } + } + } + + # Fallback: use file names without extension + if ($testClassNames.Count -eq 0) { + foreach ($file in $testFiles) { + $fileName = [System.IO.Path]::GetFileNameWithoutExtension($file) + if ($fileName -notmatch "^_" -and $testClassNames -notcontains $fileName) { + $testClassNames += $fileName + } + } + } + + if ($testClassNames.Count -eq 0) { + Write-Host "❌ Could not extract test class names from changed files." -ForegroundColor Red + Write-Host " Please provide -TestFilter parameter explicitly." -ForegroundColor Yellow + exit 1 + } + + if ($testClassNames.Count -eq 1) { + $TestFilter = $testClassNames[0] + } else { + $TestFilter = $testClassNames -join "|" + } + + Write-Host "✅ Auto-detected $($testClassNames.Count) test class(es):" -ForegroundColor Green + foreach ($name in $testClassNames) { + Write-Host " - $name" -ForegroundColor White + } + Write-Host " Filter: $TestFilter" -ForegroundColor Cyan + } + + # Create output directory + $OutputPath = Join-Path $RepoRoot $OutputDir + New-Item -ItemType Directory -Force -Path $OutputPath | Out-Null + + $ValidationLog = Join-Path $OutputPath "verification-log.txt" + $TestLog = Join-Path $OutputPath "test-failure-verification.log" + + # Initialize log + "" | Set-Content $ValidationLog + "=========================================" | Add-Content $ValidationLog + "Verify Tests Fail (Failure Only Mode)" | Add-Content $ValidationLog + "=========================================" | Add-Content $ValidationLog + "Platform: $Platform" | Add-Content $ValidationLog + "TestFilter: $TestFilter" | Add-Content $ValidationLog + "" | Add-Content $ValidationLog + + Write-Host "🧪 Running tests (expecting them to FAIL)..." -ForegroundColor Cyan + Write-Host "" + + # Use shared BuildAndRunHostApp.ps1 infrastructure with -Rebuild to ensure clean builds + $buildScript = Join-Path $RepoRoot ".github/scripts/BuildAndRunHostApp.ps1" + & $buildScript -Platform $Platform -TestFilter $TestFilter -Rebuild 2>&1 | Tee-Object -FilePath $TestLog + + # Parse test results + $testOutputLog = Join-Path $RepoRoot "CustomAgentLogsTmp/UITests/test-output.log" + $testResult = @{ Passed = $false; Error = $null } + + if (Test-Path $testOutputLog) { + $content = Get-Content $testOutputLog -Raw + if ($content -match "Failed:\s*(\d+)") { + $testResult.Passed = $false + $testResult.FailCount = [int]$matches[1] + } elseif ($content -match "Passed:\s*(\d+)") { + $testResult.Passed = $true + $testResult.PassCount = [int]$matches[1] + } else { + $testResult.Error = "Could not parse test results" + } + } else { + $testResult.Error = "Test output log not found: $testOutputLog" + } + + # Evaluate results + Write-Host "" + Write-Host "==========================================" + Write-Host "VERIFICATION RESULTS" + Write-Host "==========================================" + Write-Host "" + + if ($testResult.Error) { + Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Red + Write-Host "║ ERROR PARSING TEST RESULTS ║" -ForegroundColor Red + Write-Host "╠═══════════════════════════════════════════════════════════╣" -ForegroundColor Red + Write-Host "║ $($testResult.Error.PadRight(57)) ║" -ForegroundColor Red + Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Red + exit 1 + } + + if (-not $testResult.Passed) { + # Tests FAILED - this is what we want! + Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Green + Write-Host "║ VERIFICATION PASSED ✅ ║" -ForegroundColor Green + Write-Host "╠═══════════════════════════════════════════════════════════╣" -ForegroundColor Green + Write-Host "║ Tests FAILED as expected! ║" -ForegroundColor Green + Write-Host "║ ║" -ForegroundColor Green + Write-Host "║ This proves the tests correctly reproduce the bug. ║" -ForegroundColor Green + Write-Host "║ You can now proceed to write the fix. ║" -ForegroundColor Green + Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Green + Write-Host "" + Write-Host "Failed tests: $($testResult.FailCount)" -ForegroundColor Yellow + exit 0 + } else { + # Tests PASSED - this is bad! + Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Red + Write-Host "║ VERIFICATION FAILED ❌ ║" -ForegroundColor Red + Write-Host "╠═══════════════════════════════════════════════════════════╣" -ForegroundColor Red + Write-Host "║ Tests PASSED but they should FAIL! ║" -ForegroundColor Red + Write-Host "║ ║" -ForegroundColor Red + Write-Host "║ This means your tests don't actually reproduce the bug. ║" -ForegroundColor Red + Write-Host "║ ║" -ForegroundColor Red + Write-Host "║ Possible causes: ║" -ForegroundColor Red + Write-Host "║ 1. Test scenario doesn't match the issue description ║" -ForegroundColor Red + Write-Host "║ 2. Test assertions are wrong or too lenient ║" -ForegroundColor Red + Write-Host "║ 3. The bug was already fixed in this branch ║" -ForegroundColor Red + Write-Host "║ 4. The bug only happens in specific conditions ║" -ForegroundColor Red + Write-Host "║ ║" -ForegroundColor Red + Write-Host "║ Go back and revise your tests! ║" -ForegroundColor Red + Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Red + Write-Host "" + Write-Host "Passed tests: $($testResult.PassCount)" -ForegroundColor Yellow + exit 1 + } } # ============================================================ From 8af854a999a0084e44a0061328cb769999adac24 Mon Sep 17 00:00:00 2001 From: Jakub Florkowski Date: Tue, 13 Jan 2026 23:59:44 +0100 Subject: [PATCH 3/9] Use consistent base branch ref for git operations Introduces $BaseBranchRef to consistently reference the latest base branch, whether local or origin, for all git diff, ls-tree, and checkout operations. This improves reliability when the local branch is stale or fetch operations fail, and simplifies the logic for detecting changed files and reverting fixes. --- .../scripts/verify-tests-fail.ps1 | 30 ++++++++----------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 b/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 index 44ed2d28c328..b892c2384807 100644 --- a/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 +++ b/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 @@ -130,19 +130,22 @@ if (-not $BaseBranchDetected) { # Fetch the base branch from origin to ensure we have the latest # This ensures accurate diff detection even if local branch is stale +$BaseBranchRef = $BaseBranchDetected if ($BaseBranchDetected) { Write-Host "ℹ️ Fetching latest $BaseBranchDetected from origin..." -ForegroundColor Cyan git fetch origin "${BaseBranchDetected}:${BaseBranchDetected}" 2>$null if ($LASTEXITCODE -ne 0) { # If direct fetch fails (e.g., currently on that branch), just fetch the ref + # In this case, use origin/$BaseBranchDetected for comparisons since local wasn't updated git fetch origin $BaseBranchDetected 2>$null + $BaseBranchRef = "origin/$BaseBranchDetected" } } # Check for fix files (non-test files that changed) $DetectedFixFiles = @() if ($BaseBranchDetected) { - $changedFiles = git diff $BaseBranchDetected HEAD --name-only 2>$null + $changedFiles = git diff $BaseBranchRef HEAD --name-only 2>$null if ($changedFiles) { foreach ($file in $changedFiles) { @@ -201,11 +204,8 @@ if ($DetectedFixFiles.Count -eq 0) { Write-Host "🔍 Auto-detecting test filter from changed test files..." -ForegroundColor Cyan $changedFiles = @() - if ($BaseBranchDetected) { - $changedFiles = git diff $BaseBranchDetected HEAD --name-only 2>$null - if ($LASTEXITCODE -ne 0) { - $changedFiles = git diff "origin/$BaseBranchDetected" HEAD --name-only 2>$null - } + if ($BaseBranchRef) { + $changedFiles = git diff $BaseBranchRef HEAD --name-only 2>$null } # If no base branch, use git status to find unstaged/staged files @@ -390,7 +390,7 @@ Write-Host "" $BaseBranch = $BaseBranchDetected $FixFiles = $DetectedFixFiles -Write-Host "✅ Base branch: $BaseBranch" -ForegroundColor Green +Write-Host "✅ Base branch: $BaseBranch (using ref: $BaseBranchRef)" -ForegroundColor Green Write-Host "✅ Fix files ($($FixFiles.Count)):" -ForegroundColor Green foreach ($file in $FixFiles) { Write-Host " - $file" -ForegroundColor White @@ -400,10 +400,7 @@ foreach ($file in $FixFiles) { if (-not $TestFilter) { Write-Host "🔍 Auto-detecting test filter from changed test files..." -ForegroundColor Cyan - $changedFiles = git diff $BaseBranch HEAD --name-only 2>$null - if ($LASTEXITCODE -ne 0) { - $changedFiles = git diff "origin/$BaseBranch" HEAD --name-only 2>$null - } + $changedFiles = git diff $BaseBranchRef HEAD --name-only 2>$null # Find test files (files in test directories that are .cs files) $testFiles = @() @@ -527,10 +524,7 @@ $NewFiles = @() foreach ($file in $FixFiles) { # Check if file exists in base branch - $existsInBase = git ls-tree -r $BaseBranch --name-only -- $file 2>$null - if (-not $existsInBase) { - $existsInBase = git ls-tree -r "origin/$BaseBranch" --name-only -- $file 2>$null - } + $existsInBase = git ls-tree -r $BaseBranchRef --name-only -- $file 2>$null if ($existsInBase) { $RevertableFiles += $file @@ -587,10 +581,10 @@ Write-Log "==========================================" foreach ($file in $RevertableFiles) { Write-Log " Reverting: $file" - git checkout $BaseBranch -- $file 2>&1 | Out-Null + git checkout $BaseBranchRef -- $file 2>&1 | Out-Null if ($LASTEXITCODE -ne 0) { - Write-Log " Warning: Could not revert from $BaseBranch, trying origin/$BaseBranch" - git checkout "origin/$BaseBranch" -- $file 2>&1 | Out-Null + Write-Log " ERROR: Failed to revert $file from $BaseBranchRef" + exit 1 } } From 7b7cbb1251436b50cc5b6a1808bb04072db9b7a0 Mon Sep 17 00:00:00 2001 From: Jakub Florkowski Date: Wed, 14 Jan 2026 00:05:48 +0100 Subject: [PATCH 4/9] Update SKILL.md with dual verification modes Expanded documentation to describe two workflow modes: 'Verify Failure Only' for test creation and 'Full Verification' for validating both tests and fixes. Added usage examples, clarified requirements, and detailed expected outputs for each mode to improve clarity for contributors. --- .../verify-tests-fail-without-fix/SKILL.md | 66 +++++++++++++++---- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/.github/skills/verify-tests-fail-without-fix/SKILL.md b/.github/skills/verify-tests-fail-without-fix/SKILL.md index 5aa853bea8e0..42d85a876529 100644 --- a/.github/skills/verify-tests-fail-without-fix/SKILL.md +++ b/.github/skills/verify-tests-fail-without-fix/SKILL.md @@ -1,43 +1,85 @@ --- name: verify-tests-fail-without-fix -description: Verifies UI tests catch the bug by checking tests FAIL without the fix and PASS with the fix. Requires fix files to be detected in the PR. +description: Verifies UI tests catch the bug. Supports two modes - verify failure only (test creation) or full verification (test + fix validation). --- # Verify Tests Fail Without Fix -Verifies UI tests actually catch the issue by running tests in two states: +Verifies UI tests actually catch the issue. Supports two workflow modes: + +## Mode 1: Verify Failure Only (Test Creation) + +Use when **creating tests before writing a fix**: +- Runs tests to verify they **FAIL** (proving they catch the bug) +- No fix files required +- Perfect for test-first development + +```bash +# Auto-detect test filter from changed test files +pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform android + +# With explicit test filter +pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform ios -TestFilter "Issue33356" +``` + +## Mode 2: Full Verification (Fix Validation) + +Use when **validating both tests and fix**: 1. **Without fix** - tests should FAIL (bug is present) 2. **With fix** - tests should PASS (bug is fixed) -## Usage - ```bash -# Auto-detects everything - just specify platform (recommended for skill invocation) -pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform android -RequireFullVerification +# Auto-detect everything (recommended) +pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform android # With explicit test filter -pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform ios -TestFilter "Issue33356" -RequireFullVerification +pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform ios -TestFilter "Issue33356" + +# Enforce full verification (error if no fix files) +pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform android -RequireFullVerification ``` ## Requirements -This skill requires: -- **Fix files** in the PR (non-test code changes) -- **Test files** in the PR (to verify against) +**Verify Failure Only Mode:** +- Test files in the PR (or working directory) -If no fix files are detected, the skill will error out with troubleshooting guidance. +**Full Verification Mode:** +- Test files in the PR +- Fix files in the PR (non-test code changes) + +The script auto-detects which mode to use based on whether fix files are present. ## Expected Output +**Verify Failure Only Mode:** ``` ╔═══════════════════════════════════════════════════════════╗ ║ VERIFICATION PASSED ✅ ║ ╠═══════════════════════════════════════════════════════════╣ -║ - FAIL without fix (as expected) ║ +║ Tests FAILED as expected! ║ +║ This proves the tests correctly reproduce the bug. ║ +╚═══════════════════════════════════════════════════════════╝ +``` + +**Full Verification Mode:** +``` +╔═══════════════════════════════════════════════════════════╗ +║ Tests pass (in failure-only mode) | Tests don't detect the bug | Review test assertions, update test | +| Tests pass without fix (full mode) | Tests don't detect the bug | Review test assertions, update test | +| Tests fail with fix (full mode) | Fix doesn't work or test is wrong | Review fix implementation | +| No fix files with `-RequireFullVerification` | No non-test files changed or base branch detection failed | Use `-FixFiles` or `-BaseBranch` explicitly, or remove `-RequireFullVerification` ║ - PASS with fix (as expected) ║ ╚═══════════════════════════════════════════════════════════╝ ``` +**Verify Failure Only Mode (no fix files):** +1. Fetches base branch from origin (if available) +2. Auto-detects test classes from changed test files +3. Runs tests (should FAIL to prove they catch the bug) +4. Reports result + +**Full Verification Mode (fix files detected):** ## Troubleshooting | Problem | Cause | Solution | From 3204cec650447101ad38b6621273e076e75c561b Mon Sep 17 00:00:00 2001 From: Jakub Florkowski Date: Wed, 14 Jan 2026 00:12:29 +0100 Subject: [PATCH 5/9] Update usage examples for verify-tests-fail script Added the -RequireFullVerification flag to all usage examples for consistency and clarified its purpose in the documentation. --- .github/skills/verify-tests-fail-without-fix/SKILL.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/skills/verify-tests-fail-without-fix/SKILL.md b/.github/skills/verify-tests-fail-without-fix/SKILL.md index 42d85a876529..de9ba7800059 100644 --- a/.github/skills/verify-tests-fail-without-fix/SKILL.md +++ b/.github/skills/verify-tests-fail-without-fix/SKILL.md @@ -30,15 +30,14 @@ Use when **validating both tests and fix**: ```bash # Auto-detect everything (recommended) -pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform android +pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform android -RequireFullVerification # With explicit test filter -pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform ios -TestFilter "Issue33356" - -# Enforce full verification (error if no fix files) -pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform android -RequireFullVerification +pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform ios -TestFilter "Issue33356" -RequireFullVerification ``` +**Note:** `-RequireFullVerification` ensures the script errors if no fix files are detected, preventing silent fallback to failure-only mode. + ## Requirements **Verify Failure Only Mode:** From 8a732b77b6aaf666906651cc1b9988e2c4cc68d3 Mon Sep 17 00:00:00 2001 From: Jakub Florkowski Date: Wed, 14 Jan 2026 00:30:06 +0100 Subject: [PATCH 6/9] Update script to support verify failure only mode Enhanced the verify-tests-fail.ps1 script to support two modes: verify failure only (when no fix files are detected) and full verification (when fix files are present). Updated documentation to clarify usage, parameters, and examples for both modes, improving usability and clarity. --- .../scripts/verify-tests-fail.ps1 | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 b/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 index b892c2384807..66c05bd9d5a7 100644 --- a/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 +++ b/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 @@ -1,16 +1,23 @@ #!/usr/bin/env pwsh <# .SYNOPSIS - Verifies that UI tests catch the bug by running tests without and with the fix. + Verifies that UI tests catch the bug. Supports two modes: verify failure only or full verification. .DESCRIPTION - This script verifies that tests actually catch the issue by: + This script verifies that tests actually catch the issue. It supports two modes: + + VERIFY FAILURE ONLY MODE (no fix files detected): + - Runs tests to verify they FAIL (proving they catch the bug) + - Used when creating tests before writing a fix + + FULL VERIFICATION MODE (fix files detected): 1. Reverting fix files to base branch 2. Running tests WITHOUT fix (should FAIL) 3. Restoring fix files 4. Running tests WITH fix (should PASS) - Fix files are auto-detected from the git diff (non-test files that changed). + The script auto-detects which mode to use based on whether fix files are present. + Fix files and test filters are auto-detected from the git diff (non-test files that changed). .PARAMETER Platform Target platform: "android" or "ios" @@ -21,7 +28,7 @@ .PARAMETER FixFiles (Optional) Array of file paths to revert. If not provided, auto-detects from git diff - by excluding test directories. + by excluding test directories. If no fix files are found, runs in verify failure only mode. .PARAMETER BaseBranch Branch to revert files from. Auto-detected from PR if not specified. @@ -31,19 +38,20 @@ .PARAMETER RequireFullVerification If set, the script will fail if it cannot run full verification mode - (i.e., if no fix files are detected). Used by the skill to ensure proper validation. + (i.e., if no fix files are detected). Without this flag, the script will + automatically run in verify failure only mode when no fix files are found. .EXAMPLE - # Auto-detect everything - simplest usage + # Verify failure only mode - tests should fail (test creation workflow) ./verify-tests-fail.ps1 -Platform android .EXAMPLE - # Specify test filter, auto-detect mode and fix files - ./verify-tests-fail.ps1 -Platform android -TestFilter "Issue32030" + # Full verification mode - require fix files to be present + ./verify-tests-fail.ps1 -Platform android -RequireFullVerification .EXAMPLE - # Require full verification (fail if no fix files detected) - ./verify-tests-fail.ps1 -Platform android -RequireFullVerification + # Specify test filter explicitly (works in both modes) + ./verify-tests-fail.ps1 -Platform android -TestFilter "Issue32030" .EXAMPLE # Specify everything explicitly From a68132c2e2341b935163e06c6ac0132182498fa8 Mon Sep 17 00:00:00 2001 From: Jakub Florkowski Date: Wed, 14 Jan 2026 00:34:59 +0100 Subject: [PATCH 7/9] Update test verification command in PR guide Adds the -RequireFullVerification flag to the PowerShell test verification command in the PR documentation to ensure full verification mode is used. --- .github/agents/pr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/agents/pr.md b/.github/agents/pr.md index b2c6fbe67805..c7d4d8600e12 100644 --- a/.github/agents/pr.md +++ b/.github/agents/pr.md @@ -407,7 +407,7 @@ Tests were already verified to FAIL in Phase 2. Gate is a confirmation step: Use full verification mode - tests should FAIL without fix, PASS with fix. ```bash -pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform android +pwsh .github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 -Platform android -RequireFullVerification ``` ### Expected Output (PR with fix) From 4c3f45c46d87987008c4628f9b5988b7833df059 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Wed, 14 Jan 2026 15:27:54 -0600 Subject: [PATCH 8/9] Fix SKILL.md formatting issues - Fix corrupted ASCII box output in Full Verification Mode section - Add complete 'What It Does' section with both mode workflows - Remove duplicate section that was after Troubleshooting --- .../verify-tests-fail-without-fix/SKILL.md | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/.github/skills/verify-tests-fail-without-fix/SKILL.md b/.github/skills/verify-tests-fail-without-fix/SKILL.md index de9ba7800059..37f9a649cf0e 100644 --- a/.github/skills/verify-tests-fail-without-fix/SKILL.md +++ b/.github/skills/verify-tests-fail-without-fix/SKILL.md @@ -64,14 +64,15 @@ The script auto-detects which mode to use based on whether fix files are present **Full Verification Mode:** ``` ╔═══════════════════════════════════════════════════════════╗ -║ Tests pass (in failure-only mode) | Tests don't detect the bug | Review test assertions, update test | -| Tests pass without fix (full mode) | Tests don't detect the bug | Review test assertions, update test | -| Tests fail with fix (full mode) | Fix doesn't work or test is wrong | Review fix implementation | -| No fix files with `-RequireFullVerification` | No non-test files changed or base branch detection failed | Use `-FixFiles` or `-BaseBranch` explicitly, or remove `-RequireFullVerification` +║ VERIFICATION PASSED ✅ ║ +╠═══════════════════════════════════════════════════════════╣ +║ - FAIL without fix (as expected) ║ ║ - PASS with fix (as expected) ║ ╚═══════════════════════════════════════════════════════════╝ ``` +## What It Does + **Verify Failure Only Mode (no fix files):** 1. Fetches base branch from origin (if available) 2. Auto-detects test classes from changed test files @@ -79,6 +80,15 @@ The script auto-detects which mode to use based on whether fix files are present 4. Reports result **Full Verification Mode (fix files detected):** +1. Fetches base branch from origin to ensure accurate diff +2. Auto-detects fix files (non-test code) from git diff +3. Auto-detects test classes from `TestCases.Shared.Tests/*.cs` +4. Reverts fix files to base branch +5. Runs tests (should FAIL without fix) +6. Restores fix files +7. Runs tests (should PASS with fix) +8. Reports result + ## Troubleshooting | Problem | Cause | Solution | @@ -89,17 +99,6 @@ The script auto-detects which mode to use based on whether fix files are present | App crashes | Duplicate issue numbers, XAML error | Check device logs | | Element not found | Wrong AutomationId, app crashed | Verify IDs match | -## What It Does - -1. Fetches base branch from origin to ensure accurate diff -2. Auto-detects fix files (non-test code) from git diff -3. Auto-detects test classes from `TestCases.Shared.Tests/*.cs` -4. Reverts fix files to base branch -5. Runs tests (should FAIL without fix) -6. Restores fix files -7. Runs tests (should PASS with fix) -8. Reports result - ## Optional Parameters ```bash From e9f1d3ac7f556242d7bf653238e5ced6c2ddd970 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Wed, 14 Jan 2026 18:28:26 -0600 Subject: [PATCH 9/9] Improve verify-tests-fail script with fork-aware detection - Add Find-MergeBase function using git merge-base for robust base detection - Fetch directly from PR target repo URL (parsed from PR URL) - Works even if developer's fork main is out of sync - No need for upstream remote configuration - Extract duplicated test filter detection into Get-AutoDetectedTestFilter - Extract test result parsing into Get-TestResultFromLog - Fix 'Failed: 0' edge case in test result parsing - Use single MergeBase SHA instead of tracking branch names/refs --- .../scripts/verify-tests-fail.ps1 | 513 ++++++++++-------- 1 file changed, 285 insertions(+), 228 deletions(-) diff --git a/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 b/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 index 66c05bd9d5a7..8250d5314b86 100644 --- a/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 +++ b/.github/skills/verify-tests-fail-without-fix/scripts/verify-tests-fail.ps1 @@ -101,7 +101,7 @@ $TestPathPatterns = @( # Function to check if a file should be excluded from fix files function Test-IsTestFile { param([string]$FilePath) - + foreach ($pattern in $TestPathPatterns) { if ($FilePath -like $pattern) { return $true @@ -111,60 +111,253 @@ function Test-IsTestFile { } # ============================================================ -# AUTO-DETECT MODE: Check if there are fix files to revert +# Find the merge-base commit (where current branch diverged from base) +# This is more robust than tracking branch names/refs +# For fork workflows: fetches directly from the PR's target repo URL +# so it works even if the fork's main branch is out of sync # ============================================================ +function Find-MergeBase { + param([string]$ExplicitBaseBranch) + + # 1. If explicit base branch provided, use it directly + if ($ExplicitBaseBranch) { + # Try with origin/ prefix first, then without + foreach ($ref in @("origin/$ExplicitBaseBranch", $ExplicitBaseBranch)) { + $mergeBase = git merge-base HEAD $ref 2>$null + if ($mergeBase) { + return @{ MergeBase = $mergeBase; BaseBranch = $ExplicitBaseBranch; Source = "explicit" } + } + } + } -# Try to detect base branch -$BaseBranchDetected = $BaseBranch -if (-not $BaseBranchDetected) { - $currentBranch = git rev-parse --abbrev-ref HEAD 2>$null - - # Try gh pr view without --repo first (works for PRs from forks to upstream) - $BaseBranchDetected = gh pr view --json baseRefName --jq '.baseRefName' 2>$null - - # If that fails, try with the origin remote's repo - if (-not $BaseBranchDetected) { - $remoteUrl = git remote get-url origin 2>$null - $repo = $null - if ($remoteUrl -match "github\.com[:/]([^/]+/[^/]+?)(\.git)?$") { - $repo = $matches[1] + # 2. Try to get PR metadata including the TARGET repository + # This is critical for fork workflows where origin points to the fork, + # not the upstream repo. We fetch directly from the target repo URL. + # The PR URL contains the target repo: https://github.com/OWNER/REPO/pull/123 + $prJson = gh pr view --json baseRefName,url 2>$null + if ($prJson) { + $prInfo = $prJson | ConvertFrom-Json + $prBaseBranch = $prInfo.baseRefName + $prUrl = $prInfo.url + + # Parse owner/repo from PR URL: https://github.com/OWNER/REPO/pull/123 + $targetOwner = $null + $targetRepo = $null + if ($prUrl -match "github\.com/([^/]+)/([^/]+)/pull/") { + $targetOwner = $matches[1] + $targetRepo = $matches[2] } - - if ($repo) { - $BaseBranchDetected = gh pr view $currentBranch --repo $repo --json baseRefName --jq '.baseRefName' 2>$null + + if ($prBaseBranch -and $targetOwner -and $targetRepo) { + # Construct the target repo URL and fetch directly from it + # This works even if the developer hasn't set up an 'upstream' remote + # and even if their fork's main is completely out of sync + $targetUrl = "https://github.com/$targetOwner/$targetRepo.git" + Write-Host "ℹ️ PR targets $targetOwner/$targetRepo - fetching $prBaseBranch from upstream..." -ForegroundColor Cyan + git fetch $targetUrl $prBaseBranch 2>$null + + if ($LASTEXITCODE -eq 0) { + # FETCH_HEAD now points to the target repo's base branch + $mergeBase = git merge-base HEAD FETCH_HEAD 2>$null + if ($mergeBase) { + return @{ MergeBase = $mergeBase; BaseBranch = $prBaseBranch; Source = "pr-target-repo"; TargetRepo = "$targetOwner/$targetRepo" } + } + } + } + + # Fallback: try fetching from origin (works if origin IS the target repo) + if ($prBaseBranch) { + git fetch origin $prBaseBranch 2>$null + foreach ($ref in @("origin/$prBaseBranch", $prBaseBranch)) { + $mergeBase = git merge-base HEAD $ref 2>$null + if ($mergeBase) { + return @{ MergeBase = $mergeBase; BaseBranch = $prBaseBranch; Source = "pr-metadata" } + } + } } } + + # 3. Fallback: Find closest merge-base among common base branch patterns + # The "correct" base is the one with fewest commits between merge-base and HEAD + Write-Host "ℹ️ No PR detected, scanning remote branches for closest base..." -ForegroundColor Cyan + + # Fetch all remote refs to ensure we have latest + git fetch origin 2>$null + + # Get remote branches matching common base branch patterns + $remoteBranches = git branch -r --format='%(refname:short)' 2>$null | Where-Object { + $_ -match '^origin/(main|master|net\d+\.\d+|release/.*)$' + } + + $bestMatch = $null + $shortestDistance = [int]::MaxValue + + foreach ($branch in $remoteBranches) { + $mergeBase = git merge-base HEAD $branch 2>$null + if ($mergeBase) { + $distance = [int](git rev-list --count "$mergeBase..HEAD" 2>$null) + if ($distance -lt $shortestDistance) { + $shortestDistance = $distance + $branchName = $branch -replace '^origin/', '' + $bestMatch = @{ MergeBase = $mergeBase; BaseBranch = $branchName; Source = "closest-merge-base"; Distance = $distance } + } + } + } + + return $bestMatch } -# Fetch the base branch from origin to ensure we have the latest -# This ensures accurate diff detection even if local branch is stale -$BaseBranchRef = $BaseBranchDetected -if ($BaseBranchDetected) { - Write-Host "ℹ️ Fetching latest $BaseBranchDetected from origin..." -ForegroundColor Cyan - git fetch origin "${BaseBranchDetected}:${BaseBranchDetected}" 2>$null - if ($LASTEXITCODE -ne 0) { - # If direct fetch fails (e.g., currently on that branch), just fetch the ref - # In this case, use origin/$BaseBranchDetected for comparisons since local wasn't updated - git fetch origin $BaseBranchDetected 2>$null - $BaseBranchRef = "origin/$BaseBranchDetected" +# ============================================================ +# Auto-detect test filter from changed files +# ============================================================ +function Get-AutoDetectedTestFilter { + param([string]$MergeBase) + + $changedFiles = @() + if ($MergeBase) { + $changedFiles = git diff $MergeBase HEAD --name-only 2>$null + } + + # If no merge-base, use git status to find unstaged/staged files + if (-not $changedFiles -or $changedFiles.Count -eq 0) { + $changedFiles = git diff --name-only 2>$null + if (-not $changedFiles -or $changedFiles.Count -eq 0) { + $changedFiles = git diff --cached --name-only 2>$null + } + } + + # Find test files (files in test directories that are .cs files) + $testFiles = @() + foreach ($file in $changedFiles) { + if ($file -match "TestCases\.(Shared\.Tests|HostApp).*\.cs$" -and $file -notmatch "^_") { + $testFiles += $file + } + } + + if ($testFiles.Count -eq 0) { + return $null + } + + # Extract class names from test files + $testClassNames = @() + foreach ($file in $testFiles) { + if ($file -match "TestCases\.Shared\.Tests.*\.cs$") { + $fullPath = Join-Path $RepoRoot $file + if (Test-Path $fullPath) { + $content = Get-Content $fullPath -Raw + if ($content -match "public\s+(partial\s+)?class\s+(\w+)") { + $className = $matches[2] + if ($className -notmatch "^_" -and $testClassNames -notcontains $className) { + $testClassNames += $className + } + } + } + } + } + + # Fallback: use file names without extension + if ($testClassNames.Count -eq 0) { + foreach ($file in $testFiles) { + $fileName = [System.IO.Path]::GetFileNameWithoutExtension($file) + if ($fileName -notmatch "^_" -and $testClassNames -notcontains $fileName) { + $testClassNames += $fileName + } + } + } + + if ($testClassNames.Count -eq 0) { + return $null + } + + return @{ + Filter = if ($testClassNames.Count -eq 1) { $testClassNames[0] } else { $testClassNames -join "|" } + ClassNames = $testClassNames + } +} + +# ============================================================ +# Parse test results from log file +# ============================================================ +function Get-TestResultFromLog { + param([string]$LogFile) + + if (-not (Test-Path $LogFile)) { + return @{ Passed = $false; Error = "Test output log not found: $LogFile" } } + + $content = Get-Content $LogFile -Raw + + # Check for failures first - but only if count > 0 + if ($content -match "Failed:\s*(\d+)") { + $failCount = [int]$matches[1] + if ($failCount -gt 0) { + return @{ Passed = $false; FailCount = $failCount } + } + } + + # Check for passes + if ($content -match "Passed:\s*(\d+)") { + $passCount = [int]$matches[1] + if ($passCount -gt 0) { + return @{ Passed = $true; PassCount = $passCount } + } + } + + return @{ Passed = $false; Error = "Could not parse test results" } +} + +# ============================================================ +# AUTO-DETECT MODE: Find merge-base and fix files +# ============================================================ + +Write-Host "" +Write-Host "🔍 Detecting base branch and merge point..." -ForegroundColor Cyan + +$baseInfo = Find-MergeBase -ExplicitBaseBranch $BaseBranch + +if (-not $baseInfo) { + Write-Host "" + Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Red + Write-Host "║ ERROR: COULD NOT FIND MERGE BASE ║" -ForegroundColor Red + Write-Host "╠═══════════════════════════════════════════════════════════╣" -ForegroundColor Red + Write-Host "║ Could not determine where this branch diverged from. ║" -ForegroundColor Red + Write-Host "║ ║" -ForegroundColor Red + Write-Host "║ Tried: ║" -ForegroundColor Red + Write-Host "║ - PR metadata (gh pr view) ║" -ForegroundColor Red + Write-Host "║ - Common base branches (main, net*.0, release/*) ║" -ForegroundColor Red + Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Red + Write-Host "" + Write-Host "To fix, specify -BaseBranch explicitly:" -ForegroundColor Cyan + Write-Host " ./verify-tests-fail.ps1 -Platform android -BaseBranch main" -ForegroundColor White + exit 1 +} + +$MergeBase = $baseInfo.MergeBase +$BaseBranchName = $baseInfo.BaseBranch + +if ($baseInfo.TargetRepo) { + Write-Host "✅ PR target: $($baseInfo.TargetRepo) ($BaseBranchName branch)" -ForegroundColor Green +} else { + Write-Host "✅ Base branch: $BaseBranchName (via $($baseInfo.Source))" -ForegroundColor Green +} +Write-Host "✅ Merge base commit: $($MergeBase.Substring(0, 8))" -ForegroundColor Green +if ($baseInfo.Distance) { + Write-Host " ($($baseInfo.Distance) commits ahead of $BaseBranchName)" -ForegroundColor Gray } -# Check for fix files (non-test files that changed) +# Check for fix files (non-test files that changed since merge-base) $DetectedFixFiles = @() -if ($BaseBranchDetected) { - $changedFiles = git diff $BaseBranchRef HEAD --name-only 2>$null - - if ($changedFiles) { - foreach ($file in $changedFiles) { - if (-not (Test-IsTestFile $file)) { - $DetectedFixFiles += $file - } +$changedFiles = git diff $MergeBase HEAD --name-only 2>$null + +if ($changedFiles) { + foreach ($file in $changedFiles) { + if (-not (Test-IsTestFile $file)) { + $DetectedFixFiles += $file } } } -# Also check explicitly provided fix files +# Override with explicitly provided fix files if ($FixFiles -and $FixFiles.Count -gt 0) { $DetectedFixFiles = $FixFiles } @@ -178,19 +371,18 @@ if ($DetectedFixFiles.Count -eq 0 -and $RequireFullVerification) { Write-Host "║ Full verification mode required but no fix files found. ║" -ForegroundColor Red Write-Host "║ ║" -ForegroundColor Red Write-Host "║ Possible causes: ║" -ForegroundColor Red - Write-Host "║ - Base branch detection failed ║" -ForegroundColor Red - Write-Host "║ - No non-test files changed in this PR ║" -ForegroundColor Red - Write-Host "║ - Git fetch failed to update local branch ║" -ForegroundColor Red + Write-Host "║ - No non-test files changed since merge-base ║" -ForegroundColor Red + Write-Host "║ - All changes are in test directories ║" -ForegroundColor Red Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Red Write-Host "" Write-Host "Debug info:" -ForegroundColor Yellow - Write-Host " Base branch detected: '$BaseBranchDetected'" -ForegroundColor Yellow + Write-Host " Merge base: $MergeBase" -ForegroundColor Yellow + Write-Host " Base branch: $BaseBranchName" -ForegroundColor Yellow Write-Host " Current branch: $(git rev-parse --abbrev-ref HEAD)" -ForegroundColor Yellow Write-Host "" Write-Host "To fix, try one of:" -ForegroundColor Cyan Write-Host " 1. Specify fix files explicitly: -FixFiles @('path/to/fix.cs')" -ForegroundColor White - Write-Host " 2. Specify base branch: -BaseBranch 'main'" -ForegroundColor White - Write-Host " 3. Ensure you're on a PR branch with non-test file changes" -ForegroundColor White + Write-Host " 2. Remove -RequireFullVerification to run in failure-only mode" -ForegroundColor White exit 1 } @@ -203,97 +395,37 @@ if ($DetectedFixFiles.Count -eq 0) { Write-Host "║ No fix files detected - will only verify: ║" -ForegroundColor Cyan Write-Host "║ 1. Tests FAIL (proving they catch the bug) ║" -ForegroundColor Cyan Write-Host "║ ║" -ForegroundColor Cyan - Write-Host "║ Use this mode when creating tests before writing a fix. ║" -ForegroundColor Cyan + Write-Host "║ Use this mode when creating tests before writing a fix. ║" -ForegroundColor Cyan Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Cyan Write-Host "" - + # Auto-detect test filter if not provided if (-not $TestFilter) { Write-Host "🔍 Auto-detecting test filter from changed test files..." -ForegroundColor Cyan - - $changedFiles = @() - if ($BaseBranchRef) { - $changedFiles = git diff $BaseBranchRef HEAD --name-only 2>$null - } - - # If no base branch, use git status to find unstaged/staged files - if (-not $changedFiles -or $changedFiles.Count -eq 0) { - $changedFiles = git diff --name-only 2>$null - if ($changedFiles -and $changedFiles.Count -gt 0) { - $changedFiles = @($changedFiles) - } else { - $changedFiles = git diff --cached --name-only 2>$null - } - } - - # Find test files (files in test directories that are .cs files) - $testFiles = @() - foreach ($file in $changedFiles) { - if ($file -match "TestCases\.(Shared\.Tests|HostApp).*\.cs$" -and $file -notmatch "^_") { - $testFiles += $file - } - } - - if ($testFiles.Count -eq 0) { + $filterResult = Get-AutoDetectedTestFilter -MergeBase $MergeBase + + if (-not $filterResult) { Write-Host "❌ Could not auto-detect test filter. No test files found in changed files." -ForegroundColor Red Write-Host " Looking for files matching: TestCases.(Shared.Tests|HostApp)/*.cs" -ForegroundColor Yellow Write-Host " Please provide -TestFilter parameter explicitly." -ForegroundColor Yellow exit 1 } - - # Extract class names from test files - $testClassNames = @() - foreach ($file in $testFiles) { - if ($file -match "TestCases\.Shared\.Tests.*\.cs$") { - $fullPath = Join-Path $RepoRoot $file - if (Test-Path $fullPath) { - $content = Get-Content $fullPath -Raw - if ($content -match "public\s+(partial\s+)?class\s+(\w+)") { - $className = $matches[2] - if ($className -notmatch "^_" -and $testClassNames -notcontains $className) { - $testClassNames += $className - } - } - } - } - } - - # Fallback: use file names without extension - if ($testClassNames.Count -eq 0) { - foreach ($file in $testFiles) { - $fileName = [System.IO.Path]::GetFileNameWithoutExtension($file) - if ($fileName -notmatch "^_" -and $testClassNames -notcontains $fileName) { - $testClassNames += $fileName - } - } - } - - if ($testClassNames.Count -eq 0) { - Write-Host "❌ Could not extract test class names from changed files." -ForegroundColor Red - Write-Host " Please provide -TestFilter parameter explicitly." -ForegroundColor Yellow - exit 1 - } - - if ($testClassNames.Count -eq 1) { - $TestFilter = $testClassNames[0] - } else { - $TestFilter = $testClassNames -join "|" - } - - Write-Host "✅ Auto-detected $($testClassNames.Count) test class(es):" -ForegroundColor Green - foreach ($name in $testClassNames) { + + $TestFilter = $filterResult.Filter + Write-Host "✅ Auto-detected $($filterResult.ClassNames.Count) test class(es):" -ForegroundColor Green + foreach ($name in $filterResult.ClassNames) { Write-Host " - $name" -ForegroundColor White } Write-Host " Filter: $TestFilter" -ForegroundColor Cyan } - + # Create output directory $OutputPath = Join-Path $RepoRoot $OutputDir New-Item -ItemType Directory -Force -Path $OutputPath | Out-Null - + $ValidationLog = Join-Path $OutputPath "verification-log.txt" $TestLog = Join-Path $OutputPath "test-failure-verification.log" - + # Initialize log "" | Set-Content $ValidationLog "=========================================" | Add-Content $ValidationLog @@ -301,41 +433,27 @@ if ($DetectedFixFiles.Count -eq 0) { "=========================================" | Add-Content $ValidationLog "Platform: $Platform" | Add-Content $ValidationLog "TestFilter: $TestFilter" | Add-Content $ValidationLog + "MergeBase: $MergeBase" | Add-Content $ValidationLog "" | Add-Content $ValidationLog - + Write-Host "🧪 Running tests (expecting them to FAIL)..." -ForegroundColor Cyan Write-Host "" - + # Use shared BuildAndRunHostApp.ps1 infrastructure with -Rebuild to ensure clean builds $buildScript = Join-Path $RepoRoot ".github/scripts/BuildAndRunHostApp.ps1" & $buildScript -Platform $Platform -TestFilter $TestFilter -Rebuild 2>&1 | Tee-Object -FilePath $TestLog - - # Parse test results + + # Parse test results using shared function $testOutputLog = Join-Path $RepoRoot "CustomAgentLogsTmp/UITests/test-output.log" - $testResult = @{ Passed = $false; Error = $null } - - if (Test-Path $testOutputLog) { - $content = Get-Content $testOutputLog -Raw - if ($content -match "Failed:\s*(\d+)") { - $testResult.Passed = $false - $testResult.FailCount = [int]$matches[1] - } elseif ($content -match "Passed:\s*(\d+)") { - $testResult.Passed = $true - $testResult.PassCount = [int]$matches[1] - } else { - $testResult.Error = "Could not parse test results" - } - } else { - $testResult.Error = "Test output log not found: $testOutputLog" - } - + $testResult = Get-TestResultFromLog -LogFile $testOutputLog + # Evaluate results Write-Host "" Write-Host "==========================================" Write-Host "VERIFICATION RESULTS" Write-Host "==========================================" Write-Host "" - + if ($testResult.Error) { Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Red Write-Host "║ ERROR PARSING TEST RESULTS ║" -ForegroundColor Red @@ -344,7 +462,7 @@ if ($DetectedFixFiles.Count -eq 0) { Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Red exit 1 } - + if (-not $testResult.Passed) { # Tests FAILED - this is what we want! Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Green @@ -352,8 +470,8 @@ if ($DetectedFixFiles.Count -eq 0) { Write-Host "╠═══════════════════════════════════════════════════════════╣" -ForegroundColor Green Write-Host "║ Tests FAILED as expected! ║" -ForegroundColor Green Write-Host "║ ║" -ForegroundColor Green - Write-Host "║ This proves the tests correctly reproduce the bug. ║" -ForegroundColor Green - Write-Host "║ You can now proceed to write the fix. ║" -ForegroundColor Green + Write-Host "║ This proves the tests correctly reproduce the bug. ║" -ForegroundColor Green + Write-Host "║ You can now proceed to write the fix. ║" -ForegroundColor Green Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Green Write-Host "" Write-Host "Failed tests: $($testResult.FailCount)" -ForegroundColor Yellow @@ -365,7 +483,7 @@ if ($DetectedFixFiles.Count -eq 0) { Write-Host "╠═══════════════════════════════════════════════════════════╣" -ForegroundColor Red Write-Host "║ Tests PASSED but they should FAIL! ║" -ForegroundColor Red Write-Host "║ ║" -ForegroundColor Red - Write-Host "║ This means your tests don't actually reproduce the bug. ║" -ForegroundColor Red + Write-Host "║ This means your tests don't actually reproduce the bug. ║" -ForegroundColor Red Write-Host "║ ║" -ForegroundColor Red Write-Host "║ Possible causes: ║" -ForegroundColor Red Write-Host "║ 1. Test scenario doesn't match the issue description ║" -ForegroundColor Red @@ -395,10 +513,8 @@ Write-Host "║ 2. Tests PASS with fix ║" - Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Cyan Write-Host "" -$BaseBranch = $BaseBranchDetected $FixFiles = $DetectedFixFiles -Write-Host "✅ Base branch: $BaseBranch (using ref: $BaseBranchRef)" -ForegroundColor Green Write-Host "✅ Fix files ($($FixFiles.Count)):" -ForegroundColor Green foreach ($file in $FixFiles) { Write-Host " - $file" -ForegroundColor White @@ -407,65 +523,18 @@ foreach ($file in $FixFiles) { # Auto-detect test filter from test files if not provided if (-not $TestFilter) { Write-Host "🔍 Auto-detecting test filter from changed test files..." -ForegroundColor Cyan - - $changedFiles = git diff $BaseBranchRef HEAD --name-only 2>$null - - # Find test files (files in test directories that are .cs files) - $testFiles = @() - foreach ($file in $changedFiles) { - if ($file -match "TestCases\.(Shared\.Tests|HostApp).*\.cs$" -and $file -notmatch "^_") { - $testFiles += $file - } - } - - if ($testFiles.Count -eq 0) { + $filterResult = Get-AutoDetectedTestFilter -MergeBase $MergeBase + + if (-not $filterResult) { Write-Host "❌ Could not auto-detect test filter. No test files found in changed files." -ForegroundColor Red Write-Host " Looking for files matching: TestCases.(Shared.Tests|HostApp)/*.cs" -ForegroundColor Yellow Write-Host " Please provide -TestFilter parameter explicitly." -ForegroundColor Yellow exit 1 } - - # Extract class names from test files - $testClassNames = @() - foreach ($file in $testFiles) { - if ($file -match "TestCases\.Shared\.Tests.*\.cs$") { - $fullPath = Join-Path $RepoRoot $file - if (Test-Path $fullPath) { - $content = Get-Content $fullPath -Raw - if ($content -match "public\s+(partial\s+)?class\s+(\w+)") { - $className = $matches[2] - if ($className -notmatch "^_" -and $testClassNames -notcontains $className) { - $testClassNames += $className - } - } - } - } - } - - # Fallback: use file names without extension - if ($testClassNames.Count -eq 0) { - foreach ($file in $testFiles) { - $fileName = [System.IO.Path]::GetFileNameWithoutExtension($file) - if ($fileName -notmatch "^_" -and $testClassNames -notcontains $fileName) { - $testClassNames += $fileName - } - } - } - - if ($testClassNames.Count -eq 0) { - Write-Host "❌ Could not extract test class names from changed files." -ForegroundColor Red - Write-Host " Please provide -TestFilter parameter explicitly." -ForegroundColor Yellow - exit 1 - } - - if ($testClassNames.Count -eq 1) { - $TestFilter = $testClassNames[0] - } else { - $TestFilter = $testClassNames -join "|" - } - - Write-Host "✅ Auto-detected $($testClassNames.Count) test class(es):" -ForegroundColor Green - foreach ($name in $testClassNames) { + + $TestFilter = $filterResult.Filter + Write-Host "✅ Auto-detected $($filterResult.ClassNames.Count) test class(es):" -ForegroundColor Green + foreach ($name in $filterResult.ClassNames) { Write-Host " - $name" -ForegroundColor White } Write-Host " Filter: $TestFilter" -ForegroundColor Cyan @@ -487,20 +556,7 @@ function Write-Log { Add-Content -Path $ValidationLog -Value $logLine } -function Get-TestResult { - param([string]$LogFile) - - if (Test-Path $LogFile) { - $content = Get-Content $LogFile -Raw - if ($content -match "Failed:\s*(\d+)") { - return @{ Passed = $false; FailCount = [int]$matches[1] } - } - if ($content -match "Passed:\s*(\d+)") { - return @{ Passed = $true; PassCount = [int]$matches[1] } - } - } - return @{ Passed = $false; Error = "Could not parse test results" } -} +# Reuse the Get-TestResultFromLog function defined earlier # Initialize log "" | Set-Content $ValidationLog @@ -510,7 +566,8 @@ Write-Log "==========================================" Write-Log "Platform: $Platform" Write-Log "TestFilter: $TestFilter" Write-Log "FixFiles: $($FixFiles -join ', ')" -Write-Log "BaseBranch: $BaseBranch" +Write-Log "BaseBranch: $BaseBranchName" +Write-Log "MergeBase: $MergeBase" Write-Log "" # Verify fix files exist @@ -524,19 +581,19 @@ foreach ($file in $FixFiles) { Write-Log " ✓ $file exists" } -# Determine which files exist in the base branch (can be reverted) +# Determine which files exist at the merge-base (can be reverted) Write-Log "" -Write-Log "Checking which fix files exist in $BaseBranch..." +Write-Log "Checking which fix files exist at merge-base ($($MergeBase.Substring(0, 8)))..." $RevertableFiles = @() $NewFiles = @() foreach ($file in $FixFiles) { - # Check if file exists in base branch - $existsInBase = git ls-tree -r $BaseBranchRef --name-only -- $file 2>$null - + # Check if file exists at merge-base commit + $existsInBase = git ls-tree -r $MergeBase --name-only -- $file 2>$null + if ($existsInBase) { $RevertableFiles += $file - Write-Log " ✓ $file (exists in $BaseBranch - will revert)" + Write-Log " ✓ $file (exists at merge-base - will revert)" } else { $NewFiles += $file Write-Log " ○ $file (new file - skipping revert)" @@ -581,22 +638,22 @@ if ($uncommittedFiles.Count -gt 0) { Write-Log " ✓ All revertable fix files are committed" -# Step 1: Revert fix files to base branch +# Step 1: Revert fix files to merge-base state Write-Log "" Write-Log "==========================================" -Write-Log "STEP 1: Reverting fix files to $BaseBranch" +Write-Log "STEP 1: Reverting fix files to merge-base ($($MergeBase.Substring(0, 8)))" Write-Log "==========================================" foreach ($file in $RevertableFiles) { Write-Log " Reverting: $file" - git checkout $BaseBranchRef -- $file 2>&1 | Out-Null + git checkout $MergeBase -- $file 2>&1 | Out-Null if ($LASTEXITCODE -ne 0) { - Write-Log " ERROR: Failed to revert $file from $BaseBranchRef" + Write-Log " ERROR: Failed to revert $file from $MergeBase" exit 1 } } -Write-Log " ✓ $($RevertableFiles.Count) fix file(s) reverted to $BaseBranch state" +Write-Log " ✓ $($RevertableFiles.Count) fix file(s) reverted to merge-base state" # Step 2: Run tests WITHOUT fix Write-Log "" @@ -608,7 +665,7 @@ Write-Log "==========================================" $buildScript = Join-Path $RepoRoot ".github/scripts/BuildAndRunHostApp.ps1" & $buildScript -Platform $Platform -TestFilter $TestFilter -Rebuild 2>&1 | Tee-Object -FilePath $WithoutFixLog -$withoutFixResult = Get-TestResult -LogFile (Join-Path $RepoRoot "CustomAgentLogsTmp/UITests/test-output.log") +$withoutFixResult = Get-TestResultFromLog -LogFile (Join-Path $RepoRoot "CustomAgentLogsTmp/UITests/test-output.log") # Step 3: Restore fix files from current branch HEAD Write-Log "" @@ -635,7 +692,7 @@ Write-Log "==========================================" & $buildScript -Platform $Platform -TestFilter $TestFilter -Rebuild 2>&1 | Tee-Object -FilePath $WithFixLog -$withFixResult = Get-TestResult -LogFile (Join-Path $RepoRoot "CustomAgentLogsTmp/UITests/test-output.log") +$withFixResult = Get-TestResultFromLog -LogFile (Join-Path $RepoRoot "CustomAgentLogsTmp/UITests/test-output.log") # Step 5: Evaluate results Write-Log ""