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 ThemeTests
{
    private sealed class Canvas : IDesktopCanvas
    {
        public int Width => 1280;
        public int Height => 800;
        public bool SupportsSceneCache => true;
        public int SavedScenes { get; private set; }
        public List<(uint Color, int X, int Y, int Width, int Height)> Fills { get; } = [];
        public List<(string Text, uint Color)> Texts { get; } = [];
        public List<DesktopBounds> SavedRegions { get; } = [];
        public void Fill(uint color, int x, int y, int width, int height) => Fills.Add((color, x, y, width, height));
        public void Line(uint color, int x1, int y1, int x2, int y2) { }
        public void Text(string text, uint color, int x, int y) => Texts.Add((text, color));
        public void Present() { }
        public void SaveScene(DesktopBounds bounds) { SavedScenes++; SavedRegions.Add(bounds); }
    }
    private sealed class FailingStorage : IWorkspaceStorage
    {
        private readonly MemoryWorkspace storage = new();
        public bool IsPersistent => false;
        public bool Exists(string path) => storage.Exists(path);
        public void CreateDirectory(string path) => storage.CreateDirectory(path);
        public WorkspaceEntry[] List(string path) => storage.List(path);
        public string ReadText(string path) => storage.ReadText(path);
        public void WriteText(string path, string text) => throw new IOException("Read-only test volume");
        public void Delete(string path) => storage.Delete(path);
        public void Move(string source, string destination) => storage.Move(source, destination);
    }
    private static void Click(DesktopSession desktop, int x, int y, long timestamp = 100)
    { desktop.Pointer(x, y, true, timestampMs: timestamp); desktop.Pointer(x, y, false, timestampMs: timestamp + 1); }

    [Fact]
    public void DefaultAppearanceIsDarkAndDoesNotInventPersistentStorage()
    {
        var storage = new MemoryWorkspace(); var desktop = new DesktopSession(new Canvas(), storage);
        Assert.Equal(DesktopThemeMode.Dark, desktop.Theme.Mode);
        Assert.Equal(DesktopTheme.DefaultAccent, desktop.Theme.Accent);
        Assert.False(desktop.IsAppearancePersistent); Assert.Contains("RAM", desktop.AppearanceStatus);
        Assert.False(storage.Exists(DesktopTheme.SettingsPath));
    }

    [Fact]
    public void DarkThemePreservesOriginalSystemAndTerminalColors()
    {
        var theme = new DesktopTheme(DesktopThemeMode.Dark);
        Assert.Equal(0x09121Bu, theme.Background); Assert.Equal(0x102538u, theme.Panel);
        Assert.Equal(0x285171u, theme.ActiveTitle); Assert.Equal(0x050B10u, theme.Shadow);
        Assert.Equal(0x75C9F5u, theme.AccentText); Assert.Equal(0xE7F3FCu, theme.Text); Assert.Equal(0x8EA5BAu, theme.Muted);
        Assert.Equal("#75C9F5", XFESystem.Console.TerminalColor.ThemeForeground.Hex);
        Assert.Equal("#0C1321", XFESystem.Console.TerminalColor.ThemeBackground.Hex);
    }

    [Theory]
    [InlineData(DesktopThemeMode.Light, 0x000000u)]
    [InlineData(DesktopThemeMode.Light, 0xFFFFFFu)]
    [InlineData(DesktopThemeMode.Light, 0xFFFF00u)]
    [InlineData(DesktopThemeMode.Light, 0x0000FFu)]
    [InlineData(DesktopThemeMode.Dark, 0x000000u)]
    [InlineData(DesktopThemeMode.Dark, 0xFFFFFFu)]
    [InlineData(DesktopThemeMode.Dark, 0xFF0000u)]
    [InlineData(DesktopThemeMode.Dark, 0x0000FFu)]
    public void ArbitraryAccentsRetainRgbWhileDerivedInkRemainsReadable(DesktopThemeMode mode, uint accent)
    {
        var theme = new DesktopTheme(mode, accent);
        Assert.Equal(accent, theme.Accent);
        Assert.True(DesktopTheme.Contrast(theme.AccentInk, theme.Panel) >= 4.5);
        Assert.True(DesktopTheme.Contrast(theme.AccentInk, theme.Background) >= 4.5);
        Assert.True(DesktopTheme.Contrast(theme.AccentStroke, theme.Surface) >= 3);
        Assert.True(DesktopTheme.Contrast(theme.Text, theme.Panel) >= 4.5);
        Assert.True(DesktopTheme.Contrast(theme.Text, theme.Background) >= 4.5);
        Assert.True(DesktopTheme.Contrast(theme.Muted, theme.Panel) >= 4.5);
    }

    [Theory]
    [InlineData("#00aBcD", 0x00ABCDu)]
    [InlineData("#000000", 0u)]
    [InlineData("#FFFFFF", 0xFFFFFFu)]
    public void AccentParserAcceptsExactlySixHexDigits(string text, uint expected)
    { Assert.True(DesktopTheme.TryParseAccent(text, out var color)); Assert.Equal(expected, color); }

    [Theory]
    [InlineData("#12")]
    [InlineData("123456")]
    [InlineData("#1234567")]
    [InlineData("#GG0000")]
    [InlineData(" #123456")]
    public void AccentParserRejectsMalformedValues(string text) => Assert.False(DesktopTheme.TryParseAccent(text, out _));

    [Fact]
    public void AppearanceIsSavedInGuestNamespaceAndRestoredWithinSameRamSession()
    {
        var storage = new MemoryWorkspace(); var desktop = new DesktopSession(new Canvas(), storage);
        desktop.ApplyAppearance(DesktopThemeMode.Dark, 0x14A8B4);
        Assert.True(storage.Exists("X:\\System\\Settings\\Appearance.json"));
        Assert.Contains("\"Theme\":\"Dark\"", storage.ReadText(DesktopTheme.SettingsPath));
        Assert.Contains("lost after shutdown", desktop.AppearanceStatus);
        var restored = new DesktopSession(new Canvas(), storage);
        Assert.Equal(DesktopThemeMode.Dark, restored.Theme.Mode); Assert.Equal(0x14A8B4u, restored.Theme.Accent);
        var fresh = new DesktopSession(new Canvas(), new MemoryWorkspace());
        Assert.Equal(DesktopThemeMode.Dark, fresh.Theme.Mode);
    }

    [Theory]
    [InlineData("broken")]
    [InlineData("{\"Version\":1,\"Theme\":\"Dark\",\"Accent\":\"#12\"}")]
    [InlineData("{\"Version\":2,\"Theme\":\"Dark\",\"Accent\":\"#123456\"}")]
    [InlineData("{\"Version\":1,\"Theme\":\"Dark\",\"Theme\":\"Light\",\"Accent\":\"#123456\"}")]
    [InlineData("{\"Version\":1,\"Theme\":\"Unknown\",\"Accent\":\"#123456\"}")]
    [InlineData("{\"Version\":\"1\",\"Theme\":\"Dark\",\"Accent\":\"#123456\"}")]
    public void CorruptAppearanceReportsFailureWithoutOverwritingTheFile(string content)
    {
        var storage = new MemoryWorkspace(); storage.CreateDirectory(DesktopTheme.SettingsDirectory); storage.WriteText(DesktopTheme.SettingsPath, content);
        var desktop = new DesktopSession(new Canvas(), storage);
        Assert.Equal(DesktopThemeMode.Dark, desktop.Theme.Mode);
        Assert.Contains("could not be loaded", desktop.AppearanceStatus);
        Assert.Equal(content, storage.ReadText(DesktopTheme.SettingsPath));
    }

    [Fact]
    public void FailedSettingsWriteStillReportsActualSessionAppearance()
    {
        var desktop = new DesktopSession(new Canvas(), new FailingStorage());
        desktop.ApplyAppearance(DesktopThemeMode.Dark, 0x14A8B4);
        Assert.Equal(DesktopThemeMode.Dark, desktop.Theme.Mode); Assert.Equal(0x14A8B4u, desktop.Theme.Accent);
        Assert.Contains("save failed", desktop.AppearanceStatus); Assert.Contains("Applied for this session", desktop.AppearanceStatus);
    }

    [Fact]
    public void SettingsMouseControlsApplyModePresetAndCustomHex()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        Click(desktop, 50, 492); var settings = desktop.ActiveWindow!;
        Assert.Equal(DesktopApplication.Settings, settings.Application); Assert.DoesNotContain("Run Demo", settings.Content);
        var b = settings.Bounds;
        Click(desktop, b.X + 180, b.Y + 104); Assert.Equal(DesktopThemeMode.Dark, desktop.Theme.Mode);
        Click(desktop, b.X + 24 + 2 * 48 + 20, b.Y + 162); Assert.Equal(0x239F99u, desktop.Theme.Accent);
        Click(desktop, b.X + 40, b.Y + 218);
        foreach (char c in "#000000") desktop.Key(DesktopKey.Character, c);
        desktop.Key(DesktopKey.Enter); Assert.Equal(0u, desktop.Theme.Accent);
        Assert.True(DesktopTheme.Contrast(desktop.Theme.AccentInk, desktop.Theme.Panel) >= 4.5);
        Click(desktop, b.X + 40, b.Y + 218); foreach (char c in "#12") desktop.Key(DesktopKey.Character, c);
        Click(desktop, b.X + 60, b.Y + 262); Assert.Equal(0u, desktop.Theme.Accent); Assert.Contains("Invalid color", settings.Status);
    }

    [Fact]
    public void SettingsCanBeOperatedWithKeyboardAndReopenedWithoutDuplication()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace()); desktop.OpenSettings();
        desktop.Key(DesktopKey.Right); Assert.Equal(DesktopThemeMode.Dark, desktop.Theme.Mode);
        desktop.Key(DesktopKey.Tab); desktop.Key(DesktopKey.Right); Assert.Equal(0x9898E7u, desktop.Theme.Accent);
        desktop.Key(DesktopKey.Tab); foreach (char c in "#14A8B4") desktop.Key(DesktopKey.Character, c);
        desktop.Key(DesktopKey.Tab); desktop.Key(DesktopKey.Enter); Assert.Equal(0x14A8B4u, desktop.Theme.Accent);
        desktop.OpenSettings(); Assert.Single(desktop.Windows);
        desktop.Key(DesktopKey.Escape); Assert.Empty(desktop.Windows);
        desktop.OpenSettings(); Assert.Contains("RAM", desktop.ActiveWindow!.Status);
    }

    [Fact]
    public void SettingsHasDesktopTaskbarAndStartMenuEntrances()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace());
        Click(desktop, 1240, 776); Assert.Equal(DesktopApplication.Settings, desktop.ActiveWindow!.Application);
        desktop.Close(desktop.ActiveWindow.Id);
        desktop.Key(DesktopKey.StartMenu);
        for (int i = 0; i < 4; i++) desktop.Key(DesktopKey.Down);
        desktop.Key(DesktopKey.Enter); Assert.Equal(DesktopApplication.Settings, desktop.ActiveWindow!.Application);
        Assert.Equal(0, (int)DesktopApplication.Files); Assert.Equal(3, (int)DesktopApplication.Terminal); Assert.Equal(4, (int)DesktopApplication.Settings);
    }

    [Fact]
    public void HoverAndPressedFeedbackAreVisibleAndExpireWithoutContinuousSceneRedraw()
    {
        var canvas = new Canvas(); var desktop = new DesktopSession(canvas, new MemoryWorkspace()); desktop.Render();
        desktop.Pointer(30, 108, false, timestampMs: 100); desktop.Render();
        Assert.Contains(canvas.Fills, f => f.Color == desktop.Theme.Selection && f.X == 22 && f.Width == 100);
        int scenes = canvas.SavedScenes;
        desktop.Pointer(40, 110, false, timestampMs: 101); desktop.Render(); Assert.Equal(scenes, canvas.SavedScenes);
        canvas.Fills.Clear(); desktop.Pointer(40, 110, true, timestampMs: 110); desktop.Render();
        uint pressed = DesktopTheme.Mix(desktop.Theme.Panel, desktop.Theme.Accent, 28);
        Assert.Contains(canvas.Fills, f => f.Color == pressed && f.X == 22 && f.Width == 100);
        desktop.Pointer(40, 110, false, timestampMs: 120); desktop.Render();
        desktop.AdvanceFeedback(239); Assert.False(desktop.NeedsRender);
        desktop.AdvanceFeedback(240); Assert.True(desktop.NeedsRender);
        canvas.Fills.Clear(); desktop.Render();
        Assert.DoesNotContain(canvas.Fills, f => f.Color == pressed && f.X == 22 && f.Width == 100);
    }

    [Fact]
    public void CaptionButtonsActAtReleaseAndDraggingOutsideCancels()
    {
        var desktop = new DesktopSession(new Canvas(), new MemoryWorkspace()); var window = desktop.Open(DesktopApplication.Files); var b = window.Bounds;
        desktop.Pointer(b.X + b.Width - 16, b.Y + 16, true); desktop.Render(); Assert.Single(desktop.Windows);
        desktop.Pointer(b.X + 100, b.Y + 70, true); desktop.Pointer(b.X + 100, b.Y + 70, false); Assert.Single(desktop.Windows);
        desktop.Pointer(b.X + b.Width - 48, b.Y + 16, true); desktop.Render(); Assert.False(window.IsMinimized);
        desktop.Pointer(b.X + b.Width - 48, b.Y + 16, false); Assert.True(window.IsMinimized);
    }

    [Fact]
    public void ThemeChangeRepaintsAllChromeButKeepsExplicitTerminalAnsiColors()
    {
        var canvas = new Canvas(); var desktop = new DesktopSession(canvas, new MemoryWorkspace());
        var terminal = new XFESystem.Console.TerminalBuffer(30, 8); terminal.Write("\u001b[38;2;255;40;80mExplicit");
        desktop.SetTerminalSnapshot(terminal.Snapshot()); desktop.Render();
        desktop.ApplyAppearance(DesktopThemeMode.Dark, 0xFFFFFF); canvas.Texts.Clear(); desktop.Render();
        Assert.Contains(canvas.Texts, t => t.Text == "E" && t.Color == 0xFF2850);
        Assert.Contains(canvas.Fills, f => f.Color == 0x0C1321);
        Assert.Equal(new DesktopBounds(0, 0, 1280, 800), canvas.SavedRegions[^1]);
    }
}