using System; using System.Collections.Generic; using System.Linq; using Sandbox.ModAPI.Ingame; using VRage.Game.GUI.TextPanel; using VRageMath; using Xunit; using P = AutoMiningScript.Program; namespace AutoMiningScript.Tests { public class CarouselTests { static int ConfigurationPageCount { get { return DisplayLocalizationTests.ConfigurationPageCount(4); } } sealed class DashboardRole : P.RoleLogic { public readonly List Fleet = new List(); public readonly List Jobs = new List(); public DashboardRole(P program) : base(program) { } public override List GetTelemetry() { return Fleet; } public override List GetJobs() { return Jobs; } } // Capture actual frames from each surface. Tests deliberately avoid private carousel // fields so they verify what the player sees as telemetry and simulation time change. sealed class Rig { public readonly P Program; public readonly P.Dashboard Dashboard; public readonly DashboardRole Role; public readonly Stub Block; public readonly List[] Frames = { new List(), new List() }; public Rig(string config = "") { Program = TestRig.Program(config); var main = Surface(0); var auxiliary = Surface(1); Block = new Stub() .Set("EntityId", 20L).Set("CubeGrid", Program.Me.CubeGrid) .Set("CustomName", "Programming block").Set("CustomData", config).Set("SurfaceCount", 2) .Method("GetSurface", args => (int)args[0] == 0 ? main.Value : auxiliary.Value); TestRig.SetBase(Program, "Me", Block.Value); TestRig.SetBase(Program, "GridTerminalSystem", new Stub().Value); Dashboard = new P.Dashboard(Program); Role = new DashboardRole(Program); } Stub Surface(int index) { return new Stub().Set("TextureSize", new Vector2(512, 512)) .Set("SurfaceSize", new Vector2(512, 512)) .Method("MeasureStringInPixels", args => new Vector2(args[0].ToString().Sum(c => c >= 0x2e80 ? 32 : 16), 32) * (float)args[2]) .Method("DrawFrame", args => new MySpriteDrawFrame(frame => { Frames[index].Clear(); frame.AddToList(Frames[index]); })); } public void Draw(double now) { Program.Now = now; foreach (var telemetry in Role.Fleet) telemetry.ReceivedAt = now; Dashboard.Draw(Role); } public void Command(double now, string command) { Program.Now = now; Dashboard.Command(command); } public string[] Text(int surface = 0) { return Frames[surface].Where(s => s.Type == SpriteType.TEXT).Select(s => s.Data).ToArray(); } public void AssertConfig(int page, int surface = 0) { Assert.Contains(P.L.F(P.L.DisplayConfigPages, page, ConfigurationPageCount), Text(surface)); } public void Miners(params string[] ids) { foreach (string id in ids) Role.Fleet.Add(new P.Telemetry { Id = id, State = P.FlightState.Ready }); } } [Fact] public void ConfigurationStartsAtFirstSlideAndAutomaticallyWrapsEveryEightSeconds() { var rig = new Rig(); rig.Draw(0); rig.AssertConfig(1); rig.Draw(7.99); rig.AssertConfig(1); for (int page = 2; page <= ConfigurationPageCount; page++) { rig.Draw((page - 1) * 8); rig.AssertConfig(page); } rig.Draw(ConfigurationPageCount * 8); rig.AssertConfig(1); } [Fact] public void FirstVisibleFrameStartsItsOwnFullDwell() { var rig = new Rig(); rig.Draw(100); rig.AssertConfig(1); rig.Draw(107.99); rig.AssertConfig(1); rig.Draw(108); rig.AssertConfig(2); } [Fact] public void LongRenderingPauseAdvancesOnlyOneSlideAndRestartsDwell() { var rig = new Rig(); rig.Draw(0); rig.Draw(60); rig.AssertConfig(2); rig.Draw(60); rig.AssertConfig(2); rig.Draw(67.99); rig.AssertConfig(2); rig.Draw(68); rig.AssertConfig(3); } [Fact] public void DisabledCarouselStillAllowsManualPaging() { var rig = new Rig("[Display]\nAutoPage=false\n"); rig.Draw(0); rig.Draw(90); rig.AssertConfig(1); rig.Command(90, "page +"); rig.Draw(90); rig.AssertConfig(2); rig.Draw(200); rig.AssertConfig(2); } [Theory] [InlineData(false, true, 2)] [InlineData(true, false, 1)] public void ScreenAutoPageSettingOverridesGlobalSetting(bool global, bool screen, int expected) { var rig = new Rig("[Display]\nAutoPage=" + global + "\n[AMS.Screen]\nAutoPage=" + screen + "\n"); rig.Draw(0); rig.Draw(8); rig.AssertConfig(expected); } [Fact] public void SurfacesKeepIndependentIntervalsAndSlideCursors() { var rig = new Rig("[Display]\nPageInterval=10\n[AMS.Screen]\nPageInterval=4\n[AMS.Screen.1]\nEnabled=true\nPage=config\n"); rig.Draw(0); Assert.Equal(2, rig.Dashboard.ScreenCount); rig.AssertConfig(1); rig.AssertConfig(1, 1); rig.Draw(4); rig.AssertConfig(2); rig.AssertConfig(1, 1); rig.Draw(8); rig.AssertConfig(3); rig.AssertConfig(1, 1); rig.Draw(10); rig.AssertConfig(3); rig.AssertConfig(2, 1); } [Fact] public void ManualPagingHoldsThenRestartsACompleteInterval() { var rig = new Rig(); rig.Draw(0); rig.Command(5, "page +"); rig.Draw(5); rig.AssertConfig(2); rig.Draw(24.99); rig.AssertConfig(2); rig.Draw(25); rig.AssertConfig(2); rig.Draw(32.99); rig.AssertConfig(2); rig.Draw(33); rig.AssertConfig(3); } [Fact] public void RepeatedManualInputRenewsTheReadingPause() { var rig = new Rig(); rig.Draw(0); rig.Command(5, "page +"); rig.Draw(5); rig.Command(20, "page +"); rig.Draw(20); rig.AssertConfig(3); rig.Draw(47.99); rig.AssertConfig(3); rig.Draw(48); rig.AssertConfig(4); } [Theory] [InlineData(0, 8)] [InlineData(4, 12)] public void PerSurfaceManualHoldOverridesGlobalDefault(int hold, int nextAdvance) { var rig = new Rig("[AMS.Screen]\nManualHoldSeconds=" + hold + "\n"); rig.Draw(0); rig.Command(0, "page +"); rig.Draw(0); rig.Draw(nextAdvance - .01); rig.AssertConfig(2); rig.Draw(nextAdvance); rig.AssertConfig(3); } [Fact] public void ManualPreviousWrapsAndCommandsBeforeDiscoveryAreRetained() { var rig = new Rig(); rig.Command(0, "page -"); rig.Draw(0); rig.AssertConfig(ConfigurationPageCount); rig.Command(1, "page +"); rig.Draw(1); rig.AssertConfig(1); } [Fact] public void StopFreezesTheSlideAndPlayResumesWithFreshDwell() { var rig = new Rig(); rig.Draw(0); rig.Draw(8); rig.AssertConfig(2); rig.Command(9, "page stop"); rig.Draw(80); rig.AssertConfig(2); rig.Command(81, "page play"); rig.Draw(81); rig.AssertConfig(2); rig.Draw(88.99); rig.AssertConfig(2); rig.Draw(89); rig.AssertConfig(3); } [Fact] public void PlayOverridesDisabledConfigurationAndAutoRestoresIt() { var rig = new Rig("[Display]\nAutoPage=false\n"); rig.Draw(0); rig.Command(1, "page play"); rig.Draw(1); rig.Draw(9); rig.AssertConfig(2); rig.Command(10, "page auto"); rig.Draw(10); rig.AssertConfig(1); rig.Draw(500); rig.AssertConfig(1); } [Fact] public void AutoRestoresScreenBindingAfterManualPageSelection() { var rig = new Rig(); rig.Command(0, "page fleet"); rig.Draw(0); Assert.Contains(P.L.DisplayFleet, rig.Text()); rig.Command(1, "page auto"); rig.Draw(1); rig.AssertConfig(1); rig.Draw(8.99); rig.AssertConfig(1); rig.Draw(9); rig.AssertConfig(2); } [Theory] [InlineData("next", "fleet")] [InlineData("prev", "diagnostics")] public void NamedPageNavigationIsRelativeToConfiguredPage(string direction, string expected) { var rig = new Rig(); rig.Command(0, "page " + direction); rig.Draw(0); Assert.Contains(expected == "fleet" ? P.L.DisplayFleet : P.L.DisplayDiagnostics, rig.Text()); Assert.DoesNotContain(P.L.DisplayConfiguration, rig.Text()); } [Fact] public void AutomaticPagingDoesNotChangePageTypeOrExplicitMinerBinding() { var rig = new Rig("[Display]\nBuiltInPage=miner\n[AMS.Screen]\nMiner=beta\n"); rig.Miners("alpha", "beta"); foreach (double now in new[] { 0d, 8d, 16d, 60d }) { rig.Draw(now); Assert.Contains("beta", rig.Text()); Assert.DoesNotContain("alpha", rig.Text()); Assert.Contains(P.L.DisplayMiner, rig.Text()); Assert.DoesNotContain(P.L.DisplayConfiguration, rig.Text()); } } [Fact] public void UnboundMinerViewRotatesThroughSortedTelemetry() { var rig = new Rig("[Display]\nBuiltInPage=miner\n"); rig.Miners("beta", "alpha"); rig.Draw(0); Assert.Contains("alpha", rig.Text()); rig.Draw(8); Assert.Contains("beta", rig.Text()); rig.Draw(16); Assert.Contains("alpha", rig.Text()); } [Fact] public void FleetPageRotatesRowsAndClampsWhenTelemetryCountShrinks() { var rig = new Rig("[Display]\nBuiltInPage=fleet\n"); rig.Miners(Enumerable.Range(1, 16).Select(n => "miner-" + n.ToString("00")).ToArray()); rig.Draw(0); Assert.Contains(rig.Text(), text => text.StartsWith("miner-01 ")); rig.Draw(8); Assert.Contains(rig.Text(), text => text.StartsWith("miner-08 ")); rig.Draw(16); Assert.Contains(rig.Text(), text => text.StartsWith("miner-15 ")); rig.Role.Fleet.RemoveRange(7, 9); rig.Draw(17); Assert.Contains(rig.Text(), text => text.StartsWith("miner-01 ")); rig.Draw(40); Assert.Contains(rig.Text(), text => text.StartsWith("miner-01 ")); } [Fact] public void MiningMapRotatesDistinctRegionsInsteadOfIndividualJobs() { var rig = new Rig("[Display]\nBuiltInPage=map\n"); rig.Role.Jobs.Add(new P.Job { Id = "a1", RegionId = "region-a" }); rig.Role.Jobs.Add(new P.Job { Id = "a2", RegionId = "region-a" }); rig.Role.Jobs.Add(new P.Job { Id = "b1", RegionId = "region-b" }); rig.Draw(0); Assert.Contains(rig.Text(), text => text.StartsWith("region-a ")); rig.Draw(8); Assert.Contains(rig.Text(), text => text.StartsWith("region-b ")); rig.Draw(16); Assert.Contains(rig.Text(), text => text.StartsWith("region-a ")); } [Fact] public void RediscoveryPreservesCurrentSlideAndItsRemainingDwell() { var rig = new Rig(); rig.Draw(0); rig.Draw(8); rig.Draw(16); rig.Draw(24); rig.AssertConfig(4); rig.Draw(30); rig.AssertConfig(4); rig.Draw(31.99); rig.AssertConfig(4); rig.Draw(32); rig.AssertConfig(5); rig.Draw(39.99); rig.AssertConfig(5); rig.Draw(40); rig.AssertConfig(1); } [Fact] public void RediscoveryAdoptsChangedBindingWithFreshFirstSlide() { var rig = new Rig("[AMS.Screen]\nPage=fleet\n"); rig.Draw(0); rig.Block.Set("CustomData", "[AMS.Screen]\nPage=config\n"); rig.Draw(30); rig.AssertConfig(1); rig.Draw(37.99); rig.AssertConfig(1); rig.Draw(38); rig.AssertConfig(2); } [Fact] public void SinglePageDataRemainsStableWithoutCarouselDots() { var rig = new Rig("[Display]\nBuiltInPage=miner\n"); rig.Miners("alpha"); foreach (double now in new[] { 0d, 8d, 80d }) { rig.Draw(now); Assert.Contains("alpha", rig.Text()); Assert.DoesNotContain(rig.Frames[0], s => s.Type == SpriteType.TEXTURE && s.Data == "Circle"); } } [Fact] public void MultipleConfigurationSlidesHaveGraphicalCarouselIndicators() { var rig = new Rig(); rig.Draw(0); var dots = rig.Frames[0].Where(s => s.Type == SpriteType.TEXTURE && s.Data == "Circle").ToArray(); Assert.Equal(ConfigurationPageCount, dots.Length); Assert.All(dots, s => Assert.InRange(s.Position.Value.Y, 470, 512)); var initialColors = dots.Select(s => s.Color).ToArray(); rig.Draw(8); var nextColors = rig.Frames[0].Where(s => s.Type == SpriteType.TEXTURE && s.Data == "Circle").Select(s => s.Color).ToArray(); Assert.NotEqual(initialColors[0], nextColors[0]); Assert.Equal(initialColors[0], nextColors[1]); } } }