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]);
}
}
}