package io.airlift.concurrent;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import io.airlift.units.Duration;
import org.testng.annotations.AfterClass;
import org.testng.annotations.Test;
import java.io.IOException;
import java.sql.SQLException;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Consumer;
import java.util.function.Function;
import static com.google.common.util.concurrent.Futures.immediateFailedFuture;
import static com.google.common.util.concurrent.Futures.immediateFuture;
import static io.airlift.concurrent.MoreFutures.addTimeout;
import static io.airlift.concurrent.MoreFutures.allAsList;
import static io.airlift.concurrent.MoreFutures.failedFuture;
import static io.airlift.concurrent.MoreFutures.firstCompletedFuture;
import static io.airlift.concurrent.MoreFutures.getFutureValue;
import static io.airlift.concurrent.MoreFutures.mirror;
import static io.airlift.concurrent.MoreFutures.propagateCancellation;
import static io.airlift.concurrent.MoreFutures.toCompletableFuture;
import static io.airlift.concurrent.MoreFutures.toListenableFuture;
import static io.airlift.concurrent.MoreFutures.tryGetFutureValue;
import static io.airlift.concurrent.MoreFutures.unmodifiableFuture;
import static io.airlift.concurrent.MoreFutures.unwrapCompletionException;
import static io.airlift.concurrent.MoreFutures.whenAnyComplete;
import static io.airlift.concurrent.Threads.daemonThreadsNamed;
import static io.airlift.testing.Assertions.assertInstanceOf;
import static java.util.concurrent.CompletableFuture.completedFuture;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertSame;
import static org.testng.Assert.assertThrows;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
@SuppressWarnings("deprecation")
public class TestMoreFutures
{
private final ScheduledExecutorService executorService = newSingleThreadScheduledExecutor(daemonThreadsNamed("test-%s"));
@AfterClass
public void tearDown()
{
executorService.shutdownNow();
}
@Test
public void propagateCancellationWithoutInterrupt()
throws Exception
{
// Test interrupt override
ExtendedSettableFuture<Object> fromFuture = ExtendedSettableFuture.create();
ExtendedSettableFuture<Object> toFuture = ExtendedSettableFuture.create();
propagateCancellation(fromFuture, toFuture, false);
fromFuture.cancel(true);
assertTrue(toFuture.isCancelled());
assertFalse(toFuture.checkWasInterrupted());
fromFuture = ExtendedSettableFuture.create();
toFuture = ExtendedSettableFuture.create();
propagateCancellation(fromFuture, toFuture, false);
fromFuture.cancel(false);
assertTrue(toFuture.isCancelled());
assertFalse(toFuture.checkWasInterrupted());
}
@Test
public void propagateCancellationWithInterrupt()
throws Exception
{
ExtendedSettableFuture<Object> fromFuture = ExtendedSettableFuture.create();
ExtendedSettableFuture<Object> toFuture = ExtendedSettableFuture.create();
propagateCancellation(fromFuture, toFuture, true);
fromFuture.cancel(true);
assertTrue(toFuture.isCancelled());
assertTrue(toFuture.checkWasInterrupted());
// Test interrupt override
fromFuture = ExtendedSettableFuture.create();
toFuture = ExtendedSettableFuture.create();
propagateCancellation(fromFuture, toFuture, true);
fromFuture.cancel(false);
assertTrue(toFuture.isCancelled());
assertTrue(toFuture.checkWasInterrupted());
}
@Test
public void testMirror()
throws Exception
{
// Test return value
ExtendedSettableFuture<String> fromFuture = ExtendedSettableFuture.create();
SettableFuture<String> toFuture = SettableFuture.create();
mirror(fromFuture, toFuture, true);
fromFuture.set("abc");
assertEquals(toFuture.get(), "abc");
// Test exception
fromFuture = ExtendedSettableFuture.create();
toFuture = SettableFuture.create();
mirror(fromFuture, toFuture, true);
fromFuture.setException(new RuntimeException());
assertThrows(ExecutionException.class, toFuture::get);
// Test cancellation without interrupt
fromFuture = ExtendedSettableFuture.create();
toFuture = SettableFuture.create();
mirror(fromFuture, toFuture, false);
toFuture.cancel(true);
// Parent Future should receive the cancellation
assertTrue(fromFuture.isCancelled());
assertFalse(fromFuture.checkWasInterrupted());
// Test cancellation with interrupt
fromFuture = ExtendedSettableFuture.create();
toFuture = SettableFuture.create();
mirror(fromFuture, toFuture, true);
toFuture.cancel(false);
// Parent Future should receive the cancellation
assertTrue(fromFuture.isCancelled());
assertTrue(fromFuture.checkWasInterrupted());
}
@Test
public void testUnwrapCompletionException()
{
RuntimeException original = new RuntimeException();
assertSame(unwrapCompletionException(original), original);
assertSame(unwrapCompletionException(new CompletionException(original)), original);
CompletionException completion = new CompletionException(null);
assertSame(unwrapCompletionException(completion), completion);
}
@Test
public void testModifyUnmodifiableFuture()
throws Exception
{
CompletableFuture<String> future = new CompletableFuture<>();
CompletableFuture<String> unmodifiableFuture = unmodifiableFuture(future);
// completion results in an UnsupportedOperationException
assertFailure(() -> unmodifiableFuture.complete("fail"), UnsupportedOperationException.class::isInstance);
assertFalse(future.isDone());
assertFalse(unmodifiableFuture.isDone());
assertFailure(() -> unmodifiableFuture.completeExceptionally(new IOException()), UnsupportedOperationException.class::isInstance);
assertFalse(future.isDone());
assertFalse(unmodifiableFuture.isDone());
assertFailure(() -> unmodifiableFuture.obtrudeValue("fail"), UnsupportedOperationException.class::isInstance);
assertFalse(future.isDone());
assertFalse(unmodifiableFuture.isDone());
assertFailure(() -> unmodifiableFuture.obtrudeException(new IOException()), UnsupportedOperationException.class::isInstance);
assertFalse(future.isDone());
assertFalse(unmodifiableFuture.isDone());
// cancel is ignored
assertFalse(unmodifiableFuture.cancel(false));
assertFalse(future.isDone());
assertFalse(unmodifiableFuture.isDone());
assertFalse(unmodifiableFuture.cancel(true));
assertFalse(future.isDone());
assertFalse(unmodifiableFuture.isDone());
assertFalse(unmodifiableFuture.completeExceptionally(new CancellationException()));
assertFalse(future.isDone());
assertFalse(unmodifiableFuture.isDone());
}
@Test
public void testModifyCancelableUnmodifiableFuture()
throws Exception
{
CompletableFuture<String> future = new CompletableFuture<>();
CompletableFuture<String> unmodifiableFuture = unmodifiableFuture(future, true);
// completion results in an UnsupportedOperationException
assertFailure(() -> unmodifiableFuture.complete("fail"), UnsupportedOperationException.class::isInstance);
assertFalse(future.isDone());
assertFalse(unmodifiableFuture.isDone());
assertFailure(() -> unmodifiableFuture.completeExceptionally(new IOException()), UnsupportedOperationException.class::isInstance);
assertFalse(future.isDone());
assertFalse(unmodifiableFuture.isDone());
assertFailure(() -> unmodifiableFuture.obtrudeValue("fail"), UnsupportedOperationException.class::isInstance);
assertFalse(future.isDone());
assertFalse(unmodifiableFuture.isDone());
assertFailure(() -> unmodifiableFuture.obtrudeException(new IOException()), UnsupportedOperationException.class::isInstance);
assertFalse(future.isDone());
assertFalse(unmodifiableFuture.isDone());
// cancel is propagated so test separately
}
@Test
public void testUnmodifiableFutureCancelPropagation()
throws Exception
{
CompletableFuture<String> future = new CompletableFuture<>();
CompletableFuture<String> unmodifiableFuture = unmodifiableFuture(future, true);
assertTrue(unmodifiableFuture.cancel(false));
assertTrue(future.isDone());
assertTrue(future.isCancelled());
assertTrue(unmodifiableFuture.isDone());
assertTrue(unmodifiableFuture.isCancelled());
future = new CompletableFuture<>();
unmodifiableFuture = unmodifiableFuture(future, true);
assertTrue(unmodifiableFuture.cancel(true));
assertTrue(future.isDone());
assertTrue(future.isCancelled());
assertTrue(unmodifiableFuture.isDone());
assertTrue(unmodifiableFuture.isCancelled());
future = new CompletableFuture<>();
unmodifiableFuture = unmodifiableFuture(future, true);
assertTrue(unmodifiableFuture.completeExceptionally(new CancellationException()));
assertTrue(future.isDone());
assertTrue(future.isCancelled());
assertTrue(unmodifiableFuture.isDone());
assertTrue(unmodifiableFuture.isCancelled());
}
@Test
public void testCompleteUnmodifiableFuture()
throws Exception
{
CompletableFuture<String> future = new CompletableFuture<>();
CompletableFuture<String> unmodifiableFuture = unmodifiableFuture(future);
assertTrue(future.complete("done"));
assertEquals(future.getNow(null), "done");
assertTrue(unmodifiableFuture.isDone());
assertEquals(unmodifiableFuture.getNow(null), "done");
}
@Test
public void testCompleteExceptionallyUnmodifiableFuture()
throws Exception
{
CompletableFuture<String> future = new CompletableFuture<>();
CompletableFuture<String> unmodifiableFuture = unmodifiableFuture(future);
assertTrue(future.completeExceptionally(new SQLException("foo")));
assertFailure(() -> getFutureValue(future, SQLException.class), e -> {
assertInstanceOf(e, SQLException.class);
assertEquals(e.getMessage(), "foo");
});
assertTrue(unmodifiableFuture.isDone());
assertFailure(() -> getFutureValue(unmodifiableFuture, SQLException.class), e -> {
assertInstanceOf(e, SQLException.class);
assertEquals(e.getMessage(), "foo");
});
}
@Test
public void testAlreadyCompleteUnmodifiableFuture()
throws Exception
{
CompletableFuture<String> future = completedFuture("done");
CompletableFuture<String> unmodifiableFuture = unmodifiableFuture(future);
assertEquals(future.getNow(null), "done");
assertTrue(unmodifiableFuture.isDone());
assertEquals(unmodifiableFuture.getNow(null), "done");
}
@Test
public void testAlreadyCompleteExceptionallyUnmodifiableFuture()
throws Exception
{
CompletableFuture<String> future = failedFuture(new SQLException("foo"));
CompletableFuture<String> unmodifiableFuture = unmodifiableFuture(future);
assertFailure(() -> getFutureValue(future, SQLException.class), e -> {
assertInstanceOf(e, SQLException.class);
assertEquals(e.getMessage(), "foo");
});
assertTrue(unmodifiableFuture.isDone());
assertFailure(() -> getFutureValue(unmodifiableFuture, SQLException.class), e -> {
assertInstanceOf(e, SQLException.class);
assertEquals(e.getMessage(), "foo");
});
}
@Test
public void testFailedFuture()
throws Exception
{
CompletableFuture<Object> future = failedFuture(new SQLException("foo"));
assertTrue(future.isCompletedExceptionally());
assertFailure(future::get, e -> {
assertInstanceOf(e, ExecutionException.class);
assertTrue(e.getCause() instanceof SQLException);
assertEquals(e.getCause().getMessage(), "foo");
});
}
@Test
public void testGetFutureValue()
throws Exception
{
assertGetUnchecked(MoreFutures::getFutureValue);
}
@Test
public void testGetFutureValueWithExceptionType()
throws Exception
{
assertGetUnchecked(future -> getFutureValue(future, IOException.class));
assertFailure(() -> getFutureValue(failedFuture(new SQLException("foo")), SQLException.class), e -> {
assertInstanceOf(e, SQLException.class);
assertEquals(e.getMessage(), "foo");
});
}
@Test
public void testTryGetFutureValue()
throws Exception
{
assertGetUnchecked(future -> {
Optional<?> optional = tryGetFutureValue(future);
if (optional.isPresent()) {
return optional.get();
}
// null value is also absent
assertNull(getFutureValue(future));
return null;
});
assertEquals(tryGetFutureValue(new CompletableFuture<>()), Optional.empty());
}
@Test
public void testTryGetFutureValueWithWait()
throws Exception
{
assertGetUnchecked(future -> {
Optional<?> optional = tryGetFutureValue(future, 100, MILLISECONDS);
if (optional.isPresent()) {
return optional.get();
}
// null value is also absent
assertNull(getFutureValue(future));
return null;
});
assertEquals(tryGetFutureValue(new CompletableFuture<>(), 10, MILLISECONDS), Optional.empty());
}
@Test
public void testTryGetFutureValueWithExceptionType()
throws Exception
{
assertGetUnchecked(future -> {
Optional<?> optional = tryGetFutureValue(future, 100, MILLISECONDS, IOException.class);
if (optional.isPresent()) {
return optional.get();
}
// null value is also absent
assertNull(getFutureValue(future, IOException.class));
return null;
});
assertEquals(tryGetFutureValue(new CompletableFuture<>(), 10, MILLISECONDS), Optional.empty());
assertFailure(() -> tryGetFutureValue(failedFuture(new SQLException("foo")), 10, MILLISECONDS, SQLException.class), e -> {
assertInstanceOf(e, SQLException.class);
assertEquals(e.getMessage(), "foo");
});
}
@Test
public void testWhenAnyComplete()
throws Exception
{
assertGetUncheckedListenable(future -> getFutureValue(whenAnyComplete(ImmutableList.of(SettableFuture.create(), future, SettableFuture.create()))));
assertFailure(() -> whenAnyComplete(null), e -> assertInstanceOf(e, NullPointerException.class));
assertFailure(() -> whenAnyComplete(ImmutableList.of()), e -> assertInstanceOf(e, IllegalArgumentException.class));
assertEquals(
tryGetFutureValue(whenAnyComplete(ImmutableList.of(SettableFuture.create(), SettableFuture.create())), 10, MILLISECONDS),
Optional.empty());
}
@Test
public void testAnyOf()
throws Exception
{
assertGetUnchecked(future -> getFutureValue(firstCompletedFuture(ImmutableList.of(new CompletableFuture<>(), future, new CompletableFuture<>()))));
assertFailure(() -> firstCompletedFuture(null), e -> assertInstanceOf(e, NullPointerException.class));
assertFailure(() -> firstCompletedFuture(ImmutableList.of()), e -> assertInstanceOf(e, IllegalArgumentException.class));
assertEquals(
tryGetFutureValue(firstCompletedFuture(ImmutableList.of(new CompletableFuture<>(), new CompletableFuture<>())), 10, MILLISECONDS),
Optional.empty());
}
@Test
public void testToFromListenableFuture()
throws Exception
{
assertGetUnchecked(future -> getFutureValue(toCompletableFuture(toListenableFuture(future))));
SettableFuture<?> settableFuture = SettableFuture.create();
toCompletableFuture(settableFuture).cancel(true);
assertTrue(settableFuture.isCancelled());
CompletableFuture<Object> completableFuture = new CompletableFuture<>();
toListenableFuture(completableFuture).cancel(true);
assertTrue(completableFuture.isCancelled());
assertEquals(tryGetFutureValue(toCompletableFuture(SettableFuture.create()), 10, MILLISECONDS), Optional.empty());
assertEquals(tryGetFutureValue(toListenableFuture(new CompletableFuture<>()), 10, MILLISECONDS), Optional.empty());
}
@Test
public void testEmptyAllAsList()
throws Exception
{
CompletableFuture<List<Object>> future = allAsList(ImmutableList.of());
assertTrue(future.isDone());
assertFalse(future.isCompletedExceptionally());
assertFalse(future.isCancelled());
assertEquals(future.join(), ImmutableList.of());
}
@Test
public void testSingleElementAllAsList()
throws Exception
{
CompletableFuture<String> element1 = new CompletableFuture<>();
CompletableFuture<List<Object>> future = allAsList(ImmutableList.of(element1));
assertFalse(future.isDone());
assertFalse(future.isCancelled());
element1.complete("a");
assertTrue(future.isDone());
assertFalse(future.isCompletedExceptionally());
assertFalse(future.isCancelled());
assertEquals(future.join(), ImmutableList.of("a"));
}
@Test
public void testExceptionalSingleElementAllAsList()
throws Exception
{
CompletableFuture<String> element1 = new CompletableFuture<>();
CompletableFuture<List<Object>> future = allAsList(ImmutableList.of(element1));
assertFalse(future.isDone());
assertFalse(future.isCancelled());
element1.completeExceptionally(new RuntimeException());
assertTrue(future.isDone());
assertTrue(future.isCompletedExceptionally());
assertFalse(future.isCancelled());
}
@Test
public void testMultipleElementAllAsList()
throws Exception
{
CompletableFuture<String> element1 = new CompletableFuture<>();
CompletableFuture<String> element2 = new CompletableFuture<>();
CompletableFuture<List<Object>> future = allAsList(ImmutableList.of(element1, element2));
assertFalse(future.isDone());
assertFalse(future.isCancelled());
element1.complete("a");
assertFalse(future.isDone());
assertFalse(future.isCompletedExceptionally());
assertFalse(future.isCancelled());
element2.complete("b");
assertTrue(future.isDone());
assertFalse(future.isCompletedExceptionally());
assertFalse(future.isCancelled());
assertEquals(future.join(), ImmutableList.of("a", "b"));
}
@Test
public void testExceptionalMultipleElementAllAsList()
throws Exception
{
CompletableFuture<String> element1 = new CompletableFuture<>();
CompletableFuture<String> element2 = new CompletableFuture<>();
CompletableFuture<List<Object>> future = allAsList(ImmutableList.of(element1, element2));
assertFalse(future.isDone());
assertFalse(future.isCompletedExceptionally());
assertFalse(future.isCancelled());
element1.completeExceptionally(new RuntimeException());
assertTrue(future.isDone());
assertTrue(future.isCompletedExceptionally());
assertFalse(future.isCancelled());
}
@Test(expectedExceptions = UnsupportedOperationException.class)
public void testUnmodifiableAllAsList()
throws Exception
{
CompletableFuture<List<Object>> future = allAsList(ImmutableList.of(new CompletableFuture<String>()));
future.complete(null);
}
@Test
public void testListenableTimeout()
throws Exception
{
SettableFuture<String> rootFuture = SettableFuture.create();
ListenableFuture<String> timeoutFuture = addTimeout(rootFuture, () -> "timeout", new Duration(0, MILLISECONDS), executorService);
assertEquals(tryGetFutureValue(timeoutFuture, 10, SECONDS).orElse("failed"), "timeout");
assertTrue(timeoutFuture.isDone());
assertFalse(timeoutFuture.isCancelled());
// root exception is cancelled on a timeout
assertFailure(() -> rootFuture.get(10, SECONDS), e -> assertInstanceOf(e, CancellationException.class));
assertTrue(rootFuture.isDone());
assertTrue(rootFuture.isCancelled());
}
@Test
public void testListenableTimeoutExceptionValue()
throws Exception
{
SettableFuture<String> rootFuture = SettableFuture.create();
ListenableFuture<String> timeoutFuture = addTimeout(rootFuture, () -> { throw new SQLException("timeout"); }, new Duration(0, MILLISECONDS), executorService);
assertFailure(() -> tryGetFutureValue(timeoutFuture, 10, SECONDS, SQLException.class), e -> {
assertInstanceOf(e, SQLException.class);
assertEquals(e.getMessage(), "timeout");
});
assertTrue(timeoutFuture.isDone());
assertFalse(timeoutFuture.isCancelled());
// root exception is cancelled on a timeout
assertFailure(() -> rootFuture.get(10, SECONDS), e -> assertInstanceOf(e, CancellationException.class));
assertTrue(rootFuture.isDone());
assertTrue(rootFuture.isCancelled());
}
@Test
public void testListenableTimeoutCancel()
throws Exception
{
SettableFuture<String> rootFuture = SettableFuture.create();
ListenableFuture<String> timeoutFuture = addTimeout(rootFuture, () -> "timeout", new Duration(10, SECONDS), executorService);
// check timeout
assertEquals(tryGetFutureValue(timeoutFuture, 10, MILLISECONDS), Optional.<String>empty());
assertTrue(timeoutFuture.cancel(true));
assertTrue(timeoutFuture.isDone());
assertTrue(timeoutFuture.isCancelled());
// root exception is cancelled on a timeout
assertFailure(() -> rootFuture.get(10, SECONDS), e -> assertInstanceOf(e, CancellationException.class));
assertTrue(rootFuture.isDone());
assertTrue(rootFuture.isCancelled());
}
@Test
public void testTimeout()
throws Exception
{
CompletableFuture<String> rootFuture = new CompletableFuture<>();
CompletableFuture<String> timeoutFuture = addTimeout(rootFuture, () -> "timeout", new Duration(0, MILLISECONDS), executorService);
assertEquals(tryGetFutureValue(timeoutFuture, 10, SECONDS).orElse("failed"), "timeout");
assertTrue(timeoutFuture.isDone());
assertFalse(timeoutFuture.isCancelled());
// root exception is cancelled on a timeout
assertFailure(() -> rootFuture.get(10, SECONDS), e -> assertInstanceOf(e, CancellationException.class));
assertTrue(rootFuture.isDone());
assertTrue(rootFuture.isCancelled());
}
@Test
public void testTimeoutExceptionValue()
throws Exception
{
CompletableFuture<String> rootFuture = new CompletableFuture<>();
CompletableFuture<String> timeoutFuture = addTimeout(rootFuture, () -> { throw new SQLException("timeout"); }, new Duration(0, MILLISECONDS), executorService);
assertFailure(() -> tryGetFutureValue(timeoutFuture, 10, SECONDS, SQLException.class), e -> {
assertInstanceOf(e, SQLException.class);
assertEquals(e.getMessage(), "timeout");
});
assertTrue(timeoutFuture.isDone());
assertFalse(timeoutFuture.isCancelled());
// root exception is cancelled on a timeout
assertFailure(() -> rootFuture.get(10, SECONDS), e -> assertInstanceOf(e, CancellationException.class));
assertTrue(rootFuture.isDone());
assertTrue(rootFuture.isCancelled());
}
@Test
public void testTimeoutCancel()
throws Exception
{
CompletableFuture<String> rootFuture = new CompletableFuture<>();
CompletableFuture<String> timeoutFuture = addTimeout(rootFuture, () -> "timeout", new Duration(10, SECONDS), executorService);
// check timeout
assertEquals(tryGetFutureValue(timeoutFuture, 10, MILLISECONDS), Optional.<String>empty());
assertTrue(timeoutFuture.cancel(true));
assertTrue(timeoutFuture.isDone());
assertTrue(timeoutFuture.isCancelled());
// root exception is cancelled on a timeout
assertFailure(() -> rootFuture.get(10, SECONDS), e -> assertInstanceOf(e, CancellationException.class));
assertTrue(rootFuture.isDone());
assertTrue(rootFuture.isCancelled());
}
private static void assertGetUncheckedListenable(Function<ListenableFuture<Object>, Object> getter)
throws Exception
{
assertEquals(getter.apply(immediateFuture("foo")), "foo");
assertFailure(() -> getter.apply(immediateFailedFuture(new IllegalArgumentException("foo"))), e -> {
assertInstanceOf(e, IllegalArgumentException.class);
assertEquals(e.getMessage(), "foo");
});
assertFailure(() -> getter.apply(immediateFailedFuture(new SQLException("foo"))), e -> {
assertInstanceOf(e, RuntimeException.class);
assertInstanceOf(e.getCause(), SQLException.class);
assertEquals(e.getCause().getMessage(), "foo");
});
Thread.currentThread().interrupt();
assertFailure(() -> getter.apply(SettableFuture.create()), e -> {
assertInstanceOf(e, RuntimeException.class);
assertInstanceOf(e.getCause(), InterruptedException.class);
assertTrue(Thread.interrupted());
});
assertFalse(Thread.currentThread().isInterrupted());
SettableFuture<Object> canceledFuture = SettableFuture.create();
canceledFuture.cancel(true);
assertFailure(() -> getter.apply(canceledFuture), e -> assertInstanceOf(e, CancellationException.class));
assertEquals(getter.apply(immediateFuture(null)), null);
}
private void assertGetUnchecked(UncheckedGetter getter)
throws Exception
{
assertGetUncheckedInternal(getter);
// run all test wrapped in a timeout future that does not timeout
assertGetUncheckedInternal(future -> getter.get(addTimeout(future, () -> { throw new RuntimeException("timeout"); }, new Duration(10, SECONDS), executorService)));
}
private static void assertGetUncheckedInternal(UncheckedGetter getter)
throws Exception
{
assertEquals(getter.get(completedFuture("foo")), "foo");
assertFailure(() -> getter.get(failedFuture(new IllegalArgumentException("foo"))), e -> {
assertInstanceOf(e, IllegalArgumentException.class);
assertEquals(e.getMessage(), "foo");
});
assertFailure(() -> getter.get(failedFuture(new SQLException("foo"))), e -> {
assertInstanceOf(e, RuntimeException.class);
assertInstanceOf(e.getCause(), SQLException.class);
assertEquals(e.getCause().getMessage(), "foo");
});
Thread.currentThread().interrupt();
assertFailure(() -> getter.get(new CompletableFuture<>()), e -> {
assertInstanceOf(e, RuntimeException.class);
assertInstanceOf(e.getCause(), InterruptedException.class);
assertTrue(Thread.interrupted());
});
assertFalse(Thread.currentThread().isInterrupted());
CompletableFuture<Object> canceledFuture = new CompletableFuture<>();
canceledFuture.cancel(true);
assertFailure(() -> getter.get(canceledFuture), e -> assertInstanceOf(e, CancellationException.class));
assertEquals(getter.get(completedFuture(null)), null);
}
private static void assertFailure(Thrower thrower, Consumer<Throwable> verifier)
{
try {
thrower.execute();
}
catch (Throwable throwable) {
verifier.accept(throwable);
return;
}
fail("expected exception to be thrown");
}
private interface UncheckedGetter
{
Object get(CompletableFuture<Object> future)
throws Exception;
}
private interface Thrower
{
void execute()
throws Throwable;
}
}