@Test
public void testParallelGets() {

    AsyncLoadingCache<String, Boolean> cachedResult = new AsyncLoadingCache<>(100);
    final AtomicInteger counter = new AtomicInteger();
    CompletableFuture<Void> signal = new CompletableFuture<>();
    final Supplier<CompletableFuture<Boolean>> supplier = () -> {
        counter.incrementAndGet();
        return signal.thenApply(ignored -> true);
    };
    List<String> keys = ImmutableList.of("key-1", "key-2", "key-3");
    List<CompletableFuture<Boolean>> parallelOperations = new ArrayList<>();
    for (int i = 0; i < 20; i++) {
        for (String key : keys) {
            parallelOperations.add(cachedResult.orElseGet(key, supplier));
        }
    }
    signal.complete(null);
    List<Boolean> values = AsyncUtil.getAll(parallelOperations).join();
    for (Boolean value : values) {
        assertTrue(value);
    }
    // Don't increment after futures have already completed
    List<CompletableFuture<Boolean>> afterCompleteOperations = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
        for (String key : keys) {
            afterCompleteOperations.add(cachedResult.orElseGet(key, supplier));
        }
    }
    values = AsyncUtil.getAll(afterCompleteOperations).join();
    for (Boolean value : values) {
        assertTrue(value);
    }
    assertThat("supplier is called once per incomplete access", counter.get(), is(parallelOperations.size()));
}