/**
   * React to the completion of any of the events in the previous stage. Will not work reliably with
   * Streams where filter has been applied in earlier stages. (As Filter completes the Stream for
   * events that are filtered out, they potentially shortcircuit the completion of the stage).
   *
   * @param fn Function to apply when any of the previous events complete
   * @return Next stage in the stream
   */
  default <R> SimpleReactStream<R> anyOf(final Function<U, R> fn) {
    CompletableFuture[] array = lastActiveArray(getLastActive());
    CompletableFuture cf = CompletableFuture.anyOf(array);
    CompletableFuture onSuccess = cf.thenApplyAsync(fn, getTaskExecutor());

    return (SimpleReactStream<R>) withLastActive(new StreamWrapper(onSuccess, isEager()));
  }
Exemple #2
0
  private static void test(ExecutorService executor) throws Throwable {

    Thread.currentThread().setName("mainThread");

    // ----------------------------------------------------------------
    // supplyAsync tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<String> cf = supplyAsync(() -> "a test string");
      checkCompletedNormally(cf, cf.join());
      cf = supplyAsync(() -> "a test string", commonPool());
      checkCompletedNormally(cf, cf.join());
      cf = supplyAsync(() -> "a test string", executor);
      checkCompletedNormally(cf, cf.join());
      cf =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      checkCompletedExceptionally(cf);
      cf =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              },
              commonPool());
      checkCompletedExceptionally(cf);
      cf =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              },
              executor);
      checkCompletedExceptionally(cf);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // runAsync tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Void> cf = runAsync(() -> {});
      checkCompletedNormally(cf, cf.join());
      cf = runAsync(() -> {}, commonPool());
      checkCompletedNormally(cf, cf.join());
      cf = runAsync(() -> {}, executor);
      checkCompletedNormally(cf, cf.join());
      cf =
          runAsync(
              () -> {
                throw new RuntimeException();
              });
      checkCompletedExceptionally(cf);
      cf =
          runAsync(
              () -> {
                throw new RuntimeException();
              },
              commonPool());
      checkCompletedExceptionally(cf);
      cf =
          runAsync(
              () -> {
                throw new RuntimeException();
              },
              executor);
      checkCompletedExceptionally(cf);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // explicit completion
    // ----------------------------------------------------------------
    try {
      final Phaser phaser = new Phaser(1);
      final int phase = phaser.getPhase();
      CompletableFuture<Integer> cf;
      cf =
          supplyAsync(
              () -> {
                phaser.awaitAdvance(phase);
                return 1;
              });
      cf.complete(2);
      phaser.arrive();
      checkCompletedNormally(cf, 2);

      cf =
          supplyAsync(
              () -> {
                phaser.awaitAdvance(phase + 1);
                return 1;
              });
      cf.completeExceptionally(new Throwable());
      phaser.arrive();
      checkCompletedExceptionally(cf);

      cf =
          supplyAsync(
              () -> {
                phaser.awaitAdvance(phase + 2);
                return 1;
              });
      cf.cancel(true);
      phaser.arrive();
      checkCompletedExceptionally(cf, true);

      cf =
          supplyAsync(
              () -> {
                phaser.awaitAdvance(phase + 3);
                return 1;
              });
      check(cf.getNow(2) == 2);
      phaser.arrive();
      checkCompletedNormally(cf, 1);
      check(cf.getNow(2) == 1);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // thenApplyXXX tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Integer> cf2;
      CompletableFuture<String> cf1 = supplyAsync(() -> "a test string");
      cf2 =
          cf1.thenApply(
              (x) -> {
                if (x.equals("a test string")) return 1;
                else return 0;
              });
      checkCompletedNormally(cf1, "a test string");
      checkCompletedNormally(cf2, 1);

      cf1 = supplyAsync(() -> "a test string");
      cf2 =
          cf1.thenApplyAsync(
              (x) -> {
                if (x.equals("a test string")) return 1;
                else return 0;
              });
      checkCompletedNormally(cf1, "a test string");
      checkCompletedNormally(cf2, 1);

      cf1 = supplyAsync(() -> "a test string");
      cf2 =
          cf1.thenApplyAsync(
              (x) -> {
                if (x.equals("a test string")) return 1;
                else return 0;
              },
              executor);
      checkCompletedNormally(cf1, "a test string");
      checkCompletedNormally(cf2, 1);

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          cf1.thenApply(
              (x) -> {
                return 0;
              });
      checkCompletedExceptionally(cf1);
      checkCompletedExceptionally(cf2);

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          cf1.thenApplyAsync(
              (x) -> {
                return 0;
              });
      checkCompletedExceptionally(cf1);
      checkCompletedExceptionally(cf2);

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          cf1.thenApplyAsync(
              (x) -> {
                return 0;
              },
              executor);
      checkCompletedExceptionally(cf1);
      checkCompletedExceptionally(cf2);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // thenAcceptXXX tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Void> cf2;
      int before = atomicInt.get();
      CompletableFuture<String> cf1 = supplyAsync(() -> "a test string");
      cf2 =
          cf1.thenAccept(
              (x) -> {
                if (x.equals("a test string")) {
                  atomicInt.incrementAndGet();
                  return;
                }
                throw new RuntimeException();
              });
      checkCompletedNormally(cf1, "a test string");
      checkCompletedNormally(cf2, null);
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 = supplyAsync(() -> "a test string");
      cf2 =
          cf1.thenAcceptAsync(
              (x) -> {
                if (x.equals("a test string")) {
                  atomicInt.incrementAndGet();
                  return;
                }
                throw new RuntimeException();
              });
      checkCompletedNormally(cf1, "a test string");
      checkCompletedNormally(cf2, null);
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 = supplyAsync(() -> "a test string");
      cf2 =
          cf1.thenAcceptAsync(
              (x) -> {
                if (x.equals("a test string")) {
                  atomicInt.incrementAndGet();
                  return;
                }
                throw new RuntimeException();
              },
              executor);
      checkCompletedNormally(cf1, "a test string");
      checkCompletedNormally(cf2, null);
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          cf1.thenAccept(
              (x) -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedExceptionally(cf1);
      checkCompletedExceptionally(cf2);
      check(atomicInt.get() == before);

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          cf1.thenAcceptAsync(
              (x) -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedExceptionally(cf1);
      checkCompletedExceptionally(cf2);
      check(atomicInt.get() == before);

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          cf1.thenAcceptAsync(
              (x) -> {
                atomicInt.incrementAndGet();
              },
              executor);
      checkCompletedExceptionally(cf1);
      checkCompletedExceptionally(cf2);
      check(atomicInt.get() == before);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // thenRunXXX tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Void> cf2;
      int before = atomicInt.get();
      CompletableFuture<String> cf1 = supplyAsync(() -> "a test string");
      cf2 =
          cf1.thenRun(
              () -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cf1, "a test string");
      checkCompletedNormally(cf2, null);
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 = supplyAsync(() -> "a test string");
      cf2 =
          cf1.thenRunAsync(
              () -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cf1, "a test string");
      checkCompletedNormally(cf2, null);
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 = supplyAsync(() -> "a test string");
      cf2 =
          cf1.thenRunAsync(
              () -> {
                atomicInt.incrementAndGet();
              },
              executor);
      checkCompletedNormally(cf1, "a test string");
      checkCompletedNormally(cf2, null);
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          cf1.thenRun(
              () -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedExceptionally(cf1);
      checkCompletedExceptionally(cf2);
      check(atomicInt.get() == before);

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          cf1.thenRunAsync(
              () -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedExceptionally(cf1);
      checkCompletedExceptionally(cf2);
      check(atomicInt.get() == before);

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          cf1.thenRunAsync(
              () -> {
                atomicInt.incrementAndGet();
              },
              executor);
      checkCompletedExceptionally(cf1);
      checkCompletedExceptionally(cf2);
      check(atomicInt.get() == before);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // thenCombineXXX tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Integer> cf3;
      CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
      CompletableFuture<Integer> cf2 = supplyAsync(() -> 1);
      cf3 =
          cf1.thenCombine(
              cf2,
              (x, y) -> {
                return x + y;
              });
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf2, 1);
      checkCompletedNormally(cf3, 2);

      cf1 = supplyAsync(() -> 1);
      cf2 = supplyAsync(() -> 1);
      cf3 =
          cf1.thenCombineAsync(
              cf2,
              (x, y) -> {
                return x + y;
              });
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf2, 1);
      checkCompletedNormally(cf3, 2);

      cf1 = supplyAsync(() -> 1);
      cf2 = supplyAsync(() -> 1);
      cf3 =
          cf1.thenCombineAsync(
              cf2,
              (x, y) -> {
                return x + y;
              },
              executor);
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf2, 1);
      checkCompletedNormally(cf3, 2);

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 = supplyAsync(() -> 1);
      cf3 =
          cf1.thenCombine(
              cf2,
              (x, y) -> {
                return 0;
              });
      checkCompletedExceptionally(cf1);
      checkCompletedNormally(cf2, 1);
      checkCompletedExceptionally(cf3);

      cf1 = supplyAsync(() -> 1);
      cf2 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf3 =
          cf1.thenCombineAsync(
              cf2,
              (x, y) -> {
                return 0;
              });
      checkCompletedNormally(cf1, 1);
      checkCompletedExceptionally(cf2);
      checkCompletedExceptionally(cf3);

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf3 =
          cf1.thenCombineAsync(
              cf2,
              (x, y) -> {
                return 0;
              },
              executor);
      checkCompletedExceptionally(cf1);
      checkCompletedExceptionally(cf2);
      checkCompletedExceptionally(cf3);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // thenAcceptBothXXX tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Void> cf3;
      int before = atomicInt.get();
      CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
      CompletableFuture<Integer> cf2 = supplyAsync(() -> 1);
      cf3 =
          cf1.thenAcceptBoth(
              cf2,
              (x, y) -> {
                check(x + y == 2);
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf2, 1);
      checkCompletedNormally(cf3, null);
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 = supplyAsync(() -> 1);
      cf2 = supplyAsync(() -> 1);
      cf3 =
          cf1.thenAcceptBothAsync(
              cf2,
              (x, y) -> {
                check(x + y == 2);
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf2, 1);
      checkCompletedNormally(cf3, null);
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 = supplyAsync(() -> 1);
      cf2 = supplyAsync(() -> 1);
      cf3 =
          cf1.thenAcceptBothAsync(
              cf2,
              (x, y) -> {
                check(x + y == 2);
                atomicInt.incrementAndGet();
              },
              executor);
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf2, 1);
      checkCompletedNormally(cf3, null);
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 = supplyAsync(() -> 1);
      cf3 =
          cf1.thenAcceptBoth(
              cf2,
              (x, y) -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedExceptionally(cf1);
      checkCompletedNormally(cf2, 1);
      checkCompletedExceptionally(cf3);
      check(atomicInt.get() == before);

      cf1 = supplyAsync(() -> 1);
      cf2 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf3 =
          cf1.thenAcceptBothAsync(
              cf2,
              (x, y) -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cf1, 1);
      checkCompletedExceptionally(cf2);
      checkCompletedExceptionally(cf3);
      check(atomicInt.get() == before);

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf3 =
          cf1.thenAcceptBothAsync(
              cf2,
              (x, y) -> {
                atomicInt.incrementAndGet();
              },
              executor);
      checkCompletedExceptionally(cf1);
      checkCompletedExceptionally(cf2);
      checkCompletedExceptionally(cf3);
      check(atomicInt.get() == before);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // runAfterBothXXX tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Void> cf3;
      int before = atomicInt.get();
      CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
      CompletableFuture<Integer> cf2 = supplyAsync(() -> 1);
      cf3 =
          cf1.runAfterBoth(
              cf2,
              () -> {
                check(cf1.isDone());
                check(cf2.isDone());
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf2, 1);
      checkCompletedNormally(cf3, null);
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      CompletableFuture<Integer> cfa = supplyAsync(() -> 1);
      CompletableFuture<Integer> cfb = supplyAsync(() -> 1);
      cf3 =
          cfa.runAfterBothAsync(
              cfb,
              () -> {
                check(cfa.isDone());
                check(cfb.isDone());
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cfa, 1);
      checkCompletedNormally(cfb, 1);
      checkCompletedNormally(cf3, null);
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      CompletableFuture<Integer> cfx = supplyAsync(() -> 1);
      CompletableFuture<Integer> cfy = supplyAsync(() -> 1);
      cf3 =
          cfy.runAfterBothAsync(
              cfx,
              () -> {
                check(cfx.isDone());
                check(cfy.isDone());
                atomicInt.incrementAndGet();
              },
              executor);
      checkCompletedNormally(cfx, 1);
      checkCompletedNormally(cfy, 1);
      checkCompletedNormally(cf3, null);
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      CompletableFuture<Integer> cf4 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      CompletableFuture<Integer> cf5 = supplyAsync(() -> 1);
      cf3 =
          cf5.runAfterBothAsync(
              cf4,
              () -> {
                atomicInt.incrementAndGet();
              },
              executor);
      checkCompletedExceptionally(cf4);
      checkCompletedNormally(cf5, 1);
      checkCompletedExceptionally(cf3);
      check(atomicInt.get() == before);

      before = atomicInt.get();
      cf4 = supplyAsync(() -> 1);
      cf5 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf3 =
          cf5.runAfterBothAsync(
              cf4,
              () -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cf4, 1);
      checkCompletedExceptionally(cf5);
      checkCompletedExceptionally(cf3);
      check(atomicInt.get() == before);

      before = atomicInt.get();
      cf4 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf5 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf3 =
          cf5.runAfterBoth(
              cf4,
              () -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedExceptionally(cf4);
      checkCompletedExceptionally(cf5);
      checkCompletedExceptionally(cf3);
      check(atomicInt.get() == before);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // applyToEitherXXX tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Integer> cf3;
      CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
      CompletableFuture<Integer> cf2 = supplyAsync(() -> 2);
      cf3 =
          cf1.applyToEither(
              cf2,
              (x) -> {
                check(x == 1 || x == 2);
                return x;
              });
      checkCompletedNormally(cf3, new Object[] {1, 2});
      check(cf1.isDone() || cf2.isDone());

      cf1 = supplyAsync(() -> 1);
      cf2 = supplyAsync(() -> 2);
      cf3 =
          cf1.applyToEitherAsync(
              cf2,
              (x) -> {
                check(x == 1 || x == 2);
                return x;
              });
      checkCompletedNormally(cf3, new Object[] {1, 2});
      check(cf1.isDone() || cf2.isDone());

      cf1 = supplyAsync(() -> 1);
      cf2 = supplyAsync(() -> 2);
      cf3 =
          cf1.applyToEitherAsync(
              cf2,
              (x) -> {
                check(x == 1 || x == 2);
                return x;
              },
              executor);
      checkCompletedNormally(cf3, new Object[] {1, 2});
      check(cf1.isDone() || cf2.isDone());

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 = supplyAsync(() -> 2);
      cf3 =
          cf1.applyToEither(
              cf2,
              (x) -> {
                check(x == 2);
                return x;
              });
      try {
        check(cf3.join() == 2);
      } catch (CompletionException x) {
        pass();
      }
      check(cf3.isDone());
      check(cf1.isDone() || cf2.isDone());

      cf1 = supplyAsync(() -> 1);
      cf2 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf3 =
          cf1.applyToEitherAsync(
              cf2,
              (x) -> {
                check(x == 1);
                return x;
              });
      try {
        check(cf3.join() == 1);
      } catch (CompletionException x) {
        pass();
      }
      check(cf3.isDone());
      check(cf1.isDone() || cf2.isDone());

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf3 =
          cf1.applyToEitherAsync(
              cf2,
              (x) -> {
                fail();
                return x;
              });
      checkCompletedExceptionally(cf3);
      check(cf1.isDone() || cf2.isDone());

      final Phaser cf3Done = new Phaser(2);
      cf1 =
          supplyAsync(
              () -> {
                cf3Done.arriveAndAwaitAdvance();
                return 1;
              });
      cf2 = supplyAsync(() -> 2);
      cf3 =
          cf1.applyToEither(
              cf2,
              (x) -> {
                check(x == 2);
                return x;
              });
      checkCompletedNormally(cf3, 2);
      checkCompletedNormally(cf2, 2);
      check(!cf1.isDone());
      cf3Done.arrive();
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf3, 2);

      cf1 = supplyAsync(() -> 1);
      cf2 =
          supplyAsync(
              () -> {
                cf3Done.arriveAndAwaitAdvance();
                return 2;
              });
      cf3 =
          cf1.applyToEitherAsync(
              cf2,
              (x) -> {
                check(x == 1);
                return x;
              });
      checkCompletedNormally(cf3, 1);
      checkCompletedNormally(cf1, 1);
      check(!cf2.isDone());
      cf3Done.arrive();
      checkCompletedNormally(cf2, 2);
      checkCompletedNormally(cf3, 1);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // acceptEitherXXX tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Void> cf3;
      int before = atomicInt.get();
      CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
      CompletableFuture<Integer> cf2 = supplyAsync(() -> 2);
      cf3 =
          cf1.acceptEither(
              cf2,
              (x) -> {
                check(x == 1 || x == 2);
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cf3, null);
      check(cf1.isDone() || cf2.isDone());
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 = supplyAsync(() -> 1);
      cf2 = supplyAsync(() -> 2);
      cf3 =
          cf1.acceptEitherAsync(
              cf2,
              (x) -> {
                check(x == 1 || x == 2);
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cf3, null);
      check(cf1.isDone() || cf2.isDone());
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 = supplyAsync(() -> 1);
      cf2 = supplyAsync(() -> 2);
      cf3 =
          cf2.acceptEitherAsync(
              cf1,
              (x) -> {
                check(x == 1 || x == 2);
                atomicInt.incrementAndGet();
              },
              executor);
      checkCompletedNormally(cf3, null);
      check(cf1.isDone() || cf2.isDone());
      check(atomicInt.get() == (before + 1));

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 = supplyAsync(() -> 2);
      cf3 =
          cf2.acceptEitherAsync(
              cf1,
              (x) -> {
                check(x == 2);
              },
              executor);
      try {
        check(cf3.join() == null);
      } catch (CompletionException x) {
        pass();
      }
      check(cf3.isDone());
      check(cf1.isDone() || cf2.isDone());

      cf1 = supplyAsync(() -> 1);
      cf2 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf3 =
          cf2.acceptEitherAsync(
              cf1,
              (x) -> {
                check(x == 1);
              });
      try {
        check(cf3.join() == null);
      } catch (CompletionException x) {
        pass();
      }
      check(cf3.isDone());
      check(cf1.isDone() || cf2.isDone());

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf3 =
          cf2.acceptEitherAsync(
              cf1,
              (x) -> {
                fail();
              });
      checkCompletedExceptionally(cf3);
      check(cf1.isDone() || cf2.isDone());

      final Phaser cf3Done = new Phaser(2);
      cf1 =
          supplyAsync(
              () -> {
                cf3Done.arriveAndAwaitAdvance();
                return 1;
              });
      cf2 = supplyAsync(() -> 2);
      cf3 =
          cf1.acceptEither(
              cf2,
              (x) -> {
                check(x == 2);
              });
      checkCompletedNormally(cf3, null);
      checkCompletedNormally(cf2, 2);
      check(!cf1.isDone());
      cf3Done.arrive();
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf3, null);

      cf1 = supplyAsync(() -> 1);
      cf2 =
          supplyAsync(
              () -> {
                cf3Done.arriveAndAwaitAdvance();
                return 2;
              });
      cf3 =
          cf1.acceptEitherAsync(
              cf2,
              (x) -> {
                check(x == 1);
              });
      checkCompletedNormally(cf3, null);
      checkCompletedNormally(cf1, 1);
      check(!cf2.isDone());
      cf3Done.arrive();
      checkCompletedNormally(cf2, 2);
      checkCompletedNormally(cf3, null);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // runAfterEitherXXX tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Void> cf3;
      int before = atomicInt.get();
      CompletableFuture<Void> cf1 = runAsync(() -> {});
      CompletableFuture<Void> cf2 = runAsync(() -> {});
      cf3 =
          cf1.runAfterEither(
              cf2,
              () -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cf3, null);
      check(cf1.isDone() || cf2.isDone());
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 = runAsync(() -> {});
      cf2 = runAsync(() -> {});
      cf3 =
          cf1.runAfterEitherAsync(
              cf2,
              () -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cf3, null);
      check(cf1.isDone() || cf2.isDone());
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 = runAsync(() -> {});
      cf2 = runAsync(() -> {});
      cf3 =
          cf2.runAfterEitherAsync(
              cf1,
              () -> {
                atomicInt.incrementAndGet();
              },
              executor);
      checkCompletedNormally(cf3, null);
      check(cf1.isDone() || cf2.isDone());
      check(atomicInt.get() == (before + 1));

      before = atomicInt.get();
      cf1 =
          runAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 = runAsync(() -> {});
      cf3 =
          cf2.runAfterEither(
              cf1,
              () -> {
                atomicInt.incrementAndGet();
              });
      try {
        check(cf3.join() == null);
        check(atomicInt.get() == (before + 1));
      } catch (CompletionException x) {
        pass();
      }
      check(cf3.isDone());
      check(cf1.isDone() || cf2.isDone());

      before = atomicInt.get();
      cf1 = runAsync(() -> {});
      cf2 =
          runAsync(
              () -> {
                throw new RuntimeException();
              });
      cf3 =
          cf1.runAfterEitherAsync(
              cf2,
              () -> {
                atomicInt.incrementAndGet();
              });
      try {
        check(cf3.join() == null);
        check(atomicInt.get() == (before + 1));
      } catch (CompletionException x) {
        pass();
      }
      check(cf3.isDone());
      check(cf1.isDone() || cf2.isDone());

      before = atomicInt.get();
      cf1 =
          runAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          runAsync(
              () -> {
                throw new RuntimeException();
              });
      cf3 =
          cf2.runAfterEitherAsync(
              cf1,
              () -> {
                atomicInt.incrementAndGet();
              },
              executor);
      checkCompletedExceptionally(cf3);
      check(cf1.isDone() || cf2.isDone());
      check(atomicInt.get() == before);

      final Phaser cf3Done = new Phaser(2);
      before = atomicInt.get();
      cf1 =
          runAsync(
              () -> {
                cf3Done.arriveAndAwaitAdvance();
              });
      cf2 = runAsync(() -> {});
      cf3 =
          cf1.runAfterEither(
              cf2,
              () -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cf3, null);
      checkCompletedNormally(cf2, null);
      check(!cf1.isDone());
      check(atomicInt.get() == (before + 1));
      cf3Done.arrive();
      checkCompletedNormally(cf1, null);
      checkCompletedNormally(cf3, null);

      before = atomicInt.get();
      cf1 = runAsync(() -> {});
      cf2 =
          runAsync(
              () -> {
                cf3Done.arriveAndAwaitAdvance();
              });
      cf3 =
          cf1.runAfterEitherAsync(
              cf2,
              () -> {
                atomicInt.incrementAndGet();
              });
      checkCompletedNormally(cf3, null);
      checkCompletedNormally(cf1, null);
      check(!cf2.isDone());
      check(atomicInt.get() == (before + 1));
      cf3Done.arrive();
      checkCompletedNormally(cf2, null);
      checkCompletedNormally(cf3, null);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // thenComposeXXX tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Integer> cf2;
      CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
      cf2 =
          cf1.thenCompose(
              (x) -> {
                check(x == 1);
                return CompletableFuture.completedFuture(2);
              });
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf2, 2);

      cf1 = supplyAsync(() -> 1);
      cf2 =
          cf1.thenComposeAsync(
              (x) -> {
                check(x == 1);
                return CompletableFuture.completedFuture(2);
              });
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf2, 2);

      cf1 = supplyAsync(() -> 1);
      cf2 =
          cf1.thenComposeAsync(
              (x) -> {
                check(x == 1);
                return CompletableFuture.completedFuture(2);
              },
              executor);
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf2, 2);

      int before = atomicInt.get();
      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          cf1.thenCompose(
              (x) -> {
                atomicInt.incrementAndGet();
                return CompletableFuture.completedFuture(2);
              });
      checkCompletedExceptionally(cf1);
      checkCompletedExceptionally(cf2);
      check(atomicInt.get() == before);

      cf1 =
          supplyAsync(
              () -> {
                throw new RuntimeException();
              });
      cf2 =
          cf1.thenComposeAsync(
              (x) -> {
                atomicInt.incrementAndGet();
                return CompletableFuture.completedFuture(2);
              });
      checkCompletedExceptionally(cf1);
      checkCompletedExceptionally(cf2);
      check(atomicInt.get() == before);

      cf1 = supplyAsync(() -> 1);
      cf2 =
          cf1.thenComposeAsync(
              (x) -> {
                throw new RuntimeException();
              },
              executor);
      checkCompletedNormally(cf1, 1);
      checkCompletedExceptionally(cf2);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // anyOf tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Object> cf3;
      for (int k = 0; k < 10; k++) {
        CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
        CompletableFuture<Integer> cf2 = supplyAsync(() -> 2);
        cf3 = CompletableFuture.anyOf(cf1, cf2);
        checkCompletedNormally(cf3, new Object[] {1, 2});
        check(cf1.isDone() || cf2.isDone());
      }
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // allOf tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<?> cf3;
      for (int k = 0; k < 10; k++) {
        CompletableFuture<Integer>[] cfs =
            (CompletableFuture<Integer>[]) Array.newInstance(CompletableFuture.class, 10);
        for (int j = 0; j < 10; j++) {
          final int v = j;
          cfs[j] = supplyAsync(() -> v);
        }
        cf3 = CompletableFuture.allOf(cfs);
        for (int j = 0; j < 10; j++) checkCompletedNormally(cfs[j], j);
        checkCompletedNormally(cf3, null);
      }
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // exceptionally tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Integer> cf2;
      CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
      cf2 =
          cf1.exceptionally(
              (t) -> {
                fail("function should never be called");
                return 2;
              });
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf2, 1);

      final RuntimeException t = new RuntimeException();
      cf1 =
          supplyAsync(
              () -> {
                throw t;
              });
      cf2 =
          cf1.exceptionally(
              (x) -> {
                check(x.getCause() == t);
                return 2;
              });
      checkCompletedExceptionally(cf1);
      checkCompletedNormally(cf2, 2);
    } catch (Throwable t) {
      unexpected(t);
    }

    // ----------------------------------------------------------------
    // handle tests
    // ----------------------------------------------------------------
    try {
      CompletableFuture<Integer> cf2;
      CompletableFuture<Integer> cf1 = supplyAsync(() -> 1);
      cf2 = cf1.handle((x, t) -> x + 1);
      checkCompletedNormally(cf1, 1);
      checkCompletedNormally(cf2, 2);

      final RuntimeException ex = new RuntimeException();
      cf1 =
          supplyAsync(
              () -> {
                throw ex;
              });
      cf2 =
          cf1.handle(
              (x, t) -> {
                check(t.getCause() == ex);
                return 2;
              });
      checkCompletedExceptionally(cf1);
      checkCompletedNormally(cf2, 2);
    } catch (Throwable t) {
      unexpected(t);
    }
  }