using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.Remoting; using Sandbox.ModAPI.Ingame; using VRageMath; using Xunit; using P=AutoMiningScript.Program; namespace AutoMiningScript.Tests { public class CameraReadinessTests { static T Field(object value,string name) {return (T)value.GetType().GetField(name,BindingFlags.NonPublic|BindingFlags.Instance).GetValue(value);} static Stub Camera(ReturnNavigationTests.Rig r,string name,long id,Func pose) { var c=new Stub().Set("CustomName",name).Set("EntityId",id).Set("CubeGrid",r.Program.Me.CubeGrid) .Set("IsFunctional",true).Set("Enabled",true).Set("IsWorking",true).Set("EnableRaycast",true) .Set("AvailableScanRange",100000d).Set("RaycastConeLimit",45f).Set("RaycastDistanceLimit",-1d) .Method("get_WorldMatrix",a=>pose()).Method("GetPosition",a=>pose().Translation); c.Method("CanScan",a=>{ var target=(Vector3D)a[0];var local=Vector3D.TransformNormal(target-pose().Translation,MatrixD.Transpose(pose())); return c.Value.IsWorking && Vector3D.Distance(target,pose().Translation)<=c.Value.AvailableScanRange && -local.Z>0 && Math.Abs(Math.Atan2(local.X,-local.Z))<=Math.PI/4 && Math.Abs(Math.Atan2(local.Y,Math.Sqrt(local.X*local.X+local.Z*local.Z)))<=Math.PI/4; }); return c; } [Fact] public void SavedFourteenMetreApproachCompletesWithTheInstalledCameraLayout() { var r=new ReturnNavigationTests.Rig(); var position=new Vector3D(-67811.481492656632,-123686.02419437817,-162113.20791115251); r.Orientation=MatrixD.CreateWorld(position,new Vector3D(-.22140851616859436,.76646673679351807,.60291552543640137), new Vector3D(.86432009935379028,-.13206471502780914,.48529350757598877)); r.Controller.Method("GetPosition",a=>position); r.Ship.Radius=4.6314601354267158;r.Ship.BodyMin=new Vector3D(-2.25,-1.75,-2.25);r.Ship.BodyMax=new Vector3D(2.25,1.25,2.25); r.Ship.BodyHalfSize=Vector3D.Max(Vector3D.Abs(r.Ship.BodyMin),Vector3D.Abs(r.Ship.BodyMax));r.Ship.Cameras.Clear(); var positions=new[]{new Vector3D(0,1,1.5),new Vector3D(0,1,0),new Vector3D(2,0,1),new Vector3D(0,-.5,-.5), new Vector3D(0,-1.5,.5),new Vector3D(-2,0,1),new Vector3D(1.5,0,2),new Vector3D(-1.5,0,2)}; var directions=new[]{Vector3D.Backward,Vector3D.Forward,Vector3D.Right,Vector3D.Forward,Vector3D.Forward,Vector3D.Left,Vector3D.Backward,Vector3D.Backward}; var ups=new[]{Vector3D.Up,Vector3D.Up,Vector3D.Down,Vector3D.Down,Vector3D.Down,Vector3D.Backward,Vector3D.Right,Vector3D.Right}; for(int n=0;npose).Value); } var target=new Vector3D(-67808.36607753133,-123696.7388086597,-162121.59648775522); r.Flight.SetDockContact(50,new Vector3D(-67807.59084232424,-123699.42231776408,-162123.70730985893),1.5); for(int n=0;n<18 && r.Flight.ScanClearance==0;n++) { r.Program.Now+=1d/60; r.Flight.Move(target,Vector3D.Zero,r.Orientation.Forward,r.Orientation.Up,1.83,.1,true,1d/60,false,false); } Assert.InRange(r.Flight.ScanClearance,16,17);Assert.True(r.HasTranslation,r.Flight.Problem+" / "+r.Flight.CameraDiagnostics); } static MyDetectedEntityInfo Wall(Vector3D point) { return new MyDetectedEntityInfo(50,"Berth railing",MyDetectedEntityType.LargeGrid,point,MatrixD.Identity,Vector3.Zero, VRage.Game.MyRelationsBetweenPlayerAndBlock.NoOwnership,new BoundingBoxD(point-new Vector3D(.1),point+new Vector3D(.1)),1); } static void Approach(ReturnNavigationTests.Rig r) { r.Program.Now+=.05; r.Flight.Move(Vector3D.Forward*14,Vector3D.Zero,Vector3D.Forward,Vector3D.Up,2,.1,true,.05,false,false); } [Fact] public void FarRailingHitCertifiesTheClearApproachBeforeItInsteadOfBlockingAllFourteenMetres() { var r=new ReturnNavigationTests.Rig();int rays=0; r.Flight.SetDockContact(50,Vector3D.Forward*20,1.5); r.Camera.Method("Raycast",a=>{rays++;var end=(Vector3D)a[0];return -end.Z>10?Wall(end*(10/-end.Z)):default(MyDetectedEntityInfo);}); for(int n=0;n<9;n++) { Approach(r); if(n<8)Assert.False(r.HasTranslation); Assert.False(r.Flight.Blocked,"An unfinished sweep must not classify the whole route as impassable."); } Assert.Equal(18,rays);Assert.InRange(r.Flight.ScanClearance,9,10);Assert.True(r.HasTranslation,r.Flight.Problem); Assert.Contains("Berth railing",r.Flight.Problem); } [Fact] public void ObstacleReasonRemainsVisibleWhileTheNextSweepIsIncomplete() { var r=new ReturnNavigationTests.Rig();int rays=0; r.Flight.SetDockContact(50,Vector3D.Forward*20,1.5); r.Camera.Method("Raycast",a=>{rays++;return rays%18>=10 || rays%18==0?Wall(Vector3D.Forward*.8):default(MyDetectedEntityInfo);}); for(int n=0;n<9;n++)Approach(r); for(int n=0;n<25;n++) { Approach(r);Assert.False(r.HasTranslation); Assert.Contains("Berth railing",r.Flight.Problem);Assert.DoesNotContain(P.L.MinerEnvelopeScanPending,r.Flight.Problem); } } [Fact] public void NewObstacleImmediatelyShortensExistingClearanceBeforeSweepCompletion() { var r=new ReturnNavigationTests.Rig();r.Flight.SetDockContact(50,Vector3D.Forward*20,1.5); for(int n=0;n<9;n++)Approach(r);Assert.True(r.Flight.ScanClearance>10); r.Camera.Method("Raycast",a=>Wall(Vector3D.Forward*.8));Approach(r); Assert.False(r.HasTranslation);Assert.InRange(r.Flight.ScanClearance,0,.8);Assert.Contains("Berth railing",r.Flight.Problem); } [Fact] public void RailingClearanceIsReplacedOnlyAfterAnEntireFreshUnobstructedSweep() { var r=new ReturnNavigationTests.Rig();r.Flight.SetDockContact(50,Vector3D.Forward*20,1.5); r.Camera.Method("Raycast",a=>Wall(Vector3D.Forward*.8));for(int n=0;n<9;n++)Approach(r); r.Camera.Method("Raycast",a=>default(MyDetectedEntityInfo)); for(int n=0;n<8;n++){Approach(r);Assert.False(r.HasTranslation);Assert.Contains("Berth railing",r.Flight.Problem);} Approach(r);Assert.True(r.HasTranslation);Assert.DoesNotContain("Berth railing",r.Flight.Problem); } [Theory] [InlineData(0)] [InlineData(1)] [InlineData(2)] public void MissingOrInvalidSurfaceHitNeverCertifiesFreeSpaceToTheEntityCentre(int kind) { var r=new ReturnNavigationTests.Rig();r.Flight.SetDockContact(50,Vector3D.Forward*20,1.5); for(int n=0;n<9;n++)Approach(r);Assert.True(r.HasTranslation); Vector3D? point=kind==0?(Vector3D?)null:new Vector3D(kind==1?double.NaN:double.PositiveInfinity); r.Camera.Method("Raycast",a=>new MyDetectedEntityInfo(50,"Unknown surface",MyDetectedEntityType.LargeGrid,point, MatrixD.Identity,Vector3.Zero,VRage.Game.MyRelationsBetweenPlayerAndBlock.NoOwnership, new BoundingBoxD(new Vector3D(-1,-1,-11),new Vector3D(1,1,-9)),1)); for(int n=0;n<20;n++) { Approach(r);Assert.Equal(0,r.Flight.ScanClearance);Assert.False(r.HasTranslation); Assert.Contains(P.L.F(P.L.CameraObstacleUnknown,"Unknown surface"),r.Flight.Problem); } } [Fact] public void ALongNoseUsesVisibleSamplesInTheSameLanesInsteadOfImpossibleCloseCorners() { var r=new ReturnNavigationTests.Rig();r.Ship.Radius=44;r.Ship.BodyHalfSize=new Vector3D(5,15,40); r.Ship.BodyMin=-r.Ship.BodyHalfSize;r.Ship.BodyMax=r.Ship.BodyHalfSize; r.Ship.Cameras.Clear();Vector3D front=Vector3D.Forward*40; r.Ship.Cameras.Add(Camera(r,"Front",1,()=>MatrixD.CreateWorld(front,Vector3D.Forward,Vector3D.Up)).Value); r.Ship.Cameras.Add(Camera(r,"Rear",2,()=>MatrixD.CreateWorld(Vector3D.Backward*40,Vector3D.Backward,Vector3D.Up)).Value); var endpoints=new List();var camera=r.Ship.Cameras[0]; ((Stub)RemotingServices.GetRealProxy(camera)).Method("Raycast",a=>{ Assert.True(camera.CanScan((Vector3D)a[0]));endpoints.Add((Vector3D)a[0]);return default(MyDetectedEntityInfo); }); for(int n=0;n<30 && r.Flight.ScanClearance==0;n++)r.Step(Vector3D.Forward*10); Assert.True(r.HasTranslation);Assert.Equal(18,endpoints.Count);Assert.Equal(50.2,r.Flight.ScanClearance,8); Assert.Contains(endpoints.GetRange(0,9),point=>-point.Z>Field(r.Flight,"scanLength")*.35+.1); Assert.Contains(endpoints,point=>point.Y>15);Assert.Contains(endpoints,point=>point.Y<-15); } [Theory] [InlineData("disabled")] [InlineData("damaged")] [InlineData("power")] [InlineData("raycast")] public void CameraHardwareFailuresHaveDistinctNamesAndNeverAuthoriseMotion(string kind) { var r=new ReturnNavigationTests.Rig();r.Camera.Set("CustomName","Lens").Method("CanScan",a=>false); string format; if(kind=="raycast") {r.Camera.Set("EnableRaycast",false);format=P.L.CameraRaycastDisabled;} else { r.Camera.Set("IsWorking",false); if(kind=="disabled") {r.Camera.Set("Enabled",false);format=P.L.CameraDisabled;} else if(kind=="damaged") {r.Camera.Set("IsFunctional",false);format=P.L.CameraDamaged;} else format=P.L.CameraUnpowered; } r.Step(Vector3D.Forward*100); Assert.Equal(P.L.F(format,"Lens"),r.Flight.Problem);Assert.False(r.HasTranslation); Assert.False(Field(r.Flight,"scanWaitingForCharge")); } [Fact] public void AChargingCameraReportsAvailableAndRequiredRangeInsteadOfAnOrientationFailure() { var r=new ReturnNavigationTests.Rig();r.Charge=0;r.Camera.Set("CustomName","Lens").Method("TimeUntilScan",a=>1200); r.Step(Vector3D.Forward*100); Assert.True(Field(r.Flight,"scanWaitingForCharge"));Assert.False(r.HasTranslation); Assert.Equal(P.L.F(P.L.CameraCharging,"Lens",0d,Field(r.Flight,"scanLength")*.35,1.2),r.Flight.Problem); } [Fact] public void ChargeRangeReductionCannotShrinkInsideTheLeadingHull() { var r=new ReturnNavigationTests.Rig();r.Charge=0;r.Ship.BodyMin=new Vector3D(-1,-1,-10);r.Ship.BodyMax=new Vector3D(1,1,1); for(int n=0;n<500;n++)r.Step(Vector3D.Forward*100,false,1); Assert.True(Field(r.Flight,"scanLimit")>=11);Assert.False(r.HasTranslation);Assert.Equal(0,r.Flight.ScanClearance); } [Fact] public void PrepareFlightEnablesOnlyTheManagedLocalCameras() { var r=new ReturnNavigationTests.Rig();r.Camera.Set("Enabled",false).Set("EnableRaycast",false); var other=Camera(r,"Foreign",9,()=>MatrixD.Identity).Set("CubeGrid",new Stub().Value).Set("Enabled",false).Set("EnableRaycast",false); r.Ship.Cameras.Add(other.Value);r.Ship.PrepareFlight(); Assert.True(r.Camera.Value.Enabled);Assert.True(r.Camera.Value.EnableRaycast); Assert.False(other.Value.Enabled);Assert.False(other.Value.EnableRaycast); } [Fact] public void AStaticHundredCamerasReportTheActualDirectionalFailureWithoutLosingRange() { var r=new ReturnNavigationTests.Rig();r.Ship.Cameras.Clear(); for(int n=0;n<100;n++) r.Ship.Cameras.Add(Camera(r,"Lens-"+n,n+1,()=>MatrixD.Identity).Value); for(int n=0;n<90;n++)r.Step(Vector3D.Right*100); Assert.Contains(P.L.F(P.L.CameraInventory,100,100,0),r.Flight.CameraDiagnostics); Assert.False(r.HasTranslation);Assert.False(Field(r.Flight,"scanWaitingForCharge")); Assert.Equal(2000,Field(r.Flight,"scanLimit"));Assert.Contains("90.0",r.Flight.CameraDiagnostics); Assert.Equal(P.L.F(P.L.CameraRegionUnavailable,1,100),r.Flight.Problem); } static List TerminalBlocks(MinerStateTests.MinerRig r) { var blocks=new List {r.Controller.Value,r.Connector.Value,r.Drill.Value,r.Camera.Value}; foreach(var thrust in r.Hardware.Thrusters) { var definition=thrust.BlockDefinition;definition.SubtypeName="MockThruster"; ((Stub)RemotingServices.GetRealProxy(thrust)).Set("BlockDefinition",definition); } blocks.AddRange(r.Hardware.Thrusters);blocks.AddRange(r.Hardware.Gyros);blocks.AddRange(r.Hardware.Batteries); return blocks; } static Stub InstalledCamera(MinerStateTests.MinerRig r,long id) { return new Stub().Set("CubeGrid",r.Program.Me.CubeGrid).Set("EntityId",id) .Set("IsFunctional",true).Set("WorldMatrix",MatrixD.Identity).Set("Enabled",false).Set("EnableRaycast",false); } static void SupplyBlocks(MinerStateTests.MinerRig r,List blocks,List groupBlocks=null) { var system=new Stub().Method("GetBlocksOfType",a=>{ var list=(List)a[0];var filter=(Func)a[1]; foreach(var block in blocks)if(filter==null || filter(block))list.Add(block);return null; }); if(groupBlocks!=null) { var group=new Stub().Method("GetBlocks",a=>{((List)a[0]).AddRange(groupBlocks);return null;}); system.Method("GetBlockGroupWithName",a=>{Assert.Equal("Managed",a[0]);return group.Value;}); } TestRig.SetBase(r.Program,"GridTerminalSystem",system.Value); } [Fact] public void MinerInitializationDiscoversAndEnablesLocalCamerasAndMeasuresTheHull() { var r=new MinerStateTests.MinerRig();var grid=(Stub)RemotingServices.GetRealProxy(r.Program.Me.CubeGrid); grid.Set("Min",new Vector3I(-10)).Set("Max",new Vector3I(10)).Method("GridIntegerToWorld",a=>new Vector3D((Vector3I)a[0])*2.5); var added=InstalledCamera(r,2);var foreign=InstalledCamera(r,3).Set("CubeGrid",new Stub().Value); var blocks=TerminalBlocks(r);blocks.Add(added.Value);blocks.Add(foreign.Value);SupplyBlocks(r,blocks); var miner=new P.MinerController(r.Program);var hardware=Field(miner,"ship"); Assert.Equal(2,hardware.Cameras.Count);Assert.True(added.Value.Enabled);Assert.True(added.Value.EnableRaycast); Assert.False(foreign.Value.Enabled);Assert.True(hardware.BodyMax.X>25); Assert.True(Field(miner,"healthy")); } [Theory] [InlineData("init")] [InlineData("rescan")] public void ExplicitInitializationUpdatesAddedAndRemovedCameras(string command) { var r=new MinerStateTests.MinerRig();r.State(P.FlightState.Paused); var added=InstalledCamera(r,2);var blocks=TerminalBlocks(r);blocks.Add(added.Value);SupplyBlocks(r,blocks); Assert.Single(r.Hardware.Cameras);r.Miner.Command(command); Assert.Equal(2,r.Hardware.Cameras.Count);Assert.True(added.Value.Enabled);Assert.True(added.Value.EnableRaycast); blocks.RemoveAll(block=>object.ReferenceEquals(block,r.Camera.Value));r.Miner.Command(command); Assert.Same(added.Value,Assert.Single(r.Hardware.Cameras)); } [Fact] public void InitializationRespectsTheConfiguredHardwareGroup() { var r=new MinerStateTests.MinerRig();r.Program.Config=new P.Settings("[Hardware]\nGroup=Managed","miner","test"); var selected=TerminalBlocks(r);var outside=InstalledCamera(r,2);var all=new List(selected) {outside.Value}; SupplyBlocks(r,all,selected);var miner=new P.MinerController(r.Program); Assert.Same(r.Camera.Value,Assert.Single(Field(miner,"ship").Cameras));Assert.False(outside.Value.Enabled); } [Theory] [InlineData(P.FlightState.Returning)] [InlineData(P.FlightState.Holding)] [InlineData(P.FlightState.Paused)] [InlineData(P.FlightState.Manual)] public void HealthyMinerNeverAutomaticallyRediscoversCameras(P.FlightState state) { var r=new MinerStateTests.MinerRig();r.State(state);int scans=0; TestRig.SetBase(r.Program,"GridTerminalSystem",new Stub().Method("GetBlocksOfType",a=>{scans++;return null;}).Value); foreach(double now in new[]{0d,5d,10d,30d}) {r.Program.Now=now;r.Miner.Tick(.1);} Assert.Equal(0,scans);Assert.Same(r.Camera.Value,Assert.Single(r.Hardware.Cameras)); } static ReturnNavigationTests.Rig ViewGapRig() { var r=new ReturnNavigationTests.Rig();r.Ship.Radius=16;r.Ship.BodyHalfSize=new Vector3D(5,5,10); r.Ship.BodyMin=-r.Ship.BodyHalfSize;r.Ship.BodyMax=r.Ship.BodyHalfSize;r.Ship.Cameras.Clear(); r.Ship.Cameras.Add(Camera(r,"Front",1,()=>MatrixD.CreateWorld(Vector3D.Forward*10,Vector3D.Forward,Vector3D.Up)).Value); r.Ship.Cameras.Add(Camera(r,"Rear",2,()=>MatrixD.CreateWorld(Vector3D.Backward*10,Vector3D.Backward,Vector3D.Up)).Value); return r; } [Fact] public void AShortWaypointDoesNotSqueezeTheCameraHorizonAgainstTheNose() { var r=ViewGapRig();int rays=0; foreach(var camera in r.Ship.Cameras)((Stub)RemotingServices.GetRealProxy(camera)).Method("Raycast",a=>{rays++;return default(MyDetectedEntityInfo);}); for(int n=0;n<30 && r.Flight.ScanClearance==0;n++)r.Step(Vector3D.Forward*10); Assert.True(r.HasTranslation);Assert.False(r.Flight.Blocked);Assert.Equal("",r.Flight.Problem);Assert.Equal(18,rays); Assert.Equal(20.2,r.Flight.ScanClearance,8); Assert.Contains(P.L.F(P.L.CameraCoverage,18,1),r.Flight.CameraDiagnostics); } [Fact] public void AnIncompleteCombinedScanDoesNotCountInvisibleRegionsAsScanned() { var r=new ReturnNavigationTests.Rig();r.Flight.SetDockContact(99,Vector3D.Right*100,1); for(int n=0;n<30;n++)r.Step(Vector3D.Right*100); Assert.False(r.HasTranslation);Assert.Equal(0,r.Gyro.Value.Yaw);Assert.Equal(0,r.Flight.ScanClearance); Assert.Equal(0,r.Rays);Assert.Contains(P.L.F(P.L.CameraCoverage,0,0),r.Flight.CameraDiagnostics); } [Fact] public void CombinedScanningStillBrakesForObstaclesInsideTheCurrentLeg() { var r=ViewGapRig();r.Flight.SetDockContact(99,Vector3D.Right*100,1);int hits=0; var camera=(Stub)RemotingServices.GetRealProxy(r.Ship.Cameras[0]); camera.Method("Raycast",a=>{ if(((Vector3D)a[0]).Z<-20) {hits++;return ReturnNavigationTests.Rig.Detection(42);} return default(MyDetectedEntityInfo); }); for(int n=0;n<20 && !r.Flight.Blocked;n++) {r.Step(Vector3D.Forward*10);Assert.False(r.HasTranslation);} Assert.True(r.Flight.Blocked);Assert.Equal(1,hits);Assert.Equal(0,r.Flight.ScanClearance); } [Fact] public void CombinedClearanceDoesNotSurviveARealObstacleOnTheNextSweep() { var r=ViewGapRig();r.Flight.SetDockContact(99,Vector3D.Right*100,1); for(int n=0;n<9;n++)r.Step(Vector3D.Forward*10);Assert.True(r.HasTranslation); ((Stub)RemotingServices.GetRealProxy(r.Ship.Cameras[0])).Method("Raycast",a=>ReturnNavigationTests.Rig.Detection(42)); r.Program.Now+=.4;r.Step(Vector3D.Forward*10); Assert.True(r.Flight.Blocked);Assert.False(r.HasTranslation); } [Fact] public void CombinedClearanceExpiresWhenCamerasCannotRecharge() { var r=ViewGapRig();for(int n=0;n<9;n++)r.Step(Vector3D.Forward*10);Assert.True(r.HasTranslation); foreach(var camera in r.Ship.Cameras)((Stub)RemotingServices.GetRealProxy(camera)).Set("AvailableScanRange",0d); for(int n=0;n<40;n++)r.Step(Vector3D.Forward*10); Assert.False(r.HasTranslation);Assert.True(Field(r.Flight,"scanWaitingForCharge")); } [Fact] public void AnObservedObstaclePastAShortWaypointDoesNotBlockTheCurrentLeg() { var r=ViewGapRig();r.Flight.SetDockContact(99,Vector3D.Right*100,1);int hits=0; ((Stub)RemotingServices.GetRealProxy(r.Ship.Cameras[0])).Method("Raycast",a=>{ if(((Vector3D)a[0]).Z>-25)return default(MyDetectedEntityInfo); hits++;return new MyDetectedEntityInfo(42,"beyond waypoint",MyDetectedEntityType.LargeGrid,Vector3D.Forward*25, MatrixD.Identity,Vector3.Zero,VRage.Game.MyRelationsBetweenPlayerAndBlock.NoOwnership,new BoundingBoxD(new Vector3D(-1),new Vector3D(1)),1); }); for(int n=0;n<30 && r.Flight.ScanClearance==0;n++)r.Step(Vector3D.Forward*10); Assert.True(r.HasTranslation);Assert.Equal(9,hits);Assert.False(r.Flight.Blocked);Assert.Equal(20.2,r.Flight.ScanClearance,8); } [Fact] public void OneSelfObstructedCameraDoesNotInterruptTwoOtherCamerasOrExistingClearance() { var r=new ReturnNavigationTests.Rig();bool blocked=false;int selfHits=0,clearHits=0; r.Hit=()=>{if(blocked) {selfHits++;return ReturnNavigationTests.Rig.Detection(r.Program.Me.CubeGrid.EntityId);}return default(MyDetectedEntityInfo);}; for(int id=2;id<=3;id++)r.Ship.Cameras.Add(r.MakeCamera(id,()=>10000,()=>{clearHits++;return default(MyDetectedEntityInfo);}).Value); for(int n=0;n<9;n++)r.Step(Vector3D.Forward*100);Assert.True(r.HasTranslation); blocked=true; for(int n=0;n<90;n++) { r.Step(Vector3D.Forward*100); Assert.True(r.HasTranslation);Assert.True(r.Flight.ScanClearance>0);Assert.False(r.Flight.Blocked); } Assert.True(selfHits>=18);Assert.True(clearHits>=18); } [Fact] public void ACameraBlockedForOnePointCanStillCoverOtherPoints() { var r=new ReturnNavigationTests.Rig();int recovered=0,alternate=0; r.Hit=()=>ReturnNavigationTests.Rig.Detection(r.Program.Me.CubeGrid.EntityId); r.Ship.Cameras.Add(r.MakeCamera(2,()=>10000,()=>{alternate++;return default(MyDetectedEntityInfo);}).Value); r.Step(Vector3D.Forward*100); Assert.Equal(1,alternate);r.Hit=()=>{recovered++;return default(MyDetectedEntityInfo);}; for(int n=0;n<15 && r.Flight.ScanClearance==0;n++)r.Step(Vector3D.Forward*100); Assert.Equal(17,recovered);Assert.True(r.Flight.ScanClearance>0);Assert.True(r.HasTranslation); } [Fact] public void ThreeFrontCamerasShareRegionsEvenWhenTheCenterCameraIsObstructed() { var r=ViewGapRig();r.Ship.Cameras.Clear();int left=0,right=0,blocked=0; var center=Camera(r,"Center",1,()=>MatrixD.CreateWorld(new Vector3D(0,0,-10),Vector3D.Forward,Vector3D.Up)); center.Method("Raycast",a=>{blocked++;return ReturnNavigationTests.Rig.Detection(r.Program.Me.CubeGrid.EntityId);}); var leftCamera=Camera(r,"Left",2,()=>MatrixD.CreateWorld(new Vector3D(-4,0,-10),Vector3D.Forward,Vector3D.Up)); leftCamera.Method("Raycast",a=>{left++;return default(MyDetectedEntityInfo);}); var rightCamera=Camera(r,"Right",3,()=>MatrixD.CreateWorld(new Vector3D(4,0,-10),Vector3D.Forward,Vector3D.Up)); rightCamera.Method("Raycast",a=>{right++;return default(MyDetectedEntityInfo);}); r.Ship.Cameras.Add(center.Value);r.Ship.Cameras.Add(leftCamera.Value);r.Ship.Cameras.Add(rightCamera.Value); for(int n=0;n<40 && r.Flight.ScanClearance==0;n++)r.Step(Vector3D.Forward*10); Assert.True(r.HasTranslation);Assert.True(blocked>0);Assert.True(left>0);Assert.True(right>0);Assert.Equal(18,left+right); Assert.Contains(P.L.F(P.L.CameraCoverage,18,2),r.Flight.CameraDiagnostics); } } }