/* * Copyright 2014 Red Hat, Inc. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Apache License v2.0 which accompanies this distribution. * * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * * The Apache License v2.0 is available at * http://www.opensource.org/licenses/apache2.0.php * * You may elect to redistribute this code under either of these licenses. */ package io.vertx.test.core; import io.vertx.core.AsyncResult; import io.vertx.core.CompositeFuture; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.impl.NoStackTraceThrowable; import org.junit.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Function; /** * @author <a href="mailto:julien@julienviet.com">Julien Viet</a> */ public class FutureTest extends VertxTestBase { @Test public void testCreateWithHandler() { AtomicInteger count = new AtomicInteger(); AtomicReference<Future<String>> ref = new AtomicReference<>(); Future<String> f2 = Future.future(f1 -> { assertFalse(f1.isComplete()); count.incrementAndGet(); ref.set(f1); }); assertSame(f2, ref.get()); assertEquals(1, count.get()); new Checker<>(f2).assertNotCompleted(); ref.set(null); count.set(0); f2 = Future.future(f1 -> { count.incrementAndGet(); ref.set(f1); f1.complete("the-value"); }); assertSame(f2, ref.get()); assertEquals(1, count.get()); new Checker<>(f2).assertSucceeded("the-value"); ref.set(null); count.set(0); RuntimeException cause = new RuntimeException(); f2 = Future.future(f1 -> { count.incrementAndGet(); ref.set(f1); f1.fail(cause); }); assertSame(f2, ref.get()); assertEquals(1, count.get()); new Checker<>(f2).assertFailed(cause); try { Future.future(f -> { throw cause; }); fail(); } catch (Exception e) { assertSame(cause, e); } } @Test public void testStateAfterCompletion() { Object foo = new Object(); Future<Object> future = Future.succeededFuture(foo); assertTrue(future.succeeded()); assertFalse(future.failed()); assertTrue(future.isComplete()); assertEquals(foo, future.result()); assertNull(future.cause()); Exception cause = new Exception(); future = Future.failedFuture(cause); assertFalse(future.succeeded()); assertTrue(future.failed()); assertTrue(future.isComplete()); assertNull(future.result()); assertEquals(cause, future.cause()); } @Test public void testSetResultOnCompletedFuture() { ArrayList<Future<Object>> futures = new ArrayList<>(); futures.add(Future.succeededFuture()); futures.add(Future.succeededFuture()); futures.add(Future.succeededFuture(new Object())); futures.add(Future.succeededFuture(new Object())); futures.add(Future.failedFuture(new Exception())); futures.add(Future.failedFuture(new Exception())); for (Future<Object> future : futures) { try { future.complete(new Object()); fail(); } catch (IllegalStateException ignore) { } assertFalse(future.tryComplete(new Object())); try { future.complete(null); fail(); } catch (IllegalStateException ignore) { } assertFalse(future.tryComplete(null)); try { future.fail(new Exception()); fail(); } catch (IllegalStateException ignore) { } assertFalse(future.tryFail(new Exception())); } } @Test public void testCallSetHandlerBeforeCompletion() { AtomicBoolean called = new AtomicBoolean(); Future<Object> future = Future.future(); future.setHandler(result -> { assertTrue(result.succeeded()); assertFalse(result.failed()); assertEquals(null, result.result()); assertEquals(null, result.cause()); called.set(true); }); assertFalse(called.get()); future.complete(null); assertTrue(called.get()); called.set(false); Object foo = new Object(); future = Future.future(); future.setHandler(result -> { called.set(true); assertTrue(result.succeeded()); assertFalse(result.failed()); assertEquals(foo, result.result()); assertEquals(null, result.cause()); }); assertFalse(called.get()); future.complete(foo); assertTrue(called.get()); called.set(false); Exception cause = new Exception(); future = Future.future(); future.setHandler(result -> { called.set(true); assertFalse(result.succeeded()); assertTrue(result.failed()); assertEquals(null, result.result()); assertEquals(cause, result.cause()); }); assertFalse(called.get()); future.fail(cause); assertTrue(called.get()); } @Test public void testCallSetHandlerAfterCompletion() { AtomicBoolean called = new AtomicBoolean(); Future<Object> future = Future.succeededFuture(); future.setHandler(result -> { assertTrue(result.succeeded()); assertFalse(result.failed()); assertEquals(null, result.result()); assertEquals(null, result.cause()); called.set(true); }); assertTrue(called.get()); called.set(false); Object foo = new Object(); future = Future.succeededFuture(foo); future.setHandler(result -> { assertTrue(result.succeeded()); assertFalse(result.failed()); assertEquals(foo, result.result()); assertEquals(null, result.cause()); called.set(true); }); assertTrue(called.get()); called.set(false); Exception cause = new Exception(); future = Future.failedFuture(cause); future.setHandler(result -> { assertFalse(result.succeeded()); assertTrue(result.failed()); assertEquals(null, result.result()); assertEquals(cause, result.cause()); called.set(true); }); assertTrue(called.get()); } @Test public void testResolveFutureToHandler() { Consumer<Handler<AsyncResult<String>>> consumer = handler -> { handler.handle(Future.succeededFuture("the-result")); }; Future<String> fut = Future.future(); consumer.accept(fut); assertTrue(fut.isComplete()); assertTrue(fut.succeeded()); assertEquals("the-result", fut.result()); } @Test public void testFailFutureToHandler() { Throwable cause = new Throwable(); Consumer<Handler<AsyncResult<String>>> consumer = handler -> { handler.handle(Future.failedFuture(cause)); }; Future<String> fut = Future.future(); consumer.accept(fut); assertTrue(fut.isComplete()); assertTrue(fut.failed()); assertEquals(cause, fut.cause()); } @Test public void testCreateFailedWithNullFailure() { Future<String> future = Future.failedFuture((Throwable)null); Checker<String> checker = new Checker<>(future); NoStackTraceThrowable failure = (NoStackTraceThrowable) checker.assertFailed(); assertNull(failure.getMessage()); } @Test public void testFailureFutureWithNullFailure() { Future<String> future = Future.future(); future.fail((Throwable)null); Checker<String> checker = new Checker<>(future); NoStackTraceThrowable failure = (NoStackTraceThrowable) checker.assertFailed(); assertNull(failure.getMessage()); } @Test public void testCompositeComplete() { CompositeFuture composite = CompositeFuture.all(Future.future(), Future.future()); Checker<CompositeFuture> checker = new Checker<>(composite); composite.complete(composite); checker.assertSucceeded(composite); composite = CompositeFuture.all(Future.future(), Future.future()); checker = new Checker<>(composite); composite.complete(); checker.assertSucceeded(composite); } @Test public void testCompositeFail() { Throwable cause = new Throwable(); Future<Object> f1 = Future.future(); Future<Object> f2 = Future.future(); CompositeFuture composite = CompositeFuture.all(f1, f2); Checker<CompositeFuture> checker = new Checker<>(composite); composite.fail(cause); checker.assertFailed(cause); f1.complete(); f2.complete(); checker.assertFailed(cause); } @Test public void testAllSucceeded() { testAllSucceeded(CompositeFuture::all); } @Test public void testAllSucceededWithList() { testAllSucceeded((f1, f2) -> CompositeFuture.all(Arrays.asList(f1, f2))); } private void testAllSucceeded(BiFunction<Future<String>, Future<Integer>, CompositeFuture> all) { Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); CompositeFuture composite = all.apply(f1, f2); Checker<CompositeFuture> checker = new Checker<>(composite); checker.assertNotCompleted(); assertEquals(null, composite.<String>resultAt(0)); assertEquals(null, composite.<Integer>resultAt(1)); f1.complete("something"); checker.assertNotCompleted(); assertEquals("something", composite.resultAt(0)); assertEquals(null, composite.<Integer>resultAt(1)); f2.complete(3); checker.assertSucceeded(composite); assertEquals("something", composite.resultAt(0)); assertEquals(3, (int)composite.resultAt(1)); } @Test public void testAllWithEmptyList() { CompositeFuture composite = CompositeFuture.all(Collections.emptyList()); assertTrue(composite.isComplete()); } @Test public void testAllFailed() { testAllFailed(CompositeFuture::all); } @Test public void testAllFailedWithList() { testAllFailed((f1, f2) -> CompositeFuture.all(Arrays.asList(f1, f2))); } private void testAllFailed(BiFunction<Future<String>, Future<Integer>, CompositeFuture> all) { Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); CompositeFuture composite = all.apply(f1, f2); Checker<CompositeFuture> checker = new Checker<>(composite); f1.complete("s"); Exception cause = new Exception(); f2.fail(cause); checker.assertFailed(cause); assertEquals("s", composite.resultAt(0)); assertEquals(null, composite.<Integer>resultAt(1)); } @Test public void testAllLargeList() { testAllLargeList(63); testAllLargeList(64); testAllLargeList(65); testAllLargeList(100); } private void testAllLargeList(int size) { List<Future> list = new ArrayList<>(); for (int i = 0;i < size;i++) { list.add(Future.succeededFuture()); } CompositeFuture composite = CompositeFuture.all(list); Checker<CompositeFuture> checker = new Checker<>(composite); checker.assertSucceeded(composite); for (int i = 0;i < size;i++) { list.clear(); Throwable cause = new Exception(); for (int j = 0;j < size;j++) { list.add(i == j ? Future.failedFuture(cause) : Future.succeededFuture()); } composite = CompositeFuture.all(list); checker = new Checker<>(composite); checker.assertFailed(cause); for (int j = 0;j < size;j++) { if (i == j) { assertTrue(composite.failed(j)); } else { assertTrue(composite.succeeded(j)); } } } } @Test public void testAnySucceeded1() { testAnySucceeded1(CompositeFuture::any); } @Test public void testAnySucceeded1WithList() { testAnySucceeded1((f1, f2) -> CompositeFuture.any(Arrays.asList(f1, f2))); } private void testAnySucceeded1(BiFunction<Future<String>, Future<Integer>, CompositeFuture> any) { Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); CompositeFuture composite = any.apply(f1, f2); Checker<CompositeFuture> checker = new Checker<>(composite); checker.assertNotCompleted(); assertEquals(null, composite.<String>resultAt(0)); assertEquals(null, composite.<Integer>resultAt(1)); f1.complete("something"); checker.assertSucceeded(composite); f2.complete(3); checker.assertSucceeded(composite); } @Test public void testAnyWithEmptyList() { CompositeFuture composite = CompositeFuture.any(Collections.emptyList()); assertTrue(composite.isComplete()); } @Test public void testAnySucceeded2() { testAnySucceeded2(CompositeFuture::any); } @Test public void testAnySucceeded2WithList() { testAnySucceeded2(CompositeFuture::any); } private void testAnySucceeded2(BiFunction<Future<String>, Future<Integer>, CompositeFuture> any) { Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); CompositeFuture composite = any.apply(f1, f2); Checker<CompositeFuture> checker = new Checker<>(composite); f1.fail("failure"); checker.assertNotCompleted(); f2.complete(3); checker.assertSucceeded(composite); } @Test public void testAnyFailed() { testAnyFailed(CompositeFuture::any); } @Test public void testAnyFailedWithList() { testAnyFailed((f1, f2) -> CompositeFuture.any(Arrays.asList(f1, f2))); } private void testAnyFailed(BiFunction<Future<String>, Future<Integer>, CompositeFuture> any) { Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); CompositeFuture composite = any.apply(f1, f2); Checker<CompositeFuture> checker = new Checker<>(composite); f1.fail("failure"); checker.assertNotCompleted(); Throwable cause = new Exception(); f2.fail(cause); checker.assertFailed(cause); } @Test public void testAnyLargeList() { testAnyLargeList(63); testAnyLargeList(64); testAnyLargeList(65); testAnyLargeList(100); } private void testAnyLargeList(int size) { List<Future> list = new ArrayList<>(); for (int i = 0;i < size;i++) { list.add(Future.failedFuture(new Exception())); } CompositeFuture composite = CompositeFuture.any(list); Checker<CompositeFuture> checker = new Checker<>(composite); checker.assertFailed(); for (int i = 0;i < size;i++) { list.clear(); for (int j = 0;j < size;j++) { list.add(i == j ? Future.succeededFuture() : Future.failedFuture(new RuntimeException())); } composite = CompositeFuture.any(list); checker = new Checker<>(composite); checker.assertSucceeded(composite); for (int j = 0;j < size;j++) { if (i == j) { assertTrue(composite.succeeded(j)); } else { assertTrue(composite.failed(j)); } } } } @Test public void testJoinSucceeded() { testJoinSucceeded(CompositeFuture::join); } @Test public void testJoinSucceededWithList() { testJoinSucceeded((f1, f2) -> CompositeFuture.join(Arrays.asList(f1, f2))); } private void testJoinSucceeded(BiFunction<Future<String>, Future<Integer>, CompositeFuture> join) { Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); CompositeFuture composite = join.apply(f1, f2); Checker<CompositeFuture> checker = new Checker<>(composite); checker.assertNotCompleted(); f1.complete("foo"); checker.assertNotCompleted(); f2.complete(); checker.assertSucceeded(composite); } @Test public void testJoinFailed1() { testJoinFailed1(CompositeFuture::join); } @Test public void testJoinFailed1WithList() { testJoinFailed1((f1, f2) -> CompositeFuture.join(Arrays.asList(f1, f2))); } private void testJoinFailed1(BiFunction<Future<String>, Future<Integer>, CompositeFuture> join) { Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); CompositeFuture composite = join.apply(f1, f2); Checker<CompositeFuture> checker = new Checker<>(composite); checker.assertNotCompleted(); f1.complete("foo"); checker.assertNotCompleted(); Throwable cause = new Throwable(); f2.fail(cause); assertSame(checker.assertFailed(), cause); } @Test public void testJoinFailed2() { testJoinFailed2(CompositeFuture::join); } @Test public void testJoinFailed2WithList() { testJoinFailed2((f1, f2) -> CompositeFuture.join(Arrays.asList(f1, f2))); } private void testJoinFailed2(BiFunction<Future<String>, Future<Integer>, CompositeFuture> join) { Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); CompositeFuture composite = join.apply(f1, f2); Checker<CompositeFuture> checker = new Checker<>(composite); checker.assertNotCompleted(); Throwable cause = new Throwable(); f1.fail(cause); checker.assertNotCompleted(); f2.complete(10); assertSame(cause, checker.assertFailed()); } @Test public void testJoinFailed3() { testJoinFailed3(CompositeFuture::join); } @Test public void testJoinFailed3WithList() { testJoinFailed3((f1, f2) -> CompositeFuture.join(Arrays.asList(f1, f2))); } private void testJoinFailed3(BiFunction<Future<String>, Future<Integer>, CompositeFuture> join) { Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); CompositeFuture composite = join.apply(f1, f2); Checker<CompositeFuture> checker = new Checker<>(composite); checker.assertNotCompleted(); Throwable cause1 = new Throwable(); f1.fail(cause1); checker.assertNotCompleted(); Throwable cause2 = new Throwable(); f2.fail(cause2); assertSame(cause1, checker.assertFailed()); } @Test public void testJoinWithEmptyList() { CompositeFuture composite = CompositeFuture.join(Collections.emptyList()); assertTrue(composite.isComplete()); } @Test public void testCompositeFutureToList() { Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); CompositeFuture composite = CompositeFuture.all(f1, f2); assertEquals(Arrays.asList(null, null), composite.list()); f1.complete("foo"); assertEquals(Arrays.asList("foo", null), composite.list()); f2.complete(4); assertEquals(Arrays.asList("foo", 4), composite.list()); } @Test public void testComposeSuccessToSuccess() { Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); Checker<Integer> checker = new Checker<>(f2); f1.compose(string -> f2.complete(string.length()), f2); checker.assertNotCompleted(); f1.complete("abcdef"); checker.assertSucceeded(6); AtomicReference<String> ref = new AtomicReference<>(); Future<Integer> c = Future.future(); Future<String> f3 = Future.future(); Future<Integer> f4 = f3.compose(string -> { ref.set(string); return c; }); checker = new Checker<>(f4); f3.complete("abcdef"); checker.assertNotCompleted(); assertEquals("abcdef", ref.get()); c.complete(6); checker.assertSucceeded(6); } @Test public void testComposeSuccessToFailure() { Throwable cause = new Throwable(); AtomicReference<String> ref = new AtomicReference<>(); Future<Integer> c = Future.future(); Future<String> f3 = Future.future(); Future<Integer> f4 = f3.compose(string -> { ref.set(string); return c; }); Checker<Integer> checker = new Checker<>(f4); f3.complete("abcdef"); c.fail(cause); checker.assertFailed(cause); } @Test public void testComposeFailure() { Exception cause = new Exception(); Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); Checker<Integer> checker = new Checker<>(f2); f1.compose(string -> f2.complete(string.length()), f2); f1.fail(cause); checker.assertFailed(cause); Future<String> f3 = Future.future(); Future<Integer> f4 = f3.compose(string -> Future.succeededFuture(string.length())); checker = new Checker<>(f4); f3.fail(cause); checker.assertFailed(cause); } @Test public void testComposeFails() { RuntimeException cause = new RuntimeException(); Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); Checker<Integer> checker = new Checker<>(f2); f1.compose(string -> { throw cause; }, f2); f1.complete("foo"); checker.assertFailed(cause); Future<String> f3 = Future.future(); Future<Integer> f4 = f3.compose(string -> { throw cause; }); checker = new Checker<>(f4); f3.complete("foo"); checker.assertFailed(cause); } @Test public void testComposeFailsAfterCompletion() { Future<String> f1 = Future.future(); Future<Integer> f2 = Future.future(); Checker<Integer> checker = new Checker<>(f2); RuntimeException cause = new RuntimeException(); f1.compose(string -> { f2.complete(46); throw cause; }, f2); try { f1.complete("foo"); fail(); } catch (Exception e) { assertEquals(cause, e); } checker.assertSucceeded(46); } @Test public void testComposeWithNullFunction() { Future<Integer> fut = Future.future(); try { fut.compose((Function<Integer, Future<Integer>>) null); fail(); } catch (NullPointerException ignore) { } } @Test public void testMapSuccess() { Future<Integer> fut = Future.future(); Future<String> mapped = fut.map(Object::toString); Checker<String> checker = new Checker<>(mapped); checker.assertNotCompleted(); fut.complete(3); checker.assertSucceeded("3"); } @Test public void testMapFailure() { Throwable cause = new Throwable(); Future<Integer> fut = Future.future(); Future<String> mapped = fut.map(Object::toString); Checker<String> checker = new Checker<>(mapped); checker.assertNotCompleted(); fut.fail(cause); checker.assertFailed(cause); } @Test public void testMapFails() { RuntimeException cause = new RuntimeException(); Future<Integer> fut = Future.future(); Future<Object> mapped = fut.map(i -> { throw cause; }); Checker<Object> checker = new Checker<>(mapped); fut.fail(cause); checker.assertFailed(cause); } @Test public void testMapWithNullFunction() { Future<Integer> fut = Future.future(); try { fut.map((Function<Integer, String>) null); fail(); } catch (NullPointerException ignore) { } try { asyncResult(fut).map((Function<Integer, String>) null); fail(); } catch (NullPointerException ignore) { } } @Test public void testMapEmpty() { Future<Integer> fut = Future.future(); Future<String> mapped = fut.mapEmpty(); Checker<String> checker = new Checker<>(mapped); checker.assertNotCompleted(); fut.complete(3); checker.assertSucceeded(null); } @Test public void testRecoverSuccessWithSuccess() { AtomicBoolean called = new AtomicBoolean(); Future<String> f = Future.future(); Future<String> r = f.recover(t -> { called.set(true); throw new AssertionError(); }); Checker<String> checker = new Checker<>(r); checker.assertNotCompleted(); f.complete("yeah"); assertTrue(r.succeeded()); checker.assertSucceeded("yeah"); assertFalse(called.get()); } @Test public void testRecoverFailureWithSuccess() { Future<String> f = Future.future(); Future<String> r = f.recover(t -> Future.succeededFuture(t.getMessage())); Checker<String> checker = new Checker<>(r); checker.assertNotCompleted(); f.fail("recovered"); checker.assertSucceeded("recovered"); } @Test public void testRecoverFailureWithFailure() { Throwable cause = new Throwable(); Future<String> f = Future.future(); Future<String> r = f.recover(t -> Future.failedFuture(cause)); Checker<String> checker = new Checker<>(r); checker.assertNotCompleted(); f.fail("recovered"); checker.assertFailed(cause); } @Test public void testRecoverFailureFails() { RuntimeException cause = new RuntimeException("throw"); Future<String> f = Future.future(); Future<String> r = f.recover(t -> { throw cause; }); Checker<String> checker = new Checker<>(r); checker.assertNotCompleted(); f.fail("recovered"); checker.assertFailed(cause); } @Test public void testRecoverWithNullFunction() { Future<Integer> fut = Future.future(); try { fut.recover(null); fail(); } catch (NullPointerException ignore) { } } @Test public void testOtherwiseSuccessWithSuccess() { AtomicBoolean called = new AtomicBoolean(); Future<String> f = Future.future(); Future<String> r = f.otherwise(t -> { called.set(true); throw new AssertionError(); }); Checker<String> checker = new Checker<>(r); checker.assertNotCompleted(); f.complete("yeah"); assertTrue(r.succeeded()); checker.assertSucceeded("yeah"); assertFalse(called.get()); } @Test public void testOtherwiseFailureWithSuccess() { Future<String> f = Future.future(); Future<String> r = f.otherwise(t -> t.getMessage()); Checker<String> checker = new Checker<>(r); checker.assertNotCompleted(); f.fail("recovered"); checker.assertSucceeded("recovered"); } @Test public void testOtherwiseFails() { RuntimeException cause = new RuntimeException("throw"); Future<String> f = Future.future(); Future<String> r = f.otherwise(t -> { throw cause; }); Checker<String> checker = new Checker<>(r); checker.assertNotCompleted(); f.fail("recovered"); checker.assertFailed(cause); } @Test public void testDefaultCompleter() { AsyncResult<Object> succeededAsyncResult = new AsyncResult<Object>() { Object result = new Object(); public Object result() { return result; } public Throwable cause() { throw new UnsupportedOperationException(); } public boolean succeeded() { return true; } public boolean failed() { throw new UnsupportedOperationException(); } public <U> AsyncResult<U> map(Function<Object, U> mapper) { throw new UnsupportedOperationException(); } public <V> AsyncResult<V> map(V value) { throw new UnsupportedOperationException(); } }; AsyncResult<Object> failedAsyncResult = new AsyncResult<Object>() { Throwable cause = new Throwable(); public Object result() { throw new UnsupportedOperationException(); } public Throwable cause() { return cause; } public boolean succeeded() { return false; } public boolean failed() { throw new UnsupportedOperationException(); } public <U> AsyncResult<U> map(Function<Object, U> mapper) { throw new UnsupportedOperationException(); } public <V> AsyncResult<V> map(V value) { throw new UnsupportedOperationException(); } }; class DefaultCompleterTestFuture<T> implements Future<T> { boolean succeeded; boolean failed; T result; Throwable cause; public boolean isComplete() { throw new UnsupportedOperationException(); } public Future<T> setHandler(Handler<AsyncResult<T>> handler) { throw new UnsupportedOperationException(); } public void complete(T result) { if (!tryComplete(result)) { throw new IllegalStateException(); } } public void complete() { if (!tryComplete()) { throw new IllegalStateException(); } } public void fail(Throwable cause) { if (!tryFail(cause)) { throw new IllegalStateException(); } } public void fail(String failureMessage) { if (!tryFail(failureMessage)) { throw new IllegalStateException(); } } public boolean tryComplete(T result) { if (succeeded || failed) { return false; } succeeded = true; this.result = result; return true; } public boolean tryComplete() { throw new UnsupportedOperationException(); } public boolean tryFail(Throwable cause) { if (succeeded || failed) { return false; } failed = true; this.cause = cause; return true; } public boolean tryFail(String failureMessage) { throw new UnsupportedOperationException(); } public T result() { throw new UnsupportedOperationException(); } public Throwable cause() { throw new UnsupportedOperationException(); } public boolean succeeded() { throw new UnsupportedOperationException(); } public boolean failed() { throw new UnsupportedOperationException(); } public void handle(AsyncResult<T> asyncResult) { if (asyncResult.succeeded()) { complete(asyncResult.result()); } else { fail(asyncResult.cause()); } } } DefaultCompleterTestFuture<Object> successFuture = new DefaultCompleterTestFuture<>(); successFuture.completer().handle(succeededAsyncResult); assertTrue(successFuture.succeeded); assertEquals(succeededAsyncResult.result(), successFuture.result); DefaultCompleterTestFuture<Object> failureFuture = new DefaultCompleterTestFuture<>(); failureFuture.completer().handle(failedAsyncResult); assertTrue(failureFuture.failed); assertEquals(failedAsyncResult.cause(), failureFuture.cause); } class Checker<T> { private final Future<T> future; private final AtomicReference<AsyncResult<T>> result = new AtomicReference<>(); private final AtomicInteger count = new AtomicInteger(); Checker(Future<T> future) { future.setHandler(ar -> { count.incrementAndGet(); result.set(ar); }); this.future = future; } void assertNotCompleted() { assertFalse(future.isComplete()); assertFalse(future.succeeded()); assertFalse(future.failed()); assertNull(future.cause()); assertNull(future.result()); assertEquals(0, count.get()); assertNull(result.get()); } void assertSucceeded(T expected) { assertTrue(future.isComplete()); assertTrue(future.succeeded()); assertFalse(future.failed()); assertNull(future.cause()); assertEquals(expected, future.result()); assertEquals(1, count.get()); AsyncResult<T> ar = result.get(); assertNotNull(ar); assertTrue(ar.succeeded()); assertFalse(ar.failed()); assertNull(ar.cause()); assertEquals(expected, future.result()); } void assertFailed(Throwable expected) { assertEquals(expected, assertFailed()); } Throwable assertFailed() { assertTrue(future.isComplete()); assertFalse(future.succeeded()); assertTrue(future.failed()); assertEquals(null, future.result()); assertEquals(1, count.get()); AsyncResult<T> ar = result.get(); assertNotNull(ar); assertFalse(ar.succeeded()); assertTrue(ar.failed()); assertNull(ar.result()); return future.cause(); } } @Test public void testUncompletedAsyncResultMap() { Future<String> f = Future.future(); AsyncResult<String> res = asyncResult(f); AsyncResult<Integer> map1 = res.map(String::length); AsyncResult<Integer> map2 = res.map(17); assertNull(map1.result()); assertNull(map1.cause()); assertNull(map2.result()); assertNull(map2.cause()); } @Test public void testSucceededAsyncResultMap() { Future<String> f = Future.future(); AsyncResult<String> res = asyncResult(f); AsyncResult<Integer> map1 = res.map(String::length); AsyncResult<Integer> map2 = res.map(17); f.complete("foobar"); assertEquals(6, (int)map1.result()); assertNull(map1.cause()); assertEquals(17, (int)map2.result()); assertNull(map2.cause()); } @Test public void testFailedAsyncResultMap() { Future<String> f = Future.future(); AsyncResult<String> res = asyncResult(f); AsyncResult<Integer> map1 = res.map(String::length); AsyncResult<Integer> map2 = res.map(17); Throwable cause = new Throwable(); f.fail(cause); assertNull(map1.result()); assertSame(cause, map1.cause()); assertNull(map2.result()); assertSame(cause, map2.cause()); } @Test public void testAsyncResultMapEmpty() { Future<String> f = Future.future(); AsyncResult<String> res = asyncResult(f); AsyncResult<Integer> map = res.mapEmpty(); f.complete("foobar"); assertNull(null, map.result()); assertNull(map.cause()); } @Test public void testSucceededFutureRecover() { Future<String> f = Future.future(); Future<String> r = f.recover(t -> Future.succeededFuture(t.getMessage())); f.complete("yeah"); assertTrue(r.succeeded()); assertEquals(r.result(), "yeah"); } @Test public void testFailedFutureRecover() { Future<String> f = Future.future(); Future<String> r = f.recover(t -> Future.succeededFuture(t.getMessage())); f.fail("recovered"); assertTrue(r.succeeded()); assertEquals(r.result(), "recovered"); } @Test public void testFailedMapperFutureRecover() { Future<String> f = Future.future(); Future<String> r = f.recover(t -> { throw new RuntimeException("throw"); }); f.fail("recovered"); assertTrue(r.failed()); assertEquals(r.cause().getMessage(), "throw"); } @Test public void testUncompletedAsyncResultOtherwise() { Future<String> f = Future.future(); AsyncResult<String> res = asyncResult(f); testUncompletedAsyncResultOtherwise(res); } @Test public void testUncompletedFutureOtherwise() { Future<String> f = Future.future(); testUncompletedAsyncResultOtherwise(f); } private void testUncompletedAsyncResultOtherwise(AsyncResult<String> res) { AsyncResult<String> ar1 = res.otherwise("something-else"); assertFalse(ar1.succeeded()); assertFalse(ar1.failed()); assertNull(ar1.result()); assertNull(ar1.cause()); } @Test public void testUncompletedAsyncResultOtherwiseApplyFunction() { Future<String> f = Future.future(); AsyncResult<String> res = asyncResult(f); testUncompletedOtherwiseApplyFunction(res); } @Test public void testUncompletedFutureOtherwiseApplyFunction() { Future<String> f = Future.future(); testUncompletedOtherwiseApplyFunction(f); } private void testUncompletedOtherwiseApplyFunction(AsyncResult<String> res) { AsyncResult<String> ar1 = res.otherwise(Throwable::getMessage); assertFalse(ar1.succeeded()); assertFalse(ar1.failed()); assertNull(ar1.result()); assertNull(ar1.cause()); } @Test public void testSucceededAsyncResultOtherwise() { Future<String> f = Future.future(); AsyncResult<String> res = asyncResult(f); testSucceededOtherwise(res, f); } @Test public void testSucceededFutureOtherwise() { Future<String> f = Future.future(); testSucceededOtherwise(f, f); } private void testSucceededOtherwise(AsyncResult<String> res, Future<String> f) { AsyncResult<String> ar = res.otherwise(Throwable::getMessage); f.complete("foobar"); assertTrue(ar.succeeded()); assertFalse(ar.failed()); assertEquals("foobar", ar.result()); assertNull(ar.cause()); } @Test public void testSucceededAsyncResultOtherwiseApplyFunction() { Future<String> f = Future.future(); AsyncResult<String> res = asyncResult(f); testSucceededOtherwiseApplyFunction(res, f); } @Test public void testSucceededFutureOtherwiseApplyFunction() { Future<String> f = Future.future(); testSucceededOtherwiseApplyFunction(f, f); } private void testSucceededOtherwiseApplyFunction(AsyncResult<String> res, Future<String> f) { AsyncResult<String> ar = res.otherwise("whatever"); f.complete("foobar"); assertTrue(ar.succeeded()); assertFalse(ar.failed()); assertEquals("foobar", ar.result()); assertNull(ar.cause()); } @Test public void testFailedAsyncResultOtherwise() { Future<String> f = Future.future(); AsyncResult<String> res = asyncResult(f); testFailedOtherwise(res, f); } @Test public void testFailedFutureOtherwise() { Future<String> f = Future.future(); testFailedOtherwise(f, f); } private void testFailedOtherwise(AsyncResult<String> res, Future<String> f) { AsyncResult<String> map1 = res.otherwise("something-else"); Throwable cause = new Throwable("the-failure"); f.fail(cause); assertTrue(map1.succeeded()); assertFalse(map1.failed()); assertEquals("something-else", map1.result()); assertNull(map1.cause()); } @Test public void testFailedAsyncResultOtherwiseApplyFunction() { Future<String> f = Future.future(); AsyncResult<String> res = asyncResult(f); testFailedOtherwiseApplyFunction(res, f); } @Test public void testFailedFutureOtherwiseApplyFunction() { Future<String> f = Future.future(); testFailedOtherwiseApplyFunction(f, f); } private void testFailedOtherwiseApplyFunction(AsyncResult<String> res, Future<String> f) { AsyncResult<String> map1 = res.otherwise(Throwable::getMessage); Throwable cause = new Throwable("the-failure"); f.fail(cause); assertTrue(map1.succeeded()); assertFalse(map1.failed()); assertEquals("the-failure", map1.result()); assertNull(map1.cause()); } @Test public void testOtherwiseWithNullFunction() { Future<Integer> fut = Future.future(); try { fut.otherwise((Function<Throwable, Integer>) null); fail(); } catch (NullPointerException ignore) { } try { asyncResult(fut).otherwise((Function<Throwable, Integer>) null); fail(); } catch (NullPointerException ignore) { } } @Test public void testAsyncResultOtherwiseEmpty() { Future<String> f = Future.future(); AsyncResult<String> res = asyncResult(f); testOtherwiseEmpty(res, f); } @Test public void testFutureOtherwiseEmpty() { Future<String> f = Future.future(); testOtherwiseEmpty(f, f); } @Test public void testToString() { assertEquals("Future{unresolved}", Future.future().toString()); assertEquals("Future{result=abc}", Future.succeededFuture("abc").toString()); assertEquals("Future{cause=It's like that, and that's the way it is}", Future.failedFuture("It's like that, and that's the way it is").toString()); Future<String> f = Future.future(); f.complete("abc"); assertEquals("Future{result=abc}", f.toString()); f = Future.future(); f.fail("abc"); assertEquals("Future{cause=abc}", f.toString()); } private void testOtherwiseEmpty(AsyncResult<String> res, Future<String> f) { AsyncResult<String> otherwise = res.otherwiseEmpty(); Throwable cause = new Throwable("the-failure"); f.fail(cause); assertTrue(otherwise.succeeded()); assertFalse(otherwise.failed()); assertEquals(null, otherwise.result()); assertNull(otherwise.cause()); } private <T> AsyncResult<T> asyncResult(Future<T> fut) { return new AsyncResult<T>() { @Override public T result() { return fut.result(); } @Override public Throwable cause() { return fut.cause(); } @Override public boolean succeeded() { return fut.succeeded(); } @Override public boolean failed() { return fut.failed(); } }; } }