using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using Sandbox.ModAPI.Ingame; using VRage.Game.ModAPI.Ingame; using VRage.Game.ModAPI.Ingame.Utilities; using VRageMath; using Xunit; using P=AutoMiningScript.Program; namespace AutoMiningScript.Tests { public class AutomaticHomeTests { const BindingFlags Private=BindingFlags.Instance|BindingFlags.NonPublic; static T Read(object o,string key) {return (T)o.GetType().GetField(key,Private).GetValue(o);} static void Write(object o,string key,object value) {o.GetType().GetField(key,Private).SetValue(o,value);} static object Fill(object[] args,IEnumerable blocks) { var list=(IList)args[0];var type=list.GetType().GetGenericArguments()[0];var filter=args.Length>1?args[1] as Delegate:null; foreach(var block in blocks)if(type.IsInstanceOfType(block)&&(filter==null||(bool)filter.DynamicInvoke(block)))list.Add(block); return null; } sealed class HardwareRig { public readonly FleetStateTests.Rig Base=new FleetStateTests.Rig(0,false); public readonly Dictionary Groups=new Dictionary(); public readonly List Blocks=new List(); public readonly IMyShipConnector Port,ForeignPort; public readonly IMyCargoContainer Cargo; public P.FleetController Fleet; public HardwareRig(string dockGroup,string cargoGroup,string actualDock,string actualCargo) { var p=Base.Program;p.Config=new P.Settings("[Fleet]\nDockGroup="+dockGroup+"\nCargoGroup="+cargoGroup,"fleet","base"); Port=new Stub().Set("CubeGrid",p.Me.CubeGrid).Set("IsFunctional",true).Set("EntityId",100L).Set("WorldMatrix",MatrixD.Identity).Value; ForeignPort=new Stub().Set("CubeGrid",new Stub().Value).Set("EntityId",999L).Value; Cargo=new Stub().Set("CubeGrid",p.Me.CubeGrid).Set("HasInventory",true).Value; var foreignCargo=new Stub().Set("CubeGrid",ForeignPort.CubeGrid).Set("HasInventory",true).Value; Blocks.Add(new Stub().Set("CubeGrid",p.Me.CubeGrid).Set("IsFunctional",true).Set("WorldMatrix",MatrixD.Identity).Value); Blocks.Add(Port);Blocks.Add(Cargo);Blocks.Add(ForeignPort);Blocks.Add(foreignCargo); if(actualDock!=null)Groups[actualDock]=Group(actualDock,new IMyTerminalBlock[]{Port,ForeignPort}); if(actualCargo!=null)Groups[actualCargo]=Group(actualCargo,new IMyTerminalBlock[]{Cargo,foreignCargo}); TestRig.SetBase(p,"GridTerminalSystem",new Stub() .Method("GetBlockGroupWithName",a=>{IMyBlockGroup group;return Groups.TryGetValue((string)a[0],out group)?group:null;}) .Method("GetBlocksOfType",a=>Fill(a,Blocks)).Value); Fleet=new P.FleetController(p); } public IMyBlockGroup Group(string name,IMyTerminalBlock[] blocks) {return new Stub().Set("Name",name).Method("GetBlocksOfType",a=>Fill(a,blocks)).Method("GetBlocks",a=>Fill(a,blocks)).Value;} } [Theory] [InlineData(false)] [InlineData(true)] public void DefaultLocalizedAndOriginalGroupsResolveWithoutIncludingDockedMinerHardware(bool reverse) { var d=P.Settings.DefaultName("AMS Docks");var c=P.Settings.DefaultName("AMS Cargo"); var r=new HardwareRig(reverse?"AMS Docks":d,reverse?"AMS Cargo":c,reverse?d:"AMS Docks",reverse?c:"AMS Cargo"); Assert.Same(r.Port,Assert.Single(Read>(r.Fleet,"docks"))); Assert.Same(r.Cargo,Assert.Single(Read>(r.Fleet,"cargo"))); Assert.DoesNotContain(P.L.FleetCargoMissing,r.Fleet.Diagnostics);Assert.DoesNotContain(P.L.FleetBerthsMissing,r.Fleet.Diagnostics); } [Fact] public void CustomGroupNamesAreNotSilentlyReplacedByUnrelatedDefaults() { var r=new HardwareRig("Reserved Mining Ports","Selected Cargo","AMS Docks","AMS Cargo"); Assert.Empty(Read>(r.Fleet,"docks"));Assert.Empty(Read>(r.Fleet,"cargo")); Assert.Contains("Reserved Mining Ports",r.Fleet.Diagnostics);Assert.Contains("Selected Cargo",r.Fleet.Diagnostics); r.Groups["Reserved Mining Ports"]=r.Group("Reserved Mining Ports",new IMyTerminalBlock[]{r.Port}); r.Groups["Selected Cargo"]=r.Group("Selected Cargo",new IMyTerminalBlock[]{r.Cargo}); r.Fleet.Command("rescan");Assert.Single(Read>(r.Fleet,"docks"));Assert.Single(Read>(r.Fleet,"cargo")); } [Fact] public void AnExistingExplicitGroupTakesPrecedenceOverDefaultAlias() { var name=P.Settings.DefaultName("AMS Docks");if(name=="AMS Docks")return; var r=new HardwareRig(name,P.Settings.DefaultName("AMS Cargo"),"AMS Docks","AMS Cargo"); r.Groups[name]=r.Group(name,new IMyTerminalBlock[]{r.ForeignPort});r.Fleet.Command("rescan"); Assert.Empty(Read>(r.Fleet,"docks")); } [Fact] public void MissingPortConfigurationIsDeliveredToTheWaitingMinerAndRecoversOnRescan() { var r=new HardwareRig("Selected Ports","Selected Cargo",null,null);var miner=new MinerStateTests.MinerRig();miner.State(P.FlightState.Returning); for(int i=0;i<12;i++){r.Base.Program.Now+=.1;r.Fleet.Tick(.1);} var broadcast=r.Base.Sent.Last(p=>p.Kind=="BASE");miner.Miner.Receive(broadcast);miner.Miner.Tick(.1); Assert.Contains("Selected Ports",Read(miner.Miner,"reason"));Assert.Contains("Selected Ports",miner.Miner.Diagnostics); Assert.Equal(P.FlightState.Returning,miner.StateNow); Assert.All(miner.Hardware.Thrusters,t=>Assert.Equal(0f,t.ThrustOverridePercentage)); Assert.False(Read(miner.Miner,"flight").HasRoute); r.Groups["Selected Ports"]=r.Group("Selected Ports",new IMyTerminalBlock[]{r.Port});r.Fleet.Command("rescan"); for(int i=0;i<12;i++){r.Base.Program.Now+=.1;r.Fleet.Tick(.1);} Assert.Equal("",P.DockFrame.FromIni(r.Base.Sent.Last(p=>p.Kind=="BASE").Body).Problem); } [Fact] public void SixPortLocalizedDeploymentGrantsBothAirborneMinersWhileFleetIsPaused() { var r=new FleetStateTests.Rig(6,true,"[Fleet]\nDockGroup="+P.Settings.DefaultName("AMS Docks")); foreach(int n in new[]{2,3}) {r.SetConnected(n,false);r.Miners[n].State=P.FlightState.Returning;r.Miners[n].Position+=new Vector3D(0,0,-100);} r.RegisterAll();r.Tick(30); foreach(int n in new[]{2,3})r.Receive("DOCK_REQUEST",r.Miners[n]); r.Tick(25);var grants=r.Sent.Where(p=>p.Kind=="DOCK_GRANT").ToList(); Assert.Equal(2,grants.Count);Assert.Equal(2,grants.Select(p=>P.DockFrame.FromIni(p.Body).ConnectorId).Distinct().Count()); } [Fact] public void AConfiguredIdleMinerStartsReturnAndRestoresFlightPowerWithoutAUserCommand() { var r=new MinerStateTests.MinerRig();r.State(P.FlightState.Paused);Write(r.Miner,"running",false); r.Hardware.Batteries[0].ChargeMode=ChargeMode.Recharge; var tank=new Stub().Set("CubeGrid",r.Program.Me.CubeGrid).Set("Stockpile",true);r.Hardware.Tanks.Add(tank.Value); r.Miner.Tick(.1); Assert.Equal(P.FlightState.Returning,r.StateNow);Assert.Contains(r.Sent,p=>p.Kind=="DOCK_REQUEST"); Assert.Equal(ChargeMode.Auto,r.Hardware.Batteries[0].ChargeMode);Assert.False(tank.Value.Stockpile); Assert.False(Read(r.Miner,"calibrated"));Assert.False(Read(r.Miner,"running")); } [Theory] [InlineData("pause")] [InlineData("release")] public void AutomaticReturnNeverOverridesExplicitStopOrManualControl(string command) { var r=new MinerStateTests.MinerRig();r.Miner.Command(command);var state=r.StateNow;r.Sent.Clear();r.Miner.Tick(.1); Assert.Equal(state,r.StateNow);Assert.DoesNotContain(r.Sent,p=>p.Kind=="DOCK_REQUEST"); var saved=new MyIni();r.Miner.Save(saved);var restart=new MinerStateTests.MinerRig();restart.Miner.Load(saved);restart.Miner.Tick(.1); Assert.DoesNotContain(restart.Sent,p=>p.Kind=="DOCK_REQUEST"); } [Theory] [InlineData(P.FlightState.Returning)] [InlineData(P.FlightState.Holding)] [InlineData(P.FlightState.DockAlign)] [InlineData(P.FlightState.DockApproach)] [InlineData(P.FlightState.DockRetreat)] public void ReloadOfReturnNavigationReacquiresBerthWithoutRequiringResume(P.FlightState phase) { var r=new MinerStateTests.MinerRig();r.State(phase);Write(r.Miner,"manualStop",true); var saved=new MyIni();r.Miner.Save(saved);var restart=new MinerStateTests.MinerRig();restart.Miner.Load(saved); Assert.False(Read(restart.Miner,"leaseConfirmed"));Assert.Equal("",Read(restart.Miner,"lane")); restart.Miner.Tick(.1);Assert.Equal(P.FlightState.Returning,restart.StateNow);Assert.Contains(restart.Sent,p=>p.Kind=="DOCK_REQUEST"); } [Fact] public void RecoveryInsideBoreStartsAlongTheSavedExitInsteadOfTurningTowardBase() { var r=new MinerStateTests.MinerRig();r.Task(9);r.State(P.FlightState.Retreat);Write(r.Miner,"insideBore",true); var saved=new MyIni();r.Miner.Save(saved);var restart=new MinerStateTests.MinerRig();restart.Miner.Load(saved); restart.Miner.Tick(.1);Assert.Equal(P.FlightState.Retreat,restart.StateNow); Assert.Equal(Vector3D.Forward,Assert.Single(restart.Miner.GetJobs()).Direction);Assert.False(restart.Drill.Value.Enabled); } } }