Skip to content

[Testing] [macOS] Fix screenshot masking and scaling for Retina (high-DPI) displays - #35156

Draft
NafeelaNazhir wants to merge 18 commits into
dotnet:mainfrom
NafeelaNazhir:MacOS-snapshot-failure-fix
Draft

[Testing] [macOS] Fix screenshot masking and scaling for Retina (high-DPI) displays#35156
NafeelaNazhir wants to merge 18 commits into
dotnet:mainfrom
NafeelaNazhir:MacOS-snapshot-failure-fix

Conversation

@NafeelaNazhir

Copy link
Copy Markdown
Contributor

This pull request updates the macOS UI test pipeline configuration and improves the screenshot masking logic in UI tests to better support high-DPI (Retina) displays. The main changes ensure that screenshots are correctly masked and scaled regardless of display density, and that the pipeline uses the appropriate shared pool for macOS agents.

Screenshot logic improvements:

  • Updated the TakeScreenshot() method in UITest.cs to:
    • Detect the display scale factor by comparing screenshot pixel dimensions to logical screen size, supporting both standard and Retina displays.
    • Scale all mask and crop coordinates to pixel units, ensuring the rounded rectangle mask aligns correctly on high-DPI screens.
    • After masking, normalize the resulting image back to logical dimensions so downstream cropping logic remains consistent regardless of pixel density.

@github-actions

github-actions Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 35156

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35156"

@NafeelaNazhir NafeelaNazhir added community ✨ Community Contribution area-testing Unit tests, device tests labels Apr 27, 2026
@dotnet-policy-service dotnet-policy-service Bot added the partner/syncfusion Issues / PR's with Syncfusion collaboration label Apr 27, 2026
@Ahamed-Ali
Ahamed-Ali changed the base branch from main to AcesSharedMacOS April 28, 2026 05:33
@sheiksyedm

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines could not run because the pipeline triggers exclude this branch/path.

@Ahamed-Ali
Ahamed-Ali changed the base branch from AcesSharedMacOS to main April 28, 2026 07:15
@sheiksyedm

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@sheiksyedm

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@sheiksyedm

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

@sheiksyedm

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

MauiBot

This comment was marked as outdated.

@MauiBot MauiBot added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels May 8, 2026
@NafeelaNazhir
NafeelaNazhir force-pushed the MacOS-snapshot-failure-fix branch from 54dd6fa to 0a144f4 Compare May 11, 2026 14:20
kubaflo pushed a commit that referenced this pull request Jul 18, 2026
The A/B gate reverts committed fix files to build the without-fix
baseline, so it first refuses to run if any revertable fix file has
uncommitted changes (to avoid clobbering unstaged work). That guard used
a plain 'git status --porcelain', which reports an executable-bit-only
change (100644->100755) as ' M' — even though the file CONTENT is fully
committed and reverts cleanly via 'git checkout HEAD'.

