using System;
using System.Threading;
using Avalonia.Controls;
using Avalonia.Headless;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Themes.Simple;
using Avalonia.Threading;
using Xunit;
namespace Avalonia.UnitTests;
///
/// Test application running on the real headless platform, set up through
/// so that the platform owns its own slice of the locator.
///
///
/// This is deliberately not a flavour: the headless platform registers
/// services of its own (keyboard device, clipboard, render loop, platform settings, hotkey config),
/// and binds the same keys from its service bag, so the two
/// overwrite each other depending on when the platform happens to initialize.
///
public class HeadlessUnitTestApplication : Application
{
public HeadlessUnitTestApplication()
{
Styles.Add(new SimpleTheme());
}
public static Scope Start(AvaloniaHeadlessPlatformOptions? options = null)
{
var scope = AvaloniaLocator.EnterScope();
var oldContext = SynchronizationContext.Current;
try
{
Dispatcher.ResetBeforeUnitTests();
AppBuilder.Configure()
// Popups default to dedicated top-levels here, matching the desktop platforms
// and the app used by Avalonia.Headless.UnitTests.
.UseHeadless(options ?? new AvaloniaHeadlessPlatformOptions { OverlayPopups = false })
.AfterPlatformServicesSetup(_ => AvaloniaLocator.CurrentMutable
.Bind().ToConstant(new TestFontManager()))
.SetupUnsafe();
}
catch
{
scope.Dispose();
throw;
}
return new Scope(scope, oldContext);
}
public sealed class Scope(IDisposable locatorScope, SynchronizationContext? oldContext) : IDisposable
{
///
/// Runs pending dispatcher jobs, tied to the test's cancellation token. Headless top-levels
/// post activation, resizes and rendering, so tests have to let those settle between acts.
///
public void RunJobs(DispatcherPriority? priority = null) =>
Dispatcher.UIThread.RunJobs(priority, TestContext.Current.CancellationToken);
public void Dispose()
{
if (Dispatcher.UIThread.CheckAccess())
{
Dispatcher.UIThread.RunJobs();
}
(AvaloniaLocator.Current.GetService() as ToolTipService)?.Dispose();
(AvaloniaLocator.Current.GetService() as IDisposable)?.Dispose();
(AvaloniaLocator.Current.GetService() as IDisposable)?.Dispose();
Dispatcher.ResetForUnitTests();
locatorScope.Dispose();
Dispatcher.ResetBeforeUnitTests();
SynchronizationContext.SetSynchronizationContext(oldContext);
}
}
}