/*
# Licensed Materials - Property of IBM
# Copyright IBM Corp. 2015
*/
package com.ibm.streamsx.topology.test;
import static com.ibm.streamsx.topology.context.StreamsContextFactory.getStreamsContext;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import static org.junit.Assert.assertNotNull;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import org.junit.Before;
import org.junit.BeforeClass;
import com.ibm.streams.operator.Tuple;
import com.ibm.streams.operator.version.Product;
import com.ibm.streamsx.topology.TStream;
import com.ibm.streamsx.topology.Topology;
import com.ibm.streamsx.topology.context.ContextProperties;
import com.ibm.streamsx.topology.context.StreamsContext;
import com.ibm.streamsx.topology.context.StreamsContext.Type;
import com.ibm.streamsx.topology.context.StreamsContextFactory;
import com.ibm.streamsx.topology.spl.SPLStream;
import com.ibm.streamsx.topology.tester.Condition;
import com.ibm.streamsx.topology.tester.Tester;
/**
* Root class for topology tests.
*
*/
public class TestTopology {
private static StreamsContext.Type testerType = Type.EMBEDDED_TESTER;
@BeforeClass
public static void setTesterType() {
String testerTypeString = System.getProperty("topology.test.type");
if (testerTypeString != null) {
testerType = StreamsContext.Type.valueOf(testerTypeString);
}
}
private static File TEST_ROOT;
@BeforeClass
public static void setTesterRoot() {
String testerRoot = System.getProperty("topology.test.root");
if (testerRoot != null) {
TEST_ROOT = new File(testerRoot);
assertTrue(TEST_ROOT.getPath(), TEST_ROOT.isAbsolute());
assertTrue(TEST_ROOT.getPath(), TEST_ROOT.exists());
}
}
public static File getTestRoot() {
return TEST_ROOT;
}
private final Map<String,Object> config = new HashMap<>();
@Before
public void setupConfig() {
List<String> vmArgs = new ArrayList<>();
config.put(ContextProperties.VMARGS, vmArgs);
if (getTesterType() != Type.EMBEDDED_TESTER) {
File agentJar = new File(System.getProperty("user.home"), ".ant/lib/jacocoagent.jar");
if (agentJar.exists()) {
String now = Long.toHexString(System.currentTimeMillis());
String destFile = "jacoco_" + getTesterType().name() + now + ".exec";
String arg = "-javaagent:"
+ agentJar.getAbsolutePath()
+ "=destfile="
+ destFile;
vmArgs.add(arg);
}
}
// Look for a different compiler
String differentCompile = System.getProperty(ContextProperties.COMPILE_INSTALL_DIR);
if (differentCompile != null) {
config.put(ContextProperties.COMPILE_INSTALL_DIR, differentCompile);
Topology.STREAMS_LOGGER.setLevel(Level.INFO);
}
}
private static final AtomicInteger topoCounter = new AtomicInteger();
private static final String baseName = UUID.randomUUID().toString().replace('-', '_');
/**
* Create a new topology with a unique name.
*/
protected static Topology newTopology() {
Topology t = new Topology();
return newTopology(t.getName());
}
/**
* Create a new topology with a unique name based upon the passed in name.
*/
protected static Topology newTopology(String name) {
return new Topology(name + "_" + topoCounter.getAndIncrement() + "_" + baseName);
}
/**
* Get the default tester type.
*
* @return
*/
public StreamsContext.Type getTesterType() {
return testerType;
}
public boolean isEmbedded() {
return getTesterType() == Type.EMBEDDED_TESTER;
}
/**
* The main run of tests will be with EMBEDDED_TESTER This allows tests to
* be only run once, with the main run.
*/
public boolean isMainRun() {
return getTesterType() == Type.EMBEDDED_TESTER;
}
public Map<String,Object> getConfig() {
return config;
}
private int startupDelay = 20;
public void setStartupDelay(int delay) {
startupDelay = delay;
}
public int getStartupDelay() {
int additional = 0;
String startupDelayS = System.getProperty("topology.test.additionalStartupDelay");
if (startupDelayS != null) {
try {
additional = Integer.valueOf(startupDelayS);
} catch (NumberFormatException e) {
;
}
}
return startupDelay + additional;
}
/**
* Adds a startup delay based upon the context.
* @param stream
* @return
*/
public <T,S> TStream<T> addStartupDelay(TStream<T> stream) {
if (getTesterType() == Type.DISTRIBUTED_TESTER) {
return stream.modify(new InitialDelay<T>(getStartupDelay()*1000L));
}
return stream;
}
public SPLStream addStartupDelay(SPLStream stream) {
if (getTesterType() == Type.DISTRIBUTED_TESTER) {
return stream.modify(new InitialDelay<Tuple>(getStartupDelay()*1000L));
}
return stream;
}
/**
* Return the default context for tests. Using this allows the tests to be
* run against different contexts, assuming the results/asserts are expected
* to be the same.
*/
public StreamsContext<?> getTesterContext() {
return StreamsContextFactory.getStreamsContext(getTesterType());
}
public void complete(Tester tester) throws Exception {
tester.complete(getTesterContext());
}
/**
* Test a topology that may run forever.
* If endCondition is null then:
*
* In a distributed environment the
*/
public boolean complete(Tester tester, Condition<?> endCondition, long timeout, TimeUnit unit) throws Exception {
return tester.complete(getTesterContext(), getConfig(), endCondition, timeout, unit);
}
/**
* Once Junit has been upgraded, by default any sub-class with tests will
* only be run by the EMBEDDED_TESTER by default. A sub-class overrides this
* to run in multiple modes (to be tested to see if this idea will actually
* work!).
*/
@Before
public void runOnce() {
// assumeTrue(isMainRun());
}
/**
* Assume check field for compiling bundles with sc
*/
public static final boolean SC_OK = Boolean
.getBoolean("topology.test.sc_ok");
/**
* Assume check field for performance tests.
*/
public static final boolean PERF_OK = Boolean
.getBoolean("topology.test.perf_ok");
protected void assumeSPLOk() {
assumeTrue(getTesterType() != StreamsContext.Type.EMBEDDED_TESTER);
assumeTrue(SC_OK);
}
/**
* Only run a test at a specific minimum version or higher.
*/
protected void checkMinimumVersion(String reason, int... vrmf) {
switch (vrmf.length) {
case 2:
assumeTrue((Product.getVersion().getVersion() > vrmf[0])
|| (Product.getVersion().getVersion() == vrmf[0] &&
Product.getVersion().getRelease() >= vrmf[1]));
break;
case 1:
assumeTrue(Product.getVersion().getVersion() >= vrmf[0]);
break;
default:
fail("Invalid version supplied!");
}
}
/**
* Only run a test at a specific maximum version or lower.
*/
protected void checkMaximumVersion(String reason, int... vrmf) {
switch (vrmf.length) {
case 2:
assumeTrue((Product.getVersion().getVersion() < vrmf[0])
|| (Product.getVersion().getVersion() == vrmf[0] &&
Product.getVersion().getRelease() <= vrmf[1]));
break;
case 1:
assumeTrue(Product.getVersion().getVersion() <= vrmf[0]);
break;
default:
fail("Invalid version supplied!");
}
}
/**
* Allow a test to be skipped for a specific version.
*/
protected void skipVersion(String reason, int ...vrmf) {
switch (vrmf.length) {
case 4:
assumeTrue(Product.getVersion().getFix() != vrmf[3]);
case 3:
assumeTrue(Product.getVersion().getMod() != vrmf[2]);
case 2:
assumeTrue(Product.getVersion().getRelease() != vrmf[1]);
case 1:
assumeTrue(Product.getVersion().getVersion() != vrmf[0]);
break;
default:
fail("Invalid version supplied!");
}
}
public void completeAndValidate(TStream<?> output, int seconds, String...contents) throws Exception {
completeAndValidate(getConfig(), output, seconds, contents);
}
public void completeAndValidate(Map<String,Object> config,
TStream<?> output, int seconds, String...contents) throws Exception {
Tester tester = output.topology().getTester();
if (getTesterType() == Type.DISTRIBUTED_TESTER)
seconds += getStartupDelay();
Condition<List<String>> expectedContents = tester.completeAndTestStringOutput(
getTesterContext(),
config,
output,
seconds, TimeUnit.SECONDS,
contents);
assertTrue(expectedContents.toString(), expectedContents.valid());
}
/**
* Return a condition that is true if all conditions are valid.
* The result is a Boolean that indicates if the condition is valid.
* @param conditions
* @return
*/
public static Condition<Boolean> allConditions(final Condition<?> ...conditions) {
return Condition.all(conditions);
}
/**
* Allows a test to perform a IBM Streams BUNDLE build only.
*
* If a test requires specific configuration & external setup
* to run then it should use this method to test at least
* the topology can built into a IBM Streams bundle.
*
* When the system property topology.test.external.run is
* not set or set to false, then this method will perform
* a build and return true.
*
* If the property is set to true then any configuration/external
* setup is assumed to have been setup and this method will
* return false without performing a build.
*
* Thus the test goes on to build & execute the topology
* and test the expected conditions if this method returns false.
*/
protected boolean testBuildOnly(Topology topology) throws Exception {
if (!Boolean.getBoolean("topology.test.external.run")) {
File bundle = (File) getStreamsContext(Type.BUNDLE).submit(topology, getConfig()).get();
assertNotNull(bundle);
assertTrue(bundle.exists());
assertTrue(bundle.isFile());
bundle.delete();
return true;
}
return false;
}
}