On mac agents a setup step chmod +x's committed shell scripts
(eng/scripts/*.sh), tripping the guard and aborting all 3 retries with
'Uncommitted changes detected in fix files' -> a false INCONCLUSIVE
(observed build 14699093, #35156 catalyst: disable-/enable-notification-
center.sh flagged mode-only).

Use 'git -c core.fileMode=false status --porcelain' so an exec-bit-only
diff is ignored while a genuine content change is STILL caught (verified
locally: mode-only -> clean, content change -> flagged).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 15d2af20-e4ab-4e88-9011-cfbd83513bc0
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 18, 2026
kubaflo pushed a commit that referenced this pull request Jul 18, 2026
…s false INCONCLUSIVE on infra-only PRs)

The gate overlays TRUSTED review-branch copies of .github/scripts, .github/skills
and eng/scripts over the worktree for security (Review-PR.ps1 Restore-TrustedScripts).
A PR that itself modifies a file under those paths therefore ALWAYS shows it as an
uncommitted worktree change (trusted content != the PR's committed content), so the
uncommitted-fix-files guard aborted with a misleading 'Uncommitted changes detected /
run git add && commit' error that the caller treats as a missing-report infra failure
and retries 3x before a bare INCONCLUSIVE (build 14699515, #35156 catalyst:
eng/scripts/{disable,enable}-notification-center.sh).

Those files are also force-restored to the SAME trusted version in both the without-fix
and with-fix runs, so reverting them changes nothing - they are not A/B-testable. The
same holds for pipeline/workflow definitions (eng/pipelines, .github/workflows): the gate
runs on an already-checked-out pipeline, so editing those YAMLs in the worktree cannot
alter the gate's own execution.

Exclude all such paths from the fix-file set BEFORE the uncommitted guard. If real
product/test fix files remain, the A/B runs on those; if none remain, the change is
CI-infra-only and the gate emits a deterministic, non-retried INCONCLUSIVE (exit 3 +
report without the ENV ERROR token) that defers to the Deep UI Tests stage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 15d2af20-e4ab-4e88-9011-cfbd83513bc0
@kubaflo

This comment has been minimized.

MauiBot

This comment was marked as outdated.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 2 findings

See inline comments for details.

// App.Screenshot() returns pixels at the display's native resolution
// (1x on non-Retina, 2x on Retina displays).
// Strategy: Compare screenshot pixel dimensions to screen logical dimensions.
var screenSize = ((AppiumApp)App).Driver.Manage().Window.Size;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 AI-Generated Review (multi-model)

[critical] Logic and CorrectnessDriver.Manage().Window.Size is the current app window size, while App.Screenshot() is the full desktop screenshot on Mac. For a non-fullscreen Retina window, dividing full-screen pixels by app-window points can infer 3x/4x instead of the display scale, shifting the mask/crop to the wrong pixels. Use an actual display scale/screen logical size source, or derive the scale from matching screenshot/display dimensions rather than the app window.

@@ -16,7 +16,7 @@ public Issue33769(TestDevice device) : base(device)
[Category(UITestCategories.Stepper)]
public void ValidateStepperReachesMinMax()
{
App.WaitForElement("Issue33769_StepperStatusLabel");
App.WaitForElementTillPageNavigationSettled("Issue33769_StepperStatusLabel");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 AI-Generated Review (multi-model)

[major] Regression Prevention and Test Coverage — This added wait only settles the initial page load; the decrement half can still pass on stale state. After IncreaseStepper sets the shared status label to Success, a failed/no-op DecreaseStepper leaves the same text in place, so the second assertion does not prove the Stepper reached minimum. Reset the status/value before decrementing, or assert a distinct min-state/value after the decrement.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 19, 2026
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 19, 2026
MauiBot

This comment was marked as outdated.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 3 findings

See inline comments for details.

// App.Screenshot() returns pixels at the display's native resolution
// (1x on non-Retina, 2x on Retina displays).
// Strategy: Compare screenshot pixel dimensions to screen logical dimensions.
var screenSize = ((AppiumApp)App).Driver.Manage().Window.Size;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 AI-Generated Review (multi-model)

[major] Logic and CorrectnessDriver.Manage().Window.Size is the Appium app-window size, but App.Screenshot() on Mac captures the whole display. The ratio image.Width / screenSize.Width is only the display scale when the app window fills the screen; most screenshot tests do not call EnterFullScreen(). For a normal 800pt-wide window on a 3024px Retina display this computes about 3.8 and rounds to 4 instead of the real 2, so the subsequent mask/composite uses the wrong pxX/pxWidth and produces an incorrectly cropped screenshot. Use the actual display logical bounds/backing scale (or another native display-scale source) rather than the current app window size.

@@ -33,7 +36,7 @@ public void Issue33356NavigateShouldOccur()
App.TapBackArrow(Device == TestDevice.Android ? "" : "Cats");
}
App.WaitForElement("Issue33356CatsCollectionView");
App.WaitForElement("Abyssinian");
App.WaitForElementTillPageNavigationSettled("Abyssinian");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 AI-Generated Review (multi-model)

[major] Regression PreventionWaitForElementTillPageNavigationSettled(string) first performs a Catalyst-only WaitForElement(AppiumQuery.ById(elementId)) before falling back to the normal string wait semantics. Abyssinian is item text, not an AutomationId, so on Mac Catalyst this can time out before App.WaitForElement("Abyssinian") gets its text fallback. The same pattern was added for text/native labels such as DropCompleted and More; keep this helper to AutomationId-backed waits or make it use the same id-or-text lookup as WaitForElement(string).

// so match by identifier instead of relying on FirstOrDefault().
var decreaseButton = _appiumApp is AppiumCatalystApp
? buttons.FirstOrDefault(b =>
b.GetAttribute<string>("identifier")?.EndsWith("-Decrement", StringComparison.OrdinalIgnoreCase) == true)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 AI-Generated Review (multi-model)

[major] Logic and Correctness / Regression Prevention — If Catalyst reports a decrement button whose identifier does not end with -Decrement, this lookup returns null and decreaseButton?.Tap() silently no-ops while Decrease still returns success. Issue33769 already has Success in the status label after the increment assertion, so a missed decrement tap can make the second assertion pass without exercising the minimum-value path. Fail loudly (for example, throw when the Catalyst-specific button lookup is missing, since the helper ignores CommandResponse) instead of treating a missing button as a successful command.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 19, 2026
@vishnumenon2684

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@kubaflo

kubaflo commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Tests Failure Analysis

@NafeelaNazhir — test-failure review results are available based on commit d128e3d.

Overall Not ready Failures 33 Baseline 2 on base Platform macOS Android unknown

Test Failure Review: Not ready - click to expand

Overall verdict: Not ready

The PR is not CI-ready: 9 distinct failures are deterministic regressions where the same legs were green on all 5 sampled base builds and red on none. Only 2 of 33 distinct failures also appear on the base branch, and those two are still indeterminate because the gathered reason comparison refused a clean pre-existing dismissal.

Coverage: 153 checks · 146 passing · 7 failing · 0 pending · 0 inaccessible · 1 unmapped · 11 unexplained build legs · 0 unaccounted failing checks · 0 aborted failing checks · 0 canceled-build checks · 0 device-test unverified · 24 unattributed · 9 regressed-vs-base. Deterministic ceiling: Not ready — reasons: Build Analysis is unmapped; 11 failed build legs produced no extractable failure; 24 failures are unattributed; 9 failures regressed vs base.

Failure Verdict On base? Evidence
TitleBarWithLargeHeight Likely PR-caused regressed deterministicAttribution=regressed-vs-base; same leg was 5 green / 0 red across 5 sampled base builds, now failing in maui-pr-uitests build 1517753 with a visual snapshot mismatch.
TitleBarWithLargeHeightShell Likely PR-caused regressed deterministicAttribution=regressed-vs-base; same leg was 5 green / 0 red across 5 sampled base builds, now failing in maui-pr-uitests build 1517753 with a visual snapshot mismatch.
ChangeIncrementValue Likely PR-caused regressed deterministicAttribution=regressed-vs-base; same leg was 5 green / 0 red across 5 sampled base builds, now failing with Expected: True assertions.
GestureRecognizersOnLabelSpanShouldWork Likely PR-caused regressed deterministicAttribution=regressed-vs-base; same leg was 5 green / 0 red across 5 sampled base builds, now failing with Expected: True assertions.
Issue33274CheckInAndDeCrementation Likely PR-caused regressed deterministicAttribution=regressed-vs-base; same leg was 5 green / 0 red across 5 sampled base builds, now failing waiting for Maximumlabel to show 0.
ValidateStepperReachesMinMax Likely PR-caused regressed deterministicAttribution=regressed-vs-base; same leg was 5 green / 0 red across 5 sampled base builds, now failing waiting for the Stepper success label.
DecreaseStepper Likely PR-caused regressed deterministicAttribution=regressed-vs-base; same leg was 5 green / 0 red across 5 sampled base builds, now failing with Expected: True.
TextAndEntryCellsDataBindInTableView Likely PR-caused regressed deterministicAttribution=regressed-vs-base; same leg was 5 green / 0 red across 5 sampled base builds, now failing with System.NullReferenceException.
TableViewMemoryLeakWhenUsingSwitchCellOrEntryCell Likely PR-caused regressed deterministicAttribution=regressed-vs-base; same leg was 5 green / 0 red across 5 sampled base builds, now failing with System.NullReferenceException.
MenuFlyoutItem_ClickedEventWorks Needs human investigation flaky-on-base deterministicAttribution=indeterminate; leg was flaky on base and ci-scan #36419 tracks the leg as recurring, but there is no exact base dismissal.
MenuFlyoutItem_CommandWithParameterWorks Needs human investigation flaky-on-base deterministicAttribution=indeterminate; leg was flaky on base and ci-scan #36419 tracks the leg as recurring, but there is no exact base dismissal.
MenuFlyoutItem_CommandWorks Needs human investigation flaky-on-base deterministicAttribution=indeterminate; leg was flaky on base and ci-scan #36419 tracks the leg as recurring, but there is no exact base dismissal.
VerifyShellFlyout_* failures Needs human investigation flaky-on-base 8 Shell flyout rows are deterministicAttribution=indeterminate; the leg was flaky on base and ci-scan #36419 tracks the leg as recurring, but exact failures are not dismissible.
to find package 'platform-tools;35.0.2' (unknown and android) Needs human investigation also-red, reason conflict Both rows are deterministicAttribution=indeterminate; exact failures also appear on base, but baselineReasonConflict=true refused dismissal. ci-scan #36489 tracks the Android API 30 leg as recurring.
BorderWithRoundRectangleShouldRoundCornersOfContentWithinIt Needs human investigation flaky-on-base deterministicAttribution=indeterminate; Android API 30 leg was flaky on base and ci-scan #36489 tracks the leg as recurring, but this specific timeout is not exact-base dismissed.
ButtonsLayoutResolveWhenParentSizeChanges Needs human investigation flaky-on-base deterministicAttribution=indeterminate; visual snapshot mismatch on Android, leg was flaky on base, and ci-scan #36591 tracks this test as recurring.
Publish the mac_ui_tests_controls test results - build error Needs human investigation flaky-on-base Three publish-step rows are deterministicAttribution=indeterminate; the step reported test failures, but the same leg was flaky on base, so the wrapper failures are not independently attributable.
macOS DecreaseStepper, Issue33274CheckInAndDeCrementation, ChangeIncrementValue, ValidateStepperReachesMinMax, GestureRecognizersOnLabelSpanShouldWork, TableViewMemoryLeakWhenUsingSwitchCellOrEntryCell Needs human investigation no These test-result rows are deterministicAttribution=indeterminate and lack confirming leg diffs, although matching log-level rows include deterministic Stepper/TableView regressions.

Recommended action

Investigate and fix the macOS UI-test regressions first, especially the TitleBar visual mismatches and Stepper/TableView assertion/null-reference failures that are red on this PR but green across the sampled base builds; separately inspect the 11 unexplained failed legs and the unmapped Build Analysis check before relying on any rerun.

Evidence details
  • PR [Testing] [macOS] Fix screenshot masking and scaling for Retina (high-DPI) displays #35156 targets main from MacOS-snapshot-failure-fix at d128e3d6abeaadfa87ba3d3bdec9f2a4a2270d6f; labels include area-testing, community, partner/syncfusion, s/agent-reviewed, s/agent-changes-requested, and s/agent-fix-pr-picked.
  • Scope is test-heavy: 1490 changed files, including 1485 changed test files; area hints include UITests, CarouselView, Navigation, Layout, Shell, CollectionView, ScrollView, and Handler.
  • Failing checks: Build Analysis plus maui-pr-uitests build 1517753, including Android API 30 Border/BoxView/Brush/Button and several macOS Controls legs.
  • Baseline comparison used maui-pr-uitests base build 1503618 on main, which itself failed with 13 extracted baseline failures; only the first 8 of 11 failed base logs were inspected, so the baseline list may be incomplete.
  • Recent sampled base builds for the same definition were 1503618, 1503332, 1503036, 1499834, and 1499259.
  • Visual comparison metadata was present for ButtonsLayoutResolveWhenParentSizeChangesOriginal2.png on Android and TitleBarWithLargeHeight_LargeHeightRequest.png on macOS, but visual panels and image links are intentionally omitted here for the trusted local merger to add.
  • Known Build Error matching was available (queried=true, 1 matcher loaded) but no distinct failure had a corroborated known-issue attribution. ci-scan matching was available with 47 matchers; 15 failures matched base-branch history, and 0 regressions were demoted by ci-scan.
  • Limitation: local gathering used an Azure CLI bearer token for authenticated AzDO access; the gh-aw workflow relies on public build/timeline/log APIs unless AZDO_TOKEN is provided.

Visual failure comparisons

Full-resolution CI baseline, actual, and diff images are embedded below. They supplement the failure classification and do not change the deterministic verdict ceiling.
Relationship labels use deterministic exact test-and-platform baseline evidence plus exact changed snapshot/test scope; missing or mixed evidence remains Needs human investigation.

TitleBarWithLargeHeight - macos - Likely PR-caused - visual comparison

CI reported 2.19% difference in build 1517753.

Relationship to PR: Likely PR-caused - This PR changes the exact snapshot or visual test.

CI baselineFresh PR actualCI diff
TitleBarWithLargeHeight baseline TitleBarWithLargeHeight actual TitleBarWithLargeHeight diff
ButtonsLayoutResolveWhenParentSizeChanges - android - Needs human investigation - visual comparison

CI reported 1.85% difference in build 1517753.

Relationship to PR: Needs human investigation - No decisive exact test-and-platform baseline attribution was available.

CI baselineFresh PR actualCI diff
ButtonsLayoutResolveWhenParentSizeChanges baseline ButtonsLayoutResolveWhenParentSizeChanges actual ButtonsLayoutResolveWhenParentSizeChanges diff

@vishnumenon2684

Copy link
Copy Markdown
Contributor

/azp run maui-pr-uitests

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 23, 2026

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI Review Summary

@NafeelaNazhir — new AI review results are available based on this last commit: b4573f4. To request a fresh review after new comments or commits, comment /review rerun.

Gate Inconclusive Confidence Low Platform MacCatalyst


🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix

Gate: Test Verification

Result: ⚠️ INCONCLUSIVE

Platform: CATALYST

This PR only changes CI infrastructure the gate force-restores to trusted versions or cannot toggle at run time:

  • eng/pipelines/ci-uitests.yml
  • eng/scripts/disable-notification-center.sh
  • eng/scripts/enable-notification-center.sh

These paths are identical (trusted) in both the without-fix and with-fix runs, so the gate cannot construct a without-fix baseline and the change is not A/B-verifiable here. Its behaviour is validated end-to-end by the Deep UI Tests stage.


📱 UI Tests — Button,CarouselView,CollectionView,DisplayAlert,DragAndDrop,Entry,FlyoutPage,GraphicsView,ListView,Navigation,Picker,Shell,Stepper,TabbedPage,TableView,TitleView,ViewBaseTests,Window

Detected UI test categories: Button,CarouselView,CollectionView,DisplayAlert,DragAndDrop,Entry,FlyoutPage,GraphicsView,ListView,Navigation,Picker,Shell,Stepper,TabbedPage,TableView,TitleView,ViewBaseTests,Window

Deep UI tests — 807 passed, 4 failed across 16 categories on platform-pool agent (replaces in-process counts above). The deep UI run for 2 categories (CollectionView, Shell) exceeded the per-category time budget (a very long-running category or a slow build/deploy) and was stopped before finishing. This is an infrastructure/timeout issue, not a code problem; re-run the review, and if a category consistently needs more time the per-category budget can be raised (DEEP_UITEST_CATEGORY_CAP_MIN / DEEP_UITEST_HARDSTOP_MIN). See the build-output.log in the drop-deep-uitests artifact.

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Button 69/70 ✓
CarouselView 82/83 ✓
DisplayAlert 8/8 ✓
DragAndDrop 1/4 (3 ❌)
Entry 103/104 ✓
FlyoutPage 57/57 ✓
GraphicsView 45/45 ✓
ListView 92/93 ✓
Navigation 85/86 ✓
Picker 35/35 ✓
Stepper 29/29 ✓
TabbedPage 43/43 ✓
TableView 16/16 ✓
TitleView 29/29 ✓
ViewBaseTests 101/101 ✓
Window 12/13 (1 ❌)
🔍 AI analysis of failures — PR-related vs unrelated

🔍 AI-generated triage (GitHub Copilot CLI) — a heuristic judgement of whether each deep UI test failure is connected to this PR's changes. Verify before relying on it.

Likely unrelated: the failures appear pre-existing, flaky, or infrastructure.

  • ● Unrelated — Drag-and-drop UI navigation/setup timeouts (3 tests): the failures all timed out waiting for expected elements before/around the scenario (DragAndDropWithAnElementThatIsRemoved is representative), which matches a common Appium/Catalyst flakiness pattern and there is no available PR diff tying the changes to drag-and-drop behavior.
  • ● Unrelated — Catalyst title-bar visual baseline mismatch (1 test): the failure is a snapshot-vs-baseline delta for TitleBarWithLargeHeightShell, which is most consistent with a baseline/environment rendering mismatch absent evidence that this PR changed Catalyst title-bar rendering or snapshots.

Strongest signal: every failure matches a generic timeout or visual-baseline pattern, and the PR changed files/diff were unavailable to establish a PR-specific connection.

DragAndDrop — 3 failed tests
DragAndDropWithAnElementThatIsRemoved
System.TimeoutException : Timed out waiting for element...
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
   at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
   at UITest.Appium.HelperExtensions.WaitForElement(IApp app, IQuery query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 925
   at UITest.Appium.HelperExtensions.WaitForElementTillPageNavigationSettled(IApp app, String elementId, Nullable`1 timeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2408
   at Microsoft.Maui.TestCases.Tests.Issues.Issue28416.DragAndDropWithAnElementThatIsRemoved
...
AcceptedOperationNoneDisablesDropOperation
System.TimeoutException : Timed out waiting for element...
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
   at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
   at UITest.Appium.HelperExtensions.WaitForElement(IApp app, String marked, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 793
   at Microsoft.Maui.TestCases.Tests.Issues.Issue12060.AcceptedOperationNoneDisablesDropOperation() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue12060.cs:line 24
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at Sy
...
DropCompletedFiresWhenDroppingOnNonDropTarget
System.TimeoutException : Timed out waiting for element...
at UITest.Appium.HelperExtensions.Wait(Func`1 query, Func`2 satisfactory, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2757
   at UITest.Appium.HelperExtensions.WaitForAtLeastOne(Func`1 query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2784
   at UITest.Appium.HelperExtensions.WaitForElement(IApp app, IQuery query, String timeoutMessage, Nullable`1 timeout, Nullable`1 retryFrequency, Nullable`1 postTimeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 925
   at UITest.Appium.HelperExtensions.WaitForElementTillPageNavigationSettled(IApp app, String elementId, Nullable`1 timeout) in /_/src/TestUtils/src/UITest.Appium/HelperExtensions.cs:line 2408
   at Microsoft.Maui.TestCases.Tests.Issues.Issue17554.DropCompletedFiresWhenDroppingOnNonDr
...
Window — 1 failed test
TitleBarWithLargeHeightShell
VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: TitleBarWithLargeHeightShell_LargeHeightRequest.png (2.28% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.

More info: https://aka.ms/visual-test-workflow
at Microsoft.Maui.TestCases.Tests.Issues.Issue24489_Shell.TitleBarWithLargeHeightShell() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue24489_Shell.cs:line 77
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)


📋 Pre-Flight — Context & Validation

Issue: N/A - No linked issue found in PR body
PR: #35156 - [Testing] [macOS] Fix screenshot masking and scaling for Retina (high-DPI) displays
Platforms Affected: MacCatalyst / macOS UI tests
Files Changed: 6 implementation/config/test-source files, 1 pipeline file, many Mac snapshot baselines

Key Findings

  • PR changes shared MacCatalyst UI-test screenshot infrastructure in src/Controls/tests/TestCases.Shared.Tests/UITest.cs, macOS notification suppression scripts, HostApp/UI-test stabilization code, and regenerated Mac snapshot baselines.
  • The core PR fix derives screenshot scale from full-screen screenshot pixel dimensions divided by Appium app-window size; prior review and independent code review agree this is incorrect for non-fullscreen Retina windows.
  • Catalyst navigation-settling waits were introduced broadly, but the string overload first waits by AutomationId only on Catalyst, breaking callers that intentionally pass visible text such as DropCompleted and Abyssinian.
  • The Stepper regression test can still pass on stale "Success" text after the increment phase, so it may not verify the decrement-to-minimum path.
  • Existing gate result is inconclusive because tests could not be built/run; it must not be treated as a PR-fix failure.

Code Review Summary

Verdict: NEEDS_CHANGES
Confidence: low
Errors: 3 | Warnings: 1 | Suggestions: 0

Key code review findings:

  • src/Controls/tests/TestCases.Shared.Tests/UITest.cs:707-713 computes Retina scale from App.Screenshot() full-desktop pixels divided by Driver.Manage().Window.Size app-window points, which can inflate scale and crop the wrong region.
  • src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17554.cs:21 and Issue33356.cs:39 can time out on Catalyst because WaitForElementTillPageNavigationSettled(string) pre-waits with AppiumQuery.ById(...) before the normal id-or-text string lookup.
  • src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33769.cs:25-28 can pass after a no-op decrement because the label already says "Success" from the increment assertion.
  • Issue8529.cs:33 and XFIssue/Issue12320.cs:27 use hard-coded Catalyst back-button coordinates.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #35156 Scale Mac screenshots by comparing full screenshot pixels to Driver.Manage().Window.Size, add Catalyst wait stabilization, update snapshots and macOS CI setup ⚠️ INCONCLUSIVE (Gate build/environment error) UITest.cs, UI tests, HostApp, pipeline/scripts, snapshots Original PR; code review found unresolved correctness issues

🔬 Code Review — Deep Analysis

Code Review — PR #35156

Independent Assessment

What this changes: Updates MacCatalyst UI-test infrastructure: switches public macOS UITests to an ACES Tahoe pool, suppresses macOS dialogs/notifications, forces HostApp light theme, changes Mac screenshot masking/scaling for Retina, updates many UITests to use settled waits/retry assertions, changes Catalyst stepper/back-button workarounds, and regenerates Mac screenshot baselines.
Inferred motivation: Stabilize MacCatalyst UI tests on newer/high-DPI macOS agents and reduce flaky Appium/screenshot failures.

Reconciliation with PR Narrative

Author claims: The PR fixes Retina screenshot masking/scaling, moves macOS UI tests to the appropriate shared pool, suppresses interfering system UI, stabilizes UITests, and refreshes Mac baselines.
Agreement/disagreement: The code matches that intent, but the core screenshot scale calculation still uses the app window size as if it were the full screen logical size, which undermines the main PR goal. Several test wait conversions also change id-or-text lookup semantics on Catalyst.

Prior Review Reconciliation

Prior ❌ Error Finding Source Status Evidence
Mac screenshot scale is computed from full-screen screenshot pixels divided by Appium app-window size MauiBot inline reviews/top-level changes requested ❌ Unresolved Current src/Controls/tests/TestCases.Shared.Tests/UITest.cs:707-713 still uses Driver.Manage().Window.Size for image.Width / screenSize.Width.
WaitForElementTillPageNavigationSettled(string) breaks text-only waits on Catalyst MauiBot inline reviews ❌ Unresolved Current Issue17554.cs:21 waits for text DropCompleted; current Issue33356.cs:39 waits for item text Abyssinian; helper first does ById(...) on Catalyst.
Issue33769 decrement path can pass on stale Success text MauiBot inline reviews ❌ Unresolved Current Issue33769.cs:25-28 waits for the same "Success" label value that was already set by the increment phase.
Hard-coded Catalyst back-button tap coordinates MauiBot/expert reviewer ⚠️ Unresolved Current Issue8529.cs:33 and Issue12320.cs:27 still tap (158, 67).
Catalyst stepper identifier-specific decrement lookup MauiBot inline review 🔄 Obsolete Current code no longer uses the identifier-suffix lookup described by that comment; it sorts buttons by X position.

Blast Radius Assessment

  • Runs for all instances: Yes, for all MacCatalyst screenshot-based UI tests because UITest.TakeScreenshot() is shared infrastructure.
  • Startup impact: Limited to TestCases HostApp/UI-test startup, not product startup.
  • Static/shared state: No new product static state, but global macOS defaults/process-kill behavior affects the whole UI-test agent session.

CI Status

  • Required-check result: gh pr checks --required could not run because gh is unauthenticated. Public GitHub check-runs for head b4573f46581b3effbfca76cfed57f5d02ce160d0 show failing Build Analysis and aggregate maui-pr-uitests, including multiple failed macOS UITest legs.
  • Classification: PR-caused or at least PR-relevant failure; the failures are in the MacCatalyst UITest area this PR changes.
  • Action taken: Invoked azdo-build-investigator context; confidence capped low because required-check status could not be obtained via gh and fallback check-runs are red.

Findings

❌ Error — Retina screenshot scale still uses app-window size instead of screen size

src/Controls/tests/TestCases.Shared.Tests/UITest.cs:707-713

App.Screenshot() on Mac captures the full desktop, but Driver.Manage().Window.Size is the current app window size. For a non-fullscreen Retina desktop, image.Width / screenSize.Width can compute display-pixels/app-window-points (for example ~3x or ~4x) instead of the actual backing scale (usually 2x). The subsequent pxX, pxWidth, and mask composite then crop the wrong screen region. This is the central behavior the PR claims to fix, so it should not merge as-is.

❌ Error — New settled waits can time out on visible text-only elements on Catalyst

src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17554.cs:21
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33356.cs:39

WaitForElementTillPageNavigationSettled(string) does a Catalyst-only WaitForElement(AppiumQuery.ById(elementId)) before the normal string wait. The old WaitForElement(string) falls back from id to text; the new pre-wait does not. DropCompleted is status label text, not the StatusLabel automation id, and Abyssinian is collection item text, not an automation id. On Catalyst these can time out before the normal text fallback is reached.

❌ Error — Stepper min/max test can pass without validating decrement

src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33769.cs:25-28

After the increment phase, the status label is already "Success". The decrement phase waits for the same "Success" value and then asserts the same text, so a failed/no-op DecreaseStepper can still pass. This weakens the test exactly where the PR is trying to improve stepper reliability.

⚠️ Warning — Catalyst back-button taps are hard-coded to absolute screen coordinates

src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue8529.cs:33
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/XFIssue/Issue12320.cs:27

App.TapCoordinates(158, 67) uses macos: click absolute coordinates. The back button location can vary with window placement, toolbar metrics, display scaling, or agent resolution. Prefer a relative tap based on the app/window/back-button frame or a platform navigation command.

Failure-Mode Probing

  • Non-fullscreen MacCatalyst window on Retina display: screenshot scale can be derived from desktop pixels divided by app-window points, producing an inflated scale and wrong crop.
  • Catalyst wait for visible text with no AutomationId: the new helper times out on ById(text) before the existing text fallback can run.
  • Stepper decrement no-ops: Issue33769 still passes because the status label retained "Success" from the increment assertion.
  • Different CI display/window layout: absolute (158, 67) back-button taps can miss the app/back button.

Verdict: NEEDS_CHANGES

Confidence: low, capped by red/undetermined required CI and shared UI-test infrastructure blast radius.
Summary: The PR addresses real MacCatalyst UITest problems, but the main screenshot scaling fix is still logically incorrect for full-screen screenshots of non-fullscreen app windows. There are also unresolved test reliability regressions from Catalyst wait semantics and stale success assertions.


🛠️ Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 maui-expert-reviewer Capture the XCUIElementTypeWindow element screenshot and mask in window-local coordinates ⚠️ Blocked UITest.cs Compiled; runtime blocked by MacCatalyst HostApp registration (com.microsoft.maui.uitests not found). Risk: Appium Mac may not support/shape window element screenshots consistently.
2 maui-expert-reviewer Preserve id-or-visible-text semantics in WaitForElementTillPageNavigationSettled(string) by using the string FindElement path for the Catalyst pre-wait ⚠️ Blocked HelperExtensions.cs Compiled; same HostApp registration blocker. Fixes text-backed waits such as DropCompleted and Abyssinian.
3 maui-expert-reviewer Keep full-desktop screenshot cropping but derive scale from CoreGraphics display backing scale ⚠️ Blocked UITest.cs Compiled; same HostApp registration blocker. Stronger than PR heuristic for Retina scale, but mixed-monitor coordinate spaces remain a risk.
4 maui-expert-reviewer Make Issue33769 Stepper oracle phase-specific (MaximumReached then MinimumReached) ⚠️ Blocked Issue33769.cs HostApp + test Compiled; same HostApp registration blocker. Prevents stale generic success text from satisfying decrement validation.
PR PR #35156 Full-desktop screenshot crop with scale inferred from screenshot pixels divided by Appium window size; Catalyst wait/test stabilizations; refreshed Mac baselines ⚠️ INCONCLUSIVE (Gate) Multiple Original PR; pre-flight code review found unresolved correctness issues.

Cross-Pollination

Model Round New Ideas? Details
gpt-5.5 / maui-expert-reviewer 1 Yes Window element screenshot avoids desktop/window scale mismatch.
gpt-5.5 / maui-expert-reviewer 2 Yes Catalyst pre-wait should preserve string id-or-text semantics.
gpt-5.5 / maui-expert-reviewer 3 Yes CoreGraphics backing scale keeps the existing crop model but replaces the bad scale heuristic.
gpt-5.5 / maui-expert-reviewer 4 Yes Stepper test should use distinct max/min success states.

Test Summary

All candidates were tested with targeted Mac test commands. Each candidate compiled successfully through Controls.TestCases.Mac.Tests.dll, but runtime validation was blocked before test bodies executed because Appium could not find/register the MacCatalyst HostApp bundle:

The app representing com.microsoft.maui.uitests could not be found.

This is the same environment blocker across all attempts and is not evidence that any candidate logic failed. The prior gate result was also inconclusive due build/environment failure, so no candidate can be marked passing.

Exhaustion / Selection

Exhausted: Yes — the loop explored the meaningful distinct approaches available from the expert review findings: alternate screenshot source, native display scale, central wait semantics, and stale test oracle correction. Further attempts would be minor variations without runnable MacCatalyst validation in this environment.

Selected Fix: None verified. Candidate #3 is the best screenshot alternative on code merits because it preserves the current full-desktop crop/snapshot model while replacing only the incorrect scale source with the display backing scale. Candidate #2 and #4 should be considered companion fixes for separate correctness issues found in the PR.


📝 Recommended PR Title & Description

Assessment: ✏️ Recommend updating — the current metadata accurately describes the screenshot/pipeline intent but omits the winning fix's Catalyst wait-semantics and Stepper oracle corrections, and its screenshot-scale details should reflect the CoreGraphics backing-scale approach.

Recommended title

[macOS] UI Tests: Fix Retina screenshot masking and Catalyst stability

Recommended description

This pull request updates the macOS UI test pipeline configuration and improves the screenshot masking logic in UI tests to better support high-DPI (Retina) displays. The main changes ensure that screenshots are correctly masked and scaled regardless of display density, that MacCatalyst UI tests keep reliable navigation/wait behavior, and that the pipeline uses the appropriate shared pool for macOS agents.

**Screenshot logic improvements:**
- Updated the `TakeScreenshot()` method in `UITest.cs` to:
    - Dismiss interfering macOS system dialogs before taking full-desktop Mac screenshots.
    - Determine the display backing scale for the display containing the app window instead of comparing full-desktop screenshot pixels to app-window size.
    - Scale all mask and crop coordinates to pixel units, ensuring the rounded rectangle mask aligns correctly on high-DPI screens.
    - After masking, normalize the resulting image back to logical dimensions so downstream cropping logic remains consistent regardless of pixel density.

**MacCatalyst UI test stability:**
- Preserve `WaitForElementTillPageNavigationSettled(string)` id-or-visible-text semantics during the Catalyst pre-wait so text-backed waits such as `DropCompleted` and `Abyssinian` do not time out before the normal lookup fallback.
- Strengthen `Issue33769` coverage by using distinct `MaximumReached` and `MinimumReached` states, preventing the decrement phase from passing on stale max-phase success text.
- Force the TestCases HostApp to light theme on MacCatalyst for consistent screenshots on newer macOS agents.
- Add AutomationIds needed by updated MacCatalyst test paths and refresh macOS screenshot baselines.

**macOS CI configuration:**
- Move the public macOS UI test pool to the shared ACES Tahoe image.
- Update notification/dialog suppression scripts to reduce system UI interference during MacCatalyst UI tests.

🏁 Report — Final Recommendation

Comparative Report — PR #35156

Candidate ranking

Rank Candidate Test result Assessment
1 pr-plus-reviewer ⚠️ Blocked / unverified Best candidate. It preserves the PR's broad macOS UI-test infrastructure and baseline updates while applying the three major expert-review fixes: reliable display backing scale, preserved text/id wait semantics, and phase-specific Stepper assertions.
2 try-fix-3 ⚠️ Blocked / unverified Best single screenshot alternative. It keeps the PR's full-desktop crop model but replaces the flawed window-size scale heuristic with CoreGraphics backing scale. It does not address the Catalyst text-wait or Stepper oracle issues by itself.
3 try-fix-2 ⚠️ Blocked / unverified Correctly fixes the helper semantics regression for text-backed waits such as DropCompleted and Abyssinian, but it does not address the main Retina screenshot scaling issue.
4 try-fix-4 ⚠️ Blocked / unverified Correctly strengthens the Stepper test oracle, but it is limited to test coverage and does not fix screenshot scaling or wait semantics.
5 try-fix-1 ⚠️ Blocked / unverified Conceptually aligns screenshot pixels and window-local bounds by using a window element screenshot, but it has higher Appium compatibility/baseline-shape risk than the CoreGraphics scale approach.
6 pr ⚠️ Gate inconclusive Lowest-ranked non-failing candidate because the raw PR still has three major correctness issues found by pre-flight and expert review.

No candidate has a passed regression result, and no candidate has a failed regression result. All attempted runtime validation was blocked or inconclusive by environment/HostApp registration issues, so the required pass/fail ordering rule does not demote any candidate for a regression failure.

Key comparison

The raw PR contains necessary surrounding work: macOS CI pool changes, notification/dialog suppression, forced light mode, refreshed Mac snapshot baselines, and several MacCatalyst UI-test stabilizations. However, its main Retina scaling fix still mixes full-desktop screenshot pixels with app-window logical dimensions, which can crop the wrong pixels for non-fullscreen Retina windows.

try-fix-3 is the strongest standalone screenshot fix because it preserves the existing full-desktop screenshot flow and changes only the scale source to the display backing scale. try-fix-2 and try-fix-4 are companion fixes for separate correctness problems found in the PR, not replacements for the screenshot fix. try-fix-1 is a useful alternate direction but depends on Appium Mac window element screenshots and may alter the screenshot content differently from the existing baseline model.

pr-plus-reviewer wins because it combines the PR's complete implementation context with the best screenshot fix and the two companion correctness fixes. It leaves only the moderate coordinate-tap concern as an unresolved discussion item because the reviewed diff does not expose a safe identifier-backed replacement and App.Back() would change what the affected BackButtonBehavior tests exercise.

Winner

Winning candidate: pr-plus-reviewer

Rationale: It is the only candidate that keeps the PR's full intended scope while resolving all major expert-review findings. Since all candidates are runtime-unverified due the same environment blocker and none failed regression tests, the combined candidate is strongest on code correctness and coverage merits.


🧭 Next Steps — review latest findings

No alternative fix was selected for this run. Review the session findings and CI results before merging.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expert Review — 4 findings

See inline comments for details.

// App.Screenshot() returns pixels at the display's native resolution
// (1x on non-Retina, 2x on Retina displays).
// Strategy: Compare screenshot pixel dimensions to screen logical dimensions.
var screenSize = ((AppiumApp)App).Driver.Manage().Window.Size;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 AI-Generated Review (multi-model)

[major] Logic and Correctness — The Retina scale is derived from the full-desktop screenshot size divided by the Appium app-window size. On a Retina desktop larger than the 1024x768 test window, image.Width / screenSize.Width describes desktop-pixels-per-window-point (for example ~3024/1024) rather than the display backing scale, so pxX/pxY/pxWidth/pxHeight crop the wrong region and the regenerated Mac baselines can still be invalid on non-fullscreen Retina windows. Use a scale source that describes the same coordinate space as windowBounds (or capture the window element directly).

@@ -18,6 +18,6 @@ public void DropCompletedFiresWhenDroppingOnNonDropTarget()
App.WaitForElement("NonDropTarget");

App.DragAndDrop("DragSource", "NonDropTarget");
App.WaitForElement("DropCompleted");
App.WaitForElementTillPageNavigationSettled("DropCompleted");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 AI-Generated Review (multi-model)

[major] Logic and Correctness — This replaces the normal string wait with WaitForElementTillPageNavigationSettled, but that helper does a Catalyst-only pre-wait using AppiumQuery.ById(elementId) before falling back to the existing id-or-visible-text lookup. Callers here and in Issue33356.cs:39 pass visible text (DropCompleted, Abyssinian), so Mac Catalyst can time out before reaching the string lookup that would have found the text. Preserve the helper's original string semantics for the pre-wait or use an explicit query/AutomationId.

Assert.That(result, Is.EqualTo("Success"));

App.DecreaseStepper("Issue33769_Stepper");
result = App.WaitForElement("Issue33769_StepperStatusLabel").GetText();
Assert.That(App.WaitForTextToBePresentInElement("Issue33769_StepperStatusLabel", "Success"), Is.True);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 AI-Generated Review (multi-model)

[major] Regression Prevention and Test Coverage — The decrement phase waits for the same generic Success text that was already produced by the increment phase. If DecreaseStepper is a no-op or fails to reach the minimum, the stale label still satisfies this wait and the following equality assertion, so the test does not actually cover the minimum-bound regression. Make the page expose distinct max/min states (or reset to an intermediate value) before asserting the decrement path.

{
// On Mac Catalyst, the AutomationId set on FileImageSource inside BackButtonBehavior.IconOverride
// is not working, so tap the back button by coordinates as a workaround.
App.TapCoordinates(158, 67);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 AI-Generated Review (multi-model)

⚠️ [moderate] Navigation & Shell — This Catalyst path taps a hard-coded screen coordinate for the back button; Issue12320.cs:27 adds the same workaround. That is tied to the current fixed window size/titlebar metrics and will miss the back button if the window origin, toolbar layout, OS chrome, or scale changes. Prefer a native query/AutomationId-backed helper or expose a testable identifier on the actual back button instead of coordinates.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 23, 2026
PureWeen pushed a commit that referenced this pull request Jul 24, 2026
<!-- Please let the below note in for people that find this PR -->
> [!NOTE]
> Are you waiting for the changes in this PR to be merged?
> It would be very helpful if you could [test the resulting
artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from
this PR and let us know in a comment if this change resolves your issue.
Thank you!

### Description of Change

Automates visual snapshot evidence in `/review tests`. When public AzDO
results contain failed screenshot comparisons, the command now emits
exactly one test-failure analysis comment containing bounded, expandable
baseline/actual/diff panels.

Visual evidence remains supplementary: it does not change
`gate.verdictCeiling`, deterministic attribution, or the merge-readiness
verdict.

#### One-comment flow

1. Trusted pre-activation code discovers failed visual results through
the public AzDO `resultsbybuild` API, including retry-suffixed
attachments such as `Snapshot[1].png` and `Snapshot-diff[1].png`.
2. It resolves baselines from the exact source version tested by AzDO
and maps runtime evidence to the correct snapshot directory (`ios-26`,
`android-notch-36`, `mac`, or `windows`).
3. It streams and validates bounded PNG assets, then stores them on
`review-tests-assets` using immutable commit-pinned
`raw.githubusercontent.com` URLs.
4. The Copilot agent emits the normal single `add_comment` analysis
payload with a trusted insertion marker.
5. A sealed post-step validates the published asset manifest and injects
as many expandable comparison panels as fit into that same comment. Each
collapsed panel shows a conservative relationship label:
- `Likely PR-caused` for an exact test/platform base regression or
directly changed snapshot/test;
- `Likely unrelated` for an exact base/known-issue match without direct
visual scope;
   - `Needs human investigation` for unmatched or mixed evidence.
6. Excess comparisons are summarized as omitted instead of creating a
companion comment.

The local `.github/scripts/Review-Tests.ps1 -PostComment` path uses the
same merger. It also recognizes complete reports returned in Copilot's
final response, preserving nested evidence code fences without wrapping
a second title or badge section.

### Security and Failure Safety

- PR text, logs, test names, attachment metadata, changed files, and
visual labels remain untrusted input.
- The merger script and visual context are copied to a root-owned
location before the workflow checks out the untrusted PR branch.
- The post-step runs without `COPILOT_GITHUB_TOKEN`, `GH_TOKEN`, or
`GITHUB_TOKEN`.
- AzDO attachment URLs must match the expected public project and
attachment route.
- Published assets are size-bounded, signature-checked PNGs with
validated dimensions and repository paths.
- Raw image URLs must match the exact repository, asset commit, PR
directory, and safe filename.
- Labels are HTML-escaped and `@` is neutralized before insertion.
- Relationship labels use fixed trusted text. Untrusted attribution
values are never rendered, and same-named snapshots changed on another
platform do not count as PR scope.
- The final body is checked panel-by-panel against conservative limits
of 45 URLs, 10 mentions, and 60,000 UTF-16 characters, below gh-aw's
throwing limits.
- The analysis JSON update is atomic (written to a temp file, then
renamed over the original). Invalid context, malformed output, missing
analysis payloads, limit failures, and dry-run/noop output leave the
original analysis unchanged.
- The publisher never creates or patches PR comments; only the existing
gh-aw `add_comment` payload is mutated.

### What NOT to Do

- Do not use the ordinary anonymous AzDO test-runs listing for
discovery; it redirects to sign-in. Use the public failed-results
endpoint.
- Do not resolve baselines from the current PR head; use the source
version actually tested by the selected AzDO build.
- Do not let the agent construct or trust visual asset URLs.
- Do not publish a second companion comment; merge bounded panels into
the single analysis payload.

### Validation

- 98 focused Pester tests pass.
- Changed PowerShell scripts parse successfully.
- `gh aw compile copilot-review-tests --approve` completes without
errors or warnings.
- A real `agent_output.json` from gh-aw run
[29674953402](https://github.com/dotnet/maui/actions/runs/29674953402)
was replayed through the post-step:
  - one `add_comment` item remained one item;
  - five visual panels were inserted;
  - the final body contained 26 URLs, one mention, and 9,585 characters.

### Live Single-Comment Examples

The exact local `/review tests` path from this branch posted or repaired
these merged comments after the PRs' `/azp run` pipelines completed:

| PR | Single merged result | Included evidence | Relationship labels |
Final limits |
| --- | --- | --- | --- | --- |
| #36413 | [Test-failure analysis with visual
panels](#36413 (comment))
| 5 panels / 15 images | 1 PR-caused, 4 investigate | 23 URLs, 13,156
chars |
| #36631 | [Test-failure analysis with visual
panels](#36631 (comment))
| 6 panels / 18 images | 2 PR-caused, 4 investigate | 31 URLs, 21,367
chars |
| #36395 | [Test-failure analysis with visual
panels](#36395 (comment))
| 11 panels / 33 images; 19 omitted | 11 investigate | 43 URLs, 19,858
chars |
| #36404 | [Test-failure analysis with visual
panels](#36404 (comment))
| 14 panels / 40 images; 81 omitted | 14 investigate | 45 URLs, 23,779
chars |
| #35846 | [Test-failure analysis with visual
panels](#35846 (comment))
| 10 panels / 30 images; 9 omitted | 10 investigate | 43 URLs, 22,496
chars |
| #36277 | [Test-failure analysis with visual
panels](#36277 (comment))
| 7 panels / 19 images | 3 PR-caused, 4 investigate | 31 URLs, 18,355
chars |
| #36170 | [Test-failure analysis with visual
panels](#36170 (comment))
| 11 panels / 33 images; 8 omitted | 11 investigate | 44 URLs, 23,180
chars |
| #35578 | [Test-failure analysis with visual
panels](#35578 (comment))
| 12 panels / 36 images; 50 omitted | 12 investigate | 44 URLs, 25,443
chars |
| #36672 | [Test-failure analysis with visual
panels](#36672 (comment))
| 14 panels / 40 images; 9 omitted | 14 investigate | 45 URLs, 25,915
chars |
| #31755 | [Test-failure analysis with visual
panels](#31755 (comment))
| 12 panels / 36 images; 3 omitted | 12 investigate | 44 URLs, 22,728
chars |
| #34637 | [Test-failure analysis with visual
panels](#34637 (comment))
| 9 panels / 27 images; 81 omitted | 9 investigate | 43 URLs, 22,325
chars |
| #35156 | [Test-failure analysis with visual
panels](#35156 (comment))
| 2 panels / 6 images | 1 PR-caused, 1 investigate | 19 URLs, 11,911
chars |
| #35885 | [Test-failure analysis with no visual
failures](#35885 (comment))
| 0 panels / 0 images | No visual failures detected | 8 URLs, 4,239
chars |
| #36577 | [Test-failure analysis with visual
panels](#36577 (comment))
| 1 panel / 3 images | 1 investigate | 44 URLs, 21,768 chars |
| #36212 | [Test-failure analysis with no visual
failures](#36212 (comment))
| 0 panels / 0 images | No visual failures detected | 5 URLs, 5,982
chars |

Each result contains one `Tests Failure Analysis` title and one merged
review marker. Across 114 rendered panels, all 336 embedded image URLs
returned HTTP 200. Seven panels were safely classified as likely
PR-caused; no panel in this sample had enough exact evidence to be
safely classified as likely unrelated, so the remaining 107 stayed at
`Needs human investigation`. Another 260 comparisons were omitted safely
by the comment limits.

The latest eight-example batch was regenerated concurrently, and #36672,
#31755, #34637, #35156, #35885, #36577, and #36212 were added afterward.
The current [`review-tests-assets`
head](f937993)
retains the full asset history.

The protected `copilot-pat-pool` environment rejects feature-branch
`workflow_dispatch` runs before job execution. The live local-runner
examples validate comment generation and asset publication, while the
real gh-aw output replay validates the workflow post-step mutation
without weakening that branch protection.

### Issues Fixed

N/A - reviewer workflow enhancement.

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: kubaflo <kubaflo@users.noreply.github.com>
Copilot-Session: a280b482-e102-4ca0-9ff9-1cfe1946e21f
Copilot-Session: d00747b7-96f3-4e7a-8dfb-e3a48db04b2d
Copilot-Session: 478d195b-20f3-4bc6-aeed-f6aa88b55fda
@kubaflo

kubaflo commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

@NafeelaNazhir what is the status of this PR?

@NafeelaNazhir

Copy link
Copy Markdown
Contributor Author

@NafeelaNazhir what is the status of this PR?

@kubaflo Regarding this PR, the Stepper-related test cases are continuously failing, and each CI run is introducing new failures. I'm currently working on resolving these issues and will update you once they are fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-testing Unit tests, device tests community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants