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<IMyInventory> Inventory(double volume, double capacity, double mass, List<MyInventoryItem> items)
{
return new Stub<IMyInventory>().Set("CurrentMass", (MyFixedPoint)mass)
.Set("CurrentVolume", (MyFixedPoint)volume).Set("MaxVolume", (MyFixedPoint)capacity)
.Method("get_ItemCount", a => items.Count)
.Method("GetItems", a => { ((List<MyInventoryItem>)a[0]).AddRange(items); return null; });
}
static IMyTerminalBlock Block<T>(IMyCubeGrid grid, Stub<IMyInventory> inventory) where T : class, IMyTerminalBlock
{
return new Stub<T>().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<MyInventoryItem> CargoItems = new List<MyInventoryItem>();
public readonly Stub<IMyInventory> 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<IMyCargoContainer>(Program.Me.CubeGrid, Cargo));
Resources = new P.Resources(Program, Hardware);
}
public Stub<IMyInventory> Add<T>(double volume, double capacity, double mass, params MyInventoryItem[] items) where T : class, IMyTerminalBlock
{
var inventory = Inventory(volume, capacity, mass, new List<MyInventoryItem>(items));
Hardware.InventoryBlocks.Add(Block<T>(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<IMyReactor>(100, 100, 200, Item("Ingot", "Uranium", 200));
r.Add<IMyGasGenerator>(100, 100, 300, Item("Ore", "Ice", 300));
r.Add<IMyRefinery>(100, 100, 400, Item("Ore", "Iron", 400));
r.Add<IMyShipWelder>(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<IMyGasGenerator>(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<MyInventoryItem> { Item("Ore", "Stone", 20) };
var port = new List<MyInventoryItem> { Item("Ore", "Uranium", 10) };
r.Hardware.InventoryBlocks.Add(Block<IMyShipDrill>(r.Program.Me.CubeGrid, Inventory(1, 100, 20, drill)));
r.Hardware.InventoryBlocks.Add(Block<IMyShipConnector>(r.Program.Me.CubeGrid, Inventory(1, 100, 10, port)));
r.Add<IMyGasGenerator>(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<IMyCubeGrid>().Set("EntityId", 10L).Set("Min", Vector3I.Zero).Set("Max", Vector3I.Zero);
var body = new Stub<IMySlimBlock>().Set("CubeGrid", grid.Value).Set("Position", Vector3I.Zero).Set("Mass", 1000f);
grid.Method("GetCubeBlock", a => body.Value);
TestRig.SetBase(r.Program, "Me", new Stub<IMyProgrammableBlock>().Set("CubeGrid", grid.Value).Value);
TestRig.SetBase(r.Program, "World", new Stub<IMyGridProgramWorldInfo>().Set("InventoryMultiplier", multiplier).Value);
r.Hardware.Connector = TestRig.Connector(r.Program, true).Value;
((List<IMyShipConnector>)typeof(P.ShipHardware).GetField("connectors", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(r.Hardware)).Add(r.Hardware.Connector);
r.Hardware.InventoryBlocks.Clear();
r.Add<IMyCargoContainer>(0, 100, 100);
r.Add<IMyReactor>(100, 100, 200, Item("Ingot", "Uranium", 200));
r.Add<IMyGasGenerator>(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<IMyReactor>(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<MyInventoryItem> { Item("Ore", "Ice", 500) };
r.Hardware.InventoryBlocks.Add(Block<IMyGasGenerator>(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<IMyCubeGrid>().Set("EntityId", 1000L).Value;
var blocks = new List<IMyTerminalBlock>(); int cargoTransfers = 0, supplyReads = 0, supplyTransfers = 0;
var destination = Inventory(0, 1000, 0, new List<MyInventoryItem>());
((List<IMyTerminalBlock>)typeof(P.FleetController).GetField("cargo", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(r.Fleet))
.Add(Block<IMyCargoContainer>(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<MyInventoryItem> { Item("Ore", "Ice", 10) };
var inventory = Inventory(1, 100, 10, contents).Method("CanTransferItemTo", a => true)
.Method("GetItems", a => { if (!transport) supplyReads++; ((List<MyInventoryItem>)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<long, List<IMyTerminalBlock>>)typeof(P.FleetController).GetField("connectedBlocks", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(r.Fleet))[1000] = blocks;
else
TestRig.SetBase(r.Program, "GridTerminalSystem", new Stub<IMyGridTerminalSystem>().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);
}
}
}
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<IMyInventory> Inventory(double volume, double capacity, double mass, List<MyInventoryItem> items)
{
return new Stub<IMyInventory>().Set("CurrentMass", (MyFixedPoint)mass)
.Set("CurrentVolume", (MyFixedPoint)volume).Set("MaxVolume", (MyFixedPoint)capacity)
.Method("get_ItemCount", a => items.Count)
.Method("GetItems", a => { ((List<MyInventoryItem>)a[0]).AddRange(items); return null; });
}
static IMyTerminalBlock Block<T>(IMyCubeGrid grid, Stub<IMyInventory> inventory) where T : class, IMyTerminalBlock
{
return new Stub<T>().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<MyInventoryItem> CargoItems = new List<MyInventoryItem>();
public readonly Stub<IMyInventory> 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<IMyCargoContainer>(Program.Me.CubeGrid, Cargo));
Resources = new P.Resources(Program, Hardware);
}
public Stub<IMyInventory> Add<T>(double volume, double capacity, double mass, params MyInventoryItem[] items) where T : class, IMyTerminalBlock
{
var inventory = Inventory(volume, capacity, mass, new List<MyInventoryItem>(items));
Hardware.InventoryBlocks.Add(Block<T>(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<IMyReactor>(100, 100, 200, Item("Ingot", "Uranium", 200));
r.Add<IMyGasGenerator>(100, 100, 300, Item("Ore", "Ice", 300));
r.Add<IMyRefinery>(100, 100, 400, Item("Ore", "Iron", 400));
r.Add<IMyShipWelder>(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<IMyGasGenerator>(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<MyInventoryItem> { Item("Ore", "Stone", 20) };
var port = new List<MyInventoryItem> { Item("Ore", "Uranium", 10) };
r.Hardware.InventoryBlocks.Add(Block<IMyShipDrill>(r.Program.Me.CubeGrid, Inventory(1, 100, 20, drill)));
r.Hardware.InventoryBlocks.Add(Block<IMyShipConnector>(r.Program.Me.CubeGrid, Inventory(1, 100, 10, port)));
r.Add<IMyGasGenerator>(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<IMyCubeGrid>().Set("EntityId", 10L).Set("Min", Vector3I.Zero).Set("Max", Vector3I.Zero);
var body = new Stub<IMySlimBlock>().Set("CubeGrid", grid.Value).Set("Position", Vector3I.Zero).Set("Mass", 1000f);
grid.Method("GetCubeBlock", a => body.Value);
TestRig.SetBase(r.Program, "Me", new Stub<IMyProgrammableBlock>().Set("CubeGrid", grid.Value).Value);
TestRig.SetBase(r.Program, "World", new Stub<IMyGridProgramWorldInfo>().Set("InventoryMultiplier", multiplier).Value);
r.Hardware.Connector = TestRig.Connector(r.Program, true).Value;
((List<IMyShipConnector>)typeof(P.ShipHardware).GetField("connectors", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(r.Hardware)).Add(r.Hardware.Connector);
r.Hardware.InventoryBlocks.Clear();
r.Add<IMyCargoContainer>(0, 100, 100);
r.Add<IMyReactor>(100, 100, 200, Item("Ingot", "Uranium", 200));
r.Add<IMyGasGenerator>(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<IMyReactor>(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<MyInventoryItem> { Item("Ore", "Ice", 500) };
r.Hardware.InventoryBlocks.Add(Block<IMyGasGenerator>(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<IMyCubeGrid>().Set("EntityId", 1000L).Value;
var blocks = new List<IMyTerminalBlock>(); int cargoTransfers = 0, supplyReads = 0, supplyTransfers = 0;
var destination = Inventory(0, 1000, 0, new List<MyInventoryItem>());
((List<IMyTerminalBlock>)typeof(P.FleetController).GetField("cargo", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(r.Fleet))
.Add(Block<IMyCargoContainer>(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<MyInventoryItem> { Item("Ore", "Ice", 10) };
var inventory = Inventory(1, 100, 10, contents).Method("CanTransferItemTo", a => true)
.Method("GetItems", a => { if (!transport) supplyReads++; ((List<MyInventoryItem>)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<long, List<IMyTerminalBlock>>)typeof(P.FleetController).GetField("connectedBlocks", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(r.Fleet))[1000] = blocks;
else
TestRig.SetBase(r.Program, "GridTerminalSystem", new Stub<IMyGridTerminalSystem>().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);
}
}
}