using System.Text.Json; using System.IO.Pipes; using XFEEDToolBox.Core.Galaxy; using XFEExtension.NetCore.XUnit.Attributes; namespace XFEEDToolBox.Test; [TestFixture] public sealed class GalaxyPointOverridesTests { static JsonElement Page(long generation=9,int offset=0,int navOffset=0,int total=1,int navTotal=1,bool known=true,long id=1,int process=42)=>JsonSerializer.SerializeToElement(new{ processId=process,sourceGeneration=generation,capturedAtTick=123UL,scope="published-renderer-d50+local-navroute",navRouteViewMode=(int?)1, d50Complete=known,navRouteComplete=known,overrideOffset=offset,overrideTotal=total,navRouteOffset=navOffset,navRouteTotal=navTotal, overrides=total>offset?new[]{new{systemAddress=id.ToString(),minimumExtra=1f,farFade=false,pending=false,position=new[]{5d,5d,1005d},name=(string?)null,starClass="G",originalAppearance=(object?)null}}:[], navRouteMembers=navTotal>navOffset?new[]{id.ToString()}:[]}); [Test] public void CurrentPublishedPresenceIsIndependentOfTheConsumedPendingBitAndUnknownIsNotAbsent() { var known=NativeGalaxyCatalog.ReadPointOverridesPages([Page()]); Check.Equal(true,known.OverridePresence(1)!.Value);Check.Equal(false,known.OverridePresence(2)!.Value); Check.Equal(true,known.Membership(1)!.Value);Check.Equal(false,known.Membership(2)!.Value); Check.That(known.TryGetOverride(1,out var row)&&row is{Pending:false,MinimumExtra:1,FarFade:false,Name:null,System:null}); var unknown=NativeGalaxyCatalog.ReadPointOverridesPages([Page(total:0,navTotal:0,known:false)]); Check.That(unknown.OverridePresence(1) is null&&unknown.Membership(1) is null&&unknown.OverridePresence(0) is null); } [Test] public void PagesRetainOneSnapshotAndNeverUsePartialArraysToProveAbsence() { var first=Page(total:2,navTotal:2);var partial=NativeGalaxyCatalog.ReadPointOverridesPages([first]); Check.That(!partial.D50Complete&&!partial.NavRouteComplete&&partial.OverridePresence(2) is null&&partial.Membership(2) is null); var all=NativeGalaxyCatalog.ReadPointOverridesPages([first,Page(offset:1,navOffset:1,total:2,navTotal:2,id:2)]); Check.That(all.D50Complete&&all.NavRouteComplete&&all.Membership(2)==true&&all.Membership(3)==false); Check.Throws(()=>NativeGalaxyCatalog.ReadPointOverridesPages([first,Page(generation:10,offset:1,navOffset:1,total:2,navTotal:2,id:2)])); Check.Throws(()=>NativeGalaxyCatalog.ReadPointOverridesPages([first,Page(offset:1,navOffset:1,total:2,navTotal:2,id:1)])); } [Test] public void D50ForcesOnlyItsExactPublishedPositionAndDoesNotBypassStrictFilter() { var row=new GalaxyOriginalPointOverride((long)(GalaxyCellQueryPlan.Encode(0,0,0,100)|(1UL<<44)),1,false,false,new(5,5,1005),"Recorded","G",null); var points=new GalaxyOriginalPointOverrides(42,9,123,0,true,true,[row],[]); var system=row.System!;var camera=GalaxyCamera.Create(new(5,5,5),1600,900,0,0,4); var selection=new GalaxyCatalogueSelection(GalaxyOriginalCatalogueLod.Create(new(5,5,5),1,10,.001,1,0),new(0,0,0)){PointOverrides=points}; Check.That(selection.Contains(system)&&selection.IsForced(system)); Check.That(!selection.Contains(system with{Position=new(6,5,1005)})); Check.That(!camera.ProjectForCatalogue(system.Position).IsVisible&&selection.ApplyTo(camera).ProjectForCatalogue(system.Position).IsVisible); var physical=system with{OriginalAppearance=new(0xffffffff,null,null,0,4,.4f,.2f,false)}; Check.Equal(.4f,(selection with{SmallMode=false}).OriginalSize(physical)!.Value); var strict=GalaxyMapFilter.Default with{Mode=GalaxyMapMode.Map,Values=new HashSet(),IncludeUnknown=false}; Check.That(!strict.Matches(system)); Check.Equal(.4f,(selection with{PointOverrides=null,SmallMode=false}).OriginalSize(physical)!.Value); } [Test] public void OnlyCurrentKnownD50AbsenceCanReachFreshNavRouteFlags() { var row=new GalaxyOriginalPointOverride(1,1,false,false,new(0,0,0),"Recorded","G",null); var system=row.System! with{OriginalAppearance=new(0xffffffff,null,null,0,4,.4f,.2f,false) {NavRouteMembership=false,NavRouteMembershipMode=0,NavRouteViewMode=0}}; var descriptor=GalaxyOriginalCatalogueLod.Create(new(0,0,0),1,10,.001,1,0); GalaxyCatalogueSelection Selection(bool d50,bool nav,IEnumerable rows,IEnumerable members,int view=0)=> new(descriptor,new(0,0,0)){NavRouteViewMode=0,NavRouteMembershipMode=0, PointOverrides=new(42,9,123,view,d50,nav,rows,members)}; Check.That(Selection(true,true,[row],[]).TryFrameFadeFlags(system,out var bypass,out var far)&&!bypass&&!far); Check.That(Selection(true,true,[],[]).TryFrameFadeFlags(system,out bypass,out far)&&bypass&&far); // Fresh member=true wins over the cached row's explicit false flag. Check.That(Selection(true,true,[],[1]).TryFrameFadeFlags(system,out bypass,out far)&&!bypass&&far); Check.That(!Selection(false,true,[],[1]).TryFrameFadeFlags(system,out _,out _)); Check.That(!Selection(true,false,[],[]).TryFrameFadeFlags(system,out _,out _)); Check.That(!Selection(true,true,[],[],1).TryFrameFadeFlags(system,out _,out _)); Check.That(!Selection(true,true,[row],[]).TryFrameFadeFlags(system with{Position=new(1,0,0)},out _,out _)); } [Test] public async Task ProfileFetchJoinsPointPagesFromTheSameGenerationBeforePublishing() { var process=Random.Shared.Next(170000000,180000000);using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(6)); var serve=Task.Run(async()=> { for(var page=0;page<2;page++) { await using var server=new NamedPipeServerStream($"XFEEDToolBox.Navigation.{process}",PipeDirection.InOut,1,PipeTransmissionMode.Byte,PipeOptions.Asynchronous); await server.WaitForConnectionAsync(stop.Token);var bytes=new byte[65536];var count=await server.ReadAsync(bytes,stop.Token); using var request=JsonDocument.Parse(bytes.AsMemory(0,count)); Check.Equal(page==0?"galaxy.catalogue.profile":"galaxy.catalogue.profile.result",request.RootElement.GetProperty("op").GetString()); if(page==1){Check.Equal(1,request.RootElement.GetProperty("overrideOffset").GetInt32());Check.Equal(1,request.RootElement.GetProperty("navRouteOffset").GetInt32());} var points=Page(offset:page,navOffset:page,total:2,navTotal:2,id:page+1,process:process); var result=new{generation=9L,done=true,cancelled=false,error="",profile=new{origin=new[]{0d,0d,0d},referenceKind="camera-eye", navRouteViewMode=1,navRouteMembershipMode=1,pointOverrides=points}}; await server.WriteAsync(JsonSerializer.SerializeToUtf8Bytes(new{ok=true,result}),stop.Token);await server.WriteAsync(new byte[]{10},stop.Token); } }); var profile=await new NativeGalaxyCatalog(process).CatalogueProfileAsync(new(0,0,0),new(0,0,0),stop.Token);await serve; Check.That(profile.PointOverrides is{D50Complete:true,NavRouteComplete:true,SourceGeneration:9} points&& points.Overrides.Count==2&&points.Membership(2)==true&&points.OverridePresence(3)==false); } [Test] public async Task SlowAuxiliaryPageDoesNotHoldBasicLodAndSupersedingViewDiscardsItsCompletion() { var process=Random.Shared.Next(180000000,190000000);using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(6)); var auxiliaryStarted=new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var releaseAuxiliary=new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); NamedPipeServerStream Server()=>new($"XFEEDToolBox.Navigation.{process}",PipeDirection.InOut,NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte,PipeOptions.Asynchronous); async Task Request(NamedPipeServerStream server) { await server.WaitForConnectionAsync(stop.Token);var bytes=new byte[65536];var count=0; while(count { await using var server=Server();var request=await Request(server); Check.Equal("galaxy.catalogue.profile",request.GetProperty("op").GetString()); await Reply(server,9,Page(total:2,navTotal:2,process:process)); }); var client=new NativeGalaxyCatalog(process); var initial=await client.CatalogueProfileFirstPageAsync(new(0,0,0),new(0,0,0),stop.Token);await initialServer; Check.That(initial.HasLodInputs&&initial.PointOverrides is{D50Complete:false,NavRouteComplete:false}); var slowServer=Task.Run(async()=> { await using var server=Server();var request=await Request(server); Check.Equal("galaxy.catalogue.profile.result",request.GetProperty("op").GetString()); auxiliaryStarted.SetResult();await releaseAuxiliary.Task.WaitAsync(stop.Token); await Reply(server,9,Page(offset:1,navOffset:1,total:2,navTotal:2,id:2,process:process)); }); var pending=client.CompletePointOverridesAsync(initial,stop.Token);await auxiliaryStarted.Task.WaitAsync(stop.Token); try { var nextServer=Task.Run(async()=> { await using var server=Server();var request=await Request(server); Check.Equal("galaxy.catalogue.profile",request.GetProperty("op").GetString()); await Reply(server,10,Page(generation:10,total:0,navTotal:0,process:process)); }); var next=await client.CatalogueProfileFirstPageAsync(new(1,0,0),new(1,0,0),stop.Token);await nextServer; // The slow page is still withheld. Both basic LOD results have // already returned, and the optional reader holds neither gate. Check.That(next.HasLodInputs&&next.PointOverrides?.SourceGeneration==10&&!pending.IsCompleted); } finally{releaseAuxiliary.TrySetResult();} Check.That(await pending is null);await slowServer; } }