diff options
author | Przemyslaw Szczepaniak <pszczepaniak@google.com> | 2016-03-11 15:59:10 +0000 |
---|---|---|
committer | Przemyslaw Szczepaniak <pszczepaniak@google.com> | 2016-03-15 11:14:14 +0000 |
commit | e8b323c7cb7d55be9a4df579231e44f04f53d766 (patch) | |
tree | c40a0cadb5caa371c28b117b017daddef4b72c28 /jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java | |
parent | 0eedcb21289c1916e876810982c2c8d473edf24f (diff) |
JSR-166 update without java 1.9 method/classes
Second attempt, in frist one I've submitted some code from openJdk 1.9
that shouldn't be here, orignial change can be found at
5328e07d282bef36ac8b757bbee16a761415b2c4
Adapted from sources taken from CVS using:
cvs -d ':pserver:anonymous@gee.cs.oswego.edu/home/jsr166/jsr166' checkout -D "03/03/2016 10:00:00 GMT" jsr166
This time with hidden/removed "@since 9" methods and classes
Bug: 27426599
Change-Id: Ibd8d26e13cba091bfd983c73d005e4f8d8f5946d
(cherry picked from commit b8b75116273ecfdb8ffdd1869b1c0dd04570a95e)
Diffstat (limited to 'jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java')
-rw-r--r-- | jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java | 3959 |
1 files changed, 3959 insertions, 0 deletions
diff --git a/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java b/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java new file mode 100644 index 0000000000..1372cc48dd --- /dev/null +++ b/jsr166-tests/src/test/java/jsr166/CompletableFutureTest.java @@ -0,0 +1,3959 @@ +/* + * 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; + +// TODO(streams): +//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()); + assertTrue(f.toString().contains("[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()); + assertTrue(f.toString().contains("[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())); + assertTrue(f.toString().contains("[Completed exceptionally]")); + + for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { + f = new CompletableFuture<String>(); + assertTrue(f.cancel(mayInterruptIfRunning)); + assertTrue(f.toString().contains("[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 + */ + // TODO(streams): + // 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; + // List<Method> minimalMethods = + // Stream.of(Object.class, CompletionStage.class) + // .flatMap((klazz) -> 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(); +// } + +} |