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

XFESystem

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

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

namespace XFESystem.Browser.Tests;

public sealed class BrowserPaintIdentityTests
{
    [Fact]
    public void ActualPageAndBackgroundSnapshotsUseDistinctIdentitiesAndRestoreOnlyTheirOwnFrames()
    {
        var canvas = new FrameCanvas();
        var scheduler = new Scheduler();
        var browser = new BrowserService();
        browser.ResizeViewport(canvas.Width, canvas.Height);
        browser.Navigate("about:Start");
        var viewport = new DesktopBounds(0, 0, canvas.Width, canvas.Height);
        browser.Paint(canvas, viewport, 0);
        Assert.NotEmpty(canvas.Stores);
        long regularIdentity = canvas.Stores[^1].PageId;
        var regularPixels = (int[])canvas.Pixels.Clone();

        var preparation = new BrowserEditorPreparationService(new BrowserService(), scheduler, new CanvasFactory());
        var operation = preparation.Begin(new(7, 1, "const mixedIdentity = 42;", "Mixed.js", canvas.Width, canvas.Height));
        scheduler.RunAll();
        Assert.True(operation.TryTakeResult(out var prepared));
        Assert.Null(prepared.Error);
        var actor = Assert.IsType<BackgroundBrowserEditorSession>(prepared.Page);
        try
        {
            actor.Paint(canvas, viewport, 0);
            long actorIdentity = canvas.Requests[^1].PageId;
            Assert.NotEqual(regularIdentity, actorIdentity);
            var actorPixels = (int[])canvas.Pixels.Clone();
            Assert.False(regularPixels.AsSpan().SequenceEqual(actorPixels));
            Assert.Equal("const mixedIdentity = 42;", actor.ReadEditorText());

            // Repainting an unchanged page must still hit the real bounded
            // cache. Painting either owner after the other restores exactly
            // that owner's pixels, with no extra capture or raster calls.
            int stores = canvas.Stores.Count, fills = canvas.FillCalls;
            browser.Paint(canvas, viewport, 0);
            Assert.Equal(regularPixels, canvas.Pixels);
            Assert.Equal(1, browser.CachedPaintCount);
            actor.Paint(canvas, viewport, 0);
            Assert.Equal(actorPixels, canvas.Pixels);
            Assert.Equal(1, actor.CachedPaintCount);
            Assert.Equal(stores, canvas.Stores.Count);
            Assert.Equal(fills, canvas.FillCalls);

            var another = new BrowserService();
            try
            {
                another.Navigate("about:Start");
                another.Paint(canvas, viewport, 0);
                long otherIdentity = canvas.Stores[^1].PageId;
                Assert.NotEqual(regularIdentity, otherIdentity);
                Assert.NotEqual(actorIdentity, otherIdentity);
                Assert.Equal(0, another.CachedPaintCount);
            }
            finally { another.Release(); }
        }
        finally
        {
            actor.Cancel(); scheduler.RunAll();
            browser.Release();
        }
    }

    private sealed class Scheduler : IEditorBackgroundWorkScheduler
    {
        private readonly Queue<Action> pending = [];
        public bool TryQueue(Action work) { pending.Enqueue(work); return true; }
        public void RunAll()
        {
            int count = 0;
            while (pending.TryDequeue(out var work)) { Assert.True(++count < 20); work(); }
        }
    }
    private sealed class CanvasFactory : IEditorPreparationCanvasFactory
    {
        public IDesktopCanvas CreateEditorPreparationCanvas() => new FrameCanvas();
    }
    private sealed class FrameCanvas : IDesktopWebCanvas, IDesktopWebFrameCache
    {
        private readonly DesktopWebFrameCache frames = new();
        public int Width => 796;
        public int Height => 404;
        public int[] Pixels { get; } = new int[796 * 404];
        public List<DesktopWebFrameKey> Requests { get; } = [];
        public List<DesktopWebFrameKey> Stores { get; } = [];
        public int FillCalls { get; private set; }
        public DesktopBounds WebPaintClip => new(0, 0, Width, Height);
        public bool TryRestoreWebFrame(long page, long revision, long layout, DesktopBounds bounds, double scrollX, double scrollY)
        {
            var key = new DesktopWebFrameKey(page, revision, layout, bounds.Width, bounds.Height, 100, scrollX, scrollY);
            Requests.Add(key);
            return frames.TryRestore(key, Pixels, Width, Height, bounds.X, bounds.Y, WebPaintClip);
        }
        public void StoreWebFrame(long page, long revision, long layout, DesktopBounds bounds, double scrollX, double scrollY)
        {
            var key = new DesktopWebFrameKey(page, revision, layout, bounds.Width, bounds.Height, 100, scrollX, scrollY);
            Assert.True(frames.Store(key, Pixels, Width, Height, bounds.X, bounds.Y, WebPaintClip));
            Stores.Add(key);
        }
        public void Fill(uint rgb, int x, int y, int width, int height)
        {
            FillCalls++;
            int left = Math.Clamp(x, 0, Width), top = Math.Clamp(y, 0, Height);
            int right = Math.Clamp(x + width, left, Width), bottom = Math.Clamp(y + height, top, Height);
            for (int row = top; row < bottom; row++) Array.Fill(Pixels, (int)rgb, row * Width + left, right - left);
        }
        // Deterministic host-only text marks make cached frame mixups visible;
        // this is a cache ownership test, never native glyph/font evidence.
        public void Text(string text, uint rgb, int x, int y)
        {
            uint hash = 2166136261;
            foreach (char character in text) hash = unchecked((hash ^ character) * 16777619);
            if ((uint)x < Width && (uint)y < Height) Pixels[y * Width + x] = unchecked((int)(hash ^ rgb));
        }
        public void Line(uint rgb, int x1, int y1, int x2, int y2) { }
        public void Present() { }
        public void PushClip(DesktopBounds bounds) { }
        public void PopClip() { }
        public double MeasureWebText(string text, double size, string family, int weight) =>
            new BrowserFallbackTextMetrics().Measure(text, new(size, family, weight));
        public (int Width, int Height, byte[] Alpha) RasterizeWebText(string text, double size, string family, int weight) => (0, 0, []);
        public void WebText(string text, uint rgb, double x, double y, double size, string family, int weight, double opacity = 1) =>
            Text(text, rgb, (int)Math.Round(x), (int)Math.Round(y));
    }
}