Skip to content

Commit 614fe20

Browse files
authored
[Testing] UITest to measure layout passes on a common scenario (#25671)
* UI test to measure layout passes * Fix merge
1 parent 34f98ac commit 614fe20

File tree

4 files changed

+325
-1
lines changed

4 files changed

+325
-1
lines changed

src/Controls/tests/TestCases.HostApp/CollectionViewHostBuilderExtentions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class CollectionView2 : CollectionView { }
1313
class CarouselView1 : CarouselView { }
1414
class CarouselView2 : CarouselView { }
1515

16-
public static partial class CollectionViewHostBuilderExtentions
16+
public static partial class CollectionViewHostBuilderExtensions
1717
{
1818
/// <summary>
1919
/// Configure the .NET MAUI app to listen for fold-related events
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
4+
xmlns:i="clr-namespace:Maui.Controls.Sample.Issues"
5+
x:Class="Maui.Controls.Sample.Issues.Issue25671"
6+
Title="LayoutPassTest">
7+
8+
<i:Issue25671AbsoluteLayout>
9+
<i:Issue25671Grid Padding="0,0,0,100"
10+
BackgroundColor="DarkGray"
11+
RowDefinitions="Auto,*,Auto"
12+
AbsoluteLayout.LayoutFlags="All"
13+
AbsoluteLayout.LayoutBounds="0,0,1,1">
14+
15+
<i:Issue25671ContentView>
16+
<i:Issue25671Label Text="Hello world" Padding="16,8" TextColor="White" />
17+
</i:Issue25671ContentView>
18+
19+
<i:Issue25671CollectionView x:Name="CV" AutomationId="CV" Grid.Row="1">
20+
<i:Issue25671CollectionView.ItemTemplate>
21+
<DataTemplate>
22+
<i:Issue25671ContentView Padding="16">
23+
<i:Issue25671VerticalStackLayout Shadow="{Shadow Radius=16, Brush=Black, Opacity=0.24}"
24+
BackgroundColor="SlateBlue"
25+
Padding="16,8"
26+
Spacing="8">
27+
<i:Issue25671Label Text="{Binding Text}" TextColor="White"/>
28+
<i:Issue25671Image HorizontalOptions="Center">
29+
<i:Issue25671Image.Source>
30+
<FontImageSource Glyph="{Binding Glyph}" Color="White" FontFamily="FA" Size="24"/>
31+
</i:Issue25671Image.Source>
32+
</i:Issue25671Image>
33+
</i:Issue25671VerticalStackLayout>
34+
</i:Issue25671ContentView>
35+
</DataTemplate>
36+
</i:Issue25671CollectionView.ItemTemplate>
37+
</i:Issue25671CollectionView>
38+
39+
<i:Issue25671VerticalStackLayout Grid.Row="2" BackgroundColor="DarkBlue">
40+
<i:Issue25671Button Text="Regenerate items" Padding="16,8" TextColor="White" Clicked="RegenerateItems" AutomationId="RegenerateItems" />
41+
</i:Issue25671VerticalStackLayout>
42+
</i:Issue25671Grid>
43+
44+
<i:Issue25671VerticalStackLayout HeightRequest="100"
45+
BackgroundColor="SlateBlue"
46+
VerticalOptions="End"
47+
AbsoluteLayout.LayoutFlags="All"
48+
AbsoluteLayout.LayoutBounds="0,1,1,1">
49+
<Button Text="Press Me" Clicked="OnClick" TextColor="White" AutomationId="PressMe" />
50+
</i:Issue25671VerticalStackLayout>
51+
</i:Issue25671AbsoluteLayout>
52+
</ContentPage>
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
using System.Text.RegularExpressions;
2+
3+
namespace Maui.Controls.Sample.Issues;
4+
5+
[Issue(IssueTracker.Github, 25671, "Layout passes should not increase", PlatformAffected.All)]
6+
7+
public partial class Issue25671 : ContentPage
8+
{
9+
public static long MeasurePasses = 0;
10+
public static long ArrangePasses = 0;
11+
12+
private int _regenIndex = 2;
13+
private static readonly string _loremIpsumLongText = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper. Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue.";
14+
private static readonly string[] _words = new Regex(@"\w+").Matches(_loremIpsumLongText).Select(m => m.Value).ToArray();
15+
16+
public Issue25671()
17+
{
18+
InitializeComponent();
19+
GenerateItems();
20+
}
21+
22+
void RegenerateItems(object sender, EventArgs args)
23+
{
24+
_regenIndex = (_regenIndex - 1) % 4 + 2;
25+
GenerateItems();
26+
}
27+
28+
void OnClick(object sender, EventArgs args)
29+
{
30+
((Button)sender).Text = $"M: {MeasurePasses}, A: {ArrangePasses}";
31+
}
32+
33+
void GenerateItems()
34+
{
35+
CV.ItemsSource = Enumerable.Range(4, 200).Select(i =>
36+
new
37+
{
38+
Text = string.Join(' ', _words.Take(i % _regenIndex == 0 ? i % _words.Length : (i * 2) % _words.Length)),
39+
Glyph = char.ConvertFromUtf32(0xf127 + i % 10)
40+
});
41+
}
42+
}
43+
44+
#if IOS
45+
// When CV2 is completed and can handle resize of items we can remove this pointer to CV1
46+
internal class Issue25671CollectionView : CollectionView1
47+
#else
48+
public class Issue25671CollectionView : CollectionView
49+
#endif
50+
{
51+
}
52+
53+
public class Issue25671AbsoluteLayout : AbsoluteLayout
54+
{
55+
public static long MeasurePasses = 0;
56+
public static long ArrangePasses = 0;
57+
58+
protected override Size ArrangeOverride(Rect bounds)
59+
{
60+
Interlocked.Increment(ref Issue25671.ArrangePasses);
61+
Interlocked.Increment(ref ArrangePasses);
62+
return base.ArrangeOverride(bounds);
63+
}
64+
65+
protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
66+
{
67+
Interlocked.Increment(ref Issue25671.MeasurePasses);
68+
Interlocked.Increment(ref MeasurePasses);
69+
return base.MeasureOverride(widthConstraint, heightConstraint);
70+
}
71+
}
72+
73+
public class Issue25671VerticalStackLayout : VerticalStackLayout
74+
{
75+
public static long MeasurePasses = 0;
76+
public static long ArrangePasses = 0;
77+
78+
protected override Size ArrangeOverride(Rect bounds)
79+
{
80+
Interlocked.Increment(ref Issue25671.ArrangePasses);
81+
Interlocked.Increment(ref ArrangePasses);
82+
return base.ArrangeOverride(bounds);
83+
}
84+
85+
protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
86+
{
87+
Interlocked.Increment(ref Issue25671.MeasurePasses);
88+
Interlocked.Increment(ref MeasurePasses);
89+
return base.MeasureOverride(widthConstraint, heightConstraint);
90+
}
91+
}
92+
93+
public class Issue25671Grid : Grid
94+
{
95+
public static long MeasurePasses = 0;
96+
public static long ArrangePasses = 0;
97+
98+
protected override Size ArrangeOverride(Rect bounds)
99+
{
100+
Interlocked.Increment(ref Issue25671.ArrangePasses);
101+
Interlocked.Increment(ref ArrangePasses);
102+
return base.ArrangeOverride(bounds);
103+
}
104+
105+
protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
106+
{
107+
Interlocked.Increment(ref Issue25671.MeasurePasses);
108+
Interlocked.Increment(ref MeasurePasses);
109+
return base.MeasureOverride(widthConstraint, heightConstraint);
110+
}
111+
}
112+
113+
public class Issue25671ContentView : ContentView
114+
{
115+
public static long MeasurePasses = 0;
116+
public static long ArrangePasses = 0;
117+
118+
protected override Size ArrangeOverride(Rect bounds)
119+
{
120+
Interlocked.Increment(ref Issue25671.ArrangePasses);
121+
Interlocked.Increment(ref ArrangePasses);
122+
return base.ArrangeOverride(bounds);
123+
}
124+
125+
protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
126+
{
127+
Interlocked.Increment(ref Issue25671.MeasurePasses);
128+
Interlocked.Increment(ref MeasurePasses);
129+
return base.MeasureOverride(widthConstraint, heightConstraint);
130+
}
131+
}
132+
133+
public class Issue25671Label : Label
134+
{
135+
public static long MeasurePasses = 0;
136+
public static long ArrangePasses = 0;
137+
138+
protected override Size ArrangeOverride(Rect bounds)
139+
{
140+
Interlocked.Increment(ref Issue25671.ArrangePasses);
141+
Interlocked.Increment(ref ArrangePasses);
142+
return base.ArrangeOverride(bounds);
143+
}
144+
145+
protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
146+
{
147+
Interlocked.Increment(ref Issue25671.MeasurePasses);
148+
Interlocked.Increment(ref MeasurePasses);
149+
return base.MeasureOverride(widthConstraint, heightConstraint);
150+
}
151+
}
152+
153+
public class Issue25671Button : Button
154+
{
155+
public static long MeasurePasses = 0;
156+
public static long ArrangePasses = 0;
157+
158+
protected override Size ArrangeOverride(Rect bounds)
159+
{
160+
Interlocked.Increment(ref Issue25671.ArrangePasses);
161+
Interlocked.Increment(ref ArrangePasses);
162+
return base.ArrangeOverride(bounds);
163+
}
164+
165+
protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
166+
{
167+
Interlocked.Increment(ref Issue25671.MeasurePasses);
168+
Interlocked.Increment(ref MeasurePasses);
169+
return base.MeasureOverride(widthConstraint, heightConstraint);
170+
}
171+
}
172+
173+
public class Issue25671Image : Image
174+
{
175+
public static long MeasurePasses = 0;
176+
public static long ArrangePasses = 0;
177+
178+
protected override Size ArrangeOverride(Rect bounds)
179+
{
180+
Interlocked.Increment(ref Issue25671.ArrangePasses);
181+
Interlocked.Increment(ref ArrangePasses);
182+
return base.ArrangeOverride(bounds);
183+
}
184+
185+
protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
186+
{
187+
Interlocked.Increment(ref Issue25671.MeasurePasses);
188+
Interlocked.Increment(ref MeasurePasses);
189+
return base.MeasureOverride(widthConstraint, heightConstraint);
190+
}
191+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#if ANDROID || IOS
2+
using NUnit.Framework;
3+
using NUnit.Framework.Legacy;
4+
using UITest.Appium;
5+
using UITest.Core;
6+
7+
namespace Microsoft.Maui.TestCases.Tests.Issues
8+
{
9+
public class Issue25671 : _IssuesUITest
10+
{
11+
public Issue25671(TestDevice testDevice) : base(testDevice)
12+
{
13+
}
14+
15+
public override string Issue => "Layout passes should not increase";
16+
17+
[Test]
18+
[Category(UITestCategories.CollectionView)]
19+
public async Task LayoutPassesShouldNotIncrease()
20+
{
21+
App.WaitForElement("RegenerateItems");
22+
23+
await ScrollDown();
24+
await ScrollDown();
25+
await ScrollDown();
26+
await ScrollUp();
27+
await ScrollUp();
28+
29+
App.Tap("RegenerateItems");
30+
31+
await ScrollDown();
32+
await ScrollDown();
33+
await ScrollUp();
34+
await ScrollUp();
35+
36+
App.Tap("PressMe");
37+
// Text will be in the format "M: 0, A: 0"
38+
var text = App.WaitForElement("PressMe").GetText()!;
39+
// Get measure passes and arrange passes value via regex
40+
var match = System.Text.RegularExpressions.Regex.Match(text, @"M: (\d+), A: (\d+)");
41+
var measurePasses = int.Parse(match.Groups[1].Value);
42+
var arrangePasses = int.Parse(match.Groups[2].Value);
43+
44+
#if IOS
45+
const int maxMeasurePasses = 525;
46+
const int maxArrangePasses = 308;
47+
#elif ANDROID
48+
const int maxMeasurePasses = 353;
49+
const int maxArrangePasses = 337;
50+
#endif
51+
52+
var logMessage = @$"Measure passes: {measurePasses}, Arrange passes: {arrangePasses}";
53+
TestContext.WriteLine(logMessage);
54+
55+
// Write the log to a file and attach it to the test results for ADO
56+
var logFile = Path.Combine(Path.GetTempPath(), "LayoutPasses.log");
57+
File.WriteAllText(logFile, logMessage);
58+
TestContext.AddTestAttachment(logFile, "LayoutPasses.log");
59+
60+
// Then assert that the measure passes and arrange passes are less than the expected values.
61+
// Let's give a 5% margin of error.
62+
ClassicAssert.LessOrEqual(measurePasses, maxMeasurePasses * 1.05);
63+
ClassicAssert.LessOrEqual(arrangePasses, maxArrangePasses * 1.05);
64+
}
65+
66+
async Task ScrollDown()
67+
{
68+
await Task.Delay(50);
69+
App.ScrollDown("CV", swipePercentage: 0.5D, swipeSpeed: 1000);
70+
await Task.Delay(50);
71+
}
72+
73+
async Task ScrollUp()
74+
{
75+
await Task.Delay(50);
76+
App.ScrollUp("CV", swipePercentage: 0.5D, swipeSpeed: 1000);
77+
await Task.Delay(50);
78+
}
79+
}
80+
}
81+
#endif

0 commit comments

Comments
 (0)