/* # Licensed Materials - Property of IBM # Copyright IBM Corp. 2015 */ package com.ibm.streamsx.topology.tester; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import com.ibm.streams.flow.handlers.StreamHandler; import com.ibm.streams.operator.Tuple; import com.ibm.streamsx.topology.TStream; import com.ibm.streamsx.topology.Topology; import com.ibm.streamsx.topology.context.StreamsContext; import com.ibm.streamsx.topology.function.Predicate; import com.ibm.streamsx.topology.spl.SPLStream; /** * A {@code Tester} adds the ability to test a topology in a test * framework such as JUnit. * * The main feature is the ability to capture tuples from a * {@link TStream} in order to perform some form of verification * on them. There are two mechanisms to perform verifications: * <UL> * <LI>{@link Condition} - Provides the ability to check if a common pattern is {@link Condition#valid() valid}, such as did the stream * produce the {@link #tupleCount(TStream, long) correct number of tuples}.</LI> * <LI>{@link Tester#splHandler(SPLStream, StreamHandler) StreamHandler} - Provides the ability * to add an arbitrary handler to a stream, that will be called for every tuple on the stream. A number of implementations of * {@code StreamHandler} are in the {@code com.ibm.streams.flow.handlers} provided by the IBM Streams Java Operator API.</LI> * </UL> * The stream being verified must not be connected, but may have multiple conditions or handlers added. * <BR> * Currently, only streams that are instances of {@link SPLStream} or {@code TStream<String>} can have conditions * or handlers attached. * <P> * A {@code Tester} only modifies its {@link Topology} if the topology * is submitted to a tester {@link StreamsContext}, of type * {@link com.ibm.streamsx.topology.context.StreamsContext.Type#EMBEDDED_TESTER}, * {@link com.ibm.streamsx.topology.context.StreamsContext.Type#STANDALONE_TESTER}, * {@link com.ibm.streamsx.topology.context.StreamsContext.Type#DISTRIBUTED_TESTER}. * </P> * <P> * When running a test using {@link com.ibm.streamsx.topology.context.StreamsContext.Type#STANDALONE_TESTER} * or {@link com.ibm.streamsx.topology.context.StreamsContext.Type#DISTRIBUTED_TESTER} a TCP server is setup * within the JVM running the test (e.g. the JVM running the JUnit tests} and the topology is modified to * send tuples from streams being tested to the TCP server. The port used by the TCP server is in the * ephemeral port range. * </P> */ public interface Tester { /** * Get the topology for this tester. * @return the topology for this tester. */ Topology getTopology(); /** * Adds {@code handler} to capture the output of {@code stream}. * @param stream Stream to have its tuples captured. * @param handler {@code StreamHandler} to capture tuples. * @return {@code handler} */ <T extends StreamHandler<Tuple>> T splHandler(SPLStream stream, T handler); /** * Return a condition that evaluates if {@code stream} has submitted * exactly {@code expectedCount} number of tuples. The function may be evaluated * after the * {@link com.ibm.streamsx.topology.context.StreamsContext#submit(com.ibm.streamsx.topology.Topology) * submit} call has returned. * <BR> * The {@link Condition#getResult() result} of the returned {@code Condition} is the number of * tuples seen on {@code stream} so far. * <BR> * If the topology is still executing then the returned values from {@link Condition#valid()} * and {@link Condition#getResult()} may change as more tuples are seen on {@code stream}. * <BR> * * @param stream * Stream to be tested. * @param expectedCount * Number of tuples expected on {@code stream}. * @return True if the stream has submitted exactly {@code expectedCount} number of * tuples, false otherwise. */ Condition<Long> tupleCount(TStream<?> stream, long expectedCount); /** * Return a condition that evaluates if {@code stream} has submitted * at least {@code expectedCount} number of tuples. The function may be evaluated * after the * {@link com.ibm.streamsx.topology.context.StreamsContext#submit(com.ibm.streamsx.topology.Topology) * submit} call has returned. * <BR> * The {@link Condition#getResult() result} of the returned {@code Condition} is the number of * tuples seen on {@code stream} so far. * <BR> * If the topology is still executing then the returned values from {@link Condition#valid()} * and {@link Condition#getResult()} may change as more tuples are seen on {@code stream}. * <BR> * * @param stream * Stream to be tested. * @param expectedCount * Number of tuples expected on {@code stream}. * @return True if the stream has submitted at least {@code expectedCount} number of * tuples, false otherwise. */ Condition<Long> atLeastTupleCount(TStream<?> stream, long expectedCount); /** * Return a condition that evaluates if {@code stream} has submitted * at tuples matching {@code values} in the same order. * <BR> * The {@link Condition#getResult() result} of the returned {@code Condition} is the * tuples seen on {@code stream} so far. * <BR> * If the topology is still executing then the returned values from {@link Condition#valid()} * and {@link Condition#getResult()} may change as more tuples are seen on {@code stream}. * <BR> * * @param stream * Stream to be tested. * @param values * Expected tuples on {@code stream}. * @return True if the stream has submitted at least tuples matching {@code values} in * the same order, false otherwise. */ Condition<List<String>> stringContents(TStream<String> stream, String... values); Condition<List<Tuple>> tupleContents(SPLStream stream, Tuple... values); /** * Return a condition that evaluates if {@code stream} has submitted * at tuples matching {@code values} in any order. * <BR> * The {@link Condition#getResult() result} of the returned {@code Condition} is the * tuples seen on {@code stream} so far. * <BR> * If the topology is still executing then the returned values from {@link Condition#valid()} * and {@link Condition#getResult()} may change as more tuples are seen on {@code stream}. * <BR> * * @param stream * Stream to be tested. * @param values * Expected tuples on {@code stream}. * @return True if the stream has submitted at least tuples matching {@code values} in * the any order, false otherwise. */ Condition<List<String>> stringContentsUnordered(TStream<String> stream, String... values); Condition<String> stringTupleTester(TStream<String> stream, Predicate<String> tester); /** * Submit the topology for this tester and wait for it to complete. * A topology can only complete if it is executing as embedded or * standalone, thus only these stream context types are supported: * {@link com.ibm.streamsx.topology.context.StreamsContext.Type#EMBEDDED_TESTER} * and * {@link com.ibm.streamsx.topology.context.StreamsContext.Type#STANDALONE_TESTER}. * <P> * A topology completes when the IBM Streams runtime determines * that there is no more processing to be performed, which is typically once * all sources have indicated there is no more data to be processed, * and all of the source tuples have been fully processed by the topology. * <BR> * Note that many topologies will never complete, for example those * including polling or event sources. In this case a test case * should use {@link #complete(StreamsContext, Condition, long, TimeUnit)}. * </P> * * @param context Context to be used for submission. * * @throws Exception Failure submitting or executing the topology. * @throws IllegalStateException {@link com.ibm.streamsx.topology.context.StreamsContext#getType()} is not supported. */ void complete(StreamsContext<?> context) throws Exception ; /** * Submit the topology for this tester and wait for it to complete, * or reach an end condition. If the topology does not complete * or reach its end condition before {@code timeout} then it is * terminated. * <BR> * This is suitable for testing topologies that never complete * or any topology running in a {@link com.ibm.streamsx.topology.context.StreamsContext.Type#DISTRIBUTED_TESTER distributed} context. * <P> * End condition is usually a {@link Condition} returned from * {@link #atLeastTupleCount(TStream, long)} or {@link #tupleCount(TStream, long)} * so that this method returns once the stream has submitted a sufficient number of tuples. * <BR> * Note that the condition will be only checked periodically up to {@code timeout}, * so that if the condition is only valid for a brief period of time, then its * valid state may not be seen, and thus this method will wait for the timeout period. * </P> * * @param context Context to be used for submission. * @param endCondition Condition that will cause this method to return if it is true. * @param timeout Maximum time to wait for the topology to complete or reach its end condition. * @param unit Unit for {@code timeout}. * @return The value of {@code endCondition.valid()}. * * @throws Exception Failure submitting or executing the topology. * @throws IllegalStateException {@link com.ibm.streamsx.topology.context.StreamsContext#getType()} is not supported. * * @see #complete(StreamsContext) */ boolean complete(StreamsContext<?> context, Condition<?> endCondition, long timeout, TimeUnit unit) throws Exception; boolean complete(StreamsContext<?> context, Map<String, Object> config, Condition<?> endCondition, long timeout, TimeUnit unit) throws Exception; Condition<List<String>> completeAndTestStringOutput(StreamsContext<?> context, TStream<?> output, long timeout, TimeUnit unit, String...contents) throws Exception; Condition<List<String>> completeAndTestStringOutput(StreamsContext<?> context, Map<String, Object> config, TStream<?> output, long timeout, TimeUnit unit, String...contents) throws Exception; }