using System.Text.Json;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Outfitting;
using XFEEDToolBox.Core.Presentation;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public class EquipmentStatisticsTests
{
private static ShipRefitDocument Actual()=>JsonSerializer.Deserialize<ShipRefitDocument>(
File.ReadAllText(Path.Combine(AppContext.BaseDirectory,"Fixtures","edsy-actual-loadout.json")),ToolboxSettings.JsonOptions)!;
private static void Near(double expected,double actual)=>Check.That(Math.Abs(expected-actual)<.00001,$"Expected {expected}, actual {actual}");
[Test] public void ObservedEngineeringIsUsedBeforeHashQuantizationAndTargetsDoNotReplaceIt()
{
var doc=Actual();
doc=doc with{Slots=doc.Slots.Select(s=>s.Slot=="PowerPlant"?s with{Module=s.Module! with{Engineering=new(){Blueprint="PowerPlant_Boosted",Level=3,Quality=.65,
Modifiers=[new("PowerCapacity",39.123456,36,false)]}}}:s).ToArray()};
var stats=EdsyLinkCodec.Statistics(doc);
Near(39.123456,stats.Value("pwrcap")!.Value);
Check.That(stats.Value("pwrcap")>0&&stats.Value("pwrdraw_dep")>stats.Value("pwrdraw_ret"));
var target=ShipRefit.SetTarget(doc,"PowerPlant",new("Overcharged",5,1,"special_powerplant_cooled",0,false,true));
Near(stats.Value("pwrcap")!.Value,EdsyLinkCodec.Statistics(target).Value("pwrcap")!.Value);
var weapon=doc.Slots.First(s=>s.Category=="hardpoint"&&s.Module is not null);
var data=EquipmentStatusRows.Module(stats.Modules[weapon.Slot]);
Check.That(data.Any(r=>r.OriginalName=="Mass"&&r.Value.EndsWith(" t")));
Check.That(data.Any(r=>r.OriginalName=="Power draw"&&r.Value.EndsWith(" MW")));
Check.That(data.Any(r=>r.OriginalName=="Damage per second"));
}
[Test] public void DisabledWeaponsReduceDeployedLoadWithoutReducingRetractedLoad()
{
var doc=Actual();var before=EdsyLinkCodec.Statistics(doc);
var weapon=doc.Slots.First(s=>s.Category=="hardpoint"&&s.Module is {Enabled:true});
var disabled=doc with{Slots=doc.Slots.Select(s=>s==weapon?s with{Module=s.Module! with{Enabled=false}}:s).ToArray()};
var after=EdsyLinkCodec.Statistics(disabled);
Near(before.Value("pwrdraw_dep")!.Value-before.Modules[weapon.Slot]["pwrdraw"],after.Value("pwrdraw_dep")!.Value);
Near(before.Value("pwrdraw_ret")!.Value,after.Value("pwrdraw_ret")!.Value);
}
[Test] public void IncompleteAndUnsupportedLoadoutsNeverShowInventedPowerValues()
{
var doc=Actual();
Check.Throws<InvalidOperationException>(()=>EdsyLinkCodec.Statistics(doc with{Slots=doc.Slots.Where(s=>s.Slot!="PowerPlant").ToArray()}));
var power=doc.Slots.Single(s=>s.Slot=="PowerPlant");
Check.Throws<FormatException>(()=>EdsyLinkCodec.Statistics(doc with{Slots=doc.Slots.Select(s=>s==power?s with{Module=s.Module! with{Item="int_unknown_fake_module"}}:s).ToArray()}));
Check.That(EquipmentStatusRows.Ship(null,"等待完整装配").All(r=>r.Value.Contains('—')));
}
[Test] public void EditorOverviewMatchesReferenceAndRespondsToFuelCargoAndEnginePips()
{
var stats=EdsyLinkCodec.Statistics(Actual());var fuel=stats.Value("fuelcap")!.Value;
var maximum=ShipOverviewValues.From(stats,new(fuel,0,0,8));
Near(stats.Value("_speed")!.Value,maximum.Speed!.Value);
Near(stats.Value("_boost")!.Value,maximum.Boost!.Value);
Near(stats.Value("_jump_unladen")!.Value,maximum.JumpCurrent!.Value);
Check.That(maximum.Pitch>0&&maximum.Roll>0&&maximum.Yaw>0);
var lowPips=ShipOverviewValues.From(stats,new(fuel,0,0,0));
Check.That(lowPips.Speed<maximum.Speed&&lowPips.Pitch<maximum.Pitch&&lowPips.Roll<=maximum.Roll&&lowPips.Yaw<=maximum.Yaw);
Near(maximum.Boost.Value,lowPips.Boost!.Value);
var loaded=ShipOverviewValues.From(stats,new(fuel,0,stats.Value("cargocap"),8));
Near(stats.Value("_jump_laden")!.Value,loaded.JumpCurrent!.Value);
Check.That(loaded.Speed<=maximum.Speed);
Near(0,ShipOverviewValues.From(stats,new(0,0,0,8)).JumpCurrent!.Value);
Check.That(ShipOverviewValues.From(null).SpeedText.Contains('—')&&ShipOverviewValues.From(null).JumpText.Contains('—'));
}
[Test] public void EditorPowerBarsShowOverflowInsteadOfClippingAtCapacity()
{
var values=new Dictionary<string,double>{{"pwrcap",36},{"pwrdraw_ret",32.02},{"pwrdraw_dep",38.18}};
var overview=ShipOverviewValues.From(new(values,new Dictionary<string,IReadOnlyDictionary<string,double>>()));
Check.That(!overview.RetractedOverloaded&&overview.DeployedOverloaded&&overview.MarginText=="-2.18 MW");
Check.That(overview.DeployedExcessFraction>0&&overview.CapacityFraction<1&&overview.RetractedExcessFraction==0);
Near(1,overview.RetractedNormalFraction+overview.RetractedExcessFraction+overview.RetractedEmptyFraction);
Near(1,overview.DeployedNormalFraction+overview.DeployedExcessFraction+overview.DeployedEmptyFraction);
Near(36/38.18,overview.CapacityFraction);
}
}
using System.Text.Json;
using XFEEDToolBox.Core.Models;
using XFEEDToolBox.Core.Outfitting;
using XFEEDToolBox.Core.Presentation;
using XFEExtension.NetCore.XUnit.Attributes;
namespace XFEEDToolBox.Test;
[TestFixture]
public class EquipmentStatisticsTests
{
private static ShipRefitDocument Actual()=>JsonSerializer.Deserialize<ShipRefitDocument>(
File.ReadAllText(Path.Combine(AppContext.BaseDirectory,"Fixtures","edsy-actual-loadout.json")),ToolboxSettings.JsonOptions)!;
private static void Near(double expected,double actual)=>Check.That(Math.Abs(expected-actual)<.00001,$"Expected {expected}, actual {actual}");
[Test] public void ObservedEngineeringIsUsedBeforeHashQuantizationAndTargetsDoNotReplaceIt()
{
var doc=Actual();
doc=doc with{Slots=doc.Slots.Select(s=>s.Slot=="PowerPlant"?s with{Module=s.Module! with{Engineering=new(){Blueprint="PowerPlant_Boosted",Level=3,Quality=.65,
Modifiers=[new("PowerCapacity",39.123456,36,false)]}}}:s).ToArray()};
var stats=EdsyLinkCodec.Statistics(doc);
Near(39.123456,stats.Value("pwrcap")!.Value);
Check.That(stats.Value("pwrcap")>0&&stats.Value("pwrdraw_dep")>stats.Value("pwrdraw_ret"));
var target=ShipRefit.SetTarget(doc,"PowerPlant",new("Overcharged",5,1,"special_powerplant_cooled",0,false,true));
Near(stats.Value("pwrcap")!.Value,EdsyLinkCodec.Statistics(target).Value("pwrcap")!.Value);
var weapon=doc.Slots.First(s=>s.Category=="hardpoint"&&s.Module is not null);
var data=EquipmentStatusRows.Module(stats.Modules[weapon.Slot]);
Check.That(data.Any(r=>r.OriginalName=="Mass"&&r.Value.EndsWith(" t")));
Check.That(data.Any(r=>r.OriginalName=="Power draw"&&r.Value.EndsWith(" MW")));
Check.That(data.Any(r=>r.OriginalName=="Damage per second"));
}
[Test] public void DisabledWeaponsReduceDeployedLoadWithoutReducingRetractedLoad()
{
var doc=Actual();var before=EdsyLinkCodec.Statistics(doc);
var weapon=doc.Slots.First(s=>s.Category=="hardpoint"&&s.Module is {Enabled:true});
var disabled=doc with{Slots=doc.Slots.Select(s=>s==weapon?s with{Module=s.Module! with{Enabled=false}}:s).ToArray()};
var after=EdsyLinkCodec.Statistics(disabled);
Near(before.Value("pwrdraw_dep")!.Value-before.Modules[weapon.Slot]["pwrdraw"],after.Value("pwrdraw_dep")!.Value);
Near(before.Value("pwrdraw_ret")!.Value,after.Value("pwrdraw_ret")!.Value);
}
[Test] public void IncompleteAndUnsupportedLoadoutsNeverShowInventedPowerValues()
{
var doc=Actual();
Check.Throws<InvalidOperationException>(()=>EdsyLinkCodec.Statistics(doc with{Slots=doc.Slots.Where(s=>s.Slot!="PowerPlant").ToArray()}));
var power=doc.Slots.Single(s=>s.Slot=="PowerPlant");
Check.Throws<FormatException>(()=>EdsyLinkCodec.Statistics(doc with{Slots=doc.Slots.Select(s=>s==power?s with{Module=s.Module! with{Item="int_unknown_fake_module"}}:s).ToArray()}));
Check.That(EquipmentStatusRows.Ship(null,"等待完整装配").All(r=>r.Value.Contains('—')));
}
[Test] public void EditorOverviewMatchesReferenceAndRespondsToFuelCargoAndEnginePips()
{
var stats=EdsyLinkCodec.Statistics(Actual());var fuel=stats.Value("fuelcap")!.Value;
var maximum=ShipOverviewValues.From(stats,new(fuel,0,0,8));
Near(stats.Value("_speed")!.Value,maximum.Speed!.Value);
Near(stats.Value("_boost")!.Value,maximum.Boost!.Value);
Near(stats.Value("_jump_unladen")!.Value,maximum.JumpCurrent!.Value);
Check.That(maximum.Pitch>0&&maximum.Roll>0&&maximum.Yaw>0);
var lowPips=ShipOverviewValues.From(stats,new(fuel,0,0,0));
Check.That(lowPips.Speed<maximum.Speed&&lowPips.Pitch<maximum.Pitch&&lowPips.Roll<=maximum.Roll&&lowPips.Yaw<=maximum.Yaw);
Near(maximum.Boost.Value,lowPips.Boost!.Value);
var loaded=ShipOverviewValues.From(stats,new(fuel,0,stats.Value("cargocap"),8));
Near(stats.Value("_jump_laden")!.Value,loaded.JumpCurrent!.Value);
Check.That(loaded.Speed<=maximum.Speed);
Near(0,ShipOverviewValues.From(stats,new(0,0,0,8)).JumpCurrent!.Value);
Check.That(ShipOverviewValues.From(null).SpeedText.Contains('—')&&ShipOverviewValues.From(null).JumpText.Contains('—'));
}
[Test] public void EditorPowerBarsShowOverflowInsteadOfClippingAtCapacity()
{
var values=new Dictionary<string,double>{{"pwrcap",36},{"pwrdraw_ret",32.02},{"pwrdraw_dep",38.18}};
var overview=ShipOverviewValues.From(new(values,new Dictionary<string,IReadOnlyDictionary<string,double>>()));
Check.That(!overview.RetractedOverloaded&&overview.DeployedOverloaded&&overview.MarginText=="-2.18 MW");
Check.That(overview.DeployedExcessFraction>0&&overview.CapacityFraction<1&&overview.RetractedExcessFraction==0);
Near(1,overview.RetractedNormalFraction+overview.RetractedExcessFraction+overview.RetractedEmptyFraction);
Near(1,overview.DeployedNormalFraction+overview.DeployedExcessFraction+overview.DeployedEmptyFraction);
Near(36/38.18,overview.CapacityFraction);
}
}