package integration_tests;
import com.loadimpact.ApiTokenClient;
import com.loadimpact.resource.LoadZone;
import com.loadimpact.resource.TestConfiguration;
import com.loadimpact.resource.UserScenario;
import com.loadimpact.resource.configuration.LoadScheduleStep;
import com.loadimpact.resource.configuration.LoadTrack;
import com.loadimpact.resource.configuration.UserType;
import com.loadimpact.util.StringUtils;
import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;
/**
* Base class for all integration-tests, that finds the external API token and disables the tests if no token was found.
*
* @author jens
*/
public abstract class AbstractIntegrationTestBase {
public static final String DEFAULT_TOKEN_PATH = "../loadimpact-token.properties";
public static final String INFO_MESSAGE = "how_to_run_integration_tests.txt";
public static final String SYSPROP_TOKEN = "loadimpact.token";
public static final String SYSPROP_TOKEN_FILE = "loadimpact.token.file";
public static final String SYSPROP_HTTP_VERBOSE = "loadimpact.http.verbose";
public static final String SYSPROP_HTTP_MAX = "loadimpact.http.max";
public static final int DEFAULT_MAX_CHARS = 10000;
public static final String ENV_TOKEN_FILE = "LOADIMPACT_TOKEN_FILE";
public static final int ONE_SECOND_AS_MILLISECS = 1000;
public static final String TARGET_URL = "https://loadimpact.com/";
protected static String apiToken;
protected ApiTokenClient client;
@BeforeClass
public static void checkToken() throws Exception {
apiToken = findToken();
if (apiToken == null) showInfoMessage();
Assume.assumeTrue("Missing API token. No integration tests will be run.", apiToken != null);
}
@Before
public void createClient() {
client = new ApiTokenClient(apiToken);
if (showHttp()) {
client.setDebug(true, maxChars());
}
}
/**
* Tries to locate the API token, via a set of different ways.
* <ol>
* <li>Direct via sysprop: <code>loadimpact.token</code></li>
* <li>Via properties file: <code>../loadimpact-token.properties</code></li>
* <li>Via sysprop pointing to properties file: <code>loadimpact.token.file</code></li>
* <li>Via environment variable pointing to properties file: <code>LOADIMPACT_TOKEN</code></li>
* </ol>
* @return API token, or null
*/
protected static String findToken() {
if (System.getProperties().containsKey(SYSPROP_TOKEN)) {
return System.getProperty(SYSPROP_TOKEN);
}
return loadToken(findReader());
}
/**
* Loads a properties file and returns the API token value, if any.
*
* @param in reader pointing to properties file
* @return API token or null
*/
protected static String loadToken(Reader in) {
if (in == null) return null;
try {
Properties p = new Properties();
p.load(in);
in.close();
return p.getProperty("api.token");
} catch (Exception e) {
return null;
}
}
/**
* Locates a token file and returns a Reader or null if not found.
* The search order is:
* <ul>
* <li>File: ../loadimpact-token.properties</li>
* <li>System Property: loadimpact.token.file</li>
* <li>Environment Variable: LOADIMPACT_TOKEN_FILE</li>
* </ul>
*
* @return reader or null
* @throws FileNotFoundException
*/
protected static Reader findReader() {
Reader reader;
String path = DEFAULT_TOKEN_PATH;
if ((reader = openReader(path)) != null) {
return reader;
}
path = System.getProperty(SYSPROP_TOKEN_FILE);
if (path != null && (reader = openReader(path)) != null) {
return reader;
}
path = System.getenv(ENV_TOKEN_FILE);
if (path != null && (reader = openReader(path)) != null) {
return reader;
}
return null;
}
/**
* Opens a file as a reader.
*
* @param tokenPath path to file
* @return reader or null, if cannot open
*/
protected static Reader openReader(String tokenPath) {
File tokenFile = new File(tokenPath);
if (tokenFile.canRead()) {
try {
return new FileReader(tokenFile);
} catch (FileNotFoundException e) {
return null;
}
}
return null;
}
protected boolean showHttp() {
String showHttp = System.getProperty(SYSPROP_HTTP_VERBOSE);
if (showHttp != null) {
return Boolean.parseBoolean(showHttp);
}
return false;
}
protected int maxChars() {
String maxChars = System.getProperty(SYSPROP_HTTP_MAX);
if (maxChars != null) {
try {
return Integer.parseInt(maxChars);
} catch (NumberFormatException e) {
return DEFAULT_MAX_CHARS;
}
}
return DEFAULT_MAX_CHARS;
}
/**
* Shows an information message about how the pre-requisites to run the integration tests.
*/
protected static void showInfoMessage() {
InputStream is = AbstractIntegrationTestBase.class.getResourceAsStream(INFO_MESSAGE);
if (is == null) {
throw new IllegalArgumentException("Cannot find classpath resource: " + INFO_MESSAGE);
}
System.err.println("**********************************************************");
System.err.println(StringUtils.toString(is));
System.err.println("**********************************************************");
}
interface WaitForClosure {
boolean isDone();
}
protected void waitFor(String what, WaitForClosure expr) {
waitFor(120, what, expr);
}
protected void waitFor(int maxWaitingTimeInSeconds, String what, WaitForClosure expr) {
waitFor(maxWaitingTimeInSeconds, 5, what, expr);
}
protected void waitFor(int maxWaitingTimeInSeconds, int sleepTimeInSeconds, String what, WaitForClosure expr) {
final long deadline = now() + maxWaitingTimeInSeconds * ONE_SECOND_AS_MILLISECS;
System.out.printf("[%s] Waiting until %s %n", this.getClass().getSimpleName(), what);
while (now() < deadline) {
boolean done = false;
try {
done = expr.isDone();
} catch (Exception e) {
fail(e.toString());
}
if (done) break;
System.out.print(".");
System.out.flush();
try {
Thread.sleep(sleepTimeInSeconds * ONE_SECOND_AS_MILLISECS);
} catch (InterruptedException e) {
break;
}
}
System.out.println();
assertThat("Max waiting time (" + maxWaitingTimeInSeconds + " secs) exceeded", now(), lessThan(deadline));
}
protected long now() {
return System.currentTimeMillis();
}
protected UserScenario createScenario() {
final String scenarioScript = StringUtils.toString(getClass().getResourceAsStream(UsingScenarios.SCENARIO_RESOURCE));
final String scenarioName = "integration_test_" + System.nanoTime();
UserScenario scenarioToBeCreated = new UserScenario();
scenarioToBeCreated.name = scenarioName;
scenarioToBeCreated.loadScript = scenarioScript;
UserScenario scenario = client.createUserScenario(scenarioToBeCreated);
assertThat(scenario, notNullValue());
assertThat(scenario.name, is(scenarioName));
assertThat(scenario.id, greaterThan(0));
return scenario;
}
protected TestConfiguration createTestConfig(String targetUrl, int scenarioId) throws MalformedURLException {
final String configurationName = "integration_test_" + System.nanoTime();
final int testDuration = 1;
final int testUserCount = 10;
final LoadZone trackZone = LoadZone.AMAZON_US_ASHBURN;
final int trackPercentage = 100;
final TestConfiguration configurationToBeCreated = new TestConfiguration();
configurationToBeCreated.name = configurationName;
configurationToBeCreated.url = new URL(targetUrl);
configurationToBeCreated.userType = UserType.SBU;
configurationToBeCreated.loadSchedule.add(new LoadScheduleStep(testDuration, testUserCount));
final LoadTrack track = new LoadTrack(trackZone);
track.clip(trackPercentage, scenarioId);
configurationToBeCreated.tracks.add(track);
TestConfiguration configuration = client.createTestConfiguration(configurationToBeCreated);
assertThat(configuration, notNullValue());
assertThat(configuration.name, is(configurationName));
assertThat(configuration.id, greaterThan(0));
return configuration;
}
}