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

XFESystem

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

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Globalization;
using System.Text.RegularExpressions;
using AngleSharp.Dom;
using XFESystem.Browser;
using XFESystem.Desktop;
using Xunit;

namespace XFESystem.Documents.Tests;

/// <summary>The exact website reader script drives the generated document in
/// the real renderer; only its desktop outer-card presentation is overridden.</summary>
public sealed class DesktopDocumentReaderTests
{
    [Theory]
    [InlineData(true)]
    [InlineData(false)]
    public void DesktopArticleIsFlatWhileImportedStylesAndReaderScriptStayOriginal(bool dark)
    {
        var browser = new BrowserService();
        try
        {
            browser.ResizeViewport(800, 400); browser.NavigateDocument("# Flat document\n\nA paragraph.", "Flat.md", dark);
            var list = browser.Layout(new(800, 400));
            var article = browser.Document!.QuerySelector(".docs-article")!;
            var style = list.GetComputedStyle(article)!;
            Assert.Equal("0px", style.GetPropertyValue("border-top-width"));
            Assert.Equal("0px", style.GetPropertyValue("border-right-width"));
            Assert.Equal("0", style.GetPropertyValue("border-radius"));
            Assert.Equal("none", style.GetPropertyValue("box-shadow"));
            Assert.Equal("visible", style.OverflowY);
            Assert.DoesNotContain("radial-gradient", style.GetPropertyValue("background-image"), StringComparison.Ordinal);
            Assert.Equal(0, list.GetBoundingClientRect(article).X, 3);
            Assert.Equal(800, list.GetBoundingClientRect(article).Width, 3);
            Assert.Contains(browser.LoadedResources, item => item.Address.EndsWith("/css/docs/xfesystem-desktop.css", StringComparison.Ordinal));
            Assert.Contains(browser.LoadedResources, item => item.Address.EndsWith("/js/docs/docs-reader.js", StringComparison.Ordinal));
            Assert.Equal(dark ? "dark" : "light", browser.Document.DocumentElement!.GetAttribute("data-bs-theme"));
            Assert.NotNull(browser.Document.QuerySelector(".docs-reading-progress > [data-doc-progress]"));
        }
        finally { browser.Release(); }
    }

    [Theory]
    [InlineData(396, 404)] // Right pane in the current split editor.
    [InlineData(796, 404)]
    [InlineData(1280, 720)]
    public void ExactWebsiteProgressMovesWithRealViewportScrollAndRemainsPinnedToItsPane(int width, int height)
    {
        var browser = new BrowserService();
        try
        {
            browser.ResizeViewport(width, height); browser.NavigateDocument(LongMarkdown(), "Long.md");
            Pump(browser, 0);
            double top = Progress(browser);
            double maximum = browser.DocumentHeight - height;
            Assert.True(maximum > height * 2);
            browser.Layout(new(width, height, 0, maximum / 2)); Pump(browser, 1000);
            double middle = Progress(browser);
            Assert.True(middle > top + 20, $"Progress did not follow scrolling: {top} -> {middle}");
            Assert.InRange(middle, 10, 90);
            browser.Layout(new(width, height, 0, maximum)); Pump(browser, 2000);
            double bottom = Progress(browser);
            Assert.InRange(bottom, 90, 100);
            Assert.True(bottom > middle);
            var progress = browser.Document!.QuerySelector("[data-doc-progress]")!;
            var list = browser.Layout(new(width, height, 0, maximum));
            var track = list.GetBoundingClientRect(progress.ParentElement!);
            var fill = list.GetBoundingClientRect(progress);
            Assert.Equal(0, track.X, 3); Assert.Equal(0, track.Y, 3);
            Assert.Equal(width, track.Width, 3); Assert.Equal(3, track.Height, 3);
            Assert.InRange(fill.Width, 0, width);
            Assert.Equal(width * bottom / 100, fill.Width, 2);
            Assert.DoesNotContain("JavaScript error:", browser.CurrentPage.Text, StringComparison.Ordinal);
        }
        finally { browser.Release(); }
    }

    [Theory]
    [InlineData("")]
    [InlineData("# Short\n\nDone.")]
    public void EmptyAndShortDocumentsHaveFiniteCompletedProgress(string markdown)
    {
        var browser = new BrowserService();
        try
        {
            browser.ResizeViewport(800, 600); browser.NavigateDocument(markdown, "Short.md"); Pump(browser, 0);
            Assert.Equal(100, Progress(browser));
            Assert.DoesNotContain("NaN", browser.Document!.QuerySelector("[data-doc-progress]")!.GetAttribute("style"), StringComparison.Ordinal);
        }
        finally { browser.Release(); }
    }

