@Test
  public void testFlappingTasksBackoffTruncation() {
    expectAnyMaintenanceCalls();

    makeFlappyTask("a0", null);
    makeFlappyTask("a1", "a0");
    makeFlappyTask("a2", "a1");
    IScheduledTask taskA3 =
        IScheduledTask.build(makeTask("a3", INIT).newBuilder().setAncestorId("a2"));

    expectOfferDeclineIn(10);

    Capture<Runnable> first = expectTaskGroupBackoff(10);
    // The ancestry chain is 3 long, but if the backoff strategy truncates, we don't traverse the
    // entire history.
    expect(flappingStrategy.calculateBackoffMs(0)).andReturn(5L);
    expect(flappingStrategy.calculateBackoffMs(5L)).andReturn(5L);
    Capture<Runnable> flapping = expectTaskRetryIn(10);

    expectTaskScheduled(taskA3);

    replayAndCreateScheduler();
    offerQueue.addOffer(OFFER_A);

    changeState(taskA3, INIT, PENDING);

    first.getValue().run();
    clock.waitFor(10);
    flapping.getValue().run();
  }
  @Test
  public void testFlappingTasks() {
    expectAnyMaintenanceCalls();

    makeFlappyTask("a0", null);
    IScheduledTask taskA1 =
        IScheduledTask.build(makeTask("a1", INIT).newBuilder().setAncestorId("a0"));

    expectOfferDeclineIn(10);
    Capture<Runnable> first = expectTaskGroupBackoff(10);

    expect(flappingStrategy.calculateBackoffMs(0)).andReturn(5L);
    // Since A1 has been penalized, the task has to wait for another 10 ms until the penalty has
    // expired.
    Capture<Runnable> flapping = expectTaskRetryIn(10);

    expectTaskScheduled(taskA1);

    replayAndCreateScheduler();

    offerQueue.addOffer(OFFER_A);

    changeState(taskA1, INIT, PENDING);

    first.getValue().run();
    clock.waitFor(10);
    flapping.getValue().run();
  }
 private Capture<Runnable> expectTaskGroupBackoff(long previousPenaltyMs, long nextPenaltyMs) {
   expect(retryStrategy.calculateBackoffMs(previousPenaltyMs)).andReturn(nextPenaltyMs);
   return expectTaskRetryIn(nextPenaltyMs);
 }