From 4f50e2d37d22a72c5de4ff2382999492913393bf Mon Sep 17 00:00:00 2001 From: Lukas Connolly Date: Thu, 4 Jul 2024 20:20:57 +0200 Subject: [PATCH 1/7] TEST: NAV-108 - Move assertIsolines to Helper class. --- .../raptor/router/RaptorRouterTest.java | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java index dd7c61cd..2bdee85f 100644 --- a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java +++ b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java @@ -156,6 +156,21 @@ private static void checkIfConnectionsAreParetoOptimal(List connecti previousConnection = currentConnection; } } + + private static void assertIsoLines(Map isoLines, int expectedIsoLines) { + assertEquals(expectedIsoLines, isoLines.size()); + assertFalse(isoLines.containsKey(STOP_A), "Source stop should not be in iso lines"); + for (Map.Entry entry : isoLines.entrySet()) { + int arrivalTimeStamp = (int) entry.getValue().getArrivalTime().toEpochSecond(ZoneOffset.UTC); + assertFalse(entry.getValue().getDepartureTime().isBefore(EIGHT_AM), + "Departure time should be greater than or equal to departure time"); + assertFalse(entry.getValue().getArrivalTime().isBefore(EIGHT_AM), + "Arrival time should be greater than or equal to departure time"); + assertTrue(arrivalTimeStamp < INFINITY, "Arrival time should be less than INFINITY"); + assertEquals(STOP_A, entry.getValue().getFromStopId(), "From stop should be source stop"); + assertEquals(entry.getKey(), entry.getValue().getToStopId(), "To stop should be key of map entry"); + } + } } @Nested @@ -808,25 +823,6 @@ void createIsoLinesFromTwoNotConnectedSourceStops(RaptorRouterTestBuilder builde } } } - - private static class Helpers { - private static void assertIsoLines(Map isoLines, int expectedIsoLines) { - assertEquals(expectedIsoLines, isoLines.size()); - assertFalse(isoLines.containsKey(STOP_A), "Source stop should not be in iso lines"); - for (Map.Entry entry : isoLines.entrySet()) { - int arrivalTimeStamp = (int) entry.getValue().getArrivalTime().toEpochSecond(ZoneOffset.UTC); - assertFalse(entry.getValue().getDepartureTime().isBefore(EIGHT_AM), - "Departure time should be greater than or equal to departure time"); - assertFalse(entry.getValue().getArrivalTime().isBefore(EIGHT_AM), - "Arrival time should be greater than or equal to departure time"); - assertTrue(arrivalTimeStamp < INFINITY, "Arrival time should be less than INFINITY"); - assertEquals(STOP_A, entry.getValue().getFromStopId(), "From stop should be source stop"); - assertEquals(entry.getKey(), entry.getValue().getToStopId(), "To stop should be key of map entry"); - } - } - - } - } @Nested From eeffed01a345676256da6f1c3fa047c9184300ed Mon Sep 17 00:00:00 2001 From: Lukas Connolly Date: Thu, 4 Jul 2024 21:25:34 +0200 Subject: [PATCH 2/7] TEST: NAV-108 - Add reverse time direction test for each assertConnection. --- .../raptor/router/RaptorRouterTest.java | 128 +++++++++++++----- 1 file changed, 92 insertions(+), 36 deletions(-) diff --git a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java index 2bdee85f..a0d05b15 100644 --- a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java +++ b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java @@ -106,7 +106,7 @@ static class Helpers { private static void assertEarliestArrivalConnection(Connection connection, String sourceStop, String targetStop, LocalDateTime requestedDepartureTime, int numSameStopTransfers, int numWalkTransfers, - int numTrips) { + int numTrips, RaptorAlgorithm raptor) { assertEquals(sourceStop, connection.getFromStopId()); assertEquals(targetStop, connection.getToStopId()); @@ -122,12 +122,13 @@ private static void assertEarliestArrivalConnection(Connection connection, Strin "Number of transfers should match"); assertEquals(numTrips, connection.getRouteLegs().size(), "Number of trips should match"); + assertReverseDirectionConnection(connection, TimeType.ARRIVAL, raptor); } private static void assertLatestDepartureConnection(Connection connection, String sourceStop, String targetStop, LocalDateTime requestedArrivalTime, int numSameStopTransfers, int numWalkTransfers, - int numTrips) { + int numTrips, RaptorAlgorithm raptor) { assertEquals(sourceStop, connection.getFromStopId()); assertEquals(targetStop, connection.getToStopId()); @@ -143,6 +144,59 @@ private static void assertLatestDepartureConnection(Connection connection, Strin "Number of transfers should match"); assertEquals(numTrips, connection.getRouteLegs().size(), "Number of trips should match"); + assertReverseDirectionConnection(connection, TimeType.DEPARTURE, raptor); + } + + private static void assertReverseDirectionConnection(Connection connection, TimeType timeType, + RaptorAlgorithm raptor) { + List connections; + if (timeType == TimeType.DEPARTURE) { + connections = ConvenienceMethods.routeEarliestArrival(raptor, connection.getFromStopId(), + connection.getToStopId(), connection.getDepartureTime()); + } else { + connections = ConvenienceMethods.routeLatestDeparture(raptor, connection.getFromStopId(), + connection.getToStopId(), connection.getArrivalTime()); + } + + // find the connections with the same amount of rounds (this one should match) + Connection matchingConnection = connections.stream() + .filter(c -> c.getRouteLegs().size() == connection.getRouteLegs().size()) + .findFirst() + .orElse(null); + + assertNotNull(matchingConnection, "Matching connection should be found"); + assertEquals(connection.getFromStopId(), matchingConnection.getFromStopId(), "From stop should match"); + assertEquals(connection.getToStopId(), matchingConnection.getToStopId(), "To stop should match"); + if( timeType == TimeType.DEPARTURE ){ + assertEquals(connection.getDepartureTime(), matchingConnection.getDepartureTime(), + "Departure time should match"); + + // there is no guarantee that the arrival time is the same, but it should not be later (worse) than + // the arrival time of the matching connection + if (connection.getArrivalTime().isBefore(matchingConnection.getArrivalTime())) { + return; + } + } else { + assertEquals(connection.getArrivalTime(), matchingConnection.getArrivalTime(), "Arrival time should match"); + // there is no guarantee that the departure time is the same, but it should not be earlier (worse) than + // the departure time of the matching connection + if (connection.getDepartureTime().isBefore(matchingConnection.getArrivalTime())) { + return; + } + } + + + assertEquals(connection.getDepartureTime(), matchingConnection.getDepartureTime(), + "Departure time should match"); + assertEquals(connection.getArrivalTime(), matchingConnection.getArrivalTime(), "Arrival time should match"); + assertEquals(connection.getNumberOfSameStopTransfers(), matchingConnection.getNumberOfSameStopTransfers(), + "Number of same stop transfers should match"); + assertEquals(connection.getWalkTransfers().size(), matchingConnection.getWalkTransfers().size(), + "Number of walk transfers should match"); + assertEquals(connection.getNumberOfTotalTransfers(), matchingConnection.getNumberOfTotalTransfers(), + "Number of transfers should match"); + assertEquals(connection.getRouteLegs().size(), matchingConnection.getRouteLegs().size(), + "Number of trips should match"); } private static void checkIfConnectionsAreParetoOptimal(List connections) { @@ -194,8 +248,8 @@ void findConnectionsBetweenIntersectingRoutes(RaptorRouterTestBuilder builder) { // check if 2 connections were found assertEquals(2, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_Q, EIGHT_AM, 0, 1, 2); - Helpers.assertEarliestArrivalConnection(connections.get(1), STOP_A, STOP_Q, EIGHT_AM, 2, 0, 3); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_Q, EIGHT_AM, 0, 1, 2, raptor); + Helpers.assertEarliestArrivalConnection(connections.get(1), STOP_A, STOP_Q, EIGHT_AM, 2, 0, 3, raptor); Helpers.checkIfConnectionsAreParetoOptimal(connections); } @@ -205,7 +259,7 @@ void routeBetweenTwoStopsOnSameRoute(RaptorRouterTestBuilder builder) { List connections = ConvenienceMethods.routeEarliestArrival(raptor, STOP_A, STOP_B, EIGHT_AM); assertEquals(1, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_B, EIGHT_AM, 0, 0, 1); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_B, EIGHT_AM, 0, 0, 1, raptor); } @Test @@ -217,9 +271,9 @@ void routeWithSelfIntersectingRoute(RaptorRouterTestBuilder builder) { assertEquals(2, connections.size()); // First Connection Should have no transfers but ride the entire loop (slow) - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_H, EIGHT_AM, 0, 0, 1); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_H, EIGHT_AM, 0, 0, 1, raptor); // Second Connection Should Change at Stop B and take the earlier trip of the same route there (faster) - Helpers.assertEarliestArrivalConnection(connections.get(1), STOP_A, STOP_H, EIGHT_AM, 1, 0, 2); + Helpers.assertEarliestArrivalConnection(connections.get(1), STOP_A, STOP_H, EIGHT_AM, 1, 0, 2, raptor); Helpers.checkIfConnectionsAreParetoOptimal(connections); } @@ -234,7 +288,7 @@ void routeFromTwoSourceStopsWithSameDepartureTime(RaptorRouterTestBuilder builde // fastest and only connection should be B -> H List connections = ConvenienceMethods.routeEarliestArrival(raptor, sourceStops, targetStops); assertEquals(1, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_B, STOP_H, EIGHT_AM, 0, 0, 1); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_B, STOP_H, EIGHT_AM, 0, 0, 1, raptor); } @Test @@ -248,8 +302,8 @@ void routeFromTwoSourceStopsWithLaterDepartureTimeOnCloserStop(RaptorRouterTestB // A -> H has one transfer but earlier arrival time List connections = ConvenienceMethods.routeEarliestArrival(raptor, sourceStops, targetStops); assertEquals(2, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_B, STOP_H, NINE_AM, 0, 0, 1); - Helpers.assertEarliestArrivalConnection(connections.get(1), STOP_A, STOP_H, EIGHT_AM, 1, 0, 2); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_B, STOP_H, NINE_AM, 0, 0, 1, raptor); + Helpers.assertEarliestArrivalConnection(connections.get(1), STOP_A, STOP_H, EIGHT_AM, 1, 0, 2, raptor); assertTrue(connections.getFirst().getArrivalTime().isAfter(connections.get(1).getArrivalTime()), "Connection from A should arrive earlier than connection from B"); } @@ -264,7 +318,7 @@ void routeFromStopToTwoTargetStopsNoWalkTimeToTarget(RaptorRouterTestBuilder bui // fastest and only connection should be A -> F List connections = ConvenienceMethods.routeEarliestArrival(raptor, sourceStops, targetStops); assertEquals(1, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_F, EIGHT_AM, 0, 0, 1); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_F, EIGHT_AM, 0, 0, 1, raptor); } @Test @@ -279,8 +333,8 @@ void routeFromStopToTwoTargetStopsWithWalkTimeToTarget(RaptorRouterTestBuilder b // walk time to target, the connection A -> S should be faster (no additional walk time) List connections = ConvenienceMethods.routeEarliestArrival(raptor, sourceStops, targetStops); assertEquals(2, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_F, EIGHT_AM, 0, 0, 1); - Helpers.assertEarliestArrivalConnection(connections.get(1), STOP_A, STOP_S, EIGHT_AM, 1, 0, 2); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_F, EIGHT_AM, 0, 0, 1, raptor); + Helpers.assertEarliestArrivalConnection(connections.get(1), STOP_A, STOP_S, EIGHT_AM, 1, 0, 2, raptor); // Note since the required walk time to target is not added as a leg, the solutions will not be pareto // optimal without additional post-processing. @@ -307,7 +361,7 @@ void findConnectionBetweenOnlyFootpath(RaptorRouterTestBuilder builder) { List connections = ConvenienceMethods.routeEarliestArrival(raptor, STOP_N, STOP_D, EIGHT_AM); assertEquals(1, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_N, STOP_D, EIGHT_AM, 0, 1, 0); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_N, STOP_D, EIGHT_AM, 0, 1, 0, raptor); } @Test @@ -322,7 +376,7 @@ void takeFasterRouteOfOverlappingRoutes(RaptorRouterTestBuilder builder) { // Both Routes leave at 8:00 at Stop A, but R1 arrives at G at 8:35 whereas R1X arrives at G at 8:23 // R1X should be taken assertEquals(1, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_G, EIGHT_AM, 0, 0, 1); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_G, EIGHT_AM, 0, 0, 1, raptor); // check departure at 8:00 Connection connection = connections.getFirst(); assertEquals(EIGHT_AM, connection.getDepartureTime()); @@ -343,7 +397,7 @@ void takeSlowerRouteOfOverlappingRoutesDueToEarlierDepartureTime(RaptorRouterTes // Route R1 leaves at 8:00 at Stop A and arrives at G at 8:35 whereas R1X leaves at 8:15 from Stop A and // arrives at G at 8:38. R1 should be used. assertEquals(1, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_G, EIGHT_AM, 0, 0, 1); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_G, EIGHT_AM, 0, 0, 1, raptor); // check departure at 8:00 Connection connection = connections.getFirst(); assertEquals(EIGHT_AM, connection.getDepartureTime()); @@ -376,8 +430,8 @@ void findConnectionsBetweenIntersectingRoutes(RaptorRouterTestBuilder builder) { // check if 2 connections were found assertEquals(2, connections.size()); - Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_Q, NINE_AM, 0, 1, 2); - Helpers.assertLatestDepartureConnection(connections.get(1), STOP_A, STOP_Q, NINE_AM, 2, 0, 3); + Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_Q, NINE_AM, 0, 1, 2, raptor); + Helpers.assertLatestDepartureConnection(connections.get(1), STOP_A, STOP_Q, NINE_AM, 2, 0, 3, raptor); Helpers.checkIfConnectionsAreParetoOptimal(connections); } @@ -387,7 +441,7 @@ void routeBetweenTwoStopsOnSameRoute(RaptorRouterTestBuilder builder) { List connections = ConvenienceMethods.routeLatestDeparture(raptor, STOP_A, STOP_B, NINE_AM); assertEquals(1, connections.size()); - Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_B, NINE_AM, 0, 0, 1); + Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_B, NINE_AM, 0, 0, 1, raptor); } @Test @@ -399,9 +453,9 @@ void routeWithSelfIntersectingRoute(RaptorRouterTestBuilder builder) { assertEquals(2, connections.size()); // First Connection Should have no transfers but ride the entire loop (slow) - Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_H, NINE_AM, 0, 0, 1); + Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_H, NINE_AM, 0, 0, 1, raptor); // Second Connection Should Change at Stop B and take the earlier trip of the same route there (faster) - Helpers.assertLatestDepartureConnection(connections.get(1), STOP_A, STOP_H, NINE_AM, 1, 0, 2); + Helpers.assertLatestDepartureConnection(connections.get(1), STOP_A, STOP_H, NINE_AM, 1, 0, 2, raptor); Helpers.checkIfConnectionsAreParetoOptimal(connections); } @@ -416,7 +470,7 @@ void routeToTwoSourceStopsWitNoWalkTime(RaptorRouterTestBuilder builder) { // fastest and only connection should be B -> H List connections = ConvenienceMethods.routeLatestDeparture(raptor, sourceStops, targetStops); assertEquals(1, connections.size()); - Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_B, STOP_H, NINE_AM, 0, 0, 1); + Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_B, STOP_H, NINE_AM, 0, 0, 1, raptor); } @Test @@ -430,8 +484,8 @@ void routeToTwoSourceStopsWithWalkTimeOnCloserStop(RaptorRouterTestBuilder build // A -> H has one transfer but (theoretical) better departure time (no additional walk time List connections = ConvenienceMethods.routeLatestDeparture(raptor, sourceStops, targetStops); assertEquals(2, connections.size()); - Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_B, STOP_H, NINE_AM, 0, 0, 1); - Helpers.assertLatestDepartureConnection(connections.get(1), STOP_A, STOP_H, NINE_AM, 1, 0, 2); + Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_B, STOP_H, NINE_AM, 0, 0, 1, raptor); + Helpers.assertLatestDepartureConnection(connections.get(1), STOP_A, STOP_H, NINE_AM, 1, 0, 2, raptor); } @Test @@ -444,7 +498,7 @@ void routeFromTwoTargetStopsToTargetNoWalkTime(RaptorRouterTestBuilder builder) // fastest and only connection should be A -> F List connections = ConvenienceMethods.routeLatestDeparture(raptor, sourceStops, targetStops); assertEquals(1, connections.size()); - Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_F, NINE_AM, 0, 0, 1); + Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_F, NINE_AM, 0, 0, 1, raptor); } @Test @@ -459,8 +513,8 @@ void routeFromToTargetStopsWithDifferentArrivalTimes(RaptorRouterTestBuilder bui // earlier arrival time, the connection A -> S should be faster (no additional walk time) List connections = ConvenienceMethods.routeLatestDeparture(raptor, sourceStops, targetStops); assertEquals(2, connections.size()); - Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_F, EIGHT_AM, 0, 0, 1); - Helpers.assertLatestDepartureConnection(connections.get(1), STOP_A, STOP_S, NINE_AM, 1, 0, 2); + Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_F, EIGHT_AM, 0, 0, 1, raptor); + Helpers.assertLatestDepartureConnection(connections.get(1), STOP_A, STOP_S, NINE_AM, 1, 0, 2, raptor); } @Test @@ -484,7 +538,7 @@ void findConnectionBetweenOnlyFootpath(RaptorRouterTestBuilder builder) { List connections = ConvenienceMethods.routeLatestDeparture(raptor, STOP_N, STOP_D, NINE_AM); assertEquals(1, connections.size()); - Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_N, STOP_D, NINE_AM, 0, 1, 0); + Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_N, STOP_D, NINE_AM, 0, 1, 0, raptor); } @Test @@ -501,7 +555,8 @@ void takeFasterRouteOfOverlappingRoutes(RaptorRouterTestBuilder builder) { List connections = ConvenienceMethods.routeLatestDeparture(raptor, STOP_A, STOP_G, arrivalTime); assertEquals(1, connections.size()); - Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_G, arrivalTime, 0, 0, 1); + Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_G, arrivalTime, 0, 0, 1, + raptor); // check departure at 8:12 Connection connection = connections.getFirst(); assertEquals(EIGHT_AM.plusMinutes(12), connection.getDepartureTime()); @@ -523,7 +578,8 @@ void takeSlowerRouteOfOverlappingRoutesDueToLaterDepartureTime(RaptorRouterTestB // arrives at G at 8:08. R1 should be used. LocalDateTime arrivalTime = EIGHT_AM.plusMinutes(35); assertEquals(1, connections.size()); - Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_G, arrivalTime, 0, 0, 1); + Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_A, STOP_G, arrivalTime, 0, 0, 1, + raptor); // check departure at 8:00 Connection connection = connections.getFirst(); assertEquals(EIGHT_AM, connection.getDepartureTime()); @@ -631,8 +687,8 @@ void findWalkableTransferWithMaxWalkingTime(RaptorRouterTestBuilder builder) { // check if 2 connections were found assertEquals(2, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_Q, EIGHT_AM, 0, 1, 2); - Helpers.assertEarliestArrivalConnection(connections.get(1), STOP_A, STOP_Q, EIGHT_AM, 2, 0, 3); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_Q, EIGHT_AM, 0, 1, 2, raptor); + Helpers.assertEarliestArrivalConnection(connections.get(1), STOP_A, STOP_Q, EIGHT_AM, 2, 0, 3, raptor); Helpers.checkIfConnectionsAreParetoOptimal(connections); } @@ -647,7 +703,7 @@ void notFindWalkableTransferWithMaxWalkingTime(RaptorRouterTestBuilder builder) List connections = ConvenienceMethods.routeEarliestArrival(raptor, STOP_A, STOP_Q, EIGHT_AM, queryConfig); assertEquals(1, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_Q, EIGHT_AM, 2, 0, 3); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_Q, EIGHT_AM, 2, 0, 3, raptor); } @Test @@ -665,7 +721,7 @@ void findConnectionWithMaxTransferNumber(RaptorRouterTestBuilder builder) { List connections = ConvenienceMethods.routeEarliestArrival(raptor, STOP_A, STOP_Q, EIGHT_AM, queryConfig); assertEquals(1, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_Q, EIGHT_AM, 0, 1, 2); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_Q, EIGHT_AM, 0, 1, 2, raptor); } @Test @@ -681,7 +737,7 @@ void findConnectionWithMaxTravelTime(RaptorRouterTestBuilder builder) { List connections = ConvenienceMethods.routeEarliestArrival(raptor, STOP_A, STOP_Q, EIGHT_AM, queryConfig); assertEquals(1, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_Q, EIGHT_AM, 2, 0, 3); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_Q, EIGHT_AM, 2, 0, 3, raptor); } @Test @@ -734,7 +790,7 @@ void addMinimumTransferTimeToWalkTransferDuration(RaptorRouterTestBuilder builde assertEquals(2, connections.size()); Connection firstConnection = connections.getFirst(); - Helpers.assertEarliestArrivalConnection(firstConnection, STOP_A, STOP_Q, EIGHT_AM, 0, 1, 2); + Helpers.assertEarliestArrivalConnection(firstConnection, STOP_A, STOP_Q, EIGHT_AM, 0, 1, 2, raptor); // The walk transfer from D to N takes 60 minutes and the route from N to Q leaves every 75 minutes. Leg firstLeg = firstConnection.getRouteLegs().getFirst(); From a823017337f60762cda461d799a1d2d169ca6131 Mon Sep 17 00:00:00 2001 From: Lukas Connolly Date: Thu, 4 Jul 2024 22:39:22 +0200 Subject: [PATCH 3/7] TEST: NAV-108 - Move walk transfer tests to new sub class WalkTransfers --- .../raptor/router/RaptorRouterTest.java | 70 ++++++++++--------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java index a0d05b15..7ae9e74d 100644 --- a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java +++ b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java @@ -167,7 +167,7 @@ private static void assertReverseDirectionConnection(Connection connection, Time assertNotNull(matchingConnection, "Matching connection should be found"); assertEquals(connection.getFromStopId(), matchingConnection.getFromStopId(), "From stop should match"); assertEquals(connection.getToStopId(), matchingConnection.getToStopId(), "To stop should match"); - if( timeType == TimeType.DEPARTURE ){ + if (timeType == TimeType.DEPARTURE) { assertEquals(connection.getDepartureTime(), matchingConnection.getDepartureTime(), "Departure time should match"); @@ -177,7 +177,8 @@ private static void assertReverseDirectionConnection(Connection connection, Time return; } } else { - assertEquals(connection.getArrivalTime(), matchingConnection.getArrivalTime(), "Arrival time should match"); + assertEquals(connection.getArrivalTime(), matchingConnection.getArrivalTime(), + "Arrival time should match"); // there is no guarantee that the departure time is the same, but it should not be earlier (worse) than // the departure time of the matching connection if (connection.getDepartureTime().isBefore(matchingConnection.getArrivalTime())) { @@ -185,7 +186,6 @@ private static void assertReverseDirectionConnection(Connection connection, Time } } - assertEquals(connection.getDepartureTime(), matchingConnection.getDepartureTime(), "Departure time should match"); assertEquals(connection.getArrivalTime(), matchingConnection.getArrivalTime(), "Arrival time should match"); @@ -349,21 +349,6 @@ void notFindConnectionBetweenNotLinkedStops(RaptorRouterTestBuilder builder) { assertTrue(connections.isEmpty(), "No connection should be found"); } - @Test - void findConnectionBetweenOnlyFootpath(RaptorRouterTestBuilder builder) { - RaptorAlgorithm raptor = builder.withAddRoute1_AG() - .withAddRoute2_HL() - .withAddRoute3_MQ() - .withAddRoute4_RS() - .withAddTransfer1_ND(1) - .withAddTransfer2_LR() - .build(); - - List connections = ConvenienceMethods.routeEarliestArrival(raptor, STOP_N, STOP_D, EIGHT_AM); - assertEquals(1, connections.size()); - Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_N, STOP_D, EIGHT_AM, 0, 1, 0, raptor); - } - @Test void takeFasterRouteOfOverlappingRoutes(RaptorRouterTestBuilder builder) { // Create Two Versions of the same route with different travel speeds (both leaving at same time from A) @@ -526,21 +511,6 @@ void notFindConnectionBetweenNotLinkedStops(RaptorRouterTestBuilder builder) { assertTrue(connections.isEmpty(), "No connection should be found"); } - @Test - void findConnectionBetweenOnlyFootpath(RaptorRouterTestBuilder builder) { - RaptorAlgorithm raptor = builder.withAddRoute1_AG() - .withAddRoute2_HL() - .withAddRoute3_MQ() - .withAddRoute4_RS() - .withAddTransfer1_ND(1) - .withAddTransfer2_LR() - .build(); - - List connections = ConvenienceMethods.routeLatestDeparture(raptor, STOP_N, STOP_D, NINE_AM); - assertEquals(1, connections.size()); - Helpers.assertLatestDepartureConnection(connections.getFirst(), STOP_N, STOP_D, NINE_AM, 0, 1, 0, raptor); - } - @Test void takeFasterRouteOfOverlappingRoutes(RaptorRouterTestBuilder builder) { // Create Two Versions of the same route with different travel speeds (both leaving at same time from A) @@ -590,6 +560,40 @@ void takeSlowerRouteOfOverlappingRoutesDueToLaterDepartureTime(RaptorRouterTestB } } + @Nested + class WalkTransfers { + + @Test + void findConnectionBetweenOnlyFootpath(RaptorRouterTestBuilder builder) { + RaptorAlgorithm raptor = builder.withAddRoute1_AG() + .withAddRoute2_HL() + .withAddRoute3_MQ() + .withAddRoute4_RS() + .withAddTransfer1_ND(1) + .withAddTransfer2_LR() + .build(); + + List connections = ConvenienceMethods.routeEarliestArrival(raptor, STOP_N, STOP_D, EIGHT_AM); + assertEquals(1, connections.size()); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_N, STOP_D, EIGHT_AM, 0, 1, 0, raptor); + } + + @Test + void findConnectionBetweenWithFootpath(RaptorRouterTestBuilder builder) { + RaptorAlgorithm raptor = builder.withAddRoute1_AG().withAddRoute3_MQ().withAddTransfer1_ND().build(); + + // Should return connection with two route legs and one walk transfer + // - Route R1-F from A to D + // - Foot Transfer from D to N + // - Route R3-F from N to Q + List connections = ConvenienceMethods.routeEarliestArrival(raptor, STOP_A, STOP_Q, EIGHT_AM); + + assertEquals(1, connections.size()); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_Q, EIGHT_AM, 0, 1, 2, raptor); + } + + } + @Nested class SameStopTransfers { From 21b63101a9af27f9323b38004afcb724cc65f5bf Mon Sep 17 00:00:00 2001 From: Lukas Connolly Date: Thu, 4 Jul 2024 22:46:05 +0200 Subject: [PATCH 4/7] TEST: NAV-108 - Add unit test for zero travel time trips. --- .../raptor/router/RaptorRouterTest.java | 29 +++++++++++++++++++ .../router/RaptorRouterTestBuilder.java | 5 ++++ 2 files changed, 34 insertions(+) diff --git a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java index 7ae9e74d..555dfd97 100644 --- a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java +++ b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java @@ -592,6 +592,35 @@ void findConnectionBetweenWithFootpath(RaptorRouterTestBuilder builder) { Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_Q, EIGHT_AM, 0, 1, 2, raptor); } + @Test + void findConnectionWithZeroTravelTimeTripsAndConsequentWalkTransfer(RaptorRouterTestBuilder builder) { + // There are connections on local public transport where the travel time between stops is zero (in reality + // 30 seconds or so, but rounded to the closest minute). This test ensures that the walk transfer is not + // sorted before/after such a leg when rebuilding the connection. + RaptorAlgorithm raptor = builder.withAddRoute1_AG(0, RaptorRouterTestBuilder.DEFAULT_HEADWAY_TIME, 0, + RaptorRouterTestBuilder.DEFAULT_DWELL_TIME) + .withAddRoute3_MQ(0, RaptorRouterTestBuilder.DEFAULT_HEADWAY_TIME, 0, + RaptorRouterTestBuilder.DEFAULT_DWELL_TIME) + .withAddTransfer1_ND() + .build(); + + // Connection C <-> O will be 1-stop leg from C to D, a walk transfer to N and a 1-stop leg from N to O. + // I.e. departure time at C will be equal to walk transfer departure at D and arrival time at O will be equal + // to departure time at N. + List connections = ConvenienceMethods.routeEarliestArrival(raptor, STOP_C, STOP_O, EIGHT_AM); + assertEquals(1, connections.size()); + Connection connection = connections.getFirst(); + Leg firstRouteLeg = connection.getLegs().getFirst(); + Leg walkTransferLeg = connection.getLegs().get(1); + assertEquals(firstRouteLeg.getType(), Leg.Type.ROUTE); + assertEquals(walkTransferLeg.getType(), Leg.Type.WALK_TRANSFER); + assertEquals(firstRouteLeg.getDepartureTime(), firstRouteLeg.getArrivalTime(), + "Departure time at C should be equal to arrival time at D"); + assertEquals(firstRouteLeg.getDepartureTime(), walkTransferLeg.getDepartureTime(), + "Departure time at C should be equal to walk departure time at D"); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_C, STOP_O, EIGHT_AM, 0, 1, 2, raptor); + } + } @Nested diff --git a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTestBuilder.java b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTestBuilder.java index 980ec703..e4e934d4 100644 --- a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTestBuilder.java +++ b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTestBuilder.java @@ -142,6 +142,11 @@ public RaptorRouterTestBuilder withAddRoute3_MQ() { return this; } + public RaptorRouterTestBuilder withAddRoute3_MQ(int offset, int headway, int travelTime, int dwellTime) { + routes.add(new Route("R3", List.of("M", "K", "N", "O", "P", "Q"), offset, headway, travelTime, dwellTime)); + return this; + } + public RaptorRouterTestBuilder withAddRoute4_RS() { routes.add(new Route("R4", List.of("R", "P", "F", "S"))); return this; From 5232086d4917a11db38fb4930d8966d07a28de0b Mon Sep 17 00:00:00 2001 From: Lukas Connolly Date: Thu, 4 Jul 2024 23:03:46 +0200 Subject: [PATCH 5/7] TEST: NAV-112 - Add test to ensure no unnecessary transfers are added to start of trip. --- .../raptor/router/RaptorRouterTest.java | 21 +++++++++++++++++++ .../router/RaptorRouterTestBuilder.java | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java index 555dfd97..86511dce 100644 --- a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java +++ b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java @@ -621,6 +621,27 @@ void findConnectionWithZeroTravelTimeTripsAndConsequentWalkTransfer(RaptorRouter Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_C, STOP_O, EIGHT_AM, 0, 1, 2, raptor); } + @Test + void ensureUnnecessaryWalkTransferIsNotAdded(RaptorRouterTestBuilder builder) { + // This test should ensure that the router does not add a walk transfer at the beginning of a trip only to + // reduce the earliest arrival time at the next stop when the following route leg could have also been entered + // at the previous stop (same overall arrival time only more walking and earlier departure time). + RaptorAlgorithm raptor = builder.withAddRoute1_AG().withAddTransfer("A", "B", 15).build(); + + // Route connection from A <-> C can be connected by a route trip starting at 8:15 at A, arriving at B at + // 8:20 and then at C at 8:26. + // Since the earliest arrival request is set to depart at 08:01 and the walk to B takes 15 minutes, the + // earliest arrival at B is 8:16. However, in this case the traveller still has to wait until 8:21 to depart + // from B. The walk transfer should not be added in this case. + LocalDateTime requestedDepartureTime = EIGHT_AM.plusMinutes(1); + List connections = ConvenienceMethods.routeEarliestArrival(raptor, STOP_A, STOP_C, + requestedDepartureTime); + + assertEquals(1, connections.size()); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_C, requestedDepartureTime, 0, + 0, 1, raptor); + } + } @Nested diff --git a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTestBuilder.java b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTestBuilder.java index e4e934d4..0e8b6960 100644 --- a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTestBuilder.java +++ b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTestBuilder.java @@ -171,6 +171,11 @@ public RaptorRouterTestBuilder withAddTransfer2_LR() { return this; } + public RaptorRouterTestBuilder withAddTransfer(String sourceStop, String targetStop, int duration) { + transfers.add(new Transfer(sourceStop, targetStop, duration)); + return this; + } + public RaptorRouterTestBuilder withSameStopTransferTime(int time) { this.sameStopTransferTime = time; return this; From 870d1b8d6cb5a2a31236b03210ac554c9b8cc607 Mon Sep 17 00:00:00 2001 From: Lukas Connolly Date: Thu, 4 Jul 2024 23:13:59 +0200 Subject: [PATCH 6/7] TEST: NAV-112 - Add tests to ensure final walk transfer is only added where needed. --- .../raptor/router/RaptorRouterTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java index 86511dce..77619713 100644 --- a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java +++ b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java @@ -642,6 +642,39 @@ void ensureUnnecessaryWalkTransferIsNotAdded(RaptorRouterTestBuilder builder) { 0, 1, raptor); } + @Test + void ensureFinalLegDoesNotFavorWalkTransferBecauseOfSameStopTransferTime(RaptorRouterTestBuilder builder) { + // This test is intended to ensure that due to the subtraction of the same transfer time from the arrival time + // of a walk transfer, the walk transfer is not falsely favored over a route leg arriving at the final stop. + RaptorAlgorithm raptor = builder.withAddRoute1_AG() + .withAddTransfer("B", "C", 7) + .withSameStopTransferTime(120) + .build(); + + // Route connection from A <-> C can be connected by a route trip starting at 8:00 at A, arriving at B at + // 8:05 and then at C at 8:11. If the walk from B to C takes 7 minutes the "comparable" arrival time at C + // will be also 8:10. However, since the "real" arrival time will be 8:12, the walk transfer should not be + // favored. + List connections = ConvenienceMethods.routeEarliestArrival(raptor, STOP_A, STOP_C, EIGHT_AM); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_C, EIGHT_AM, 0, 0, 1, raptor); + } + + @Test + void ensureFinalWalkTransferIsAddedIfArrivesEarlierThanRouteLeg(RaptorRouterTestBuilder builder) { + // This test is intended to ensure that a walk transfer is added at the final stop if it arrives earlier than + // the route leg. + RaptorAlgorithm raptor = builder.withAddRoute1_AG() + .withAddTransfer("B", "C", 5) + .withSameStopTransferTime(120) + .build(); + + // Route connection from A <-> C can be connected by a route trip starting at 8:00 at A, arriving at B at + // 8:05 and then at C at 8:11. If the walk from B to C takes 5 minutes the arrival time at C is 8:10 and + // should be favored over the route leg arriving at 8:11. + List connections = ConvenienceMethods.routeEarliestArrival(raptor, STOP_A, STOP_C, EIGHT_AM); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_C, EIGHT_AM, 0, 1, 1, raptor); + } + } @Nested From d0b303290539bde367626c1908b15d4435df0df6 Mon Sep 17 00:00:00 2001 From: Lukas Connolly Date: Thu, 4 Jul 2024 23:22:17 +0200 Subject: [PATCH 7/7] TEST: NAV-112 - Test that initial walk transfer departs as late as possible to reach first route departure. --- .../naviqore/raptor/router/RaptorRouterTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java index 77619713..813167cf 100644 --- a/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java +++ b/src/test/java/ch/naviqore/raptor/router/RaptorRouterTest.java @@ -675,6 +675,21 @@ void ensureFinalWalkTransferIsAddedIfArrivesEarlierThanRouteLeg(RaptorRouterTest Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_A, STOP_C, EIGHT_AM, 0, 1, 1, raptor); } + @Test + void initialWalkTransferShouldLeaveAsLateAsPossible(RaptorRouterTestBuilder builder) { + // This test tests that the walk transfer at a beginning of a trip leaves as late as possible, i.e. arriving + // at the next stop at the same time as the route leg departs. + RaptorAlgorithm raptor = builder.withAddRoute1_AG().withAddRoute3_MQ().withAddTransfer1_ND(15).build(); + + // Connection from N to E can be connected by a walk transfer from N to D and then a route trip from D to E. + // The route trip from D to E leaves at 8:18. The walk transfer requires 15 minutes of walking, thus should + // leave N at 8:03 to reach D on time (8:18). The requested earliest departure time is 8:00. + List connections = ConvenienceMethods.routeEarliestArrival(raptor, STOP_N, STOP_E, EIGHT_AM); + assertEquals(1, connections.size()); + assertEquals(EIGHT_AM.plusMinutes(3), connections.getFirst().getDepartureTime()); + Helpers.assertEarliestArrivalConnection(connections.getFirst(), STOP_N, STOP_E, EIGHT_AM, 0, 1, 1, raptor); + } + } @Nested