using System;
using System.Collections.Generic;
using System.Reflection;
using Sandbox.ModAPI.Ingame;
using VRageMath;
using Xunit;
using P = AutoMiningScript.Program;
namespace AutoMiningScript.Tests
{
public class ReturnStateTests
{
static readonly BindingFlags Private = BindingFlags.Instance | BindingFlags.NonPublic;
static T Read<T>(object instance, string name) { return (T)instance.GetType().GetField(name, Private).GetValue(instance); }
static void Write(object instance, string name, object value) { instance.GetType().GetField(name, Private).SetValue(instance, value); }
static void Call(object instance, string name, params object[] args) { instance.GetType().GetMethod(name, Private).Invoke(instance, args); }
static P.DockFrame Waiting(MinerStateTests.MinerRig r,bool granted=false)
{
r.PrepareDeparture("valid-lease"); r.Connector.Set("Status", MyShipConnectorStatus.Unconnected);
Write(r.Miner, "laneKind", "dock"); if(!granted)Write(r.Miner,"lane",""); Write(r.Miner, "stableSince", -1d); r.State(P.FlightState.Holding);
var dock = Read<P.DockFrame>(r.Miner, "dock"); dock.WaitingLocal = new Vector3D(0, 0, -80); return dock;
}
static void Step(MinerStateTests.MinerRig r, P.DockFrame dock, double seconds = .1)
{ r.Program.Now += seconds; dock.ReceivedAt = r.Program.Now; Call(r.Miner, "Waiting", seconds); }
static MatrixD Desired(MinerStateTests.MinerRig r)
{ return Read<MatrixD>(Read<P.FlightController>(r.Miner, "flight"), "previousDesired"); }
[Fact]
public void WaitingKeepsTravelAndHoldingControlUntilTheGrantAllowsDirectApproach()
{
var r = new MinerStateTests.MinerRig(); var dock = Waiting(r);
r.Position = new Vector3D(100, 0, -80);
MatrixD orientation = MatrixD.Identity; Vector3D velocity = Vector3D.Zero;
r.Controller.Method("get_WorldMatrix", a => orientation).Method("GetShipVelocities", a => new MyShipVelocities(velocity, Vector3D.Zero));
Step(r, dock);
Assert.True(Vector3D.Dot(Desired(r).Forward, Vector3D.Left) > .999);
Assert.False(Read<bool>(r.Miner, "waitingAligning")); Assert.Equal(P.FlightState.Holding, r.StateNow);
r.Position = dock.WaitingLocal + Vector3D.Right;
orientation = MatrixD.CreateWorld(Vector3D.Zero, Vector3D.Left, Vector3D.Up);
velocity = Vector3D.Left * .4;
Step(r, dock); Assert.False(Read<bool>(r.Miner, "waitingAligning"));
velocity = Vector3D.Zero;
Step(r, dock); Assert.True(Read<bool>(r.Miner, "waitingAligning"));
Assert.True(Vector3D.Dot(Desired(r).Forward, Vector3D.Forward) > .999);
Assert.Equal(-1d, Read<double>(r.Miner, "stableSince")); Assert.Equal(P.FlightState.Holding, r.StateNow);
orientation = MatrixD.Identity;
Step(r, dock); Assert.Equal(P.FlightState.Holding,r.StateNow);
Assert.Contains(P.L.MinerDockGrantMissing,Read<string>(r.Miner,"reason"));
Write(r.Miner,"lane","valid-lease");Step(r,dock);
Assert.Equal(P.FlightState.DockAlign, r.StateNow);
Assert.Equal("valid-lease", Read<string>(r.Miner, "lane"));
}
[Fact]
public void WaitingAlignmentLatchAvoidsAlternatingAttitudesButResetsAfterFourMetresDrift()
{
var r = new MinerStateTests.MinerRig(); var dock = Waiting(r); r.Position = dock.WaitingLocal;
Step(r, dock); Assert.True(Read<bool>(r.Miner, "waitingAligning"));
r.Position += Vector3D.Right * 3;
Step(r, dock); Assert.True(Read<bool>(r.Miner, "waitingAligning"));
Assert.True(Vector3D.Dot(Desired(r).Forward, Vector3D.Forward) > .999);
r.Position += Vector3D.Right * 2;
Step(r, dock); Assert.False(Read<bool>(r.Miner, "waitingAligning"));
Assert.True(Vector3D.Dot(Desired(r).Forward, Vector3D.Left) > .999);
Assert.Equal(-1d, Read<double>(r.Miner, "stableSince"));
}
[Fact]
public void NearPortReturnPreservesCalibratedAttitudeInsteadOfTurningBesideMother()
{
var r = new MinerStateTests.MinerRig(); var dock = Waiting(r);
r.Position = Vector3D.Right * 10; Step(r, dock);
Assert.False(Read<bool>(r.Miner, "waitingAligning"));
Assert.True(Vector3D.Dot(Desired(r).Forward, Vector3D.Forward) > .999);
Assert.Equal(P.FlightState.Holding, r.StateNow);
}
[Theory]
[InlineData(P.FlightState.Returning)]
[InlineData(P.FlightState.Holding)]
[InlineData(P.FlightState.DockAlign)]
[InlineData(P.FlightState.DockApproach)]
[InlineData(P.FlightState.DockRetreat)]
public void RepeatedReturnKeepsNavigationProgressScanAndExistingLane(P.FlightState state)
{
var r = new MinerStateTests.MinerRig(); Waiting(r,true); r.State(state);
Read<List<Vector3D>>(r.Miner, "breadcrumbs").AddRange(new[] { Vector3D.Zero, Vector3D.Right * 20, Vector3D.Right * 40 });
Write(r.Miner, "returnIndex", 1); Write(r.Miner, "waitingAligning", true); Write(r.Miner, "stableSince", 2d);
var flight = Read<P.FlightController>(r.Miner, "flight"); Write(flight, "scanIndex", 7); Write(flight, "scanning", true);
double since = Read<double>(r.Miner, "stateSince");
for (int n = 0; n < 3; n++) { r.Program.Now++; r.Miner.Command("return"); }
Assert.Equal(state, r.StateNow); Assert.Equal(1, Read<int>(r.Miner, "returnIndex"));
Assert.Equal(7, Read<int>(flight, "scanIndex")); Assert.True(Read<bool>(flight, "scanning"));
Assert.True(Read<bool>(r.Miner, "waitingAligning")); Assert.Equal(2d, Read<double>(r.Miner, "stableSince"));
Assert.Equal(since, Read<double>(r.Miner, "stateSince")); Assert.Equal("valid-lease", Read<string>(r.Miner, "lane"));
Assert.DoesNotContain(r.Sent, p => p.Kind == "LANE_RELEASE");
}
[Fact]
public void ReturnDuringDepartureFinishesStraightRetreatBeforeReleasingTheDepartureLane()
{
var r = new MinerStateTests.MinerRig(); r.PrepareDeparture("valid-lease");
r.Connector.Set("Status", MyShipConnectorStatus.Unconnected); r.Position = Vector3D.Forward * 5; r.State(P.FlightState.Departing);
var dock = Read<P.DockFrame>(r.Miner, "dock");
r.Miner.Command("return");
Assert.Equal(P.FlightState.DockRetreat, r.StateNow);
Vector3D retreat = Read<Vector3D>(r.Miner, "abortPoint");
Assert.True(Vector3D.Dot(retreat - r.Position, dock.Forward) >= 30);
Assert.True(Vector3D.Cross(retreat - r.Position, dock.Forward).Length() < .00001);
Assert.Equal("valid-lease", Read<string>(r.Miner, "lane"));
r.Program.Now += .1; r.Miner.Command("return");
Assert.Equal(retreat, Read<Vector3D>(r.Miner, "abortPoint"));
Call(r.Miner, "DockRetreat", .1);
Assert.True(Vector3D.Dot(Desired(r).Forward, Vector3D.Forward) > .999);
Assert.DoesNotContain(r.Sent, p => p.Kind == "LANE_RELEASE");
r.Position = retreat; Call(r.Miner, "DockRetreat", .1);
Assert.Equal(P.FlightState.Holding, r.StateNow); Assert.Equal("", Read<string>(r.Miner, "lane"));
Assert.Single(r.Sent, p => p.Kind == "LANE_RELEASE");
}
}
}
using System;
using System.Collections.Generic;
using System.Reflection;
using Sandbox.ModAPI.Ingame;
using VRageMath;
using Xunit;
using P = AutoMiningScript.Program;
namespace AutoMiningScript.Tests
{
public class ReturnStateTests
{
static readonly BindingFlags Private = BindingFlags.Instance | BindingFlags.NonPublic;
static T Read<T>(object instance, string name) { return (T)instance.GetType().GetField(name, Private).GetValue(instance); }
static void Write(object instance, string name, object value) { instance.GetType().GetField(name, Private).SetValue(instance, value); }
static void Call(object instance, string name, params object[] args) { instance.GetType().GetMethod(name, Private).Invoke(instance, args); }
static P.DockFrame Waiting(MinerStateTests.MinerRig r,bool granted=false)
{
r.PrepareDeparture("valid-lease"); r.Connector.Set("Status", MyShipConnectorStatus.Unconnected);
Write(r.Miner, "laneKind", "dock"); if(!granted)Write(r.Miner,"lane",""); Write(r.Miner, "stableSince", -1d); r.State(P.FlightState.Holding);
var dock = Read<P.DockFrame>(r.Miner, "dock"); dock.WaitingLocal = new Vector3D(0, 0, -80); return dock;
}
static void Step(MinerStateTests.MinerRig r, P.DockFrame dock, double seconds = .1)
{ r.Program.Now += seconds; dock.ReceivedAt = r.Program.Now; Call(r.Miner, "Waiting", seconds); }
static MatrixD Desired(MinerStateTests.MinerRig r)
{ return Read<MatrixD>(Read<P.FlightController>(r.Miner, "flight"), "previousDesired"); }
[Fact]
public void WaitingKeepsTravelAndHoldingControlUntilTheGrantAllowsDirectApproach()
{
var r = new MinerStateTests.MinerRig(); var dock = Waiting(r);
r.Position = new Vector3D(100, 0, -80);
MatrixD orientation = MatrixD.Identity; Vector3D velocity = Vector3D.Zero;
r.Controller.Method("get_WorldMatrix", a => orientation).Method("GetShipVelocities", a => new MyShipVelocities(velocity, Vector3D.Zero));
Step(r, dock);
Assert.True(Vector3D.Dot(Desired(r).Forward, Vector3D.Left) > .999);
Assert.False(Read<bool>(r.Miner, "waitingAligning")); Assert.Equal(P.FlightState.Holding, r.StateNow);
r.Position = dock.WaitingLocal + Vector3D.Right;
orientation = MatrixD.CreateWorld(Vector3D.Zero, Vector3D.Left, Vector3D.Up);
velocity = Vector3D.Left * .4;
Step(r, dock); Assert.False(Read<bool>(r.Miner, "waitingAligning"));
velocity = Vector3D.Zero;
Step(r, dock); Assert.True(Read<bool>(r.Miner, "waitingAligning"));
Assert.True(Vector3D.Dot(Desired(r).Forward, Vector3D.Forward) > .999);
Assert.Equal(-1d, Read<double>(r.Miner, "stableSince")); Assert.Equal(P.FlightState.Holding, r.StateNow);
orientation = MatrixD.Identity;
Step(r, dock); Assert.Equal(P.FlightState.Holding,r.StateNow);
Assert.Contains(P.L.MinerDockGrantMissing,Read<string>(r.Miner,"reason"));
Write(r.Miner,"lane","valid-lease");Step(r,dock);
Assert.Equal(P.FlightState.DockAlign, r.StateNow);
Assert.Equal("valid-lease", Read<string>(r.Miner, "lane"));
}
[Fact]
public void WaitingAlignmentLatchAvoidsAlternatingAttitudesButResetsAfterFourMetresDrift()
{
var r = new MinerStateTests.MinerRig(); var dock = Waiting(r); r.Position = dock.WaitingLocal;
Step(r, dock); Assert.True(Read<bool>(r.Miner, "waitingAligning"));
r.Position += Vector3D.Right * 3;
Step(r, dock); Assert.True(Read<bool>(r.Miner, "waitingAligning"));
Assert.True(Vector3D.Dot(Desired(r).Forward, Vector3D.Forward) > .999);
r.Position += Vector3D.Right * 2;
Step(r, dock); Assert.False(Read<bool>(r.Miner, "waitingAligning"));
Assert.True(Vector3D.Dot(Desired(r).Forward, Vector3D.Left) > .999);
Assert.Equal(-1d, Read<double>(r.Miner, "stableSince"));
}
[Fact]
public void NearPortReturnPreservesCalibratedAttitudeInsteadOfTurningBesideMother()
{
var r = new MinerStateTests.MinerRig(); var dock = Waiting(r);
r.Position = Vector3D.Right * 10; Step(r, dock);
Assert.False(Read<bool>(r.Miner, "waitingAligning"));
Assert.True(Vector3D.Dot(Desired(r).Forward, Vector3D.Forward) > .999);
Assert.Equal(P.FlightState.Holding, r.StateNow);
}
[Theory]
[InlineData(P.FlightState.Returning)]
[InlineData(P.FlightState.Holding)]
[InlineData(P.FlightState.DockAlign)]
[InlineData(P.FlightState.DockApproach)]
[InlineData(P.FlightState.DockRetreat)]
public void RepeatedReturnKeepsNavigationProgressScanAndExistingLane(P.FlightState state)
{
var r = new MinerStateTests.MinerRig(); Waiting(r,true); r.State(state);
Read<List<Vector3D>>(r.Miner, "breadcrumbs").AddRange(new[] { Vector3D.Zero, Vector3D.Right * 20, Vector3D.Right * 40 });
Write(r.Miner, "returnIndex", 1); Write(r.Miner, "waitingAligning", true); Write(r.Miner, "stableSince", 2d);
var flight = Read<P.FlightController>(r.Miner, "flight"); Write(flight, "scanIndex", 7); Write(flight, "scanning", true);
double since = Read<double>(r.Miner, "stateSince");
for (int n = 0; n < 3; n++) { r.Program.Now++; r.Miner.Command("return"); }
Assert.Equal(state, r.StateNow); Assert.Equal(1, Read<int>(r.Miner, "returnIndex"));
Assert.Equal(7, Read<int>(flight, "scanIndex")); Assert.True(Read<bool>(flight, "scanning"));
Assert.True(Read<bool>(r.Miner, "waitingAligning")); Assert.Equal(2d, Read<double>(r.Miner, "stableSince"));
Assert.Equal(since, Read<double>(r.Miner, "stateSince")); Assert.Equal("valid-lease", Read<string>(r.Miner, "lane"));
Assert.DoesNotContain(r.Sent, p => p.Kind == "LANE_RELEASE");
}
[Fact]
public void ReturnDuringDepartureFinishesStraightRetreatBeforeReleasingTheDepartureLane()
{
var r = new MinerStateTests.MinerRig(); r.PrepareDeparture("valid-lease");
r.Connector.Set("Status", MyShipConnectorStatus.Unconnected); r.Position = Vector3D.Forward * 5; r.State(P.FlightState.Departing);
var dock = Read<P.DockFrame>(r.Miner, "dock");
r.Miner.Command("return");
Assert.Equal(P.FlightState.DockRetreat, r.StateNow);
Vector3D retreat = Read<Vector3D>(r.Miner, "abortPoint");
Assert.True(Vector3D.Dot(retreat - r.Position, dock.Forward) >= 30);
Assert.True(Vector3D.Cross(retreat - r.Position, dock.Forward).Length() < .00001);
Assert.Equal("valid-lease", Read<string>(r.Miner, "lane"));
r.Program.Now += .1; r.Miner.Command("return");
Assert.Equal(retreat, Read<Vector3D>(r.Miner, "abortPoint"));
Call(r.Miner, "DockRetreat", .1);
Assert.True(Vector3D.Dot(Desired(r).Forward, Vector3D.Forward) > .999);
Assert.DoesNotContain(r.Sent, p => p.Kind == "LANE_RELEASE");
r.Position = retreat; Call(r.Miner, "DockRetreat", .1);
Assert.Equal(P.FlightState.Holding, r.StateNow); Assert.Equal("", Read<string>(r.Miner, "lane"));
Assert.Single(r.Sent, p => p.Kind == "LANE_RELEASE");
}
}
}