using System.Linq; using Eu07.BlackBox; namespace StarterNG.Tests.BlackBox; /// The light signals at the head and the tail of the user's train, against those it was to show. public class HeadCodeTests { [Theory] [InlineData(Lights.HeadlightLeft | Lights.HeadlightRight | Lights.HeadlightUpper, HeadCode.Pc1)] [InlineData(Lights.HeadlightLeft | Lights.HeadlightRight | Lights.HeadlightUpper | Lights.HighBeamLeft | Lights.HighBeamRight, HeadCode.Pc1)] [InlineData(Lights.RedMarkerLeft | Lights.HeadlightRight | Lights.HeadlightUpper, HeadCode.Pc2)] [InlineData(Lights.RedMarkerLeft | Lights.RedMarkerRight, HeadCode.Pc5)] [InlineData(Lights.RearEndSignals, HeadCode.Pc5)] [InlineData(Lights.HeadlightRight, HeadCode.Tb1)] [InlineData(Lights.HeadlightLeft, HeadCode.Tb1)] [InlineData(Lights.None, HeadCode.Off)] [InlineData(Lights.HeadlightLeft | Lights.HeadlightRight, HeadCode.Other)] public void The_lamps_lit_tell_the_signal(Lights lights, HeadCode code) => Assert.Equal(code, HeadCodes.Of(lights)); private const ushort Pc1 = 0x15, Pc5 = 0x22, Tb1 = 0x10; private static Recording Drive(params (double From, double To, ushort Head, ushort Tail, byte Movement, short HintHead)[] stretches) { var f = new BlackBoxFile().Info(); foreach (var (from, to, head, tail, movement, hint) in stretches) for (double t = from; t < to; t += 0.5) f.Cab(t, 1, 1000 + t * 10, 40, lightsHead: head, lightsTail: tail, movement: movement, hintHead: hint); return BlackBoxReader.Read(f.End(stretches[^1].To).Stream()); } [Fact] public void A_train_run_is_to_show_Pc1_and_Pc5() { var summary = Drive((0, 60, Pc1, Pc5, 1, -1)).Summarize(); Assert.True(summary.LightsJudgedSeconds > 55); Assert.Equal(0, summary.WrongLightsSeconds); Assert.Empty(summary.LightsFaults); } [Fact] public void Shunting_lights_on_a_train_run_count_as_wrong_once_for_the_whole_stretch() { var summary = Drive((0, 30, Pc1, Pc5, 1, -1), (30, 60, Tb1, Tb1, 1, -1), (60, 90, Pc1, Pc5, 1, -1)).Summarize(); Assert.Equal(30, summary.WrongLightsSeconds, 0); var fault = Assert.Single(summary.LightsFaults); Assert.Equal(HeadCode.Tb1, fault.Head); Assert.Equal(HeadCode.Pc1, fault.HeadRequired); Assert.True(fault.HeadWrong && fault.TailWrong); } [Fact] public void Shunting_is_to_show_Tb1() { var summary = Drive((0, 30, Tb1, Tb1, 2, -1), (30, 40, Pc1, Pc5, 2, -1)).Summarize(); var fault = Assert.Single(summary.LightsFaults); Assert.Equal(HeadCode.Tb1, fault.HeadRequired); } [Fact] public void The_pattern_the_timetable_asks_for_is_the_one_to_show() { // Pc 2 asked for at the head: the left red marker, the right headlight and the upper one. const short pc2 = 0x16; var summary = Drive((0, 30, Pc1, Pc5, 1, pc2)).Summarize(); var fault = Assert.Single(summary.LightsFaults); Assert.Equal(HeadCode.Pc2, fault.HeadRequired); Assert.False(fault.TailWrong); } [Fact] public void An_older_recording_tells_nothing_of_the_signals() { var f = new BlackBoxFile().Info(); for (double t = 0; t < 30; t += 0.5) f.Cab(t, 1, 1000 + t * 10, 40, lightsHead: Tb1); var summary = BlackBoxReader.Read(f.End(30).Stream()).Summarize(); Assert.Equal(0, summary.LightsJudgedSeconds); } } /// The user going over to another vehicle: the timetable of the train is the one of the train the user is in. public class UserVehicleTests { private const byte Coupled = 0x83; [Fact] public void Another_vehicle_of_the_same_train_keeps_its_timetable_another_train_has_none() { // The user in vehicle 1 of the unit 1-2 with ROJ1; at t = 50 over to the other cab, at t = 100 into vehicle 9, on its own. var f = new BlackBoxFile().Info() .Coupling(0, 1, 1, 2, 0, Coupled, 0, 0) .Timetable(0, 1, "ROJ1", "A", "B", ("A", -1, 635), ("B", 640, -1)); for (double t = 0; t < 150; t += 0.5) f.Cab(t, t < 50 ? 1u : t < 100 ? 2u : 9u, 1000, 0); var r = BlackBoxReader.Read(f.End(150).Stream()); Assert.Equal("ROJ1", r.TimetableAt(20)!.Train); Assert.Equal("ROJ1", r.TimetableAt(70)!.Train); Assert.Null(r.TimetableAt(120)); } } /// The departures and the platforms in the summary. public class StationSummaryTests { [Fact] public void The_summary_tells_the_largest_delay_leaving_and_the_stops_off_the_platform() { // Clock 10:34 at t = 0, leaves A at once. Stops at B (10:36 to 10:37) from t = 120, leaves at t = 240 (10:38): a minute late. var f = new BlackBoxFile().Info() .Timetable(0, 1, "ROJ1", "A", "C", ("A", -1, 634), ("B", 636, 637), ("C", 640, -1)) .Station(1, 1, 1, 1, "A", 1000) .Station(120, 1, 1, 2, "Stacja_B", 1000, distance: 74.7f, trainLength: 129.8f, platform: 201); for (double t = 0; t < 300; t += 0.5) f.Cab(t, 1, 1000, t is >= 120 and < 240 or < 2 ? 0 : 40); var r = BlackBoxReader.Read(f.End(300).Stream()); var summary = r.Summarize(); Assert.Equal(1, summary.MaxDepartureDelayMinutes!.Value, 1); var off = Assert.Single(summary.OffPlatform); Assert.Equal("B", off.Station); Assert.Equal(3.5, off.Metres, 1); } }