Skip to content

Commit

Permalink
adds more specific smoke tests to be used on local deployments
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-heppner-ibigroup committed Nov 20, 2024
1 parent b280d88 commit bc6c310
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
public class SeattleSmokeTest {

private static final String CCSWW_ROUTE = "Volunteer Services: Northwest";
static final Coordinate LYNNWOOD_STA = new Coordinate(47.8154272,-122.2940715);
static final Coordinate SODO = new Coordinate(47.5811, -122.3290);
static final Coordinate CLYDE_HILL = new Coordinate(47.6316, -122.2173);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public class SmokeTest {

public static final OtpApiClient API_CLIENT = new OtpApiClient(
ZoneId.of("America/New_York"),
"http://localhost:8080"
System.getenv().getOrDefault("OTP_API_URL", "https://sound-transit-qa-otp.ibi-transit.com/")
);

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.opentripplanner.smoketest;

import static org.opentripplanner.client.model.RequestMode.TRANSIT;
import static org.opentripplanner.client.model.RequestMode.WALK;
import static org.opentripplanner.smoketest.SeattleSmokeTest.LYNNWOOD_STA;
import static org.opentripplanner.smoketest.SeattleSmokeTest.OLIVE_WAY;
import static org.opentripplanner.smoketest.SeattleSmokeTest.SHORELINE;
import static org.opentripplanner.smoketest.SeattleSmokeTest.SODO;

import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.opentripplanner.smoketest.util.SmokeTestItinerary;
import org.opentripplanner.smoketest.util.SmokeTestRequest;

@Tag("smoke-test")
@Tag("soundtransit")
public class SoundTransitSmokeTest {

@Test
public void testItinerary() {
var modes = Set.of(TRANSIT, WALK);
var plan = SmokeTest.basicRouteTest(
new SmokeTestRequest(OLIVE_WAY, SHORELINE, modes),
List.of("WALK", "BUS", "WALK")
);

SmokeTestItinerary
.from(plan)
.hasLeg()
.withMode("BUS")
.withRouteShortName("E Line")
.withFarePrice(2.75f)
.assertMatches();

plan = SmokeTest.basicRouteTest(
new SmokeTestRequest(LYNNWOOD_STA, SODO, modes),
List.of("WALK", "TRAM", "WALK")
);

SmokeTestItinerary
.from(plan)
.hasLeg()
.withMode("TRAM")
.withRouteShortName("1 Line")
.withFarePrice(3f)
.assertMatches();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package org.opentripplanner.smoketest.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.opentripplanner.client.model.Itinerary;
import org.opentripplanner.client.model.Leg;
import org.opentripplanner.client.model.TripPlan;

public class SmokeTestItinerary {

private final List<LegMatcher> legMatchers = new ArrayList<>();
private final TripPlan tripPlan;

public SmokeTestItinerary(TripPlan tripPlan) {
this.tripPlan = tripPlan;
}

public static SmokeTestItinerary from(TripPlan tripPlan) {
return new SmokeTestItinerary(tripPlan);
}

public SmokeTestItinerary hasLeg() {
legMatchers.add(new LegMatcher());
return this;
}

public SmokeTestItinerary withRouteShortName(String shortName) {
getCurrentMatcher().routeShortName = shortName;
return this;
}

public SmokeTestItinerary withFarePrice(float price) {
getCurrentMatcher().farePrice = price;
return this;
}

public SmokeTestItinerary withMode(String mode) {
getCurrentMatcher().mode = mode;
return this;
}

public void assertMatches() {
for (LegMatcher matcher : legMatchers) {
List<PartialMatch> partialMatches = new ArrayList<>();
boolean foundMatch = false;

for (Itinerary itinerary : tripPlan.itineraries()) {
for (Leg leg : itinerary.legs()) {
MatchResult result = matcher.matches(leg);
if (result.isFullMatch()) {
foundMatch = true;
break;
} else if (result.hasAnyMatch()) {
partialMatches.add(new PartialMatch(leg, result));
}
}
if (foundMatch) break;
}

if (!foundMatch) {
StringBuilder error = new StringBuilder("No leg found matching all criteria:\n");
error.append(matcher.describeCriteria()).append("\n\n");

if (!partialMatches.isEmpty()) {
error.append("Partial matches found:\n");
for (PartialMatch match : partialMatches) {
error
.append("- Leg with ")
.append(match.result.getMatchingCriteria())
.append(" but missing ")
.append(match.result.getMissingCriteria())
.append("\n");
}
}
throw new AssertionError(error.toString());
}
}
}

private LegMatcher getCurrentMatcher() {
if (legMatchers.isEmpty()) {
throw new IllegalStateException("Call hasLeg() first before adding criteria");
}
return legMatchers.get(legMatchers.size() - 1);
}

private static class MatchResult {

private final Map<String, Boolean> criteria = new HashMap<>();

void addCriterion(String name, boolean matched) {
criteria.put(name, matched);
}

boolean isFullMatch() {
return criteria.values().stream().allMatch(v -> v);
}

boolean hasAnyMatch() {
return criteria.values().stream().anyMatch(v -> v);
}

String getMatchingCriteria() {
return criteria
.entrySet()
.stream()
.filter(Map.Entry::getValue)
.map(Map.Entry::getKey)
.collect(Collectors.joining(", "));
}

String getMissingCriteria() {
return criteria
.entrySet()
.stream()
.filter(e -> !e.getValue())
.map(Map.Entry::getKey)
.collect(Collectors.joining(", "));
}
}

private static class PartialMatch {

private final Leg leg;
private final MatchResult result;

PartialMatch(Leg leg, MatchResult result) {
this.leg = leg;
this.result = result;
}
}

private static class LegMatcher {

String routeShortName;
float farePrice;
String mode;

MatchResult matches(Leg leg) {
MatchResult result = new MatchResult();

if (routeShortName != null) {
boolean matches =
leg.isTransit() &&
leg.route().shortName().isPresent() &&
leg.route().shortName().get().equals(routeShortName);
result.addCriterion("route '" + routeShortName + "'", matches);
}

if (farePrice > 0) {
boolean matches = leg
.fareProducts()
.stream()
.anyMatch(fp -> fp.product().price().amount().floatValue() == farePrice);
result.addCriterion("fare $" + farePrice, matches);
}

if (mode != null) {
boolean matches = leg.mode().toString().equals(mode);
result.addCriterion("mode " + mode, matches);
}

return result;
}

String describeCriteria() {
List<String> criteria = new ArrayList<>();
if (routeShortName != null) criteria.add("route '" + routeShortName + "'");
if (farePrice > 0) criteria.add("fare $" + farePrice);
if (mode != null) criteria.add("mode " + mode);
return String.join(", ", criteria);
}
}
}

0 comments on commit bc6c310

Please sign in to comment.