using Eu07.BlackBox;
namespace StarterNG.Tests.BlackBox;
///
/// A journey of two timetables one after another: the stations, the horn points, the train
/// protection, the camera and the couplings, as the simulator writes them, and what the
/// recording tells of them.
///
public class BlackBoxJourneyTests
{
private const uint Head = 1, Car = 2, Tail = 3, Stranger = 9;
private const byte Coupled = 0x83;
///
/// The user in vehicle 1 of a unit 1-2-3, the clock at 10:34 (634 min). ROJ1 stands at A, stops at B
/// at t = 60 until 70, passes C at 100 without stopping; ROJ2 from t = 110 stops at D at 115.
///
private static Recording Journey()
{
var f = new BlackBoxFile().Info()
.StopPoint(0, "Stacja_B#1", 1300, 10, 150)
.StopPoint(1, "Stacja_B#2", 1310, 10, 150)
.HornPoint(0, "w6_1", 1200)
.User(0, Head, true, -1)
.Coupling(0, Head, 1, Car, 0, Coupled, 0, 0)
.Coupling(0, Car, 1, Tail, 0, Coupled, 0, 0)
.Camera(0, 0, Head, 1150)
.Timetable(0, Head, "ROJ1", "A", "C", ("A", -1, 634), ("Stacja_B", 635, 635.5f), ("C", 637.5f, 638))
.Station(1, Head, 4, 1, "A", 1150);
for (double t = 0; t <= 130; t += 0.5)
{
float speed = t is >= 55 and < 70 || t >= 112 && t < 120 ? 0 : 40;
uint devices = 0x0007 | (t is >= 22 and < 24 ? 0x0200u : 0);
byte security = (byte)(t is >= 30 and < 32 ? 0x11 : 0x01);
f.Cab(t, Head, 1150 + t * 10, speed, devices: devices, security: security, lateral: t is >= 45 and < 48 ? 1.2f : 0.1f);
}
f.HornRequest(20, Head, 1200)
.HornRequest(40, Head, 1550)
.Camera(50, 2, 0, 1650)
.Station(60, Head, 1, 2, "Stacja_B#2", 1300)
.Station(60, Stranger, 1, 2, "Stacja_B#1", 9000)
.Coupling(80, Car, 1, 0, 0, 0, Coupled, Tail)
.Station(100, Head, 3, 3, "C", 2150)
.Timetable(110, Head, "ROJ2", "C", "D", ("C", -1, 638), ("D", 640, -1))
.Station(115, Head, 1, 2, "D", 2300);
return BlackBoxReader.Read(f.End(130).Stream());
}
[Fact]
public void The_new_records_are_read_field_by_field()
{
var r = Journey();
Assert.Equal(0, r.Unknown);
Assert.Equal(0, r.Malformed);
Assert.Equal(2, r.StopPoints.Count);
Assert.Equal("Stacja_B#2", r.StopPoints[1].Station);
Assert.Equal(10f, r.StopPoints[1].StopAt);
Assert.Equal(150f, r.StopPoints[1].Platform);
Assert.Equal("w6_1", Assert.Single(r.HornPoints).Name);
Assert.Equal(2, r.Timetables.Count);
Assert.Equal("ROJ2", r.Timetables[1].Train);
Assert.Equal(640f, r.Timetables[1].Row(2)!.Arrival);
Assert.False(r.Timetables[1].Row(2)!.Maintenance);
Assert.Equal(5, r.Stations.Count);
Assert.Equal(StationKind.Missed, r.Stations.Single(s => s.Row == 3 && s.Vehicle == Head).Kind);
Assert.Equal(2, r.HornRequests.Count);
Assert.Equal(2, r.Cameras.Count);
Assert.Equal(CameraMode.Free, r.Cameras[1].Mode);
Assert.Equal(-0.1f, r.Cameras[1].Pitch);
Assert.Equal(3, r.Couplings.Count);
Assert.True(r.Couplings[2].Uncoupled);
Assert.Equal(-1, Assert.Single(r.UserVehicles).CabEnd);
var cab = r.Cab[0];
Assert.Equal(1, cab.CabEnd);
Assert.Equal(SecurityFlags.Vigilance, cab.Security);
Assert.Equal(0.1f, cab.LateralAcceleration);
Assert.Equal(Lights.HeadlightLeft | Lights.HeadlightUpper | Lights.HeadlightRight, cab.LightsHead);
}
[Fact]
public void A_cab_written_before_the_protection_and_the_lateral_acceleration_is_still_read()
{
var r = BlackBoxReader.Read(new BlackBoxFile().Info().Cab(0, Head, 1150, 40, extended: false).User(0, Head, true).End(1).Stream());
var cab = Assert.Single(r.Cab);
Assert.Equal(0, cab.CabEnd);
Assert.Equal(SecurityFlags.None, cab.Security);
Assert.Equal(0f, cab.LateralAcceleration);
Assert.Equal(0, Assert.Single(r.UserVehicles).CabEnd);
Assert.Equal(0, r.Malformed);
}
[Fact]
public void The_couplings_tell_the_train_at_any_moment()
{
var r = Journey();
Assert.Equal(new HashSet { 1, 2, 3 }, r.ConsistAt((int)Head, 79));
Assert.Equal(new HashSet { 1, 2 }, r.ConsistAt((int)Head, 81));
Assert.Equal(new HashSet { 3 }, r.ConsistAt((int)Tail, 81));
// What the trains were made of at the start is no coupling done.
var change = Assert.Single(r.CouplingEvents());
Assert.Equal(80, change.Time);
Assert.Single(r.UserCouplings());
}
[Fact]
public void The_stations_of_someone_else_s_train_are_not_the_user_s()
{
var r = Journey();
Assert.DoesNotContain(r.UserStations(), s => s.Vehicle == Stranger);
Assert.Equal(4, r.UserStations().Count());
}
[Fact]
public void The_timetable_shown_is_the_one_the_train_had_then()
{
var r = Journey();
Assert.Equal("ROJ1", r.TimetableAt(100)!.Train);
Assert.Equal("ROJ2", r.TimetableAt(110)!.Train);
var first = StationProgress.Of(r, 50);
Assert.Equal(3, first.Count);
Assert.Equal(StationOutcome.Stopped, first[1].Outcome);
Assert.Equal(60, first[1].ArrivedAt);
Assert.Equal(70, first[1].DepartedAt);
Assert.Equal(0, first[1].ArrivalDelay!.Value, 2);
Assert.Equal(-1 / 3.0, first[1].DepartureDelay!.Value, 2);
Assert.Equal(StationOutcome.Missed, first[2].Outcome);
Assert.Equal(StationOutcome.Stopped, StationProgress.Of(r, 120)[1].Outcome);
}
[Fact]
public void The_delay_follows_the_train_from_station_to_station()
{
var r = Journey();
Assert.Null(StationProgress.DelayAt(r, 0.5));
var standing = StationProgress.DelayAt(r, 65)!;
Assert.True(standing.Standing);
Assert.Equal("Stacja_B", standing.Station);
Assert.Equal(0, standing.Minutes, 2);
var left = StationProgress.DelayAt(r, 80)!;
Assert.False(left.Standing);
Assert.Equal(-1 / 3.0, left.Minutes, 2);
// At D, arrived at 10:35:55 for 10:40: early, and nothing to depart by.
var second = StationProgress.DelayAt(r, 118)!;
Assert.Equal("ROJ2", second.Train);
Assert.True(second.Minutes < -4);
// Left B at 10:35:10, C planned 10:37:30: by 10:35:50 (t = 110 of ROJ1's time) not yet overdue.
Assert.Equal(-1 / 3.0, StationProgress.DelayAt(r, 90)!.Minutes, 2);
var delays = StationProgress.StationDelays(r, 80);
Assert.Equal(-1 / 3.0, delays["Stacja B"], 2);
}
[Fact]
public void The_names_of_the_stations_are_shown_without_what_tells_their_stop_points_apart()
{
Assert.Equal("Bałtyk Główny", StationNames.Display("Bałtyk_Główny#2"));
Assert.Equal("Turów", StationNames.Display("Turów"));
Assert.Equal("Psie Pole po", StationNames.Display("Psie_Pole_po#"));
}
[Fact]
public void The_horn_counts_when_it_sounded_after_the_horn_point()
{
var checks = Journey().UserHornChecks().ToList();
Assert.Equal(2, checks.Count);
Assert.True(checks[0].Sounded);
Assert.False(checks[1].Sounded);
}
[Fact]
public void The_camera_is_known_from_its_first_record()
{
var r = Journey();
Assert.Null(r.CameraAt(-1));
Assert.Equal(CameraMode.Cab, r.CameraAt(49)!.Value.Mode);
Assert.Equal(CameraMode.Free, r.CameraAt(51)!.Value.Mode);
}
[Fact]
public void The_summary_counts_the_stops_of_every_timetable_the_horns_and_the_protection()
{
var summary = Journey().Summarize();
Assert.Equal(2, summary.Stops);
Assert.Equal(1, summary.StopsMissed);
Assert.Equal(2, summary.HornPoints);
Assert.Equal(1, summary.HornsMissed);
Assert.Equal(1, summary.SecurityBrakings);
// Taken over half a second, as the lateral acceleration jumps from sample to sample.
Assert.Equal(1.2f, summary.MaxLateralAcceleration, 3);
}
}
public class TrainDelayTests
{
[Fact]
public void A_train_held_on_the_way_grows_late_by_the_time_of_the_next_station()
{
// Clock 10:34 at t = 0. Departs A at 10:35 (t = 60) on time, held short of B planned at 10:37.
var f = new BlackBoxFile().Info()
.Timetable(0, 1, "ROJ9", "A", "B", ("A", -1, 635), ("B", 637, 638))
.Station(1, 1, 1, 1, "A", 1150);
for (double t = 0; t <= 400; t += 0.5)
f.Cab(t, 1, 1150 + Math.Max(0, Math.Min(t, 90) - 60) * 10, t is > 60 and < 90 ? 40 : 0);
var r = BlackBoxReader.Read(f.End(400).Stream());
Assert.Equal(0, StationProgress.DelayAt(r, 100)!.Minutes, 1);
// 10:40 at t = 360: three minutes past B's arrival, and not there.
var held = StationProgress.DelayAt(r, 360)!;
Assert.False(held.Standing);
Assert.Equal(3, held.Minutes, 2);
}
}
/// A train given its next timetable, one recording holding both.
public class NextTimetableTests
{
[Fact]
public void A_station_written_just_before_the_next_timetable_goes_with_it()
{
// ROJ1 stops at A and ends at B; at B it is given ROJ2, which starts there. A recording made before
// the simulator wrote the timetable first has B of ROJ2 half a second ahead of ROJ2 itself (here left behind,
// which on ROJ1 would mark A missed).
var f = new BlackBoxFile().Info()
.Timetable(0, 1, "ROJ1", "A", "B", ("A", -1, 635), ("B", 637, -1))
.Station(1, 1, 1, 1, "A", 1150)
.Station(50, 1, 1, 2, "B", 1150)
.Station(99.5, 1, 3, 1, "B", 1150)
.Timetable(100, 1, "ROJ2", "B", "C", ("B", -1, 640), ("C", 645, -1));
for (double t = 0; t <= 120; t += 0.5)
f.Cab(t, 1, 1150, 0);
var r = BlackBoxReader.Read(f.End(120).Stream());
var first = StationProgress.Of(r, r.Timetables[0]);
Assert.Equal(StationOutcome.Stopped, first[0].Outcome);
Assert.Equal(1, first[0].Event!.Time);
var second = StationProgress.Of(r, r.Timetables[1]);
Assert.Equal(StationOutcome.Missed, second[0].Outcome);
Assert.Equal(99.5, second[0].Event!.Time);
}
}