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.Core.Tests;

public sealed class DesktopTests
{
    private sealed class Canvas(int width = 1280, int height = 800, bool cached = false) : IDesktopIconCanvas
    {
        public int Width => width;
        public int Height => height;
        public bool SupportsSceneCache => cached;
        public int Frames { get; private set; }
        public int SavedScenes { get; private set; }
        public List<DesktopBounds> RestoredRegions { get; } = [];
        public List<DesktopBounds> PresentedRegions { get; } = [];
        public List<DesktopBounds> SceneRegions { get; } = [];
        public List<DesktopBounds> SavedRegions { get; } = [];
        public List<DesktopIcon> Icons { get; } = [];
        public List<(uint Color, int X, int Y, int Width, int Height)> Fills { get; } = [];
        public List<string> TextRuns { get; } = [];
        public List<(string Text, int X, int Y)> DrawnTexts { get; } = [];
        public int TextWidth(string text) => text.Sum(c => c < 128 ? 8 : 16);
        public void Fill(uint rgb, int x, int y, int width, int height) => Fills.Add((rgb, x, y, width, 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) { TextRuns.Add(text); DrawnTexts.Add((text, x, y)); }
        public void Present() => Frames++;
        public void DrawIcon(DesktopIcon icon, int x, int y, int size) => Icons.Add(icon);
        public void SaveScene() => SavedScenes++;
        public void SaveScene(DesktopBounds damage) { SavedRegions.Add(damage); SavedScenes++; }
        public void BeginScene(DesktopBounds damage) => SceneRegions.Add(damage);
        public void RestoreSceneRegion(DesktopBounds bounds) => RestoredRegions.Add(bounds);
        public void Present(DesktopBounds damage) { PresentedRegions.Add(damage); Frames++; }
    }
    private sealed class Browser : IBrowserService
    {
        public BrowserPage Navigate(string address) => new(address, "Test", "");
        public string ExecuteJavaScript(string source) => "";
        public string PreviewMarkdown(string markdown) => markdown;
    }

    [Fact]
    public void StartupIsDesktopAndIdleDoesNotRenderRepeatedly()
    {
        var canvas = new Canvas();
        var desktop = new DesktopSession(canvas, new MemoryWorkspace());
        Assert.Empty(desktop.Windows);
        desktop.Render(); desktop.Render();
        Assert.Equal(1, canvas.Frames);
        Assert.Contains("XFESystem", canvas.TextRuns);
    }

    [Fact]
    public void MouseOpensFilesAndFileEntryOpensActualEditor()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        desktop.Pointer(50, 108, true); desktop.Pointer(50, 108, false);
        Assert.Equal(DesktopApplication.Files, desktop.ActiveWindow!.Application);
        var b = desktop.ActiveWindow.Bounds;
        desktop.Pointer(b.X + 90, b.Y + 106, true); desktop.Pointer(b.X + 90, b.Y + 106, false);
        Assert.Equal(DesktopApplication.Editor, desktop.ActiveWindow!.Application);
        Assert.EndsWith("Hello.cs", desktop.ActiveWindow.Location);
        Assert.Contains("Console.ForegroundColor", desktop.ActiveWindow.Content);
    }

    [Fact]
    public void EditingMiddleAndSavingAffectsOnlyGuestMemory()
    {
        var storage = new MemoryWorkspace(false);
        var path = SystemPaths.Combine(SystemPaths.Documents, "Test.txt");
        storage.WriteText(path, "ab\ncd");
        var desktop = new DesktopSession(new Canvas(), storage);
        var window = desktop.Open(DesktopApplication.Editor, path);
        desktop.Key(DesktopKey.Right); desktop.Key(DesktopKey.Character, 'X');
        Assert.Equal("aXb\ncd", window.Content);
        desktop.Key(DesktopKey.Down); desktop.Key(DesktopKey.Home); desktop.Key(DesktopKey.Delete);
        desktop.Key(DesktopKey.Save);
        Assert.Equal("aXb\nd", storage.ReadText(path));
        Assert.False(window.IsDirty);
        Assert.Contains("RAM live", window.Status);
    }

