using System.IO.Pipes;
using System.Text;
using System.Text.Json;
using XFEExtension.NetCore.XUnit.Attributes;
using XFEEDToolBox.Core.Session;
namespace XFEEDToolBox.Test;
[TestFixture]
public class NativeRelogCapabilityTests
{
private static async Task Serve(int pid,object[] responses,List<string> commands,CancellationToken token)
{
foreach(var response in responses)
{
await using var pipe=new NamedPipeServerStream($"XFEEDToolBox.Navigation.{pid}",PipeDirection.InOut,1,PipeTransmissionMode.Byte,PipeOptions.Asynchronous);
await pipe.WaitForConnectionAsync(token);var buffer=new byte[4096];var count=await pipe.ReadAsync(buffer,token);
using var request=JsonDocument.Parse(buffer.AsMemory(0,count));commands.Add(request.RootElement.GetProperty("op").GetString()!);
await pipe.WriteAsync(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new{ok=true,result=response})+"\n"),token);await pipe.FlushAsync(token);
pipe.WaitForPipeDrain();
}
}
private static object Hello(int pid)=>new{processId=pid,gameSha256=NativeRelogChannel.SupportedGameSha256,nativeRelog=true};
[Test] public async Task EntryCapabilityCannotMutateTheUnverifiedReturnToMenuPath()
{
var pid=Random.Shared.Next(20000000,29000000);using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(5));var commands=new List<string>();
var server=Serve(pid,[Hello(pid)],commands,stop.Token);var channel=new NativeRelogChannel(pid,NativeRelogChannel.SupportedGameSha256);
var refused=false;try{await channel.BeginAsync(Guid.NewGuid().ToString("N"),stop.Token);}catch(NotSupportedException){refused=true;}
await server;Check.That(refused);Check.Equal("describe",string.Join(',',commands));
}
[Test] public async Task VerifiedMenuEntryRemainsAvailableWithoutAClaimAboutMenuReturn()
{
var pid=Random.Shared.Next(20000000,29000000);using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(5));var commands=new List<string>();
var server=Serve(pid,[Hello(pid),new{schema=1,name="XFEEDToolBox.Session",processId=pid,gameSha256=NativeRelogChannel.SupportedGameSha256}],commands,stop.Token);
await new NativeRelogChannel(pid,NativeRelogChannel.SupportedGameSha256,entryOnly:true).BeginAsync(Guid.NewGuid().ToString("N"),stop.Token);
await server;Check.Equal("describe,session.begin",string.Join(',',commands));
}
[Test] public async Task StartupNoticePreparationCannotRunDuringReleaseOrAfterCancellation()
{
var pid=Random.Shared.Next(20000000,29000000);using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(5));var commands=new List<string>();var preparations=0;
var response=new{schema=1,name="XFEEDToolBox.Session",processId=pid,gameSha256=NativeRelogChannel.SupportedGameSha256};
var server=Serve(pid,[Hello(pid),response,response],commands,stop.Token);
var channel=new NativeRelogChannel(pid,NativeRelogChannel.SupportedGameSha256,entryOnly:true,prepareEntry:_=>{preparations++;return Task.CompletedTask;});
await channel.BeginAsync("owner",stop.Token);await channel.EndAsync("owner");await server;
Check.Equal(1,preparations);Check.Equal("describe,session.begin,session.end",string.Join(',',commands));
using var cancelled=new CancellationTokenSource();cancelled.Cancel();var refused=false;
try{await channel.BeginAsync("other",cancelled.Token);}catch(OperationCanceledException){refused=true;}
Check.That(refused);Check.Equal(1,preparations);
}
}
using System.IO.Pipes;
using System.Text;
using System.Text.Json;
using XFEExtension.NetCore.XUnit.Attributes;
using XFEEDToolBox.Core.Session;
namespace XFEEDToolBox.Test;
[TestFixture]
public class NativeRelogCapabilityTests
{
private static async Task Serve(int pid,object[] responses,List<string> commands,CancellationToken token)
{
foreach(var response in responses)
{
await using var pipe=new NamedPipeServerStream($"XFEEDToolBox.Navigation.{pid}",PipeDirection.InOut,1,PipeTransmissionMode.Byte,PipeOptions.Asynchronous);
await pipe.WaitForConnectionAsync(token);var buffer=new byte[4096];var count=await pipe.ReadAsync(buffer,token);
using var request=JsonDocument.Parse(buffer.AsMemory(0,count));commands.Add(request.RootElement.GetProperty("op").GetString()!);
await pipe.WriteAsync(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(new{ok=true,result=response})+"\n"),token);await pipe.FlushAsync(token);
pipe.WaitForPipeDrain();
}
}
private static object Hello(int pid)=>new{processId=pid,gameSha256=NativeRelogChannel.SupportedGameSha256,nativeRelog=true};
[Test] public async Task EntryCapabilityCannotMutateTheUnverifiedReturnToMenuPath()
{
var pid=Random.Shared.Next(20000000,29000000);using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(5));var commands=new List<string>();
var server=Serve(pid,[Hello(pid)],commands,stop.Token);var channel=new NativeRelogChannel(pid,NativeRelogChannel.SupportedGameSha256);
var refused=false;try{await channel.BeginAsync(Guid.NewGuid().ToString("N"),stop.Token);}catch(NotSupportedException){refused=true;}
await server;Check.That(refused);Check.Equal("describe",string.Join(',',commands));
}
[Test] public async Task VerifiedMenuEntryRemainsAvailableWithoutAClaimAboutMenuReturn()
{
var pid=Random.Shared.Next(20000000,29000000);using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(5));var commands=new List<string>();
var server=Serve(pid,[Hello(pid),new{schema=1,name="XFEEDToolBox.Session",processId=pid,gameSha256=NativeRelogChannel.SupportedGameSha256}],commands,stop.Token);
await new NativeRelogChannel(pid,NativeRelogChannel.SupportedGameSha256,entryOnly:true).BeginAsync(Guid.NewGuid().ToString("N"),stop.Token);
await server;Check.Equal("describe,session.begin",string.Join(',',commands));
}
[Test] public async Task StartupNoticePreparationCannotRunDuringReleaseOrAfterCancellation()
{
var pid=Random.Shared.Next(20000000,29000000);using var stop=new CancellationTokenSource(TimeSpan.FromSeconds(5));var commands=new List<string>();var preparations=0;
var response=new{schema=1,name="XFEEDToolBox.Session",processId=pid,gameSha256=NativeRelogChannel.SupportedGameSha256};
var server=Serve(pid,[Hello(pid),response,response],commands,stop.Token);
var channel=new NativeRelogChannel(pid,NativeRelogChannel.SupportedGameSha256,entryOnly:true,prepareEntry:_=>{preparations++;return Task.CompletedTask;});
await channel.BeginAsync("owner",stop.Token);await channel.EndAsync("owner");await server;
Check.Equal(1,preparations);Check.Equal("describe,session.begin,session.end",string.Join(',',commands));
using var cancelled=new CancellationTokenSource();cancelled.Cancel();var refused=false;
try{await channel.BeginAsync("other",cancelled.Token);}catch(OperationCanceledException){refused=true;}
Check.That(refused);Check.Equal(1,preparations);
}
}