using System; using System.Linq; using System.Reflection; using Xunit; using VRageMath; using P = AutoMiningScript.Program; namespace AutoMiningScript.Tests { public class DiscoveryDispatchTests { const BindingFlags Private = BindingFlags.Instance | BindingFlags.NonPublic; static void Set(object o, string field, object value) => o.GetType().GetField(field, Private).SetValue(o, value); static void Assign(FleetStateTests.Rig r) => r.Fleet.GetType().GetMethod("Assign", Private).Invoke(r.Fleet, null); static FleetStateTests.Rig ScanningFleet() { var r = new FleetStateTests.Rig(3, true); r.RegisterAll(); r.Fleet.Command("survey 300"); while (r.Planner.Generating) r.Planner.GenerateStep(); var entries = r.Planner.Jobs.GroupBy(j => j.Entry).Take(3).Select(g => g.First()).ToArray(); for (int n = 0; n < 3; n++) { r.SetConnected(n, false); var t = r.Miners[n]; var j = entries[n]; r.Planner.SetOwner(j, t.Id); t.JobId = j.Id; t.Position = j.Entry; t.State = P.FlightState.Survey; r.Receive("TELEMETRY", t); } r.Fleet.Command("start"); r.Sent.Clear(); Set(r.Fleet, "assignMinerCursor", 0); return r; } static P.JobReport Detection(FleetStateTests.Rig r) { var t = r.Miners[2]; return new P.JobReport { JobId = t.JobId, Outcome = "SurveyHit", Position = Vector3D.Forward * 150, Direction = Vector3D.Forward, Up = Vector3D.Up, EntityId = 4242 }; } static void Report(FleetStateTests.Rig r, P.JobReport hit) => r.Fleet.Receive(new P.Packet { Kind = "RESULT", From = r.Miners[2].Id, Source = 5002, Body = hit.ToIni() }); static void Ready(FleetStateTests.Rig r, P.JobReport hit) { var t = r.Miners[2]; t.JobId = ""; t.NextAfter = hit.JobId; r.Receive("TELEMETRY", t); } static P.Packet NextAssignment(FleetStateTests.Rig r) { // A 294-ray search is intentionally processed in bounded chunks. The // next completed pass must concern the discoverer, not a fleet lap. for (int n = 0; n < 12 && !r.Sent.Any(p => p.Kind == "JOB"); n++) Assign(r); return Assert.Single(r.Sent, p => p.Kind == "JOB"); } [Theory] [InlineData(false)] [InlineData(true)] public void NewAsteroidDispatchesOneProbeBeforeAnotherFleetLapWithoutCancellingOtherScans(bool telemetryFirst) { var r = ScanningFleet(); var held = r.Miners.Take(2).Select(t => r.Planner.Find(t.JobId)).ToArray(); var hit = Detection(r); if (telemetryFirst) { Ready(r, hit); Report(r, hit); } else { Report(r, hit); Ready(r, hit); } var packet = NextAssignment(r); var probe = P.Job.FromIni(packet.Body); Assert.Equal(r.Miners[2].Id, packet.Target); Assert.Equal(P.JobKind.Probe, probe.Kind); Assert.Equal(hit.Position, probe.Entry); Assert.Equal(hit.EntityId, probe.EntityId); Assert.Equal(hit.JobId, P.Data.Text(packet.Body, "next", "After")); Assert.Equal(3, r.Planner.OwnedJobs.Count); for (int n = 0; n < 2; n++) { Assert.Same(held[n], r.Planner.OwnedJobs[r.Miners[n].Id]); Assert.Equal("", held[n].Outcome); } Assert.DoesNotContain(r.Sent, p => p.Kind == "COMMAND"); } [Fact] public void NewAsteroidDoesNotPreemptAnOccupiedBore() { var r = ScanningFleet(); var bore = r.Planner.Find(r.Miners[0].JobId); bore.Kind = P.JobKind.Probe; bore.Entry = Vector3D.Right * 240; bore.Direction = Vector3D.Forward; bore.Depth = 40; r.Miners[0].State = P.FlightState.Drilling; r.Miners[0].Position = bore.Entry + bore.Direction * 8; r.Receive("TELEMETRY", r.Miners[0]); var hit = Detection(r); Report(r, hit); Ready(r, hit); Assert.Equal(P.JobKind.Probe, P.Job.FromIni(NextAssignment(r).Body).Kind); Assert.Same(bore, r.Planner.OwnedJobs[r.Miners[0].Id]); Assert.Equal(P.FlightState.Drilling, r.Fleet.GetTelemetry().Single(t => t.Id == r.Miners[0].Id).State); Assert.DoesNotContain(r.Sent, p => p.Kind == "COMMAND"); } [Fact] public void UnfitDiscovererLeavesTheProbeForAnotherAvailableMiner() { var r = ScanningFleet(); var hit = Detection(r); Report(r, hit); r.Miners[2].Battery = .1; Ready(r, hit); // Miner 0 becomes free at its dock; miner 1 retains its active ray. var previous = r.Planner.Find(r.Miners[0].JobId); r.Planner.Report(previous, new P.JobReport { Outcome = "SurveyEmpty" }); r.SetConnected(0, true); r.Miners[0].JobId = ""; r.Miners[0].State = P.FlightState.Servicing; r.Receive("TELEMETRY", r.Miners[0]); var active = r.Planner.Find(r.Miners[1].JobId); var packet = NextAssignment(r); Assert.Equal(r.Miners[0].Id, packet.Target); Assert.Equal(P.JobKind.Probe, P.Job.FromIni(packet.Body).Kind); Assert.Empty(r.Planner.Jobs.Single(j => j.Id == hit.JobId).Owner); Assert.Same(active, r.Planner.OwnedJobs[r.Miners[1].Id]); } [Fact] public void DuplicateDetectionDoesNotCreateASecondProbeOrResetAnActiveScanPass() { var r = ScanningFleet(); var hit = Detection(r); Report(r, hit); Ready(r, hit); var assigned = P.Job.FromIni(NextAssignment(r).Body); Set(r.Fleet, "assignMinerCursor", 1); r.Receive("TELEMETRY", r.Miners[2]); Report(r, hit); Assert.Equal(1, (int)r.Fleet.GetType().GetField("assignMinerCursor", Private).GetValue(r.Fleet)); Assert.Single(r.Planner.Jobs, j => j.Kind == P.JobKind.Probe); Assert.Equal(assigned.Id, r.Planner.OwnedJobs[r.Miners[2].Id].Id); } } }