XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
using XFEExtension.NetCore.XUnit.Attributes;

namespace XFEEDToolBox.Test;

[TestFixture]
public sealed class GameRouteMirrorTests
{
    private static readonly DateTimeOffset Now=DateTimeOffset.Parse("2026-10-23T00:00:00Z");
    private static NavWaypoint Point(long id)=>new(id,"System "+id,new(id,0,0),"K");
    private static PlayerState Player=>new(){InGame=true,CommanderId="commander",LoadGameAt=Now,Navigation=new(){CurrentSystemAddress=1,Route=[Point(1),Point(2),Point(3)],RouteAt=Now}};
    [Test] public void AGamePlottedRouteIsAdoptedOnceWithoutPlottingOrFlightCommands()
    {
        var mirror=new GameRouteMirror();var update=mirror.Observe(Player);
        Check.That(update.Changed&&!update.Cleared&&!update.Arrived);Check.Equal(3L,update.Destination!.SystemAddress);
        Check.That(!mirror.Observe(Player).Changed);
        Check.That(!mirror.Observe(Player with{InGame=false,Navigation=new()}).Changed);
        Check.That(!mirror.Observe(Player with{Navigation=Player.Navigation with{Route=[Point(1),Point(4)]}},true).Changed);
    }
    [Test] public void ManualClearAndFinalArrivalRemainDistinctAndANewGameRouteReplacesTheOldOne()
    {
        var mirror=new GameRouteMirror();mirror.Observe(Player);
        var cleared=mirror.Observe(Player with{Navigation=Player.Navigation with{Route=[],RouteAt=Now.AddSeconds(1)}});
        Check.That(cleared.Cleared&&!cleared.Arrived);
        mirror.Observe(Player);
        var arrived=mirror.Observe(Player with{Navigation=Player.Navigation with{Route=[],CurrentSystemAddress=3,RouteAt=Now.AddSeconds(2)}});
        Check.That(arrived.Arrived&&!arrived.Cleared);
        mirror.Observe(Player);
        var replaced=mirror.Observe(Player with{Navigation=Player.Navigation with{Route=[Point(1),Point(4)],RouteAt=Now.AddSeconds(3)}});
        Check.That(replaced.Replaced);Check.Equal(4L,replaced.Destination!.SystemAddress);
    }
    [Test] public void ARecentMatchingFinalJumpClearRetainsTheRouteUntilActualArrival()
    {
        var mirror=new GameRouteMirror(()=>Now.AddSeconds(12));mirror.Observe(Player);
        var jumping=Player with{Navigation=Player.Navigation with{CurrentSystemAddress=2,Route=[],RouteAt=Now.AddSeconds(11),JumpInProgress=true,
            JumpTargetSystemAddress=3,JumpTargetSystem="System 3",JumpStartedAt=Now.AddSeconds(10)}};
        Check.That(!mirror.Observe(jumping).Cleared);
        var arrived=mirror.Observe(jumping with{Navigation=jumping.Navigation with{JumpInProgress=false,LastJumpAt=Now.AddSeconds(12),CurrentSystemAddress=3}});
        Check.That(arrived.Arrived&&!arrived.Cleared);
        mirror.Observe(Player);
        Check.That(mirror.Observe(jumping with{Navigation=jumping.Navigation with{JumpTargetSystemAddress=4,JumpTargetSystem="System 4"}}).Cleared);
        mirror.Observe(Player);
        Check.That(mirror.Observe(jumping with{Navigation=jumping.Navigation with{JumpStartedAt=Now.AddMinutes(-2)}}).Cleared);
    }
    [Test] public async Task CancellationRequiresBothTheOriginalGameAcknowledgementAndTheJournalClear()
    {
        var player=Player;var driver=new FakeDriver();
        driver.Read=number=>
        {
            if(number==2)player=player with{Navigation=player.Navigation with{Route=[],RouteAt=Now.AddSeconds(1)}};
            return new(71,true,false,false,0,0,false,true,0,0);
        };
        await new GameRouteCancellation(()=>player,TimeSpan.FromSeconds(2)).CancelAsync(3,driver);
        Check.Equal(1,driver.Mutations);Check.That(driver.Reads>=3);
    }
    [Test] public async Task ChangingTheDestinationDuringCancellationPreservesTheNewRoute()
    {
        var player=Player;var driver=new FakeDriver();
        driver.Read=_=>{player=player with{Navigation=player.Navigation with{Route=[Point(1),Point(4)]}};return new(71,false,true,false,0,0,false,false,0,0);};
        await Rejects<InvalidOperationException>(()=>new GameRouteCancellation(()=>player).CancelAsync(3,driver));
        Check.Equal(1,driver.Mutations);Check.Equal(4L,player.Navigation.Route[^1].SystemAddress);
    }
    [Test] public async Task UnsupportedOrAlreadyChangedRoutesCannotIssueNativeClear()
    {
        var driver=new FakeDriver{SupportsGameRouteCancellation=false};
        await Rejects<InvalidOperationException>(()=>new GameRouteCancellation(()=>Player).CancelAsync(3,driver));
        Check.Equal(0,driver.Mutations);driver.SupportsGameRouteCancellation=true;
        await Rejects<InvalidOperationException>(()=>new GameRouteCancellation(()=>Player).CancelAsync(4,driver));
        Check.Equal(0,driver.Mutations);
    }
    [Test] public async Task AFalseNativeClearNeverClearsTheGameRouteOnScreen()
    {
        var driver=new FakeDriver{Read=_=>new(71,true,false,false,0,0,false,true,0,0)};
        await Rejects<OperationCanceledException>(()=>new GameRouteCancellation(()=>Player,TimeSpan.FromMilliseconds(150)).CancelAsync(3,driver));
        Check.Equal(1,driver.Mutations);Check.Equal(3,Player.Navigation.Route.Count);
    }
    private static async Task Rejects<T>(Func<Task> action)where T:Exception
    {try{await action();}catch(T){return;}throw new InvalidOperationException("Expected "+typeof(T).Name);}
    private sealed class FakeDriver:IGameRouteCancellationDriver
    {
        public bool SupportsGameRouteCancellation{get;set;}=true;
        public int Mutations,Reads;
        public Func<int,GameRouteStatus> Read{get;set;}=_=>new(71,false,true,false,3,2,true,true,3,123);
        public Task<long> RequestGameRouteCancellationAsync(long destination,CancellationToken token){Mutations++;return Task.FromResult(71L);}
        public Task<GameRouteStatus> ReadGameRouteStatusAsync(long generation,long destination,CancellationToken token)=>Task.FromResult(Read(++Reads));
    }
}