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 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(); 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(); 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();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); } }