/* # Licensed Materials - Property of IBM # Copyright IBM Corp. 2015 */ package com.ibm.streamsx.topology.test.api; import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; import org.junit.Test; import com.ibm.streamsx.topology.TStream; import com.ibm.streamsx.topology.Topology; import com.ibm.streamsx.topology.context.StreamsContext; import com.ibm.streamsx.topology.context.StreamsContextFactory; import com.ibm.streamsx.topology.function.Function; import com.ibm.streamsx.topology.function.FunctionContext; import com.ibm.streamsx.topology.function.Initializable; import com.ibm.streamsx.topology.function.UnaryOperator; import com.ibm.streamsx.topology.test.TestTopology; import com.ibm.streamsx.topology.tester.Condition; import com.ibm.streamsx.topology.tester.Tester; public class IsolateTest extends TestTopology { @Test public void simpleIsolationTest() throws Exception { assumeTrue(SC_OK); assumeTrue(getTesterType() == StreamsContext.Type.DISTRIBUTED_TESTER); Topology topology = newTopology("simpleIsolationTest"); // Construct topology TStream<String> ss = topology.strings("hello"); TStream<String> ss1 = ss.transform(getContainerId()).isolate(); TStream<String> ss2 = ss.isolate().transform(getContainerId()) .isolate(); Tester tester = topology.getTester(); Condition<List<String>> condss1 = tester.stringContents(ss1, ""); Condition<List<String>> condss2 = tester.stringContents(ss2, ""); Condition<Long> condss1Cnt = tester.tupleCount(ss1, 1); Condition<Long> condss2Cnt = tester.tupleCount(ss2, 1); Condition<Long> endCond = new MultiLongCondition(Arrays.asList(condss1Cnt, condss2Cnt)); complete(topology.getTester(), endCond, 15, TimeUnit.SECONDS); Integer result1 = Integer.parseInt(condss1.getResult().get(0)); Integer result2 = Integer.parseInt(condss2.getResult().get(0)); Set<Integer> m = new HashSet<>(); m.add(result1); m.add(result2); assertEquals(2, m.size()); } @Test public void isolateIsEndOfStreamTest() throws Exception { assumeTrue(SC_OK); assumeTrue(isMainRun()); Topology topology = newTopology("isolateIsEndOfStreamTest"); // Construct topology TStream<String> ss = topology.strings("hello"); TStream<String> ss1 = topology.strings("hello"); TStream<String> un = ss.union(ss1); un.isolate(); StreamsContextFactory.getStreamsContext(StreamsContext.Type.TOOLKIT) .submit(topology).get(); } @Test public void multipleIsolationTest() throws Exception { assumeTrue(SC_OK); assumeTrue(isMainRun()); Topology topology = newTopology("multipleIsolationTest"); TStream<String> ss = topology.strings("hello", "world"); TStream<String> ss0 = ss.isolate(); TStream<String> ss1 = ss0.transform(getContainerId()); ss1.isolate().transform(getContainerId()) .transform(getContainerId()).print(); TStream<String> ss3 = ss.transform(getContainerId()).isolate(); TStream<String> ss4 = ss3.transform(getContainerId()).isolate(); TStream<String> ss5 = ss4.transform(getContainerId()).isolate(); ss5.transform(getContainerId()).print(); TStream<String> ss7 = ss3.transform(getContainerId()); StreamsContextFactory.getStreamsContext(StreamsContext.Type.TOOLKIT) .submit(topology).get(); } /** * Test that a topology fails to generate SPL if an isolated stream is * unioned with its parent. * @throws Exception Thrown because the ss4 stream is the parent of the ss7 * stream. Taking the union of the two is currently not supported. In * future releases, we will automatically insert an isolation marker to * support this kind of union. */ @Test(expected = IllegalStateException.class) public void multipleIsolationExceptionTest() throws Exception { assumeTrue(SC_OK); assumeTrue(isMainRun()); Topology topology = newTopology("multipleIsolationExceptionTest"); TStream<String> ss = topology.strings("hello", "world"); TStream<String> ss0 = ss.isolate(); TStream<String> ss1 = ss0.transform(getContainerId()); ss1.isolate().transform(getContainerId()) .transform(getContainerId()).print(); TStream<String> ss3 = ss.transform(getContainerId()).isolate(); TStream<String> ss4 = ss3.transform(getContainerId()).isolate(); TStream<String> ss5 = ss4.transform(getContainerId()).isolate(); ss5.transform(getContainerId()).print(); TStream<String> ss7 = ss3.transform(getContainerId()); // Unions a stream with its parent. ss7.union(ss4).print(); StreamsContextFactory.getStreamsContext(StreamsContext.Type.TOOLKIT) .submit(topology).get(); } @Test public void islandIsolationTest() throws Exception { assumeTrue(SC_OK); assumeTrue(isMainRun()); Topology topology = newTopology("islandIsolationTest"); TStream<String> ss = topology.strings("hello", "world"); ss.transform(getContainerId()).isolate() .transform(getContainerId()); // Create island subgraph TStream<String> ss2 = topology.strings("hello", "world"); ss2.transform(getContainerId()).print(); StreamsContextFactory.getStreamsContext(StreamsContext.Type.TOOLKIT) .submit(topology).get(); } @Test public void unionIsolateTest() throws Exception { assumeTrue(SC_OK); assumeTrue(isMainRun()); Topology topology = newTopology("unionIsolateTest"); TStream<String> s1 = topology.strings("1"); TStream<String> s2 = topology.strings("2"); TStream<String> s3 = topology.strings("3"); TStream<String> s4 = topology.strings("4"); Set<TStream<String>> l = new HashSet<>(); l.add(s1); l.add(s2); l.add(s3); l.add(s4); TStream<String> n = s1.union(l).isolate(); n.print(); n.print(); n.print(); n.print(); Tester tester = topology.getTester(); Condition<Long> expectedCount = tester.tupleCount(n, 4); Condition<List<String>> expectedContent = tester .stringContentsUnordered(n, "1", "2", "3", "4"); StreamsContextFactory.getStreamsContext(StreamsContext.Type.TOOLKIT) .submit(topology).get(); // assertTrue(expectedCount.valid()); // assertTrue(expectedContent.valid()); } @Test(expected = IllegalStateException.class) public void lowLatencyViolationTest() throws Exception { assumeTrue(isMainRun()); /** lowLatency -> ... isolate ... -> endLowLatency */ final Topology topology = newTopology("lowLatencyViolationTest"); topology.strings("a") .lowLatency() .modify(getContainerIdAppend()) .isolate() // expect ISE: not legal in a low latency region .modify(getContainerIdAppend()) .endLowLatency() ; } /** * Get the container ids from a tuple of the form produced with * getContainerIdAgg() - i.e. <some-tag> <id1> [<id2> ...] * @param results * @return */ public static Set<String> getContainerIds(List<String> results) { Set<String> ids = new HashSet<>(); for (String s : results) { boolean first = true; for (String stok : s.split(" ")) { if (first) { first = false; continue; } // see GetContainerIdAndChannelAppend String[] idParts = stok.split("::ch-"); ids.add(idParts[0]); // just the container id } } return ids; } public static Function<String, String> getContainerId() { return new GetContainerId(); } @SuppressWarnings("serial") public static final class GetContainerId implements Function<String, String> , Initializable { private String id; @Override public String apply(String v) { return id; } @Override public void initialize(FunctionContext functionContext) throws Exception { id = functionContext.getContainer().getId(); } } /** * Create a UnaryOperator function that appends the fn's container id * onto the tuple's value. * @return the function */ public static UnaryOperator<String> getContainerIdAppend() { return new GetContainerIdAppend(); } /** * A UnaryOperator that appends the fn's container id onto the tuple's value. */ @SuppressWarnings("serial") public static final class GetContainerIdAppend implements UnaryOperator<String> , Initializable { private String id; @Override public String apply(String v) { return v + " " + id; } @Override public void initialize(FunctionContext functionContext) throws Exception { id = functionContext.getContainer().getId(); } } /** * Create a UnaryOperator function that appends the fn's container id * and channel onto the tuple's value. * @return the function */ public static UnaryOperator<String> getContainerIdAndChannelAppend() { return new GetContainerIdAppend(); } /** * A UnaryOperator that appends the fn's container id and parallel channel onto the tuple's value. */ @SuppressWarnings("serial") public static final class GetContainerIdAndChannelAppend implements UnaryOperator<String> , Initializable { private String id; private int channel; @Override public String apply(String v) { return String.format("%s %s::ch-%d", v, id, channel); } @Override public void initialize(FunctionContext functionContext) throws Exception { id = functionContext.getContainer().getId(); channel = functionContext.getChannel(); } } }