From 51ffbe8064935d72c6d06ba1cac71ba77489bbb3 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Tue, 23 Jul 2024 16:05:47 -0500 Subject: [PATCH 1/4] Move WebViewDidNotLeak to UITest --- .../tests/DeviceTests/Memory/MemoryTests.cs | 36 -------- .../TestCases.HostApp/Issues/Issue22972.cs | 86 +++++++++++++++++++ .../Tests/Issues/Issue22972.cs | 22 +++++ 3 files changed, 108 insertions(+), 36 deletions(-) create mode 100644 src/Controls/tests/TestCases.HostApp/Issues/Issue22972.cs create mode 100644 src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22972.cs diff --git a/src/Controls/tests/DeviceTests/Memory/MemoryTests.cs b/src/Controls/tests/DeviceTests/Memory/MemoryTests.cs index 81b17e6eb38a..01444ecee66e 100644 --- a/src/Controls/tests/DeviceTests/Memory/MemoryTests.cs +++ b/src/Controls/tests/DeviceTests/Memory/MemoryTests.cs @@ -242,42 +242,6 @@ await InvokeOnMainThreadAsync(async () => await AssertionExtensions.WaitForGC(viewReference, handlerReference, platformViewReference); } - [Fact("WebView Does Not Leak")] - public async Task WebViewDoesNotLeak() - { - SetupBuilder(); - - var references = new List(); - var navPage = new NavigationPage(new ContentPage { Title = "Page 1" }); - - await CreateHandlerAndAddToWindow(new Window(navPage), async () => - { - { - var webView = new WebView - { - HeightRequest = 500, // NOTE: non-zero size required for Windows - Source = new HtmlWebViewSource { Html = "

hi

" }, - }; - var page = new ContentPage - { - Content = new VerticalStackLayout { webView } - }; - await navPage.Navigation.PushAsync(page); - await OnLoadedAsync(page); - await Task.Delay(1000); // give the WebView time to load - - references.Add(new(webView)); - references.Add(new(webView.Handler)); - references.Add(new(webView.Handler.PlatformView)); - - await navPage.Navigation.PopAsync(); - } - - // Assert *before* the Window is closed - await AssertionExtensions.WaitForGC(references.ToArray()); - }); - } - [Theory("Gesture Does Not Leak")] [InlineData(typeof(DragGestureRecognizer))] [InlineData(typeof(DropGestureRecognizer))] diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue22972.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue22972.cs new file mode 100644 index 000000000000..feef1c6ff378 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue22972.cs @@ -0,0 +1,86 @@ +using Microsoft.Maui.Controls; + +namespace Maui.Controls.Sample.Issues +{ + [Issue(IssueTracker.Github, 22972, "Win platform WebView cannot be release after its parent window get close")] + public class Issue22972 : NavigationPage + { + ContentPage _rootPage = new ContentPage { Title = "Page 1" }; + public Issue22972() + { + PushAsync(_rootPage); + _rootPage.Content = new VerticalStackLayout() + { + new Label + { + Text = "If you don't see a success label this test has failed" + } + }; + + _rootPage.Loaded += OnPageLoaded; + } + + async void OnPageLoaded(object sender, EventArgs e) + { + var references = new List(); + CurrentPage.Loaded -= OnPageLoaded; + + { + var webView = new WebView + { + HeightRequest = 500, // NOTE: non-zero size required for Windows + Source = new HtmlWebViewSource { Html = "

hi

" }, + }; + var page = new ContentPage + { + Content = new VerticalStackLayout { webView } + }; + await Navigation.PushAsync(page); + await Task.Delay(1000); // give the WebView time to load + + references.Add(new(webView)); + references.Add(new(webView.Handler)); + references.Add(new(webView.Handler.PlatformView)); + + await Navigation.PopAsync(); + webView.Handler.DisconnectHandler(); + } + + + try + { + _rootPage.Content = new VerticalStackLayout() + { + new Label + { + Text = "Waiting for resources to cleanup" + } + }; + + + // Assert *before* the Window is closed + await GarbageCollectionHelper.WaitForGC(references.ToArray()); + _rootPage.Content = new VerticalStackLayout() + { + new Label + { + Text = "Success, everything has been cleaned up", + AutomationId = "Success" + } + }; + } + catch + { + var stillAlive = references.Where(x=> x.IsAlive).Select(x=> x.Target).ToList(); + _rootPage.Content = new VerticalStackLayout() + { + new Label + { + Text = "Failed to cleanup: " + string.Join(", ", stillAlive), + AutomationId = "Failed" + } + }; + } + } + } +} diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22972.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22972.cs new file mode 100644 index 000000000000..38dbaef9c4c6 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22972.cs @@ -0,0 +1,22 @@ +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues +{ + public class Issue22972 : _IssuesUITest + { + public Issue22972(TestDevice device) : base(device) + { + } + + public override string Issue => "Win platform WebView cannot be release after its parent window get close"; + + [Test] + [Category(UITestCategories.WebView)] + public void WebViewDoesNotLeak() + { + App.WaitForElement("Success", timeout: TimeSpan.FromSeconds(10)); + } + } +} \ No newline at end of file From 63ef7a2f5cf3f358dcac7235f7c6779d721a3a97 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Tue, 23 Jul 2024 16:07:43 -0500 Subject: [PATCH 2/4] - remove --- src/Controls/tests/TestCases.HostApp/Issues/Issue22972.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue22972.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue22972.cs index feef1c6ff378..2bb0bd36d606 100644 --- a/src/Controls/tests/TestCases.HostApp/Issues/Issue22972.cs +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue22972.cs @@ -43,7 +43,6 @@ async void OnPageLoaded(object sender, EventArgs e) references.Add(new(webView.Handler.PlatformView)); await Navigation.PopAsync(); - webView.Handler.DisconnectHandler(); } From 9cfd7ddf1d5c3c44c7f1d25b0f3ab43bf3fc242f Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Tue, 23 Jul 2024 18:08:11 -0500 Subject: [PATCH 3/4] - make code more reusable --- .../TestCases.HostApp/Issues/Issue22972.cs | 73 +----------------- .../Utils/GarbageCollectionHelper.cs | 77 ++++++++++++++++++- .../Tests/Issues/Issue22972.cs | 2 +- .../TestCases.Shared.Tests/UtilExtensions.cs | 19 +++++ 4 files changed, 99 insertions(+), 72 deletions(-) diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue22972.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue22972.cs index 2bb0bd36d606..d8af187a9006 100644 --- a/src/Controls/tests/TestCases.HostApp/Issues/Issue22972.cs +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue22972.cs @@ -5,81 +5,16 @@ namespace Maui.Controls.Sample.Issues [Issue(IssueTracker.Github, 22972, "Win platform WebView cannot be release after its parent window get close")] public class Issue22972 : NavigationPage { - ContentPage _rootPage = new ContentPage { Title = "Page 1" }; public Issue22972() { - PushAsync(_rootPage); - _rootPage.Content = new VerticalStackLayout() + this.RunMemoryTest(() => { - new Label - { - Text = "If you don't see a success label this test has failed" - } - }; - - _rootPage.Loaded += OnPageLoaded; - } - - async void OnPageLoaded(object sender, EventArgs e) - { - var references = new List(); - CurrentPage.Loaded -= OnPageLoaded; - - { - var webView = new WebView + return new WebView { HeightRequest = 500, // NOTE: non-zero size required for Windows Source = new HtmlWebViewSource { Html = "

hi

" }, }; - var page = new ContentPage - { - Content = new VerticalStackLayout { webView } - }; - await Navigation.PushAsync(page); - await Task.Delay(1000); // give the WebView time to load - - references.Add(new(webView)); - references.Add(new(webView.Handler)); - references.Add(new(webView.Handler.PlatformView)); - - await Navigation.PopAsync(); - } - - - try - { - _rootPage.Content = new VerticalStackLayout() - { - new Label - { - Text = "Waiting for resources to cleanup" - } - }; - - - // Assert *before* the Window is closed - await GarbageCollectionHelper.WaitForGC(references.ToArray()); - _rootPage.Content = new VerticalStackLayout() - { - new Label - { - Text = "Success, everything has been cleaned up", - AutomationId = "Success" - } - }; - } - catch - { - var stillAlive = references.Where(x=> x.IsAlive).Select(x=> x.Target).ToList(); - _rootPage.Content = new VerticalStackLayout() - { - new Label - { - Text = "Failed to cleanup: " + string.Join(", ", stillAlive), - AutomationId = "Failed" - } - }; - } - } + }); + } } } diff --git a/src/Controls/tests/TestCases.HostApp/Utils/GarbageCollectionHelper.cs b/src/Controls/tests/TestCases.HostApp/Utils/GarbageCollectionHelper.cs index d366cc4b37f3..d0b365eacfb1 100644 --- a/src/Controls/tests/TestCases.HostApp/Utils/GarbageCollectionHelper.cs +++ b/src/Controls/tests/TestCases.HostApp/Utils/GarbageCollectionHelper.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Text; -using System.Threading.Tasks; - namespace Maui.Controls.Sample { public static class GarbageCollectionHelper @@ -50,5 +48,80 @@ public static async Task AssertEventually(this Func assertion, int timeout throw new Exception(message); } } + + public static void RunMemoryTest(this NavigationPage navigationPage, Func elementToTest) + { + ContentPage rootPage = new ContentPage { Title = "Page 1" }; + navigationPage.PushAsync(rootPage); + rootPage.Content = new VerticalStackLayout() + { + new Label + { + Text = "If you don't see a success label this test has failed" + } + }; + + rootPage.Loaded += OnPageLoaded; + + async void OnPageLoaded(object sender, EventArgs e) + { + var references = new List(); + rootPage.Loaded -= OnPageLoaded; + + { + var element = elementToTest(); + var page = new ContentPage + { + Content = new VerticalStackLayout { element } + }; + + await navigationPage.PushAsync(page); + await Task.Delay(500); // give the View time to load + + references.Add(new(element)); + references.Add(new(element.Handler)); + references.Add(new(element.Handler.PlatformView)); + + await navigationPage.PopAsync(); + } + + try + { + rootPage.Content = new VerticalStackLayout() + { + new Label + { + Text = "Waiting for resources to cleanup", + AutomationId = "Waiting" + + } + }; + + + // Assert *before* the Window is closed + await WaitForGC(references.ToArray()); + rootPage.Content = new VerticalStackLayout() + { + new Label + { + Text = "Success, everything has been cleaned up", + AutomationId = "Success" + } + }; + } + catch + { + var stillAlive = references.Where(x=> x.IsAlive).Select(x=> x.Target).ToList(); + rootPage.Content = new VerticalStackLayout() + { + new Label + { + Text = "Failed to cleanup: " + string.Join(", ", stillAlive), + AutomationId = "Failed" + } + }; + } + } + } } } diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22972.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22972.cs index 38dbaef9c4c6..5508b3f0fff1 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22972.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22972.cs @@ -16,7 +16,7 @@ public Issue22972(TestDevice device) : base(device) [Category(UITestCategories.WebView)] public void WebViewDoesNotLeak() { - App.WaitForElement("Success", timeout: TimeSpan.FromSeconds(10)); + App.AssertMemoryTest(); } } } \ No newline at end of file diff --git a/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs b/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs index 801862b6f34f..2c63caa036c7 100644 --- a/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs +++ b/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs @@ -1,4 +1,5 @@ using System.Drawing; +using NUnit.Framework; using UITest.Appium; using UITest.Appium.NUnit; using UITest.Core; @@ -56,5 +57,23 @@ public static int CenterY(this Rectangle rect) { return rect.Y + rect.Height / 2; } + + public static void AssertMemoryTest(this IApp app) + { + try + { + app.WaitForElement("Success", timeout: TimeSpan.FromSeconds(10)); + } + catch + { + var failure = app.FindElement("Failed")?.GetText(); + if(failure is not null) + { + Assert.Fail(failure); + } + + throw; + } + } } } \ No newline at end of file From 7d21d8ff8def85de0ef8e459c27864b4429f72f2 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Tue, 23 Jul 2024 18:11:26 -0500 Subject: [PATCH 4/4] Update GarbageCollectionHelper.cs --- .../tests/TestCases.HostApp/Utils/GarbageCollectionHelper.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Controls/tests/TestCases.HostApp/Utils/GarbageCollectionHelper.cs b/src/Controls/tests/TestCases.HostApp/Utils/GarbageCollectionHelper.cs index d0b365eacfb1..a5e22c447dbe 100644 --- a/src/Controls/tests/TestCases.HostApp/Utils/GarbageCollectionHelper.cs +++ b/src/Controls/tests/TestCases.HostApp/Utils/GarbageCollectionHelper.cs @@ -97,8 +97,6 @@ async void OnPageLoaded(object sender, EventArgs e) } }; - - // Assert *before* the Window is closed await WaitForGC(references.ToArray()); rootPage.Content = new VerticalStackLayout() {