using System; using System.Linq; using System.Reflection; using System.Runtime.Remoting; using Sandbox.ModAPI.Ingame; using VRage.Game.ModAPI.Ingame; using VRageMath; using Xunit; using P = AutoMiningScript.Program; namespace AutoMiningScript.Tests { public class InstantBerthTests { static void Returning(FleetStateTests.Rig rig, int index = 0, double distance = 100) { var miner = rig.Miners[index]; miner.State = P.FlightState.Returning; miner.Connected = false; miner.Position += Vector3D.Forward * distance; } static P.DockFrame Grant(FleetStateTests.Rig rig, string miner) { var packet = Assert.Single(rig.Sent, p => p.Kind == "DOCK_GRANT" && p.Target == miner); var pose = P.DockFrame.FromIni(packet.Body); Assert.Equal(miner, pose.Owner); Assert.Equal("base", pose.BaseId); Assert.NotEmpty(pose.Token); Assert.Equal("", pose.Problem); return pose; } static void Near(Vector3D expected, Vector3D actual) { Assert.True(Vector3D.Distance(expected, actual) < .000001, expected + " vs " + actual); } [Theory] [InlineData(false, false)] [InlineData(true, false)] [InlineData(false, true)] [InlineData(true, true)] public void ReturningRequestReceivesItsBerthBeforeAnyTickWhetherRegisteredOrBasePaused(bool registered, bool paused) { var rig = new FleetStateTests.Rig(1, false, "[Dock]\nStableSeconds=30\n"); Returning(rig); if (registered) rig.RegisterAll(); if (paused) { rig.Fleet.Command("start"); rig.Fleet.Command("pause"); } rig.Sent.Clear(); rig.Receive("DOCK_REQUEST", rig.Miners[0]); Assert.Equal(0, rig.Program.Now); var pose = Grant(rig, rig.Miners[0].Id); Assert.Equal(100, pose.ConnectorId); Assert.Equal(30, pose.ApproachDistance); Assert.Equal(5000, Assert.Single(rig.Fleet.GetTelemetry()).Address); // The first motion sample remains unqualified for an actual docking // maneuver. That must not prevent choosing the free connector. pose.ReceivedAt = rig.Program.Now; Assert.False(pose.IsWindow(rig.Program.Config, rig.Program.Now)); } [Fact] public void SeveralFirstRequestsReceiveDifferentAvailableBerthsWithoutAdvancingTheSchedulerClock() { var rig = new FleetStateTests.Rig(3, false); for (int i = 0; i < rig.Miners.Count; i++) { Returning(rig, i); rig.Receive("DOCK_REQUEST", rig.Miners[i]); Assert.Equal(i + 1, rig.Sent.Count(p => p.Kind == "DOCK_GRANT")); } var grants = rig.Miners.Select(t => Grant(rig, t.Id)).ToList(); Assert.Equal(0, rig.Program.Now); Assert.Equal(3, grants.Select(p => p.ConnectorId).Distinct().Count()); Assert.Equal(new[] { 30d, 40d, 50d }, grants.Select(p => p.ApproachDistance).OrderBy(d => d)); } [Theory] [InlineData(false)] [InlineData(true)] public void RequestSamplesCurrentRotatingBerthEvenBeforeTheNextFrameUpdate(bool hadEarlierFrame) { var rig = new FleetStateTests.Rig(1, false); Returning(rig); if (hadEarlierFrame) rig.Tick(1); var now = rig.Program.Now; var position = new Vector3D(7, -3, 11); var forward = Vector3D.Normalize(new Vector3D(1, 2, -1)); var center = new Vector3D(-12, 4, 6); var velocity = new Vector3D(2, 1, -3); var angular = new Vector3D(0, .04, 0); rig.SetPort(0, position, forward); var controller = (IMyShipController)typeof(P.FleetController).GetField("controller", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(rig.Fleet); var stub = (Stub)RemotingServices.GetRealProxy(controller); stub.Set("CenterOfMass", center).Method("GetShipVelocities", a => new MyShipVelocities(velocity, angular)); rig.Receive("DOCK_REQUEST", rig.Miners[0]); var pose = Grant(rig, rig.Miners[0].Id); Assert.Equal(now, rig.Program.Now); Near(position, pose.Position); Near(forward, pose.Forward); Near(center, pose.Center); Near(velocity, pose.Velocity); Near(angular, pose.AngularVelocity); Near(position + forward * pose.ApproachDistance, pose.Position + pose.Forward * pose.ApproachDistance); pose.ReceivedAt = now; Assert.False(pose.IsWindow(rig.Program.Config, now)); } [Theory] [InlineData(15)] [InlineData(100)] public void RestoredInboundOwnerReceivesANewGrantOnItsFreshRequestWithoutAStabilityWait(double distance) { var before = new FleetStateTests.Rig(1, false); Returning(before, distance: distance); before.Receive("DOCK_REQUEST", before.Miners[0]); var original = Grant(before, before.Miners[0].Id); var saved = before.Checkpoint(); var after = new FleetStateTests.Rig(1, false, "[Dock]\nStableSeconds=30\n"); after.Restore(saved); Returning(after, distance: distance); after.RegisterAll(); Assert.DoesNotContain(after.Sent, p => p.Kind == "DOCK_GRANT"); after.Receive("DOCK_REQUEST", after.Miners[0]); var renewed = Grant(after, after.Miners[0].Id); Assert.Equal(0, after.Program.Now); Assert.NotEqual(original.Token, renewed.Token); Assert.Equal(original.ConnectorId, renewed.ConnectorId); Assert.Equal(original.ApproachDistance, renewed.ApproachDistance); } [Fact] public void FirstRequestCannotClaimAConnectorThatIsPhysicallyOccupied() { var rig = new FleetStateTests.Rig(1, true); Returning(rig); rig.Miners[0].ConnectorId = 9999; rig.Receive("DOCK_REQUEST", rig.Miners[0]); Assert.Single(rig.Fleet.GetTelemetry()); Assert.DoesNotContain(rig.Sent, p => p.Kind == "DOCK_GRANT"); Assert.Equal(0, P.Data.ReadLong(rig.Save(), "fleet", "LaneCount")); } static void CloseCachedBerth(FleetStateTests.Rig rig,long connector) { var ports=(System.Collections.Generic.List)typeof(P.FleetController).GetField("docks",BindingFlags.NonPublic|BindingFlags.Instance).GetValue(rig.Fleet); var port=ports.Single(p=>p.EntityId==connector); ((Stub)RemotingServices.GetRealProxy(port)).Set("Closed",true).Set("IsFunctional",true); Assert.True(port.Closed);Assert.True(port.IsFunctional); } [Theory] [InlineData(false)] [InlineData(true)] public void RemovedCachedBerthCannotBlockTheRemainingFreeBerthBeforeHardwareRescan(bool restoredAssignment) { var rig=new FleetStateTests.Rig(2,false);Returning(rig); if(restoredAssignment) { rig.Receive("DOCK_REQUEST",rig.Miners[0]);Assert.Equal(100,Grant(rig,rig.Miners[0].Id).ConnectorId); var saved=rig.Checkpoint();rig=new FleetStateTests.Rig(2,false);rig.Restore(saved);Returning(rig); } CloseCachedBerth(rig,100);rig.Sent.Clear(); rig.Receive("DOCK_REQUEST",rig.Miners[0]); Assert.Equal(0,rig.Program.Now);Assert.Equal(101,Grant(rig,rig.Miners[0].Id).ConnectorId); Assert.Equal(1,P.Data.ReadLong(rig.Save(),"fleet","LaneCount")); } [Fact] public void ARemovedBerthCannotRenewAStillNearbyRestoredAssignment() { var before=new FleetStateTests.Rig(2,false);Returning(before,distance:15);before.Receive("DOCK_REQUEST",before.Miners[0]); Assert.Equal(100,Grant(before,before.Miners[0].Id).ConnectorId); var after=new FleetStateTests.Rig(2,false);after.Restore(before.Checkpoint());Returning(after,distance:15); CloseCachedBerth(after,100);after.Receive("DOCK_REQUEST",after.Miners[0]); Assert.DoesNotContain(after.Sent,p=>p.Kind=="DOCK_GRANT"); Assert.Empty(P.Data.Text(after.Save(),"fleet","LaneToken")); Assert.Equal(100,P.Data.ReadLong(after.Save(),"fleet","LaneDock")); } [Fact] public void AFirstRequestOverTheWireRegistersAndReceivesTheGrantInTheSameDrain() { var rig = new FleetStateTests.Rig(1, false); Returning(rig); var incoming = new System.Collections.Generic.Queue(); var listener = (Stub)RemotingServices.GetRealProxy(rig.Program.IGC.UnicastListener); listener.Method("get_HasPendingMessage", a => incoming.Count > 0).Method("AcceptMessage", a => incoming.Dequeue()); var packet = new P.Packet { From = rig.Miners[0].Id, Target = "base", Kind = "DOCK_REQUEST", Session = "fresh-miner", Id = "fresh-miner-1", Seq = 1, Reliable = true, Body = rig.Miners[0].ToIni() }; incoming.Enqueue(new MyIGCMessage(packet.Encode("mining"), "AMS2/mining", 5000)); rig.Program.Bus.Drain(rig.Fleet.Receive); Assert.Empty(incoming); Assert.Equal(0, rig.Program.Now); Grant(rig, rig.Miners[0].Id); Assert.Equal(5000, Assert.Single(rig.Fleet.GetTelemetry()).Address); } [Fact] public void FirstRequestStillHonorsTheRegistryCapacityLimit() { var rig = new FleetStateTests.Rig(2, false, "[Fleet]\nMaxMiners=1\n"); Returning(rig, 0); Returning(rig, 1); rig.Receive("DOCK_REQUEST", rig.Miners[0]); Grant(rig, rig.Miners[0].Id); rig.Receive("DOCK_REQUEST", rig.Miners[1]); Assert.Equal(rig.Miners[0].Id, Assert.Single(rig.Fleet.GetTelemetry()).Id); Assert.DoesNotContain(rig.Sent, p => p.Kind == "DOCK_GRANT" && p.Target == rig.Miners[1].Id); Assert.Equal(1, P.Data.ReadLong(rig.Save(), "fleet", "LaneCount")); } } }