using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using Sandbox.ModAPI.Ingame; using VRage.Game.GUI.TextPanel; using VRage.Game.ModAPI.Ingame.Utilities; using VRageMath; using Xunit; using P=AutoMiningScript.Program; namespace AutoMiningScript.Tests { public class ExplorationMapTests { static MyDetectedEntityInfo Hit(long id=42, MyDetectedEntityType type=MyDetectedEntityType.Asteroid) { return new MyDetectedEntityInfo(id,"rock",type,new Vector3D(80,60,-20),MatrixD.Identity,Vector3.Zero, VRage.Game.MyRelationsBetweenPlayerAndBlock.NoOwnership,new BoundingBoxD(new Vector3D(50,30,-40),new Vector3D(110,90,20)),1); } static P.ScanRecord Scan(long id=42) { return P.ScanRecord.Capture(Vector3D.Zero,new Vector3D(300,225,-75),Hit(id)); } sealed class MapRole:P.RoleLogic { public readonly List Jobs=new List(); public readonly List Fleet=new List(); public P.MiningRegion Region=new P.MiningRegion {Id="search-1",Search=true,Radius=300,Center=Vector3D.Zero}; public MapRole(P p):base(p){} public override List GetJobs(){return Jobs;} public override List GetTelemetry(){return Fleet;} public override P.MiningRegion MapRegion(string id){return Region.Id==id?Region:null;} } sealed class Rig { public readonly P Program=TestRig.Program("[Display]\nBuiltInPage=map\n"); public readonly List Sprites=new List(); public readonly P.Dashboard Dashboard; public readonly MapRole Role; public Rig(int width=1024,int height=512) { var surface=new Stub().Set("SurfaceSize",new Vector2(width,height)).Set("TextureSize",new Vector2(width,height)) .Method("MeasureStringInPixels",a=>new Vector2(a[0].ToString().Sum(ch=>ch>=0x2e80?32:16),32)*(float)a[2]) .Method("DrawFrame",a=>new MySpriteDrawFrame(f=>{Sprites.Clear();f.AddToList(Sprites);})); var pb=new Stub().Set("EntityId",20L).Set("CubeGrid",Program.Me.CubeGrid).Set("CustomData","") .Set("CustomName","PB").Set("SurfaceCount",1).Method("GetSurface",a=>surface.Value); TestRig.SetBase(Program,"Me",pb.Value);TestRig.SetBase(Program,"GridTerminalSystem",new Stub().Value); Dashboard=new P.Dashboard(Program);Role=new MapRole(Program); } public void Add(P.ScanRecord scan=null,string outcome="",long entity=0) { Role.Jobs.Add(new P.Job {Id="ray-"+Role.Jobs.Count,RegionId=Role.Region.Id,Kind=P.JobKind.Survey,Entry=Vector3D.Zero,Depth=300,Scan=scan,Outcome=outcome,EntityId=entity}); } public void Draw(double now=0){Program.Now=now;Dashboard.Draw(Role);} public string[] Text=>Sprites.Where(s=>s.Type==SpriteType.TEXT).Select(s=>s.Data).ToArray(); public MySprite[] Cells=>Sprites.Where(s=>s.Color==new Color(34,84,113)).ToArray(); } [Fact] public void ActualHitStopsTheRecordedRayAndCarriesWorldBoundsThroughMessageAndJob() { var scan=Scan();Assert.Equal(Hit().HitPosition,scan.End);Assert.NotEqual(new Vector3D(300,225,-75),scan.End); var report=P.JobReport.FromIni(new P.JobReport {Scan=scan,Outcome="SurveyHit"}.ToIni()); var job=P.Job.FromIni(new P.Job {Id="j",Scan=report.Scan}.ToIni()); Assert.Equal(42,job.Scan.Asteroid);Assert.Equal(Hit().BoundingBox.Center,job.Scan.Center);Assert.Equal(new Vector3D(60),job.Scan.Size);Assert.Equal(Vector3D.Zero,job.Scan.Start); } [Fact] public void LegacyMessagesAndJobsHaveNoInventedRayOrSize() { Assert.Null(P.JobReport.FromIni(new MyIni()).Scan);Assert.Null(P.Job.FromIni(new P.Job {Id="old"}.ToIni()).Scan); var r=new Rig();r.Add(outcome:"SurveyEmpty");r.Draw();Assert.Empty(r.Cells); Assert.Contains(P.L.F(P.L.DisplayScanSummary,0,1,0,0),r.Text); } [Fact] public void EmptyRayIsRecordedWhilePlanetAndGridAreNotCountedAsAsteroids() { var end=new Vector3D(20,30,40);var empty=P.ScanRecord.Capture(Vector3D.Zero,end,new MyDetectedEntityInfo()); Assert.Equal(end,empty.End);Assert.Equal(0,empty.Asteroid); Assert.Equal(0,P.ScanRecord.Capture(Vector3D.Zero,end,Hit(type:MyDetectedEntityType.Planet)).Asteroid); Assert.Equal(0,P.ScanRecord.Capture(Vector3D.Zero,end,Hit(type:MyDetectedEntityType.LargeGrid)).Asteroid); } [Fact] public void InvalidAndMissingCoordinatesDoNotInventMapEvidence() { Assert.Null(P.ScanRecord.Capture(new Vector3D(double.NaN),Vector3D.Zero,Hit())); var i=new MyIni();i.Set("job","ScanStart","NaN,0,0");Assert.Null(P.ScanRecord.Read(i,"job")); var info=new MyDetectedEntityInfo(42,"rock",MyDetectedEntityType.Asteroid,null,MatrixD.Identity,Vector3.Zero,VRage.Game.MyRelationsBetweenPlayerAndBlock.NoOwnership,new BoundingBoxD(),1); Assert.Null(P.ScanRecord.Capture(Vector3D.Zero,Vector3D.Forward,info)); } [Fact] public void RepeatedHitsCountOneAsteroidAndOneBoundingProjection() { var r=new Rig();r.Add(Scan(),"SurveyHit");r.Add(Scan(),"SurveyHit");r.Draw(); Assert.Contains(P.L.F(P.L.DisplayScanSummary,2,2,1,0),r.Text); Assert.Contains(P.L.F(P.L.DisplayRockArea,"3600 m2",1,1,1),r.Text); Assert.Single(r.Text,s=>s.StartsWith("A1 "));Assert.DoesNotContain(r.Text,s=>s.StartsWith("A2 ")); } [Fact] public void UnknownSizeRemainsUnknownAndCanBeReplacedByMeasuredBounds() { var r=new Rig();var unknown=Scan();unknown.Size=Vector3D.Zero;r.Add(unknown,"SurveyHit");r.Draw(); Assert.Contains("A1 ?",r.Text);Assert.Contains(P.L.F(P.L.DisplayScanSummary,1,1,1,1),r.Text); r.Add(Scan(),"SurveyHit");r.Draw(1);Assert.Contains("A1 ~3600 m2",r.Text);Assert.Contains(P.L.F(P.L.DisplayScanSummary,2,2,1,0),r.Text); } [Fact] public void RotatedMapUsesProjectedDimensionsRatherThanWorldXYArea() { var r=new Rig();r.Role.Region.Direction=Vector3D.Right;r.Role.Region.Up=Vector3D.Up; var scan=Scan();scan.Size=new Vector3D(40,50,60);r.Add(scan,"SurveyHit");r.Draw(); Assert.Contains("A1 ~3000 m2",r.Text); } [Fact] public void UnexecutedAndBlockedWithoutScanDoNotPaintCoverage() { var r=new Rig();r.Add();r.Add(outcome:"Blocked");r.Draw();Assert.Empty(r.Cells); r.Add(new P.ScanRecord {Start=Vector3D.Zero,End=new Vector3D(90,0,0)},"SurveyEmpty");r.Draw(1); Assert.NotEmpty(r.Cells);Assert.All(r.Cells,s=>Assert.InRange(s.Position.Value.X,512,555)); Assert.Contains(P.L.DisplayScanCaution,r.Text); } [Fact] public void ParallelRayEntirelyOutsideSearchDoesNotPaintAnEdge() { var r=new Rig();r.Add(new P.ScanRecord {Start=new Vector3D(400,-200,0),End=new Vector3D(400,200,0)},"SurveyEmpty");r.Draw();Assert.Empty(r.Cells); } [Theory] [InlineData(512,512)] [InlineData(1024,512)] [InlineData(512,1024)] public void SixteenCoincidentMinersKeepDistinctIdRowsAndMarkOffline(int width,int height) { var r=new Rig(width,height);r.Add(Scan(),"SurveyHit"); for(int n=1;n<=16;n++)r.Role.Fleet.Add(new P.Telemetry {Id="miner-"+n.ToString("00"),State=P.FlightState.Transit,ReceivedAt=n==16?-100:0}); r.Draw();var labels=r.Sprites.Where(s=>s.Type==SpriteType.TEXT && s.Data.StartsWith("miner-")).ToArray(); Assert.Equal(16,labels.Length);Assert.Equal(16,labels.Select(s=>s.Position.Value).Distinct().Count()); Assert.Contains(labels,s=>s.Data.Contains(P.L.DisplayOffline)); Assert.All(labels,s=>{Assert.InRange(s.Position.Value.X,0,width);Assert.InRange(s.Position.Value.Y,0,height);}); } [Fact] public void MoreAsteroidsCarouselWithoutLosingTotalOrCoverage() { var r=new Rig();for(int n=0;n<12;n++)r.Add(Scan(42+n),"SurveyHit");r.Draw();var cells=r.Cells.Length; Assert.Contains(r.Text,s=>s.StartsWith("A1 "));Assert.DoesNotContain(r.Text,s=>s.StartsWith("A9 ")); r.Draw(8);Assert.Contains(r.Text,s=>s.StartsWith("A9 "));Assert.Equal(cells,r.Cells.Length); Assert.Contains(P.L.F(P.L.DisplayScanSummary,12,12,12,0),r.Text); } [Fact] public void PlannerCheckpointRetainsBoundsAndClearTasksRemovesThem() { var planner=new P.TaskPlanner(TestRig.Program());planner.Survey(Vector3D.Zero,Vector3D.Zero,Vector3D.Forward,Vector3D.Up,300); var job=planner.Jobs.First();planner.Report(job,new P.JobReport {JobId=job.Id,Outcome="SurveyHit",Position=Scan().End,Direction=Vector3D.Forward,EntityId=42,Scan=Scan()}); var save=new MyIni();planner.Save(save);var restored=new P.TaskPlanner(TestRig.Program());restored.Load(save); Assert.Equal(new Vector3D(60),restored.Find(job.Id).Scan.Size);restored.ClearTasks();Assert.Empty(restored.Jobs); } [Fact] public void CameraSurveySendsActualRayEvidenceToTheFleet() { var r=new MinerStateTests.MinerRig();r.Task();var flags=BindingFlags.Instance|BindingFlags.NonPublic; var job=(P.Job)r.Miner.GetType().GetField("job",flags).GetValue(r.Miner);job.Kind=P.JobKind.Survey;r.CameraHit=Hit();r.State(P.FlightState.Survey); r.Miner.GetType().GetMethod("Survey",flags).Invoke(r.Miner,new object[]{.1}); var report=P.JobReport.FromIni(Assert.Single(r.Sent,p=>p.Kind=="RESULT").Body); Assert.Equal(r.Position,report.Scan.Start);Assert.Equal(Hit().HitPosition,report.Scan.End);Assert.True(report.Scan.Sized); } [Fact] public void RepresentativeMapFrameContainsRaysRocksAndMinerIds() { var r=new Rig();for(int n=0;n<24;n++){double a=n*Math.PI/12;r.Add(new P.ScanRecord {Start=Vector3D.Zero,End=new Vector3D(Math.Cos(a)*280,Math.Sin(a)*280,0)},"SurveyEmpty");} r.Add(Scan(),"SurveyHit");var other=Scan(99);other.Center=new Vector3D(-120,-100,0);other.End=other.Center;other.Size=new Vector3D(90,70,30);r.Add(other,"SurveyHit"); for(int n=1;n<=5;n++)r.Role.Fleet.Add(new P.Telemetry {Id=P.L.Language=="zh-CN"?"深空工业"+n+"号":"Miner-"+n,Position=new Vector3D((n-3)*40,40,0),ReceivedAt=n==5?-100:0}); r.Draw();Assert.NotEmpty(r.Cells);Assert.Contains("A1 ~3600 m2",r.Text); string path=Environment.GetEnvironmentVariable("AMS_MAP_PREVIEW");if(string.IsNullOrEmpty(path))return; File.WriteAllLines(path,r.Sprites.Select(s=>string.Join("\t",new[]{s.Type.ToString(),Convert.ToBase64String(Encoding.UTF8.GetBytes(s.Data??"")), (s.Position??Vector2.Zero).X.ToString(P.Data.Culture),(s.Position??Vector2.Zero).Y.ToString(P.Data.Culture),(s.Size??Vector2.Zero).X.ToString(P.Data.Culture),(s.Size??Vector2.Zero).Y.ToString(P.Data.Culture), s.RotationOrScale.ToString(P.Data.Culture),s.Alignment.ToString(),(s.Color??Color.White).R.ToString(),(s.Color??Color.White).G.ToString(),(s.Color??Color.White).B.ToString(),(s.Color??Color.White).A.ToString()})),Encoding.UTF8); } } }