/** Returns the server-side annotations that should be added to a Zipkin span. */
  protected List<KeyValueAnnotation> annotations(ServiceRequestContext ctx, RequestLog req, O res) {

    final List<KeyValueAnnotation> annotations = new ArrayList<>(5);

    final StringBuilder uriBuilder = new StringBuilder();
    uriBuilder.append(req.scheme().uriText());
    uriBuilder.append("://");
    uriBuilder.append(req.host());
    uriBuilder.append(ctx.path());
    if (req.method() != null) {
      uriBuilder.append('#');
      uriBuilder.append(req.method());
    }
    annotations.add(KeyValueAnnotation.create("server.uri", uriBuilder.toString()));

    if (ctx.remoteAddress() != null) {
      annotations.add(KeyValueAnnotation.create("server.remote", ctx.remoteAddress().toString()));
    }

    if (ctx.localAddress() != null) {
      annotations.add(KeyValueAnnotation.create("server.local", ctx.localAddress().toString()));
    }

    final CompletableFuture<?> f = res.closeFuture();
    if (f.isDone()) {
      // Need to use a callback because CompletableFuture does not have a getter for the cause of
      // failure.
      // The callback will be invoked immediately because the future is done already.
      f.handle(
              voidFunction(
                  (result, cause) -> {
                    final String resultText = cause == null ? "success" : "failure";
                    annotations.add(KeyValueAnnotation.create("server.result", resultText));

                    if (cause != null) {
                      annotations.add(KeyValueAnnotation.create("server.cause", cause.toString()));
                    }
                  }))
          .exceptionally(CompletionActions::log);
    }

    return annotations;
  }
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);
    }
  }