/* * Copyright (C) 2018-2020 Lightbend Inc. */ package akka.dispatch; import akka.testkit.AkkaJUnitActorSystemResource; import akka.actor.ActorSystem; import akka.japi.*; import org.junit.ClassRule; import org.scalatestplus.junit.JUnitSuite; import scala.concurrent.Await; import scala.concurrent.Future; import scala.concurrent.Promise; import scala.concurrent.duration.Duration; import org.junit.Test; import static org.junit.Assert.*; import java.io.IOException; import java.util.concurrent.Callable; import java.util.LinkedList; import java.lang.Iterable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static akka.japi.Util.classTag; import akka.testkit.AkkaSpec; public class JavaFutureTests extends JUnitSuite { @ClassRule public static AkkaJUnitActorSystemResource actorSystemResource = new AkkaJUnitActorSystemResource("JavaFutureTests", AkkaSpec.testConf()); private final ActorSystem system = actorSystemResource.getSystem(); private final Duration timeout = Duration.create(5, TimeUnit.SECONDS); @Test public void mustBeAbleToMapAFuture() throws Exception { Future f1 = Futures.future(new Callable() { public String call() { return "Hello"; } }, system.dispatcher()); Future f2 = f1.map(new Mapper() { public String apply(String s) { return s + " World"; } }, system.dispatcher()); assertEquals("Hello World", Await.result(f2, timeout)); } @Test public void mustBeAbleToExecuteAnOnResultCallback() throws Throwable { final CountDownLatch latch = new CountDownLatch(1); Promise cf = Futures.promise(); Future f = cf.future(); f.onComplete(new OnComplete() { public void onComplete(Throwable t, String r) { if ("foo".equals(r)) latch.countDown(); } }, system.dispatcher()); cf.success("foo"); assertTrue(latch.await(5, TimeUnit.SECONDS)); assertEquals(Await.result(f, timeout), "foo"); } @Test public void mustBeAbleToExecuteAnOnExceptionCallback() throws Throwable { final CountDownLatch latch = new CountDownLatch(1); Promise cf = Futures.promise(); Future f = cf.future(); f.onComplete(new OnComplete() { public void onComplete(Throwable t, String r) { // 'null instanceof ...' is always false if (t instanceof NullPointerException) latch.countDown(); } }, system.dispatcher()); Throwable exception = new NullPointerException(); cf.failure(exception); assertTrue(latch.await(5, TimeUnit.SECONDS)); assertEquals(f.value().get().failed().get(), exception); } @Test public void mustBeAbleToExecuteAnOnCompleteCallback() throws Throwable { final CountDownLatch latch = new CountDownLatch(1); Promise cf = Futures.promise(); Future f = cf.future(); f.onComplete(new OnComplete() { public void onComplete(Throwable t, String r) { latch.countDown(); } }, system.dispatcher()); cf.success("foo"); assertTrue(latch.await(5, TimeUnit.SECONDS)); assertEquals(Await.result(f, timeout), "foo"); } @Test public void mustBeAbleToForeachAFuture() throws Throwable { final CountDownLatch latch = new CountDownLatch(1); Promise cf = Futures.promise(); Future f = cf.future(); f.foreach(new Foreach() { public void each(String future) { latch.countDown(); } },system.dispatcher()); cf.success("foo"); assertTrue(latch.await(5, TimeUnit.SECONDS)); assertEquals(Await.result(f, timeout), "foo"); } @Test public void mustBeAbleToFlatMapAFuture() throws Throwable { final CountDownLatch latch = new CountDownLatch(1); Promise cf = Futures.promise(); cf.success("1000"); Future f = cf.future(); Future r = f.flatMap(new Mapper>() { public Future checkedApply(String r) throws Throwable { if (false) throw new IOException("Just here to make sure this compiles."); latch.countDown(); Promise cf = Futures.promise(); cf.success(Integer.parseInt(r)); return cf.future(); } }, system.dispatcher()); assertEquals(Await.result(f, timeout), "1000"); assertEquals(Await.result(r, timeout).intValue(), 1000); assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void mustBeAbleToFilterAFuture() throws Throwable { final CountDownLatch latch = new CountDownLatch(1); Promise cf = Futures.promise(); Future f = cf.future(); Future r = f.filter(Filter.filterOf(new Function() { public Boolean apply(String r) { latch.countDown(); return r.equals("foo"); } }), system.dispatcher()); cf.success("foo"); assertTrue(latch.await(5, TimeUnit.SECONDS)); assertEquals(Await.result(f, timeout), "foo"); assertEquals(Await.result(r, timeout), "foo"); } // TODO: Improve this test, perhaps with an Actor @Test public void mustSequenceAFutureList() throws Exception{ LinkedList> listFutures = new LinkedList>(); LinkedList listExpected = new LinkedList(); for (int i = 0; i < 10; i++) { listExpected.add("test"); listFutures.add(Futures.future(new Callable() { public String call() { return "test"; } }, system.dispatcher())); } Future> futureList = Futures.sequence(listFutures, system.dispatcher()); assertEquals(Await.result(futureList, timeout), listExpected); } // TODO: Improve this test, perhaps with an Actor @Test public void foldForJavaApiMustWork() throws Exception{ LinkedList> listFutures = new LinkedList>(); StringBuilder expected = new StringBuilder(); for (int i = 0; i < 10; i++) { expected.append("test"); listFutures.add(Futures.future(new Callable() { public String call() { return "test"; } }, system.dispatcher())); } Future result = Futures.fold("", listFutures, new Function2() { public String apply(String r, String t) { return r + t; } }, system.dispatcher()); assertEquals(Await.result(result, timeout), expected.toString()); } @Test public void reduceForJavaApiMustWork() throws Exception{ LinkedList> listFutures = new LinkedList>(); StringBuilder expected = new StringBuilder(); for (int i = 0; i < 10; i++) { expected.append("test"); listFutures.add(Futures.future(new Callable() { public String call() { return "test"; } }, system.dispatcher())); } Future result = Futures.reduce(listFutures, new Function2() { public String apply(String r, String t) { return r + t; } }, system.dispatcher()); assertEquals(Await.result(result, timeout), expected.toString()); } @Test public void traverseForJavaApiMustWork() throws Exception{ LinkedList listStrings = new LinkedList(); LinkedList expectedStrings = new LinkedList(); for (int i = 0; i < 10; i++) { expectedStrings.add("TEST"); listStrings.add("test"); } Future> result = Futures.traverse(listStrings, new Function>() { public Future apply(final String r) { return Futures.future(new Callable() { public String call() { return r.toUpperCase(); } }, system.dispatcher()); } }, system.dispatcher()); assertEquals(Await.result(result, timeout), expectedStrings); } @Test public void findForJavaApiMustWork() throws Exception{ LinkedList> listFutures = new LinkedList>(); for (int i = 0; i < 10; i++) { final Integer fi = i; listFutures.add(Futures.future(new Callable() { public Integer call() { return fi; } }, system.dispatcher())); } final Integer expect = 5; Future> f = Futures.find(listFutures, new Function() { public Boolean apply(Integer i) { return i == 5; } }, system.dispatcher()); assertEquals(expect, Await.result(f, timeout).get()); } @Test public void blockMustBeCallable() throws Exception { Promise p = Futures.promise(); Duration d = Duration.create(1, TimeUnit.SECONDS); p.success("foo"); Await.ready(p.future(), d); assertEquals(Await.result(p.future(), d), "foo"); } @Test public void mapToMustBeCallable() throws Exception { Promise p = Futures.promise(); Future f = p.future().mapTo(classTag(String.class)); Duration d = Duration.create(1, TimeUnit.SECONDS); p.success("foo"); Await.ready(p.future(), d); assertEquals(Await.result(p.future(), d), "foo"); } @Test public void recoverToMustBeCallable() throws Exception { final IllegalStateException fail = new IllegalStateException("OHNOES"); Promise p = Futures.promise(); Future f = p.future().recover(new Recover() { public Object recover(Throwable t) throws Throwable { if (t == fail) return "foo"; else throw t; } }, system.dispatcher()); Duration d = Duration.create(1, TimeUnit.SECONDS); p.failure(fail); assertEquals(Await.result(f, d), "foo"); } @Test public void recoverWithToMustBeCallable() throws Exception{ final IllegalStateException fail = new IllegalStateException("OHNOES"); Promise p = Futures.promise(); Future f = p.future().recoverWith(new Recover>() { public Future recover(Throwable t) throws Throwable { if (t == fail) return Futures.successful("foo"); else throw t; } }, system.dispatcher()); Duration d = Duration.create(1, TimeUnit.SECONDS); p.failure(fail); assertEquals(Await.result(f, d), "foo"); } }