OverviewFilesCommitsIssuesDocsReleasesPeers

ifsrenderer-linux / AvaloniaShell.Tests / MainWindowHarness.cs

77 lines · csharp ref:
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;

using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Threading;

using IFSRenderer.AvaloniaShell.Tests.Fakes;

using Xunit;

namespace IFSRenderer.AvaloniaShell.Tests;

/// <summary>Brings up the real window against a GPU-less queue.</summary>
public static class MainWindowHarness
{
    /// <summary>
    /// Shows <see cref="MainWindow"/> with a <see cref="FakeRenderQueue"/> and waits for
    /// startup to settle.
    /// </summary>
    /// <remarks>
    /// Settings are redirected to a scratch directory: the suite must never read or write the
    /// developer's real <c>~/.config/ifsrenderer</c>, and the Welcome window has to stay shut
    /// or startup would block on a dialog.
    /// </remarks>
    public static (MainWindow Window, FakeRenderQueue Queue) Start()
    {
        string config = Path.Combine(Path.GetTempPath(),
            "ifsrenderer-tests-" + Guid.NewGuid().ToString("N"));
        Directory.CreateDirectory(Path.Combine(config, "ifsrenderer"));
        File.WriteAllText(Path.Combine(config, "ifsrenderer", "settings.json"),
            "{\"ShowWelcomeOnStartup\":false}");
        Environment.SetEnvironmentVariable("XDG_CONFIG_HOME", config);

        var queue = new FakeRenderQueue();
        var window = new MainWindow(queue);
        window.Show();

        // Startup is an async void handler: pump until it has produced a fractal.
        Pump(() => queue.Current is not null);
        Settle(queue);

        Assert.NotNull(queue.Current);
        return (window, queue);
    }

    /// <summary>Runs dispatcher jobs until <paramref name="until"/> holds, or fails.</summary>
    public static void Pump(Func<bool> until, int timeoutMs = 20000)
    {
        var clock = Stopwatch.StartNew();
        while (clock.ElapsedMilliseconds < timeoutMs)
        {
            Dispatcher.UIThread.RunJobs();
            if (until())
                return;
            Thread.Sleep(10);
        }
        Assert.Fail($"timed out after {timeoutMs} ms waiting for the window to settle");
    }

    /// <summary>Performs the gesture's engine work, then the UI refresh it posted back.</summary>
    public static void Settle(FakeRenderQueue queue)
    {
        queue.Drain();
        Dispatcher.UIThread.RunJobs();
    }

    public static void Click(Control root, string name)
    {
        var button = root.FindControl<Button>(name);
        Assert.NotNull(button);
        button.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
    }
}