using XFEEDToolBox.Core.Engineering;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Outfitting;
using XFEEDToolBox.Core.Presentation;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public class InterfaceRefinementTests
{
private static NavigationLegPreview Leg(int number)=>new(number,number,$"Star {number}","K",20,true,false,number==1,null);
[Test] public void RouteHistorySurvivesSuffixUpdatesAndArrivalClearing()
{
var route=new NavigationItinerary();route.UpdateRoute([Leg(1),Leg(2),Leg(3)]);route.ConfirmPosition(1);
route.UpdateRoute([Leg(2) with{Number=1},Leg(3) with{Number=2}]);
var rows=route.Present(NavigationRunState.Running,"正在充能","Star 2");
Check.Equal(3,rows.Count);Check.Equal(NavigationLegState.Completed,rows[0].Status);
Check.Equal(NavigationLegState.Running,rows[1].Status);Check.Equal("正在充能",rows[1].Message);Check.Equal(2,rows[1].Number);
Check.Equal(NavigationLegState.Waiting,rows[2].Status);Check.Equal("",rows[2].Message);
route.ConfirmPosition(3);route.UpdateRoute([]);Check.That(route.Present(NavigationRunState.Completed).All(r=>r.Status==NavigationLegState.Completed));
}
[Test] public void FailureAndCancellationOnlyMarkThePendingHop()
{
var route=new NavigationItinerary();route.UpdateRoute([Leg(1),Leg(2),Leg(3)]);route.ConfirmPosition(1);
foreach(var (state,expected) in new[]{(NavigationRunState.Failed,NavigationLegState.Error),(NavigationRunState.Stopped,NavigationLegState.Warning)})
{
var rows=route.Present(state,"测试状态");Check.Equal(NavigationLegState.Completed,rows[0].Status);Check.Equal(expected,rows[1].Status);Check.Equal(NavigationLegState.Waiting,rows[2].Status);
}
route.Reset();Check.Equal(0,route.Present(NavigationRunState.Idle).Count);
}
[Test] public void ChangedGameRouteReplacesTheOldSequence()
{
var route=new NavigationItinerary();route.UpdateRoute([Leg(1),Leg(2),Leg(3)]);
route.UpdateRoute([Leg(5),Leg(6)]);var rows=route.Present(NavigationRunState.Idle);
Check.That(rows.Select(r=>r.SystemAddress).SequenceEqual(new long[]{5,6}));Check.That(rows.Select(r=>r.Number).SequenceEqual(new[]{1,2}));
}
[Test] public void LiveRefitUpdatesDoNotReplaceTheSelectedSlotOrPropertyRows()
{
var module=new FittedModule{Slot="LargeHardpoint1",Item="hpt_multicannon_gimbal_large",Health=1,Engineering=new(){Level=3,Quality=.65}};
var slot=new RefitSlot{Slot=module.Slot,Module=module,Category="hardpoint",Capacity=3};
var slots=new LiveCollection<RefitSlotLiveRow,RefitSlot,string>(s=>s.Slot,s=>new(s));slots.Apply([slot]);var selected=slots.Single();
var events=new List<string>();selected.PropertyChanged+=(_,e)=>events.Add(e.PropertyName!);
var delta=slots.Apply([slot with{Module=module with{Health=.8,Engineering=module.Engineering with{Level=4,Quality=.2}}}]);
Check.That(ReferenceEquals(selected,slots.Single()));Check.Equal(0,delta.Added+delta.Removed+delta.Moved);Check.Equal("G4",selected.CurrentGrade);Check.That(!events.Contains(nameof(RefitSlotLiveRow.IconItem)));
var properties=new LiveCollection<RefitPropertyLiveRow,RefitPropertyRow,string>(p=>p.OriginalName,p=>new(p));
properties.Apply([new("完整度","Integrity","100%")]);var health=properties.Single();
var change=properties.Apply([new("完整度","Integrity","80%")]);Check.That(ReferenceEquals(health,properties.Single()));Check.Equal("80%",health.Value);Check.Equal(0,change.Added+change.Removed);
}
[Test] public void ModuleBrowserOnlyOffersCompatibleTypesThenTheirModels()
{
var catalog=OutfittingCatalog.Default;var doc=ShipRefit.Create(new(){ShipId=1,Type="anaconda",ConfigurationAt=DateTimeOffset.UtcNow,Modules=[]},catalog);
var slots=new[]{doc.Slots.Single(s=>s.Slot=="SmallHardpoint1"),doc.Slots.Single(s=>s.Slot=="Slot01_Size7"),doc.Slots.Single(s=>s.Slot=="Military01")};
var choices=ShipRefit.Choices(new(),catalog);
foreach(var slot in slots)
{
var compatible=RefitModuleBrowser.Compatible(doc,slot,choices,catalog);var types=RefitModuleBrowser.Types(compatible);
Check.That(compatible.Count>0&&types.Count>0&&compatible.All(c=>ShipRefit.Fits(doc,slot,c.Definition,catalog)));
Check.Equal(types.Count,types.Select(t=>t.Key).Distinct().Count());
foreach(var type in types)Check.That(compatible.Any(c=>XFEEDToolBox.Core.Outfitting.RefitModuleBrowser.TypeKey(c.Definition)==type.Key)&&!type.Name.Contains("1A "));
}
}
[Test] public void CancelledEngineeringCalculationDoesNotReturnAMaterialBudget()
{
using var stop=new CancellationTokenSource();stop.Cancel();
Check.Throws<OperationCanceledException>(()=>EngineeringPlanner.Calculate(EngineeringCatalog.Default,new(){Item="hpt_multicannon_gimbal_large"},"Overcharged Weapon",5,1,null,new(){Rank=5,Progress="Unlocked"},[],token:stop.Token));
}
}
using XFEEDToolBox.Core.Engineering;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Navigation;
using XFEEDToolBox.Core.Outfitting;
using XFEEDToolBox.Core.Presentation;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public class InterfaceRefinementTests
{
private static NavigationLegPreview Leg(int number)=>new(number,number,$"Star {number}","K",20,true,false,number==1,null);
[Test] public void RouteHistorySurvivesSuffixUpdatesAndArrivalClearing()
{
var route=new NavigationItinerary();route.UpdateRoute([Leg(1),Leg(2),Leg(3)]);route.ConfirmPosition(1);
route.UpdateRoute([Leg(2) with{Number=1},Leg(3) with{Number=2}]);
var rows=route.Present(NavigationRunState.Running,"正在充能","Star 2");
Check.Equal(3,rows.Count);Check.Equal(NavigationLegState.Completed,rows[0].Status);
Check.Equal(NavigationLegState.Running,rows[1].Status);Check.Equal("正在充能",rows[1].Message);Check.Equal(2,rows[1].Number);
Check.Equal(NavigationLegState.Waiting,rows[2].Status);Check.Equal("",rows[2].Message);
route.ConfirmPosition(3);route.UpdateRoute([]);Check.That(route.Present(NavigationRunState.Completed).All(r=>r.Status==NavigationLegState.Completed));
}
[Test] public void FailureAndCancellationOnlyMarkThePendingHop()
{
var route=new NavigationItinerary();route.UpdateRoute([Leg(1),Leg(2),Leg(3)]);route.ConfirmPosition(1);
foreach(var (state,expected) in new[]{(NavigationRunState.Failed,NavigationLegState.Error),(NavigationRunState.Stopped,NavigationLegState.Warning)})
{
var rows=route.Present(state,"测试状态");Check.Equal(NavigationLegState.Completed,rows[0].Status);Check.Equal(expected,rows[1].Status);Check.Equal(NavigationLegState.Waiting,rows[2].Status);
}
route.Reset();Check.Equal(0,route.Present(NavigationRunState.Idle).Count);
}
[Test] public void ChangedGameRouteReplacesTheOldSequence()
{
var route=new NavigationItinerary();route.UpdateRoute([Leg(1),Leg(2),Leg(3)]);
route.UpdateRoute([Leg(5),Leg(6)]);var rows=route.Present(NavigationRunState.Idle);
Check.That(rows.Select(r=>r.SystemAddress).SequenceEqual(new long[]{5,6}));Check.That(rows.Select(r=>r.Number).SequenceEqual(new[]{1,2}));
}
[Test] public void LiveRefitUpdatesDoNotReplaceTheSelectedSlotOrPropertyRows()
{
var module=new FittedModule{Slot="LargeHardpoint1",Item="hpt_multicannon_gimbal_large",Health=1,Engineering=new(){Level=3,Quality=.65}};
var slot=new RefitSlot{Slot=module.Slot,Module=module,Category="hardpoint",Capacity=3};
var slots=new LiveCollection<RefitSlotLiveRow,RefitSlot,string>(s=>s.Slot,s=>new(s));slots.Apply([slot]);var selected=slots.Single();
var events=new List<string>();selected.PropertyChanged+=(_,e)=>events.Add(e.PropertyName!);
var delta=slots.Apply([slot with{Module=module with{Health=.8,Engineering=module.Engineering with{Level=4,Quality=.2}}}]);
Check.That(ReferenceEquals(selected,slots.Single()));Check.Equal(0,delta.Added+delta.Removed+delta.Moved);Check.Equal("G4",selected.CurrentGrade);Check.That(!events.Contains(nameof(RefitSlotLiveRow.IconItem)));
var properties=new LiveCollection<RefitPropertyLiveRow,RefitPropertyRow,string>(p=>p.OriginalName,p=>new(p));
properties.Apply([new("完整度","Integrity","100%")]);var health=properties.Single();
var change=properties.Apply([new("完整度","Integrity","80%")]);Check.That(ReferenceEquals(health,properties.Single()));Check.Equal("80%",health.Value);Check.Equal(0,change.Added+change.Removed);
}
[Test] public void ModuleBrowserOnlyOffersCompatibleTypesThenTheirModels()
{
var catalog=OutfittingCatalog.Default;var doc=ShipRefit.Create(new(){ShipId=1,Type="anaconda",ConfigurationAt=DateTimeOffset.UtcNow,Modules=[]},catalog);
var slots=new[]{doc.Slots.Single(s=>s.Slot=="SmallHardpoint1"),doc.Slots.Single(s=>s.Slot=="Slot01_Size7"),doc.Slots.Single(s=>s.Slot=="Military01")};
var choices=ShipRefit.Choices(new(),catalog);
foreach(var slot in slots)
{
var compatible=RefitModuleBrowser.Compatible(doc,slot,choices,catalog);var types=RefitModuleBrowser.Types(compatible);
Check.That(compatible.Count>0&&types.Count>0&&compatible.All(c=>ShipRefit.Fits(doc,slot,c.Definition,catalog)));
Check.Equal(types.Count,types.Select(t=>t.Key).Distinct().Count());
foreach(var type in types)Check.That(compatible.Any(c=>XFEEDToolBox.Core.Outfitting.RefitModuleBrowser.TypeKey(c.Definition)==type.Key)&&!type.Name.Contains("1A "));
}
}
[Test] public void CancelledEngineeringCalculationDoesNotReturnAMaterialBudget()
{
using var stop=new CancellationTokenSource();stop.Cancel();
Check.Throws<OperationCanceledException>(()=>EngineeringPlanner.Calculate(EngineeringCatalog.Default,new(){Item="hpt_multicannon_gimbal_large"},"Overcharged Weapon",5,1,null,new(){Rank=5,Progress="Unlocked"},[],token:stop.Token));
}
}