using System.IO.Pipes; using System.Text.Json; using XFEEDToolBox.Core.Models; using XFEEDToolBox.Core.Navigation; using XFEExtension.NetCore.XUnit.Attributes; namespace XFEEDToolBox.Test; [TestFixture] public sealed class NativeDepartureEntryCleanupTests { [Test]public async Task DirectAndDelegatedPublicDepartureEntriesReleaseTheirOwnObservationClaimAfterCancellation() { foreach(var delegated in new[]{false,true})foreach(var continuation in new[]{false,true}) { var pid=Random.Shared.Next(200000000,290000000);var hash=new string('a',64);var released=0;string? claimedOwner=null; var state=new PlayerState{InGame=true,Docked=!continuation,CommanderId="F1",CurrentShipId=7,LoadGameAt=DateTimeOffset.UtcNow.AddMinutes(-1), Navigation=new(){CurrentSystemAddress=1},Ships=[new(){ShipId=7,Type="type9",Modules=[new(){Slot="Slot01",Item="int_dockingcomputer_advanced",Enabled=true}]}]}; using var serverStop=new CancellationTokenSource(TimeSpan.FromSeconds(5)); var server=Task.Run(async()=> { while(!serverStop.IsCancellationRequested) { try { await using var pipe=new NamedPipeServerStream($"XFEEDToolBox.Navigation.{pid}",PipeDirection.InOut,1,PipeTransmissionMode.Byte,PipeOptions.Asynchronous); await pipe.WaitForConnectionAsync(serverStop.Token);var buffer=new byte[8192];var count=await pipe.ReadAsync(buffer,serverStop.Token); using var document=JsonDocument.Parse(buffer.AsMemory(0,count));var request=document.RootElement;var op=request.GetProperty("op").GetString();object result; if(op=="describe")result=new{schema=1,name="XFEEDToolBox.Navigation",processId=pid,gameSha256=hash,verified=true,owningThread=1,localTargets=true,evidence="isolated cleanup fixture"}; else if(op=="local.select") { claimedOwner=request.GetProperty("owner").GetString(); result=new{generation=1,selected=true,target=new{key=request.GetProperty("key").GetInt32(),bodyAddress=request.GetProperty("bodyAddress").GetString(), kind=request.GetProperty("kind").GetInt32(),marketId=request.GetProperty("marketId").GetUInt32(),name=request.GetProperty("name").GetString()}}; } else if(op=="release") {Check.That(claimedOwner is not null&&request.GetProperty("owner").GetString()==claimedOwner);released++;result=new{released=true};} else throw new InvalidOperationException("Cancellation cleanup sent an unexpected flight command: "+op); await pipe.WriteAsync(JsonSerializer.SerializeToUtf8Bytes(new{ok=true,result}),serverStop.Token);await pipe.WriteAsync(new byte[]{10},serverStop.Token); if(OperatingSystem.IsWindows())pipe.WaitForPipeDrain(); } catch(OperationCanceledException)when(serverStop.IsCancellationRequested){break;} } }); var local=new NativeSystemFlightControlDriver(pid,hash,()=>state); var galaxy=new NativeFlightControlDriver(pid,hash,()=>state); ISystemFlightControlDriver selection=delegated?galaxy:local;IFlightDepartureDriver departure=delegated?galaxy:local; try { await selection.SelectAsync(new(1,101,"Body",SystemTargetKind.Orbit){NativeKey=7,NativeKind=1,NativeIdentity=hash},serverStop.Token); using var userStop=new CancellationTokenSource();userStop.Cancel();var cancelled=false; try { if(continuation)await departure.EnsureDepartureClearanceAsync(null,userStop.Token); else await departure.DepartAsync(userStop.Token); } catch(OperationCanceledException){cancelled=true;} Check.That(cancelled);Check.Equal(1,released); } finally{serverStop.Cancel();await server;} } } }