From 6d4395e7b474f2aaceb323cf36e2337a22e48381 Mon Sep 17 00:00:00 2001 From: Matthew Leibowitz Date: Tue, 12 Dec 2023 14:18:18 +0200 Subject: [PATCH] Reduce the complexity of the AttachAndRun code (#19352) Sometimes the Windows tests fail with no apparent reason, hopefully the slightly reduced complexit and more linear flow makes remote debugging easier. A specific change to first remove the window content before removing the view from the container might help. --- .../AssertionExtensions.Windows.cs | 163 +++++++++--------- 1 file changed, 81 insertions(+), 82 deletions(-) diff --git a/src/TestUtils/src/DeviceTests/AssertionExtensions.Windows.cs b/src/TestUtils/src/DeviceTests/AssertionExtensions.Windows.cs index a697cffc6..b760c8779 100644 --- a/src/TestUtils/src/DeviceTests/AssertionExtensions.Windows.cs +++ b/src/TestUtils/src/DeviceTests/AssertionExtensions.Windows.cs @@ -24,7 +24,7 @@ namespace Microsoft.Maui.DeviceTests public static partial class AssertionExtensions { public static Task CreateColorAtPointErrorAsync(this CanvasBitmap bitmap, WColor expectedColor, int x, int y) => - CreateColorError(bitmap, $"Expected {expectedColor} at point {x},{y} in renderered view."); + CreateColorError(bitmap, $"Expected {expectedColor} at point {x},{y} in rendered view."); public static Task WaitForKeyboardToShow(this FrameworkElement view, int timeout = 1000) { @@ -104,7 +104,7 @@ namespace Microsoft.Maui.DeviceTests } public static Task CreateColorAtPointError(this CanvasBitmap bitmap, WColor expectedColor, int x, int y) => - CreateColorError(bitmap, $"Expected {expectedColor} at point {x},{y} in renderered view."); + CreateColorError(bitmap, $"Expected {expectedColor} at point {x},{y} in rendered view."); public static async Task CreateColorError(this CanvasBitmap bitmap, string message) => $"{message} This is what it looked like:{await bitmap.ToBase64StringAsync()}"; @@ -165,74 +165,8 @@ namespace Microsoft.Maui.DeviceTests if (view.Parent is Border wrapper) view = wrapper; - TaskCompletionSource? tcs = null; - TaskCompletionSource? unloadedTcs = null; - - if (view.Parent == null) - { - T result; - - try - { - await _attachAndRunSemaphore.WaitAsync(); - - // prepare to wait for element to be in the UI - tcs = new TaskCompletionSource(); - unloadedTcs = new TaskCompletionSource(); - - view.Loaded += OnViewLoaded; - - // attach to the UI - Grid grid; - var window = (Window)mauiContext!.Services!.GetService(typeof(Window))!; - - if (window.Content is not null) - throw new Exception("The window retrieved from the service is already attached to existing content"); - - window.Content = new Grid - { - HorizontalAlignment = WHorizontalAlignment.Center, - VerticalAlignment = WVerticalAlignment.Center, - Children = - { - (grid = new Grid - { - Width = view.Width, - Height = view.Height, - Children = - { - view - } - }) - } - }; - - window.Activate(); - - // wait for element to be loaded - await tcs.Task; - view.Unloaded += OnViewUnloaded; - - try - { - result = await Run(() => action(window)); - } - finally - { - grid.Children.Clear(); - await unloadedTcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); - await Task.Delay(10); - window.Close(); - } - } - finally - { - _attachAndRunSemaphore.Release(); - } - - return result; - } - else + // If the view has a parent, it's already attached to the UI + if (view.Parent != null) { // Window is not a XAML type so is never on the hierarchy var window = (Window)mauiContext!.Services!.GetService(typeof(Window))!; @@ -243,22 +177,87 @@ namespace Microsoft.Maui.DeviceTests return await Run(() => action(window)); } + // If the view has no parent, we need to attach it to the UI by creating a new window and using that as the host + try + { + await _attachAndRunSemaphore.WaitAsync(); + + // prepare to wait for element to be in the UI + var loadedTcs = new TaskCompletionSource(); + var unloadedTcs = new TaskCompletionSource(); + + view.Loaded += OnViewLoaded; + + // attach to the UI + Grid viewContainer; + var window = (Window)mauiContext!.Services!.GetService(typeof(Window))!; + + if (window.Content is not null) + throw new Exception("The window retrieved from the service is already attached to existing content"); + + window.Content = new Grid + { + HorizontalAlignment = WHorizontalAlignment.Center, + VerticalAlignment = WVerticalAlignment.Center, + Children = + { + (viewContainer = new Grid + { + Width = view.Width, + Height = view.Height, + Children = + { + view + } + }) + } + }; + + window.Activate(); + + // wait for element to be loaded + await loadedTcs.Task; + view.Unloaded += OnViewUnloaded; + + try + { + return await Run(() => action(window)); + } + finally + { + // release all views + window.Content = null; + viewContainer.Children.Clear(); + + // wait for an unload + await unloadedTcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + await Task.Delay(10); + + // close the window + window.Close(); + } + + void OnViewLoaded(object sender, RoutedEventArgs e) + { + view.Loaded -= OnViewLoaded; + loadedTcs?.SetResult(); + } + + void OnViewUnloaded(object sender, RoutedEventArgs e) + { + view.Unloaded -= OnViewUnloaded; + unloadedTcs?.SetResult(); + } + } + finally + { + _attachAndRunSemaphore.Release(); + } + static async Task Run(Func> action) { return await action(); } - - void OnViewLoaded(object sender, RoutedEventArgs e) - { - view.Loaded -= OnViewLoaded; - tcs?.SetResult(); - } - - void OnViewUnloaded(object sender, RoutedEventArgs e) - { - view.Unloaded -= OnViewUnloaded; - unloadedTcs?.SetResult(); - } } public static Task ToBitmap(this FrameworkElement view, IMauiContext mauiContext) =>