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.
This commit is contained in:
Matthew Leibowitz 2023-12-12 14:18:18 +02:00 коммит произвёл GitHub
Родитель e3f4ed7685
Коммит 6d4395e7b4
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
1 изменённых файлов: 81 добавлений и 82 удалений

Просмотреть файл

@ -24,7 +24,7 @@ namespace Microsoft.Maui.DeviceTests
public static partial class AssertionExtensions
{
public static Task<string> 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<string> 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<string> CreateColorError(this CanvasBitmap bitmap, string message) =>
$"{message} This is what it looked like:<img>{await bitmap.ToBase64StringAsync()}</img>";
@ -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<T> Run(Func<Task<T>> 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<CanvasBitmap> ToBitmap(this FrameworkElement view, IMauiContext mauiContext) =>