AsyncRetrier { public ListenableFuture<T> execute(Supplier<ListenableFuture<T>> supplier) { return execute(supplier, maxAttempts); } AsyncRetrier(
            int maxAttempts,
            Duration delayBetweenAttempts,
            ListeningScheduledExecutorService schedulingExecutor,
            ListeningExecutorService executionExecutor,
            Predicate<T> predicate); ListenableFuture<T> execute(Supplier<ListenableFuture<T>> supplier);  }
@Test public void failsFutureIfSupplierThrows() { RuntimeException exception = new RuntimeException(); replaceableFunction = () -> { throw exception; }; ListenableFuture<Integer> hopefullyFailedFuture = retrier.execute(function); scheduler.runUntilIdle(); assertThatThrownBy(() -> Futures.getDone(hopefullyFailedFuture)).hasCause(exception); }
@Test public void returnsImmediatelyIfPredicatePasses() throws ExecutionException { ListenableFuture<Integer> result = retrier.execute(function); future.set(0); scheduler.runUntilIdle(); assertThat(Futures.getDone(result)).isZero(); }
@Test public void retriesIfPredicateFails() throws ExecutionException { ListenableFuture<Integer> result = retrier.execute(function); future.set(-1); scheduler.runUntilIdle(); verify(function, times(1)).get(); tick(RETRY_PERIOD.minus(DELTA)); verify(function, times(1)).get(); assertThat(result).isNotDone(); tick(DELTA); verify(function, times(2)).get(); assertThat(result).isNotDone(); tick(RETRY_PERIOD.minus(DELTA)); verify(function, times(2)).get(); future = SettableFuture.create(); future.set(2); tick(DELTA); assertThat(Futures.getDone(result)).isEqualTo(2); }
@Test public void doesNotRetryIndefinitely() throws ExecutionException { ListenableFuture<Integer> result = retrier.execute(function); future.set(-1); tick(RETRY_PERIOD.multipliedBy(MAX_ATTEMPTS)); assertThat(Futures.getDone(result)).isEqualTo(-1); }