Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,30 @@ public static String getStringFromDate(LocalDateTime localDate, String expectedD
return localDate.format(expectedDateFormat);
}

/**
* Compares two Strings, returning {@code true} if they are equal or if the only difference
* is between a space and a narrow no-break space (NNBSP). Intended for strings that have
* formatted time in the {@code en_US} locale which includes AM or PM. Older JDKs formatted
* these with an ASCII space ({@code U+0020}) before "AM" or "PM", but as of JDK 20 the space
* is replaced with NNBSP ({@code U+202F}) by default.
* <p>
* Use of {@code null}s will return {@code false} but not throw an exception. It is intended
* that the first string was made using {@link DateTimeFormatter} and may have either a space
* or a NNBSP, whereas the second "test string" to compare it against will have a hardcoded
* NNBSP in it regardless of the JDK.
*
* @param str formatted text to test, e.g. {@code "6:30 PM"} or {@code "6:30\u202fPM"}
* @param testStr text to be tested against using NNBSP format, e.g. {@code "6:30\u202fPM"}
*/
public static boolean equalsAmPm(final String str, final String testStr) {
if (str == null || testStr == null) {
return false;
} else {
// The first string in `replaceAll()` is an NNBSP, not a space.
return str.equals(testStr) || str.equals(testStr.replaceAll(" ", " "));
}
}

