XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESystem

【Rust】XFE系统,基于Rust和汇编

公开
关注 0 Fork 0 Star 0
UTF-8
using XFESystem.Core;
using XFESystem.Desktop;
using Xunit;

namespace XFESystem.Desktop.Tests;

public sealed class EditorRetirementTests
{
    [Fact]
    public void ClosedWorkerPageIsRetriedUntilItsOwnerActuallyReleasesIt()
    {
        var page = new WorkerPage();
        var preparation = new Preparation(page, publish: true);
        var desktop = Create(preparation);
        var editor = desktop.Open(DesktopApplication.Editor, Path);
        desktop.AdvanceFeedback(1);
        Assert.Same(page, editor.BrowserSession);

        desktop.Close(editor.Id);
        Assert.Empty(desktop.Windows);
        Assert.True(page.Cancelled);
        Assert.False(page.IsRetired);
        desktop.AdvanceFeedback(2);
        desktop.AdvanceFeedback(3);
        Assert.True(page.Advances >= 2);

        page.AllowRetirement = true;
        desktop.AdvanceFeedback(4);
        Assert.True(page.IsRetired);
        int completed = page.Advances;
        desktop.AdvanceFeedback(5);
        Assert.Equal(completed, page.Advances);
    }

    [Fact]
    public void ClosingWhilePreparingKeepsRetryingEvenThoughTheWindowIsGone()
    {
        var preparation = new Preparation(new WorkerPage(), publish: false);
        var desktop = Create(preparation);
        var editor = desktop.Open(DesktopApplication.Editor, Path);
        desktop.Close(editor.Id);
        Assert.Empty(desktop.Windows);
        Assert.True(preparation.Operation!.Cancelled);

        desktop.AdvanceFeedback(1);
        desktop.AdvanceFeedback(2);
        Assert.Equal(2, preparation.Operation.RetirementAdvances);
        preparation.Operation.AllowRetirement = true;
        desktop.AdvanceFeedback(3);
        Assert.True(preparation.Operation.IsRetired);
        desktop.AdvanceFeedback(4);
        Assert.Equal(3, preparation.Operation.RetirementAdvances);
    }

    [Fact]
    public void UiOwnedBrowserReleasesOnCloseOnlyOnce()
    {
        var page = new Page();
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace(), new Factory(page));
        var window = desktop.Open(DesktopApplication.Browser);
        desktop.Close(window.Id);
        desktop.Close(window.Id);
        Assert.Equal(1, page.Releases);
        Assert.Equal(Environment.CurrentManagedThreadId, page.ReleaseThread);
    }

    [Fact]
    public void FailedSynchronousEditorInitializationReleasesItsPartlyCreatedPage()
    {
        var page = new Page { FailEditor = true };
        var storage = new MemoryWorkspace();
        storage.WriteText(Path, Source);
        var desktop = new DesktopSession(new Canvas(), storage, new Factory(page));
        var editor = desktop.Open(DesktopApplication.Editor, Path);
        Assert.Null(editor.BrowserSession);
        Assert.Equal(Source, editor.Content);
        Assert.Equal(1, page.Releases);
        desktop.Close(editor.Id);
        Assert.Equal(1, page.Releases);
    }

    private const string Source = "using System;";
    private static readonly string Path = SystemPaths.Combine(SystemPaths.Documents, "Retire.cs");
    private static DesktopSession Create(Preparation preparation)
    {
        var storage = new MemoryWorkspace();
        storage.WriteText(Path, Source);
        return new(new Canvas(), storage, new Factory(new Page()), editorPreparation: preparation);
    }

    private sealed class Preparation(WorkerPage page, bool publish) : IEditorPreparationService
    {
        public Operation? Operation;
        public IEditorPreparationOperation Begin(EditorPreparationRequest request) => Operation = new(request, page, publish);
    }
    private sealed class Operation(EditorPreparationRequest request, WorkerPage page, bool publish) : IEditorPreparationOperation
    {
        private bool taken;
        public EditorPreparationRequest Request => request;
        public bool Cancelled, AllowRetirement;
        public int RetirementAdvances;
        public bool IsRetired { get; private set; }
        public void Cancel() => Cancelled = true;
        public void AdvanceRetirement(long now) { RetirementAdvances++; IsRetired = AllowRetirement; }
        public bool TryTakeResult(out EditorPreparationResult result)
        {
            result = new(page, null, 42);
            if (!publish || taken || Cancelled) return false;
            taken = true;
            return true;
        }
    }
    private class Page : IInteractiveBrowserSession
    {
        public int Releases, ReleaseThread;
        public bool FailEditor;
        public BrowserPage CurrentPage => new("about:Test", "Test", Source);
        public long Revision => 0;
        public double DocumentHeight => 10;
        public BrowserPage Navigate(string address) => CurrentPage;
        public BrowserPage NavigateEditor(string source, string path) => FailEditor ? throw new IOException("partly initialized") : CurrentPage;
        public void Paint(IDesktopCanvas canvas, DesktopBounds viewport, double scrollY) { }
        public bool Pointer(BrowserPointerInput input) => false;
        public bool Key(BrowserKeyboardInput input) => false;
        public virtual bool AdvanceTime(long milliseconds) => false;
        public void Release() { Releases++; ReleaseThread = Environment.CurrentManagedThreadId; }
    }
    private sealed class WorkerPage : Page, IBackgroundEditorSession
    {
        public bool Cancelled, AllowRetirement;
        public int Advances;
        public bool IsRetired { get; private set; }
        public BackgroundEditorDiagnostics Diagnostics => new("{}", "", 0, 0, 0, 0, 42);
        public long RasterPaintCount => 0;
        public long CachedPaintCount => 0;
        public bool RequestEditorReadback(Action<string?, string?> completed) => false;
        public void Cancel() => Cancelled = true;
        public override bool AdvanceTime(long milliseconds)
        {
            if (Cancelled) { Advances++; IsRetired = AllowRetirement; }
            return false;
        }
    }
    private sealed class Factory(Page page) : IBrowserService, IInteractiveBrowserFactory
    {
        public IInteractiveBrowserSession CreatePageSession() => page;
        public BrowserPage Navigate(string address) => page.CurrentPage;
        public string ExecuteJavaScript(string source) => "";
        public string PreviewMarkdown(string source) => "";
    }
    private sealed class Canvas : IDesktopCanvas
    {
        public int Width => 1920;
        public int Height => 1080;
        public void Fill(uint rgb, int x, int y, int width, int height) { }
        public void Line(uint rgb, int x1, int y1, int x2, int y2) { }
        public void Text(string text, uint rgb, int x, int y) { }
        public void Present() { }
    }
}