ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/tck/CompletableFutureTest.java
(Generate patch)

Comparing jsr166/src/test/tck/CompletableFutureTest.java (file contents):
Revision 1.125 by jsr166, Thu Sep 10 17:59:13 2015 UTC vs.
Revision 1.135 by jsr166, Sun Nov 15 20:03:08 2015 UTC

# Line 7 | Line 7
7  
8   import static java.util.concurrent.TimeUnit.MILLISECONDS;
9   import static java.util.concurrent.TimeUnit.SECONDS;
10 + import static java.util.concurrent.CompletableFuture.completedFuture;
11 + import static java.util.concurrent.CompletableFuture.failedFuture;
12  
13   import java.lang.reflect.Method;
14   import java.lang.reflect.Modifier;
# Line 39 | Line 41 | import java.util.function.Function;
41   import java.util.function.Predicate;
42   import java.util.function.Supplier;
43  
44 + import junit.framework.AssertionFailedError;
45   import junit.framework.Test;
46   import junit.framework.TestSuite;
47  
# Line 837 | Line 840 | public class CompletableFutureTest exten
840          if (!createIncomplete) assertTrue(f.complete(v1));
841          final CompletableFuture<Integer> g = f.exceptionally
842              ((Throwable t) -> {
840                // Should not be called
843                  a.getAndIncrement();
844 <                throw new AssertionError();
844 >                threadFail("should not be called");
845 >                return null;            // unreached
846              });
847          if (createIncomplete) assertTrue(f.complete(v1));
848  
# Line 898 | Line 901 | public class CompletableFutureTest exten
901       * whenComplete action executes on normal completion, propagating
902       * source result.
903       */
904 <    public void testWhenComplete_normalCompletion1() {
904 >    public void testWhenComplete_normalCompletion() {
905          for (ExecutionMode m : ExecutionMode.values())
906          for (boolean createIncomplete : new boolean[] { true, false })
907          for (Integer v1 : new Integer[] { 1, null })
# Line 908 | Line 911 | public class CompletableFutureTest exten
911          if (!createIncomplete) assertTrue(f.complete(v1));
912          final CompletableFuture<Integer> g = m.whenComplete
913              (f,
914 <             (Integer x, Throwable t) -> {
914 >             (Integer result, Throwable t) -> {
915                  m.checkExecutionMode();
916 <                threadAssertSame(x, v1);
916 >                threadAssertSame(result, v1);
917                  threadAssertNull(t);
918                  a.getAndIncrement();
919              });
# Line 935 | Line 938 | public class CompletableFutureTest exten
938          if (!createIncomplete) f.completeExceptionally(ex);
939          final CompletableFuture<Integer> g = m.whenComplete
940              (f,
941 <             (Integer x, Throwable t) -> {
941 >             (Integer result, Throwable t) -> {
942                  m.checkExecutionMode();
943 <                threadAssertNull(x);
943 >                threadAssertNull(result);
944                  threadAssertSame(t, ex);
945                  a.getAndIncrement();
946              });
# Line 962 | Line 965 | public class CompletableFutureTest exten
965          if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
966          final CompletableFuture<Integer> g = m.whenComplete
967              (f,
968 <             (Integer x, Throwable t) -> {
968 >             (Integer result, Throwable t) -> {
969                  m.checkExecutionMode();
970 <                threadAssertNull(x);
970 >                threadAssertNull(result);
971                  threadAssertTrue(t instanceof CancellationException);
972                  a.getAndIncrement();
973              });
# Line 979 | Line 982 | public class CompletableFutureTest exten
982       * If a whenComplete action throws an exception when triggered by
983       * a normal completion, it completes exceptionally
984       */
985 <    public void testWhenComplete_actionFailed() {
985 >    public void testWhenComplete_sourceCompletedNormallyActionFailed() {
986          for (boolean createIncomplete : new boolean[] { true, false })
987          for (ExecutionMode m : ExecutionMode.values())
988          for (Integer v1 : new Integer[] { 1, null })
# Line 990 | Line 993 | public class CompletableFutureTest exten
993          if (!createIncomplete) assertTrue(f.complete(v1));
994          final CompletableFuture<Integer> g = m.whenComplete
995              (f,
996 <             (Integer x, Throwable t) -> {
996 >             (Integer result, Throwable t) -> {
997                  m.checkExecutionMode();
998 <                threadAssertSame(x, v1);
998 >                threadAssertSame(result, v1);
999                  threadAssertNull(t);
1000                  a.getAndIncrement();
1001                  throw ex;
# Line 1007 | Line 1010 | public class CompletableFutureTest exten
1010      /**
1011       * If a whenComplete action throws an exception when triggered by
1012       * a source completion that also throws an exception, the source
1013 <     * exception takes precedence.
1013 >     * exception takes precedence (unlike handle)
1014       */
1015 <    public void testWhenComplete_actionFailedSourceFailed() {
1015 >    public void testWhenComplete_sourceFailedActionFailed() {
1016          for (boolean createIncomplete : new boolean[] { true, false })
1017          for (ExecutionMode m : ExecutionMode.values())
1018      {
# Line 1021 | Line 1024 | public class CompletableFutureTest exten
1024          if (!createIncomplete) f.completeExceptionally(ex1);
1025          final CompletableFuture<Integer> g = m.whenComplete
1026              (f,
1027 <             (Integer x, Throwable t) -> {
1027 >             (Integer result, Throwable t) -> {
1028                  m.checkExecutionMode();
1029                  threadAssertSame(t, ex1);
1030 <                threadAssertNull(x);
1030 >                threadAssertNull(result);
1031                  a.getAndIncrement();
1032                  throw ex2;
1033              });
# Line 1049 | Line 1052 | public class CompletableFutureTest exten
1052          if (!createIncomplete) assertTrue(f.complete(v1));
1053          final CompletableFuture<Integer> g = m.handle
1054              (f,
1055 <             (Integer x, Throwable t) -> {
1055 >             (Integer result, Throwable t) -> {
1056                  m.checkExecutionMode();
1057 <                threadAssertSame(x, v1);
1057 >                threadAssertSame(result, v1);
1058                  threadAssertNull(t);
1059                  a.getAndIncrement();
1060                  return inc(v1);
# Line 1078 | Line 1081 | public class CompletableFutureTest exten
1081          if (!createIncomplete) f.completeExceptionally(ex);
1082          final CompletableFuture<Integer> g = m.handle
1083              (f,
1084 <             (Integer x, Throwable t) -> {
1084 >             (Integer result, Throwable t) -> {
1085                  m.checkExecutionMode();
1086 <                threadAssertNull(x);
1086 >                threadAssertNull(result);
1087                  threadAssertSame(t, ex);
1088                  a.getAndIncrement();
1089                  return v1;
# Line 1107 | Line 1110 | public class CompletableFutureTest exten
1110          if (!createIncomplete) assertTrue(f.cancel(mayInterruptIfRunning));
1111          final CompletableFuture<Integer> g = m.handle
1112              (f,
1113 <             (Integer x, Throwable t) -> {
1113 >             (Integer result, Throwable t) -> {
1114                  m.checkExecutionMode();
1115 <                threadAssertNull(x);
1115 >                threadAssertNull(result);
1116                  threadAssertTrue(t instanceof CancellationException);
1117                  a.getAndIncrement();
1118                  return v1;
# Line 1122 | Line 1125 | public class CompletableFutureTest exten
1125      }}
1126  
1127      /**
1128 <     * handle result completes exceptionally if action does
1128 >     * If a "handle action" throws an exception when triggered by
1129 >     * a normal completion, it completes exceptionally
1130       */
1131 <    public void testHandle_sourceFailedActionFailed() {
1131 >    public void testHandle_sourceCompletedNormallyActionFailed() {
1132          for (ExecutionMode m : ExecutionMode.values())
1133          for (boolean createIncomplete : new boolean[] { true, false })
1134 +        for (Integer v1 : new Integer[] { 1, null })
1135      {
1136          final CompletableFuture<Integer> f = new CompletableFuture<>();
1137          final AtomicInteger a = new AtomicInteger(0);
1138 <        final CFException ex1 = new CFException();
1139 <        final CFException ex2 = new CFException();
1135 <        if (!createIncomplete) f.completeExceptionally(ex1);
1138 >        final CFException ex = new CFException();
1139 >        if (!createIncomplete) assertTrue(f.complete(v1));
1140          final CompletableFuture<Integer> g = m.handle
1141              (f,
1142 <             (Integer x, Throwable t) -> {
1142 >             (Integer result, Throwable t) -> {
1143                  m.checkExecutionMode();
1144 <                threadAssertNull(x);
1145 <                threadAssertSame(ex1, t);
1144 >                threadAssertSame(result, v1);
1145 >                threadAssertNull(t);
1146                  a.getAndIncrement();
1147 <                throw ex2;
1147 >                throw ex;
1148              });
1149 <        if (createIncomplete) f.completeExceptionally(ex1);
1149 >        if (createIncomplete) assertTrue(f.complete(v1));
1150  
1151 <        checkCompletedWithWrappedException(g, ex2);
1152 <        checkCompletedExceptionally(f, ex1);
1151 >        checkCompletedWithWrappedException(g, ex);
1152 >        checkCompletedNormally(f, v1);
1153          assertEquals(1, a.get());
1154      }}
1155  
1156 <    public void testHandle_sourceCompletedNormallyActionFailed() {
1157 <        for (ExecutionMode m : ExecutionMode.values())
1156 >    /**
1157 >     * If a "handle action" throws an exception when triggered by
1158 >     * a source completion that also throws an exception, the action
1159 >     * exception takes precedence (unlike whenComplete)
1160 >     */
1161 >    public void testHandle_sourceFailedActionFailed() {
1162          for (boolean createIncomplete : new boolean[] { true, false })
1163 <        for (Integer v1 : new Integer[] { 1, null })
1163 >        for (ExecutionMode m : ExecutionMode.values())
1164      {
1157        final CompletableFuture<Integer> f = new CompletableFuture<>();
1165          final AtomicInteger a = new AtomicInteger(0);
1166 <        final CFException ex = new CFException();
1167 <        if (!createIncomplete) assertTrue(f.complete(v1));
1166 >        final CFException ex1 = new CFException();
1167 >        final CFException ex2 = new CFException();
1168 >        final CompletableFuture<Integer> f = new CompletableFuture<>();
1169 >
1170 >        if (!createIncomplete) f.completeExceptionally(ex1);
1171          final CompletableFuture<Integer> g = m.handle
1172              (f,
1173 <             (Integer x, Throwable t) -> {
1173 >             (Integer result, Throwable t) -> {
1174                  m.checkExecutionMode();
1175 <                threadAssertSame(x, v1);
1176 <                threadAssertNull(t);
1175 >                threadAssertNull(result);
1176 >                threadAssertSame(ex1, t);
1177                  a.getAndIncrement();
1178 <                throw ex;
1178 >                throw ex2;
1179              });
1180 <        if (createIncomplete) assertTrue(f.complete(v1));
1180 >        if (createIncomplete) f.completeExceptionally(ex1);
1181  
1182 <        checkCompletedWithWrappedException(g, ex);
1183 <        checkCompletedNormally(f, v1);
1182 >        checkCompletedWithWrappedException(g, ex2);
1183 >        checkCompletedExceptionally(f, ex1);
1184          assertEquals(1, a.get());
1185      }}
1186  
# Line 3641 | Line 3651 | public class CompletableFutureTest exten
3651          funs.add((y) -> m.thenAcceptBoth(y, v42, new SubtractAction(m)));
3652          funs.add((y) -> m.thenCombine(y, v42, new SubtractFunction(m)));
3653  
3654 <        funs.add((y) -> m.whenComplete(y, (Integer x, Throwable t) -> {}));
3654 >        funs.add((y) -> m.whenComplete(y, (Integer r, Throwable t) -> {}));
3655  
3656          funs.add((y) -> m.thenCompose(y, new CompletableFutureInc(m)));
3657  
# Line 3760 | Line 3770 | public class CompletableFutureTest exten
3770              throw new Error("Methods did not throw UOE: " + bugs.toString());
3771      }
3772  
3773 +    static class Monad {
3774 +        static class ZeroException extends RuntimeException {
3775 +            public ZeroException() { super("monadic zero"); }
3776 +        }
3777 +        // "return", "unit"
3778 +        static <T> CompletableFuture<T> unit(T value) {
3779 +            return completedFuture(value);
3780 +        }
3781 +        // monadic zero ?
3782 +        static <T> CompletableFuture<T> zero() {
3783 +            return failedFuture(new ZeroException());
3784 +        }
3785 +        // >=>
3786 +        static <T,U,V> Function<T, CompletableFuture<V>> compose
3787 +            (Function<T, CompletableFuture<U>> f,
3788 +             Function<U, CompletableFuture<V>> g) {
3789 +            return (x) -> f.apply(x).thenCompose(g);
3790 +        }
3791 +
3792 +        static void assertZero(CompletableFuture<?> f) {
3793 +            try {
3794 +                f.getNow(null);
3795 +                throw new AssertionFailedError("should throw");
3796 +            } catch (CompletionException success) {
3797 +                assertTrue(success.getCause() instanceof ZeroException);
3798 +            }
3799 +        }
3800 +
3801 +        static <T> void assertFutureEquals(CompletableFuture<T> f,
3802 +                                           CompletableFuture<T> g) {
3803 +            T fval = null, gval = null;
3804 +            Throwable fex = null, gex = null;
3805 +
3806 +            try { fval = f.get(); }
3807 +            catch (ExecutionException ex) { fex = ex.getCause(); }
3808 +            catch (Throwable ex) { fex = ex; }
3809 +
3810 +            try { gval = g.get(); }
3811 +            catch (ExecutionException ex) { gex = ex.getCause(); }
3812 +            catch (Throwable ex) { gex = ex; }
3813 +
3814 +            if (fex != null || gex != null)
3815 +                assertSame(fex.getClass(), gex.getClass());
3816 +            else
3817 +                assertEquals(fval, gval);
3818 +        }
3819 +
3820 +        static class PlusFuture<T> extends CompletableFuture<T> {
3821 +            AtomicReference<Throwable> firstFailure = new AtomicReference<>(null);
3822 +        }
3823 +
3824 +        // Monadic "plus"
3825 +        static <T> CompletableFuture<T> plus(CompletableFuture<? extends T> f,
3826 +                                             CompletableFuture<? extends T> g) {
3827 +            PlusFuture<T> plus = new PlusFuture<T>();
3828 +            BiConsumer<T, Throwable> action = (T result, Throwable ex) -> {
3829 +                if (ex == null) {
3830 +                    if (plus.complete(result))
3831 +                        if (plus.firstFailure.get() != null)
3832 +                            plus.firstFailure.set(null);
3833 +                }
3834 +                else if (plus.firstFailure.compareAndSet(null, ex)) {
3835 +                    if (plus.isDone())
3836 +                        plus.firstFailure.set(null);
3837 +                }
3838 +                else {
3839 +                    // first failure has precedence
3840 +                    Throwable first = plus.firstFailure.getAndSet(null);
3841 +
3842 +                    // may fail with "Self-suppression not permitted"
3843 +                    try { first.addSuppressed(ex); }
3844 +                    catch (Exception ignored) {}
3845 +
3846 +                    plus.completeExceptionally(first);
3847 +                }
3848 +            };
3849 +            f.whenComplete(action);
3850 +            g.whenComplete(action);
3851 +            return plus;
3852 +        }
3853 +    }
3854 +
3855 +    /**
3856 +     * CompletableFuture is an additive monad - sort of.
3857 +     * https://en.wikipedia.org/wiki/Monad_(functional_programming)#Additive_monads
3858 +     */
3859 +    public void testAdditiveMonad() throws Throwable {
3860 +        Function<Long, CompletableFuture<Long>> unit = Monad::unit;
3861 +        CompletableFuture<Long> zero = Monad.zero();
3862 +
3863 +        // Some mutually non-commutative functions
3864 +        Function<Long, CompletableFuture<Long>> triple
3865 +            = (x) -> Monad.unit(3 * x);
3866 +        Function<Long, CompletableFuture<Long>> inc
3867 +            = (x) -> Monad.unit(x + 1);
3868 +
3869 +        // unit is a right identity: m >>= unit === m
3870 +        Monad.assertFutureEquals(inc.apply(5L).thenCompose(unit),
3871 +                                 inc.apply(5L));
3872 +        // unit is a left identity: (unit x) >>= f === f x
3873 +        Monad.assertFutureEquals(unit.apply(5L).thenCompose(inc),
3874 +                                 inc.apply(5L));
3875 +
3876 +        // associativity: (m >>= f) >>= g === m >>= ( \x -> (f x >>= g) )
3877 +        Monad.assertFutureEquals(
3878 +            unit.apply(5L).thenCompose(inc).thenCompose(triple),
3879 +            unit.apply(5L).thenCompose((x) -> inc.apply(x).thenCompose(triple)));
3880 +
3881 +        // The case for CompletableFuture as an additive monad is weaker...
3882 +
3883 +        // zero is a monadic zero
3884 +        Monad.assertZero(zero);
3885 +
3886 +        // left zero: zero >>= f === zero
3887 +        Monad.assertZero(zero.thenCompose(inc));
3888 +        // right zero: f >>= (\x -> zero) === zero
3889 +        Monad.assertZero(inc.apply(5L).thenCompose((x) -> zero));
3890 +
3891 +        // f plus zero === f
3892 +        Monad.assertFutureEquals(Monad.unit(5L),
3893 +                                 Monad.plus(Monad.unit(5L), zero));
3894 +        // zero plus f === f
3895 +        Monad.assertFutureEquals(Monad.unit(5L),
3896 +                                 Monad.plus(zero, Monad.unit(5L)));
3897 +        // zero plus zero === zero
3898 +        Monad.assertZero(Monad.plus(zero, zero));
3899 +        {
3900 +            CompletableFuture<Long> f = Monad.plus(Monad.unit(5L),
3901 +                                                   Monad.unit(8L));
3902 +            // non-determinism
3903 +            assertTrue(f.get() == 5L || f.get() == 8L);
3904 +        }
3905 +
3906 +        CompletableFuture<Long> godot = new CompletableFuture<>();
3907 +        // f plus godot === f (doesn't wait for godot)
3908 +        Monad.assertFutureEquals(Monad.unit(5L),
3909 +                                 Monad.plus(Monad.unit(5L), godot));
3910 +        // godot plus f === f (doesn't wait for godot)
3911 +        Monad.assertFutureEquals(Monad.unit(5L),
3912 +                                 Monad.plus(godot, Monad.unit(5L)));
3913 +    }
3914 +
3915   //     static <U> U join(CompletionStage<U> stage) {
3916   //         CompletableFuture<U> f = new CompletableFuture<>();
3917   //         stage.whenComplete((v, ex) -> {

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines