package me.escoffier.vertx.completablefuture; import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.vertx.core.Context; import io.vertx.core.Future; import io.vertx.core.Vertx; import io.vertx.ext.unit.Async; import io.vertx.ext.unit.TestContext; import io.vertx.ext.unit.junit.Repeat; import io.vertx.ext.unit.junit.RepeatRule; import io.vertx.ext.unit.junit.VertxUnitRunner; import org.junit.*; import org.junit.runner.RunWith; import java.util.concurrent.*; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.assertThat; @RunWith(VertxUnitRunner.class) public class VertxCompletableFutureTest { private Vertx vertx; private Executor executor; @Rule public RepeatRule rule = new RepeatRule(); @Before public void setUp(TestContext tc) { vertx = Vertx.vertx(); ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("my-sad-thread-%d").build(); executor = Executors.newSingleThreadExecutor(namedThreadFactory); vertx.exceptionHandler(tc.exceptionHandler()); } @After public void tearDown() { vertx.close(); } @Test public void testApply(TestContext tc) { Async async = tc.async(); VertxCompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); future.complete(42); vertx.runOnContext(x -> { String thread = Thread.currentThread().getName(); future.thenApply(i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, i); async.complete(); return null; }); }); } @Test public void testApplyAsync(TestContext tc) { Async async = tc.async(); vertx.runOnContext(x -> { VertxCompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); future.complete(42); String thread = Thread.currentThread().getName(); future.thenApplyAsync(i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, i); async.complete(); return null; }); }); } @Test public void testApplyAsyncWithExecutor(TestContext tc) { Async async = tc.async(); vertx.runOnContext(x -> { VertxCompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); future.complete(42); String thread = Thread.currentThread().getName(); future.thenApplyAsync(i -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, i); async.complete(); return null; }, executor); }); } @Test public void testAccept(TestContext tc) { Async async = tc.async(); VertxCompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); future.complete(42); vertx.runOnContext(x -> { String thread = Thread.currentThread().getName(); future.thenAccept(i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, i); async.complete(); }); }); } @Test public void testAcceptAsync(TestContext tc) { Async async = tc.async(); vertx.runOnContext(x -> { VertxCompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); future.complete(42); String thread = Thread.currentThread().getName(); future.thenAcceptAsync(i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, i); async.complete(); }); }); } @Test public void testAcceptAsyncWithExecutor(TestContext tc) { Async async = tc.async(); vertx.runOnContext(x -> { VertxCompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); future.complete(42); String thread = Thread.currentThread().getName(); future.thenAcceptAsync(i -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, i); async.complete(); }, executor); }); } @Test public void testRun(TestContext tc) { Async async = tc.async(); VertxCompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); future.complete(42); vertx.runOnContext(x -> { String thread = Thread.currentThread().getName(); future.thenRun(() -> { tc.assertEquals(thread, Thread.currentThread().getName()); async.complete(); }); }); } @Test public void testRunAsync(TestContext tc) { Async async = tc.async(); vertx.runOnContext(x -> { VertxCompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); future.complete(42); String thread = Thread.currentThread().getName(); future.thenRunAsync(() -> { tc.assertEquals(thread, Thread.currentThread().getName()); async.complete(); }); }); } @Test public void testRunAsyncWithExecutor(TestContext tc) { Async async = tc.async(); vertx.runOnContext(x -> { VertxCompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); future.complete(42); String thread = Thread.currentThread().getName(); future.thenRunAsync(() -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); async.complete(); }, executor); }); } @Test public void testRunAfterEither(TestContext tc) { // Success and success Async async1 = tc.async(); // Failure and success Async async2 = tc.async(); // Success and Failure Async async3 = tc.async(); // Failure and failure Async async4 = tc.async(); VertxCompletableFuture<Integer> success1 = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> success2 = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> failure1 = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> failure2 = new VertxCompletableFuture<>(vertx); vertx.runOnContext(x -> { String thread = Thread.currentThread().getName(); success1.runAfterEither(success2, () -> { tc.assertEquals(thread, Thread.currentThread().getName()); async1.complete(); }); failure1.runAfterEither(success2, () -> { tc.assertEquals(thread, Thread.currentThread().getName()); async2.complete(); }); success1.runAfterEither(failure2, () -> { tc.assertEquals(thread, Thread.currentThread().getName()); async3.complete(); }); failure1.runAfterEither(failure2, () -> { tc.fail("Should not be called"); }).whenComplete((res, err) -> { tc.assertNotNull(err); tc.assertNull(res); tc.assertEquals(thread, Thread.currentThread().getName()); async4.complete(); }); success1.complete(42); failure2.completeExceptionally(new Exception("2")); vertx.setTimer(100, v -> { success2.complete(1); failure1.completeExceptionally(new Exception("1")); }); }); } @Test public void testRunAfterEitherAsync(TestContext tc) { // Success and success Async async1 = tc.async(); // Failure and success Async async2 = tc.async(); // Success and Failure Async async3 = tc.async(); // Failure and failure Async async4 = tc.async(); vertx.runOnContext(x -> { VertxCompletableFuture<Integer> success1 = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> success2 = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> failure1 = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> failure2 = new VertxCompletableFuture<>(vertx); String thread = Thread.currentThread().getName(); success1.runAfterEitherAsync(success2, () -> { tc.assertEquals(thread, Thread.currentThread().getName()); async1.complete(); }); failure1 .runAfterEitherAsync(success2, () -> tc.fail("Should not be called")) .whenComplete((res, err) -> { tc.assertNotNull(err); tc.assertNull(res); tc.assertEquals(thread, Thread.currentThread().getName()); async2.complete(); }); success1.runAfterEitherAsync(failure2, () -> { tc.assertEquals(thread, Thread.currentThread().getName()); async3.complete(); }); failure1.runAfterEitherAsync(failure2, () -> { tc.fail("Should not be called"); }).whenComplete((res, err) -> { tc.assertNotNull(err); tc.assertNull(res); tc.assertEquals(thread, Thread.currentThread().getName()); async4.complete(); }); success1.complete(42); failure2.completeExceptionally(new Exception("2")); vertx.setTimer(100, v -> { success2.complete(1); failure1.completeExceptionally(new Exception("1")); }); }); } @Test public void testRunAfterEitherAsyncWithExecutor(TestContext tc) { // Success and success Async async1 = tc.async(); // Failure and success Async async2 = tc.async(); // Success and Failure Async async3 = tc.async(); // Failure and failure Async async4 = tc.async(); VertxCompletableFuture<Integer> success1 = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> success2 = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> failure1 = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> failure2 = new VertxCompletableFuture<>(vertx); vertx.runOnContext(x -> { String thread = Thread.currentThread().getName(); success1.runAfterEitherAsync(success2, () -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); async1.complete(); }, executor); failure2 .runAfterEitherAsync(success2, () -> tc.fail("Should not be called"), executor) .whenComplete((res, err) -> { tc.assertNotNull(err); tc.assertNull(res); tc.assertNotEquals(thread, Thread.currentThread().getName()); async2.complete(); }); success1.runAfterEitherAsync(failure2, () -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); async3.complete(); }, executor); failure1.runAfterEitherAsync(failure2, () -> { tc.fail("Should not be called"); }, executor).whenComplete((res, err) -> { tc.assertNotNull(err); tc.assertNull(res); tc.assertNotEquals(thread, Thread.currentThread().getName()); async4.complete(); }); success1.complete(42); failure2.completeExceptionally(new Exception("2")); vertx.setTimer(100, v -> { success2.complete(1); failure1.completeExceptionally(new Exception("1")); }); }); } @Test public void testCombine(TestContext tc) { Async async = tc.async(); VertxCompletableFuture<Integer> future1 = new VertxCompletableFuture<>(vertx); future1.complete(42); VertxCompletableFuture<Integer> future2 = new VertxCompletableFuture<>(vertx); future2.complete(1); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); future1.thenCombine(future2, (x, y) -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, x); tc.assertEquals(1, y); return x + y; }).thenAccept(i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 43); async.complete(); }); }); } @Test public void testCombineWithFailures(TestContext tc) { // Success and success Async async1 = tc.async(); // Failure and success Async async2 = tc.async(); // Success and Failure Async async3 = tc.async(); // Failure and failure Async async4 = tc.async(); VertxCompletableFuture<Integer> success1 = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> success2 = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> failure1 = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> failure2 = new VertxCompletableFuture<>(vertx); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); success1.thenCombine(success2, (x, y) -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, x); tc.assertEquals(1, y); return x + y; }).thenAccept(i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 43); async1.complete(); }); failure1.thenCombine(success1, (x, y) -> { tc.fail("Should not be called"); return null; }).whenComplete((res, err) -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertNotNull(err); tc.assertNull(res); async2.complete(); }); success1.thenCombine(failure2, (x, y) -> { tc.fail("Should not be called"); return null; }).whenComplete((res, err) -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertNotNull(err); tc.assertNull(res); async3.complete(); }); failure1.thenCombine(failure2, (x, y) -> { tc.fail("Should not be called"); return null; }).whenComplete((res, err) -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertNotNull(err); tc.assertNull(res); async4.complete(); }); success1.complete(42); success2.complete(1); failure1.completeExceptionally(new Exception("1")); failure2.completeExceptionally(new Exception("2")); }); } @Test public void testCombineAsync(TestContext tc) { Async async = tc.async(); VertxCompletableFuture<Integer> future2 = new VertxCompletableFuture<>(vertx); future2.complete(1); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); VertxCompletableFuture<Integer> future1 = new VertxCompletableFuture<>(vertx); future1.complete(42); future1.thenCombineAsync(future2, (x, y) -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, x); tc.assertEquals(1, y); return x + y; }).thenAccept(i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 43); async.complete(); }); }); } @Test public void testCombineAsyncWithExecutor(TestContext tc) { Async async = tc.async(); VertxCompletableFuture<Integer> future2 = new VertxCompletableFuture<>(vertx); future2.complete(1); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); VertxCompletableFuture<Integer> future1 = new VertxCompletableFuture<>(vertx); future1.complete(42); CompletableFuture<Integer> test = future1.thenCombineAsync(future2, (x, y) -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, x); tc.assertEquals(1, y); return x + y; }, executor); System.out.println(test); test.thenAcceptAsync(i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 43); async.complete(); }); }); } @Test public void testExceptionally(TestContext tc) { Async async1 = tc.async(); Async async2 = tc.async(); VertxCompletableFuture<Integer> failure = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> success = new VertxCompletableFuture<>(vertx); failure.exceptionally(t -> 43).thenAccept(i -> { tc.assertEquals(i, 43); async1.complete(); }); success.exceptionally(t -> 43).thenAccept(i -> { tc.assertEquals(i, 42); async2.complete(); }); success.complete(42); failure.completeExceptionally(new RuntimeException("my bad")); } @Test public void testHandle(TestContext tc) { Async async1 = tc.async(); Async async2 = tc.async(); VertxCompletableFuture<Integer> failure = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> success = new VertxCompletableFuture<>(vertx); failure.handle((r, t) -> 43).thenAccept(i -> { tc.assertEquals(i, 43); async1.complete(); }); success.handle((r, t) -> r).thenAccept(i -> { tc.assertEquals(i, 42); async2.complete(); }); success.complete(42); failure.completeExceptionally(new RuntimeException("my bad")); } @Test public void testHandleAsync(TestContext tc) { Async async1 = tc.async(); Async async2 = tc.async(); VertxCompletableFuture<Integer> failure = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> success = new VertxCompletableFuture<>(vertx); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); Context context = Vertx.currentContext(); failure.withContext(context).handleAsync((r, t) -> { tc.assertEquals(thread, Thread.currentThread().getName()); if (t != null) { return 43; } return r; }).thenAccept(i -> { tc.assertEquals(i, 43); async1.complete(); }); success.withContext(context).handleAsync((r, t) -> { tc.assertEquals(Thread.currentThread().getName(), thread); if (t != null) { return 43; } return r; }).thenAccept(i -> { tc.assertEquals(i, 42); async2.complete(); }); }); success.complete(42); failure.completeExceptionally(new RuntimeException("my bad")); } @Test public void testHandleAsyncWithExecutor(TestContext tc) { Async async1 = tc.async(); Async async2 = tc.async(); VertxCompletableFuture<Integer> failure = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> success = new VertxCompletableFuture<>(vertx); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); Context context = Vertx.currentContext(); failure.withContext(context).handleAsync((r, t) -> { tc.assertNotEquals(Thread.currentThread().getName(), thread); if (t != null) { return 43; } return r; }, executor).thenAccept(i -> { tc.assertEquals(i, 43); async1.complete(); }); success.withContext(context).handleAsync((r, t) -> { tc.assertNotEquals(Thread.currentThread().getName(), thread); if (t != null) { return 43; } return r; }, executor).thenAccept(i -> { tc.assertEquals(i, 42); async2.complete(); }); }); success.complete(42); failure.completeExceptionally(new RuntimeException("my bad")); } @Test public void testAcceptBoth(TestContext tc) { Async async = tc.async(); VertxCompletableFuture<Integer> future1 = new VertxCompletableFuture<>(vertx); future1.complete(42); VertxCompletableFuture<Integer> future2 = new VertxCompletableFuture<>(vertx); future2.complete(1); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); future1.thenAcceptBoth(future2, (x, y) -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, x); tc.assertEquals(1, y); async.complete(); }); }); } @Test public void testAcceptBothAsync(TestContext tc) { Async async = tc.async(); VertxCompletableFuture<Integer> future2 = new VertxCompletableFuture<>(vertx); future2.complete(1); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); VertxCompletableFuture<Integer> future1 = new VertxCompletableFuture<>(vertx); future1.complete(42); future1.thenAcceptBothAsync(future2, (x, y) -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, x); tc.assertEquals(1, y); async.complete(); }); }); } @Test public void testAcceptBothAsyncWithExecutor(TestContext tc) { Async async = tc.async(); Async async2 = tc.async(); VertxCompletableFuture<Integer> future2 = new VertxCompletableFuture<>(vertx); future2.complete(1); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); VertxCompletableFuture<Integer> future1 = new VertxCompletableFuture<>(vertx); vertx.setTimer(10, l -> future1.complete(42)); future1.thenAcceptBothAsync(future2, (x, y) -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, x); tc.assertEquals(1, y); async.complete(); }, executor); future2.thenAcceptBothAsync(future1, (x, y) -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); tc.assertEquals(42, y); tc.assertEquals(1, x); async2.complete(); }, executor); }); } @Test public void testRunAfterBoth(TestContext tc) { Async async = tc.async(); VertxCompletableFuture<Integer> future1 = new VertxCompletableFuture<>(vertx); future1.complete(42); VertxCompletableFuture<Integer> future2 = new VertxCompletableFuture<>(vertx); future2.complete(1); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); future1.runAfterBoth(future2, () -> { tc.assertEquals(thread, Thread.currentThread().getName()); async.complete(); }); }); } @Test public void testRunAfterBothAsync(TestContext tc) { Async async = tc.async(); VertxCompletableFuture<Integer> future2 = new VertxCompletableFuture<>(vertx); future2.complete(1); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); VertxCompletableFuture<Integer> future1 = new VertxCompletableFuture<>(vertx); future1.complete(42); future1.runAfterBoth(future2, () -> { tc.assertEquals(thread, Thread.currentThread().getName()); async.complete(); }); }); } @Test public void testRunAfterBothAsyncWithExecutor(TestContext tc) { Async async = tc.async(); Async async2 = tc.async(); VertxCompletableFuture<Integer> future2 = new VertxCompletableFuture<>(vertx); future2.complete(1); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); VertxCompletableFuture<Integer> future1 = new VertxCompletableFuture<>(vertx); vertx.setTimer(10, l -> future1.complete(42)); future1.runAfterBothAsync(future2, () -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); async.complete(); }, executor); future2.runAfterBothAsync(future1, () -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); async2.complete(); }, executor); }); } private CompletableFuture<Integer> inc(int input) { VertxCompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); vertx.setTimer(10, l -> future.complete(input + 1)); return future; } @Test public void testThenCompose(TestContext tc) { Async async = tc.async(); VertxCompletableFuture<Integer> future1 = new VertxCompletableFuture<>(vertx); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); future1.thenCompose(this::inc).thenCompose(this::inc).thenAccept(i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 44); async.complete(); }); future1.complete(42); }); } @Test public void testThenComposeAsync(TestContext tc) { Async async = tc.async(); VertxCompletableFuture<Integer> future1 = new VertxCompletableFuture<>(vertx); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); future1.withContext() .thenComposeAsync(this::inc) .thenComposeAsync(this::inc) .thenAccept(i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 44); async.complete(); }); future1.complete(42); }); } @Test public void testThenComposeAsyncWithExecutor(TestContext tc) { Async async = tc.async(); VertxCompletableFuture<Integer> future1 = new VertxCompletableFuture<>(vertx); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); future1.thenComposeAsync(this::inc, executor).thenCompose(this::inc).thenAccept(i -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 44); async.complete(); }); future1.complete(42); }); } @Test public void testAcceptEither(TestContext tc) { // Success and success Async async1 = tc.async(); Async async11 = tc.async(); // Failure and Success Async async2 = tc.async(); // Success and Failure Async async3 = tc.async(); VertxCompletableFuture<Integer> success = new VertxCompletableFuture<>(vertx); success.complete(1); VertxCompletableFuture<Integer> success2 = new VertxCompletableFuture<>(vertx); vertx.setTimer(100, l -> success2.complete(42)); VertxCompletableFuture<Integer> failure = new VertxCompletableFuture<>(vertx); failure.completeExceptionally(new RuntimeException("My bad")); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); success.acceptEither( success2, i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); async1.complete(); } ); success2.acceptEither( success, i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); async11.complete(); } ); failure.acceptEither( success, i -> { tc.fail("Should not be called"); } ).whenComplete((res, err) -> { tc.assertNotNull(err); tc.assertNull(res); tc.assertEquals(thread, Thread.currentThread().getName()); async2.complete(); }); success.acceptEither( failure, i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); async3.complete(); } ); }); } @Test public void testAcceptEitherAsync(TestContext tc) { // Success and success Async async1 = tc.async(); Async async11 = tc.async(); // Failure and Success Async async2 = tc.async(); // Success and Failure Async async3 = tc.async(); VertxCompletableFuture<Integer> success = new VertxCompletableFuture<>(vertx); success.complete(1); VertxCompletableFuture<Integer> success2 = new VertxCompletableFuture<>(vertx); vertx.setTimer(100, l -> success2.complete(42)); VertxCompletableFuture<Integer> failure = new VertxCompletableFuture<>(vertx); failure.completeExceptionally(new RuntimeException("My bad")); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); success.withContext().acceptEitherAsync( success2, i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); async1.complete(); } ); success2.withContext().acceptEitherAsync( success, i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); async11.complete(); } ); failure.acceptEitherAsync( success, i -> tc.fail("Should not be called") ).whenComplete((res, err) -> { tc.assertNotNull(err); tc.assertNull(res); async2.complete(); }); success.withContext().acceptEitherAsync( failure, i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); async3.complete(); } ); }); } @Test @Repeat(50) public void testAcceptEitherAsyncWithExecutor(TestContext tc) { // Success and success Async async1 = tc.async(); Async async11 = tc.async(); // Failure and Success Async async2 = tc.async(); // Success and Failure Async async3 = tc.async(); VertxCompletableFuture<Integer> success = new VertxCompletableFuture<>(vertx); success.complete(1); VertxCompletableFuture<Integer> success2 = new VertxCompletableFuture<>(vertx); vertx.setTimer(100, l -> success2.complete(42)); VertxCompletableFuture<Integer> failure = new VertxCompletableFuture<>(vertx); failure.completeExceptionally(new RuntimeException("My bad")); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); success.acceptEitherAsync( success2, i -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); async1.complete(); }, executor ); success2.acceptEitherAsync( success, i -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); async11.complete(); }, executor ); failure.acceptEitherAsync( success, i -> tc.fail("Should not be called"), executor ).whenComplete((res, err) -> { tc.assertNotNull(err); tc.assertNull(res); // We can't enforce the thread used here - it's non-deterministic. async2.complete(); }); success.acceptEitherAsync( failure, i -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); async3.complete(); }, executor ); }); } @Test public void testApplyToEither(TestContext tc) { // Success and success Async async1 = tc.async(); Async async11 = tc.async(); // Failure and Success Async async2 = tc.async(); // Success and Failure Async async3 = tc.async(); VertxCompletableFuture<Integer> success = new VertxCompletableFuture<>(vertx); success.complete(1); VertxCompletableFuture<Integer> success2 = new VertxCompletableFuture<>(vertx); vertx.setTimer(100, l -> success2.complete(42)); VertxCompletableFuture<Integer> failure = new VertxCompletableFuture<>(vertx); failure.completeExceptionally(new RuntimeException("My bad")); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); success.applyToEither( success2, i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); return i + 1; } ).whenComplete((res, err) -> { tc.assertNotNull(res); tc.assertNull(err); tc.assertEquals(res, 2); async1.complete(); }); success2.applyToEither( success, i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); return i + 5; } ).whenComplete((res, err) -> { tc.assertNotNull(res); tc.assertNull(err); tc.assertEquals(res, 6); async11.complete(); }); success.applyToEither( failure, i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); return i * 2; } ).whenComplete((res, err) -> { tc.assertNotNull(res); tc.assertNull(err); tc.assertEquals(res, 2); async3.complete(); }); // Fails, because the first one fails. failure.applyToEither( success, i -> { tc.fail("Should not be called"); return null; } ).whenComplete((res, err) -> { tc.assertNotNull(err); tc.assertNull(res); tc.assertEquals(thread, Thread.currentThread().getName()); async2.complete(); }); }); } @Test public void testApplyEitherAsync(TestContext tc) { // Success and success Async async1 = tc.async(); Async async11 = tc.async(); // Failure and Success Async async2 = tc.async(); // Success and Failure Async async3 = tc.async(); vertx.runOnContext(v -> { VertxCompletableFuture<Integer> success = new VertxCompletableFuture<>(vertx); success.complete(1); VertxCompletableFuture<Integer> success2 = new VertxCompletableFuture<>(vertx); vertx.setTimer(100, l -> success2.complete(42)); VertxCompletableFuture<Integer> failure = new VertxCompletableFuture<>(vertx); failure.completeExceptionally(new RuntimeException("My bad")); String thread = Thread.currentThread().getName(); success.applyToEitherAsync( success2, i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); return i + 1; } ).whenComplete((res, err) -> { tc.assertNotNull(res); tc.assertNull(err); tc.assertEquals(res, 2); async1.complete(); }); success2.applyToEitherAsync( success, i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); return i + 5; } ).whenComplete((res, err) -> { tc.assertNotNull(res); tc.assertNull(err); tc.assertEquals(res, 6); async11.complete(); }); failure.applyToEitherAsync( success, i -> { tc.fail("Should not be called"); return null; } ).whenComplete((res, err) -> { tc.assertNotNull(err); tc.assertNull(res); tc.assertEquals(thread, Thread.currentThread().getName()); async2.complete(); }); success.applyToEitherAsync( failure, i -> { tc.assertEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); return i * 2; } ).whenComplete((res, err) -> { tc.assertNotNull(res); tc.assertNull(err); tc.assertEquals(res, 2); async3.complete(); }); }); } @Test @Repeat(50) public void testApplyEitherAsyncWithExecutor(TestContext tc) { // Success and success Async async1 = tc.async(); Async async11 = tc.async(); // Failure and Success Async async2 = tc.async(); // Success and Failure Async async3 = tc.async(); VertxCompletableFuture<Integer> success = new VertxCompletableFuture<>(vertx); vertx.setTimer(500, l -> success.complete(1)); VertxCompletableFuture<Integer> success2 = new VertxCompletableFuture<>(vertx); vertx.setTimer(1500, l -> success2.complete(42)); VertxCompletableFuture<Integer> failure = new VertxCompletableFuture<>(vertx); failure.completeExceptionally(new RuntimeException("My bad")); VertxCompletableFuture<Integer> failure2 = new VertxCompletableFuture<>(vertx); vertx.setTimer(1500, l -> failure.completeExceptionally(new RuntimeException("My bad"))); vertx.runOnContext(v -> { String thread = Thread.currentThread().getName(); success.applyToEitherAsync( success2, i -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); return i + 1; }, executor ).whenComplete((res, err) -> { tc.assertNotNull(res); tc.assertNull(err); tc.assertEquals(res, 2); async1.complete(); }); success2.applyToEitherAsync( success, i -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); return i + 5; }, executor ).whenComplete((res, err) -> { tc.assertNotNull(res); tc.assertNull(err); tc.assertEquals(res, 6); async11.complete(); }); failure .applyToEitherAsync( success2, i -> { tc.fail("should not be called"); return "not the right result"; }, executor ) .whenComplete((res, err) -> { tc.assertNotNull(err); tc.assertNull(res); // We can't enforce the thread used here. async2.complete(); }); success.applyToEitherAsync( failure2, i -> { tc.assertNotEquals(thread, Thread.currentThread().getName()); tc.assertEquals(i, 1); return i * 2; }, executor ).whenComplete((res, err) -> { tc.assertNotNull(res); tc.assertNull(err); tc.assertNotEquals(thread, Thread.currentThread().getName()); tc.assertEquals(res, 2); async3.complete(); }); }); } @Test public void testGetGetNowAndJoin(TestContext tc) throws ExecutionException, InterruptedException, TimeoutException { CompletableFuture<Integer> success = new VertxCompletableFuture<>(vertx); CompletableFuture<Integer> failure = new VertxCompletableFuture<>(vertx); vertx.setTimer(1000, l -> { success.complete(42); failure.completeExceptionally(new Exception("My Bad !")); }); try { success.get(1, TimeUnit.MILLISECONDS); tc.fail("timeout expected"); } catch (TimeoutException e) { // OK } tc.assertEquals(22, success.getNow(22)); tc.assertEquals(success.get(), 42); tc.assertEquals(success.join(), 42); try { failure.get(); tc.fail("Exception expected"); } catch (ExecutionException e) { // OK. } try { failure.join(); tc.fail("Exception expected"); } catch (CompletionException e) { // OK } tc.assertEquals(42, success.get(10, TimeUnit.SECONDS)); CompletableFuture<Integer> immediate = new VertxCompletableFuture<>(vertx); immediate.complete(55); // Immediate get. tc.assertEquals(immediate.get(), 55); tc.assertEquals(immediate.join(), 55); tc.assertEquals(immediate.getNow(23), 55); tc.assertEquals(immediate.get(1, TimeUnit.SECONDS), 55); immediate.obtrudeValue(23); tc.assertEquals(immediate.get(), 23); tc.assertEquals(immediate.join(), 23); tc.assertEquals(immediate.get(1, TimeUnit.SECONDS), 23); CompletableFuture<Integer> cancelled = new VertxCompletableFuture<>(vertx); cancelled.cancel(false); try { cancelled.get(); tc.fail("Exception expected"); } catch (CancellationException e) { // OK } try { cancelled.join(); tc.fail("Exception expected"); } catch (CancellationException e) { // OK } try { cancelled.getNow(22); tc.fail("Exception expected"); } catch (CancellationException e) { // OK } } @Test public void testTwoDependents(TestContext tc) { Async async1 = tc.async(); Async async2 = tc.async(); vertx.runOnContext(v -> { VertxCompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); future.complete(42); future.thenApplyAsync(i -> { tc.assertEquals(42, i); return i + 1; }).thenAcceptAsync(i -> { tc.assertEquals(43, i); async1.complete(); }); future.thenAccept(i -> { tc.assertEquals(42, i); async2.complete(); }); }); } private CompletableFuture<String> display(String s) { CompletableFuture<String> future = new VertxCompletableFuture<>(vertx); future.complete("Result: " + s); return future; } @Test public void testMoreComplexComposition() throws ExecutionException, InterruptedException { CompletableFuture<Integer> first = new VertxCompletableFuture<>(vertx); CompletableFuture<Integer> second = first.thenApply(x -> x + 1); CompletableFuture<String> toString = second.thenApplyAsync(i -> Integer.toString(i)); CompletableFuture<String> displayed = toString.thenCompose(this::display); vertx.setTimer(100, l -> first.complete(42)); Assert.assertEquals("Result: " + 43, displayed.join()); Assert.assertTrue(first.get() == 42); Assert.assertTrue(second.get() == 43); Assert.assertEquals(toString.get(), "43"); Assert.assertTrue(displayed.isDone()); } @Test public void testCancellationOnDependents() { CompletableFuture<Integer> first = new VertxCompletableFuture<>(vertx); CompletableFuture<Integer> second = first.thenApply(x -> x + 1); CompletableFuture<String> toString = second.thenApplyAsync(i -> Integer.toString(i)); CompletableFuture<String> displayed = toString.thenCompose(this::display); vertx.setTimer(1000, l -> first.complete(42)); first.cancel(true); Assert.assertTrue(first.isCancelled()); Assert.assertTrue(second.isCompletedExceptionally()); Assert.assertTrue(toString.isCompletedExceptionally()); Assert.assertTrue(displayed.isCompletedExceptionally()); } @Test public void testWhenComplete(TestContext tc) { Async async1 = tc.async(); Async async2 = tc.async(); CompletableFuture<Integer> first = new VertxCompletableFuture<>(vertx); first .thenApply(x -> x + 1) .thenApplyAsync(i -> Integer.toString(i)) .thenCompose(this::display) .whenComplete((res, err) -> { tc.assertNull(err); tc.assertEquals(res, "Result: " + 43); async1.complete(); }); first.complete(42); VertxCompletableFuture<Integer> second = new VertxCompletableFuture<>(vertx); second .thenApply(x -> x + 1) .thenApplyAsync(i -> Integer.toString(i)) .thenCompose(this::display) .whenComplete((res, err) -> { tc.assertNull(res); tc.assertNotNull(err); async2.complete(); }); second.completeExceptionally(new Exception("My bad")); } @Test public void testWhenCompleteAsync(TestContext tc) { Async async1 = tc.async(); Async async2 = tc.async(); VertxCompletableFuture<Integer> first = new VertxCompletableFuture<>(vertx); first .thenApply(x -> x + 1) .thenApplyAsync(i -> Integer.toString(i)) .thenCompose(this::display) .whenCompleteAsync((res, err) -> { tc.assertTrue(Context.isOnEventLoopThread()); tc.assertNull(err); tc.assertEquals(res, "Result: " + 43); async1.complete(); }); first.complete(42); VertxCompletableFuture<Integer> second = new VertxCompletableFuture<>(vertx); second .thenApply(x -> x + 1) .thenApplyAsync(i -> Integer.toString(i)) .thenCompose(this::display) .whenCompleteAsync((res, err) -> { tc.assertTrue(Context.isOnEventLoopThread()); tc.assertNull(res); tc.assertNotNull(err); async2.complete(); }); second.completeExceptionally(new Exception("My bad")); } @Test public void testWhenCompleteAsyncWithExecutor(TestContext tc) { Async async1 = tc.async(); Async async2 = tc.async(); VertxCompletableFuture<Integer> first = new VertxCompletableFuture<>(vertx); first .thenApply(x -> x + 1) .thenApplyAsync(i -> Integer.toString(i)) .thenCompose(this::display) .whenCompleteAsync((res, err) -> { tc.assertFalse(Context.isOnEventLoopThread()); tc.assertNull(err); tc.assertEquals(res, "Result: " + 43); async1.complete(); }, executor); first.complete(42); VertxCompletableFuture<Integer> second = new VertxCompletableFuture<>(vertx); second .thenApply(x -> x + 1) .thenApplyAsync(i -> Integer.toString(i)) .thenCompose(this::display) .whenCompleteAsync((res, err) -> { tc.assertFalse(Context.isOnEventLoopThread()); tc.assertNull(res); tc.assertNotNull(err); async2.complete(); }, executor); second.completeExceptionally(new Exception("My bad")); } @Test public void testAcceptFailurePropagation(TestContext tc) { Async async0 = tc.async(); Async async1 = tc.async(); Async async2 = tc.async(); CompletableFuture<Integer> failure1 = new VertxCompletableFuture<>(vertx); CompletableFuture<Integer> failure2 = new VertxCompletableFuture<>(vertx); failure1.thenAccept(i -> tc.fail("Should not be called")) .whenComplete((res, err) -> { tc.assertEquals(err.getClass(), CompletionException.class); tc.assertTrue(err.getMessage().contains("A")); async0.complete(); }); failure1.thenAcceptBoth(failure2, (a, b) -> tc.fail("Should not be called")) .whenComplete((res, err) -> { tc.assertEquals(err.getClass(), CompletionException.class); tc.assertTrue(err.getMessage().contains("A")); async2.complete(); }); failure1.acceptEither(failure2, i -> tc.fail("Should not be called")) .whenComplete((res, err) -> { tc.assertEquals(err.getClass(), CompletionException.class); tc.assertTrue(err.getMessage().contains("B")); async1.complete(); }); failure2.completeExceptionally(new RuntimeException("B")); failure1.completeExceptionally(new RuntimeException("A")); } @Test public void testApplyFailurePropagation(TestContext tc) { Async async0 = tc.async(); Async async1 = tc.async(); CompletableFuture<Integer> failure1 = new VertxCompletableFuture<>(vertx); CompletableFuture<Integer> failure2 = new VertxCompletableFuture<>(vertx); failure1.thenApply(i -> { tc.fail("Should not be called"); return 0; }).whenComplete((res, err) -> { tc.assertEquals(err.getClass(), CompletionException.class); tc.assertTrue(err.getMessage().contains("A")); async0.complete(); }); failure1.applyToEither(failure2, i -> { tc.fail("Should not be called"); return 0; }).whenComplete((res, err) -> { tc.assertEquals(err.getClass(), CompletionException.class); tc.assertTrue(err.getMessage().contains("B")); async1.complete(); }); failure2.completeExceptionally(new RuntimeException("B")); failure1.completeExceptionally(new RuntimeException("A")); } @Test public void testCombineFailurePropagation(TestContext tc) { Async async0 = tc.async(); Async async1 = tc.async(); CompletableFuture<Integer> failure1 = new VertxCompletableFuture<>(vertx); CompletableFuture<Integer> failure2 = new VertxCompletableFuture<>(vertx); VertxCompletableFuture<Integer> future1 = new VertxCompletableFuture<>(vertx); future1.complete(42); VertxCompletableFuture<Integer> future2 = new VertxCompletableFuture<>(vertx); future2.complete(1); failure1.thenCombine(failure2, (a, b) -> { tc.fail("Should not be called"); return 0; }).whenComplete((res, err) -> { tc.assertEquals(err.getClass(), CompletionException.class); tc.assertTrue(err.getMessage().contains("A")); async0.complete(); }); failure1.thenCombine(failure2, (a, b) -> { tc.fail("Should not be called"); return 0; }).whenComplete((res, err) -> { tc.assertEquals(err.getClass(), CompletionException.class); // Here we are not mimicking CompletableFuture behavior. We return the failure1 cause instead of the first // one that has arrived. For pure completable future is would be: //tc.assertTrue(err.getMessage().contains("B")); // For us it's tc.assertTrue(err.getMessage().contains("A")); async1.complete(); }); failure2.completeExceptionally(new RuntimeException("B")); failure1.completeExceptionally(new RuntimeException("A")); } private Integer square(int x) { return x * x; } @Test public void testExceptionallyPropagation(TestContext tc) { Async async1 = tc.async(); Async async2 = tc.async(); CompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); future.thenApply(this::square) .exceptionally(t -> 22) .whenComplete((res, err) -> { tc.assertEquals(res, 16); async1.complete(); }) .thenAccept(System.out::print) .thenRun(System.out::println); future.complete(4); future = new VertxCompletableFuture<>(vertx); future.thenApply(this::square).thenApply(i -> { throw new RuntimeException("My Bad"); }) .exceptionally(t -> 22) .whenComplete((res, err) -> { tc.assertEquals(res, 22); async2.complete(); }) .thenAccept(System.out::print) .thenRun(System.out::println); future.complete(4); } @Test public void testRunFailurePropagation(TestContext tc) { Async async0 = tc.async(); Async async1 = tc.async(); Async async2 = tc.async(); CompletableFuture<Integer> failure1 = new VertxCompletableFuture<>(vertx); CompletableFuture<Integer> failure2 = new VertxCompletableFuture<>(vertx); failure1.thenRun(() -> tc.fail("Should not be called")) .whenComplete((res, err) -> { tc.assertEquals(err.getClass(), CompletionException.class); tc.assertTrue(err.getMessage().contains("A")); async0.complete(); }); failure1.runAfterBoth(failure2, () -> tc.fail("Should not be called")) .whenComplete((res, err) -> { tc.assertEquals(err.getClass(), CompletionException.class); tc.assertTrue(err.getMessage().contains("A")); async2.complete(); }); failure1.runAfterEither(failure2, () -> tc.fail("Should not be called")) .whenComplete((res, err) -> { tc.assertEquals(err.getClass(), CompletionException.class); tc.assertTrue(err.getMessage().contains("B")); async1.complete(); }); failure2.completeExceptionally(new RuntimeException("B")); failure1.completeExceptionally(new RuntimeException("A")); } @Test public void testFromCompletableStageJavadoc() { VertxCompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); future .thenApply(this::square) .thenAccept(System.out::print) .thenRun(System.out::println); future.complete(42); } @Test public void testWithContext(TestContext tc) { Async async1 = tc.async(); Async async2 = tc.async(); VertxCompletableFuture<Void> future = new VertxCompletableFuture<>(vertx); assertThat(future.context(), is(not(vertx.getOrCreateContext()))); vertx.runOnContext(v -> { assertThat(future.withContext().context(), is(vertx.getOrCreateContext())); async1.complete(); }); vertx.<Void>executeBlocking( fut -> { assertThat(future.withContext().context(), is(Vertx.currentContext())); async2.complete(); }, null ); } @Test public void testToVertxFuture(TestContext tc) { Async async1 = tc.async(); Async async2 = tc.async(); Async async3 = tc.async(); Async async4 = tc.async(); Async async5 = tc.async(); VertxCompletableFuture<Integer> future = new VertxCompletableFuture<>(vertx); Future<Integer> success = future.thenApply(i -> i + 1).thenApplyAsync(i -> i + 1).toFuture(); Future<Void> success2 = future.thenApply(i -> i + 1).thenApplyAsync(i -> i + 1).thenRunAsync(() -> { // Do nothing }).toFuture(); success.setHandler(ar -> { assertThat(ar.failed(), is(false)); assertThat(ar.succeeded(), is(true)); assertThat(ar.result(), is(44)); async1.complete(); }); success2.setHandler(ar -> { assertThat(ar.failed(), is(false)); assertThat(ar.succeeded(), is(true)); assertThat(ar.result(), is(nullValue())); async2.complete(); }); VertxCompletableFuture<Integer> failingFuture = new VertxCompletableFuture<>(vertx); Future<Integer> failure = failingFuture.thenApply(i -> i + 1).thenApplyAsync(i -> i + i + 1).toFuture(); failure.setHandler(ar -> { assertThat(ar.failed(), is(true)); assertThat(ar.succeeded(), is(false)); assertThat(ar.result(), is(nullValue())); assertThat(ar.cause(), is(notNullValue())); assertThat(ar.cause().getMessage(), containsString("My bad")); async3.complete(); }); // test that `VertxCompletableFuture` receives callbacks from the Vert.x Future VertxCompletableFuture<Integer> awaitedFuture = new VertxCompletableFuture<>(vertx); awaitedFuture.handle((val, except) -> { assertThat(val, is(42)); assertThat(except, is(nullValue())); async4.complete(); return (Void) null; }); VertxCompletableFuture<Integer> awaitedFailingFuture = new VertxCompletableFuture<>(vertx); awaitedFailingFuture.handle((val, except) -> { assertThat(val, is(nullValue())); assertThat(except, is(notNullValue())); assertThat(except.getMessage(), containsString("My bad again")); async5.complete(); return (Void) null; }); future.complete(42); failingFuture.completeExceptionally(new Exception("My bad")); awaitedFuture.toFuture().complete(42); awaitedFailingFuture.completeExceptionally(new Exception("My bad again")); } @Test public void testFromVertxFuture(TestContext tc) { Async async1 = tc.async(); Async async2 = tc.async(); Future<Integer> vertxFuture1 = Future.future(); Future<Integer> vertxFuture2 = Future.future(); VertxCompletableFuture.from(vertx, vertxFuture1).thenApply(i -> i + 1).whenComplete((res, err) -> { tc.assertNotNull(res); tc.assertNull(err); tc.assertEquals(43, res); async1.complete(); }); VertxCompletableFuture.from(vertx, vertxFuture2).thenApply(i -> i + 1).whenComplete((res, err) -> { tc.assertNotNull(err); tc.assertNull(res); tc.assertTrue(err.getMessage().contains("My bad")); async2.complete(); }); vertxFuture1.complete(42); vertxFuture2.fail(new Exception("My bad")); } }