/**
* Helper to format a date in short format (e.g. "5:40 PM" - no seconds) in the specified locale.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ void testDelayNotifications(
int minutesLate,
int previousMinutesLate,
NotificationType notificationType,
String expectedNotificationPattern,
String expectedBody,
String message
) throws Exception {
long previousDelayMillis = TimeUnit.MILLISECONDS.convert(previousMinutesLate, TimeUnit.MINUTES);
Expand Down Expand Up @@ -264,12 +264,12 @@ void testDelayNotifications(
}

TripMonitorNotification notification = check.checkTripForDelays();
if (expectedNotificationPattern == null) {
if (expectedBody == null) {
assertNull(notification, message);
} else {
assertNotNull(notification);
assertEquals(notificationType, notification.type);
assertThat(message, notification.body, matchesPattern(expectedNotificationPattern));
assertTrue(DateTimeUtils.equalsAmPm(notification.body, expectedBody), message);
}
}

Expand Down Expand Up @@ -318,31 +318,31 @@ private static Stream<Arguments> createDelayNotificationTestCases() {
0,
NotificationType.DEPARTURE_AND_ARRIVAL_DELAY,
STOPWATCH_ICON +
" Your trip is now predicted to depart 20 minutes late at 9:00[\\u202f ]AM \\(Now arriving at 9:18[\\u202f ]AM\\)\\.",
" Your trip is now predicted to depart 20 minutes late at 9:00\u202fAM (Now arriving at 9:18\u202fAM).",
"20m-late trip previously on-time => show dep/arr delay notifications"
),
Arguments.of(
20,
0,
NotificationType.DEPARTURE_DELAY,
STOPWATCH_ICON +
" Your trip is now predicted to depart 20 minutes late \\(at 9:00[\\u202f ]AM\\)\\.",
" Your trip is now predicted to depart 20 minutes late (at 9:00\u202fAM).",
"20m-late departure previously on-time, but still arriving on-time => show departure-only delay notifications"
),
Arguments.of(
20,
0,
NotificationType.ARRIVAL_DELAY,
STOPWATCH_ICON +
" Your trip is now predicted to arrive 20 minutes late \\(at 9:18[\\u202f ]AM\\)\\.",
" Your trip is now predicted to arrive 20 minutes late (at 9:18\u202fAM).",
"20m-late arrival previously on-time, but still departing on-time => show arrival-only delay notifications"
),
Arguments.of(
-18,
0,
NotificationType.DEPARTURE_AND_ARRIVAL_DELAY,
STOPWATCH_ICON +
" Your trip is now predicted to depart 18 minutes early at 8:22[\\u202f ]AM \\(Now arriving at 8:40[\\u202f ]AM\\)\\.",
" Your trip is now predicted to depart 18 minutes early at 8:22\u202fAM (Now arriving at 8:40\u202fAM).",
"18m-early trip previously on-time => show delay (early) notifications"
),
Arguments.of(
Expand All @@ -357,7 +357,7 @@ private static Stream<Arguments> createDelayNotificationTestCases() {
15,
NotificationType.DEPARTURE_AND_ARRIVAL_DELAY,
STOPWATCH_ICON +
" Your trip is now predicted to depart about on time at 8:40[\\u202f ]AM \\(Now arriving at 8:58[\\u202f ]AM\\)\\.",
" Your trip is now predicted to depart about on time at 8:40\u202fAM (Now arriving at 8:58\u202fAM).",
"On-time trip previously late => show on-time notifications"
)
);
Expand Down Expand Up @@ -788,16 +788,15 @@ void canSendDelayNotifications(boolean isOneTime) throws Exception {
mockTrip.itineraryExistence.tuesday = new ItineraryExistence.ItineraryExistenceResult();

List<DelayCase> cases = List.of(
// TODO: fix time separator char
// Add some delays for the trip.
new DelayCase(300, 420, true, TUESDAY_20200609_0800, 1, "⏱ Your trip is now predicted to depart 5 minutes late (at 8:45 AM)."),
new DelayCase(300, 420, true, TUESDAY_20200609_0800, 1, "⏱ Your trip is now predicted to depart 5 minutes late (at 8:45\u202fAM)."),
// Decrease real-time delays (subtract delays) from the OTP response.
new DelayCase(-100, -60, true, TUESDAY_20200609_0800, 1, "⏱ Your trip is now predicted to arrive 6 minutes late (at 9:04 AM)."),
new DelayCase(-100, -60, true, TUESDAY_20200609_0800, 1, "⏱ Your trip is now predicted to arrive 6 minutes late (at 9:04\u202fAM)."),
// Drop real-time updates (subtract delays) from the OTP response.
new DelayCase(-200, -360, false, TUESDAY_20200609_0800, 1, "⏱ Real-time updates for your trip were lost. Monitoring will be based on your originally saved trip."),

// Add back delays for the trip.
new DelayCase(300, 420, true, TUESDAY_20200609_0800, 1, "⏱ Your trip is now predicted to depart 5 minutes late (at 8:45 AM)."),
new DelayCase(300, 420, true, TUESDAY_20200609_0800, 1, "⏱ Your trip is now predicted to depart 5 minutes late (at 8:45\u202fAM)."),
// Drop real-time updates and simulate a time at which the trip is considered over.
// No notifications should be sent when the trip is considered over.
new DelayCase(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.opentripplanner.middleware.triptracker.ManageLegTraversal.getSecondsToMilliseconds;
import static org.opentripplanner.middleware.triptracker.ManageLegTraversal.interpolatePoints;
import static org.opentripplanner.middleware.triptracker.TravelerLocator.getNextOrClosestWayPoint;
Expand Down Expand Up @@ -471,7 +472,11 @@ void canTrackAtBusStop(String message, Itinerary itinerary, int currentLegIndex,
.build();
travelerPosition.locale = locale;
TripInstruction tripInstruction = TravelerLocator.getInstruction(traceData.tripStatus, travelerPosition);
assertEquals(traceData.expectedInstruction, tripInstruction != null ? tripInstruction.build() : NO_INSTRUCTION, message);
if (tripInstruction == null) {
assertEquals(traceData.expectedInstruction, NO_INSTRUCTION, message);
} else {
assertTrue(DateTimeUtils.equalsAmPm(traceData.expectedInstruction, tripInstruction.build()), message);
}

// If a Gwinnett County bus notification was sent, check that the agency, route, and trip id fields are not null.
if (!travelerPosition.trackedJourney.busNotificationMessages.isEmpty() && currentLeg.route != null) {
Expand Down Expand Up @@ -532,7 +537,7 @@ private static Stream<Arguments> createBusStopTrace() {
.withPosition(busStopCoords)
.withTripStatus(TripStatus.BEHIND_SCHEDULE)
.withInstant(Instant.now())
.withExpectedInstruction("Wait for your bus, route 20, scheduled at 7:58 AM (That time has passed)")
.withExpectedInstruction("Wait for your bus, route 20, scheduled at 7:58\u202fAM (That time has passed)")
),
Arguments.of(
"Arrive at bus stop well after the bus departure (indicates past departure).",
Expand All @@ -542,7 +547,7 @@ private static Stream<Arguments> createBusStopTrace() {
.withPosition(walkToBusTransition.legs.get(0).to.toCoordinates())
.withTripStatus(TripStatus.BEHIND_SCHEDULE)
.withInstant(Instant.now())
.withExpectedInstruction("Wait for your bus, route 40, scheduled at 6:41 AM (That time has passed)")
.withExpectedInstruction("Wait for your bus, route 40, scheduled at 6:41\u202fAM (That time has passed)")
),
Arguments.of(
"Arrive at bus stop well in advance.",
Expand All @@ -552,7 +557,7 @@ private static Stream<Arguments> createBusStopTrace() {
.withPosition(walkToBusTransition.legs.get(0).to.toCoordinates())
.withTripStatus(TripStatus.AHEAD_OF_SCHEDULE)
.withInstant(walkToBusTransition.legs.get(1).startTime.toInstant().minus(40, ChronoUnit.MINUTES))
.withExpectedInstruction("Wait 40 minutes for your bus, route 40, scheduled at 6:41 AM (On time)")
.withExpectedInstruction("Wait 40 minutes for your bus, route 40, scheduled at 6:41\u202fAM (On time)")
),
Arguments.of(
"Arrive at bus stop where the walk geometry is so the stop is farther than the last walk shape. Should produce a bus-stop-in-vicinity instruction, not 'destination in vicinity'.",
Expand Down Expand Up @@ -615,7 +620,7 @@ private static Stream<Arguments> createTransitRideTrace() {
"If present at the transit stop after the trip departure, instruct to wait (indicate past departure).",
new TraceData()
.withPosition(originCoords)
.withExpectedInstruction("Wait for your bus, route 27, scheduled at 9:18 AM (That time has passed)")
.withExpectedInstruction("Wait for your bus, route 27, scheduled at 9:18\u202fAM (That time has passed)")
.withTripStatus(TripStatus.BEHIND_SCHEDULE)
.withInstant(Instant.now())
),
Expand Down
Loading