public void testPickupDropoff() throws Exception {
    Vertex stop_o = graph.getVertex("agency_O_depart");
    Vertex stop_p = graph.getVertex("agency_P");
    int i = 0;
    for (@SuppressWarnings("unused") Edge e : stop_o.getOutgoing()) {
      ++i;
    }
    assertTrue(i == 3);

    long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 12, 0, 0);
    RoutingRequest options = new RoutingRequest();
    options.dateTime = startTime;
    options.setRoutingContext(graph, stop_o, stop_p);
    ShortestPathTree spt = aStar.getShortestPathTree(options);
    GraphPath path = spt.getPath(stop_p, false);
    assertNotNull(path);
    long endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 12, 10, 0);
    assertEquals(endTime, path.getEndTime());

    startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 12, 0, 1);
    options.dateTime = startTime;
    options.setRoutingContext(graph, stop_o, stop_p);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_p, false);
    assertNotNull(path);
    endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 15, 10, 0);
    assertEquals(endTime, path.getEndTime());
  }
  public void testRoutingOverMidnight() throws Exception {
    // this route only runs on weekdays
    Vertex stop_g = graph.getVertex("agency_G_depart");
    Vertex stop_h = graph.getVertex("agency_H_arrive");

    ShortestPathTree spt;
    GraphPath path;
    RoutingRequest options = new RoutingRequest();

    // Friday evening
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 18, 23, 20, 0);
    options.setRoutingContext(graph, stop_g, stop_h);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_h, false);
    assertNotNull(path);
    assertEquals(4, path.states.size());

    // Saturday morning
    long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 0, 5, 0);
    options.dateTime = startTime;
    options.setRoutingContext(graph, stop_g.getLabel(), stop_h.getLabel());
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_h, false);
    assertNotNull(path);
    assertEquals(4, path.states.size());
    long endTime = path.getEndTime();
    assertTrue(endTime < startTime + 60 * 60);
  }
  public void testFewestTransfers() {
    Vertex stop_c = graph.getVertex("agency_C");
    Vertex stop_d = graph.getVertex("agency_D");
    RoutingRequest options = new RoutingRequest();
    options.optimize = OptimizeType.QUICK;
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 0, 0);
    options.setRoutingContext(graph, stop_c, stop_d);

    ShortestPathTree spt = aStar.getShortestPathTree(options);

    // when optimizing for speed, take the fast two-bus path
    GraphPath path = spt.getPath(stop_d, false);
    assertNotNull(path);
    assertEquals(
        TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 20, 0), path.getEndTime());

    // when optimizing for fewest transfers, take the slow one-bus path
    options.transferPenalty = 1800;
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_d, false);
    assertNotNull(path);
    assertEquals(
        TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 50, 0), path.getEndTime());
  }
  public void testFrequencies() {
    Vertex stop_u = graph.getVertex("agency_U_depart");
    Vertex stop_v = graph.getVertex("agency_V_arrive");

    ShortestPathTree spt;
    GraphPath path;

    RoutingRequest options = new RoutingRequest();
    options.setModes(new TraverseModeSet("TRANSIT"));
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0);
    options.setRoutingContext(graph, stop_u, stop_v);

    // U to V - original stop times - shouldn't be used
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_v, false);
    assertNotNull(path);
    assertEquals(4, path.states.size());
    long endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 6, 40, 0);
    assertEquals(endTime, path.getEndTime());

    // U to V - first frequency
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 7, 0, 0);
    options.setRoutingContext(graph, stop_u, stop_v);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_v, false);
    assertNotNull(path);
    assertEquals(4, path.states.size());
    endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 7, 40, 0);
    assertEquals(endTime, path.getEndTime());

    // U to V - second frequency
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 14, 0, 0);
    options.setRoutingContext(graph, stop_u, stop_v);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_v, false);
    assertNotNull(path);
    assertEquals(4, path.states.size());
    endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 14, 40, 0);
    assertEquals(endTime, path.getEndTime());

    boolean boarded = false;
    for (FrequencyBoard e : filter(stop_u.getOutgoing(), FrequencyBoard.class)) {
      boarded = true;
      FrequencyBoard board = (FrequencyBoard) e;
      FrequencyBasedTripPattern pattern = board.getPattern();
      int previousArrivalTime = pattern.getPreviousArrivalTime(0, 0, false, false, false);
      assertTrue(previousArrivalTime < 0);

      previousArrivalTime = pattern.getPreviousArrivalTime(0, 60 * 60 * 7 - 1, false, false, false);
      assertEquals(60 * 60 * 6, previousArrivalTime);

      previousArrivalTime = pattern.getPreviousArrivalTime(0, 60 * 60 * 11, false, false, false);
      assertEquals(60 * 60 * 10, previousArrivalTime);

      previousArrivalTime = pattern.getPreviousArrivalTime(0, 60 * 60 * 18, false, false, false);
      assertEquals(60 * 60 * 16, previousArrivalTime);
    }
    assertTrue(boarded);
  }
  public void testRouting() throws Exception {

    Vertex stop_a = graph.getVertex("agency_A");
    Vertex stop_b = graph.getVertex("agency_B");
    Vertex stop_c = graph.getVertex("agency_C");
    Vertex stop_d = graph.getVertex("agency_D");
    Vertex stop_e = graph.getVertex("agency_E");

    RoutingRequest options = new RoutingRequest();
    // test feed is designed for instantaneous transfers
    options.setTransferSlack(0);

    long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0);
    options.dateTime = startTime;

    ShortestPathTree spt;
    GraphPath path;

    // A to B
    options.setRoutingContext(graph, stop_a, stop_b);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_b, false);
    assertNotNull(path);
    assertEquals(6, path.states.size());

    // A to C
    options.setRoutingContext(graph, stop_a, stop_c);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_c, false);
    assertNotNull(path);
    assertEquals(8, path.states.size());

    // A to D (change at C)
    options.setRoutingContext(graph, stop_a, stop_d);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_d, false);
    assertNotNull(path);
    // there are two paths of different lengths
    // both arrive at 40 minutes after midnight
    // assertTrue(path.states.size() == 13);
    long endTime = startTime + 40 * 60;
    assertEquals(endTime, path.getEndTime());

    // A to E (change at C)
    options.setRoutingContext(graph, stop_a, stop_e);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_e, false);
    assertNotNull(path);
    assertTrue(path.states.size() == 14);
    endTime = startTime + 70 * 60;
    assertEquals(endTime, path.getEndTime());
  }
  public void testRouting() throws Exception {

    Vertex stop_a = graph.getVertex("agency_A");
    Vertex stop_b = graph.getVertex("agency_B");
    Vertex stop_c = graph.getVertex("agency_C");
    Vertex stop_d = graph.getVertex("agency_D");
    Vertex stop_e = graph.getVertex("agency_E");

    RoutingRequest options = new RoutingRequest();
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0);

    ShortestPathTree spt;
    GraphPath path;

    // A to B
    options.setRoutingContext(graph, stop_a, stop_b);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_b, false);
    assertNotNull(path);
    assertEquals(extractStopVertices(path), Lists.newArrayList(stop_a, stop_b));

    // A to C
    options.setRoutingContext(graph, stop_a, stop_c);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_c, false);
    assertNotNull(path);
    assertEquals(extractStopVertices(path), Lists.newArrayList(stop_a, stop_c));

    // A to D
    options.setRoutingContext(graph, stop_a, stop_d);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_d, false);
    assertNotNull(path);
    assertEquals(extractStopVertices(path), Lists.newArrayList(stop_a, stop_c, stop_d));
    long endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0) + 40 * 60;
    assertEquals(endTime, path.getEndTime());

    // A to E
    options.setRoutingContext(graph, stop_a, stop_e);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_e, false);
    assertNotNull(path);
    assertEquals(extractStopVertices(path), Lists.newArrayList(stop_a, stop_c, stop_e));
    endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0) + 70 * 60;
    assertEquals(endTime, path.getEndTime());
  }
  public void testTransfers() throws Exception {
    TransferTable transferTable = graph.getTransferTable();

    // create dummy routes and trips
    Route fromRoute = new Route();
    fromRoute.setId(new AgencyAndId("agency", "1"));
    Trip fromTrip = new Trip();
    fromTrip.setId(new AgencyAndId("agency", "1.1"));
    fromTrip.setRoute(fromRoute);
    Route toRoute = new Route();
    toRoute.setId(new AgencyAndId("agency", "2"));
    Trip toTrip = new Trip();
    toTrip.setId(new AgencyAndId("agency", "2.1"));
    toTrip.setRoute(toRoute);
    Trip toTrip2 = new Trip();
    toTrip2.setId(new AgencyAndId("agency", "2.2"));
    toTrip2.setRoute(toRoute);

    // find stops
    Stop stopK = ((TransitStopArrive) graph.getVertex("agency_K_arrive")).getStop();
    Stop stopN = ((TransitStopDepart) graph.getVertex("agency_N_depart")).getStop();
    Stop stopM = ((TransitStopDepart) graph.getVertex("agency_M_depart")).getStop();

    assertTrue(transferTable.hasPreferredTransfers());
    assertEquals(
        StopTransfer.UNKNOWN_TRANSFER,
        transferTable.getTransferTime(stopN, stopM, fromTrip, toTrip));
    assertEquals(
        StopTransfer.FORBIDDEN_TRANSFER,
        transferTable.getTransferTime(stopK, stopM, fromTrip, toTrip));
    assertEquals(
        StopTransfer.PREFERRED_TRANSFER,
        transferTable.getTransferTime(stopN, stopK, toTrip, toTrip2));
    assertEquals(
        StopTransfer.TIMED_TRANSFER, transferTable.getTransferTime(stopN, stopK, fromTrip, toTrip));
    assertEquals(15, transferTable.getTransferTime(stopN, stopK, fromTrip, toTrip2));

    Vertex e_arrive = graph.getVertex("agency_E_arrive");
    Vertex f_depart = graph.getVertex("agency_F_depart");
    Edge edge = new TransferEdge(e_arrive, f_depart, 10000, 10000);

    long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 18, 0, 50, 0);
    Vertex stop_b = graph.getVertex("agency_B_depart");
    Vertex stop_g = graph.getVertex("agency_G_arrive");
    RoutingRequest options = new RoutingRequest();
    options.dateTime = startTime;
    options.setRoutingContext(graph, stop_b, stop_g);
    ShortestPathTree spt = aStar.getShortestPathTree(options);

    GraphPath path = spt.getPath(stop_g, false);
    assertNotNull(path);

    assertTrue(
        "expected to use much later trip due to min transfer time",
        path.getEndTime() - startTime > 4.5 * 60 * 60);

    /* cleanup */
    e_arrive.removeOutgoing(edge);
    f_depart.removeIncoming(edge);
  }
  public void testWheelchairAccessible() throws Exception {
    Vertex near_a = graph.getVertex("near_1_agency_A");
    Vertex near_b = graph.getVertex("near_1_agency_B");
    Vertex near_c = graph.getVertex("near_1_agency_C");

    Vertex stop_d = graph.getVertex("agency_D");
    Vertex split_d = null;
    for (StreetTransitLink e : filter(stop_d.getOutgoing(), StreetTransitLink.class)) {
      split_d = e.getToVertex();
    }

    RoutingRequest options = new RoutingRequest();
    options.wheelchairAccessible = true;
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 18, 0, 0, 0);

    ShortestPathTree spt;
    GraphPath path;

    // stop B is accessible, so there should be a path.
    options.setRoutingContext(graph, near_a, near_b);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(near_b, false);
    assertNotNull(path);

    // stop C is not accessible, so there should be no path.
    options.setRoutingContext(graph, near_a, near_c);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(near_c, false);
    assertNull(path);

    // from stop A to stop D would normally be trip 1.1 to trip 2.1, arriving at 00:30. But trip
    // 2 is not accessible, so we'll do 1.1 to 3.1, arriving at 01:00
    GregorianCalendar time = new GregorianCalendar(2009, 8, 18, 0, 0, 0);
    time.setTimeZone(TimeZone.getTimeZone("America/New_York"));
    options.dateTime = TestUtils.toSeconds(time);
    options.setRoutingContext(graph, near_a, split_d);
    spt = aStar.getShortestPathTree(options);

    time.add(Calendar.HOUR, 1);
    time.add(Calendar.SECOND, 1); // for the StreetTransitLink
    path = spt.getPath(split_d, false);
    assertNotNull(path);
    assertEquals(TestUtils.toSeconds(time), path.getEndTime());
  }
  public void testTraverseMode() throws Exception {
    Vertex stop_a = graph.getVertex("agency_A_depart");
    Vertex stop_b = graph.getVertex("agency_B_arrive");

    ShortestPathTree spt;

    RoutingRequest options = new RoutingRequest();
    options.setModes(new TraverseModeSet("TRAINISH"));
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 0, 0, 0, 0);
    options.setRoutingContext(graph, stop_a, stop_b);
    spt = aStar.getShortestPathTree(options);

    // a to b is bus only
    assertNull(spt.getPath(stop_b, false));

    options.setModes(new TraverseModeSet("TRAINISH,BUSISH"));
    spt = aStar.getShortestPathTree(options);

    assertNotNull(spt.getPath(stop_b, false));
  }
  public void testInterlining() throws Exception {
    Vertex stop_i = graph.getVertex("agency_I_depart");
    Vertex stop_k = graph.getVertex("agency_K_arrive");

    long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 12, 0, 0);
    RoutingRequest options = new RoutingRequest();
    options.dateTime = startTime;
    options.setRoutingContext(graph, stop_i, stop_k);
    ShortestPathTree spt = aStar.getShortestPathTree(options);

    GraphPath path = spt.getPath(stop_k, false);
    int num_alights = 0;
    for (State s : path.states) {
      if (s.getBackEdge() instanceof TransitBoardAlight
          && !((TransitBoardAlight) s.getBackEdge()).isBoarding()) {
        num_alights += 1;
      }
      if (s.getBackEdge() instanceof PatternDwell) {
        assertEquals(10 * 60, s.getTimeDeltaSeconds());
      }
    }
    assertEquals(1, num_alights);

    options.setArriveBy(true);
    options.setRoutingContext(graph, stop_i, stop_k);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_i, false);
    //        path.reverse();
    num_alights = 0;
    for (State s : path.states) {
      if (s.getBackEdge() instanceof TransitBoardAlight
          && !((TransitBoardAlight) s.getBackEdge()).isBoarding()) {
        num_alights += 1;
      }
      if (s.getBackEdge() instanceof PatternDwell) {
        assertEquals(10 * 60, s.getTimeDeltaSeconds());
      }
    }
    assertEquals(1, num_alights);
  }
  /** Test that useless dwell edges are in fact removed. */
  public void testDwellSimplification() {
    Vertex stop_f = graph.getVertex("agency_F_depart");
    Vertex stop_h = graph.getVertex("agency_H_arrive");

    RoutingRequest options = new RoutingRequest();
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 18, 5, 0, 0);
    options.setRoutingContext(graph, stop_f, stop_h);

    ShortestPathTree spt = aStar.getShortestPathTree(options);
    GraphPath path = spt.getPath(stop_h, false);
    assertNotNull(path);
    assertEquals(5, path.states.size());
  }
  public void testTimelessStops() throws Exception {
    Vertex stop_d = graph.getVertex("agency_D");
    Vertex stop_c = graph.getVertex("agency_C");
    RoutingRequest options = new RoutingRequest();
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 10, 0, 0);
    options.setRoutingContext(graph, stop_d, stop_c);
    ShortestPathTree spt = aStar.getShortestPathTree(options);

    GraphPath path = spt.getPath(stop_c, false);
    assertNotNull(path);
    assertEquals(
        TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 11, 0, 0), path.getEndTime());
  }
  public void testTripBikesAllowed() throws Exception {
    Vertex stop_a = graph.getVertex("agency_A");
    Vertex stop_b = graph.getVertex("agency_B");
    Vertex stop_c = graph.getVertex("agency_C");
    Vertex stop_d = graph.getVertex("agency_D");

    RoutingRequest options = new RoutingRequest();
    options.getModes().setWalk(false);
    options.getModes().setBicycle(true);
    options.getModes().setTransit(true);
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 18, 0, 0, 0);
    options.setRoutingContext(graph, stop_a, stop_b);

    ShortestPathTree spt;
    GraphPath path;

    // route: bikes allowed, trip: no value
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_b, false);
    assertNotNull(path);

    // route: bikes allowed, trip: bikes not allowed
    options.setRoutingContext(graph, stop_d, stop_c);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_c, false);
    assertNull(path);

    // route: bikes not allowed, trip: bikes allowed
    options.setRoutingContext(graph, stop_c, stop_d);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_d, false);
    assertNotNull(path);
  }
  public void testDwell() throws Exception {
    Vertex stop_a = graph.getVertex("agency_A_depart");
    Vertex stop_c = graph.getVertex("agency_C_arrive");

    RoutingRequest options = new RoutingRequest();
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 8, 0, 0);
    options.setRoutingContext(graph, stop_a, stop_c);
    ShortestPathTree spt = aStar.getShortestPathTree(options);

    GraphPath path = spt.getPath(stop_c, false);
    assertNotNull(path);
    assertEquals(6, path.states.size());
    long endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 8, 30, 0);
    assertEquals(endTime, path.getEndTime());
  }
  public void testPathways() throws Exception {

    Vertex entrance = graph.getVertex("agency_entrance_a");
    assertNotNull(entrance);
    Vertex stop = graph.getVertex("agency_A");
    assertNotNull(stop);

    RoutingRequest options = new RoutingRequest();
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 0, 0);
    options.setRoutingContext(graph, entrance, stop);
    ShortestPathTree spt = aStar.getShortestPathTree(options);

    GraphPath path = spt.getPath(stop, false);
    assertNotNull(path);
    assertEquals(
        TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 0, 34), path.getEndTime());
  }
  public void testRunForTrain() {
    /**
     * This is the notorious Byrd bug: we're going from Q to T at 8:30. There's a trip from S to T
     * at 8:50 and a second one at 9:50. To get to S by 8:50, we need to take trip 12.1 from Q to R,
     * and 13.1 from R to S. If we take the direct-but-slower 11.1, we'll miss the 8:50 and have to
     * catch the 9:50.
     */
    Vertex destination = graph.getVertex("agency_T");
    RoutingRequest options = new RoutingRequest();
    // test is designed such that transfers must be instantaneous
    options.setTransferSlack(0);
    GregorianCalendar startTime = new GregorianCalendar(2009, 11, 2, 8, 30, 0);
    startTime.setTimeZone(TimeZone.getTimeZone("America/New_York"));
    options.dateTime = TestUtils.toSeconds(startTime);
    options.setRoutingContext(graph, "agency_Q", destination.getLabel());
    ShortestPathTree spt = aStar.getShortestPathTree(options);
    GraphPath path = spt.getPath(destination, false);

    // TODO this is wrong (milliseconds)
    //        long endTime = path.getEndTime();
    //        Calendar c = new GregorianCalendar();
    //        c.setTimeInMillis(endTime);
    //        assertTrue(endTime - startTime.getTimeInMillis() < 7200);
  }
  @Test
  public void testUpdate() {
    TripUpdate tripUpdate;
    TripUpdate.Builder tripUpdateBuilder;
    TripDescriptor.Builder tripDescriptorBuilder;
    StopTimeUpdate.Builder stopTimeUpdateBuilder;
    StopTimeEvent.Builder stopTimeEventBuilder;

    int trip_1_1_index = timetable.getTripIndex(new AgencyAndId("agency", "1.1"));

    Vertex stop_a = graph.getVertex("agency_A");
    Vertex stop_c = graph.getVertex("agency_C");
    RoutingRequest options = new RoutingRequest();

    ShortestPathTree spt;
    GraphPath path;

    // non-existing trip
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("b");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.CANCELED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    tripUpdate = tripUpdateBuilder.build();
    assertFalse(timetable.update(tripUpdate, "a", timeZone, serviceDate));

    // update trip with bad data
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(0);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SKIPPED);
    tripUpdate = tripUpdateBuilder.build();
    assertFalse(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    // update trip with non-increasing data
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(2);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder();
    stopTimeEventBuilder.setTime(
        TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 10, 1));
    stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder();
    stopTimeEventBuilder.setTime(
        TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 10, 0));
    tripUpdate = tripUpdateBuilder.build();
    assertFalse(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    // ---
    long startTime = TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 0, 0);
    long endTime;
    options.dateTime = startTime;

    // ---
    options.setRoutingContext(graph, stop_a, stop_c);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_c, false);
    assertNotNull(path);
    endTime = startTime + 20 * 60;
    assertEquals(endTime, path.getEndTime());

    // update trip
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(1);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder();
    stopTimeEventBuilder.setTime(
        TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 2, 0));
    stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder();
    stopTimeEventBuilder.setTime(
        TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 2, 0));
    tripUpdate = tripUpdateBuilder.build();
    assertEquals(20 * 60, timetable.getTripTimes(trip_1_1_index).getArrivalTime(2));
    assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate));
    assertEquals(20 * 60 + 120, timetable.getTripTimes(trip_1_1_index).getArrivalTime(2));

    // ---
    options.setRoutingContext(graph, stop_a, stop_c);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_c, false);
    assertNotNull(path);
    endTime = startTime + 20 * 60 + 120;
    assertEquals(endTime, path.getEndTime());

    // cancel trip
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.CANCELED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    tripUpdate = tripUpdateBuilder.build();
    assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    TripTimes tripTimes = timetable.getTripTimes(trip_1_1_index);
    for (int i = 0; i < tripTimes.getNumStops(); i++) {
      assertEquals(TripTimes.UNAVAILABLE, tripTimes.getDepartureTime(i));
      assertEquals(TripTimes.UNAVAILABLE, tripTimes.getArrivalTime(i));
    }

    // ---
    options.setRoutingContext(graph, stop_a, stop_c);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_c, false);
    assertNotNull(path);
    endTime = startTime + 40 * 60;
    assertEquals(endTime, path.getEndTime());

    // update trip arrival time incorrectly
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(1);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder();
    stopTimeEventBuilder.setDelay(0);
    tripUpdate = tripUpdateBuilder.build();
    assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    // update trip arrival time only
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(2);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder();
    stopTimeEventBuilder.setDelay(1);
    tripUpdate = tripUpdateBuilder.build();
    assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    // update trip departure time only
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(2);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder();
    stopTimeEventBuilder.setDelay(-1);
    tripUpdate = tripUpdateBuilder.build();
    assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    // update trip using stop id
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopId("B");
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder();
    stopTimeEventBuilder.setDelay(-1);
    tripUpdate = tripUpdateBuilder.build();
    assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    // update trip arrival time at first stop and make departure time incoherent at second stop
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(1);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder();
    stopTimeEventBuilder.setDelay(0);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(1);
    stopTimeUpdateBuilder.setStopSequence(2);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder();
    stopTimeEventBuilder.setDelay(-1);
    tripUpdate = tripUpdateBuilder.build();
    assertFalse(timetable.update(tripUpdate, "agency", timeZone, serviceDate));
  }