using System.IO.Pipes;
using System.Text.Json;
using XFEEDToolBox.Core.Galaxy;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public class NativeGalaxyCellsTests
{
private static async Task Serve(int id,object result,CancellationToken token,Action<JsonElement>? inspect=null)
{
await using var server=new NamedPipeServerStream($"XFEEDToolBox.Navigation.{id}",PipeDirection.InOut,1,PipeTransmissionMode.Byte,PipeOptions.Asynchronous);
await server.WaitForConnectionAsync(token);var buffer=new byte[65536];var size=await server.ReadAsync(buffer,token);
using(var request=JsonDocument.Parse(buffer.AsMemory(0,size)))inspect?.Invoke(request.RootElement);
await server.WriteAsync(JsonSerializer.SerializeToUtf8Bytes(new{ok=true,result}),token);await server.WriteAsync(new byte[]{10},token);
if(OperatingSystem.IsWindows())server.WaitForPipeDrain();
}
[Test] public async Task ExactCellsPublishRealRowsBeforeDoneAndKeepTheWholeCellCorner()
{
var id=Random.Shared.Next(100000000,110000000);using var deadline=new CancellationTokenSource(TimeSpan.FromSeconds(5));
var completed=false;var partialBeforeDone=false;var address=(1UL<<44)|7;
var server=Task.Run(async()=>
{
await Serve(id,new{generation=7,done=false,error="",total=1,items=new[]{new{name="Cell corner",systemAddress=address.ToString(),position=new[]{1000d,1000d,1000d},starClass="M"}},
scope="requested-cells",requestedCells=1,minimumMassCode=7,processedCells=0,totalCells=1,failedCells=0,truncated=false,complete=false,origin=new[]{1d,2d,3d}},deadline.Token,r=>
{Check.Equal("galaxy.cells",r.GetProperty("op").GetString());Check.Equal("7",r.GetProperty("cellAddresses")[0].GetString());Check.Equal(8,r.GetProperty("radii").GetArrayLength());});
await Serve(id,new{generation=7,done=true,error="",total=1,items=Array.Empty<object>(),scope="requested-cells",requestedCells=1,
minimumMassCode=7,processedCells=1,totalCells=1,failedCells=0,truncated=false,complete=true,origin=new[]{1d,2d,3d}},deadline.Token,r=>
{Check.Equal(1,r.GetProperty("offset").GetInt32());completed=true;});
});
var catalog=new NativeGalaxyCatalog(id);catalog.RegionProgress+=progress=>
{if(progress.Systems.Count>0)partialBeforeDone=!completed&&!progress.Done;};
var result=await catalog.CellsAsync(new(0,0,0),Enumerable.Repeat(1d,8).ToArray(),new[]{7UL},deadline.Token);await server;
Check.That(partialBeforeDone);Check.Equal(1,result.Systems.Count);Check.Equal(1000d,result.Systems[0].Position.X);
Check.That(result.AreRequestedCellsCovered&&!result.IsFullyCovered);Check.Equal(7,result.MinimumMassCode);
Check.Equal(2d,result.Origin!.Y);
}
[Test] public void RequestedMassSevenCoverageDoesNotClaimAllMassSphereCoverage()
{
using var json=JsonDocument.Parse("{\"scope\":\"requested-cells\",\"requestedCells\":1,\"minimumMassCode\":7,\"processedCells\":1,\"totalCells\":1,\"failedCells\":0,\"truncated\":false,\"complete\":true}");
var result=NativeGalaxyCatalog.ReadCoverage(json.RootElement,[]);
Check.That(result.AreRequestedCellsCovered&&!result.IsFullyCovered);Check.Equal(1,result.RequestedCells);
using var changed=JsonDocument.Parse("{\"scope\":\"requested-cells\",\"requestedCells\":2,\"minimumMassCode\":7,\"processedCells\":1,\"totalCells\":1}");
Check.Throws<InvalidDataException>(()=>NativeGalaxyCatalog.ReadCoverage(changed.RootElement,[]));
}
[Test] public void CellRequestRejectsNoncanonicalAddressesAndUnsupportedKnownProtocolBeforeIpc()
{
var catalog=new NativeGalaxyCatalog(123);var radii=Enumerable.Repeat(1d,8).ToArray();
Check.Throws<ArgumentException>(()=>catalog.CellsAsync(new(0,0,0),radii,new[]{1UL<<44}));
Check.Throws<ArgumentException>(()=>catalog.CellsAsync(new(0,0,0),radii,new[]{7UL,7UL}));
using var description=JsonDocument.Parse("{\"processId\":123}");catalog.ApplyProtocolDescription(description.RootElement);
Check.Throws<NotSupportedException>(()=>catalog.CellsAsync(new(0,0,0),radii,new[]{7UL}));
}
[Test] public async Task CellPagesRejectMissingOrChangedOriginsAndConflictingStarIdentities()
{
for(var scenario=0;scenario<4;scenario++)
{
var id=Random.Shared.Next(114000000,115000000);using var deadline=new CancellationTokenSource(TimeSpan.FromSeconds(5));
var variant=scenario;var address=1UL<<44;
object Reply(bool first)
{
var origin=variant==0&&first||variant==2&&!first?null:new[]{variant==1&&!first?4d:1d,2d,3d};
var row=new{name=variant==3&&!first?"Conflicting name":"Fixture star",systemAddress=address.ToString(),position=new[]{0d,0d,0d},starClass="M"};
return new{generation=72,done=!first,error="",total=2,items=new[]{row},scope="requested-cells",requestedCells=1,
minimumMassCode=0,processedCells=first?0:1,totalCells=1,failedCells=0,truncated=false,complete=!first,origin};
}
var server=Task.Run(async()=>
{
await Serve(id,Reply(true),deadline.Token);
if(variant!=0)await Serve(id,Reply(false),deadline.Token);
await Serve(id,new{generation=72,done=true},deadline.Token,r=>Check.Equal("galaxy.cancel",r.GetProperty("op").GetString()));
await Serve(id,new{generation=72,done=true},deadline.Token,r=>Check.Equal("galaxy.result",r.GetProperty("op").GetString()));
});
try
{
await new NativeGalaxyCatalog(id).CellsAsync(new(0,0,0),Enumerable.Repeat(1d,8).ToArray(),new[]{0UL},deadline.Token);
throw new Exception("Invalid cell page was accepted");
}
catch(InvalidDataException){}
await server;
}
}
}
using System.IO.Pipes;
using System.Text.Json;
using XFEEDToolBox.Core.Galaxy;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public class NativeGalaxyCellsTests
{
private static async Task Serve(int id,object result,CancellationToken token,Action<JsonElement>? inspect=null)
{
await using var server=new NamedPipeServerStream($"XFEEDToolBox.Navigation.{id}",PipeDirection.InOut,1,PipeTransmissionMode.Byte,PipeOptions.Asynchronous);
await server.WaitForConnectionAsync(token);var buffer=new byte[65536];var size=await server.ReadAsync(buffer,token);
using(var request=JsonDocument.Parse(buffer.AsMemory(0,size)))inspect?.Invoke(request.RootElement);
await server.WriteAsync(JsonSerializer.SerializeToUtf8Bytes(new{ok=true,result}),token);await server.WriteAsync(new byte[]{10},token);
if(OperatingSystem.IsWindows())server.WaitForPipeDrain();
}
[Test] public async Task ExactCellsPublishRealRowsBeforeDoneAndKeepTheWholeCellCorner()
{
var id=Random.Shared.Next(100000000,110000000);using var deadline=new CancellationTokenSource(TimeSpan.FromSeconds(5));
var completed=false;var partialBeforeDone=false;var address=(1UL<<44)|7;
var server=Task.Run(async()=>
{
await Serve(id,new{generation=7,done=false,error="",total=1,items=new[]{new{name="Cell corner",systemAddress=address.ToString(),position=new[]{1000d,1000d,1000d},starClass="M"}},
scope="requested-cells",requestedCells=1,minimumMassCode=7,processedCells=0,totalCells=1,failedCells=0,truncated=false,complete=false,origin=new[]{1d,2d,3d}},deadline.Token,r=>
{Check.Equal("galaxy.cells",r.GetProperty("op").GetString());Check.Equal("7",r.GetProperty("cellAddresses")[0].GetString());Check.Equal(8,r.GetProperty("radii").GetArrayLength());});
await Serve(id,new{generation=7,done=true,error="",total=1,items=Array.Empty<object>(),scope="requested-cells",requestedCells=1,
minimumMassCode=7,processedCells=1,totalCells=1,failedCells=0,truncated=false,complete=true,origin=new[]{1d,2d,3d}},deadline.Token,r=>
{Check.Equal(1,r.GetProperty("offset").GetInt32());completed=true;});
});
var catalog=new NativeGalaxyCatalog(id);catalog.RegionProgress+=progress=>
{if(progress.Systems.Count>0)partialBeforeDone=!completed&&!progress.Done;};
var result=await catalog.CellsAsync(new(0,0,0),Enumerable.Repeat(1d,8).ToArray(),new[]{7UL},deadline.Token);await server;
Check.That(partialBeforeDone);Check.Equal(1,result.Systems.Count);Check.Equal(1000d,result.Systems[0].Position.X);
Check.That(result.AreRequestedCellsCovered&&!result.IsFullyCovered);Check.Equal(7,result.MinimumMassCode);
Check.Equal(2d,result.Origin!.Y);
}
[Test] public void RequestedMassSevenCoverageDoesNotClaimAllMassSphereCoverage()
{
using var json=JsonDocument.Parse("{\"scope\":\"requested-cells\",\"requestedCells\":1,\"minimumMassCode\":7,\"processedCells\":1,\"totalCells\":1,\"failedCells\":0,\"truncated\":false,\"complete\":true}");
var result=NativeGalaxyCatalog.ReadCoverage(json.RootElement,[]);
Check.That(result.AreRequestedCellsCovered&&!result.IsFullyCovered);Check.Equal(1,result.RequestedCells);
using var changed=JsonDocument.Parse("{\"scope\":\"requested-cells\",\"requestedCells\":2,\"minimumMassCode\":7,\"processedCells\":1,\"totalCells\":1}");
Check.Throws<InvalidDataException>(()=>NativeGalaxyCatalog.ReadCoverage(changed.RootElement,[]));
}
[Test] public void CellRequestRejectsNoncanonicalAddressesAndUnsupportedKnownProtocolBeforeIpc()
{
var catalog=new NativeGalaxyCatalog(123);var radii=Enumerable.Repeat(1d,8).ToArray();
Check.Throws<ArgumentException>(()=>catalog.CellsAsync(new(0,0,0),radii,new[]{1UL<<44}));
Check.Throws<ArgumentException>(()=>catalog.CellsAsync(new(0,0,0),radii,new[]{7UL,7UL}));
using var description=JsonDocument.Parse("{\"processId\":123}");catalog.ApplyProtocolDescription(description.RootElement);
Check.Throws<NotSupportedException>(()=>catalog.CellsAsync(new(0,0,0),radii,new[]{7UL}));
}
[Test] public async Task CellPagesRejectMissingOrChangedOriginsAndConflictingStarIdentities()
{
for(var scenario=0;scenario<4;scenario++)
{
var id=Random.Shared.Next(114000000,115000000);using var deadline=new CancellationTokenSource(TimeSpan.FromSeconds(5));
var variant=scenario;var address=1UL<<44;
object Reply(bool first)
{
var origin=variant==0&&first||variant==2&&!first?null:new[]{variant==1&&!first?4d:1d,2d,3d};
var row=new{name=variant==3&&!first?"Conflicting name":"Fixture star",systemAddress=address.ToString(),position=new[]{0d,0d,0d},starClass="M"};
return new{generation=72,done=!first,error="",total=2,items=new[]{row},scope="requested-cells",requestedCells=1,
minimumMassCode=0,processedCells=first?0:1,totalCells=1,failedCells=0,truncated=false,complete=!first,origin};
}
var server=Task.Run(async()=>
{
await Serve(id,Reply(true),deadline.Token);
if(variant!=0)await Serve(id,Reply(false),deadline.Token);
await Serve(id,new{generation=72,done=true},deadline.Token,r=>Check.Equal("galaxy.cancel",r.GetProperty("op").GetString()));
await Serve(id,new{generation=72,done=true},deadline.Token,r=>Check.Equal("galaxy.result",r.GetProperty("op").GetString()));
});
try
{
await new NativeGalaxyCatalog(id).CellsAsync(new(0,0,0),Enumerable.Repeat(1d,8).ToArray(),new[]{0UL},deadline.Token);
throw new Exception("Invalid cell page was accepted");
}
catch(InvalidDataException){}
await server;
}
}
}