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 class SystemNavigationRunnerTests
{
    private static readonly DateTimeOffset Now=DateTimeOffset.Parse("2026-10-22T01:00:00Z");
    private static readonly SystemNavigationTarget Target=new(1,10,"Exact station",SystemTargetKind.Station,20);
    private static PlayerState Player=>new(){CommanderId="F1",CurrentShipId=7,InGame=true,LoadGameAt=Now.AddMinutes(-1),Navigation=new(){CurrentSystemAddress=1},Ships=[new(){ShipId=7,HullHealth=1}]};
    private static SystemFlightFrame Docked=>new(){Sequence=1,ObservedAt=Now,GameSha256=new('a',64),LoadoutFingerprint="exact-ship",ShipEntity=17,SystemAddress=1,ReferenceWorld=10,
        SelectedTarget=Target,GeometryVerified=true,InGame=true,Mode=LocalFlightMode.Docked,DockedMarketId=20,
        Forward=new(0,0,1),Up=new(0,1,0),MaximumNormalSpeed=100,MaximumCruiseSpeed=1e9,BrakingAcceleration=10,TranslationAcceleration=10,LegalApproachSpeed=100};
    private sealed class Fake:ISystemFlightControlDriver
    {
        public readonly List<string> Calls=[];
        public Func<int,SystemFlightFrame> Frame=_=>Docked;
        public Action? OnPrepare;
        public Action? OnApply;
        public Action<SystemFlightDecision>? OnDecision;
        public bool Ready=true;
        private int reads;
        public Task<SystemNavigationCapabilities> InspectAsync(CancellationToken t){Calls.Add("inspect");return Task.FromResult(new SystemNavigationCapabilities(new('a',64),Ready,true,true,true,true,true,"Isolated fixture"));}
        public Task SelectAsync(SystemNavigationTarget target,CancellationToken t){Calls.Add("select");Check.That(target.SameIdentity(Target));return Task.CompletedTask;}
        public Task PrepareAsync(CancellationToken t){Calls.Add("prepare");OnPrepare?.Invoke();return Task.CompletedTask;}
        public Task<SystemFlightFrame> ReadSystemFlightAsync(CancellationToken t){Calls.Add("read");return Task.FromResult(Frame(++reads));}
        public Task ApplySystemFlightAsync(SystemFlightDecision d,SystemFlightFrame f,SystemNavigationTarget target,DateTimeOffset expires,CancellationToken t)
        {Calls.Add("apply");Check.That(target.SameIdentity(Target)&&expires==f.ObservedAt.AddMilliseconds(250));OnDecision?.Invoke(d);OnApply?.Invoke();return Task.CompletedTask;}
        public Task ReleaseSystemFlightAsync(){Calls.Add("release");return Task.CompletedTask;}
    }
    [Test] public async Task ExactDockedAcknowledgementCompletesAndAlwaysReleasesControl()
    {
        var driver=new Fake();var runner=new SystemNavigationRunner(()=>Player,()=>Now);await runner.RunAsync(Target,driver);
        Check.Equal(NavigationRunState.Completed,runner.State);Check.Equal("inspect,select,prepare,read,apply,release",string.Join(',',driver.Calls));
    }
    [Test]public async Task DockingAtTheSameMarketDuringPreparationCompletesWithoutReadingOrApplyingFlightInputs()
    {
        var player=Player;var driver=new Fake{OnPrepare=()=>player=player with{Docked=true,MarketId=20},Frame=_=>throw new InvalidOperationException("Docked physics need not keep updating.")};
        var runner=new SystemNavigationRunner(()=>player,()=>Now);await runner.RunAsync(Target,driver);
        Check.Equal(NavigationRunState.Completed,runner.State);Check.Equal("inspect,select,prepare,release",string.Join(',',driver.Calls));
    }
    [Test]public async Task ADockedArrivalNeverOverridesAChangedShipOrAnExternalCancellation()
    {
        foreach(var cancel in new[]{false,true})
        {
            using var stop=new CancellationTokenSource();var player=Player;var driver=new Fake{OnPrepare=()=>
            {player=player with{Docked=true,MarketId=20};if(cancel)stop.Cancel();else player=player with{CurrentShipId=8};}};
            var runner=new SystemNavigationRunner(()=>player,()=>Now);await runner.RunAsync(Target,driver,stop.Token);
            Check.Equal(cancel?NavigationRunState.Stopped:NavigationRunState.Failed,runner.State);Check.Equal(0,driver.Calls.Count(call=>call=="read"||call=="apply"));
        }
    }
    [Test]public async Task APreCancelledRequestAtTheDestinationDoesNotReportSuccessfulArrival()
    {
        using var stop=new CancellationTokenSource();stop.Cancel();var driver=new Fake();
        var runner=new SystemNavigationRunner(()=>Player with{Docked=true,MarketId=20},()=>Now);await runner.RunAsync(Target,driver,stop.Token);
        Check.Equal(NavigationRunState.Stopped,runner.State);Check.Equal("release",string.Join(',',driver.Calls));
    }
    [Test]public async Task RepeatedStillFreshPhysicsFrameWaitsWithoutReleasingControlOrReplayingInputs()
    {
        var at=Now;var driver=new Fake{Frame=n=>
        {
            at=Now.AddMilliseconds((n-1)*100);
            return n<3?Landing(1,Now,NativeAssistState.Ready):Docked with{Sequence=2,ObservedAt=at};
        }};
        var runner=new SystemNavigationRunner(()=>Player,()=>at);await runner.RunAsync(Target,driver);
        Check.Equal(NavigationRunState.Completed,runner.State);Check.Equal(3,driver.Calls.Count(call=>call=="read"));
        Check.Equal(1,driver.Calls.Count(call=>call=="release"));Check.Equal(2,driver.Calls.Count(call=>call=="apply"));
    }
    private static SystemFlightFrame Landing(int sequence,DateTimeOffset at,NativeAssistState assist)=>Docked with{Sequence=sequence,ObservedAt=at,Mode=LocalFlightMode.Normal,
        TargetDistance=7000,TargetPosition=new(0,0,7000),Speed=50,Assists=new(false,true,false),DockingAssist=assist,
        Clearance=new(20,DockingPermission.Granted,9,Now,Now.AddMinutes(5))};
    [Test] public async Task RejectedUndispatchedResetObservesActualAiOnDamagedButProtectedShip()
    {
        var at=Now;var arms=0;var ship=Player with{Ships=[new(){ShipId=7,HullHealth=.784498}]};
        var driver=new Fake{Frame=n=>{at=Now.AddMilliseconds((n-1)*100);return n==3?Docked with{Sequence=n,ObservedAt=at}:Landing(n,at,n==1?NativeAssistState.Ready:NativeAssistState.Engaged);},
            OnDecision=d=>{if(d.Actions.HasFlag(SystemNavigationAction.EnableDockingAssist)){arms++;throw new DockingResetObservationPendingException("rejected-before-dispatch",20,Now,new InvalidOperationException("initial telemetry rejected"));}}};
        var runner=new SystemNavigationRunner(()=>ship,()=>at);await runner.RunAsync(Target,driver);
        Check.Equal(NavigationRunState.Completed,runner.State);Check.Equal(1,arms);Check.Equal(1,driver.Calls.Count(x=>x=="apply"));Check.Equal(1,driver.Calls.Count(x=>x=="release"));
    }
    [Test] public async Task LostResetAcknowledgementObservesActualAiAndDockingWithoutRearmingOrSuspending()
    {
        var at=Now;var arms=0;var driver=new Fake{Frame=n=>{at=Now.AddMilliseconds((n-1)*100);if(n==2)throw new IOException("Brief telemetry interruption during handoff");return n==4?Docked with{Sequence=n,ObservedAt=at}:Landing(n,at,n==1?NativeAssistState.Ready:NativeAssistState.Engaged);},
            OnDecision=d=>{if(d.Actions.HasFlag(SystemNavigationAction.EnableDockingAssist)){arms++;throw new DockingResetObservationPendingException("immutable-request",20,Now,new IOException("Lost status reply"));}}};
        var runner=new SystemNavigationRunner(()=>Player,()=>at);await runner.RunAsync(Target,driver);
        Check.Equal(NavigationRunState.Completed,runner.State);Check.Equal(1,arms);Check.Equal(1,driver.Calls.Count(x=>x=="apply"));Check.Equal(1,driver.Calls.Count(x=>x=="release"));
    }
    [Test] public async Task LostAcknowledgementKeepsTheOriginalTenSecondDeadlineWithoutRearming()
    {
        var at=Now;var arms=0;var exits=0;var driver=new Fake{Frame=n=>{at=Now.AddMilliseconds((n-1)*2100);return Landing(n,at,NativeAssistState.Ready);},
            OnDecision=d=>{if(d.Actions.HasFlag(SystemNavigationAction.EnableDockingAssist)){arms++;throw new DockingResetObservationPendingException("immutable-request",20,Now,new IOException("Lost status reply"));}
                if(d.Actions.HasFlag(SystemNavigationAction.ExitForFailedHandoff))exits++;}};
        var runner=new SystemNavigationRunner(()=>Player,()=>at);await runner.RunAsync(Target,driver);
        Check.Equal(NavigationRunState.Stopped,runner.State);Check.That(runner.HandoffFailed);Check.Equal(1,arms);Check.Equal(1,exits);
        Check.That(runner.Failure is DockingResetObservationPendingException{InnerException:IOException});Check.Equal(6,driver.Calls.Count(x=>x=="read"));
    }
    [Test]public async Task ADelayedNineSecondHandoffCompletesWithoutSessionRecoveryOrAnotherRequest()
    {
        var at=Now;var arms=0;var exits=0;var driver=new Fake{Frame=n=>{at=Now.AddSeconds(n switch{1=>0,2=>3,3=>9,_=>10.5});return n>=4?Docked with{Sequence=n,ObservedAt=at}:Landing(n,at,n==3?NativeAssistState.Engaged:NativeAssistState.Ready);},
            OnDecision=d=>{if(d.Actions.HasFlag(SystemNavigationAction.EnableDockingAssist)){arms++;throw new DockingResetObservationPendingException("immutable-request",20,Now,new IOException());}if(d.Actions.HasFlag(SystemNavigationAction.ExitForFailedHandoff))exits++;}};
        var runner=new SystemNavigationRunner(()=>Player,()=>at);await runner.RunAsync(Target,driver);
        Check.Equal(NavigationRunState.Completed,runner.State);Check.Equal(1,arms);Check.Equal(0,exits);
    }
    [Test]public async Task AnUnconfirmedHandoffAtTwoKilometresRecoversBeforeTheTenSecondDeadline()
    {
        var at=Now;var driver=new Fake{Frame=n=>{at=Now.AddMilliseconds((n-1)*100);var f=Landing(n,at,NativeAssistState.Ready);return n==1?f:f with{TargetDistance=2000,TargetPosition=new(0,0,2000)};},
            OnDecision=d=>{if(d.Actions.HasFlag(SystemNavigationAction.EnableDockingAssist))throw new DockingResetObservationPendingException("immutable-request",20,Now,new IOException());}};
        var runner=new SystemNavigationRunner(()=>Player,()=>at);await runner.RunAsync(Target,driver);Check.That(runner.HandoffFailed);Check.Equal(2,driver.Calls.Count(x=>x=="read"));
    }
    [Test]public async Task HumanOverrideDuringAnUnconfirmedHandoffDoesNotTriggerMenuRecovery()
    {
        var at=Now;var exits=0;var driver=new Fake{Frame=n=>{at=Now.AddSeconds((n-1)*11);return Landing(n,at,NativeAssistState.Ready) with{PlayerOverride=n>1};},
            OnDecision=d=>{if(d.Actions.HasFlag(SystemNavigationAction.EnableDockingAssist))throw new DockingResetObservationPendingException("immutable-request",20,Now,new IOException());if(d.Actions.HasFlag(SystemNavigationAction.ExitForFailedHandoff))exits++;}};
        var runner=new SystemNavigationRunner(()=>Player,()=>at);await runner.RunAsync(Target,driver);Check.Equal(NavigationRunState.Stopped,runner.State);Check.Equal(0,exits);Check.That(!runner.HandoffFailed);
    }
    [Test]public async Task PendingResetKeepsReadingWithoutAlternatingOrRepeatingProgressEveryFrame()
    {
        var at=Now;var arms=0;var messages=new List<string>();
        var driver=new Fake{Frame=n=>{at=Now.AddMilliseconds((n-1)*100);return n==13?Docked with{Sequence=n,ObservedAt=at}:Landing(n,at,NativeAssistState.Ready);},
            OnDecision=d=>{if(d.Actions.HasFlag(SystemNavigationAction.EnableDockingAssist)){arms++;throw new DockingResetObservationPendingException("immutable-request",20,Now,new IOException("Lost status reply"));}}};
        var runner=new SystemNavigationRunner(()=>Player,()=>at);runner.Progress+=p=>messages.Add(p.Message);
        await runner.RunAsync(Target,driver);
        Check.Equal(NavigationRunState.Completed,runner.State);Check.Equal(1,arms);Check.Equal(13,driver.Calls.Count(x=>x=="read"));
        Check.Equal(1,messages.Count(x=>x=="正在核对归零回执与自动降落状态,不重发请求。"));Check.That(messages.Count<=6);
    }
    [Test] public async Task AResetReceiptForAnotherMarketCannotBecomeAnObservedHandoff()
    {
        var driver=new Fake{Frame=_=>Landing(1,Now,NativeAssistState.Ready),OnApply=()=>throw new DockingResetObservationPendingException("wrong-market",99,Now,new IOException())};
        var runner=new SystemNavigationRunner(()=>Player,()=>Now);await runner.RunAsync(Target,driver);
        Check.Equal(NavigationRunState.Failed,runner.State);Check.That(runner.Failure is InvalidDataException);Check.Equal(1,driver.Calls.Count(x=>x=="apply"));
    }
    [Test] public async Task WrongSystemOrUnhealthyHullNeverSelectsOrControlsATarget()
    {
        foreach(var player in new[]{Player with{Navigation=new(){CurrentSystemAddress=2}},Player with{Ships=[Player.Ships[0] with{HullHealth=0}]}})
        {
            var driver=new Fake();var runner=new SystemNavigationRunner(()=>player,()=>Now);await runner.RunAsync(Target,driver);
            Check.Equal(NavigationRunState.Failed,runner.State);Check.Equal("release",string.Join(',',driver.Calls));
        }
    }
    [Test]public async Task StableHistoricalHullDamageDoesNotBlockANewVerifiedLocalNavigation()
    {
        var driver=new Fake{Frame=_=>Docked};var runner=new SystemNavigationRunner(()=>Player with{Ships=[Player.Ships[0] with{HullHealth=.49}]},()=>Now);
        await runner.RunAsync(Target,driver);Check.Equal(NavigationRunState.Completed,runner.State);Check.That(driver.Calls.Contains("select"));
    }
    [Test]public async Task ActualThreatDuringNativeLandingIsTypedAndReleasesInputsWithoutAnotherApply()
    {
        var current=Player;var driver=new Fake{OnPrepare=()=>current=current with{Navigation=current.Navigation with{ExternalThreat=true}}};
        var runner=new SystemNavigationRunner(()=>current,()=>Now);await runner.RunAsync(Target,driver);
        Check.Equal(NavigationRunState.Failed,runner.State);Check.That(runner.Failure is FlightThreatPauseException);
        Check.That(!driver.Calls.Contains("apply"));Check.Equal("release",driver.Calls[^1]);
    }
    private static SystemFlightFrame AwaitingRequest(int sequence,DateTimeOffset at)=>Docked with{Sequence=sequence,ObservedAt=at,Mode=LocalFlightMode.Normal,
        TargetPosition=new(0,0,5000),TargetDistance=5000,Assists=new(false,true,false),DockingAssist=NativeAssistState.Ready,Clearance=null};
    [Test]public async Task ThreeUnexecutedDockingRequestsTriggerBoundedSessionRecovery()
    {
        var at=Now;var requests=0;
        var driver=new Fake{Frame=n=>{at=Now.AddSeconds(n*11);return AwaitingRequest(n,at);},
            OnDecision=d=>{if(d.Actions.HasFlag(SystemNavigationAction.RequestDocking)){requests++;throw new DockingRequestNotAppliedException();}}};
        var runner=new SystemNavigationRunner(()=>Player,()=>at);await runner.RunAsync(Target,driver);
        Check.Equal(3,requests);Check.Equal(NavigationRunState.Failed,runner.State);Check.That(runner.HandoffFailed);
        Check.That(runner.Failure is DockingHandoffException);Check.Equal("release",driver.Calls[^1]);
    }
    [Test]public async Task AcknowledgedRequestsAndRealQueuesDoNotBecomeUnexecutedRequestFailures()
    {
        var at=Now;var requests=0;
        var driver=new Fake{Frame=n=>{at=Now.AddSeconds(n*11);return n<25?AwaitingRequest(n,at):Docked with{Sequence=n,ObservedAt=at};},
            OnDecision=d=>{if(d.Actions.HasFlag(SystemNavigationAction.RequestDocking))requests++;}};
        var runner=new SystemNavigationRunner(()=>Player,()=>at);await runner.RunAsync(Target,driver);
        Check.Equal(24,requests);Check.Equal(NavigationRunState.Completed,runner.State);Check.That(!runner.HandoffFailed);
    }
    [Test]public async Task AConfirmedRequestResetsOnlyItsConsecutiveFailureCount()
    {
        var at=Now;var requests=0;
        var driver=new Fake{Frame=n=>{at=Now.AddSeconds(n*11);return n<4?AwaitingRequest(n,at):Docked with{Sequence=n,ObservedAt=at};},
            OnDecision=d=>{if(d.Actions.HasFlag(SystemNavigationAction.RequestDocking)&&++requests!=2)throw new DockingRequestNotAppliedException();}};
        var runner=new SystemNavigationRunner(()=>Player,()=>at);await runner.RunAsync(Target,driver);
        Check.Equal(3,requests);Check.Equal(NavigationRunState.Completed,runner.State);Check.That(!runner.HandoffFailed);
    }
    [Test]public void BernalStationsAreClassifiedWithOtherDockableStarports()
        =>Check.Equal(SystemLocationCategory.Station,XFEEDToolBox.Core.Native.NativeSystemLocationReader.Category(10));
    [Test] public async Task UnsupportedAdapterAndWrongTargetOrBuildCannotSendFlightInput()
    {
        foreach(var driver in new[]{new Fake{Ready=false},new Fake{Frame=_=>Docked with{SelectedTarget=Target with{MarketId=99}}},new Fake{Frame=_=>Docked with{GameSha256=new('b',64)}}})
        {
            var runner=new SystemNavigationRunner(()=>Player,()=>Now);await runner.RunAsync(Target,driver);
            Check.That(runner.State is NavigationRunState.Stopped or NavigationRunState.Failed);Check.That(!driver.Calls.Contains("apply"));Check.Equal("release",driver.Calls[^1]);
        }
    }
    [Test] public async Task TransientTransportFailureRetriesWithoutReplacingTheRequestedTarget()
    {
        var driver=new Fake{Frame=n=>n==1?throw new IOException("Transient fixture"):Docked};
        var runner=new SystemNavigationRunner(()=>Player,()=>Now);await runner.RunAsync(Target,driver);
        Check.Equal(NavigationRunState.Completed,runner.State);Check.Equal(1,driver.Calls.Count(x=>x=="select"));Check.Equal(2,driver.Calls.Count(x=>x=="read"));
    }
    [Test] public async Task CancellationAfterPreparationSendsNoFlightInputAndReleases()
    {
        using var stop=new CancellationTokenSource();var driver=new Fake{OnPrepare=stop.Cancel};var runner=new SystemNavigationRunner(()=>Player,()=>Now);
        await runner.RunAsync(Target,driver,stop.Token);Check.Equal(NavigationRunState.Stopped,runner.State);Check.That(!driver.Calls.Contains("apply"));Check.Equal("release",driver.Calls[^1]);
    }
    [Test] public async Task FreshGeometryCannotResetRecoveryWhileEveryControlRequestIsRefused()
    {
        var at=Now;
        var driver=new Fake{Frame=n=>{at=Now.AddSeconds(n*20);return Docked with{Sequence=n,ObservedAt=at,Mode=LocalFlightMode.Normal,TargetPosition=new(0,0,7000),TargetDistance=7000};},
            OnApply=()=>throw new FlightTelemetryUnavailableException("Health fixture is temporarily unavailable")};
        var runner=new SystemNavigationRunner(()=>Player,()=>at);await runner.RunAsync(Target,driver);
        Check.Equal(NavigationRunState.Failed,runner.State);Check.Equal(3,driver.Calls.Count(x=>x=="apply"));Check.Equal("release",driver.Calls[^1]);
    }
    [Test] public async Task ConfirmedOriginalSceneTransitionCanReplaceOnlyItsPreviousEntity()
    {
        var player=Player with{Navigation=Player.Navigation with{SupercruiseEnteredAt=Now}};
        var driver=new Fake{Frame=n=>n==1?Docked with{Mode=LocalFlightMode.Normal,TargetDistance=1000,TargetPosition=new(0,0,1000)}
            :Docked with{Sequence=2,ShipEntity=18,ReplacesShipEntity=17,SceneTransitionAt=Now}};
        var runner=new SystemNavigationRunner(()=>player,()=>Now);await runner.RunAsync(Target,driver);
        Check.Equal(NavigationRunState.Completed,runner.State);Check.Equal(2,driver.Calls.Count(x=>x=="apply"));
    }
    [Test] public async Task SceneTransitionCannotHideWrongEntityOldEventOrChangedLoadout()
    {
        var player=Player with{Navigation=Player.Navigation with{SupercruiseEnteredAt=Now}};
        foreach(var replacement in new[]{Docked with{Sequence=2,ShipEntity=18,ReplacesShipEntity=16,SceneTransitionAt=Now},
            Docked with{Sequence=2,ShipEntity=18,ReplacesShipEntity=17,SceneTransitionAt=Now.AddMinutes(-1)},
            Docked with{Sequence=2,ShipEntity=18,ReplacesShipEntity=17,SceneTransitionAt=Now,LoadoutFingerprint="changed"}})
        {
            var driver=new Fake{Frame=n=>n==1?Docked with{Mode=LocalFlightMode.Normal,TargetDistance=1000,TargetPosition=new(0,0,1000)}:replacement};
            var runner=new SystemNavigationRunner(()=>player,()=>Now);await runner.RunAsync(Target,driver);
            Check.Equal(NavigationRunState.Failed,runner.State);Check.Equal(1,driver.Calls.Count(x=>x=="apply"));Check.Equal("release",driver.Calls[^1]);
        }
    }
    [Test]public async Task SceneTransitionUsesTheVerifiedGameClockWithoutAcceptingAnUnexplainedFutureEvent()
    {
        foreach(var verified in new[]{false,true})
        {
            var transition=Now.AddSeconds(6.464);var player=Player with{Navigation=Player.Navigation with{SupercruiseEnteredAt=transition},
                Status=new(16777232,0,32,1,0,null,null,null,null,[]),StatusAt=transition,StatusReadAt=Now,StatusWrittenAt=verified?Now:null};
            var driver=new Fake{Frame=n=>n==1?Docked with{Mode=LocalFlightMode.Normal,TargetDistance=1000,TargetPosition=new(0,0,1000)}
                :Docked with{Sequence=2,ShipEntity=18,ReplacesShipEntity=17,SceneTransitionAt=transition}};
            var runner=new SystemNavigationRunner(()=>player,()=>Now);await runner.RunAsync(Target,driver);
            Check.Equal(verified?NavigationRunState.Completed:NavigationRunState.Failed,runner.State);
        }
    }
}