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

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.IO.Pipes;
using System.Text.Json;
using System.Text.Json.Nodes;
using XFEEDToolBox.Core.Galaxy;
using XFEEDToolBox.Core.Rendering;
using XFEExtension.NetCore.XUnit.Attributes;

namespace XFEEDToolBox.Test;

[TestFixture]
public sealed class NativeGalaxyDustProfileTests
{
    private static JsonObject Profile()=>JsonNode.Parse("""
        {"origin":[1,2,3],"queryCentre":[4,5,6],"surfaceDensity":0.4,"qualityStarCount":0,"runtimeRadiusMultiplier":1,
         "sparseExpansion":4,"smallMode":false,"originalViewMode":0,"debugEnabled":false,"referenceKind":"camera-eye"}
        """)!.AsObject();
    private static JsonObject Dust()=>JsonNode.Parse("""
        {"scope":"observed-game-dust-producer-inputs","step":1066,"height":1280,"layerAlphaSize":[1,2,3,4,5,6],
         "alphaMultiplier":0.5,"sizeMultiplier":32,"transitionPerStep":100,"minimumDistance":200,"runtimeTint":null,
         "observedGamePublications":[{"ordinal":0,"publishedRevision":10,"requestedRevision":11,"radius":10000,"records":7},
          {"ordinal":1,"publishedRevision":0,"requestedRevision":22,"radius":null,"records":0},
          {"ordinal":2,"publishedRevision":30,"requestedRevision":30,"radius":1000,"records":9}]}
        """)!.AsObject();
    private static GalaxyCatalogueProfile Read(JsonObject value)=>NativeGalaxyCatalog.ReadCatalogueProfile(JsonSerializer.SerializeToElement(value));
    private static GalaxyCatalogueProfile WithDust(JsonNode? dust)
    {var value=Profile();value["dustInputs"]=dust;return Read(value);}

