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

XFESystem

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

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Reflection;
using XFESystem.Collections;

namespace XFESystem.Browser.Tests;

public sealed class NavigationFailureLifetimeTests
{
    private sealed class FailureTransport:IWebTransport
    {
        public WebResponse Get(Uri address,BrowserLimits limits)
        {
            if(address.AbsolutePath=="/Unsupported")return new(address,200,"application/json","{}");
            throw new IOException("Controlled navigation transport failure.");
        }
    }
    private static T Owner<T>(BrowserService browser,string field)where T:class =>
        Assert.IsType<T>(typeof(BrowserService).GetField(field,BindingFlags.Instance|BindingFlags.NonPublic)!.GetValue(browser));

    [Theory]
    [InlineData("about:Unsupported")]
    [InlineData("https://")]
    [InlineData("http://failure.test/Offline")]
    [InlineData("http://failure.test/Unsupported")]
    public void FailedNavigationReleasesEveryOldOwnerBeforeDroppingItAndCanRecover(string address)
    {
        var trace=new List<string>();var browser=new BrowserService(new FailureTransport()){Trace=trace.Add};
        browser.Navigate("about:Start");
        Assert.Equal("ready",browser.ExecuteJavaScript("globalThis.kept=document.createElement('button');document.body.append(kept);kept.addEventListener('click',()=>{});globalThis.map=new WeakMap();map.set(kept,{same:kept});setInterval(()=>map.has(kept),1000);'ready'"));
        browser.Layout(new(796,404));
        // Inspection holds the old owners alive deliberately: passing cannot
        // depend on host GC/finalizers collecting an otherwise leaked owner.
        var runtime=Owner<JavaScriptRuntime>(browser,"runtime");
        var dom=Owner<DomBridge>(browser,"dom");
        var layout=Owner<BrowserLayoutEngine>(browser,"layoutEngine");
        var scope=((IWeakCollectionOwner)(object)runtime.Engine).WeakCollections;
        Assert.True(dom.Lifetime.OutstandingHandles>0);Assert.True(scope.Lifetime.Created>0);
        Assert.True(runtime.PendingTaskCount>0);Assert.True(layout.RetainedNodeIdentityCount>0);

        var failed=browser.Navigate(address);
        Assert.Equal("Navigation failed",failed.Title);Assert.Equal(address,failed.Address);
        Assert.StartsWith("Navigation failed: ",failed.Text,StringComparison.Ordinal);
        Assert.Null(browser.Document);Assert.Null(browser.ReadLifetimeDiagnostics());Assert.Null(browser.ReadWeakCollectionLifetime());
        Assert.Equal(0,dom.Lifetime.OutstandingHandles);Assert.Equal(dom.Lifetime.HandlesCreated,dom.Lifetime.HandlesDisposed);
        Assert.Equal(0,runtime.PendingTaskCount);Assert.Equal(0,layout.RetainedNodeIdentityCount);
        var released=scope.Lifetime;
        Assert.Equal(0,released.Tables);Assert.Equal(0,released.Entries);Assert.Equal(released.Created,released.Disposed);
        Assert.Equal(released.TableHandlesCreated,released.TableHandlesDisposed);
        var release=Assert.Single(trace,line=>line.Contains("XFE_BROWSER_WEAK_RELEASE|",StringComparison.Ordinal));
        Assert.Contains("|TablesAfter|0|EntriesAfter|0|Created|"+released.Created+"|Disposed|"+released.Disposed,release,StringComparison.Ordinal);
        Assert.Contains("|OwnerHandlesCreated|"+released.TableHandlesCreated+"|OwnerHandlesDisposed|"+released.TableHandlesDisposed,release,StringComparison.Ordinal);
        Assert.EndsWith("|Reason|NavigationFailed\n",release,StringComparison.Ordinal);
        Assert.Throws<ObjectDisposedException>(()=>new OwnedWeakTable<object,object>(scope));

        var recovered=browser.Navigate("about:Start");
        Assert.Contains("JavaScript ready: 12",recovered.Text,StringComparison.Ordinal);
        Assert.NotSame(runtime,Owner<JavaScriptRuntime>(browser,"runtime"));
        Assert.Equal("true",browser.ExecuteJavaScript("const key={};const m=new WeakMap();m.set(key,42);m.get(key)===42"));
        browser.Release();Assert.Equal(released,scope.Lifetime);
    }
}