    [Fact]
    public void IndependentPanesDoNotShareProgressAndResizeRecomputesTheActualReaderState()
    {
        var left = new BrowserService(); var right = new BrowserService();
        try
        {
            left.ResizeViewport(396, 404); right.ResizeViewport(796, 404);
            left.NavigateDocument(LongMarkdown(), "Left.md"); right.NavigateDocument(LongMarkdown(), "Right.md");
            Pump(left, 0); Pump(right, 0);
            double originalRight = Progress(right);
            double scroll = (left.DocumentHeight - 404) / 2;
            left.Layout(new(396, 404, 0, scroll)); Pump(left, 1000);
            double beforeResize = Progress(left);
            Assert.True(beforeResize > originalRight + 20);
            Assert.Equal(originalRight, Progress(right));
            left.ResizeViewport(1000, 720); Pump(left, 2000);
            Assert.InRange(Progress(left), 0, 100);
            Assert.NotEqual(beforeResize, Progress(left));
            var bounds = left.Layout(new(1000, 720, 0, scroll)).GetBoundingClientRect(left.Document!.QuerySelector(".docs-reading-progress")!);
            Assert.Equal(1000, bounds.Width, 3); Assert.Equal(0, bounds.Y, 3);
            Assert.Equal(originalRight, Progress(right));
        }
        finally { left.Release(); right.Release(); }
    }

    [Fact]
    public void OriginalWebsitePurpleToCyanProgressActuallyPaintsAcrossTheFlatPaneTop()
    {
        var browser = new BrowserService(); var canvas = new PixelCanvas(600, 400);
        try
        {
            browser.ResizeViewport(600, 400); browser.NavigateDocument("# Color progress\n\nShort document.", "Gradient.md"); Pump(browser, 0);
            Assert.Equal(100, Progress(browser));
            browser.Paint(canvas, new(0, 0, 600, 400), 0);
            uint start = canvas.Pixel(10, 1), end = canvas.Pixel(589, 1);
            Assert.True((start >> 16 & 255) > (end >> 16 & 255) + 40, $"Gradient has no purple-to-cyan red change: {start:x6} {end:x6}");
            Assert.True((end >> 8 & 255) > (start >> 8 & 255) + 20, $"Gradient has no cyan green change: {start:x6} {end:x6}");
            Assert.True((start & 255) > 160 && (end & 255) > 160);
        }
        finally { browser.Release(); }
    }

    private static string LongMarkdown() => "# Reading progress\n\n" + string.Join("\n\n", Enumerable.Repeat(
        "A real document paragraph with enough text to wrap in a narrow split pane and retain its original website appearance.", 100));
    private static void Pump(BrowserService browser, int begin) { for (int frame = 1; frame <= 8; frame++) browser.AdvanceTime(begin + frame * 20); }
    private static double Progress(BrowserService browser)
    {
        string style = browser.Document!.QuerySelector("[data-doc-progress]")!.GetAttribute("style") ?? "";
        var match = Regex.Match(style, @"(?:^|;)\s*width\s*:\s*([0-9.]+)%", RegexOptions.IgnoreCase);
        Assert.True(match.Success, "The real reader did not set finite progress: " + style);
        return double.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
    }
    private sealed class PixelCanvas(int width, int height) : IDesktopCoverageCanvas
    {
        private readonly uint[] pixels = new uint[width * height];
        public int Width => width; public int Height => height;
        public uint Pixel(int x, int y) => pixels[y * width + x] & 0xFFFFFF;
        public void Fill(uint rgb, int x, int y, int w, int h)
        { for (int yy = Math.Max(0, y); yy < Math.Min(height, y + h); yy++) for (int xx = Math.Max(0, x); xx < Math.Min(width, x + w); xx++) pixels[yy * width + xx] = rgb; }
        public void BlendCoverage(uint rgb, int x, int y, int w, int h, ReadOnlySpan<byte> coverage)
        {
            for (int yy = Math.Max(0, y); yy < Math.Min(height, y + h); yy++)
                for (int xx = Math.Max(0, x); xx < Math.Min(width, x + w); xx++)
                {
                    uint alpha = coverage[(yy - y) * w + xx - x], old = pixels[yy * width + xx];
                    uint r = ((rgb >> 16 & 255) * alpha + (old >> 16 & 255) * (255 - alpha) + 127) / 255;
                    uint g = ((rgb >> 8 & 255) * alpha + (old >> 8 & 255) * (255 - alpha) + 127) / 255;
                    uint b = ((rgb & 255) * alpha + (old & 255) * (255 - alpha) + 127) / 255;
                    pixels[yy * width + xx] = r << 16 | g << 8 | b;
                }
        }
        public void Text(string text, uint rgb, int x, int y) { }
        public void Line(uint rgb, int x1, int y1, int x2, int y2) { }
        public void Present() { }
    }
}