    [Fact]
    public void UnsavedCloseIsPreventedUntilExplicitDiscard()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        var window = desktop.Open(DesktopApplication.Editor);
        desktop.Key(DesktopKey.Character, 'x');
        desktop.Close(window.Id);
        Assert.Single(desktop.Windows);
        Assert.Contains("Unsaved", window.Status);
        desktop.Key(DesktopKey.Escape);
        Assert.Empty(desktop.Windows);
    }

    [Fact]
    public void DraggingKeepsWindowWithinScreenAndScrollIsBounded()
    {
        var storage = new MemoryWorkspace();
        var path = SystemPaths.Combine(SystemPaths.Documents, "Long.txt");
        storage.WriteText(path, string.Join('\n', Enumerable.Range(0, 100)));
        var desktop = new DesktopSession(new Canvas(), storage);
        var window = desktop.Open(DesktopApplication.Editor, path);
        var b = window.Bounds;
        desktop.Pointer(b.X + 20, b.Y + 15, true); desktop.Pointer(1279, 799, true); desktop.Pointer(1279, 799, false);
        Assert.InRange(window.Bounds.X, 0, 1280 - b.Width);
        Assert.InRange(window.Bounds.Y, 0, 800 - b.Height - 48);
        desktop.Pointer(0, 0, false, -1000);
        Assert.InRange(window.ScrollLine, 1, 100);
        desktop.Pointer(0, 0, false, 1000);
        Assert.Equal(0, window.ScrollLine);
    }

    [Fact]
    public void UnconnectedBrowserDoesNotClaimNavigationWorks()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        var browser = desktop.Open(DesktopApplication.Browser);
        Assert.Contains("unavailable", browser.Status);
        Assert.Contains("does not claim", browser.Content);
    }

    [Fact]
    public void ManagedFileUsesOnlyExplicitGuestApplicationCallback()
    {
        string? invoked = null;
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace(), managedApplication: path => invoked = path);
        desktop.OpenFile("X:\\Programs\\Hello.dll");
        Assert.Equal("X:\\Programs\\Hello.dll", invoked);
        Assert.Empty(desktop.Windows);
    }

    [Fact]
    public void WindowLimitDoesNotEscapePointerOrPreviewEvent()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace(), new Browser());
        for (int i = 0; i < 12; i++) desktop.Open(DesktopApplication.Editor);
        desktop.Pointer(50, 108, true);
        Assert.Equal(12, desktop.Windows.Count);
        desktop.Key(DesktopKey.Run);
        Assert.Equal(12, desktop.Windows.Count);
        Assert.Contains("Close a window", desktop.ActiveWindow!.Status);
    }

    [Fact]
    public void FilesStatusBarCannotOpenAnInvisibleEntry()
    {
        var storage = new MemoryWorkspace(false);
        for (int i = 0; i < 16; i++) storage.WriteText(SystemPaths.Combine(SystemPaths.Documents, $"File{i:D2}.txt"), "");
        var desktop = new DesktopSession(new Canvas(), storage);
        var files = desktop.Open(DesktopApplication.Files);
        desktop.Pointer(files.Bounds.X + 50, files.Bounds.Y + files.Bounds.Height - 20, true);
        Assert.Same(files, desktop.ActiveWindow);
        Assert.Single(desktop.Windows);
    }

    [Fact]
    public void BrowserAddressFocusReplacesExistingAddress()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace(), new Browser());
        var window = desktop.Open(DesktopApplication.Browser);
        foreach (char c in "http://example.test/") desktop.Key(DesktopKey.Character, c);
        Assert.Equal("http://example.test/", window.Location);
        desktop.Key(DesktopKey.Enter);
        desktop.Key(DesktopKey.FocusAddress);
        foreach (char c in "about:Start") desktop.Key(DesktopKey.Character, c);
        Assert.Equal("about:Start", window.Location);
    }

    [Fact]
    public void RunningAnotherProgramShowsItsOutputInExistingTerminal()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        var terminal = desktop.Open(DesktopApplication.Terminal);
        var shell = Assert.IsType<TerminalShell>(terminal.Shell);
        Assert.True(desktop.PasteTerminalInput("echo Previous shell output"));
        desktop.Key(DesktopKey.Enter);
        Assert.True(desktop.PasteTerminalInput("draft command"));
        desktop.Key(DesktopKey.Left);
        int caret = shell.Caret;
        string directory = shell.CurrentDirectory;
        desktop.Open(DesktopApplication.Files);
        var output = new XFESystem.Console.TerminalBuffer(40, 10);
        output.Write("\u001b[1;38;2;23;45;67;48;2;7;8;9mProgram output\u001b[0m");
        var snapshot = output.Snapshot();
        desktop.SetTerminalSnapshot(snapshot);
        Assert.Same(terminal, desktop.ActiveWindow);
        Assert.Equal(2, desktop.Windows.Count);
        Assert.Same(terminal, Assert.Single(desktop.Windows, window => window.Application == DesktopApplication.Terminal));
        Assert.Same(shell, terminal.Shell);
        Assert.Equal(directory, shell.CurrentDirectory);
        Assert.Equal("draft command", shell.Input);
        Assert.Equal(caret, shell.Caret);
        Assert.Equal(new[] { "echo Previous shell output" }, shell.History);
        var combined = Assert.IsType<XFESystem.Console.TerminalSnapshot>(terminal.Terminal);
        string transcript = string.Concat(combined.Cells.Select(cell => cell.Text));
        Assert.Contains("XFESystem|XFEstudio", transcript);
        Assert.Contains(desktop.TerminalVersion, transcript);
        Assert.Contains("Type help for commands.", transcript);
        Assert.Contains("Previous shell output", transcript);
        Assert.Contains("Program output", transcript);
        int programRow = Assert.Single(Enumerable.Range(0, combined.Rows), row =>
            string.Concat(Enumerable.Range(0, combined.Columns).Select(column => combined.GetCell(column, row).Text))
                .StartsWith("Program output", StringComparison.Ordinal));
        for (int index = 0; index < "Program output".Length; index++)
            Assert.Equal(snapshot.GetCell(index, 0).Style, combined.GetCell(index, programRow).Style);
        desktop.Key(DesktopKey.Up);
        Assert.Equal("echo Previous shell output", shell.Input);
        desktop.Key(DesktopKey.Down);
        Assert.Equal("draft command", shell.Input);
    }

    [Fact]
    public void ChineseTextUsesActualGlyphWidthForClippingAndCaretPlacement()
    {
        var canvas = new Canvas();
        var storage = new MemoryWorkspace();
        var path = SystemPaths.Combine(SystemPaths.Documents, "Wide.txt");
        storage.WriteText(path, new string('中', 100));
        var desktop = new DesktopSession(canvas, storage);
        var window = desktop.Open(DesktopApplication.Editor, path);
        desktop.Pointer(window.Bounds.X + 20 + 32, window.Bounds.Y + 76, true);
        desktop.Pointer(window.Bounds.X + 20 + 32, window.Bounds.Y + 76, false);
        Assert.Equal(2, window.Caret);
        desktop.Render();
        var body = Assert.Single(canvas.DrawnTexts, t => t.Y == window.Bounds.Y + 76);
        Assert.True(canvas.TextWidth(body.Text) <= window.Bounds.Width - 48);
        Assert.EndsWith("...", body.Text);
        desktop.Key(DesktopKey.End);
        desktop.Render();
        Assert.Equal(100, window.Caret);
    }

    private static void Click(DesktopSession desktop, int x, int y, long timestamp = -1)
    {
        desktop.Pointer(x, y, true, timestampMs: timestamp);
        desktop.Pointer(x, y, false, timestampMs: timestamp);
    }

    [Fact]
    public void TitleDoubleClickMaximizesToWorkAreaAndRestoresExactBounds()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        var window = desktop.Open(DesktopApplication.Files); var original = window.Bounds;
        Click(desktop, original.X + 100, original.Y + 16, 100);
        Click(desktop, original.X + 100, original.Y + 16, 220);
        Assert.True(window.IsMaximized);
        Assert.Equal(desktop.WorkArea, window.Bounds);
        Click(desktop, 100, desktop.WorkArea.Y + 16, 500);
        Click(desktop, 100, desktop.WorkArea.Y + 16, 640);
        Assert.False(window.IsMaximized);
        Assert.Equal(original, window.Bounds);
    }

    [Fact]
    public void SlowOrDistantTitleClicksDoNotMaximize()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        var window = desktop.Open(DesktopApplication.Files); var b = window.Bounds;
        Click(desktop, b.X + 80, b.Y + 16, 100);
        Click(desktop, b.X + 80, b.Y + 16, 800);
        Click(desktop, b.X + 100, b.Y + 16, 900);
        Assert.False(window.IsMaximized);
    }

    [Fact]
    public void DraggingMaximizedTitleRestoresWindowUnderPointer()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        var window = desktop.Open(DesktopApplication.Editor); var original = window.Bounds;
        desktop.ToggleMaximize(window.Id);
        desktop.Pointer(320, 15, true, timestampMs: 1000);
        desktop.Pointer(350, 38, true, timestampMs: 1050);
        desktop.Pointer(350, 38, false, timestampMs: 1050);
        Assert.False(window.IsMaximized);
        Assert.Equal(original.Width, window.Bounds.Width);
        Assert.Equal(original.Height, window.Bounds.Height);
        Assert.Equal(150, window.Bounds.X);
        Assert.Equal(23, window.Bounds.Y);
    }

    [Fact]
    public void ResizeGripClampsMinimumAndWorkAreaWithoutMovingOrigin()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        var window = desktop.Open(DesktopApplication.Files); var original = window.Bounds;
        desktop.Pointer(original.X + original.Width - 2, original.Y + original.Height - 2, true);
        desktop.Pointer(1279, 799, true); desktop.Pointer(1279, 799, false);
        Assert.Equal(original.X, window.Bounds.X); Assert.Equal(original.Y, window.Bounds.Y);
        Assert.True(window.Bounds.X + window.Bounds.Width <= desktop.WorkArea.Width);
        Assert.True(window.Bounds.Y + window.Bounds.Height <= desktop.WorkArea.Y + desktop.WorkArea.Height);
        var b = window.Bounds;
        desktop.Pointer(b.X + b.Width - 2, b.Y + b.Height - 2, true);
        desktop.Pointer(0, 0, true); desktop.Pointer(0, 0, false);
        Assert.Equal(320, window.Bounds.Width); Assert.Equal(200, window.Bounds.Height);
        Assert.Equal(original.X, window.Bounds.X); Assert.Equal(original.Y, window.Bounds.Y);
    }

    [Fact]
    public void TitleHasOnlyMinimizeAndCloseWhileDoubleClickMaximizes()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        var window = desktop.Open(DesktopApplication.Files); var b = window.Bounds;
        Click(desktop, b.X + b.Width - 80, b.Y + 16);
        Assert.False(window.IsMinimized); Assert.False(window.IsMaximized);
        Click(desktop, b.X + b.Width - 48, b.Y + 16);
        Assert.True(window.IsMinimized); Assert.Null(desktop.ActiveWindow);
        desktop.Activate(window.Id); b = window.Bounds;
        Click(desktop, b.X + 150, b.Y + 16, 1000);
        Click(desktop, b.X + 150, b.Y + 16, 1100);
        Assert.True(window.IsMaximized);
        b = window.Bounds; Click(desktop, b.X + b.Width - 16, b.Y + 16);
        Assert.Empty(desktop.Windows);
    }

    [Fact]
    public void TaskbarButtonsKeepCreationOrderAndRestoreOrMinimizeWindows()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        var first = desktop.Open(DesktopApplication.Files); var second = desktop.Open(DesktopApplication.Terminal);
        Click(desktop, 90, 776); Assert.Same(first, desktop.ActiveWindow);
        Click(desktop, 90, 776); Assert.True(first.IsMinimized); Assert.Same(second, desktop.ActiveWindow);
        Click(desktop, 90, 776); Assert.False(first.IsMinimized); Assert.Same(first, desktop.ActiveWindow);
    }

    [Fact]
    public void StartMenuCanOpenApplicationsWithMouseOrKeyboard()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        desktop.Key(DesktopKey.StartMenu); Assert.True(desktop.IsStartMenuOpen);
        desktop.Key(DesktopKey.Down); desktop.Key(DesktopKey.Enter);
        Assert.Equal(DesktopApplication.Editor, desktop.ActiveWindow!.Application);
        Assert.False(desktop.IsStartMenuOpen);
        Click(desktop, 30, 776); Assert.True(desktop.IsStartMenuOpen);
        Click(desktop, 80, 644);
        Assert.Equal(DesktopApplication.Browser, desktop.ActiveWindow!.Application);
        desktop.Key(DesktopKey.StartMenu); desktop.Key(DesktopKey.Escape);
        Assert.False(desktop.IsStartMenuOpen); Assert.Equal(2, desktop.Windows.Count);
    }

    [Fact]
    public void AltTabCyclesStableMruAndCommitsOnlyOnRelease()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        var first = desktop.Open(DesktopApplication.Files);
        var second = desktop.Open(DesktopApplication.Editor);
        var third = desktop.Open(DesktopApplication.Terminal);
        desktop.Key(DesktopKey.SwitchWindow); Assert.Same(second, desktop.ActiveWindow);
        desktop.Key(DesktopKey.SwitchWindow); Assert.Same(first, desktop.ActiveWindow);
        desktop.Key(DesktopKey.SwitchWindow); Assert.Same(third, desktop.ActiveWindow);
        desktop.Key(DesktopKey.SwitchWindowBack); Assert.Same(first, desktop.ActiveWindow);
        desktop.Key(DesktopKey.EndWindowSwitch); Assert.False(desktop.IsWindowSwitching);
        desktop.Key(DesktopKey.SwitchWindow); Assert.Same(third, desktop.ActiveWindow);
        desktop.Key(DesktopKey.EndWindowSwitch);
    }

    [Fact]
    public void ShowDesktopPreservesPreviouslyMinimizedWindows()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        var first = desktop.Open(DesktopApplication.Files); var second = desktop.Open(DesktopApplication.Editor);
        desktop.Minimize(first.Id);
        desktop.Key(DesktopKey.ShowDesktop); Assert.Null(desktop.ActiveWindow);
        desktop.Key(DesktopKey.ShowDesktop); Assert.Same(second, desktop.ActiveWindow); Assert.True(first.IsMinimized);
        desktop.Key(DesktopKey.OpenFiles); Assert.Equal(DesktopApplication.Files, desktop.ActiveWindow!.Application);
    }

    [Fact]
    public void SnapAndMaximizeRestoreOriginalGeometryAndRespectTaskbar()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        var window = desktop.Open(DesktopApplication.Files); var original = window.Bounds;
        desktop.Key(DesktopKey.SnapRight);
        Assert.True(window.IsSnapped); Assert.Equal(640, window.Bounds.X); Assert.Equal(640, window.Bounds.Width);
        Assert.Equal(desktop.WorkArea.Height, window.Bounds.Height);
        desktop.Key(DesktopKey.Maximize); Assert.Equal(desktop.WorkArea, window.Bounds);
        desktop.Key(DesktopKey.Restore); Assert.Equal(original, window.Bounds);
        desktop.Key(DesktopKey.Restore); Assert.True(window.IsMinimized);
    }

    [Fact]
    public void SmallScreenStillContainsWindowsAndTaskbar()
    {
        var desktop = new DesktopSession(new Canvas(480, 360), new MemoryWorkspace());
        for (int i = 0; i < 6; i++)
        {
            var window = desktop.Open(DesktopApplication.Editor);
            Assert.True(window.Bounds.X >= 0 && window.Bounds.Y >= desktop.WorkArea.Y);
            Assert.True(window.Bounds.X + window.Bounds.Width <= 480);
            Assert.True(window.Bounds.Y + window.Bounds.Height <= 312);
        }
        desktop.Render();
    }

    [Fact]
    public void PointerOnlyFrameRestoresCachedRegionsWithoutRedrawingScene()
    {
        var canvas = new Canvas(cached: true);
        var desktop = new DesktopSession(canvas, new MemoryWorkspace());
        desktop.Render(); Assert.Equal(1, canvas.SavedScenes);
        canvas.RestoredRegions.Clear();
        int textCount = canvas.TextRuns.Count;
        desktop.Pointer(600, 500, false); desktop.Render();
        Assert.Equal(textCount, canvas.TextRuns.Count);
        Assert.Equal(1, canvas.SavedScenes); Assert.Equal(2, canvas.RestoredRegions.Count);
        Assert.Single(canvas.PresentedRegions);
        desktop.Open(DesktopApplication.Files); desktop.Render();
        Assert.Equal(2, canvas.SavedScenes);
    }

    [Fact]
    public void FilesAndApplicationEntrancesRequestTheirSpecificThemeIcons()
    {
        var canvas = new Canvas(); var storage = new MemoryWorkspace();
        storage.WriteText(SystemPaths.Combine(SystemPaths.Documents, "Readme.md"), "");
        storage.WriteText(SystemPaths.Combine(SystemPaths.Documents, "Program.dll"), "");
        storage.CreateDirectory(SystemPaths.Combine(SystemPaths.Documents, "Folder"));
        var desktop = new DesktopSession(canvas, storage);
        desktop.Open(DesktopApplication.Files); desktop.Render();
        Assert.Contains(DesktopIcon.Files, canvas.Icons); Assert.Contains(DesktopIcon.Editor, canvas.Icons);
        Assert.Contains(DesktopIcon.Browser, canvas.Icons); Assert.Contains(DesktopIcon.Terminal, canvas.Icons);
        Assert.Contains(DesktopIcon.Folder, canvas.Icons); Assert.Contains(DesktopIcon.CodeFile, canvas.Icons);
        Assert.Contains(DesktopIcon.MarkdownFile, canvas.Icons); Assert.Contains(DesktopIcon.TextFile, canvas.Icons);
        Assert.Contains(DesktopIcon.ApplicationFile, canvas.Icons);
    }

    [Fact]
    public void DragResizeScrollAndTypingUseBoundedSceneDamage()
    {
        var canvas = new Canvas(cached: true); var storage = new MemoryWorkspace();
        var path = SystemPaths.Combine(SystemPaths.Documents, "Long.txt");
        storage.WriteText(path, string.Join('\n', Enumerable.Range(0, 100)));
        var desktop = new DesktopSession(canvas, storage);
        var window = desktop.Open(DesktopApplication.Editor, path); desktop.Render();
        var b = window.Bounds;
        desktop.Pointer(b.X + 100, b.Y + 15, true); desktop.Render();
        desktop.Pointer(b.X + 130, b.Y + 35, true); desktop.Render();
        Assert.True(canvas.SavedRegions[^1].Width < canvas.Width);
        Assert.True(canvas.SavedRegions[^1].Height < canvas.Height);
        desktop.Pointer(b.X + 130, b.Y + 35, false);
        desktop.Pointer(window.Bounds.X + 60, window.Bounds.Y + 100, false, -1); desktop.Render();
        Assert.True(canvas.SavedRegions[^1].Width < canvas.Width);
        desktop.Key(DesktopKey.Character, 'X'); desktop.Render();
        Assert.True(canvas.SavedRegions[^1].Width < canvas.Width);
        Assert.Equal(new DesktopBounds(0, 0, canvas.Width, canvas.Height), canvas.SceneRegions[^1]);
        b = window.Bounds;
        desktop.Pointer(b.X + b.Width - 2, b.Y + b.Height - 2, true); desktop.Render();
        desktop.Pointer(b.X + b.Width + 20, b.Y + b.Height + 20, true); desktop.Render();
        Assert.True(canvas.SavedRegions[^1].Width < canvas.Width);
    }

    [Fact]
    public void LegacyCanvasPointerUsesOnlyTheNarrowThemeCoreWithoutAnOpaqueHalo()
    {
        var canvas = new Canvas(cached: true);
        var desktop = new DesktopSession(canvas, new MemoryWorkspace()); desktop.Render();
        canvas.Fills.Clear(); desktop.Pointer(600, 500, false); desktop.Render();
        Assert.NotEmpty(canvas.Fills);
        Assert.All(canvas.Fills, fill => Assert.Equal(desktop.Theme.AccentStroke, fill.Color));
        Assert.True(canvas.Fills.Sum(fill => fill.Width * fill.Height) < 60);
        Assert.Contains(canvas.Fills, fill => fill.Width == 1);
    }

    [Fact]
    public void ScrollingShortLinesDamagesOnlyInkColumnsAndKeepsUnchangedPointer()
    {
        var canvas = new Canvas(cached: true); var storage = new MemoryWorkspace();
        var path = SystemPaths.Combine(SystemPaths.Documents, "Narrow.txt");
        storage.WriteText(path, string.Join('\n', Enumerable.Range(0, 100).Select(i => $"row{i:D2}")));
        var desktop = new DesktopSession(canvas, storage);
        var window = desktop.Open(DesktopApplication.Editor, path);
        desktop.Pointer(window.Bounds.X + 180, window.Bounds.Y + 150, false);
        desktop.Render(); canvas.RestoredRegions.Clear();
        desktop.Pointer(window.Bounds.X + 180, window.Bounds.Y + 150, false, -1); desktop.Render();
        Assert.True(canvas.SavedRegions[^1].Width < 80);
        Assert.True(canvas.PresentedRegions[^1].Width < 80);
        Assert.Empty(canvas.RestoredRegions);
        desktop.Key(DesktopKey.End);
        desktop.Key(DesktopKey.Character, 'X'); desktop.Render();
        Assert.True(canvas.SavedRegions[^1].Width > 80); // Edits still include dirty status/title and full window bounds.
    }

    [Fact]
    public void DesktopAndWindowSwitchCancelExistingMouseCapture()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        desktop.Open(DesktopApplication.Files); var editor = desktop.Open(DesktopApplication.Editor);
        var b = editor.Bounds;
        desktop.Pointer(b.X + 100, b.Y + 16, true, timestampMs: 100);
        desktop.Key(DesktopKey.ShowDesktop);
        desktop.Pointer(b.X + 150, b.Y + 66, true, timestampMs: 200);
        Assert.Equal(b, editor.Bounds); Assert.True(editor.IsMinimized);
        desktop.Pointer(b.X + 150, b.Y + 66, false);
        desktop.Key(DesktopKey.ShowDesktop);
        desktop.Pointer(b.X + 100, b.Y + 16, true, timestampMs: 400);
        desktop.Key(DesktopKey.SwitchWindow);
        desktop.Pointer(b.X + 170, b.Y + 86, true, timestampMs: 450);
        Assert.Equal(b, editor.Bounds);
        desktop.Pointer(b.X + 170, b.Y + 86, false);
        desktop.Key(DesktopKey.EndWindowSwitch); desktop.Activate(editor.Id);
        desktop.Pointer(b.X + 100, b.Y + 16, true, timestampMs: 700);
        desktop.Key(DesktopKey.SnapRight); var snapped = editor.Bounds;
        desktop.Pointer(b.X + 170, b.Y + 86, true, timestampMs: 750);
        Assert.Equal(snapped, editor.Bounds); Assert.True(editor.IsSnapped);
        desktop.Pointer(b.X + 170, b.Y + 86, false);
    }

    [Fact]
    public void TitleButtonsAndTaskbarBreakPreviousDoubleClickSequence()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        var window = desktop.Open(DesktopApplication.Files);
        desktop.Key(DesktopKey.SnapLeft); var b = window.Bounds;
        Click(desktop, b.X + 100, b.Y + 16, 100);
        Click(desktop, b.X + b.Width - 48, b.Y + 16, 200);
        Assert.True(window.IsMinimized);
        Click(desktop, 90, 776, 210);
        Click(desktop, b.X + 100, b.Y + 16, 220);
        Assert.False(window.IsMaximized);
        desktop.ToggleMaximize(window.Id);
        Assert.True(window.IsMaximized);
        Click(desktop, 100, desktop.WorkArea.Y + 16, 250);
        Assert.True(window.IsMaximized);
        Click(desktop, 90, 776, 280); Assert.True(window.IsMinimized);
        Click(desktop, 90, 776, 300); Assert.False(window.IsMinimized);
        Click(desktop, 100, desktop.WorkArea.Y + 16, 320);
        Assert.True(window.IsMaximized);
    }
}