diff --git a/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java b/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java index fa1af949bd3..cfe080c47dc 100644 --- a/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java +++ b/src/ext-test/java/org/opentripplanner/ext/siri/SiriTimetableSnapshotSourceTest.java @@ -13,7 +13,6 @@ import org.junit.jupiter.api.Test; import org.opentripplanner.DateTimeHelper; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; -import org.opentripplanner.model.PickDrop; import org.opentripplanner.model.StopTime; import org.opentripplanner.model.calendar.CalendarServiceData; import org.opentripplanner.transit.model._data.TransitModelForTest; @@ -29,6 +28,7 @@ import org.opentripplanner.transit.model.timetable.TripOnServiceDate; import org.opentripplanner.transit.model.timetable.TripTimes; import org.opentripplanner.transit.model.timetable.TripTimesFactory; +import org.opentripplanner.transit.model.timetable.TripTimesStringBuilder; import org.opentripplanner.transit.service.DefaultTransitService; import org.opentripplanner.transit.service.StopModel; import org.opentripplanner.transit.service.TransitModel; @@ -73,10 +73,11 @@ void testAddJourney() { var result = env.applyEstimatedTimetable(updates); assertEquals(1, result.successful()); - var tripTimes = env.getTripTimesForTrip("newJourney"); - assertEquals(RealTimeState.ADDED, tripTimes.getRealTimeState()); - assertEquals(2 * 60, tripTimes.getDepartureTime(0)); - assertEquals(4 * 60, tripTimes.getDepartureTime(1)); + assertEquals("ADDED | C1 [R] 0:02 0:02 | D1 0:04 0:04", env.getRealtimeTimetable("newJourney")); + assertEquals( + "SCHEDULED | C1 0:01 0:01 | D1 0:03 0:03", + env.getScheduledTimetable("newJourney") + ); } @Test @@ -98,14 +99,13 @@ void testReplaceJourney() { assertEquals(1, result.successful()); - var tripTimes = env.getTripTimesForTrip("newJourney"); - var pattern = env.getPatternForTrip(env.id("newJourney")); - assertEquals(RealTimeState.ADDED, tripTimes.getRealTimeState()); - assertEquals(2 * 60, tripTimes.getDepartureTime(0)); - assertEquals(4 * 60, tripTimes.getDepartureTime(1)); - assertEquals(env.stopA1.getId(), pattern.getStop(0).getId()); - assertEquals(env.stopC1.getId(), pattern.getStop(1).getId()); + assertEquals("ADDED | A1 [R] 0:02 0:02 | C1 0:04 0:04", env.getRealtimeTimetable("newJourney")); + assertEquals( + "SCHEDULED | A1 0:01 0:01 | C1 0:03 0:03", + env.getScheduledTimetable("newJourney") + ); + // Original trip should not get canceled var originalTripTimes = env.getTripTimesForTrip(env.trip1); assertEquals(RealTimeState.SCHEDULED, originalTripTimes.getRealTimeState()); } @@ -123,6 +123,10 @@ void testUpdateJourneyWithDatedVehicleJourneyRef() { var result = env.applyEstimatedTimetable(updates); assertEquals(1, result.successful()); assertTripUpdated(env); + assertEquals( + "UPDATED | A1 0:00:15 0:00:15 | B1 0:00:25 0:00:25", + env.getRealtimeTimetable(env.trip1) + ); } /** @@ -152,7 +156,7 @@ void testUpdateJourneyWithoutJourneyRef() { var updates = updatedJourneyBuilder(env).buildEstimatedTimetableDeliveries(); var result = env.applyEstimatedTimetable(updates); assertEquals(0, result.successful()); - assertFailure(result, UpdateError.UpdateErrorType.TRIP_NOT_FOUND); + assertFailure(UpdateError.UpdateErrorType.TRIP_NOT_FOUND, result); } /** @@ -191,7 +195,7 @@ void testUpdateJourneyWithFuzzyMatchingAndMissingAimedDepartureTime() { var result = env.applyEstimatedTimetableWithFuzzyMatcher(updates); assertEquals(0, result.successful(), "Should fail gracefully"); - assertFailure(result, UpdateError.UpdateErrorType.NO_FUZZY_TRIP_MATCH); + assertFailure(UpdateError.UpdateErrorType.NO_FUZZY_TRIP_MATCH, result); } /** @@ -214,15 +218,10 @@ void testChangeQuay() { var result = env.applyEstimatedTimetable(updates); assertEquals(1, result.successful()); - - var pattern = env.getPatternForTrip(env.trip1.getId()); - var tripTimes = env.getTripTimesForTrip(env.trip1); - assertEquals(RealTimeState.MODIFIED, tripTimes.getRealTimeState()); - assertEquals(11, tripTimes.getScheduledDepartureTime(0)); - assertEquals(15, tripTimes.getDepartureTime(0)); - assertEquals(20, tripTimes.getScheduledArrivalTime(1)); - assertEquals(33, tripTimes.getArrivalTime(1)); - assertEquals(env.stopB2, pattern.getStop(1)); + assertEquals( + "MODIFIED | A1 [R] 0:00:15 0:00:15 | B2 0:00:33 0:00:33", + env.getRealtimeTimetable(env.trip1) + ); } @Test @@ -245,12 +244,10 @@ void testCancelStop() { var result = env.applyEstimatedTimetable(updates); assertEquals(1, result.successful()); - - var pattern = env.getPatternForTrip(env.trip2.getId()); - - assertEquals(PickDrop.SCHEDULED, pattern.getAlightType(0)); - assertEquals(PickDrop.CANCELLED, pattern.getAlightType(1)); - assertEquals(PickDrop.SCHEDULED, pattern.getAlightType(2)); + assertEquals( + "MODIFIED | A1 0:01:01 0:01:01 | B1 [C] 0:01:10 0:01:11 | C1 0:01:30 0:01:30", + env.getRealtimeTimetable(env.trip2) + ); } // TODO: support this @@ -278,23 +275,10 @@ void testAddStop() { var result = env.applyEstimatedTimetable(updates); assertEquals(1, result.successful()); - - var pattern = env.getPatternForTrip(env.trip1.getId()); - var tripTimes = env.getTripTimesForTrip(env.trip1); - assertEquals(RealTimeState.MODIFIED, tripTimes.getRealTimeState()); - assertEquals(11, tripTimes.getScheduledDepartureTime(0)); - assertEquals(15, tripTimes.getDepartureTime(0)); - - // Should it work to get the scheduled times from an extra call? - assertEquals(19, tripTimes.getScheduledArrivalTime(1)); - assertEquals(24, tripTimes.getScheduledDepartureTime(1)); - - assertEquals(20, tripTimes.getDepartureTime(1)); - assertEquals(25, tripTimes.getDepartureTime(1)); - - assertEquals(20, tripTimes.getScheduledArrivalTime(2)); - assertEquals(33, tripTimes.getArrivalTime(2)); - assertEquals(List.of(env.stopA1, env.stopD1, env.stopB1), pattern.getStops()); + assertEquals( + "MODIFIED | A1 0:00:15 0:00:15 | D1 [C] 0:00:20 0:00:25 | B1 0:00:33 0:00:33", + env.getRealtimeTimetable(env.trip1) + ); } ///////////////// @@ -311,7 +295,7 @@ void testNotMonitored() { var result = env.applyEstimatedTimetable(updates); - assertFailure(result, UpdateError.UpdateErrorType.NOT_MONITORED); + assertFailure(UpdateError.UpdateErrorType.NOT_MONITORED, result); } @Test @@ -336,7 +320,7 @@ void testReplaceJourneyWithoutEstimatedVehicleJourneyCode() { var result = env.applyEstimatedTimetable(updates); // TODO: this should have a more specific error type - assertFailure(result, UpdateError.UpdateErrorType.UNKNOWN); + assertFailure(UpdateError.UpdateErrorType.UNKNOWN, result); } @Test @@ -356,7 +340,7 @@ void testNegativeHopTime() { var result = env.applyEstimatedTimetable(updates); - assertFailure(result, UpdateError.UpdateErrorType.NEGATIVE_HOP_TIME); + assertFailure(UpdateError.UpdateErrorType.NEGATIVE_HOP_TIME, result); } @Test @@ -379,7 +363,7 @@ void testNegativeDwellTime() { var result = env.applyEstimatedTimetable(updates); - assertFailure(result, UpdateError.UpdateErrorType.NEGATIVE_DWELL_TIME); + assertFailure(UpdateError.UpdateErrorType.NEGATIVE_DWELL_TIME, result); } // TODO: support this @@ -405,30 +389,29 @@ void testExtraUnknownStop() { var result = env.applyEstimatedTimetable(updates); - assertFailure(result, UpdateError.UpdateErrorType.INVALID_STOP_SEQUENCE); + assertFailure(UpdateError.UpdateErrorType.INVALID_STOP_SEQUENCE, result); } - private void assertFailure(UpdateResult result, UpdateError.UpdateErrorType errorType) { - assertEquals(result.failures().keySet(), Set.of(errorType)); + private void assertFailure(UpdateError.UpdateErrorType expectedError, UpdateResult result) { + assertEquals(Set.of(expectedError), result.failures().keySet()); } private static SiriEtBuilder updatedJourneyBuilder(RealtimeTestEnvironment env) { return new SiriEtBuilder(env.getDateTimeHelper()) - .withRecordedCalls(builder -> - builder.call(env.stopA1).departAimedActual("00:00:11", "00:00:15") - ) .withEstimatedCalls(builder -> - builder.call(env.stopB1).arriveAimedExpected("00:00:20", "00:00:25") + builder + .call(env.stopA1) + .departAimedExpected("00:00:11", "00:00:15") + .call(env.stopB1) + .arriveAimedExpected("00:00:20", "00:00:25") ); } private static void assertTripUpdated(RealtimeTestEnvironment env) { - var tripTimes = env.getTripTimesForTrip(env.trip1); - assertEquals(RealTimeState.UPDATED, tripTimes.getRealTimeState()); - assertEquals(11, tripTimes.getScheduledDepartureTime(0)); - assertEquals(15, tripTimes.getDepartureTime(0)); - assertEquals(20, tripTimes.getScheduledArrivalTime(1)); - assertEquals(25, tripTimes.getArrivalTime(1)); + assertEquals( + "UPDATED | A1 0:00:15 0:00:15 | B1 0:00:25 0:00:25", + env.getRealtimeTimetable(env.trip1) + ); } private static class RealtimeTestEnvironment { @@ -565,6 +548,32 @@ public TripTimes getTripTimesForTrip(Trip trip) { return getTripTimesForTrip(trip.getId(), serviceDate); } + public String getRealtimeTimetable(String tripId) { + return getRealtimeTimetable(id(tripId), serviceDate); + } + + public String getRealtimeTimetable(Trip trip) { + return getRealtimeTimetable(trip.getId(), serviceDate); + } + + public String getRealtimeTimetable(FeedScopedId tripId, LocalDate serviceDate) { + var tt = getTripTimesForTrip(tripId, serviceDate); + var pattern = getPatternForTrip(tripId); + + return TripTimesStringBuilder.encodeTripTimes(tt, pattern); + } + + public String getScheduledTimetable(String tripId) { + return getScheduledTimetable(id(tripId)); + } + + public String getScheduledTimetable(FeedScopedId tripId) { + var pattern = getPatternForTrip(tripId); + var tt = pattern.getScheduledTimetable().getTripTimes(tripId); + + return TripTimesStringBuilder.encodeTripTimes(tt, pattern); + } + /** * Find the current TripTimes for a trip id on the default serviceDate */ diff --git a/src/main/java/org/opentripplanner/transit/model/timetable/TripTimesStringBuilder.java b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimesStringBuilder.java new file mode 100644 index 00000000000..a5e77037dea --- /dev/null +++ b/src/main/java/org/opentripplanner/transit/model/timetable/TripTimesStringBuilder.java @@ -0,0 +1,64 @@ +package org.opentripplanner.transit.model.timetable; + +import java.util.ArrayList; +import org.opentripplanner.framework.time.TimeUtils; +import org.opentripplanner.transit.model.network.TripPattern; + +public class TripTimesStringBuilder { + + /** + * This encodes the trip times and information about stops in a readable way in order to simplify + * testing/debugging. The format of the outputput string is: + * + *
+ * REALTIME_STATE | stop1 [FLAGS] arrivalTime departureTime | stop2 ... + * + * Where flags are: + * C: Canceled + * R: Recorded + * PI: Prediction Inaccurate + * ND: No Data + *+ * + * @throws IllegalStateException if TripTimes does not match the TripPattern + */ + public static String encodeTripTimes(TripTimes tripTimes, TripPattern pattern) { + var stops = pattern.getStops(); + + if (tripTimes.getNumStops() != stops.size()) { + throw new IllegalArgumentException( + "TripTimes and TripPattern have different number of stops" + ); + } + + StringBuilder s = new StringBuilder(tripTimes.getRealTimeState().toString()); + for (int i = 0; i < tripTimes.getNumStops(); i++) { + var depart = tripTimes.getDepartureTime(i); + var arrive = tripTimes.getArrivalTime(i); + var flags = new ArrayList