    [Test] public void OldOrUnavailableDustKeepsItsOwnUnknownStateWithoutBlockingLod()
    {
        var old=Read(Profile());var unknown=WithDust(null);
        Check.That(old.HasLodInputs&&unknown.HasLodInputs&&old.DustInputs is null&&unknown.DustInputs is null);
        var catalogue=new NativeGalaxyCatalog(123);
        catalogue.ApplyProtocolDescription(JsonSerializer.SerializeToElement(new{processId=123,galaxyCatalogueProfile=true}));
        Check.That(catalogue.SupportsCatalogueProfile&&!catalogue.SupportsDustInputs);
        catalogue.ApplyProtocolDescription(JsonSerializer.SerializeToElement(new{processId=123,galaxyCatalogueProfile=true,dustInputs=true}));
        Check.That(catalogue.SupportsDustInputs);
    }
    [Test] public void ActualParametersPreserveInterleavedLayerFactorsAndNeverBecomeToolCacheReady()
    {
        var profile=WithDust(Dust());var dust=profile.DustInputs!;
        Check.That(profile.HasLodInputs&&!dust.ToolCacheReady&&dust.RuntimeTint is null);
        Check.Equal(1066u,dust.Settings.Step);Check.Equal(1280u,dust.Settings.Height);
        Check.That(dust.Settings.Layers.SequenceEqual(new[]{new GalaxyOriginalDustLayerFactors(1,2),new(3,4),new(5,6)}));
        Check.Equal(.5f,dust.Settings.AlphaMultiplier);Check.Equal(32f,dust.Settings.SizeMultiplier);
        Check.Equal(100f,dust.Settings.TransitionPerStep);Check.Equal(200f,dust.Settings.MinimumDistance);
        Check.That(dust.ObservedGamePublications[1] is{PublishedRevision:0,RequestedRevision:22,Radius:null,RecordCount:0});
        Check.That(dust.ObservedGamePublications[2] is{PublishedRevision:30,RequestedRevision:30,Radius:1000,RecordCount:9});
        Check.That(!GalaxyOriginalDustFrame.TryCreate(dust.Settings,null,0,out _));
        var zero=Dust();zero["alphaMultiplier"]=0;zero["sizeMultiplier"]=0;zero["step"]=uint.MaxValue;
        var explicitZero=WithDust(zero).DustInputs!;
        Check.Equal(0f,explicitZero.Settings.AlphaMultiplier);Check.Equal(0f,explicitZero.Settings.SizeMultiplier);Check.Equal(uint.MaxValue,explicitZero.Settings.Step);
    }
    [Test] public void MalformedOrOverclaimedDustInputsAreRejectedRatherThanDefaulted()
    {
        Action<JsonObject>[] invalid=[
            d=>d["scope"]="tool-cache-ready",d=>d["step"]=0,d=>d["height"]=-1,d=>d.Remove("runtimeTint"),d=>d["runtimeTint"]=new JsonArray(0,0,0),
            d=>d["layerAlphaSize"]=new JsonArray(1,2,3),d=>d["layerAlphaSize"]![1]=-1,d=>d["layerAlphaSize"]![2]=JsonNode.Parse("1e39"),
            d=>d["alphaMultiplier"]="0.5",d=>d["transitionPerStep"]=-1,d=>d["minimumDistance"]=null,
            d=>d["observedGamePublications"]=new JsonArray(),d=>d["observedGamePublications"]![1]!["ordinal"]=0,
            d=>d["observedGamePublications"]![0]!["records"]=1_048_577,d=>d["observedGamePublications"]![1]!["radius"]=0,
            d=>d["observedGamePublications"]![2]!["radius"]=null,d=>d["observedGamePublications"]![0]!["requestedRevision"]=-1
        ];
        foreach(var change in invalid){var dust=Dust();change(dust);Check.Throws<InvalidDataException>(()=>WithDust(dust));}
    }
    [Test] public async Task FirstLodPageCarriesOptionalDustWithoutWaitingForPointTableCompletion()
    {
        var process=Random.Shared.Next(190000000,195000000);using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(5));
        var profile=Profile();profile["dustInputs"]=Dust();
        profile["pointOverrides"]=JsonSerializer.SerializeToNode(new{scope="published-renderer-d50+local-navroute",processId=process,sourceGeneration=9,
            capturedAtTick=1UL,navRouteViewMode=0,d50Complete=true,navRouteComplete=true,overrideOffset=0,overrideTotal=2,navRouteOffset=0,navRouteTotal=0,
            overrides=new[]{new{systemAddress="1",minimumExtra=1f,farFade=false,pending=false,position=new[]{1d,2d,3d},name=(string?)null,starClass="G",originalAppearance=(object?)null}},
            navRouteMembers=Array.Empty<string>()});
        var server=Task.Run(async()=>
        {
            await using var pipe=new NamedPipeServerStream($"XFEEDToolBox.Navigation.{process}",PipeDirection.InOut,1,PipeTransmissionMode.Byte,PipeOptions.Asynchronous);
            await pipe.WaitForConnectionAsync(stop.Token);var bytes=new byte[4096];var count=0;JsonElement? request=null;
            while(request is null)
            {
                var read=await pipe.ReadAsync(bytes.AsMemory(count),stop.Token);if(read==0)throw new IOException("Empty request");
                count+=read;request=ParseRequest(bytes,count);if(count==bytes.Length&&request is null)throw new IOException("Oversized request");
            }
            Check.Equal("galaxy.catalogue.profile",request.Value.GetProperty("op").GetString());
            var result=JsonSerializer.SerializeToUtf8Bytes(new{ok=true,result=new{generation=9,done=true,error="",profile}});
            await pipe.WriteAsync(result,stop.Token);await pipe.WriteAsync(new byte[]{10},stop.Token);await pipe.FlushAsync(stop.Token);
            if(OperatingSystem.IsWindows())pipe.WaitForPipeDrain();
        });
        var value=await new NativeGalaxyCatalog(process).CatalogueProfileFirstPageAsync(new(0,0,0),new(0,0,0),stop.Token);await server;
        Check.That(value.HasLodInputs&&value.PointOverrides is{D50Complete:false}&&value.DustInputs is{ToolCacheReady:false,RuntimeTint:null});
        Check.Equal(1280u,value.DustInputs!.Settings.Height);
    }
    private static JsonElement? ParseRequest(byte[] data,int count)
    {
        var reader=new Utf8JsonReader(data.AsSpan(0,count),false,default);
        if(!JsonDocument.TryParseValue(ref reader,out var doc))return null;
        using(doc)return doc.RootElement.Clone();
    }
}