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

XFESystem

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

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text.Json;

namespace XFESystem.Browser.Tests;

public sealed class RemotePageProofTests
{
    private const string Hash = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
    [Fact]
    public void PublishedProofBindsActualLayoutSourceAndSnapshotWithoutInvalidatingPaint()
    {
        var page = new BrowserService(new Fixture()); page.Navigate("https://example.test/");
        page.Layout(new(800, 500)); long passes = page.LayoutPassCount, revision = page.Revision;
        using var proof = JsonDocument.Parse(page.ReadRemotePageProof(12)); var value = proof.RootElement;
        Assert.Equal(Hash, value.GetProperty("BodySha256").GetString());
        Assert.Equal(12, value.GetProperty("SnapshotSequence").GetInt64());
        Assert.Equal("Remote fixture", value.GetProperty("Title").GetString());
        Assert.Contains(value.GetProperty("TextRuns").EnumerateArray(), item => item.GetProperty("Text").GetString()!.Contains("Actual"));
        Assert.Contains(value.GetProperty("SearchInputs").EnumerateArray(), item => item.GetProperty("Name").GetString() == "q" && item.GetProperty("Bounds").GetProperty("Width").GetDouble() > 0);
        Assert.Single(value.GetProperty("LoadedStyles").EnumerateArray());
        Assert.Single(value.GetProperty("DeclaredStyleLinks").EnumerateArray());
        Assert.Equal(passes, page.LayoutPassCount); Assert.Equal(revision, page.Revision);
    }
    [Fact]
    public void OfflineNavigationNeverReusesPreviousRemoteEvidence()
    {
        var page = new BrowserService(new Fixture()); page.Navigate("https://example.test/"); page.Layout(new(800, 500));
        Assert.NotEqual("{}", page.ReadRemotePageProof());
        page.Navigate("about:Start"); page.Layout(new(800, 500)); Assert.Equal("{}", page.ReadRemotePageProof());
    }
    [Fact]
    public void MissingCertificateOrBodyEvidenceCannotBecomeRemoteProof()
    {
        var page = new BrowserService(new Fixture(verified: false));
        Assert.Equal("Navigation failed", page.Navigate("https://example.test/").Title);
        Assert.Equal("{}", page.ReadRemotePageProof());
        page = new(new Fixture(hash: null)); page.Navigate("https://example.test/"); page.Layout(new(800, 500));
        Assert.Equal("{}", page.ReadRemotePageProof());
    }
    [Theory]
    [InlineData(false)]
    [InlineData(true)]
    public void ActualStylePaintAndProofRetainRequestedAndFinalUrls(bool redirectStyle)
    {
        var page = new BrowserService(new Fixture(redirectStyle: redirectStyle));
        page.Navigate("https://example.test/");
        var layout = page.Layout(new(800, 500));
        Assert.Contains(layout.Commands, command => command.Kind == BrowserDrawKind.Text &&
            command.Text.Contains("Actual", StringComparison.Ordinal) && command.Color == 0x123456);
        var receipt = Assert.Single(page.LoadedResources, item => item.Kind == "style");
        string final = redirectStyle ? "https://cdn.example.test/FinalStyle.css" : "https://example.test/Style.css";
        Assert.Equal("https://example.test/Style.css", receipt.RequestedAddress);
        Assert.Equal(final, receipt.Address);
        using var proof = JsonDocument.Parse(page.ReadRemotePageProof(14));
        var style = Assert.Single(proof.RootElement.GetProperty("LoadedStyles").EnumerateArray());
        Assert.Equal("https://example.test/Style.css", style.GetProperty("RequestedAddress").GetString());
        Assert.Equal(final, style.GetProperty("Address").GetString());
        Assert.Equal(final, style.GetProperty("FinalAddress").GetString());
        Assert.Equal(style.GetProperty("RequestedAddress").GetString(),
            Assert.Single(proof.RootElement.GetProperty("DeclaredStyleLinks").EnumerateArray()).GetString());
    }
    [Fact]
    public void RedirectedStylesheetWithInvalidTlsDoesNotBecomeLoadedProof()
    {
        var page = new BrowserService(new Fixture(redirectStyle: true, styleVerified: false));
        page.Navigate("https://example.test/"); page.Layout(new(800, 500));
        Assert.DoesNotContain(page.LoadedResources, item => item.Kind == "style");
        using var proof = JsonDocument.Parse(page.ReadRemotePageProof(15));
        Assert.Empty(proof.RootElement.GetProperty("LoadedStyles").EnumerateArray());
        Assert.Single(proof.RootElement.GetProperty("DeclaredStyleLinks").EnumerateArray());
    }
    [Fact]
    public void VisibleMissingImageCannotBeHiddenByClippedOrOffscreenTelemetry()
    {
        var page = new BrowserService(new GeometryFixture("<img width=24 height=24 src='/missing.png'><div style='height:0;overflow:hidden'><img width=24 height=24 src='/clipped.png'></div><img style='position:absolute;top:900px' width=24 height=24 src='/offscreen.png'>"));
        try
        {
            page.Navigate("https://example.test/"); page.Layout(new(800, 500));
            using var proof = JsonDocument.Parse(page.ReadRemotePageProof(20));
            var missing = Assert.Single(proof.RootElement.GetProperty("MissingVisibleImages").EnumerateArray());
            Assert.EndsWith("/missing.png", missing.GetProperty("Source").GetString(), StringComparison.Ordinal);
            Assert.True(proof.RootElement.GetProperty("DocumentHeight").GetDouble() >= 900);
        }
        finally { page.Release(); }
    }
    [Fact]
    public void CitationIdentityAndAffineSamplesComeFromTheActualLayout()
    {
        var page = new BrowserService(new GeometryFixture("<ul><li><cite>Actual citation</cite><h2>Actual heading</h2></li></ul><div class='arrow' style='position:absolute;left:120px;top:150px;width:40px;height:40px;transform:scale(.25) rotate(45deg)'></div>"));
        try
        {
            page.Navigate("https://example.test/"); page.Layout(new(800, 500));
            long passes = page.LayoutPassCount, revision = page.Revision;
            using var proof = JsonDocument.Parse(page.ReadRemotePageProof(21)); var value = proof.RootElement;
            var cite = Assert.Single(value.GetProperty("TextRuns").EnumerateArray(), item => item.GetProperty("Text").GetString() == "citation");
            var heading = Assert.Single(value.GetProperty("TextRuns").EnumerateArray(), item => item.GetProperty("Text").GetString() == "heading");
            Assert.True(cite.GetProperty("ListItemId").GetInt32() > 0);
            Assert.Equal(cite.GetProperty("ListItemId").GetInt32(), heading.GetProperty("ListItemId").GetInt32());
            Assert.Contains("cite|li", cite.GetProperty("Ancestors").GetString(), StringComparison.Ordinal);
            var arrow = Assert.Single(value.GetProperty("LayoutSamples").EnumerateArray(), item => item.GetProperty("Class").GetString() == "arrow");
            Assert.InRange(arrow.GetProperty("Bounds").GetProperty("Width").GetDouble(), 14.14, 14.15);
            Assert.InRange(arrow.GetProperty("Transform")[0].GetDouble(), .176, .177);
            Assert.Equal(passes, page.LayoutPassCount); Assert.Equal(revision, page.Revision);
        }
        finally { page.Release(); }
    }
    [Fact]
    public void NavigationReadbackUsesPaintCommandsAndRetainsClippedLinksAsUnpainted()
    {
        var page = new BrowserService(new GeometryFixture("<nav style='height:25px;overflow:hidden'><a href='/one' style='display:block;height:20px;line-height:20px'>One</a><a href='/two' style='display:block;height:20px;line-height:20px'>Two</a><a href='/three' style='display:block;height:20px;line-height:20px'>Three</a></nav>"));
        try
        {
            page.Navigate("https://example.test/"); page.Layout(new(800, 500));
            using var proof = JsonDocument.Parse(page.ReadRemotePageProof(22));
            var links = proof.RootElement.GetProperty("NavigationLinks").EnumerateArray().ToArray();
            Assert.Equal(3, links.Length); Assert.True(links[0].GetProperty("Painted").GetBoolean());
            Assert.False(links[2].GetProperty("Painted").GetBoolean());
            Assert.True(links[0].GetProperty("MenuId").GetInt32() > 0);
        }
        finally { page.Release(); }
    }
    private sealed class GeometryFixture(string body) : IWebTransport
    {
        public WebResponse Get(Uri address, BrowserLimits limits) => address.AbsolutePath == "/"
            ? new(address, 200, "text/html", "<html><head><title>Geometry</title></head><body>" + body + "</body></html>", true, BodySha256: Hash)
            : new(address, 404, "text/plain", "Missing", true);
    }
    private sealed class Fixture(bool verified = true, string? hash = Hash, bool redirectStyle = false, bool styleVerified = true) : IWebTransport
    {
        public WebResponse Get(Uri address, BrowserLimits limits) => address.AbsolutePath == "/Style.css"
            ? new(redirectStyle ? new Uri("https://cdn.example.test/FinalStyle.css") : address, 200, "text/css", "p{color:#123456} input{width:240px;height:32px}", verified && styleVerified)
            : new(address, 200, "text/html", "<html><head><title>Remote fixture</title><link rel='stylesheet' href='/Style.css'></head><body><p>Actual remote text</p><input name='q' value='Query'></body></html>", verified, BodySha256: hash);
    }
}