--- jsr166/src/test/tck/CompletableFutureTest.java 2015/11/15 01:33:02 1.129 +++ jsr166/src/test/tck/CompletableFutureTest.java 2015/11/15 19:55:38 1.134 @@ -982,7 +982,7 @@ public class CompletableFutureTest exten * If a whenComplete action throws an exception when triggered by * a normal completion, it completes exceptionally */ - public void testWhenComplete_actionFailed() { + public void testWhenComplete_sourceCompletedNormallyActionFailed() { for (boolean createIncomplete : new boolean[] { true, false }) for (ExecutionMode m : ExecutionMode.values()) for (Integer v1 : new Integer[] { 1, null }) @@ -1010,9 +1010,9 @@ public class CompletableFutureTest exten /** * If a whenComplete action throws an exception when triggered by * a source completion that also throws an exception, the source - * exception takes precedence. + * exception takes precedence (unlike handle) */ - public void testWhenComplete_actionFailedSourceFailed() { + public void testWhenComplete_sourceFailedActionFailed() { for (boolean createIncomplete : new boolean[] { true, false }) for (ExecutionMode m : ExecutionMode.values()) { @@ -1125,55 +1125,62 @@ public class CompletableFutureTest exten }} /** - * handle result completes exceptionally if action does + * If a "handle action" throws an exception when triggered by + * a normal completion, it completes exceptionally */ - public void testHandle_sourceFailedActionFailed() { + 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 f = new CompletableFuture<>(); final AtomicInteger a = new AtomicInteger(0); - final CFException ex1 = new CFException(); - final CFException ex2 = new CFException(); - if (!createIncomplete) f.completeExceptionally(ex1); + final CFException ex = new CFException(); + if (!createIncomplete) assertTrue(f.complete(v1)); final CompletableFuture g = m.handle (f, (Integer x, Throwable t) -> { m.checkExecutionMode(); - threadAssertNull(x); - threadAssertSame(ex1, t); + threadAssertSame(x, v1); + threadAssertNull(t); a.getAndIncrement(); - throw ex2; + throw ex; }); - if (createIncomplete) f.completeExceptionally(ex1); + if (createIncomplete) assertTrue(f.complete(v1)); - checkCompletedWithWrappedException(g, ex2); - checkCompletedExceptionally(f, ex1); + checkCompletedWithWrappedException(g, ex); + checkCompletedNormally(f, v1); assertEquals(1, a.get()); }} - public void testHandle_sourceCompletedNormallyActionFailed() { - for (ExecutionMode m : ExecutionMode.values()) + /** + * 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 (Integer v1 : new Integer[] { 1, null }) + for (ExecutionMode m : ExecutionMode.values()) { - final CompletableFuture f = new CompletableFuture<>(); final AtomicInteger a = new AtomicInteger(0); - final CFException ex = new CFException(); - if (!createIncomplete) assertTrue(f.complete(v1)); + final CFException ex1 = new CFException(); + final CFException ex2 = new CFException(); + final CompletableFuture f = new CompletableFuture<>(); + + if (!createIncomplete) f.completeExceptionally(ex1); final CompletableFuture g = m.handle (f, (Integer x, Throwable t) -> { m.checkExecutionMode(); - threadAssertSame(x, v1); - threadAssertNull(t); + threadAssertNull(x); + threadAssertSame(ex1, t); a.getAndIncrement(); - throw ex; + throw ex2; }); - if (createIncomplete) assertTrue(f.complete(v1)); + if (createIncomplete) f.completeExceptionally(ex1); - checkCompletedWithWrappedException(g, ex); - checkCompletedNormally(f, v1); + checkCompletedWithWrappedException(g, ex2); + checkCompletedExceptionally(f, ex1); assertEquals(1, a.get()); }} @@ -3764,8 +3771,8 @@ public class CompletableFutureTest exten } static class Monad { - static class MonadError extends Error { - public MonadError() { super("monadic zero"); } + static class ZeroException extends RuntimeException { + public ZeroException() { super("monadic zero"); } } // "return", "unit" static CompletableFuture unit(T value) { @@ -3773,7 +3780,7 @@ public class CompletableFutureTest exten } // monadic zero ? static CompletableFuture zero() { - return failedFuture(new MonadError()); + return failedFuture(new ZeroException()); } // >=> static Function> compose @@ -3787,7 +3794,7 @@ public class CompletableFutureTest exten f.getNow(null); throw new AssertionFailedError("should throw"); } catch (CompletionException success) { - assertTrue(success.getCause() instanceof MonadError); + assertTrue(success.getCause() instanceof ZeroException); } } @@ -3819,7 +3826,7 @@ public class CompletableFutureTest exten CompletableFuture g) { PlusFuture plus = new PlusFuture(); BiConsumer action = (T result, Throwable ex) -> { - if (result != null) { + if (ex == null) { if (plus.complete(result)) if (plus.firstFailure.get() != null) plus.firstFailure.set(null); @@ -3827,9 +3834,16 @@ public class CompletableFutureTest exten else if (plus.firstFailure.compareAndSet(null, ex)) { if (plus.isDone()) plus.firstFailure.set(null); - } else { - if (plus.completeExceptionally(ex)) - 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); } }; f.whenComplete(action);