package io.trane.future; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.time.Duration; import java.util.Arrays; import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.junit.After; import org.junit.Test; public class PromiseTest { private <T> T get(Future<T> future) throws CheckedFutureException { return future.get(Duration.ZERO); } private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); private final Exception ex = new TestException(); @After public void shutdownScheduler() { scheduler.shutdown(); } /*** apply ***/ @Test public void apply() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); p.setValue(1); assertEquals(new Integer(1), get(p)); } @Test public void applyWithLocal() { AtomicReference<Optional<Integer>> localValue = new AtomicReference<>(); Local<Integer> l = Local.apply(); l.update(1); Promise<Integer> p = Promise.apply(); l.update(2); p.ensure(() -> localValue.set(l.get())); p.setValue(1); assertEquals(Optional.of(1), localValue.get()); } @Test public void applyHandler() throws CheckedFutureException { AtomicReference<Throwable> interrupt = new AtomicReference<Throwable>(); InterruptHandler handler = interrupt::set; Promise<Integer> p = Promise.apply(handler); p.raise(ex); assertEquals(ex, interrupt.get()); } @Test public void applyHandlerWithLocal() { AtomicReference<Optional<Integer>> localValue = new AtomicReference<>(); Local<Integer> l = Local.apply(); l.update(1); Promise<Integer> p = Promise.apply((ex) -> { }); l.update(2); p.ensure(() -> localValue.set(l.get())); p.setValue(1); assertEquals(Optional.of(1), localValue.get()); } @Test public void applyTwoHandlers() throws CheckedFutureException { AtomicReference<Throwable> interrupt1 = new AtomicReference<Throwable>(); AtomicReference<Throwable> interrupt2 = new AtomicReference<Throwable>(); InterruptHandler handler1 = interrupt1::set; InterruptHandler handler2 = interrupt2::set; Promise<Integer> p = Promise.apply(handler1, handler2); p.raise(ex); assertEquals(ex, interrupt1.get()); assertEquals(ex, interrupt2.get()); } @Test public void applyTwoHandlersWithLocal() { AtomicReference<Optional<Integer>> localValue = new AtomicReference<>(); Local<Integer> l = Local.apply(); l.update(1); Promise<Integer> p = Promise.apply((ex) -> { }, (ex) -> { }); l.update(2); p.ensure(() -> localValue.set(l.get())); p.setValue(1); assertEquals(Optional.of(1), localValue.get()); } @Test public void applyHandlers() throws CheckedFutureException { AtomicReference<Throwable> interrupt1 = new AtomicReference<Throwable>(); AtomicReference<Throwable> interrupt2 = new AtomicReference<Throwable>(); InterruptHandler handler1 = interrupt1::set; InterruptHandler handler2 = interrupt2::set; Promise<Integer> p = Promise.apply(Arrays.asList(handler1, handler2)); p.raise(ex); assertEquals(ex, interrupt1.get()); assertEquals(ex, interrupt2.get()); } @Test public void applyHandlersWithLocal() { AtomicReference<Optional<Integer>> localValue = new AtomicReference<>(); Local<Integer> l = Local.apply(); l.update(1); Promise<Integer> p = Promise.apply(Arrays.asList((ex) -> { }, (ex) -> { })); l.update(2); p.ensure(() -> localValue.set(l.get())); p.setValue(1); assertEquals(Optional.of(1), localValue.get()); } /*** create ***/ @Test public void create() { AtomicReference<Promise<Integer>> pRef = new AtomicReference<>(); AtomicReference<Throwable> interrupt = new AtomicReference<Throwable>(); Promise<Integer> p = Promise.create(p2 -> { pRef.set(p2); return interrupt::set; }); p.raise(ex); assertEquals(p, pRef.get()); assertEquals(interrupt.get(), ex); } /*** becomeIfEmpty ***/ @Test public void becomeIfEmptyContinuation() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); p2.becomeIfEmpty(p1.map(i -> i + 1)); p1.setValue(1); assertEquals(new Integer(1), get(p1)); assertEquals(new Integer(2), get(p2)); } @Test public void becomeIfEmptyTwoContinuations() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); Future<Integer> f1 = p1.map(i -> i + 1); Future<Integer> f2 = p2.map(i -> i + 1); p2.becomeIfEmpty(f1); p1.setValue(1); assertEquals(new Integer(1), get(p1)); assertEquals(new Integer(2), get(p2)); assertEquals(new Integer(2), get(f1)); assertEquals(new Integer(3), get(f2)); } @Test public void becomeIfEmptySatisfied() { Promise<Integer> p = Promise.apply(); p.setValue(1); assertFalse(p.becomeIfEmpty(Future.value(1))); } @Test public void becomeIfEmptyLinked() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); p1.become(p2); assertTrue(p2.becomeIfEmpty(Future.value(1))); assertEquals(new Integer(1), get(p1)); assertEquals(new Integer(1), get(p2)); } @Test public void becomeIfEmptyLinkedSatisfied() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); p2.setValue(1); p1.become(p2); assertFalse(p2.becomeIfEmpty(Future.value(1))); } @Test public void becomeIfEmptyNestedLinked() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); p1.become(p2); assertTrue(p2.becomeIfEmpty(Future.value(1))); assertEquals(new Integer(1), get(p1)); assertEquals(new Integer(1), get(p2)); } @Test public void becomeIfEmptyWaiting() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Integer> c = p.map(i -> i + 1); assertTrue(p.becomeIfEmpty(Future.value(1))); assertEquals(new Integer(1), get(p)); assertEquals(new Integer(2), get(c)); } @Test public void becomeIfEmptyLocals() { AtomicReference<Optional<Integer>> restoredLocal = new AtomicReference<>(); Local<Integer> l = Local.apply(); l.set(Optional.of(1)); Promise<Integer> p = Promise.apply(); l.set(Optional.empty()); p.map(i -> { restoredLocal.set(l.get()); return i + 1; }); assertTrue(p.becomeIfEmpty(Future.value(1))); assertEquals(Optional.of(1), restoredLocal.get()); assertEquals(Optional.empty(), l.get()); } @Test public void becomeIfEmptyConcurrent() throws CheckedFutureException, InterruptedException { ExecutorService es = Executors.newFixedThreadPool(10); try { Promise<Integer> p = Promise.apply(); AtomicInteger expected = new AtomicInteger(-1); AtomicBoolean start = new AtomicBoolean(); for (int i = 0; i < 10; i++) { final int ii = i; es.submit(() -> { while (true) { if (start.get()) break; } if (p.becomeIfEmpty(Future.value(ii))) expected.set(ii); }); } start.set(true); int result = p.get(Duration.ofMillis(100)); Thread.sleep(100); assertEquals(expected.get(), result); } finally { es.shutdown(); } } @Test public void becomeIfEmptyWithPromise() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); Future<Integer> f = p1.map(i -> i + 1); assertTrue(p1.becomeIfEmpty(p2)); p2.setValue(1); assertEquals(new Integer(2), get(f)); assertEquals(new Integer(1), get(p1)); assertEquals(new Integer(1), get(p2)); } @Test(expected = NullPointerException.class) public void becomeIfEmptyError() { Promise<Integer> p = Promise.<Integer>apply(); p.map(i -> i + 1); p.becomeIfEmpty(null); } @Test public void becomeIfEmptyNoStakOverflow() throws CheckedFutureException { Promise<Integer> p = Promise.<Integer>apply(); Future<Integer> f = p; for (int i = 0; i < 20000; i++) f = f.map(v -> v + 1); p.becomeIfEmpty(Future.value(0)); assertEquals(new Integer(20000), get(f)); } @Test(expected = StackOverflowError.class) public void becomeIfEmptyStackOverflow() throws CheckedFutureException { Promise<Integer> p = Promise.<Integer>apply(); Future<Integer> f = p; for (int i = 0; i < 20000; i++) { f.map(v -> v + 1); f = f.map(v -> v + 1); } p.becomeIfEmpty(Future.value(0)); } /*** setValue ***/ @Test public void setValueSuccess() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); p.setValue(1); assertEquals(new Integer(1), get(p)); } @Test(expected = IllegalStateException.class) public void setValueFailure() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); p.setValue(1); p.setValue(1); } /*** setException ***/ @Test(expected = TestException.class) public void setExceptionSuccess() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); p.setException(ex); get(p); } @Test(expected = IllegalStateException.class) public void setExceptionFailure() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); p.setException(ex); p.setException(ex); } /*** raise ***/ @Test public void raise() { AtomicReference<Throwable> interrupt = new AtomicReference<>(); Promise<Integer> p = Promise.apply(interrupt::set); p.raise(ex); assertEquals(ex, interrupt.get()); } @Test public void raiseDone() { AtomicReference<Throwable> interrupt = new AtomicReference<>(); Promise<Integer> p = Promise.apply(interrupt::set); p.setValue(1); p.raise(ex); assertNull(interrupt.get()); } @Test public void raiseLinked() { AtomicReference<Throwable> interrupt = new AtomicReference<>(); Promise<Integer> p1 = Promise.apply(interrupt::set); Promise<Integer> p2 = Promise.apply(); p1.become(p2); p2.raise(ex); assertEquals(ex, interrupt.get()); } @Test public void raiseLinkedContinuation() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p1 = Promise.apply(intr::set); Promise<Integer> p2 = Promise.apply(); @SuppressWarnings("unchecked") Continuation<Integer, Integer> c = (Continuation<Integer, Integer>) p1.map(i -> i + 1); c.become(p2); p2.raise(ex); assertEquals(ex, intr.get()); } @Test public void raiseNoHandler() { Promise<Integer> p = Promise.apply(); p.raise(ex); } /*** become ***/ @Test public void becomeSuccess() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); p.become(Future.value(1)); assertEquals(new Integer(1), get(p)); } @Test(expected = IllegalStateException.class) public void becomeFailure() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); p.become(Future.value(1)); p.become(Future.value(1)); } @Test public void becomeAPromise() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); p1.become(p2); p2.setValue(1); assertEquals(new Integer(1), get(p1)); assertEquals(new Integer(1), get(p2)); } @Test public void becomeAContinuation() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); p1.become(p2.map(i -> i + 1)); p2.setValue(1); assertEquals(new Integer(2), get(p1)); assertEquals(new Integer(1), get(p2)); } @Test public void becomeASatisfiedFuture() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); p.become(Future.value(1)); assertEquals(new Integer(1), get(p)); } @Test public void becomeLinkedChain() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); Promise<Integer> p3 = Promise.apply(); p2.become(p1); p3.become(p2); p1.setValue(1); assertEquals(new Integer(1), get(p1)); assertEquals(new Integer(1), get(p2)); assertEquals(new Integer(1), get(p3)); } @Test public void becomeDoubleLinked() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); Promise<Integer> p3 = Promise.apply(); p1.become(p3); p2.become(p3); p3.setValue(1); assertEquals(new Integer(1), get(p1)); assertEquals(new Integer(1), get(p2)); assertEquals(new Integer(1), get(p3)); } @Test public void becomeLinkedWhenWaiting() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); Future<Integer> f = p2.map(i -> i + 1); p1.become(p2); p2.setValue(1); assertEquals(new Integer(1), get(p1)); assertEquals(new Integer(1), get(p2)); assertEquals(new Integer(2), get(f)); } @Test public void becomeCompress() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); Promise<Integer> p3 = Promise.apply(); p2.become(p1); p3.become(p2); p1.setValue(1); assertEquals(new Integer(1), get(p1)); assertEquals(new Integer(1), get(p2)); assertEquals(new Integer(1), get(p3)); } @Test(expected = IllegalStateException.class) public void becomeAlreadySatisfiedFailure() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); p1.setValue(1); p2.setValue(2); p1.become(p2); } /*** isDefined ***/ @Test public void isDefinedDone() { Promise<Integer> p = Promise.apply(); p.setValue(1); assertTrue(p.isDefined()); } @Test public void isDefinedLinkedDone() { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); p2.become(p1); p1.setValue(1); assertTrue(p1.isDefined()); } @Test public void isDefinedLinkedWaiting() { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); p2.become(p1); assertFalse(p2.isDefined()); } @Test public void isDefinedLinkedContinuationDone() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p1 = Promise.apply(intr::set); Promise<Integer> p2 = Promise.apply(); @SuppressWarnings("unchecked") Continuation<Integer, Integer> c = (Continuation<Integer, Integer>) p1.map(i -> i + 1); c.become(p2); p2.setValue(1); assertTrue(p2.isDefined()); } @Test public void isDefinedLinkedContinuationWaiting() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p1 = Promise.apply(intr::set); Promise<Integer> p2 = Promise.apply(); @SuppressWarnings("unchecked") Continuation<Integer, Integer> c = (Continuation<Integer, Integer>) p1.map(i -> i + 1); c.become(p2); assertFalse(p2.isDefined()); } @Test public void isDefinedWaiting() { Promise<Integer> p = Promise.apply(); assertFalse(p.isDefined()); } /*** voided ***/ @Test public void voidedSuccess() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Void> future = p.voided(); p.setValue(1); assertNull(get(future)); } @Test(expected = TestException.class) public void voidedFailure() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Void> future = p.voided(); p.setException(ex); get(future); } @Test public void voidedInterrupt() throws CheckedFutureException { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p = Promise.apply(intr::set); Future<Void> f = p.voided(); f.raise(ex); assertEquals(ex, intr.get()); } /*** delayed ***/ @Test public void delayed() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); long delay = 10; long start = System.currentTimeMillis(); Future<Integer> delayed = p.delayed(Duration.ofMillis(delay), scheduler); p.setValue(1); int result = delayed.get(Duration.ofMillis(20)); assertTrue(System.currentTimeMillis() - start >= delay); assertEquals(1, result); } @Test public void doubleDelayed() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); long delay = 10; long start = System.currentTimeMillis(); Future<Integer> delayed = p.delayed(Duration.ofMillis(delay), scheduler).delayed(Duration.ofMillis(delay), scheduler); p.setValue(1); int result = delayed.get(Duration.ofMillis(20)); assertTrue(System.currentTimeMillis() - start >= delay); assertEquals(1, result); } @Test public void delayedInterrupt() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p = Promise.apply(intr::set); Future<Integer> future = p.delayed(Duration.ofMillis(100), scheduler); future.raise(ex); assertEquals(ex, intr.get()); } /*** proxyTo ***/ @Test(expected = IllegalStateException.class) public void proxySatisified() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); p2.setValue(1); p1.proxyTo(p2); } @Test public void proxyToSuccess() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); p1.proxyTo(p2); p1.setValue(1); assertEquals(new Integer(1), get(p2)); } @Test(expected = TestException.class) public void proxyToFailure() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); p1.proxyTo(p2); p1.setException(ex); get(p2); } /*** within ***/ @Test public void withinMaxLongWait() { Future<Integer> future = Promise.apply(); assertEquals(future, future.within(Duration.ofMillis(Long.MAX_VALUE), scheduler, ex)); } @Test public void withinPromiseSuccess() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Integer> future = p.within(Duration.ofMillis(100), scheduler, ex); p.setValue(1); assertEquals(new Integer(1), get(future)); } @Test(expected = TestException.class) public void withinPromiseFailure() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Integer> future = p.within(Duration.ofMillis(100), scheduler, ex); p.setException(ex); get(future); } @Test(expected = TimeoutException.class) public void withinPromiseTimeout() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Integer> future = p.within(Duration.ofMillis(100), scheduler, ex); get(future); } @Test public void withinInterrupt() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p = Promise.apply(intr::set); Future<Integer> f = p.within(Duration.ofMillis(100), scheduler); f.raise(ex); assertEquals(ex, intr.get()); } /*** get ***/ @Test public void getSuccess() throws InterruptedException { ExecutorService es = Executors.newCachedThreadPool(); try { Promise<Integer> p = Promise.apply(); CountDownLatch latch = new CountDownLatch(1); es.submit(() -> { try { p.get(Duration.ofMillis(100)); latch.countDown(); } catch (CheckedFutureException e) { } }); es.submit(() -> { p.setValue(1); }); latch.await(); } finally { es.shutdown(); } } @Test(expected = TimeoutException.class) public void getTimeout() throws CheckedFutureException { (Promise.<Integer>apply()).get(Duration.ofMillis(100)); } @Test public void getInterrupted() throws CheckedFutureException, InterruptedException { AtomicReference<Throwable> cause = new AtomicReference<>(); Thread t = new Thread() { @Override public void run() { try { (Promise.<Integer>apply()).get(Duration.ofMinutes(1)); } catch (CheckedFutureException e) { cause.set(e.getCause()); } } }; t.start(); t.interrupt(); t.join(); assertTrue(cause.get() instanceof InterruptedException); } /*** continuation ***/ @Test public void multiple() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Integer> f1 = p.map(i -> i + 1); Future<Integer> f2 = p.map(i -> i + 2); Future<Integer> f3 = p.map(i -> i + 3); p.setValue(1); assertEquals(new Integer(2), get(f1)); assertEquals(new Integer(3), get(f2)); assertEquals(new Integer(4), get(f3)); } @Test public void linkedContinuation() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); @SuppressWarnings("unchecked") Continuation<Integer, Integer> c = (Continuation<Integer, Integer>) p1.map(i -> i + 1); c.become(p2); Future<Integer> f = p2.map(i -> i + 1); p2.setValue(1); assertEquals(new Integer(1), get(p2)); assertEquals(new Integer(2), get(f)); } @Test public void linkedContinuationWithWaitQueue() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); Future<Integer> f1 = p2.map(i -> i + 1); @SuppressWarnings("unchecked") Continuation<Integer, Integer> c = (Continuation<Integer, Integer>) p1.map(i -> i + 1); c.become(p2); Future<Integer> f2 = p2.map(i -> i + 2); p2.setValue(1); assertEquals(new Integer(1), get(p2)); assertEquals(new Integer(2), get(f1)); assertEquals(new Integer(3), get(f2)); } @Test public void map() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Integer> f = p.map(i -> i + 1); p.setValue(1); assertEquals(new Integer(1), get(p)); assertEquals(new Integer(2), get(f)); } @Test public void mapInterrupt() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p = Promise.apply(intr::set); Future<Integer> f = p.map(i -> i + 1); f.raise(ex); assertEquals(ex, intr.get()); } @Test public void flatMap() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Integer> f = p.flatMap(i -> Future.value(i + 1)); p.setValue(1); assertEquals(new Integer(1), get(p)); assertEquals(new Integer(2), get(f)); } @Test public void flatMapInterrupt() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p = Promise.apply(intr::set); Future<Integer> f = p.flatMap(i -> Future.value(i + 1)); f.raise(ex); assertEquals(ex, intr.get()); } @Test public void transform() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Integer> f = p.transform(new Transformer<Integer, Integer>() { @Override public Integer onException(Throwable ex) { return null; } @Override public Integer onValue(Integer value) { assertEquals(new Integer(1), value); return value + 1; } }); p.setValue(1); assertEquals(new Integer(1), get(p)); assertEquals(new Integer(2), get(f)); } @Test public void transformInterrupt() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p = Promise.apply(intr::set); Future<Integer> f = p.transform(new Transformer<Integer, Integer>() { @Override public Integer onException(Throwable ex) { return null; } @Override public Integer onValue(Integer value) { assertEquals(new Integer(1), value); return null; } }); f.raise(ex); assertEquals(ex, intr.get()); } @Test public void transformWith() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Integer> f = p.transformWith(new Transformer<Integer, Future<Integer>>() { @Override public Future<Integer> onException(Throwable ex) { return null; } @Override public Future<Integer> onValue(Integer value) { assertEquals(new Integer(1), value); return Future.value(value + 1); } }); p.setValue(1); assertEquals(new Integer(1), get(p)); assertEquals(new Integer(2), get(f)); } @Test public void transformWithInterrupt() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p = Promise.apply(intr::set); Future<Integer> f = p.transformWith(new Transformer<Integer, Future<Integer>>() { @Override public Future<Integer> onException(Throwable ex) { return null; } @Override public Future<Integer> onValue(Integer value) { assertEquals(new Integer(1), value); return null; } }); f.raise(ex); assertEquals(ex, intr.get()); } @Test public void ensure() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); AtomicBoolean called = new AtomicBoolean(false); Future<Integer> f = p.ensure(() -> called.set(true)); p.setValue(1); assertTrue(called.get()); assertEquals(new Integer(1), get(f)); } @Test public void ensureInterrupt() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p = Promise.apply(intr::set); Future<Integer> f = p.ensure(() -> { }); f.raise(ex); assertEquals(ex, intr.get()); } @Test public void biMap() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Integer> f = p.biMap(Future.value(1), (a, b) -> a + b); p.setValue(1); assertEquals(new Integer(1), get(p)); assertEquals(new Integer(2), get(f)); } @Test public void biMapInterrupt() { AtomicReference<Throwable> intr1 = new AtomicReference<>(); AtomicReference<Throwable> intr2 = new AtomicReference<>(); Promise<Integer> p1 = Promise.apply(intr1::set); Promise<Integer> p2 = Promise.apply(intr2::set); Future<Integer> f = p1.biMap(p2, (a, b) -> a + b); f.raise(ex); assertEquals(ex, intr1.get()); assertEquals(ex, intr2.get()); } @Test public void biMapAnotherPromise() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); Future<Integer> f = p1.biMap(p2, (a, b) -> a + b); p1.setValue(1); p2.setValue(2); assertEquals(new Integer(1), get(p1)); assertEquals(new Integer(2), get(p2)); assertEquals(new Integer(3), get(f)); } @Test public void biFlatMap() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Integer> f = p.biFlatMap(Future.value(1), (a, b) -> Future.value(a + b)); p.setValue(1); assertEquals(new Integer(1), get(p)); assertEquals(new Integer(2), get(f)); } @Test public void biFlatMapInterrupt() { AtomicReference<Throwable> intr1 = new AtomicReference<>(); AtomicReference<Throwable> intr2 = new AtomicReference<>(); Promise<Integer> p1 = Promise.apply(intr1::set); Promise<Integer> p2 = Promise.apply(intr2::set); Future<Integer> f = p1.biFlatMap(p2, (a, b) -> Future.value(a + b)); f.raise(ex); assertEquals(ex, intr1.get()); assertEquals(ex, intr2.get()); } @Test public void biFlatMapAnotherPromise() throws CheckedFutureException { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); Future<Integer> f = p1.biFlatMap(p2, (a, b) -> Future.value(a + b)); p1.setValue(1); p2.setValue(2); assertEquals(new Integer(1), get(p1)); assertEquals(new Integer(2), get(p2)); assertEquals(new Integer(3), get(f)); } @Test public void onSuccess() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); AtomicInteger result = new AtomicInteger(); Future<Integer> f = p.onSuccess(result::set); p.setValue(1); assertEquals(new Integer(1), get(p)); assertEquals(new Integer(1), get(f)); } @Test public void onSuccessInterrupt() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p = Promise.apply(intr::set); Future<Integer> f = p.onSuccess(i -> { }); f.raise(ex); assertEquals(ex, intr.get()); } @Test(expected = TestException.class) public void onFailure() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); AtomicReference<Throwable> result = new AtomicReference<>(); Future<Integer> f = p.onFailure(result::set); p.setException(ex); assertEquals(ex, result.get()); get(f); } @Test public void onFailureInterrupt() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p = Promise.apply(intr::set); Future<Integer> f = p.onFailure(i -> { }); f.raise(ex); assertEquals(ex, intr.get()); } @Test public void respondSuccess() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); AtomicInteger result = new AtomicInteger(-1); AtomicBoolean failure = new AtomicBoolean(false); Responder<Integer> r = new Responder<Integer>() { @Override public void onException(Throwable ex) { failure.set(true); } @Override public void onValue(Integer value) { result.set(value); } }; Future<Integer> f = p.respond(r); p.setValue(1); assertEquals(1, result.get()); assertFalse(failure.get()); assertEquals(new Integer(1), get(p)); assertEquals(new Integer(1), get(f)); } @Test(expected = TestException.class) public void respondFailure() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); AtomicBoolean success = new AtomicBoolean(false); AtomicReference<Throwable> failure = new AtomicReference<Throwable>(); Responder<Integer> r = new Responder<Integer>() { @Override public void onException(Throwable ex) { failure.set(ex); } @Override public void onValue(Integer value) { success.set(true); } }; Future<Integer> f = p.respond(r); p.setException(ex); assertEquals(ex, failure.get()); assertFalse(success.get()); get(f); } @Test public void respondInterrupt() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p = Promise.apply(intr::set); Responder<Integer> r = new Responder<Integer>() { @Override public void onException(Throwable ex) { } @Override public void onValue(Integer value) { } }; Future<Integer> f = p.respond(r); f.raise(ex); assertEquals(ex, intr.get()); } @Test public void rescue() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); AtomicReference<Throwable> exception = new AtomicReference<>(); Future<Integer> f = p.rescue(t -> { exception.set(t); return Future.value(2); }); p.setException(ex); assertEquals(ex, exception.get()); assertEquals(new Integer(2), get(f)); } @Test public void rescueInterrupt() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p = Promise.apply(intr::set); Future<Integer> f = p.rescue(ex -> Future.value(1)); f.raise(ex); assertEquals(ex, intr.get()); } public void interruptibleValue() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Integer> f = p.interruptible(); p.setValue(1); assertEquals(new Integer(1), get(p)); assertEquals(new Integer(1), get(f)); } @Test(expected = TestException.class) public void interruptibleException() throws CheckedFutureException { Promise<Integer> p = Promise.apply(); Future<Integer> f = p.interruptible(); p.setException(ex); get(f); } @Test(expected = TestException.class) public void interruptibleInterrupt() throws CheckedFutureException { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p = Promise.apply(intr::set); Future<Integer> f = p.interruptible(); f.raise(ex); assertNull(intr.get()); get(f); } /*** toString ***/ private String hexHashCode(Future<?> p) { return Integer.toHexString(p.hashCode()); } @Test public void toStringSatisfied() { Promise<Integer> p = Promise.apply(); p.setValue(1); assertEquals("Promise(ValueFuture(1))@" + hexHashCode(p), p.toString()); } @Test public void toStringLinked() { Promise<Integer> p1 = Promise.apply(); Promise<Integer> p2 = Promise.apply(); p1.become(p2); assertEquals("Promise(Linked(" + p1.toString() + "))@" + hexHashCode(p2), p2.toString()); } @Test public void toStringLinkedContinuationWaiting() { AtomicReference<Throwable> intr = new AtomicReference<>(); Promise<Integer> p1 = Promise.apply(intr::set); Promise<Integer> p2 = Promise.apply(); @SuppressWarnings("unchecked") Continuation<Integer, Integer> c = (Continuation<Integer, Integer>) p1.map(i -> i + 1); c.become(p2); assertEquals("Promise(Linked(" + c.toString() + "))@" + hexHashCode(p2), p2.toString()); } @Test public void toStringWaiting() { Promise<Integer> p = Promise.apply(); assertEquals("Promise(Waiting)@" + hexHashCode(p), p.toString()); } @Test public void toStringWaitingNonEmptyQueue() { Promise<Integer> p = Promise.apply(); p.map(i -> i + 1); assertEquals("Promise(Waiting)@" + hexHashCode(p), p.toString()); } @Test public void toStringContinuation() { Future<Integer> p = (Promise.<Integer>apply()).map(i -> i + 1); assertEquals("Continuation(Waiting)@" + hexHashCode(p), p.toString()); } }