using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using Sandbox.ModAPI.Ingame; using SpaceEngineers.Game.ModAPI.Ingame; using VRage; using VRage.Game.ModAPI.Ingame; using VRageMath; using Xunit; using P = AutoMiningScript.Program; namespace AutoMiningScript.Tests { public class CargoInventoryTests { static MyInventoryItem Item(string kind, string subtype, double amount) { object item = default(MyInventoryItem); var type = kind == "Ore" ? MyItemType.MakeOre(subtype) : kind == "Ingot" ? MyItemType.MakeIngot(subtype) : MyItemType.MakeComponent(subtype); typeof(MyInventoryItem).GetField("Type").SetValue(item, type); typeof(MyInventoryItem).GetField("Amount").SetValue(item, (MyFixedPoint)amount); return (MyInventoryItem)item; } static Stub Inventory(double volume, double capacity, double mass, List items) { return new Stub().Set("CurrentMass", (MyFixedPoint)mass) .Set("CurrentVolume", (MyFixedPoint)volume).Set("MaxVolume", (MyFixedPoint)capacity) .Method("get_ItemCount", a => items.Count) .Method("GetItems", a => { ((List)a[0]).AddRange(items); return null; }); } static IMyTerminalBlock Block(IMyCubeGrid grid, Stub inventory) where T : class, IMyTerminalBlock { return new Stub().Set("CubeGrid", grid).Set("HasInventory", true).Set("InventoryCount", 1) .Method("GetInventory", a => inventory.Value).Value; } sealed class Rig { public readonly P Program; public readonly P.ShipHardware Hardware; public readonly P.Resources Resources; public readonly List CargoItems = new List(); public readonly Stub Cargo; public Rig(string config = "") { Program = TestRig.Program(config); Hardware = new P.ShipHardware(Program); Hardware.Connector = TestRig.Connector(Program, true).Value; Hardware.Batteries.Add(TestRig.Battery(Program, 1, 1).Value); Cargo = Inventory(0, 100, 0, CargoItems); Hardware.InventoryBlocks.Add(Block(Program.Me.CubeGrid, Cargo)); Resources = new P.Resources(Program, Hardware); } public Stub Add(double volume, double capacity, double mass, params MyInventoryItem[] items) where T : class, IMyTerminalBlock { var inventory = Inventory(volume, capacity, mass, new List(items)); Hardware.InventoryBlocks.Add(Block(Program.Me.CubeGrid, inventory)); return inventory; } public void Scan(P.FlightState state = P.FlightState.Servicing) { Program.Now += .5; Resources.Update(.5, state, 0, 0); } } [Theory] [InlineData(0)] [InlineData(.05)] public void FunctionalFuelAndProductionSuppliesDoNotRequireUnloadingOrBlockDeparture(double departureFill) { var r = new Rig("[Cargo]\nDepartureFill=" + departureFill.ToString(System.Globalization.CultureInfo.InvariantCulture)); r.Add(100, 100, 200, Item("Ingot", "Uranium", 200)); r.Add(100, 100, 300, Item("Ore", "Ice", 300)); r.Add(100, 100, 400, Item("Ore", "Iron", 400)); r.Add(100, 100, 100, Item("Component", "SteelPlate", 100)); r.Scan(); Assert.False(r.Resources.Empty); Assert.False(r.Resources.DepartureReady); r.Scan(); r.Resources.Service(.5); Assert.Equal(0, r.Resources.Cargo); Assert.Equal(0, r.Resources.OreAmount); Assert.Empty(r.Resources.Ores); Assert.True(r.Resources.Empty); Assert.Equal(0, r.Resources.ServiceFlags); Assert.True(r.Resources.DepartureReady); string reason; Assert.True(r.Resources.ReadyForSortie(10, 1, out reason)); } [Fact] public void FunctionalInventoryCapacityCannotHideFullTransportCargo() { var r = new Rig(); r.Cargo.Set("CurrentVolume", (MyFixedPoint)98); r.Add(0, 10000, 0); r.Scan(); r.Scan(); Assert.Equal(.98, r.Resources.Cargo, 8); Assert.False(r.Resources.DepartureReady); string reason; Assert.True(r.Resources.MustReturn(out reason)); Assert.Equal(P.L.MinerCargoFull, reason); } [Fact] public void TransportedIceStoneAndUraniumRemainUnloadableInAllThreeCargoBlockTypes() { var r = new Rig(); r.CargoItems.Add(Item("Ore", "Ice", 30)); var drill = new List { Item("Ore", "Stone", 20) }; var port = new List { Item("Ore", "Uranium", 10) }; r.Hardware.InventoryBlocks.Add(Block(r.Program.Me.CubeGrid, Inventory(1, 100, 20, drill))); r.Hardware.InventoryBlocks.Add(Block(r.Program.Me.CubeGrid, Inventory(1, 100, 10, port))); r.Add(100, 100, 500, Item("Ore", "Ice", 500)); r.Scan(); Assert.Equal(60, r.Resources.OreAmount); Assert.Equal(30, r.Resources.Ores["Ice"]); Assert.Equal(1, r.Resources.ServiceFlags); Assert.False(r.Resources.Empty); r.CargoItems.Clear(); drill.Clear(); port.Clear(); r.Scan(); r.Scan(); Assert.Equal(0, r.Resources.OreAmount); Assert.Equal(0, r.Resources.ServiceFlags); Assert.True(r.Resources.Empty); } [Theory] [InlineData(1)] [InlineData(10)] public void DepartureMassStillIncludesReactorFuelAndGeneratorIce(float multiplier) { var r = new Rig(); var grid = new Stub().Set("EntityId", 10L).Set("Min", Vector3I.Zero).Set("Max", Vector3I.Zero); var body = new Stub().Set("CubeGrid", grid.Value).Set("Position", Vector3I.Zero).Set("Mass", 1000f); grid.Method("GetCubeBlock", a => body.Value); TestRig.SetBase(r.Program, "Me", new Stub().Set("CubeGrid", grid.Value).Value); TestRig.SetBase(r.Program, "World", new Stub().Set("InventoryMultiplier", multiplier).Value); r.Hardware.Connector = TestRig.Connector(r.Program, true).Value; ((List)typeof(P.ShipHardware).GetField("connectors", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(r.Hardware)).Add(r.Hardware.Connector); r.Hardware.InventoryBlocks.Clear(); r.Add(0, 100, 100); r.Add(100, 100, 200, Item("Ingot", "Uranium", 200)); r.Add(100, 100, 300, Item("Ore", "Ice", 300)); r.Scan(); r.Hardware.UpdateDepartureMass(); Assert.Equal(1000 + 600 / multiplier, r.Hardware.AutoDepartureMass, 6); Assert.Equal(0, r.Resources.Cargo); Assert.Equal(0, r.Resources.OreAmount); } [Fact] public void InvalidFunctionalInventoryMassStillRevokesDeparturePermission() { var r = new Rig(); var fuel = r.Add(100, 100, 200, Item("Ingot", "Uranium", 200)); r.Scan(); r.Scan(); Assert.True(r.Resources.DepartureReady); fuel.Set("CurrentMass", (MyFixedPoint)(-1)); r.Scan(); Assert.False(r.Resources.DepartureReady); Assert.Equal(-1, r.Resources.ServiceFlags); } [Fact] public void ConsumingFunctionalSuppliesDoesNotCorruptTheMinedOreSample() { var r = new Rig(); r.Hardware.Connector = TestRig.Connector(r.Program, false).Value; r.Hardware.SamplingIsolated = true; r.CargoItems.Add(Item("Ore", "Iron", 10)); var ice = new List { Item("Ore", "Ice", 500) }; r.Hardware.InventoryBlocks.Add(Block(r.Program.Me.CubeGrid, Inventory(100, 100, 500, ice))); r.Scan(P.FlightState.Drilling); r.Resources.ResetSampleValidation(); ice.Clear(); ice.Add(Item("Ore", "Ice", 400)); r.Scan(P.FlightState.Drilling); Assert.True(r.Resources.SamplingValid); Assert.Equal(10, r.Resources.OreAmount); Assert.False(r.Resources.Ores.ContainsKey("Ice")); } [Theory] [InlineData(false)] [InlineData(true)] public void FleetUnloadsTransportOreWithoutReadingOrRemovingFunctionalSupplies(bool cached) { var r = new FleetStateTests.Rig(1, true); r.RegisterAll(); var minerGrid = new Stub().Set("EntityId", 1000L).Value; var blocks = new List(); int cargoTransfers = 0, supplyReads = 0, supplyTransfers = 0; var destination = Inventory(0, 1000, 0, new List()); ((List)typeof(P.FleetController).GetField("cargo", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(r.Fleet)) .Add(Block(r.Program.Me.CubeGrid, destination)); foreach (var type in new[] { typeof(IMyCargoContainer), typeof(IMyShipDrill), typeof(IMyShipConnector), typeof(IMyReactor), typeof(IMyGasGenerator), typeof(IMyRefinery) }) { bool transport = type == typeof(IMyCargoContainer) || type == typeof(IMyShipDrill) || type == typeof(IMyShipConnector); var contents = new List { Item("Ore", "Ice", 10) }; var inventory = Inventory(1, 100, 10, contents).Method("CanTransferItemTo", a => true) .Method("GetItems", a => { if (!transport) supplyReads++; ((List)a[0]).AddRange(contents); return null; }) .Method("TransferItemTo", a => { if (transport) cargoTransfers++; else supplyTransfers++; return true; }); blocks.Add((IMyTerminalBlock)typeof(CargoInventoryTests).GetMethod("Block", BindingFlags.NonPublic | BindingFlags.Static) .MakeGenericMethod(type).Invoke(null, new object[] { minerGrid, inventory })); } if (cached) ((Dictionary>)typeof(P.FleetController).GetField("connectedBlocks", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(r.Fleet))[1000] = blocks; else TestRig.SetBase(r.Program, "GridTerminalSystem", new Stub().Method("GetBlocksOfType", a => { var list = (IList)a[0]; var filter = a[1] as Delegate; foreach (var block in blocks) if (filter == null || (bool)filter.DynamicInvoke(block)) list.Add(block); return null; }).Value); typeof(P.FleetController).GetMethod("UnloadOne", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(r.Fleet, new object[0]); Assert.Equal(3, cargoTransfers); Assert.Equal(0, supplyReads); Assert.Equal(0, supplyTransfers); } } }