| /* |
| * Written by Doug Lea and Martin Buchholz with assistance from |
| * members of JCP JSR-166 Expert Group and released to the public |
| * domain, as explained at |
| * http://creativecommons.org/publicdomain/zero/1.0/ |
| */ |
| |
| package jsr166; |
| |
| import static java.util.concurrent.TimeUnit.MILLISECONDS; |
| import static java.util.concurrent.TimeUnit.SECONDS; |
| import static java.util.concurrent.CompletableFuture.completedFuture; |
| import static java.util.concurrent.CompletableFuture.failedFuture; |
| |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| |
| import java.util.stream.Collectors; |
| import java.util.stream.Stream; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.Set; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.CancellationException; |
| import java.util.concurrent.CompletableFuture; |
| import java.util.concurrent.CompletionException; |
| import java.util.concurrent.CompletionStage; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.Executor; |
| import java.util.concurrent.ForkJoinPool; |
| import java.util.concurrent.ForkJoinTask; |
| import java.util.concurrent.TimeoutException; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.concurrent.atomic.AtomicReference; |
| import java.util.function.BiConsumer; |
| import java.util.function.BiFunction; |
| import java.util.function.Consumer; |
| import java.util.function.Function; |
| import java.util.function.Predicate; |
| import java.util.function.Supplier; |
| |
| import junit.framework.AssertionFailedError; |
| import junit.framework.Test; |
| import junit.framework.TestSuite; |
| |
| public class CompletableFutureTest extends JSR166TestCase { |
| |
| // android-note: Removed because the CTS runner does a bad job of |
| // retrying tests that have suite() declarations. |
| // |
| // public static void main(String[] args) { |
| // main(suite(), args); |
| // } |
| // public static Test suite() { |
| // return new TestSuite(CompletableFutureTest.class); |
| // } |
| |
| static class CFException extends RuntimeException {} |
| |
| void checkIncomplete(CompletableFuture<?> f) { |
| assertFalse(f.isDone()); |
| assertFalse(f.isCancelled()); |
| assertTrue(f.toString().contains("Not completed")); |
| try { |
| assertNull(f.getNow(null)); |
| } catch (Throwable fail) { threadUnexpectedException(fail); } |
| try { |
| f.get(0L, SECONDS); |
| shouldThrow(); |
| } |
| catch (TimeoutException success) {} |
| catch (Throwable fail) { threadUnexpectedException(fail); } |
| } |
| |
| <T> void checkCompletedNormally(CompletableFuture<T> f, T value) { |
| checkTimedGet(f, value); |
| |
| try { |
| assertEquals(value, f.join()); |
| } catch (Throwable fail) { threadUnexpectedException(fail); } |
| try { |
| assertEquals(value, f.getNow(null)); |
| } catch (Throwable fail) { threadUnexpectedException(fail); } |
| try { |
| assertEquals(value, f.get()); |
| } catch (Throwable fail) { threadUnexpectedException(fail); } |
| assertTrue(f.isDone()); |
| assertFalse(f.isCancelled()); |
| assertFalse(f.isCompletedExceptionally()); |
| assertTrue(f.toString().contains("[Completed normally]")); |
| } |
| |
| /** |
| * Returns the "raw" internal exceptional completion of f, |
| * without any additional wrapping with CompletionException. |
| */ |
| <U> Throwable exceptionalCompletion(CompletableFuture<U> f) { |
| // handle (and whenComplete) can distinguish between "direct" |
| // and "wrapped" exceptional completion |
| return f.handle((U u, Throwable t) -> t).join(); |
| } |
| |
| void checkCompletedExceptionally(CompletableFuture<?> f, |
| boolean wrapped, |
| Consumer<Throwable> checker) { |
| Throwable cause = exceptionalCompletion(f); |
| if (wrapped) { |
| assertTrue(cause instanceof CompletionException); |
| cause = cause.getCause(); |
| } |
| checker.accept(cause); |
| |
| long startTime = System.nanoTime(); |
| try { |
| f.get(LONG_DELAY_MS, MILLISECONDS); |
| shouldThrow(); |
| } catch (ExecutionException success) { |
| assertSame(cause, success.getCause()); |
| } catch (Throwable fail) { threadUnexpectedException(fail); } |
| assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2); |
| |
| try { |
| f.join(); |
| shouldThrow(); |
| } catch (CompletionException success) { |
| assertSame(cause, success.getCause()); |
| } catch (Throwable fail) { threadUnexpectedException(fail); } |
| |
| try { |
| f.getNow(null); |
| shouldThrow(); |
| } catch (CompletionException success) { |
| assertSame(cause, success.getCause()); |
| } catch (Throwable fail) { threadUnexpectedException(fail); } |
| |
| try { |
| f.get(); |
| shouldThrow(); |
| } catch (ExecutionException success) { |
| assertSame(cause, success.getCause()); |
| } catch (Throwable fail) { threadUnexpectedException(fail); } |
| |
| assertFalse(f.isCancelled()); |
| assertTrue(f.isDone()); |
| assertTrue(f.isCompletedExceptionally()); |
| // Android-changed: compatible toString() check (b/233721784) |
| // assertTrue(f.toString().contains("[Completed exceptionally]")); |
| assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]")); |
| } |
| |
| void checkCompletedWithWrappedCFException(CompletableFuture<?> f) { |
| checkCompletedExceptionally(f, true, |
| (t) -> assertTrue(t instanceof CFException)); |
| } |
| |
| void checkCompletedWithWrappedCancellationException(CompletableFuture<?> f) { |
| checkCompletedExceptionally(f, true, |
| (t) -> assertTrue(t instanceof CancellationException)); |
| } |
| |
| void checkCompletedWithTimeoutException(CompletableFuture<?> f) { |
| checkCompletedExceptionally(f, false, |
| (t) -> assertTrue(t instanceof TimeoutException)); |
| } |
| |
| void checkCompletedWithWrappedException(CompletableFuture<?> f, |
| Throwable ex) { |
| checkCompletedExceptionally(f, true, (t) -> assertSame(t, ex)); |
| } |
| |
| void checkCompletedExceptionally(CompletableFuture<?> f, Throwable ex) { |
| checkCompletedExceptionally(f, false, (t) -> assertSame(t, ex)); |
| } |
| |
| void checkCancelled(CompletableFuture<?> f) { |
| long startTime = System.nanoTime(); |
| try { |
| f.get(LONG_DELAY_MS, MILLISECONDS); |
| shouldThrow(); |
| } catch (CancellationException success) { |
| } catch (Throwable fail) { threadUnexpectedException(fail); } |
| assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2); |
| |
| try { |
| f.join(); |
| shouldThrow(); |
| } catch (CancellationException success) {} |
| try { |
| f.getNow(null); |
| shouldThrow(); |
| } catch (CancellationException success) {} |
| try { |
| f.get(); |
| shouldThrow(); |
| } catch (CancellationException success) { |
| } catch (Throwable fail) { threadUnexpectedException(fail); } |
| |
| assertTrue(exceptionalCompletion(f) instanceof CancellationException); |
| |
| assertTrue(f.isDone()); |
| assertTrue(f.isCompletedExceptionally()); |
| assertTrue(f.isCancelled()); |
| // Android-changed: compatible toString() check (b/233721784) |
| // assertTrue(f.toString().contains("[Completed exceptionally]")); |
| assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]")); |
| } |
| |
| /** |
| * A newly constructed CompletableFuture is incomplete, as indicated |
| * by methods isDone, isCancelled, and getNow |
| */ |
| public void testConstructor() { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| checkIncomplete(f); |
| } |
| |
| /** |
| * complete completes normally, as indicated by methods isDone, |
| * isCancelled, join, get, and getNow |
| */ |
| public void testComplete() { |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| checkIncomplete(f); |
| assertTrue(f.complete(v1)); |
| assertFalse(f.complete(v1)); |
| checkCompletedNormally(f, v1); |
| }} |
| |
| /** |
| * completeExceptionally completes exceptionally, as indicated by |
| * methods isDone, isCancelled, join, get, and getNow |
| */ |
| public void testCompleteExceptionally() { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| CFException ex = new CFException(); |
| checkIncomplete(f); |
| f.completeExceptionally(ex); |
| checkCompletedExceptionally(f, ex); |
| } |
| |
| /** |
| * cancel completes exceptionally and reports cancelled, as indicated by |
| * methods isDone, isCancelled, join, get, and getNow |
| */ |
| public void testCancel() { |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| checkIncomplete(f); |
| assertTrue(f.cancel(mayInterruptIfRunning)); |
| assertTrue(f.cancel(mayInterruptIfRunning)); |
| assertTrue(f.cancel(!mayInterruptIfRunning)); |
| checkCancelled(f); |
| }} |
| |
| /** |
| * obtrudeValue forces completion with given value |
| */ |
| public void testObtrudeValue() { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| checkIncomplete(f); |
| assertTrue(f.complete(one)); |
| checkCompletedNormally(f, one); |
| f.obtrudeValue(three); |
| checkCompletedNormally(f, three); |
| f.obtrudeValue(two); |
| checkCompletedNormally(f, two); |
| f = new CompletableFuture<>(); |
| f.obtrudeValue(three); |
| checkCompletedNormally(f, three); |
| f.obtrudeValue(null); |
| checkCompletedNormally(f, null); |
| f = new CompletableFuture<>(); |
| f.completeExceptionally(new CFException()); |
| f.obtrudeValue(four); |
| checkCompletedNormally(f, four); |
| } |
| |
| /** |
| * obtrudeException forces completion with given exception |
| */ |
| public void testObtrudeException() { |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| CFException ex; |
| CompletableFuture<Integer> f; |
| |
| f = new CompletableFuture<>(); |
| assertTrue(f.complete(v1)); |
| for (int i = 0; i < 2; i++) { |
| f.obtrudeException(ex = new CFException()); |
| checkCompletedExceptionally(f, ex); |
| } |
| |
| f = new CompletableFuture<>(); |
| for (int i = 0; i < 2; i++) { |
| f.obtrudeException(ex = new CFException()); |
| checkCompletedExceptionally(f, ex); |
| } |
| |
| f = new CompletableFuture<>(); |
| f.completeExceptionally(ex = new CFException()); |
| f.obtrudeValue(v1); |
| checkCompletedNormally(f, v1); |
| f.obtrudeException(ex = new CFException()); |
| checkCompletedExceptionally(f, ex); |
| f.completeExceptionally(new CFException()); |
| checkCompletedExceptionally(f, ex); |
| assertFalse(f.complete(v1)); |
| checkCompletedExceptionally(f, ex); |
| }} |
| |
| /** |
| * getNumberOfDependents returns number of dependent tasks |
| */ |
| public void testGetNumberOfDependents() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| assertEquals(0, f.getNumberOfDependents()); |
| final CompletableFuture<Void> g = m.thenRun(f, new Noop(m)); |
| assertEquals(1, f.getNumberOfDependents()); |
| assertEquals(0, g.getNumberOfDependents()); |
| final CompletableFuture<Void> h = m.thenRun(f, new Noop(m)); |
| assertEquals(2, f.getNumberOfDependents()); |
| assertEquals(0, h.getNumberOfDependents()); |
| assertTrue(f.complete(v1)); |
| checkCompletedNormally(g, null); |
| checkCompletedNormally(h, null); |
| assertEquals(0, f.getNumberOfDependents()); |
| assertEquals(0, g.getNumberOfDependents()); |
| assertEquals(0, h.getNumberOfDependents()); |
| }} |
| |
| /** |
| * toString indicates current completion state |
| */ |
| public void testToString() { |
| CompletableFuture<String> f; |
| |
| f = new CompletableFuture<String>(); |
| assertTrue(f.toString().contains("[Not completed]")); |
| |
| assertTrue(f.complete("foo")); |
| assertTrue(f.toString().contains("[Completed normally]")); |
| |
| f = new CompletableFuture<String>(); |
| assertTrue(f.completeExceptionally(new IndexOutOfBoundsException())); |
| // Android-changed: compatible toString() check (b/233721784) |
| // assertTrue(f.toString().contains("[Completed exceptionally]")); |
| assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]")); |
| |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { |
| f = new CompletableFuture<String>(); |
| assertTrue(f.cancel(mayInterruptIfRunning)); |
| // Android-changed: compatible toString() check (b/233721784) |
| // assertTrue(f.toString().contains("[Completed exceptionally]")); |
| assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]")); |
| } |
| } |
| |
| /** |
| * completedFuture returns a completed CompletableFuture with given value |
| */ |
| public void testCompletedFuture() { |
| CompletableFuture<String> f = CompletableFuture.completedFuture("test"); |
| checkCompletedNormally(f, "test"); |
| } |
| |
| abstract class CheckedAction { |
| int invocationCount = 0; |
| final ExecutionMode m; |
| CheckedAction(ExecutionMode m) { this.m = m; } |
| void invoked() { |
| m.checkExecutionMode(); |
| assertEquals(0, invocationCount++); |
| } |
| void assertNotInvoked() { assertEquals(0, invocationCount); } |
| void assertInvoked() { assertEquals(1, invocationCount); } |
| } |
| |
| abstract class CheckedIntegerAction extends CheckedAction { |
| Integer value; |
| CheckedIntegerAction(ExecutionMode m) { super(m); } |
| void assertValue(Integer expected) { |
| assertInvoked(); |
| assertEquals(expected, value); |
| } |
| } |
| |
| class IntegerSupplier extends CheckedAction |
| implements Supplier<Integer> |
| { |
| final Integer value; |
| IntegerSupplier(ExecutionMode m, Integer value) { |
| super(m); |
| this.value = value; |
| } |
| public Integer get() { |
| invoked(); |
| return value; |
| } |
| } |
| |
| // A function that handles and produces null values as well. |
| static Integer inc(Integer x) { |
| return (x == null) ? null : x + 1; |
| } |
| |
| class NoopConsumer extends CheckedIntegerAction |
| implements Consumer<Integer> |
| { |
| NoopConsumer(ExecutionMode m) { super(m); } |
| public void accept(Integer x) { |
| invoked(); |
| value = x; |
| } |
| } |
| |
| class IncFunction extends CheckedIntegerAction |
| implements Function<Integer,Integer> |
| { |
| IncFunction(ExecutionMode m) { super(m); } |
| public Integer apply(Integer x) { |
| invoked(); |
| return value = inc(x); |
| } |
| } |
| |
| // Choose non-commutative actions for better coverage |
| // A non-commutative function that handles and produces null values as well. |
| static Integer subtract(Integer x, Integer y) { |
| return (x == null && y == null) ? null : |
| ((x == null) ? 42 : x.intValue()) |
| - ((y == null) ? 99 : y.intValue()); |
| } |
| |
| class SubtractAction extends CheckedIntegerAction |
| implements BiConsumer<Integer, Integer> |
| { |
| SubtractAction(ExecutionMode m) { super(m); } |
| public void accept(Integer x, Integer y) { |
| invoked(); |
| value = subtract(x, y); |
| } |
| } |
| |
| class SubtractFunction extends CheckedIntegerAction |
| implements BiFunction<Integer, Integer, Integer> |
| { |
| SubtractFunction(ExecutionMode m) { super(m); } |
| public Integer apply(Integer x, Integer y) { |
| invoked(); |
| return value = subtract(x, y); |
| } |
| } |
| |
| class Noop extends CheckedAction implements Runnable { |
| Noop(ExecutionMode m) { super(m); } |
| public void run() { |
| invoked(); |
| } |
| } |
| |
| class FailingSupplier extends CheckedAction |
| implements Supplier<Integer> |
| { |
| FailingSupplier(ExecutionMode m) { super(m); } |
| public Integer get() { |
| invoked(); |
| throw new CFException(); |
| } |
| } |
| |
| class FailingConsumer extends CheckedIntegerAction |
| implements Consumer<Integer> |
| { |
| FailingConsumer(ExecutionMode m) { super(m); } |
| public void accept(Integer x) { |
| invoked(); |
| value = x; |
| throw new CFException(); |
| } |
| } |
| |
| class FailingBiConsumer extends CheckedIntegerAction |
| implements BiConsumer<Integer, Integer> |
| { |
| FailingBiConsumer(ExecutionMode m) { super(m); } |
| public void accept(Integer x, Integer y) { |
| invoked(); |
| value = subtract(x, y); |
| throw new CFException(); |
| } |
| } |
| |
| class FailingFunction extends CheckedIntegerAction |
| implements Function<Integer, Integer> |
| { |
| FailingFunction(ExecutionMode m) { super(m); } |
| public Integer apply(Integer x) { |
| invoked(); |
| value = x; |
| throw new CFException(); |
| } |
| } |
| |
| class FailingBiFunction extends CheckedIntegerAction |
| implements BiFunction<Integer, Integer, Integer> |
| { |
| FailingBiFunction(ExecutionMode m) { super(m); } |
| public Integer apply(Integer x, Integer y) { |
| invoked(); |
| value = subtract(x, y); |
| throw new CFException(); |
| } |
| } |
| |
| class FailingRunnable extends CheckedAction implements Runnable { |
| FailingRunnable(ExecutionMode m) { super(m); } |
| public void run() { |
| invoked(); |
| throw new CFException(); |
| } |
| } |
| |
| class CompletableFutureInc extends CheckedIntegerAction |
| implements Function<Integer, CompletableFuture<Integer>> |
| { |
| CompletableFutureInc(ExecutionMode m) { super(m); } |
| public CompletableFuture<Integer> apply(Integer x) { |
| invoked(); |
| value = x; |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| assertTrue(f.complete(inc(x))); |
| return f; |
| } |
| } |
| |
| class FailingCompletableFutureFunction extends CheckedIntegerAction |
| implements Function<Integer, CompletableFuture<Integer>> |
| { |
| FailingCompletableFutureFunction(ExecutionMode m) { super(m); } |
| public CompletableFuture<Integer> apply(Integer x) { |
| invoked(); |
| value = x; |
| throw new CFException(); |
| } |
| } |
| |
| // Used for explicit executor tests |
| static final class ThreadExecutor implements Executor { |
| final AtomicInteger count = new AtomicInteger(0); |
| static final ThreadGroup tg = new ThreadGroup("ThreadExecutor"); |
| static boolean startedCurrentThread() { |
| return Thread.currentThread().getThreadGroup() == tg; |
| } |
| |
| public void execute(Runnable r) { |
| count.getAndIncrement(); |
| new Thread(tg, r).start(); |
| } |
| } |
| |
| static final boolean defaultExecutorIsCommonPool |
| = ForkJoinPool.getCommonPoolParallelism() > 1; |
| |
| /** |
| * Permits the testing of parallel code for the 3 different |
| * execution modes without copy/pasting all the test methods. |
| */ |
| enum ExecutionMode { |
| SYNC { |
| public void checkExecutionMode() { |
| assertFalse(ThreadExecutor.startedCurrentThread()); |
| assertNull(ForkJoinTask.getPool()); |
| } |
| public CompletableFuture<Void> runAsync(Runnable a) { |
| throw new UnsupportedOperationException(); |
| } |
| public <U> CompletableFuture<U> supplyAsync(Supplier<U> a) { |
| throw new UnsupportedOperationException(); |
| } |
| public <T> CompletableFuture<Void> thenRun |
| (CompletableFuture<T> f, Runnable a) { |
| return f.thenRun(a); |
| } |
| public <T> CompletableFuture<Void> thenAccept |
| (CompletableFuture<T> f, Consumer<? super T> a) { |
| return f.thenAccept(a); |
| } |
| public <T,U> CompletableFuture<U> thenApply |
| (CompletableFuture<T> f, Function<? super T,U> a) { |
| return f.thenApply(a); |
| } |
| public <T,U> CompletableFuture<U> thenCompose |
| (CompletableFuture<T> f, |
| Function<? super T,? extends CompletionStage<U>> a) { |
| return f.thenCompose(a); |
| } |
| public <T,U> CompletableFuture<U> handle |
| (CompletableFuture<T> f, |
| BiFunction<? super T,Throwable,? extends U> a) { |
| return f.handle(a); |
| } |
| public <T> CompletableFuture<T> whenComplete |
| (CompletableFuture<T> f, |
| BiConsumer<? super T,? super Throwable> a) { |
| return f.whenComplete(a); |
| } |
| public <T,U> CompletableFuture<Void> runAfterBoth |
| (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a) { |
| return f.runAfterBoth(g, a); |
| } |
| public <T,U> CompletableFuture<Void> thenAcceptBoth |
| (CompletableFuture<T> f, |
| CompletionStage<? extends U> g, |
| BiConsumer<? super T,? super U> a) { |
| return f.thenAcceptBoth(g, a); |
| } |
| public <T,U,V> CompletableFuture<V> thenCombine |
| (CompletableFuture<T> f, |
| CompletionStage<? extends U> g, |
| BiFunction<? super T,? super U,? extends V> a) { |
| return f.thenCombine(g, a); |
| } |
| public <T> CompletableFuture<Void> runAfterEither |
| (CompletableFuture<T> f, |
| CompletionStage<?> g, |
| java.lang.Runnable a) { |
| return f.runAfterEither(g, a); |
| } |
| public <T> CompletableFuture<Void> acceptEither |
| (CompletableFuture<T> f, |
| CompletionStage<? extends T> g, |
| Consumer<? super T> a) { |
| return f.acceptEither(g, a); |
| } |
| public <T,U> CompletableFuture<U> applyToEither |
| (CompletableFuture<T> f, |
| CompletionStage<? extends T> g, |
| Function<? super T,U> a) { |
| return f.applyToEither(g, a); |
| } |
| }, |
| |
| ASYNC { |
| public void checkExecutionMode() { |
| assertEquals(defaultExecutorIsCommonPool, |
| (ForkJoinPool.commonPool() == ForkJoinTask.getPool())); |
| } |
| public CompletableFuture<Void> runAsync(Runnable a) { |
| return CompletableFuture.runAsync(a); |
| } |
| public <U> CompletableFuture<U> supplyAsync(Supplier<U> a) { |
| return CompletableFuture.supplyAsync(a); |
| } |
| public <T> CompletableFuture<Void> thenRun |
| (CompletableFuture<T> f, Runnable a) { |
| return f.thenRunAsync(a); |
| } |
| public <T> CompletableFuture<Void> thenAccept |
| (CompletableFuture<T> f, Consumer<? super T> a) { |
| return f.thenAcceptAsync(a); |
| } |
| public <T,U> CompletableFuture<U> thenApply |
| (CompletableFuture<T> f, Function<? super T,U> a) { |
| return f.thenApplyAsync(a); |
| } |
| public <T,U> CompletableFuture<U> thenCompose |
| (CompletableFuture<T> f, |
| Function<? super T,? extends CompletionStage<U>> a) { |
| return f.thenComposeAsync(a); |
| } |
| public <T,U> CompletableFuture<U> handle |
| (CompletableFuture<T> f, |
| BiFunction<? super T,Throwable,? extends U> a) { |
| return f.handleAsync(a); |
| } |
| public <T> CompletableFuture<T> whenComplete |
| (CompletableFuture<T> f, |
| BiConsumer<? super T,? super Throwable> a) { |
| return f.whenCompleteAsync(a); |
| } |
| public <T,U> CompletableFuture<Void> runAfterBoth |
| (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a) { |
| return f.runAfterBothAsync(g, a); |
| } |
| public <T,U> CompletableFuture<Void> thenAcceptBoth |
| (CompletableFuture<T> f, |
| CompletionStage<? extends U> g, |
| BiConsumer<? super T,? super U> a) { |
| return f.thenAcceptBothAsync(g, a); |
| } |
| public <T,U,V> CompletableFuture<V> thenCombine |
| (CompletableFuture<T> f, |
| CompletionStage<? extends U> g, |
| BiFunction<? super T,? super U,? extends V> a) { |
| return f.thenCombineAsync(g, a); |
| } |
| public <T> CompletableFuture<Void> runAfterEither |
| (CompletableFuture<T> f, |
| CompletionStage<?> g, |
| java.lang.Runnable a) { |
| return f.runAfterEitherAsync(g, a); |
| } |
| public <T> CompletableFuture<Void> acceptEither |
| (CompletableFuture<T> f, |
| CompletionStage<? extends T> g, |
| Consumer<? super T> a) { |
| return f.acceptEitherAsync(g, a); |
| } |
| public <T,U> CompletableFuture<U> applyToEither |
| (CompletableFuture<T> f, |
| CompletionStage<? extends T> g, |
| Function<? super T,U> a) { |
| return f.applyToEitherAsync(g, a); |
| } |
| }, |
| |
| EXECUTOR { |
| public void checkExecutionMode() { |
| assertTrue(ThreadExecutor.startedCurrentThread()); |
| } |
| public CompletableFuture<Void> runAsync(Runnable a) { |
| return CompletableFuture.runAsync(a, new ThreadExecutor()); |
| } |
| public <U> CompletableFuture<U> supplyAsync(Supplier<U> a) { |
| return CompletableFuture.supplyAsync(a, new ThreadExecutor()); |
| } |
| public <T> CompletableFuture<Void> thenRun |
| (CompletableFuture<T> f, Runnable a) { |
| return f.thenRunAsync(a, new ThreadExecutor()); |
| } |
| public <T> CompletableFuture<Void> thenAccept |
| (CompletableFuture<T> f, Consumer<? super T> a) { |
| return f.thenAcceptAsync(a, new ThreadExecutor()); |
| } |
| public <T,U> CompletableFuture<U> thenApply |
| (CompletableFuture<T> f, Function<? super T,U> a) { |
| return f.thenApplyAsync(a, new ThreadExecutor()); |
| } |
| public <T,U> CompletableFuture<U> thenCompose |
| (CompletableFuture<T> f, |
| Function<? super T,? extends CompletionStage<U>> a) { |
| return f.thenComposeAsync(a, new ThreadExecutor()); |
| } |
| public <T,U> CompletableFuture<U> handle |
| (CompletableFuture<T> f, |
| BiFunction<? super T,Throwable,? extends U> a) { |
| return f.handleAsync(a, new ThreadExecutor()); |
| } |
| public <T> CompletableFuture<T> whenComplete |
| (CompletableFuture<T> f, |
| BiConsumer<? super T,? super Throwable> a) { |
| return f.whenCompleteAsync(a, new ThreadExecutor()); |
| } |
| public <T,U> CompletableFuture<Void> runAfterBoth |
| (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a) { |
| return f.runAfterBothAsync(g, a, new ThreadExecutor()); |
| } |
| public <T,U> CompletableFuture<Void> thenAcceptBoth |
| (CompletableFuture<T> f, |
| CompletionStage<? extends U> g, |
| BiConsumer<? super T,? super U> a) { |
| return f.thenAcceptBothAsync(g, a, new ThreadExecutor()); |
| } |
| public <T,U,V> CompletableFuture<V> thenCombine |
| (CompletableFuture<T> f, |
| CompletionStage<? extends U> g, |
| BiFunction<? super T,? super U,? extends V> a) { |
| return f.thenCombineAsync(g, a, new ThreadExecutor()); |
| } |
| public <T> CompletableFuture<Void> runAfterEither |
| (CompletableFuture<T> f, |
| CompletionStage<?> g, |
| java.lang.Runnable a) { |
| return f.runAfterEitherAsync(g, a, new ThreadExecutor()); |
| } |
| public <T> CompletableFuture<Void> acceptEither |
| (CompletableFuture<T> f, |
| CompletionStage<? extends T> g, |
| Consumer<? super T> a) { |
| return f.acceptEitherAsync(g, a, new ThreadExecutor()); |
| } |
| public <T,U> CompletableFuture<U> applyToEither |
| (CompletableFuture<T> f, |
| CompletionStage<? extends T> g, |
| Function<? super T,U> a) { |
| return f.applyToEitherAsync(g, a, new ThreadExecutor()); |
| } |
| }; |
| |
| public abstract void checkExecutionMode(); |
| public abstract CompletableFuture<Void> runAsync(Runnable a); |
| public abstract <U> CompletableFuture<U> supplyAsync(Supplier<U> a); |
| public abstract <T> CompletableFuture<Void> thenRun |
| (CompletableFuture<T> f, Runnable a); |
| public abstract <T> CompletableFuture<Void> thenAccept |
| (CompletableFuture<T> f, Consumer<? super T> a); |
| public abstract <T,U> CompletableFuture<U> thenApply |
| (CompletableFuture<T> f, Function<? super T,U> a); |
| public abstract <T,U> CompletableFuture<U> thenCompose |
| (CompletableFuture<T> f, |
| Function<? super T,? extends CompletionStage<U>> a); |
| public abstract <T,U> CompletableFuture<U> handle |
| (CompletableFuture<T> f, |
| BiFunction<? super T,Throwable,? extends U> a); |
| public abstract <T> CompletableFuture<T> whenComplete |
| (CompletableFuture<T> f, |
| BiConsumer<? super T,? super Throwable> a); |
| public abstract <T,U> CompletableFuture<Void> runAfterBoth |
| (CompletableFuture<T> f, CompletableFuture<U> g, Runnable a); |
| public abstract <T,U> CompletableFuture<Void> thenAcceptBoth |
| (CompletableFuture<T> f, |
| CompletionStage<? extends U> g, |
| BiConsumer<? super T,? super U> a); |
| public abstract <T,U,V> CompletableFuture<V> thenCombine |
| (CompletableFuture<T> f, |
| CompletionStage<? extends U> g, |
| BiFunction<? super T,? super U,? extends V> a); |
| public abstract <T> CompletableFuture<Void> runAfterEither |
| (CompletableFuture<T> f, |
| CompletionStage<?> g, |
| java.lang.Runnable a); |
| public abstract <T> CompletableFuture<Void> acceptEither |
| (CompletableFuture<T> f, |
| CompletionStage<? extends T> g, |
| Consumer<? super T> a); |
| public abstract <T,U> CompletableFuture<U> applyToEither |
| (CompletableFuture<T> f, |
| CompletionStage<? extends T> g, |
| Function<? super T,U> a); |
| } |
| |
| /** |
| * exceptionally action is not invoked when source completes |
| * normally, and source result is propagated |
| */ |
| public void testExceptionally_normalCompletion() { |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final AtomicInteger a = new AtomicInteger(0); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| if (!createIncomplete) assertTrue(f.complete(v1)); |
| final CompletableFuture<Integer> g = f.exceptionally |
| ((Throwable t) -> { |
| a.getAndIncrement(); |
| threadFail("should not be called"); |
| return null; // unreached |
| }); |
| if (createIncomplete) assertTrue(f.complete(v1)); |
| |
| checkCompletedNormally(g, v1); |
| checkCompletedNormally(f, v1); |
| assertEquals(0, a.get()); |
| }} |
| |
| /** |
| * exceptionally action completes with function value on source |
| * exception |
| */ |
| public void testExceptionally_exceptionalCompletion() { |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final AtomicInteger a = new AtomicInteger(0); |
| final CFException ex = new CFException(); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| if (!createIncomplete) f.completeExceptionally(ex); |
| final CompletableFuture<Integer> g = f.exceptionally |
| ((Throwable t) -> { |
| ExecutionMode.SYNC.checkExecutionMode(); |
| threadAssertSame(t, ex); |
| a.getAndIncrement(); |
| return v1; |
| }); |
| if (createIncomplete) f.completeExceptionally(ex); |
| |
| checkCompletedNormally(g, v1); |
| assertEquals(1, a.get()); |
| }} |
| |
| /** |
| * If an "exceptionally action" throws an exception, it completes |
| * exceptionally with that exception |
| */ |
| public void testExceptionally_exceptionalCompletionActionFailed() { |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| { |
| final AtomicInteger a = new AtomicInteger(0); |
| final CFException ex1 = new CFException(); |
| final CFException ex2 = new CFException(); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| if (!createIncomplete) f.completeExceptionally(ex1); |
| final CompletableFuture<Integer> g = f.exceptionally |
| ((Throwable t) -> { |
| ExecutionMode.SYNC.checkExecutionMode(); |
| threadAssertSame(t, ex1); |
| a.getAndIncrement(); |
| throw ex2; |
| }); |
| if (createIncomplete) f.completeExceptionally(ex1); |
| |
| checkCompletedWithWrappedException(g, ex2); |
| checkCompletedExceptionally(f, ex1); |
| assertEquals(1, a.get()); |
| }} |
| |
| /** |
| * whenComplete action executes on normal completion, propagating |
| * source result. |
| */ |
| public void testWhenComplete_normalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final AtomicInteger a = new AtomicInteger(0); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| if (!createIncomplete) assertTrue(f.complete(v1)); |
| final CompletableFuture<Integer> g = m.whenComplete |
| (f, |
| (Integer result, Throwable t) -> { |
| m.checkExecutionMode(); |
| threadAssertSame(result, v1); |
| threadAssertNull(t); |
| a.getAndIncrement(); |
| }); |
| if (createIncomplete) assertTrue(f.complete(v1)); |
| |
| checkCompletedNormally(g, v1); |
| checkCompletedNormally(f, v1); |
| assertEquals(1, a.get()); |
| }} |
| |
| /** |
| * whenComplete action executes on exceptional completion, propagating |
| * source result. |
| */ |
| public void testWhenComplete_exceptionalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| { |
| final AtomicInteger a = new AtomicInteger(0); |
| final CFException ex = new CFException(); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| if (!createIncomplete) f.completeExceptionally(ex); |
| final CompletableFuture<Integer> g = m.whenComplete |
| (f, |
| (Integer result, Throwable t) -> { |
| m.checkExecutionMode(); |
| threadAssertNull(result); |
| threadAssertSame(t, ex); |
| a.getAndIncrement(); |
| }); |
| if (createIncomplete) f.completeExceptionally(ex); |
| |
| checkCompletedWithWrappedException(g, ex); |
| checkCompletedExceptionally(f, ex); |
| assertEquals(1, a.get()); |
| }} |
| |
| /** |
| * whenComplete action executes on cancelled source, propagating |
| * CancellationException. |
| */ |
| public void testWhenComplete_sourceCancelled() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| { |
| final AtomicInteger a = new AtomicInteger(0); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning)); |
| final CompletableFuture<Integer> g = m.whenComplete |
| (f, |
| (Integer result, Throwable t) -> { |
| m.checkExecutionMode(); |
| threadAssertNull(result); |
| threadAssertTrue(t instanceof CancellationException); |
| a.getAndIncrement(); |
| }); |
| if (createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning)); |
| |
| checkCompletedWithWrappedCancellationException(g); |
| checkCancelled(f); |
| assertEquals(1, a.get()); |
| }} |
| |
| /** |
| * If a whenComplete action throws an exception when triggered by |
| * a normal completion, it completes exceptionally |
| */ |
| public void testWhenComplete_sourceCompletedNormallyActionFailed() { |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final AtomicInteger a = new AtomicInteger(0); |
| final CFException ex = new CFException(); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| if (!createIncomplete) assertTrue(f.complete(v1)); |
| final CompletableFuture<Integer> g = m.whenComplete |
| (f, |
| (Integer result, Throwable t) -> { |
| m.checkExecutionMode(); |
| threadAssertSame(result, v1); |
| threadAssertNull(t); |
| a.getAndIncrement(); |
| throw ex; |
| }); |
| if (createIncomplete) assertTrue(f.complete(v1)); |
| |
| checkCompletedWithWrappedException(g, ex); |
| checkCompletedNormally(f, v1); |
| assertEquals(1, a.get()); |
| }} |
| |
| /** |
| * If a whenComplete action throws an exception when triggered by |
| * a source completion that also throws an exception, the source |
| * exception takes precedence (unlike handle) |
| */ |
| public void testWhenComplete_sourceFailedActionFailed() { |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| for (ExecutionMode m : ExecutionMode.values()) |
| { |
| final AtomicInteger a = new AtomicInteger(0); |
| final CFException ex1 = new CFException(); |
| final CFException ex2 = new CFException(); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| |
| if (!createIncomplete) f.completeExceptionally(ex1); |
| final CompletableFuture<Integer> g = m.whenComplete |
| (f, |
| (Integer result, Throwable t) -> { |
| m.checkExecutionMode(); |
| threadAssertSame(t, ex1); |
| threadAssertNull(result); |
| a.getAndIncrement(); |
| throw ex2; |
| }); |
| if (createIncomplete) f.completeExceptionally(ex1); |
| |
| checkCompletedWithWrappedException(g, ex1); |
| checkCompletedExceptionally(f, ex1); |
| if (testImplementationDetails) { |
| assertEquals(1, ex1.getSuppressed().length); |
| assertSame(ex2, ex1.getSuppressed()[0]); |
| } |
| assertEquals(1, a.get()); |
| }} |
| |
| /** |
| * handle action completes normally with function value on normal |
| * completion of source |
| */ |
| public void testHandle_normalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final AtomicInteger a = new AtomicInteger(0); |
| if (!createIncomplete) assertTrue(f.complete(v1)); |
| final CompletableFuture<Integer> g = m.handle |
| (f, |
| (Integer result, Throwable t) -> { |
| m.checkExecutionMode(); |
| threadAssertSame(result, v1); |
| threadAssertNull(t); |
| a.getAndIncrement(); |
| return inc(v1); |
| }); |
| if (createIncomplete) assertTrue(f.complete(v1)); |
| |
| checkCompletedNormally(g, inc(v1)); |
| checkCompletedNormally(f, v1); |
| assertEquals(1, a.get()); |
| }} |
| |
| /** |
| * handle action completes normally with function value on |
| * exceptional completion of source |
| */ |
| public void testHandle_exceptionalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final AtomicInteger a = new AtomicInteger(0); |
| final CFException ex = new CFException(); |
| if (!createIncomplete) f.completeExceptionally(ex); |
| final CompletableFuture<Integer> g = m.handle |
| (f, |
| (Integer result, Throwable t) -> { |
| m.checkExecutionMode(); |
| threadAssertNull(result); |
| threadAssertSame(t, ex); |
| a.getAndIncrement(); |
| return v1; |
| }); |
| if (createIncomplete) f.completeExceptionally(ex); |
| |
| checkCompletedNormally(g, v1); |
| checkCompletedExceptionally(f, ex); |
| assertEquals(1, a.get()); |
| }} |
| |
| /** |
| * handle action completes normally with function value on |
| * cancelled source |
| */ |
| public void testHandle_sourceCancelled() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final AtomicInteger a = new AtomicInteger(0); |
| if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning)); |
| final CompletableFuture<Integer> g = m.handle |
| (f, |
| (Integer result, Throwable t) -> { |
| m.checkExecutionMode(); |
| threadAssertNull(result); |
| threadAssertTrue(t instanceof CancellationException); |
| a.getAndIncrement(); |
| return v1; |
| }); |
| if (createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning)); |
| |
| checkCompletedNormally(g, v1); |
| checkCancelled(f); |
| assertEquals(1, a.get()); |
| }} |
| |
| /** |
| * If a "handle action" throws an exception when triggered by |
| * a normal completion, it completes exceptionally |
| */ |
| public void testHandle_sourceCompletedNormallyActionFailed() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final AtomicInteger a = new AtomicInteger(0); |
| final CFException ex = new CFException(); |
| if (!createIncomplete) assertTrue(f.complete(v1)); |
| final CompletableFuture<Integer> g = m.handle |
| (f, |
| (Integer result, Throwable t) -> { |
| m.checkExecutionMode(); |
| threadAssertSame(result, v1); |
| threadAssertNull(t); |
| a.getAndIncrement(); |
| throw ex; |
| }); |
| if (createIncomplete) assertTrue(f.complete(v1)); |
| |
| checkCompletedWithWrappedException(g, ex); |
| checkCompletedNormally(f, v1); |
| assertEquals(1, a.get()); |
| }} |
| |
| /** |
| * If a "handle action" throws an exception when triggered by |
| * a source completion that also throws an exception, the action |
| * exception takes precedence (unlike whenComplete) |
| */ |
| public void testHandle_sourceFailedActionFailed() { |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| for (ExecutionMode m : ExecutionMode.values()) |
| { |
| final AtomicInteger a = new AtomicInteger(0); |
| final CFException ex1 = new CFException(); |
| final CFException ex2 = new CFException(); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| |
| if (!createIncomplete) f.completeExceptionally(ex1); |
| final CompletableFuture<Integer> g = m.handle |
| (f, |
| (Integer result, Throwable t) -> { |
| m.checkExecutionMode(); |
| threadAssertNull(result); |
| threadAssertSame(ex1, t); |
| a.getAndIncrement(); |
| throw ex2; |
| }); |
| if (createIncomplete) f.completeExceptionally(ex1); |
| |
| checkCompletedWithWrappedException(g, ex2); |
| checkCompletedExceptionally(f, ex1); |
| assertEquals(1, a.get()); |
| }} |
| |
| /** |
| * runAsync completes after running Runnable |
| */ |
| public void testRunAsync_normalCompletion() { |
| ExecutionMode[] executionModes = { |
| ExecutionMode.ASYNC, |
| ExecutionMode.EXECUTOR, |
| }; |
| for (ExecutionMode m : executionModes) |
| { |
| final Noop r = new Noop(m); |
| final CompletableFuture<Void> f = m.runAsync(r); |
| assertNull(f.join()); |
| checkCompletedNormally(f, null); |
| r.assertInvoked(); |
| }} |
| |
| /** |
| * failing runAsync completes exceptionally after running Runnable |
| */ |
| public void testRunAsync_exceptionalCompletion() { |
| ExecutionMode[] executionModes = { |
| ExecutionMode.ASYNC, |
| ExecutionMode.EXECUTOR, |
| }; |
| for (ExecutionMode m : executionModes) |
| { |
| final FailingRunnable r = new FailingRunnable(m); |
| final CompletableFuture<Void> f = m.runAsync(r); |
| checkCompletedWithWrappedCFException(f); |
| r.assertInvoked(); |
| }} |
| |
| /** |
| * supplyAsync completes with result of supplier |
| */ |
| public void testSupplyAsync_normalCompletion() { |
| ExecutionMode[] executionModes = { |
| ExecutionMode.ASYNC, |
| ExecutionMode.EXECUTOR, |
| }; |
| for (ExecutionMode m : executionModes) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final IntegerSupplier r = new IntegerSupplier(m, v1); |
| final CompletableFuture<Integer> f = m.supplyAsync(r); |
| assertSame(v1, f.join()); |
| checkCompletedNormally(f, v1); |
| r.assertInvoked(); |
| }} |
| |
| /** |
| * Failing supplyAsync completes exceptionally |
| */ |
| public void testSupplyAsync_exceptionalCompletion() { |
| ExecutionMode[] executionModes = { |
| ExecutionMode.ASYNC, |
| ExecutionMode.EXECUTOR, |
| }; |
| for (ExecutionMode m : executionModes) |
| { |
| FailingSupplier r = new FailingSupplier(m); |
| CompletableFuture<Integer> f = m.supplyAsync(r); |
| checkCompletedWithWrappedCFException(f); |
| r.assertInvoked(); |
| }} |
| |
| // seq completion methods |
| |
| /** |
| * thenRun result completes normally after normal completion of source |
| */ |
| public void testThenRun_normalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final Noop[] rs = new Noop[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); |
| |
| final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]); |
| final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]); |
| final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| checkIncomplete(h2); |
| assertTrue(f.complete(v1)); |
| final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]); |
| final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]); |
| final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]); |
| |
| checkCompletedNormally(h0, null); |
| checkCompletedNormally(h1, null); |
| checkCompletedNormally(h2, null); |
| checkCompletedNormally(h3, null); |
| checkCompletedNormally(h4, null); |
| checkCompletedNormally(h5, null); |
| checkCompletedNormally(f, v1); |
| for (Noop r : rs) r.assertInvoked(); |
| }} |
| |
| /** |
| * thenRun result completes exceptionally after exceptional |
| * completion of source |
| */ |
| public void testThenRun_exceptionalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| { |
| final CFException ex = new CFException(); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final Noop[] rs = new Noop[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); |
| |
| final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]); |
| final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]); |
| final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| checkIncomplete(h2); |
| assertTrue(f.completeExceptionally(ex)); |
| final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]); |
| final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]); |
| final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]); |
| |
| checkCompletedWithWrappedException(h0, ex); |
| checkCompletedWithWrappedException(h1, ex); |
| checkCompletedWithWrappedException(h2, ex); |
| checkCompletedWithWrappedException(h3, ex); |
| checkCompletedWithWrappedException(h4, ex); |
| checkCompletedWithWrappedException(h5, ex); |
| checkCompletedExceptionally(f, ex); |
| for (Noop r : rs) r.assertNotInvoked(); |
| }} |
| |
| /** |
| * thenRun result completes exceptionally if source cancelled |
| */ |
| public void testThenRun_sourceCancelled() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final Noop[] rs = new Noop[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); |
| |
| final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]); |
| final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]); |
| final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| checkIncomplete(h2); |
| assertTrue(f.cancel(mayInterruptIfRunning)); |
| final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]); |
| final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]); |
| final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]); |
| |
| checkCompletedWithWrappedCancellationException(h0); |
| checkCompletedWithWrappedCancellationException(h1); |
| checkCompletedWithWrappedCancellationException(h2); |
| checkCompletedWithWrappedCancellationException(h3); |
| checkCompletedWithWrappedCancellationException(h4); |
| checkCompletedWithWrappedCancellationException(h5); |
| checkCancelled(f); |
| for (Noop r : rs) r.assertNotInvoked(); |
| }} |
| |
| /** |
| * thenRun result completes exceptionally if action does |
| */ |
| public void testThenRun_actionFailed() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final FailingRunnable[] rs = new FailingRunnable[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new FailingRunnable(m); |
| |
| final CompletableFuture<Void> h0 = m.thenRun(f, rs[0]); |
| final CompletableFuture<Void> h1 = m.runAfterBoth(f, f, rs[1]); |
| final CompletableFuture<Void> h2 = m.runAfterEither(f, f, rs[2]); |
| assertTrue(f.complete(v1)); |
| final CompletableFuture<Void> h3 = m.thenRun(f, rs[3]); |
| final CompletableFuture<Void> h4 = m.runAfterBoth(f, f, rs[4]); |
| final CompletableFuture<Void> h5 = m.runAfterEither(f, f, rs[5]); |
| |
| checkCompletedWithWrappedCFException(h0); |
| checkCompletedWithWrappedCFException(h1); |
| checkCompletedWithWrappedCFException(h2); |
| checkCompletedWithWrappedCFException(h3); |
| checkCompletedWithWrappedCFException(h4); |
| checkCompletedWithWrappedCFException(h5); |
| checkCompletedNormally(f, v1); |
| }} |
| |
| /** |
| * thenApply result completes normally after normal completion of source |
| */ |
| public void testThenApply_normalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final IncFunction[] rs = new IncFunction[4]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); |
| |
| final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]); |
| final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| assertTrue(f.complete(v1)); |
| final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]); |
| final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]); |
| |
| checkCompletedNormally(h0, inc(v1)); |
| checkCompletedNormally(h1, inc(v1)); |
| checkCompletedNormally(h2, inc(v1)); |
| checkCompletedNormally(h3, inc(v1)); |
| checkCompletedNormally(f, v1); |
| for (IncFunction r : rs) r.assertValue(inc(v1)); |
| }} |
| |
| /** |
| * thenApply result completes exceptionally after exceptional |
| * completion of source |
| */ |
| public void testThenApply_exceptionalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| { |
| final CFException ex = new CFException(); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final IncFunction[] rs = new IncFunction[4]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); |
| |
| final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]); |
| final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]); |
| assertTrue(f.completeExceptionally(ex)); |
| final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]); |
| final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]); |
| |
| checkCompletedWithWrappedException(h0, ex); |
| checkCompletedWithWrappedException(h1, ex); |
| checkCompletedWithWrappedException(h2, ex); |
| checkCompletedWithWrappedException(h3, ex); |
| checkCompletedExceptionally(f, ex); |
| for (IncFunction r : rs) r.assertNotInvoked(); |
| }} |
| |
| /** |
| * thenApply result completes exceptionally if source cancelled |
| */ |
| public void testThenApply_sourceCancelled() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final IncFunction[] rs = new IncFunction[4]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); |
| |
| final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]); |
| final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]); |
| assertTrue(f.cancel(mayInterruptIfRunning)); |
| final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]); |
| final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]); |
| |
| checkCompletedWithWrappedCancellationException(h0); |
| checkCompletedWithWrappedCancellationException(h1); |
| checkCompletedWithWrappedCancellationException(h2); |
| checkCompletedWithWrappedCancellationException(h3); |
| checkCancelled(f); |
| for (IncFunction r : rs) r.assertNotInvoked(); |
| }} |
| |
| /** |
| * thenApply result completes exceptionally if action does |
| */ |
| public void testThenApply_actionFailed() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final FailingFunction[] rs = new FailingFunction[4]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new FailingFunction(m); |
| |
| final CompletableFuture<Integer> h0 = m.thenApply(f, rs[0]); |
| final CompletableFuture<Integer> h1 = m.applyToEither(f, f, rs[1]); |
| assertTrue(f.complete(v1)); |
| final CompletableFuture<Integer> h2 = m.thenApply(f, rs[2]); |
| final CompletableFuture<Integer> h3 = m.applyToEither(f, f, rs[3]); |
| |
| checkCompletedWithWrappedCFException(h0); |
| checkCompletedWithWrappedCFException(h1); |
| checkCompletedWithWrappedCFException(h2); |
| checkCompletedWithWrappedCFException(h3); |
| checkCompletedNormally(f, v1); |
| }} |
| |
| /** |
| * thenAccept result completes normally after normal completion of source |
| */ |
| public void testThenAccept_normalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final NoopConsumer[] rs = new NoopConsumer[4]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); |
| |
| final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]); |
| final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| assertTrue(f.complete(v1)); |
| final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]); |
| final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]); |
| |
| checkCompletedNormally(h0, null); |
| checkCompletedNormally(h1, null); |
| checkCompletedNormally(h2, null); |
| checkCompletedNormally(h3, null); |
| checkCompletedNormally(f, v1); |
| for (NoopConsumer r : rs) r.assertValue(v1); |
| }} |
| |
| /** |
| * thenAccept result completes exceptionally after exceptional |
| * completion of source |
| */ |
| public void testThenAccept_exceptionalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| { |
| final CFException ex = new CFException(); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final NoopConsumer[] rs = new NoopConsumer[4]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); |
| |
| final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]); |
| final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]); |
| assertTrue(f.completeExceptionally(ex)); |
| final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]); |
| final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]); |
| |
| checkCompletedWithWrappedException(h0, ex); |
| checkCompletedWithWrappedException(h1, ex); |
| checkCompletedWithWrappedException(h2, ex); |
| checkCompletedWithWrappedException(h3, ex); |
| checkCompletedExceptionally(f, ex); |
| for (NoopConsumer r : rs) r.assertNotInvoked(); |
| }} |
| |
| /** |
| * thenAccept result completes exceptionally if source cancelled |
| */ |
| public void testThenAccept_sourceCancelled() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final NoopConsumer[] rs = new NoopConsumer[4]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); |
| |
| final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]); |
| final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]); |
| assertTrue(f.cancel(mayInterruptIfRunning)); |
| final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]); |
| final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]); |
| |
| checkCompletedWithWrappedCancellationException(h0); |
| checkCompletedWithWrappedCancellationException(h1); |
| checkCompletedWithWrappedCancellationException(h2); |
| checkCompletedWithWrappedCancellationException(h3); |
| checkCancelled(f); |
| for (NoopConsumer r : rs) r.assertNotInvoked(); |
| }} |
| |
| /** |
| * thenAccept result completes exceptionally if action does |
| */ |
| public void testThenAccept_actionFailed() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final FailingConsumer[] rs = new FailingConsumer[4]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new FailingConsumer(m); |
| |
| final CompletableFuture<Void> h0 = m.thenAccept(f, rs[0]); |
| final CompletableFuture<Void> h1 = m.acceptEither(f, f, rs[1]); |
| assertTrue(f.complete(v1)); |
| final CompletableFuture<Void> h2 = m.thenAccept(f, rs[2]); |
| final CompletableFuture<Void> h3 = m.acceptEither(f, f, rs[3]); |
| |
| checkCompletedWithWrappedCFException(h0); |
| checkCompletedWithWrappedCFException(h1); |
| checkCompletedWithWrappedCFException(h2); |
| checkCompletedWithWrappedCFException(h3); |
| checkCompletedNormally(f, v1); |
| }} |
| |
| /** |
| * thenCombine result completes normally after normal completion |
| * of sources |
| */ |
| public void testThenCombine_normalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| for (Integer v2 : new Integer[] { 2, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final SubtractFunction[] rs = new SubtractFunction[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new SubtractFunction(m); |
| |
| final CompletableFuture<Integer> fst = fFirst ? f : g; |
| final CompletableFuture<Integer> snd = !fFirst ? f : g; |
| final Integer w1 = fFirst ? v1 : v2; |
| final Integer w2 = !fFirst ? v1 : v2; |
| |
| final CompletableFuture<Integer> h0 = m.thenCombine(f, g, rs[0]); |
| final CompletableFuture<Integer> h1 = m.thenCombine(fst, fst, rs[1]); |
| assertTrue(fst.complete(w1)); |
| final CompletableFuture<Integer> h2 = m.thenCombine(f, g, rs[2]); |
| final CompletableFuture<Integer> h3 = m.thenCombine(fst, fst, rs[3]); |
| checkIncomplete(h0); rs[0].assertNotInvoked(); |
| checkIncomplete(h2); rs[2].assertNotInvoked(); |
| checkCompletedNormally(h1, subtract(w1, w1)); |
| checkCompletedNormally(h3, subtract(w1, w1)); |
| rs[1].assertValue(subtract(w1, w1)); |
| rs[3].assertValue(subtract(w1, w1)); |
| assertTrue(snd.complete(w2)); |
| final CompletableFuture<Integer> h4 = m.thenCombine(f, g, rs[4]); |
| |
| checkCompletedNormally(h0, subtract(v1, v2)); |
| checkCompletedNormally(h2, subtract(v1, v2)); |
| checkCompletedNormally(h4, subtract(v1, v2)); |
| rs[0].assertValue(subtract(v1, v2)); |
| rs[2].assertValue(subtract(v1, v2)); |
| rs[4].assertValue(subtract(v1, v2)); |
| |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v2); |
| }} |
| |
| /** |
| * thenCombine result completes exceptionally after exceptional |
| * completion of either source |
| */ |
| public void testThenCombine_exceptionalCompletion() throws Throwable { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (boolean failFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final CFException ex = new CFException(); |
| final SubtractFunction r1 = new SubtractFunction(m); |
| final SubtractFunction r2 = new SubtractFunction(m); |
| final SubtractFunction r3 = new SubtractFunction(m); |
| |
| final CompletableFuture<Integer> fst = fFirst ? f : g; |
| final CompletableFuture<Integer> snd = !fFirst ? f : g; |
| final Callable<Boolean> complete1 = failFirst ? |
| () -> fst.completeExceptionally(ex) : |
| () -> fst.complete(v1); |
| final Callable<Boolean> complete2 = failFirst ? |
| () -> snd.complete(v1) : |
| () -> snd.completeExceptionally(ex); |
| |
| final CompletableFuture<Integer> h1 = m.thenCombine(f, g, r1); |
| assertTrue(complete1.call()); |
| final CompletableFuture<Integer> h2 = m.thenCombine(f, g, r2); |
| checkIncomplete(h1); |
| checkIncomplete(h2); |
| assertTrue(complete2.call()); |
| final CompletableFuture<Integer> h3 = m.thenCombine(f, g, r3); |
| |
| checkCompletedWithWrappedException(h1, ex); |
| checkCompletedWithWrappedException(h2, ex); |
| checkCompletedWithWrappedException(h3, ex); |
| r1.assertNotInvoked(); |
| r2.assertNotInvoked(); |
| r3.assertNotInvoked(); |
| checkCompletedNormally(failFirst ? snd : fst, v1); |
| checkCompletedExceptionally(failFirst ? fst : snd, ex); |
| }} |
| |
| /** |
| * thenCombine result completes exceptionally if either source cancelled |
| */ |
| public void testThenCombine_sourceCancelled() throws Throwable { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (boolean failFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final SubtractFunction r1 = new SubtractFunction(m); |
| final SubtractFunction r2 = new SubtractFunction(m); |
| final SubtractFunction r3 = new SubtractFunction(m); |
| |
| final CompletableFuture<Integer> fst = fFirst ? f : g; |
| final CompletableFuture<Integer> snd = !fFirst ? f : g; |
| final Callable<Boolean> complete1 = failFirst ? |
| () -> fst.cancel(mayInterruptIfRunning) : |
| () -> fst.complete(v1); |
| final Callable<Boolean> complete2 = failFirst ? |
| () -> snd.complete(v1) : |
| () -> snd.cancel(mayInterruptIfRunning); |
| |
| final CompletableFuture<Integer> h1 = m.thenCombine(f, g, r1); |
| assertTrue(complete1.call()); |
| final CompletableFuture<Integer> h2 = m.thenCombine(f, g, r2); |
| checkIncomplete(h1); |
| checkIncomplete(h2); |
| assertTrue(complete2.call()); |
| final CompletableFuture<Integer> h3 = m.thenCombine(f, g, r3); |
| |
| checkCompletedWithWrappedCancellationException(h1); |
| checkCompletedWithWrappedCancellationException(h2); |
| checkCompletedWithWrappedCancellationException(h3); |
| r1.assertNotInvoked(); |
| r2.assertNotInvoked(); |
| r3.assertNotInvoked(); |
| checkCompletedNormally(failFirst ? snd : fst, v1); |
| checkCancelled(failFirst ? fst : snd); |
| }} |
| |
| /** |
| * thenCombine result completes exceptionally if action does |
| */ |
| public void testThenCombine_actionFailed() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| for (Integer v2 : new Integer[] { 2, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final FailingBiFunction r1 = new FailingBiFunction(m); |
| final FailingBiFunction r2 = new FailingBiFunction(m); |
| final FailingBiFunction r3 = new FailingBiFunction(m); |
| |
| final CompletableFuture<Integer> fst = fFirst ? f : g; |
| final CompletableFuture<Integer> snd = !fFirst ? f : g; |
| final Integer w1 = fFirst ? v1 : v2; |
| final Integer w2 = !fFirst ? v1 : v2; |
| |
| final CompletableFuture<Integer> h1 = m.thenCombine(f, g, r1); |
| assertTrue(fst.complete(w1)); |
| final CompletableFuture<Integer> h2 = m.thenCombine(f, g, r2); |
| assertTrue(snd.complete(w2)); |
| final CompletableFuture<Integer> h3 = m.thenCombine(f, g, r3); |
| |
| checkCompletedWithWrappedCFException(h1); |
| checkCompletedWithWrappedCFException(h2); |
| checkCompletedWithWrappedCFException(h3); |
| r1.assertInvoked(); |
| r2.assertInvoked(); |
| r3.assertInvoked(); |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v2); |
| }} |
| |
| /** |
| * thenAcceptBoth result completes normally after normal |
| * completion of sources |
| */ |
| public void testThenAcceptBoth_normalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| for (Integer v2 : new Integer[] { 2, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final SubtractAction r1 = new SubtractAction(m); |
| final SubtractAction r2 = new SubtractAction(m); |
| final SubtractAction r3 = new SubtractAction(m); |
| |
| final CompletableFuture<Integer> fst = fFirst ? f : g; |
| final CompletableFuture<Integer> snd = !fFirst ? f : g; |
| final Integer w1 = fFirst ? v1 : v2; |
| final Integer w2 = !fFirst ? v1 : v2; |
| |
| final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1); |
| assertTrue(fst.complete(w1)); |
| final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2); |
| checkIncomplete(h1); |
| checkIncomplete(h2); |
| r1.assertNotInvoked(); |
| r2.assertNotInvoked(); |
| assertTrue(snd.complete(w2)); |
| final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3); |
| |
| checkCompletedNormally(h1, null); |
| checkCompletedNormally(h2, null); |
| checkCompletedNormally(h3, null); |
| r1.assertValue(subtract(v1, v2)); |
| r2.assertValue(subtract(v1, v2)); |
| r3.assertValue(subtract(v1, v2)); |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v2); |
| }} |
| |
| /** |
| * thenAcceptBoth result completes exceptionally after exceptional |
| * completion of either source |
| */ |
| public void testThenAcceptBoth_exceptionalCompletion() throws Throwable { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (boolean failFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final CFException ex = new CFException(); |
| final SubtractAction r1 = new SubtractAction(m); |
| final SubtractAction r2 = new SubtractAction(m); |
| final SubtractAction r3 = new SubtractAction(m); |
| |
| final CompletableFuture<Integer> fst = fFirst ? f : g; |
| final CompletableFuture<Integer> snd = !fFirst ? f : g; |
| final Callable<Boolean> complete1 = failFirst ? |
| () -> fst.completeExceptionally(ex) : |
| () -> fst.complete(v1); |
| final Callable<Boolean> complete2 = failFirst ? |
| () -> snd.complete(v1) : |
| () -> snd.completeExceptionally(ex); |
| |
| final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1); |
| assertTrue(complete1.call()); |
| final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2); |
| checkIncomplete(h1); |
| checkIncomplete(h2); |
| assertTrue(complete2.call()); |
| final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3); |
| |
| checkCompletedWithWrappedException(h1, ex); |
| checkCompletedWithWrappedException(h2, ex); |
| checkCompletedWithWrappedException(h3, ex); |
| r1.assertNotInvoked(); |
| r2.assertNotInvoked(); |
| r3.assertNotInvoked(); |
| checkCompletedNormally(failFirst ? snd : fst, v1); |
| checkCompletedExceptionally(failFirst ? fst : snd, ex); |
| }} |
| |
| /** |
| * thenAcceptBoth result completes exceptionally if either source cancelled |
| */ |
| public void testThenAcceptBoth_sourceCancelled() throws Throwable { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (boolean failFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final SubtractAction r1 = new SubtractAction(m); |
| final SubtractAction r2 = new SubtractAction(m); |
| final SubtractAction r3 = new SubtractAction(m); |
| |
| final CompletableFuture<Integer> fst = fFirst ? f : g; |
| final CompletableFuture<Integer> snd = !fFirst ? f : g; |
| final Callable<Boolean> complete1 = failFirst ? |
| () -> fst.cancel(mayInterruptIfRunning) : |
| () -> fst.complete(v1); |
| final Callable<Boolean> complete2 = failFirst ? |
| () -> snd.complete(v1) : |
| () -> snd.cancel(mayInterruptIfRunning); |
| |
| final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1); |
| assertTrue(complete1.call()); |
| final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2); |
| checkIncomplete(h1); |
| checkIncomplete(h2); |
| assertTrue(complete2.call()); |
| final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3); |
| |
| checkCompletedWithWrappedCancellationException(h1); |
| checkCompletedWithWrappedCancellationException(h2); |
| checkCompletedWithWrappedCancellationException(h3); |
| r1.assertNotInvoked(); |
| r2.assertNotInvoked(); |
| r3.assertNotInvoked(); |
| checkCompletedNormally(failFirst ? snd : fst, v1); |
| checkCancelled(failFirst ? fst : snd); |
| }} |
| |
| /** |
| * thenAcceptBoth result completes exceptionally if action does |
| */ |
| public void testThenAcceptBoth_actionFailed() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| for (Integer v2 : new Integer[] { 2, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final FailingBiConsumer r1 = new FailingBiConsumer(m); |
| final FailingBiConsumer r2 = new FailingBiConsumer(m); |
| final FailingBiConsumer r3 = new FailingBiConsumer(m); |
| |
| final CompletableFuture<Integer> fst = fFirst ? f : g; |
| final CompletableFuture<Integer> snd = !fFirst ? f : g; |
| final Integer w1 = fFirst ? v1 : v2; |
| final Integer w2 = !fFirst ? v1 : v2; |
| |
| final CompletableFuture<Void> h1 = m.thenAcceptBoth(f, g, r1); |
| assertTrue(fst.complete(w1)); |
| final CompletableFuture<Void> h2 = m.thenAcceptBoth(f, g, r2); |
| assertTrue(snd.complete(w2)); |
| final CompletableFuture<Void> h3 = m.thenAcceptBoth(f, g, r3); |
| |
| checkCompletedWithWrappedCFException(h1); |
| checkCompletedWithWrappedCFException(h2); |
| checkCompletedWithWrappedCFException(h3); |
| r1.assertInvoked(); |
| r2.assertInvoked(); |
| r3.assertInvoked(); |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v2); |
| }} |
| |
| /** |
| * runAfterBoth result completes normally after normal |
| * completion of sources |
| */ |
| public void testRunAfterBoth_normalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| for (Integer v2 : new Integer[] { 2, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final Noop r1 = new Noop(m); |
| final Noop r2 = new Noop(m); |
| final Noop r3 = new Noop(m); |
| |
| final CompletableFuture<Integer> fst = fFirst ? f : g; |
| final CompletableFuture<Integer> snd = !fFirst ? f : g; |
| final Integer w1 = fFirst ? v1 : v2; |
| final Integer w2 = !fFirst ? v1 : v2; |
| |
| final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1); |
| assertTrue(fst.complete(w1)); |
| final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2); |
| checkIncomplete(h1); |
| checkIncomplete(h2); |
| r1.assertNotInvoked(); |
| r2.assertNotInvoked(); |
| assertTrue(snd.complete(w2)); |
| final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3); |
| |
| checkCompletedNormally(h1, null); |
| checkCompletedNormally(h2, null); |
| checkCompletedNormally(h3, null); |
| r1.assertInvoked(); |
| r2.assertInvoked(); |
| r3.assertInvoked(); |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v2); |
| }} |
| |
| /** |
| * runAfterBoth result completes exceptionally after exceptional |
| * completion of either source |
| */ |
| public void testRunAfterBoth_exceptionalCompletion() throws Throwable { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (boolean failFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final CFException ex = new CFException(); |
| final Noop r1 = new Noop(m); |
| final Noop r2 = new Noop(m); |
| final Noop r3 = new Noop(m); |
| |
| final CompletableFuture<Integer> fst = fFirst ? f : g; |
| final CompletableFuture<Integer> snd = !fFirst ? f : g; |
| final Callable<Boolean> complete1 = failFirst ? |
| () -> fst.completeExceptionally(ex) : |
| () -> fst.complete(v1); |
| final Callable<Boolean> complete2 = failFirst ? |
| () -> snd.complete(v1) : |
| () -> snd.completeExceptionally(ex); |
| |
| final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1); |
| assertTrue(complete1.call()); |
| final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2); |
| checkIncomplete(h1); |
| checkIncomplete(h2); |
| assertTrue(complete2.call()); |
| final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3); |
| |
| checkCompletedWithWrappedException(h1, ex); |
| checkCompletedWithWrappedException(h2, ex); |
| checkCompletedWithWrappedException(h3, ex); |
| r1.assertNotInvoked(); |
| r2.assertNotInvoked(); |
| r3.assertNotInvoked(); |
| checkCompletedNormally(failFirst ? snd : fst, v1); |
| checkCompletedExceptionally(failFirst ? fst : snd, ex); |
| }} |
| |
| /** |
| * runAfterBoth result completes exceptionally if either source cancelled |
| */ |
| public void testRunAfterBoth_sourceCancelled() throws Throwable { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (boolean failFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final Noop r1 = new Noop(m); |
| final Noop r2 = new Noop(m); |
| final Noop r3 = new Noop(m); |
| |
| final CompletableFuture<Integer> fst = fFirst ? f : g; |
| final CompletableFuture<Integer> snd = !fFirst ? f : g; |
| final Callable<Boolean> complete1 = failFirst ? |
| () -> fst.cancel(mayInterruptIfRunning) : |
| () -> fst.complete(v1); |
| final Callable<Boolean> complete2 = failFirst ? |
| () -> snd.complete(v1) : |
| () -> snd.cancel(mayInterruptIfRunning); |
| |
| final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1); |
| assertTrue(complete1.call()); |
| final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2); |
| checkIncomplete(h1); |
| checkIncomplete(h2); |
| assertTrue(complete2.call()); |
| final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3); |
| |
| checkCompletedWithWrappedCancellationException(h1); |
| checkCompletedWithWrappedCancellationException(h2); |
| checkCompletedWithWrappedCancellationException(h3); |
| r1.assertNotInvoked(); |
| r2.assertNotInvoked(); |
| r3.assertNotInvoked(); |
| checkCompletedNormally(failFirst ? snd : fst, v1); |
| checkCancelled(failFirst ? fst : snd); |
| }} |
| |
| /** |
| * runAfterBoth result completes exceptionally if action does |
| */ |
| public void testRunAfterBoth_actionFailed() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| for (Integer v2 : new Integer[] { 2, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final FailingRunnable r1 = new FailingRunnable(m); |
| final FailingRunnable r2 = new FailingRunnable(m); |
| final FailingRunnable r3 = new FailingRunnable(m); |
| |
| final CompletableFuture<Integer> fst = fFirst ? f : g; |
| final CompletableFuture<Integer> snd = !fFirst ? f : g; |
| final Integer w1 = fFirst ? v1 : v2; |
| final Integer w2 = !fFirst ? v1 : v2; |
| |
| final CompletableFuture<Void> h1 = m.runAfterBoth(f, g, r1); |
| assertTrue(fst.complete(w1)); |
| final CompletableFuture<Void> h2 = m.runAfterBoth(f, g, r2); |
| assertTrue(snd.complete(w2)); |
| final CompletableFuture<Void> h3 = m.runAfterBoth(f, g, r3); |
| |
| checkCompletedWithWrappedCFException(h1); |
| checkCompletedWithWrappedCFException(h2); |
| checkCompletedWithWrappedCFException(h3); |
| r1.assertInvoked(); |
| r2.assertInvoked(); |
| r3.assertInvoked(); |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v2); |
| }} |
| |
| /** |
| * applyToEither result completes normally after normal completion |
| * of either source |
| */ |
| public void testApplyToEither_normalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| for (Integer v2 : new Integer[] { 2, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final IncFunction[] rs = new IncFunction[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); |
| |
| final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]); |
| final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| rs[0].assertNotInvoked(); |
| rs[1].assertNotInvoked(); |
| f.complete(v1); |
| checkCompletedNormally(h0, inc(v1)); |
| checkCompletedNormally(h1, inc(v1)); |
| final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]); |
| final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]); |
| checkCompletedNormally(h2, inc(v1)); |
| checkCompletedNormally(h3, inc(v1)); |
| g.complete(v2); |
| |
| // unspecified behavior - both source completions available |
| final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]); |
| final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]); |
| rs[4].assertValue(h4.join()); |
| rs[5].assertValue(h5.join()); |
| assertTrue(Objects.equals(inc(v1), h4.join()) || |
| Objects.equals(inc(v2), h4.join())); |
| assertTrue(Objects.equals(inc(v1), h5.join()) || |
| Objects.equals(inc(v2), h5.join())); |
| |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v2); |
| checkCompletedNormally(h0, inc(v1)); |
| checkCompletedNormally(h1, inc(v1)); |
| checkCompletedNormally(h2, inc(v1)); |
| checkCompletedNormally(h3, inc(v1)); |
| for (int i = 0; i < 4; i++) rs[i].assertValue(inc(v1)); |
| }} |
| |
| /** |
| * applyToEither result completes exceptionally after exceptional |
| * completion of either source |
| */ |
| public void testApplyToEither_exceptionalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final CFException ex = new CFException(); |
| final IncFunction[] rs = new IncFunction[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); |
| |
| final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]); |
| final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| rs[0].assertNotInvoked(); |
| rs[1].assertNotInvoked(); |
| f.completeExceptionally(ex); |
| checkCompletedWithWrappedException(h0, ex); |
| checkCompletedWithWrappedException(h1, ex); |
| final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]); |
| final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]); |
| checkCompletedWithWrappedException(h2, ex); |
| checkCompletedWithWrappedException(h3, ex); |
| g.complete(v1); |
| |
| // unspecified behavior - both source completions available |
| final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]); |
| final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]); |
| try { |
| assertEquals(inc(v1), h4.join()); |
| rs[4].assertValue(inc(v1)); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h4, ex); |
| rs[4].assertNotInvoked(); |
| } |
| try { |
| assertEquals(inc(v1), h5.join()); |
| rs[5].assertValue(inc(v1)); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h5, ex); |
| rs[5].assertNotInvoked(); |
| } |
| |
| checkCompletedExceptionally(f, ex); |
| checkCompletedNormally(g, v1); |
| checkCompletedWithWrappedException(h0, ex); |
| checkCompletedWithWrappedException(h1, ex); |
| checkCompletedWithWrappedException(h2, ex); |
| checkCompletedWithWrappedException(h3, ex); |
| checkCompletedWithWrappedException(h4, ex); |
| for (int i = 0; i < 4; i++) rs[i].assertNotInvoked(); |
| }} |
| |
| public void testApplyToEither_exceptionalCompletion2() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final CFException ex = new CFException(); |
| final IncFunction[] rs = new IncFunction[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); |
| |
| final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]); |
| final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]); |
| assertTrue(fFirst ? f.complete(v1) : g.completeExceptionally(ex)); |
| assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex)); |
| final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]); |
| final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]); |
| |
| // unspecified behavior - both source completions available |
| try { |
| assertEquals(inc(v1), h0.join()); |
| rs[0].assertValue(inc(v1)); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h0, ex); |
| rs[0].assertNotInvoked(); |
| } |
| try { |
| assertEquals(inc(v1), h1.join()); |
| rs[1].assertValue(inc(v1)); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h1, ex); |
| rs[1].assertNotInvoked(); |
| } |
| try { |
| assertEquals(inc(v1), h2.join()); |
| rs[2].assertValue(inc(v1)); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h2, ex); |
| rs[2].assertNotInvoked(); |
| } |
| try { |
| assertEquals(inc(v1), h3.join()); |
| rs[3].assertValue(inc(v1)); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h3, ex); |
| rs[3].assertNotInvoked(); |
| } |
| |
| checkCompletedNormally(f, v1); |
| checkCompletedExceptionally(g, ex); |
| }} |
| |
| /** |
| * applyToEither result completes exceptionally if either source cancelled |
| */ |
| public void testApplyToEither_sourceCancelled() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final IncFunction[] rs = new IncFunction[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); |
| |
| final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]); |
| final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| rs[0].assertNotInvoked(); |
| rs[1].assertNotInvoked(); |
| f.cancel(mayInterruptIfRunning); |
| checkCompletedWithWrappedCancellationException(h0); |
| checkCompletedWithWrappedCancellationException(h1); |
| final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]); |
| final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]); |
| checkCompletedWithWrappedCancellationException(h2); |
| checkCompletedWithWrappedCancellationException(h3); |
| g.complete(v1); |
| |
| // unspecified behavior - both source completions available |
| final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]); |
| final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]); |
| try { |
| assertEquals(inc(v1), h4.join()); |
| rs[4].assertValue(inc(v1)); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedCancellationException(h4); |
| rs[4].assertNotInvoked(); |
| } |
| try { |
| assertEquals(inc(v1), h5.join()); |
| rs[5].assertValue(inc(v1)); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedCancellationException(h5); |
| rs[5].assertNotInvoked(); |
| } |
| |
| checkCancelled(f); |
| checkCompletedNormally(g, v1); |
| checkCompletedWithWrappedCancellationException(h0); |
| checkCompletedWithWrappedCancellationException(h1); |
| checkCompletedWithWrappedCancellationException(h2); |
| checkCompletedWithWrappedCancellationException(h3); |
| for (int i = 0; i < 4; i++) rs[i].assertNotInvoked(); |
| }} |
| |
| public void testApplyToEither_sourceCancelled2() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final IncFunction[] rs = new IncFunction[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new IncFunction(m); |
| |
| final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]); |
| final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]); |
| assertTrue(fFirst ? f.complete(v1) : g.cancel(mayInterruptIfRunning)); |
| assertTrue(!fFirst ? f.complete(v1) : g.cancel(mayInterruptIfRunning)); |
| final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]); |
| final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]); |
| |
| // unspecified behavior - both source completions available |
| try { |
| assertEquals(inc(v1), h0.join()); |
| rs[0].assertValue(inc(v1)); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedCancellationException(h0); |
| rs[0].assertNotInvoked(); |
| } |
| try { |
| assertEquals(inc(v1), h1.join()); |
| rs[1].assertValue(inc(v1)); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedCancellationException(h1); |
| rs[1].assertNotInvoked(); |
| } |
| try { |
| assertEquals(inc(v1), h2.join()); |
| rs[2].assertValue(inc(v1)); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedCancellationException(h2); |
| rs[2].assertNotInvoked(); |
| } |
| try { |
| assertEquals(inc(v1), h3.join()); |
| rs[3].assertValue(inc(v1)); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedCancellationException(h3); |
| rs[3].assertNotInvoked(); |
| } |
| |
| checkCompletedNormally(f, v1); |
| checkCancelled(g); |
| }} |
| |
| /** |
| * applyToEither result completes exceptionally if action does |
| */ |
| public void testApplyToEither_actionFailed() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| for (Integer v2 : new Integer[] { 2, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final FailingFunction[] rs = new FailingFunction[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new FailingFunction(m); |
| |
| final CompletableFuture<Integer> h0 = m.applyToEither(f, g, rs[0]); |
| final CompletableFuture<Integer> h1 = m.applyToEither(g, f, rs[1]); |
| f.complete(v1); |
| final CompletableFuture<Integer> h2 = m.applyToEither(f, g, rs[2]); |
| final CompletableFuture<Integer> h3 = m.applyToEither(g, f, rs[3]); |
| checkCompletedWithWrappedCFException(h0); |
| checkCompletedWithWrappedCFException(h1); |
| checkCompletedWithWrappedCFException(h2); |
| checkCompletedWithWrappedCFException(h3); |
| for (int i = 0; i < 4; i++) rs[i].assertValue(v1); |
| |
| g.complete(v2); |
| |
| // unspecified behavior - both source completions available |
| final CompletableFuture<Integer> h4 = m.applyToEither(f, g, rs[4]); |
| final CompletableFuture<Integer> h5 = m.applyToEither(g, f, rs[5]); |
| |
| checkCompletedWithWrappedCFException(h4); |
| assertTrue(Objects.equals(v1, rs[4].value) || |
| Objects.equals(v2, rs[4].value)); |
| checkCompletedWithWrappedCFException(h5); |
| assertTrue(Objects.equals(v1, rs[5].value) || |
| Objects.equals(v2, rs[5].value)); |
| |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v2); |
| }} |
| |
| /** |
| * acceptEither result completes normally after normal completion |
| * of either source |
| */ |
| public void testAcceptEither_normalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| for (Integer v2 : new Integer[] { 2, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final NoopConsumer[] rs = new NoopConsumer[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); |
| |
| final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]); |
| final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| rs[0].assertNotInvoked(); |
| rs[1].assertNotInvoked(); |
| f.complete(v1); |
| checkCompletedNormally(h0, null); |
| checkCompletedNormally(h1, null); |
| rs[0].assertValue(v1); |
| rs[1].assertValue(v1); |
| final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]); |
| final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]); |
| checkCompletedNormally(h2, null); |
| checkCompletedNormally(h3, null); |
| rs[2].assertValue(v1); |
| rs[3].assertValue(v1); |
| g.complete(v2); |
| |
| // unspecified behavior - both source completions available |
| final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]); |
| final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]); |
| checkCompletedNormally(h4, null); |
| checkCompletedNormally(h5, null); |
| assertTrue(Objects.equals(v1, rs[4].value) || |
| Objects.equals(v2, rs[4].value)); |
| assertTrue(Objects.equals(v1, rs[5].value) || |
| Objects.equals(v2, rs[5].value)); |
| |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v2); |
| checkCompletedNormally(h0, null); |
| checkCompletedNormally(h1, null); |
| checkCompletedNormally(h2, null); |
| checkCompletedNormally(h3, null); |
| for (int i = 0; i < 4; i++) rs[i].assertValue(v1); |
| }} |
| |
| /** |
| * acceptEither result completes exceptionally after exceptional |
| * completion of either source |
| */ |
| public void testAcceptEither_exceptionalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final CFException ex = new CFException(); |
| final NoopConsumer[] rs = new NoopConsumer[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); |
| |
| final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]); |
| final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| rs[0].assertNotInvoked(); |
| rs[1].assertNotInvoked(); |
| f.completeExceptionally(ex); |
| checkCompletedWithWrappedException(h0, ex); |
| checkCompletedWithWrappedException(h1, ex); |
| final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]); |
| final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]); |
| checkCompletedWithWrappedException(h2, ex); |
| checkCompletedWithWrappedException(h3, ex); |
| |
| g.complete(v1); |
| |
| // unspecified behavior - both source completions available |
| final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]); |
| final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]); |
| try { |
| assertNull(h4.join()); |
| rs[4].assertValue(v1); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h4, ex); |
| rs[4].assertNotInvoked(); |
| } |
| try { |
| assertNull(h5.join()); |
| rs[5].assertValue(v1); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h5, ex); |
| rs[5].assertNotInvoked(); |
| } |
| |
| checkCompletedExceptionally(f, ex); |
| checkCompletedNormally(g, v1); |
| checkCompletedWithWrappedException(h0, ex); |
| checkCompletedWithWrappedException(h1, ex); |
| checkCompletedWithWrappedException(h2, ex); |
| checkCompletedWithWrappedException(h3, ex); |
| checkCompletedWithWrappedException(h4, ex); |
| for (int i = 0; i < 4; i++) rs[i].assertNotInvoked(); |
| }} |
| |
| public void testAcceptEither_exceptionalCompletion2() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final CFException ex = new CFException(); |
| final NoopConsumer[] rs = new NoopConsumer[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); |
| |
| final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]); |
| final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]); |
| assertTrue(fFirst ? f.complete(v1) : g.completeExceptionally(ex)); |
| assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex)); |
| final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]); |
| final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]); |
| |
| // unspecified behavior - both source completions available |
| try { |
| assertEquals(null, h0.join()); |
| rs[0].assertValue(v1); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h0, ex); |
| rs[0].assertNotInvoked(); |
| } |
| try { |
| assertEquals(null, h1.join()); |
| rs[1].assertValue(v1); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h1, ex); |
| rs[1].assertNotInvoked(); |
| } |
| try { |
| assertEquals(null, h2.join()); |
| rs[2].assertValue(v1); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h2, ex); |
| rs[2].assertNotInvoked(); |
| } |
| try { |
| assertEquals(null, h3.join()); |
| rs[3].assertValue(v1); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h3, ex); |
| rs[3].assertNotInvoked(); |
| } |
| |
| checkCompletedNormally(f, v1); |
| checkCompletedExceptionally(g, ex); |
| }} |
| |
| /** |
| * acceptEither result completes exceptionally if either source cancelled |
| */ |
| public void testAcceptEither_sourceCancelled() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final NoopConsumer[] rs = new NoopConsumer[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new NoopConsumer(m); |
| |
| final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]); |
| final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| rs[0].assertNotInvoked(); |
| rs[1].assertNotInvoked(); |
| f.cancel(mayInterruptIfRunning); |
| checkCompletedWithWrappedCancellationException(h0); |
| checkCompletedWithWrappedCancellationException(h1); |
| final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]); |
| final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]); |
| checkCompletedWithWrappedCancellationException(h2); |
| checkCompletedWithWrappedCancellationException(h3); |
| |
| g.complete(v1); |
| |
| // unspecified behavior - both source completions available |
| final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]); |
| final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]); |
| try { |
| assertNull(h4.join()); |
| rs[4].assertValue(v1); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedCancellationException(h4); |
| rs[4].assertNotInvoked(); |
| } |
| try { |
| assertNull(h5.join()); |
| rs[5].assertValue(v1); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedCancellationException(h5); |
| rs[5].assertNotInvoked(); |
| } |
| |
| checkCancelled(f); |
| checkCompletedNormally(g, v1); |
| checkCompletedWithWrappedCancellationException(h0); |
| checkCompletedWithWrappedCancellationException(h1); |
| checkCompletedWithWrappedCancellationException(h2); |
| checkCompletedWithWrappedCancellationException(h3); |
| for (int i = 0; i < 4; i++) rs[i].assertNotInvoked(); |
| }} |
| |
| /** |
| * acceptEither result completes exceptionally if action does |
| */ |
| public void testAcceptEither_actionFailed() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| for (Integer v2 : new Integer[] { 2, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final FailingConsumer[] rs = new FailingConsumer[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new FailingConsumer(m); |
| |
| final CompletableFuture<Void> h0 = m.acceptEither(f, g, rs[0]); |
| final CompletableFuture<Void> h1 = m.acceptEither(g, f, rs[1]); |
| f.complete(v1); |
| final CompletableFuture<Void> h2 = m.acceptEither(f, g, rs[2]); |
| final CompletableFuture<Void> h3 = m.acceptEither(g, f, rs[3]); |
| checkCompletedWithWrappedCFException(h0); |
| checkCompletedWithWrappedCFException(h1); |
| checkCompletedWithWrappedCFException(h2); |
| checkCompletedWithWrappedCFException(h3); |
| for (int i = 0; i < 4; i++) rs[i].assertValue(v1); |
| |
| g.complete(v2); |
| |
| // unspecified behavior - both source completions available |
| final CompletableFuture<Void> h4 = m.acceptEither(f, g, rs[4]); |
| final CompletableFuture<Void> h5 = m.acceptEither(g, f, rs[5]); |
| |
| checkCompletedWithWrappedCFException(h4); |
| assertTrue(Objects.equals(v1, rs[4].value) || |
| Objects.equals(v2, rs[4].value)); |
| checkCompletedWithWrappedCFException(h5); |
| assertTrue(Objects.equals(v1, rs[5].value) || |
| Objects.equals(v2, rs[5].value)); |
| |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v2); |
| }} |
| |
| /** |
| * runAfterEither result completes normally after normal completion |
| * of either source |
| */ |
| public void testRunAfterEither_normalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| for (Integer v2 : new Integer[] { 2, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final Noop[] rs = new Noop[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); |
| |
| final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]); |
| final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| rs[0].assertNotInvoked(); |
| rs[1].assertNotInvoked(); |
| f.complete(v1); |
| checkCompletedNormally(h0, null); |
| checkCompletedNormally(h1, null); |
| rs[0].assertInvoked(); |
| rs[1].assertInvoked(); |
| final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]); |
| final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]); |
| checkCompletedNormally(h2, null); |
| checkCompletedNormally(h3, null); |
| rs[2].assertInvoked(); |
| rs[3].assertInvoked(); |
| |
| g.complete(v2); |
| |
| final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]); |
| final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]); |
| |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v2); |
| checkCompletedNormally(h0, null); |
| checkCompletedNormally(h1, null); |
| checkCompletedNormally(h2, null); |
| checkCompletedNormally(h3, null); |
| checkCompletedNormally(h4, null); |
| checkCompletedNormally(h5, null); |
| for (int i = 0; i < 6; i++) rs[i].assertInvoked(); |
| }} |
| |
| /** |
| * runAfterEither result completes exceptionally after exceptional |
| * completion of either source |
| */ |
| public void testRunAfterEither_exceptionalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final CFException ex = new CFException(); |
| final Noop[] rs = new Noop[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); |
| |
| final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]); |
| final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| rs[0].assertNotInvoked(); |
| rs[1].assertNotInvoked(); |
| assertTrue(f.completeExceptionally(ex)); |
| checkCompletedWithWrappedException(h0, ex); |
| checkCompletedWithWrappedException(h1, ex); |
| final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]); |
| final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]); |
| checkCompletedWithWrappedException(h2, ex); |
| checkCompletedWithWrappedException(h3, ex); |
| |
| assertTrue(g.complete(v1)); |
| |
| // unspecified behavior - both source completions available |
| final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]); |
| final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]); |
| try { |
| assertNull(h4.join()); |
| rs[4].assertInvoked(); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h4, ex); |
| rs[4].assertNotInvoked(); |
| } |
| try { |
| assertNull(h5.join()); |
| rs[5].assertInvoked(); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h5, ex); |
| rs[5].assertNotInvoked(); |
| } |
| |
| checkCompletedExceptionally(f, ex); |
| checkCompletedNormally(g, v1); |
| checkCompletedWithWrappedException(h0, ex); |
| checkCompletedWithWrappedException(h1, ex); |
| checkCompletedWithWrappedException(h2, ex); |
| checkCompletedWithWrappedException(h3, ex); |
| checkCompletedWithWrappedException(h4, ex); |
| for (int i = 0; i < 4; i++) rs[i].assertNotInvoked(); |
| }} |
| |
| public void testRunAfterEither_exceptionalCompletion2() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean fFirst : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final CFException ex = new CFException(); |
| final Noop[] rs = new Noop[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); |
| |
| final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]); |
| final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]); |
| assertTrue( fFirst ? f.complete(v1) : g.completeExceptionally(ex)); |
| assertTrue(!fFirst ? f.complete(v1) : g.completeExceptionally(ex)); |
| final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]); |
| final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]); |
| |
| // unspecified behavior - both source completions available |
| try { |
| assertEquals(null, h0.join()); |
| rs[0].assertInvoked(); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h0, ex); |
| rs[0].assertNotInvoked(); |
| } |
| try { |
| assertEquals(null, h1.join()); |
| rs[1].assertInvoked(); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h1, ex); |
| rs[1].assertNotInvoked(); |
| } |
| try { |
| assertEquals(null, h2.join()); |
| rs[2].assertInvoked(); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h2, ex); |
| rs[2].assertNotInvoked(); |
| } |
| try { |
| assertEquals(null, h3.join()); |
| rs[3].assertInvoked(); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedException(h3, ex); |
| rs[3].assertNotInvoked(); |
| } |
| |
| checkCompletedNormally(f, v1); |
| checkCompletedExceptionally(g, ex); |
| }} |
| |
| /** |
| * runAfterEither result completes exceptionally if either source cancelled |
| */ |
| public void testRunAfterEither_sourceCancelled() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final Noop[] rs = new Noop[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new Noop(m); |
| |
| final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]); |
| final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]); |
| checkIncomplete(h0); |
| checkIncomplete(h1); |
| rs[0].assertNotInvoked(); |
| rs[1].assertNotInvoked(); |
| f.cancel(mayInterruptIfRunning); |
| checkCompletedWithWrappedCancellationException(h0); |
| checkCompletedWithWrappedCancellationException(h1); |
| final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]); |
| final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]); |
| checkCompletedWithWrappedCancellationException(h2); |
| checkCompletedWithWrappedCancellationException(h3); |
| |
| assertTrue(g.complete(v1)); |
| |
| // unspecified behavior - both source completions available |
| final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]); |
| final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]); |
| try { |
| assertNull(h4.join()); |
| rs[4].assertInvoked(); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedCancellationException(h4); |
| rs[4].assertNotInvoked(); |
| } |
| try { |
| assertNull(h5.join()); |
| rs[5].assertInvoked(); |
| } catch (CompletionException ok) { |
| checkCompletedWithWrappedCancellationException(h5); |
| rs[5].assertNotInvoked(); |
| } |
| |
| checkCancelled(f); |
| checkCompletedNormally(g, v1); |
| checkCompletedWithWrappedCancellationException(h0); |
| checkCompletedWithWrappedCancellationException(h1); |
| checkCompletedWithWrappedCancellationException(h2); |
| checkCompletedWithWrappedCancellationException(h3); |
| for (int i = 0; i < 4; i++) rs[i].assertNotInvoked(); |
| }} |
| |
| /** |
| * runAfterEither result completes exceptionally if action does |
| */ |
| public void testRunAfterEither_actionFailed() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (Integer v1 : new Integer[] { 1, null }) |
| for (Integer v2 : new Integer[] { 2, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final FailingRunnable[] rs = new FailingRunnable[6]; |
| for (int i = 0; i < rs.length; i++) rs[i] = new FailingRunnable(m); |
| |
| final CompletableFuture<Void> h0 = m.runAfterEither(f, g, rs[0]); |
| final CompletableFuture<Void> h1 = m.runAfterEither(g, f, rs[1]); |
| assertTrue(f.complete(v1)); |
| final CompletableFuture<Void> h2 = m.runAfterEither(f, g, rs[2]); |
| final CompletableFuture<Void> h3 = m.runAfterEither(g, f, rs[3]); |
| checkCompletedWithWrappedCFException(h0); |
| checkCompletedWithWrappedCFException(h1); |
| checkCompletedWithWrappedCFException(h2); |
| checkCompletedWithWrappedCFException(h3); |
| for (int i = 0; i < 4; i++) rs[i].assertInvoked(); |
| assertTrue(g.complete(v2)); |
| final CompletableFuture<Void> h4 = m.runAfterEither(f, g, rs[4]); |
| final CompletableFuture<Void> h5 = m.runAfterEither(g, f, rs[5]); |
| checkCompletedWithWrappedCFException(h4); |
| checkCompletedWithWrappedCFException(h5); |
| |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v2); |
| for (int i = 0; i < 6; i++) rs[i].assertInvoked(); |
| }} |
| |
| /** |
| * thenCompose result completes normally after normal completion of source |
| */ |
| public void testThenCompose_normalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFutureInc r = new CompletableFutureInc(m); |
| if (!createIncomplete) assertTrue(f.complete(v1)); |
| final CompletableFuture<Integer> g = m.thenCompose(f, r); |
| if (createIncomplete) assertTrue(f.complete(v1)); |
| |
| checkCompletedNormally(g, inc(v1)); |
| checkCompletedNormally(f, v1); |
| r.assertValue(v1); |
| }} |
| |
| /** |
| * thenCompose result completes exceptionally after exceptional |
| * completion of source |
| */ |
| public void testThenCompose_exceptionalCompletion() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| { |
| final CFException ex = new CFException(); |
| final CompletableFutureInc r = new CompletableFutureInc(m); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| if (!createIncomplete) f.completeExceptionally(ex); |
| final CompletableFuture<Integer> g = m.thenCompose(f, r); |
| if (createIncomplete) f.completeExceptionally(ex); |
| |
| checkCompletedWithWrappedException(g, ex); |
| checkCompletedExceptionally(f, ex); |
| r.assertNotInvoked(); |
| }} |
| |
| /** |
| * thenCompose result completes exceptionally if action does |
| */ |
| public void testThenCompose_actionFailed() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final FailingCompletableFutureFunction r |
| = new FailingCompletableFutureFunction(m); |
| if (!createIncomplete) assertTrue(f.complete(v1)); |
| final CompletableFuture<Integer> g = m.thenCompose(f, r); |
| if (createIncomplete) assertTrue(f.complete(v1)); |
| |
| checkCompletedWithWrappedCFException(g); |
| checkCompletedNormally(f, v1); |
| }} |
| |
| /** |
| * thenCompose result completes exceptionally if source cancelled |
| */ |
| public void testThenCompose_sourceCancelled() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (boolean createIncomplete : new boolean[] { true, false }) |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| { |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFutureInc r = new CompletableFutureInc(m); |
| if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning)); |
| final CompletableFuture<Integer> g = m.thenCompose(f, r); |
| if (createIncomplete) { |
| checkIncomplete(g); |
| assertTrue(f.cancel(mayInterruptIfRunning)); |
| } |
| |
| checkCompletedWithWrappedCancellationException(g); |
| checkCancelled(f); |
| }} |
| |
| /** |
| * thenCompose result completes exceptionally if the result of the action does |
| */ |
| public void testThenCompose_actionReturnsFailingFuture() { |
| for (ExecutionMode m : ExecutionMode.values()) |
| for (int order = 0; order < 6; order++) |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| final CFException ex = new CFException(); |
| final CompletableFuture<Integer> f = new CompletableFuture<>(); |
| final CompletableFuture<Integer> g = new CompletableFuture<>(); |
| final CompletableFuture<Integer> h; |
| // Test all permutations of orders |
| switch (order) { |
| case 0: |
| assertTrue(f.complete(v1)); |
| assertTrue(g.completeExceptionally(ex)); |
| h = m.thenCompose(f, (x -> g)); |
| break; |
| case 1: |
| assertTrue(f.complete(v1)); |
| h = m.thenCompose(f, (x -> g)); |
| assertTrue(g.completeExceptionally(ex)); |
| break; |
| case 2: |
| assertTrue(g.completeExceptionally(ex)); |
| assertTrue(f.complete(v1)); |
| h = m.thenCompose(f, (x -> g)); |
| break; |
| case 3: |
| assertTrue(g.completeExceptionally(ex)); |
| h = m.thenCompose(f, (x -> g)); |
| assertTrue(f.complete(v1)); |
| break; |
| case 4: |
| h = m.thenCompose(f, (x -> g)); |
| assertTrue(f.complete(v1)); |
| assertTrue(g.completeExceptionally(ex)); |
| break; |
| case 5: |
| h = m.thenCompose(f, (x -> g)); |
| assertTrue(f.complete(v1)); |
| assertTrue(g.completeExceptionally(ex)); |
| break; |
| default: throw new AssertionError(); |
| } |
| |
| checkCompletedExceptionally(g, ex); |
| checkCompletedWithWrappedException(h, ex); |
| checkCompletedNormally(f, v1); |
| }} |
| |
| // other static methods |
| |
| /** |
| * allOf(no component futures) returns a future completed normally |
| * with the value null |
| */ |
| public void testAllOf_empty() throws Exception { |
| CompletableFuture<Void> f = CompletableFuture.allOf(); |
| checkCompletedNormally(f, null); |
| } |
| |
| /** |
| * allOf returns a future completed normally with the value null |
| * when all components complete normally |
| */ |
| public void testAllOf_normal() throws Exception { |
| for (int k = 1; k < 10; k++) { |
| CompletableFuture<Integer>[] fs |
| = (CompletableFuture<Integer>[]) new CompletableFuture[k]; |
| for (int i = 0; i < k; i++) |
| fs[i] = new CompletableFuture<>(); |
| CompletableFuture<Void> f = CompletableFuture.allOf(fs); |
| for (int i = 0; i < k; i++) { |
| checkIncomplete(f); |
| checkIncomplete(CompletableFuture.allOf(fs)); |
| fs[i].complete(one); |
| } |
| checkCompletedNormally(f, null); |
| checkCompletedNormally(CompletableFuture.allOf(fs), null); |
| } |
| } |
| |
| public void testAllOf_backwards() throws Exception { |
| for (int k = 1; k < 10; k++) { |
| CompletableFuture<Integer>[] fs |
| = (CompletableFuture<Integer>[]) new CompletableFuture[k]; |
| for (int i = 0; i < k; i++) |
| fs[i] = new CompletableFuture<>(); |
| CompletableFuture<Void> f = CompletableFuture.allOf(fs); |
| for (int i = k - 1; i >= 0; i--) { |
| checkIncomplete(f); |
| checkIncomplete(CompletableFuture.allOf(fs)); |
| fs[i].complete(one); |
| } |
| checkCompletedNormally(f, null); |
| checkCompletedNormally(CompletableFuture.allOf(fs), null); |
| } |
| } |
| |
| public void testAllOf_exceptional() throws Exception { |
| for (int k = 1; k < 10; k++) { |
| CompletableFuture<Integer>[] fs |
| = (CompletableFuture<Integer>[]) new CompletableFuture[k]; |
| CFException ex = new CFException(); |
| for (int i = 0; i < k; i++) |
| fs[i] = new CompletableFuture<>(); |
| CompletableFuture<Void> f = CompletableFuture.allOf(fs); |
| for (int i = 0; i < k; i++) { |
| checkIncomplete(f); |
| checkIncomplete(CompletableFuture.allOf(fs)); |
| if (i != k / 2) { |
| fs[i].complete(i); |
| checkCompletedNormally(fs[i], i); |
| } else { |
| fs[i].completeExceptionally(ex); |
| checkCompletedExceptionally(fs[i], ex); |
| } |
| } |
| checkCompletedWithWrappedException(f, ex); |
| checkCompletedWithWrappedException(CompletableFuture.allOf(fs), ex); |
| } |
| } |
| |
| /** |
| * anyOf(no component futures) returns an incomplete future |
| */ |
| public void testAnyOf_empty() throws Exception { |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| CompletableFuture<Object> f = CompletableFuture.anyOf(); |
| checkIncomplete(f); |
| |
| f.complete(v1); |
| checkCompletedNormally(f, v1); |
| }} |
| |
| /** |
| * anyOf returns a future completed normally with a value when |
| * a component future does |
| */ |
| public void testAnyOf_normal() throws Exception { |
| for (int k = 0; k < 10; k++) { |
| CompletableFuture[] fs = new CompletableFuture[k]; |
| for (int i = 0; i < k; i++) |
| fs[i] = new CompletableFuture<>(); |
| CompletableFuture<Object> f = CompletableFuture.anyOf(fs); |
| checkIncomplete(f); |
| for (int i = 0; i < k; i++) { |
| fs[i].complete(i); |
| checkCompletedNormally(f, 0); |
| int x = (int) CompletableFuture.anyOf(fs).join(); |
| assertTrue(0 <= x && x <= i); |
| } |
| } |
| } |
| public void testAnyOf_normal_backwards() throws Exception { |
| for (int k = 0; k < 10; k++) { |
| CompletableFuture[] fs = new CompletableFuture[k]; |
| for (int i = 0; i < k; i++) |
| fs[i] = new CompletableFuture<>(); |
| CompletableFuture<Object> f = CompletableFuture.anyOf(fs); |
| checkIncomplete(f); |
| for (int i = k - 1; i >= 0; i--) { |
| fs[i].complete(i); |
| checkCompletedNormally(f, k - 1); |
| int x = (int) CompletableFuture.anyOf(fs).join(); |
| assertTrue(i <= x && x <= k - 1); |
| } |
| } |
| } |
| |
| /** |
| * anyOf result completes exceptionally when any component does. |
| */ |
| public void testAnyOf_exceptional() throws Exception { |
| for (int k = 0; k < 10; k++) { |
| CompletableFuture[] fs = new CompletableFuture[k]; |
| CFException[] exs = new CFException[k]; |
| for (int i = 0; i < k; i++) { |
| fs[i] = new CompletableFuture<>(); |
| exs[i] = new CFException(); |
| } |
| CompletableFuture<Object> f = CompletableFuture.anyOf(fs); |
| checkIncomplete(f); |
| for (int i = 0; i < k; i++) { |
| fs[i].completeExceptionally(exs[i]); |
| checkCompletedWithWrappedException(f, exs[0]); |
| checkCompletedWithWrappedCFException(CompletableFuture.anyOf(fs)); |
| } |
| } |
| } |
| |
| public void testAnyOf_exceptional_backwards() throws Exception { |
| for (int k = 0; k < 10; k++) { |
| CompletableFuture[] fs = new CompletableFuture[k]; |
| CFException[] exs = new CFException[k]; |
| for (int i = 0; i < k; i++) { |
| fs[i] = new CompletableFuture<>(); |
| exs[i] = new CFException(); |
| } |
| CompletableFuture<Object> f = CompletableFuture.anyOf(fs); |
| checkIncomplete(f); |
| for (int i = k - 1; i >= 0; i--) { |
| fs[i].completeExceptionally(exs[i]); |
| checkCompletedWithWrappedException(f, exs[k - 1]); |
| checkCompletedWithWrappedCFException(CompletableFuture.anyOf(fs)); |
| } |
| } |
| } |
| |
| /** |
| * Completion methods throw NullPointerException with null arguments |
| */ |
| public void testNPE() { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| CompletableFuture<Integer> g = new CompletableFuture<>(); |
| CompletableFuture<Integer> nullFuture = (CompletableFuture<Integer>)null; |
| ThreadExecutor exec = new ThreadExecutor(); |
| |
| Runnable[] throwingActions = { |
| () -> CompletableFuture.supplyAsync(null), |
| () -> CompletableFuture.supplyAsync(null, exec), |
| () -> CompletableFuture.supplyAsync(new IntegerSupplier(ExecutionMode.SYNC, 42), null), |
| |
| () -> CompletableFuture.runAsync(null), |
| () -> CompletableFuture.runAsync(null, exec), |
| () -> CompletableFuture.runAsync(() -> {}, null), |
| |
| () -> f.completeExceptionally(null), |
| |
| () -> f.thenApply(null), |
| () -> f.thenApplyAsync(null), |
| () -> f.thenApplyAsync((x) -> x, null), |
| () -> f.thenApplyAsync(null, exec), |
| |
| () -> f.thenAccept(null), |
| () -> f.thenAcceptAsync(null), |
| () -> f.thenAcceptAsync((x) -> {} , null), |
| () -> f.thenAcceptAsync(null, exec), |
| |
| () -> f.thenRun(null), |
| () -> f.thenRunAsync(null), |
| () -> f.thenRunAsync(() -> {} , null), |
| () -> f.thenRunAsync(null, exec), |
| |
| () -> f.thenCombine(g, null), |
| () -> f.thenCombineAsync(g, null), |
| () -> f.thenCombineAsync(g, null, exec), |
| () -> f.thenCombine(nullFuture, (x, y) -> x), |
| () -> f.thenCombineAsync(nullFuture, (x, y) -> x), |
| () -> f.thenCombineAsync(nullFuture, (x, y) -> x, exec), |
| () -> f.thenCombineAsync(g, (x, y) -> x, null), |
| |
| () -> f.thenAcceptBoth(g, null), |
| () -> f.thenAcceptBothAsync(g, null), |
| () -> f.thenAcceptBothAsync(g, null, exec), |
| () -> f.thenAcceptBoth(nullFuture, (x, y) -> {}), |
| () -> f.thenAcceptBothAsync(nullFuture, (x, y) -> {}), |
| () -> f.thenAcceptBothAsync(nullFuture, (x, y) -> {}, exec), |
| () -> f.thenAcceptBothAsync(g, (x, y) -> {}, null), |
| |
| () -> f.runAfterBoth(g, null), |
| () -> f.runAfterBothAsync(g, null), |
| () -> f.runAfterBothAsync(g, null, exec), |
| () -> f.runAfterBoth(nullFuture, () -> {}), |
| () -> f.runAfterBothAsync(nullFuture, () -> {}), |
| () -> f.runAfterBothAsync(nullFuture, () -> {}, exec), |
| () -> f.runAfterBothAsync(g, () -> {}, null), |
| |
| () -> f.applyToEither(g, null), |
| () -> f.applyToEitherAsync(g, null), |
| () -> f.applyToEitherAsync(g, null, exec), |
| () -> f.applyToEither(nullFuture, (x) -> x), |
| () -> f.applyToEitherAsync(nullFuture, (x) -> x), |
| () -> f.applyToEitherAsync(nullFuture, (x) -> x, exec), |
| () -> f.applyToEitherAsync(g, (x) -> x, null), |
| |
| () -> f.acceptEither(g, null), |
| () -> f.acceptEitherAsync(g, null), |
| () -> f.acceptEitherAsync(g, null, exec), |
| () -> f.acceptEither(nullFuture, (x) -> {}), |
| () -> f.acceptEitherAsync(nullFuture, (x) -> {}), |
| () -> f.acceptEitherAsync(nullFuture, (x) -> {}, exec), |
| () -> f.acceptEitherAsync(g, (x) -> {}, null), |
| |
| () -> f.runAfterEither(g, null), |
| () -> f.runAfterEitherAsync(g, null), |
| () -> f.runAfterEitherAsync(g, null, exec), |
| () -> f.runAfterEither(nullFuture, () -> {}), |
| () -> f.runAfterEitherAsync(nullFuture, () -> {}), |
| () -> f.runAfterEitherAsync(nullFuture, () -> {}, exec), |
| () -> f.runAfterEitherAsync(g, () -> {}, null), |
| |
| () -> f.thenCompose(null), |
| () -> f.thenComposeAsync(null), |
| () -> f.thenComposeAsync(new CompletableFutureInc(ExecutionMode.EXECUTOR), null), |
| () -> f.thenComposeAsync(null, exec), |
| |
| () -> f.exceptionally(null), |
| |
| () -> f.handle(null), |
| |
| () -> CompletableFuture.allOf((CompletableFuture<?>)null), |
| () -> CompletableFuture.allOf((CompletableFuture<?>[])null), |
| () -> CompletableFuture.allOf(f, null), |
| () -> CompletableFuture.allOf(null, f), |
| |
| () -> CompletableFuture.anyOf((CompletableFuture<?>)null), |
| () -> CompletableFuture.anyOf((CompletableFuture<?>[])null), |
| () -> CompletableFuture.anyOf(f, null), |
| () -> CompletableFuture.anyOf(null, f), |
| |
| () -> f.obtrudeException(null), |
| |
| () -> CompletableFuture.delayedExecutor(1L, SECONDS, null), |
| () -> CompletableFuture.delayedExecutor(1L, null, new ThreadExecutor()), |
| () -> CompletableFuture.delayedExecutor(1L, null), |
| |
| () -> f.orTimeout(1L, null), |
| () -> f.completeOnTimeout(42, 1L, null), |
| |
| () -> CompletableFuture.failedFuture(null), |
| () -> CompletableFuture.failedStage(null), |
| }; |
| |
| assertThrows(NullPointerException.class, throwingActions); |
| assertEquals(0, exec.count.get()); |
| } |
| |
| /** |
| * toCompletableFuture returns this CompletableFuture. |
| */ |
| public void testToCompletableFuture() { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| assertSame(f, f.toCompletableFuture()); |
| } |
| |
| // jdk9 |
| |
| /** |
| * newIncompleteFuture returns an incomplete CompletableFuture |
| */ |
| public void testNewIncompleteFuture() { |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| CompletableFuture<Integer> g = f.newIncompleteFuture(); |
| checkIncomplete(f); |
| checkIncomplete(g); |
| f.complete(v1); |
| checkCompletedNormally(f, v1); |
| checkIncomplete(g); |
| g.complete(v1); |
| checkCompletedNormally(g, v1); |
| assertSame(g.getClass(), CompletableFuture.class); |
| }} |
| |
| /** |
| * completedStage returns a completed CompletionStage |
| */ |
| public void testCompletedStage() { |
| AtomicInteger x = new AtomicInteger(0); |
| AtomicReference<Throwable> r = new AtomicReference<Throwable>(); |
| CompletionStage<Integer> f = CompletableFuture.completedStage(1); |
| f.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);}); |
| assertEquals(x.get(), 1); |
| assertNull(r.get()); |
| } |
| |
| /** |
| * defaultExecutor by default returns the commonPool if |
| * it supports more than one thread. |
| */ |
| public void testDefaultExecutor() { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| Executor e = f.defaultExecutor(); |
| Executor c = ForkJoinPool.commonPool(); |
| if (ForkJoinPool.getCommonPoolParallelism() > 1) |
| assertSame(e, c); |
| else |
| assertNotSame(e, c); |
| } |
| |
| /** |
| * failedFuture returns a CompletableFuture completed |
| * exceptionally with the given Exception |
| */ |
| public void testFailedFuture() { |
| CFException ex = new CFException(); |
| CompletableFuture<Integer> f = CompletableFuture.failedFuture(ex); |
| checkCompletedExceptionally(f, ex); |
| } |
| |
| /** |
| * failedFuture(null) throws NPE |
| */ |
| public void testFailedFuture_null() { |
| try { |
| CompletableFuture<Integer> f = CompletableFuture.failedFuture(null); |
| shouldThrow(); |
| } catch (NullPointerException success) {} |
| } |
| |
| /** |
| * copy returns a CompletableFuture that is completed normally, |
| * with the same value, when source is. |
| */ |
| public void testCopy() { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| CompletableFuture<Integer> g = f.copy(); |
| checkIncomplete(f); |
| checkIncomplete(g); |
| f.complete(1); |
| checkCompletedNormally(f, 1); |
| checkCompletedNormally(g, 1); |
| } |
| |
| /** |
| * copy returns a CompletableFuture that is completed exceptionally |
| * when source is. |
| */ |
| public void testCopy2() { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| CompletableFuture<Integer> g = f.copy(); |
| checkIncomplete(f); |
| checkIncomplete(g); |
| CFException ex = new CFException(); |
| f.completeExceptionally(ex); |
| checkCompletedExceptionally(f, ex); |
| checkCompletedWithWrappedException(g, ex); |
| } |
| |
| /** |
| * minimalCompletionStage returns a CompletableFuture that is |
| * completed normally, with the same value, when source is. |
| */ |
| public void testMinimalCompletionStage() { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| CompletionStage<Integer> g = f.minimalCompletionStage(); |
| AtomicInteger x = new AtomicInteger(0); |
| AtomicReference<Throwable> r = new AtomicReference<Throwable>(); |
| checkIncomplete(f); |
| g.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);}); |
| f.complete(1); |
| checkCompletedNormally(f, 1); |
| assertEquals(x.get(), 1); |
| assertNull(r.get()); |
| } |
| |
| /** |
| * minimalCompletionStage returns a CompletableFuture that is |
| * completed exceptionally when source is. |
| */ |
| public void testMinimalCompletionStage2() { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| CompletionStage<Integer> g = f.minimalCompletionStage(); |
| AtomicInteger x = new AtomicInteger(0); |
| AtomicReference<Throwable> r = new AtomicReference<Throwable>(); |
| g.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);}); |
| checkIncomplete(f); |
| CFException ex = new CFException(); |
| f.completeExceptionally(ex); |
| checkCompletedExceptionally(f, ex); |
| assertEquals(x.get(), 0); |
| assertEquals(r.get().getCause(), ex); |
| } |
| |
| /** |
| * failedStage returns a CompletionStage completed |
| * exceptionally with the given Exception |
| */ |
| public void testFailedStage() { |
| CFException ex = new CFException(); |
| CompletionStage<Integer> f = CompletableFuture.failedStage(ex); |
| AtomicInteger x = new AtomicInteger(0); |
| AtomicReference<Throwable> r = new AtomicReference<Throwable>(); |
| f.whenComplete((v, e) -> {if (e != null) r.set(e); else x.set(v);}); |
| assertEquals(x.get(), 0); |
| assertEquals(r.get(), ex); |
| } |
| |
| /** |
| * completeAsync completes with value of given supplier |
| */ |
| public void testCompleteAsync() { |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| f.completeAsync(() -> v1); |
| f.join(); |
| checkCompletedNormally(f, v1); |
| }} |
| |
| /** |
| * completeAsync completes exceptionally if given supplier throws |
| */ |
| public void testCompleteAsync2() { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| CFException ex = new CFException(); |
| f.completeAsync(() -> {if (true) throw ex; return 1;}); |
| try { |
| f.join(); |
| shouldThrow(); |
| } catch (CompletionException success) {} |
| checkCompletedWithWrappedException(f, ex); |
| } |
| |
| /** |
| * completeAsync with given executor completes with value of given supplier |
| */ |
| public void testCompleteAsync3() { |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| ThreadExecutor executor = new ThreadExecutor(); |
| f.completeAsync(() -> v1, executor); |
| assertSame(v1, f.join()); |
| checkCompletedNormally(f, v1); |
| assertEquals(1, executor.count.get()); |
| }} |
| |
| /** |
| * completeAsync with given executor completes exceptionally if |
| * given supplier throws |
| */ |
| public void testCompleteAsync4() { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| CFException ex = new CFException(); |
| ThreadExecutor executor = new ThreadExecutor(); |
| f.completeAsync(() -> {if (true) throw ex; return 1;}, executor); |
| try { |
| f.join(); |
| shouldThrow(); |
| } catch (CompletionException success) {} |
| checkCompletedWithWrappedException(f, ex); |
| assertEquals(1, executor.count.get()); |
| } |
| |
| /** |
| * orTimeout completes with TimeoutException if not complete |
| */ |
| public void testOrTimeout_timesOut() { |
| long timeoutMillis = timeoutMillis(); |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| long startTime = System.nanoTime(); |
| f.orTimeout(timeoutMillis, MILLISECONDS); |
| checkCompletedWithTimeoutException(f); |
| assertTrue(millisElapsedSince(startTime) >= timeoutMillis); |
| } |
| |
| /** |
| * orTimeout completes normally if completed before timeout |
| */ |
| public void testOrTimeout_completed() { |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| CompletableFuture<Integer> g = new CompletableFuture<>(); |
| long startTime = System.nanoTime(); |
| f.complete(v1); |
| f.orTimeout(LONG_DELAY_MS, MILLISECONDS); |
| g.orTimeout(LONG_DELAY_MS, MILLISECONDS); |
| g.complete(v1); |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v1); |
| assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2); |
| }} |
| |
| /** |
| * completeOnTimeout completes with given value if not complete |
| */ |
| public void testCompleteOnTimeout_timesOut() { |
| testInParallel(() -> testCompleteOnTimeout_timesOut(42), |
| () -> testCompleteOnTimeout_timesOut(null)); |
| } |
| |
| public void testCompleteOnTimeout_timesOut(Integer v) { |
| long timeoutMillis = timeoutMillis(); |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| long startTime = System.nanoTime(); |
| f.completeOnTimeout(v, timeoutMillis, MILLISECONDS); |
| assertSame(v, f.join()); |
| assertTrue(millisElapsedSince(startTime) >= timeoutMillis); |
| f.complete(99); // should have no effect |
| checkCompletedNormally(f, v); |
| } |
| |
| /** |
| * completeOnTimeout has no effect if completed within timeout |
| */ |
| public void testCompleteOnTimeout_completed() { |
| for (Integer v1 : new Integer[] { 1, null }) |
| { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| CompletableFuture<Integer> g = new CompletableFuture<>(); |
| long startTime = System.nanoTime(); |
| f.complete(v1); |
| f.completeOnTimeout(-1, LONG_DELAY_MS, MILLISECONDS); |
| g.completeOnTimeout(-1, LONG_DELAY_MS, MILLISECONDS); |
| g.complete(v1); |
| checkCompletedNormally(f, v1); |
| checkCompletedNormally(g, v1); |
| assertTrue(millisElapsedSince(startTime) < LONG_DELAY_MS / 2); |
| }} |
| |
| /** |
| * delayedExecutor returns an executor that delays submission |
| */ |
| public void testDelayedExecutor() { |
| testInParallel(() -> testDelayedExecutor(null, null), |
| () -> testDelayedExecutor(null, 1), |
| () -> testDelayedExecutor(new ThreadExecutor(), 1), |
| () -> testDelayedExecutor(new ThreadExecutor(), 1)); |
| } |
| |
| public void testDelayedExecutor(Executor executor, Integer v) throws Exception { |
| long timeoutMillis = timeoutMillis(); |
| // Use an "unreasonably long" long timeout to catch lingering threads |
| long longTimeoutMillis = 1000 * 60 * 60 * 24; |
| final Executor delayer, longDelayer; |
| if (executor == null) { |
| delayer = CompletableFuture.delayedExecutor(timeoutMillis, MILLISECONDS); |
| longDelayer = CompletableFuture.delayedExecutor(longTimeoutMillis, MILLISECONDS); |
| } else { |
| delayer = CompletableFuture.delayedExecutor(timeoutMillis, MILLISECONDS, executor); |
| longDelayer = CompletableFuture.delayedExecutor(longTimeoutMillis, MILLISECONDS, executor); |
| } |
| long startTime = System.nanoTime(); |
| CompletableFuture<Integer> f = |
| CompletableFuture.supplyAsync(() -> v, delayer); |
| CompletableFuture<Integer> g = |
| CompletableFuture.supplyAsync(() -> v, longDelayer); |
| |
| assertNull(g.getNow(null)); |
| |
| assertSame(v, f.get(LONG_DELAY_MS, MILLISECONDS)); |
| long millisElapsed = millisElapsedSince(startTime); |
| assertTrue(millisElapsed >= timeoutMillis); |
| assertTrue(millisElapsed < LONG_DELAY_MS / 2); |
| |
| checkCompletedNormally(f, v); |
| |
| checkIncomplete(g); |
| assertTrue(g.cancel(true)); |
| } |
| |
| //--- tests of implementation details; not part of official tck --- |
| |
| Object resultOf(CompletableFuture<?> f) { |
| try { |
| java.lang.reflect.Field resultField |
| = CompletableFuture.class.getDeclaredField("result"); |
| resultField.setAccessible(true); |
| return resultField.get(f); |
| } catch (Throwable t) { throw new AssertionError(t); } |
| } |
| |
| public void testExceptionPropagationReusesResultObject() { |
| if (!testImplementationDetails) return; |
| for (ExecutionMode m : ExecutionMode.values()) |
| { |
| final CFException ex = new CFException(); |
| final CompletableFuture<Integer> v42 = CompletableFuture.completedFuture(42); |
| final CompletableFuture<Integer> incomplete = new CompletableFuture<>(); |
| |
| List<Function<CompletableFuture<Integer>, CompletableFuture<?>>> funs |
| = new ArrayList<>(); |
| |
| funs.add((y) -> m.thenRun(y, new Noop(m))); |
| funs.add((y) -> m.thenAccept(y, new NoopConsumer(m))); |
| funs.add((y) -> m.thenApply(y, new IncFunction(m))); |
| |
| funs.add((y) -> m.runAfterEither(y, incomplete, new Noop(m))); |
| funs.add((y) -> m.acceptEither(y, incomplete, new NoopConsumer(m))); |
| funs.add((y) -> m.applyToEither(y, incomplete, new IncFunction(m))); |
| |
| funs.add((y) -> m.runAfterBoth(y, v42, new Noop(m))); |
| funs.add((y) -> m.thenAcceptBoth(y, v42, new SubtractAction(m))); |
| funs.add((y) -> m.thenCombine(y, v42, new SubtractFunction(m))); |
| |
| funs.add((y) -> m.whenComplete(y, (Integer r, Throwable t) -> {})); |
| |
| funs.add((y) -> m.thenCompose(y, new CompletableFutureInc(m))); |
| |
| funs.add((y) -> CompletableFuture.allOf(new CompletableFuture<?>[] {y, v42})); |
| funs.add((y) -> CompletableFuture.anyOf(new CompletableFuture<?>[] {y, incomplete})); |
| |
| for (Function<CompletableFuture<Integer>, CompletableFuture<?>> |
| fun : funs) { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| f.completeExceptionally(ex); |
| CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m)); |
| checkCompletedWithWrappedException(src, ex); |
| CompletableFuture<?> dep = fun.apply(src); |
| checkCompletedWithWrappedException(dep, ex); |
| assertSame(resultOf(src), resultOf(dep)); |
| } |
| |
| for (Function<CompletableFuture<Integer>, CompletableFuture<?>> |
| fun : funs) { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m)); |
| CompletableFuture<?> dep = fun.apply(src); |
| f.completeExceptionally(ex); |
| checkCompletedWithWrappedException(src, ex); |
| checkCompletedWithWrappedException(dep, ex); |
| assertSame(resultOf(src), resultOf(dep)); |
| } |
| |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| for (Function<CompletableFuture<Integer>, CompletableFuture<?>> |
| fun : funs) { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| f.cancel(mayInterruptIfRunning); |
| checkCancelled(f); |
| CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m)); |
| checkCompletedWithWrappedCancellationException(src); |
| CompletableFuture<?> dep = fun.apply(src); |
| checkCompletedWithWrappedCancellationException(dep); |
| assertSame(resultOf(src), resultOf(dep)); |
| } |
| |
| for (boolean mayInterruptIfRunning : new boolean[] { true, false }) |
| for (Function<CompletableFuture<Integer>, CompletableFuture<?>> |
| fun : funs) { |
| CompletableFuture<Integer> f = new CompletableFuture<>(); |
| CompletableFuture<Integer> src = m.thenApply(f, new IncFunction(m)); |
| CompletableFuture<?> dep = fun.apply(src); |
| f.cancel(mayInterruptIfRunning); |
| checkCancelled(f); |
| checkCompletedWithWrappedCancellationException(src); |
| checkCompletedWithWrappedCancellationException(dep); |
| assertSame(resultOf(src), resultOf(dep)); |
| } |
| }} |
| |
| /** |
| * Minimal completion stages throw UOE for all non-CompletionStage methods |
| */ |
| public void testMinimalCompletionStage_minimality() { |
| if (!testImplementationDetails) return; |
| Function<Method, String> toSignature = |
| (method) -> method.getName() + Arrays.toString(method.getParameterTypes()); |
| Predicate<Method> isNotStatic = |
| (method) -> (method.getModifiers() & Modifier.STATIC) == 0; |
| // Android-changed: Added a cast to workaround an ECJ bug. http://b/33371837 |
| List<Method> minimalMethods = |
| Stream.of(Object.class, CompletionStage.class) |
| .flatMap((klazz) -> (Stream<Method>) Stream.of(klazz.getMethods())) |
| .filter(isNotStatic) |
| .collect(Collectors.toList()); |
| // Methods from CompletableFuture permitted NOT to throw UOE |
| String[] signatureWhitelist = { |
| "newIncompleteFuture[]", |
| "defaultExecutor[]", |
| "minimalCompletionStage[]", |
| "copy[]", |
| }; |
| Set<String> permittedMethodSignatures = |
| Stream.concat(minimalMethods.stream().map(toSignature), |
| Stream.of(signatureWhitelist)) |
| .collect(Collectors.toSet()); |
| List<Method> allMethods = Stream.of(CompletableFuture.class.getMethods()) |
| .filter(isNotStatic) |
| .filter((method) -> !permittedMethodSignatures.contains(toSignature.apply(method))) |
| .collect(Collectors.toList()); |
| |
| CompletionStage<Integer> minimalStage = |
| new CompletableFuture<Integer>().minimalCompletionStage(); |
| |
| List<Method> bugs = new ArrayList<>(); |
| for (Method method : allMethods) { |
| Class<?>[] parameterTypes = method.getParameterTypes(); |
| Object[] args = new Object[parameterTypes.length]; |
| // Manufacture boxed primitives for primitive params |
| for (int i = 0; i < args.length; i++) { |
| Class<?> type = parameterTypes[i]; |
| if (parameterTypes[i] == boolean.class) |
| args[i] = false; |
| else if (parameterTypes[i] == int.class) |
| args[i] = 0; |
| else if (parameterTypes[i] == long.class) |
| args[i] = 0L; |
| } |
| try { |
| method.invoke(minimalStage, args); |
| bugs.add(method); |
| } |
| catch (java.lang.reflect.InvocationTargetException expected) { |
| if (! (expected.getCause() instanceof UnsupportedOperationException)) { |
| bugs.add(method); |
| // expected.getCause().printStackTrace(); |
| } |
| } |
| catch (ReflectiveOperationException bad) { throw new Error(bad); } |
| } |
| if (!bugs.isEmpty()) |
| throw new Error("Methods did not throw UOE: " + bugs.toString()); |
| } |
| |
| static class Monad { |
| static class ZeroException extends RuntimeException { |
| public ZeroException() { super("monadic zero"); } |
| } |
| // "return", "unit" |
| static <T> CompletableFuture<T> unit(T value) { |
| return completedFuture(value); |
| } |
| // monadic zero ? |
| static <T> CompletableFuture<T> zero() { |
| return failedFuture(new ZeroException()); |
| } |
| // >=> |
| static <T,U,V> Function<T, CompletableFuture<V>> compose |
| (Function<T, CompletableFuture<U>> f, |
| Function<U, CompletableFuture<V>> g) { |
| return (x) -> f.apply(x).thenCompose(g); |
| } |
| |
| static void assertZero(CompletableFuture<?> f) { |
| try { |
| f.getNow(null); |
| throw new AssertionFailedError("should throw"); |
| } catch (CompletionException success) { |
| assertTrue(success.getCause() instanceof ZeroException); |
| } |
| } |
| |
| static <T> void assertFutureEquals(CompletableFuture<T> f, |
| CompletableFuture<T> g) { |
| T fval = null, gval = null; |
| Throwable fex = null, gex = null; |
| |
| try { fval = f.get(); } |
| catch (ExecutionException ex) { fex = ex.getCause(); } |
| catch (Throwable ex) { fex = ex; } |
| |
| try { gval = g.get(); } |
| catch (ExecutionException ex) { gex = ex.getCause(); } |
| catch (Throwable ex) { gex = ex; } |
| |
| if (fex != null || gex != null) |
| assertSame(fex.getClass(), gex.getClass()); |
| else |
| assertEquals(fval, gval); |
| } |
| |
| static class PlusFuture<T> extends CompletableFuture<T> { |
| AtomicReference<Throwable> firstFailure = new AtomicReference<>(null); |
| } |
| |
| /** Implements "monadic plus". */ |
| static <T> CompletableFuture<T> plus(CompletableFuture<? extends T> f, |
| CompletableFuture<? extends T> g) { |
| PlusFuture<T> plus = new PlusFuture<T>(); |
| BiConsumer<T, Throwable> action = (T result, Throwable ex) -> { |
| try { |
| if (ex == null) { |
| if (plus.complete(result)) |
| if (plus.firstFailure.get() != null) |
| plus.firstFailure.set(null); |
| } |
| else if (plus.firstFailure.compareAndSet(null, ex)) { |
| if (plus.isDone()) |
| plus.firstFailure.set(null); |
| } |
| else { |
| // first failure has precedence |
| Throwable first = plus.firstFailure.getAndSet(null); |
| |
| // may fail with "Self-suppression not permitted" |
| try { first.addSuppressed(ex); } |
| catch (Exception ignored) {} |
| |
| plus.completeExceptionally(first); |
| } |
| } catch (Throwable unexpected) { |
| plus.completeExceptionally(unexpected); |
| } |
| }; |
| f.whenComplete(action); |
| g.whenComplete(action); |
| return plus; |
| } |
| } |
| |
| /** |
| * CompletableFuture is an additive monad - sort of. |
| * https://en.wikipedia.org/wiki/Monad_(functional_programming)#Additive_monads |
| */ |
| public void testAdditiveMonad() throws Throwable { |
| Function<Long, CompletableFuture<Long>> unit = Monad::unit; |
| CompletableFuture<Long> zero = Monad.zero(); |
| |
| // Some mutually non-commutative functions |
| Function<Long, CompletableFuture<Long>> triple |
| = (x) -> Monad.unit(3 * x); |
| Function<Long, CompletableFuture<Long>> inc |
| = (x) -> Monad.unit(x + 1); |
| |
| // unit is a right identity: m >>= unit === m |
| Monad.assertFutureEquals(inc.apply(5L).thenCompose(unit), |
| inc.apply(5L)); |
| // unit is a left identity: (unit x) >>= f === f x |
| Monad.assertFutureEquals(unit.apply(5L).thenCompose(inc), |
| inc.apply(5L)); |
| |
| // associativity: (m >>= f) >>= g === m >>= ( \x -> (f x >>= g) ) |
| Monad.assertFutureEquals( |
| unit.apply(5L).thenCompose(inc).thenCompose(triple), |
| unit.apply(5L).thenCompose((x) -> inc.apply(x).thenCompose(triple))); |
| |
| // The case for CompletableFuture as an additive monad is weaker... |
| |
| // zero is a monadic zero |
| Monad.assertZero(zero); |
| |
| // left zero: zero >>= f === zero |
| Monad.assertZero(zero.thenCompose(inc)); |
| // right zero: f >>= (\x -> zero) === zero |
| Monad.assertZero(inc.apply(5L).thenCompose((x) -> zero)); |
| |
| // f plus zero === f |
| Monad.assertFutureEquals(Monad.unit(5L), |
| Monad.plus(Monad.unit(5L), zero)); |
| // zero plus f === f |
| Monad.assertFutureEquals(Monad.unit(5L), |
| Monad.plus(zero, Monad.unit(5L))); |
| // zero plus zero === zero |
| Monad.assertZero(Monad.plus(zero, zero)); |
| { |
| CompletableFuture<Long> f = Monad.plus(Monad.unit(5L), |
| Monad.unit(8L)); |
| // non-determinism |
| assertTrue(f.get() == 5L || f.get() == 8L); |
| } |
| |
| CompletableFuture<Long> godot = new CompletableFuture<>(); |
| // f plus godot === f (doesn't wait for godot) |
| Monad.assertFutureEquals(Monad.unit(5L), |
| Monad.plus(Monad.unit(5L), godot)); |
| // godot plus f === f (doesn't wait for godot) |
| Monad.assertFutureEquals(Monad.unit(5L), |
| Monad.plus(godot, Monad.unit(5L))); |
| } |
| |
| // static <U> U join(CompletionStage<U> stage) { |
| // CompletableFuture<U> f = new CompletableFuture<>(); |
| // stage.whenComplete((v, ex) -> { |
| // if (ex != null) f.completeExceptionally(ex); else f.complete(v); |
| // }); |
| // return f.join(); |
| // } |
| |
| // static <U> boolean isDone(CompletionStage<U> stage) { |
| // CompletableFuture<U> f = new CompletableFuture<>(); |
| // stage.whenComplete((v, ex) -> { |
| // if (ex != null) f.completeExceptionally(ex); else f.complete(v); |
| // }); |
| // return f.isDone(); |
| // } |
| |
| // static <U> U join2(CompletionStage<U> stage) { |
| // return stage.toCompletableFuture().copy().join(); |
| // } |
| |
| // static <U> boolean isDone2(CompletionStage<U> stage) { |
| // return stage.toCompletableFuture().copy().isDone(); |
| // } |
| |
| } |