/** * Copyright (C) 2015 Typesafe Inc. */ package akka.stream.javadsl; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; import org.junit.ClassRule; import org.junit.Test; import scala.concurrent.Await; import scala.concurrent.Future; import scala.concurrent.duration.Duration; import scala.concurrent.duration.FiniteDuration; import scala.runtime.BoxedUnit; import akka.japi.Pair; import akka.stream.*; import akka.stream.testkit.AkkaSpec; import akka.stream.javadsl.FlowGraph.Builder; import akka.japi.function.*; import akka.util.ByteString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertArrayEquals; public class BidiFlowTest extends StreamTest { public BidiFlowTest() { super(actorSystemResource); } @ClassRule public static AkkaJUnitActorSystemResource actorSystemResource = new AkkaJUnitActorSystemResource( "FlowTest", AkkaSpec.testConf()); private final BidiFlow bidi = BidiFlow .factory() .create( new Function, BidiShape>() { @Override public BidiShape apply(Builder b) throws Exception { final FlowShape top = b.graph(Flow . empty().map(new Function() { @Override public Long apply(Integer arg) { return (long) ((int) arg) + 2; } })); final FlowShape bottom = b.graph(Flow . empty().map(new Function() { @Override public String apply(ByteString arg) { return arg.decodeString("UTF-8"); } })); return new BidiShape(top .inlet(), top.outlet(), bottom.inlet(), bottom.outlet()); } }); private final BidiFlow inverse = BidiFlow .factory() .create( new Function, BidiShape>() { @Override public BidiShape apply(Builder b) throws Exception { final FlowShape top = b.graph(Flow. empty() .map(new Function() { @Override public Integer apply(Long arg) { return (int) ((long) arg) + 2; } })); final FlowShape bottom = b.graph(Flow . empty().map(new Function() { @Override public ByteString apply(String arg) { return ByteString.fromString(arg); } })); return new BidiShape(top .inlet(), top.outlet(), bottom.inlet(), bottom.outlet()); } }); private final BidiFlow> bidiMat = BidiFlow .factory() .create( Sink. head(), new Function2>, SinkShape, BidiShape>() { @Override public BidiShape apply(Builder> b, SinkShape sink) throws Exception { b.from(Source.single(42)).to(sink); final FlowShape top = b.graph(Flow . empty().map(new Function() { @Override public Long apply(Integer arg) { return (long) ((int) arg) + 2; } })); final FlowShape bottom = b.graph(Flow . empty().map(new Function() { @Override public String apply(ByteString arg) { return arg.decodeString("UTF-8"); } })); return new BidiShape(top .inlet(), top.outlet(), bottom.inlet(), bottom.outlet()); } }); private final String str = "Hello World"; private final ByteString bytes = ByteString.fromString(str); private final List list = new ArrayList(); { list.add(1); list.add(2); list.add(3); } private final FiniteDuration oneSec = Duration.create(1, TimeUnit.SECONDS); @Test public void mustWorkInIsolation() throws Exception { final Pair, Future> p = FlowGraph .factory() .closed(Sink. head(), Sink. head(), Keep., Future> both(), new Procedure3, Future>>, SinkShape, SinkShape>() { @Override public void apply(Builder, Future>> b, SinkShape st, SinkShape sb) throws Exception { final BidiShape s = b .graph(bidi); b.from(Source.single(1)).to(s.in1()); b.from(s.out1()).to(st); b.from(Source.single(bytes)).to(s.in2()); b.from(s.out2()).to(sb); } }).run(materializer); final Long rt = Await.result(p.first(), oneSec); final String rb = Await.result(p.second(), oneSec); assertEquals((Long) 3L, rt); assertEquals(str, rb); } @Test public void mustWorkAsAFlowThatIsOpenOnTheLeft() throws Exception { final Flow f = bidi.join(Flow. empty().map( new Function() { @Override public ByteString apply(Long arg) { return ByteString.fromString("Hello " + arg); } })); final Future> result = Source.from(list).via(f).grouped(10).runWith(Sink.> head(), materializer); assertEquals(Arrays.asList("Hello 3", "Hello 4", "Hello 5"), Await.result(result, oneSec)); } @Test public void mustWorkAsAFlowThatIsOpenOnTheRight() throws Exception { final Flow f = Flow. empty().map( new Function() { @Override public Integer apply(String arg) { return Integer.valueOf(arg); } }).join(bidi); final List inputs = Arrays.asList(ByteString.fromString("1"), ByteString.fromString("2")); final Future> result = Source.from(inputs).via(f).grouped(10).runWith(Sink.> head(), materializer); assertEquals(Arrays.asList(3L, 4L), Await.result(result, oneSec)); } @Test public void mustWorkWhenAtopItsInverse() throws Exception { final Flow f = bidi.atop(inverse).join(Flow. empty().map( new Function() { @Override public String apply(Integer arg) { return arg.toString(); } })); final Future> result = Source.from(list).via(f).grouped(10).runWith(Sink.> head(), materializer); assertEquals(Arrays.asList("5", "6", "7"), Await.result(result, oneSec)); } @Test public void mustWorkWhenReversed() throws Exception { final Flow f = Flow. empty().map( new Function() { @Override public String apply(Integer arg) { return arg.toString(); } }).join(inverse.reversed()).join(bidi.reversed()); final Future> result = Source.from(list).via(f).grouped(10).runWith(Sink.> head(), materializer); assertEquals(Arrays.asList("5", "6", "7"), Await.result(result, oneSec)); } @Test public void mustMaterializeToItsValue() throws Exception { final Future f = FlowGraph.factory().closed(bidiMat, new Procedure2 >, BidiShape>() { @Override public void apply(Builder> b, BidiShape shape) throws Exception { final FlowShape left = b.graph(Flow. empty().map( new Function() { @Override public Integer apply(String arg) { return Integer.valueOf(arg); } })); final FlowShape right = b.graph(Flow. empty().map( new Function() { @Override public ByteString apply(Long arg) { return ByteString.fromString("Hello " + arg); } })); b.from(shape.out2()).via(left).to(shape.in1()) .from(shape.out1()).via(right).to(shape.in2()); } }).run(materializer); assertEquals((Integer) 42, Await.result(f, oneSec)); } @Test public void mustCombineMaterializationValues() throws Exception { final Flow> left = Flow.factory().create( Sink. head(), new Function2 >, SinkShape, Pair, Outlet>>() { @Override public Pair, Outlet> apply(Builder> b, SinkShape sink) throws Exception { final UniformFanOutShape bcast = b.graph(Broadcast. create(2)); final UniformFanInShape merge = b.graph(Merge. create(2)); final FlowShape flow = b.graph(Flow. empty().map( new Function() { @Override public Integer apply(String arg) { return Integer.valueOf(arg); } })); b.from(bcast).to(sink) .from(Source.single(1)).via(bcast).to(merge) .from(flow).to(merge); return new Pair, Outlet>(flow.inlet(), merge.out()); } }); final Flow>> right = Flow.factory().create( Sink.> head(), new Function2>>, SinkShape>, Pair, Outlet>>() { @Override public Pair, Outlet> apply(Builder>> b, SinkShape> sink) throws Exception { final FlowShape> flow = b.graph(Flow. empty().grouped(10)); b.from(flow).to(sink); return new Pair, Outlet>(flow.inlet(), b.source(Source.single(ByteString.fromString("10")))); } }); final Pair, Future>, Future>> result = left.joinMat(bidiMat, Keep., Future> both()).joinMat(right, Keep., Future>, Future>> both()).run(materializer); final Future l = result.first().first(); final Future m = result.first().second(); final Future> r = result.second(); assertEquals((Integer) 1, Await.result(l, oneSec)); assertEquals((Integer) 42, Await.result(m, oneSec)); final Long[] rr = Await.result(r, oneSec).toArray(new Long[0]); Arrays.sort(rr); assertArrayEquals(new Long[] { 3L, 12L }, rr); } }