/*
* Copyright 2004-2014 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.tools;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.cli.CommandLine;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.wsdl.WsdlProject;
import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTest;
import com.eviware.soapui.impl.wsdl.loadtest.data.actions.ExportLoadTestLogAction;
import com.eviware.soapui.impl.wsdl.loadtest.data.actions.ExportStatisticsAction;
import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLog;
import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLogEntry;
import com.eviware.soapui.model.project.ProjectFactoryRegistry;
import com.eviware.soapui.model.testsuite.LoadTestRunContext;
import com.eviware.soapui.model.testsuite.LoadTestRunListener;
import com.eviware.soapui.model.testsuite.LoadTestRunner;
import com.eviware.soapui.model.testsuite.TestCase;
import com.eviware.soapui.model.testsuite.TestCaseRunContext;
import com.eviware.soapui.model.testsuite.TestCaseRunner;
import com.eviware.soapui.model.testsuite.TestRunner.Status;
import com.eviware.soapui.model.testsuite.TestStep;
import com.eviware.soapui.model.testsuite.TestStepResult;
import com.eviware.soapui.model.testsuite.TestSuite;
import com.eviware.soapui.settings.UISettings;
import com.eviware.soapui.support.SoapUIException;
import com.eviware.soapui.support.StringUtils;
/**
* Standalone test-runner used from maven-plugin, can also be used from
* command-line (see xdocs) or directly from other classes.
* <p>
* For standalone usage, set the project file (with setProjectFile) and other
* desired properties before calling run
* </p>
*
* @author Ole.Matzura
*/
public class SoapUILoadTestRunner extends AbstractSoapUITestRunner implements LoadTestRunListener {
private String testSuite;
private String testCase;
private String loadTest;
private boolean printReport;
private List<LoadTestRunner> failedTests = new ArrayList<LoadTestRunner>();
private int testCaseCount;
private int loadTestCount;
private int limit = -1;
private long threadCount = -1;
private boolean saveAfterRun;
public static String TITLE = "SoapUI " + SoapUI.SOAPUI_VERSION + " LoadTest Runner";
/**
* Runs the loadtests in the specified soapUI project file, see SoapUI xdocs
* for details.
*
* @param args
* @throws Exception
*/
public static void main(String[] args) {
System.exit(new SoapUILoadTestRunner().runFromCommandLine(args));
}
protected boolean processCommandLine(CommandLine cmd) {
String message = "";
if (cmd.hasOption("e")) {
setEndpoint(cmd.getOptionValue("e"));
}
if (cmd.hasOption("s")) {
String testSuite = getCommandLineOptionSubstSpace(cmd, "s");
setTestSuite(testSuite);
}
if (cmd.hasOption("c")) {
String testCase = cmd.getOptionValue("c");
setTestCase(testCase);
}
if (cmd.hasOption("l")) {
setLoadTest(cmd.getOptionValue("l"));
}
if (cmd.hasOption("u")) {
setUsername(cmd.getOptionValue("u"));
}
if (cmd.hasOption("p")) {
setPassword(cmd.getOptionValue("p"));
}
if (cmd.hasOption("w")) {
setWssPasswordType(cmd.getOptionValue("w"));
}
if (cmd.hasOption("d")) {
setDomain(cmd.getOptionValue("d"));
}
if (cmd.hasOption("h")) {
setHost(cmd.getOptionValue("h"));
}
if (cmd.hasOption("m")) {
setLimit(Integer.parseInt(cmd.getOptionValue("m")));
}
if (cmd.hasOption("n")) {
setThreadCount(Integer.parseInt(cmd.getOptionValue("n")));
}
if (cmd.hasOption("f")) {
setOutputFolder(getCommandLineOptionSubstSpace(cmd, "f"));
}
if (cmd.hasOption("t")) {
setSettingsFile(getCommandLineOptionSubstSpace(cmd, "t"));
}
setPrintReport(cmd.hasOption("r"));
setSaveAfterRun(cmd.hasOption("S"));
if (cmd.hasOption("x")) {
setProjectPassword(cmd.getOptionValue("x"));
}
if (cmd.hasOption("v")) {
setSoapUISettingsPassword(cmd.getOptionValue("v"));
}
if (cmd.hasOption("D")) {
setSystemProperties(cmd.getOptionValues("D"));
}
if (cmd.hasOption("G")) {
setGlobalProperties(cmd.getOptionValues("G"));
}
if (cmd.hasOption("P")) {
setProjectProperties(cmd.getOptionValues("P"));
}
if (message.length() > 0) {
log.error(message);
return false;
}
return true;
}
public void setLimit(int limit) {
this.limit = limit;
}
public void setThreadCount(long threadCount) {
this.threadCount = threadCount;
}
protected SoapUIOptions initCommandLineOptions() {
SoapUIOptions options = new SoapUIOptions("loadtestrunner");
options.addOption("e", true, "Sets the endpoint");
options.addOption("s", true, "Sets the testsuite");
options.addOption("c", true, "Sets the testcase");
options.addOption("l", true, "Sets the loadtest");
options.addOption("u", true, "Sets the username");
options.addOption("p", true, "Sets the password");
options.addOption("w", true, "Sets the WSS password type, either 'Text' or 'Digest'");
options.addOption("d", true, "Sets the domain");
options.addOption("h", true, "Sets the host");
options.addOption("m", true, "Overrides the LoadTest Limit");
options.addOption("n", true, "Overrides the LoadTest ThreadCount");
options.addOption("r", false, "Exports statistics and testlogs for each LoadTest run");
options.addOption("f", true, "Sets the output folder to export to");
options.addOption("t", true, "Sets the soapui-settings.xml file to use");
options.addOption("x", true, "Sets project password for decryption if project is encrypted");
options.addOption("v", true, "Sets password for soapui-settings.xml file");
options.addOption("D", true, "Sets system property with name=value");
options.addOption("G", true, "Sets global property with name=value");
options.addOption("P", true, "Sets or overrides project property with name=value");
options.addOption("S", false, "Saves the project after running the tests");
return options;
}
public SoapUILoadTestRunner() {
this(TITLE);
}
public SoapUILoadTestRunner(String title) {
super(title);
}
public void setLoadTest(String loadTest) {
this.loadTest = loadTest;
}
public void setPrintReport(boolean printReport) {
this.printReport = printReport;
}
public void setSaveAfterRun(boolean saveAfterRun) {
this.saveAfterRun = saveAfterRun;
}
/**
* Runs the testcases as configured with setXXX methods
*
* @throws Exception thrown if any tests fail
*/
public boolean runRunner() throws Exception {
if (SoapUI.getSettings().getBoolean(UISettings.DONT_DISABLE_GROOVY_LOG)) {
initGroovyLog();
}
String projectFile = getProjectFile();
// WsdlProject project = new WsdlProject( projectFile,
// getProjectPassword() );
WsdlProject project = (WsdlProject) ProjectFactoryRegistry.getProjectFactory("wsdl").createNew(projectFile,
getProjectPassword());
if (project.isDisabled()) {
throw new Exception("Failed to load SoapUI project file [" + projectFile + "]");
}
initProjectProperties(project);
int suiteCount = 0;
if (testSuite != null && project.getTestSuiteByName(testSuite) == null) {
throw new Exception("Missing TestSuite named [" + testSuite + "]");
}
for (int c = 0; c < project.getTestSuiteCount(); c++) {
if (testSuite == null || project.getTestSuiteAt(c).getName().equalsIgnoreCase(testSuite)) {
runSuite(project.getTestSuiteAt(c));
suiteCount++;
}
}
if (suiteCount == 0) {
log.warn("No test-suites matched argument [" + testSuite + "]");
} else if (testCaseCount == 0) {
log.warn("No test-cases matched argument [" + testCase + "]");
} else if (loadTestCount == 0) {
log.warn("No load-tests matched argument [" + loadTest + "]");
} else {
if (saveAfterRun && !project.isRemote()) {
try {
project.save();
} catch (Throwable t) {
log.error("Failed to save project", t);
}
}
if (!failedTests.isEmpty()) {
log.info(failedTests.size() + " load tests failed:");
for (LoadTestRunner loadTestRunner : failedTests) {
log.info(loadTestRunner.getLoadTest().getName() + ": " + loadTestRunner.getReason());
}
throw new SoapUIException("LoadTests failed");
}
}
return true;
}
/**
* Run tests in the specified TestSuite
*
* @param suite the TestSuite to run
*/
public void runSuite(TestSuite suite) {
if (testCase != null && suite.getTestCaseByName(testCase) == null) {
return;
}
long start = System.currentTimeMillis();
for (int c = 0; c < suite.getTestCaseCount(); c++) {
String name = suite.getTestCaseAt(c).getName();
if (testCase == null || name.equalsIgnoreCase(testCase)) {
runTestCase(suite.getTestCaseAt(c));
testCaseCount++;
} else {
log.info("Skipping testcase [" + name + "], filter is [" + testCase + "]");
}
}
log.info("SoapUI suite [" + suite.getName() + "] finished in " + (System.currentTimeMillis() - start) + "ms");
}
/**
* Runs the specified TestCase
*
* @param testCase the testcase to run
*/
private void runTestCase(TestCase testCase) {
if (loadTest != null && testCase.getLoadTestByName(loadTest) == null) {
return;
}
for (int c = 0; c < testCase.getLoadTestCount(); c++) {
String name = testCase.getLoadTestAt(c).getName();
if (loadTest == null || loadTest.equalsIgnoreCase(name)) {
runWsdlLoadTest((WsdlLoadTest) testCase.getLoadTestAt(c));
loadTestCount++;
}
}
}
/**
* Runs the specified LoadTest
*
* @param loadTest the loadTest to run
*/
protected void runWsdlLoadTest(WsdlLoadTest loadTest) {
try {
log.info("Running LoadTest [" + loadTest.getName() + "]");
if (limit >= 0) {
log.info("Overriding limit [" + loadTest.getTestLimit() + "] with specified [" + limit + "]");
loadTest.setTestLimit(limit);
}
if (threadCount >= 0) {
log.info("Overriding threadCount [" + loadTest.getThreadCount() + "] with specified [" + threadCount + "]");
loadTest.setThreadCount(threadCount);
}
loadTest.addLoadTestRunListener(this);
LoadTestRunner runner = loadTest.run();
// wait for test to finish
while (!runner.hasStopped()) {
if (runner.getStatus() == Status.RUNNING) {
log.info("LoadTest [" + loadTest.getName() + "] progress: " + runner.getProgress() + ", "
+ runner.getRunningThreadCount());
}
Thread.sleep(1000);
}
log.info("LoadTest [" + loadTest.getName() + "] finished with status " + runner.getStatus().toString());
if (printReport) {
log.info("Exporting log and statistics for LoadTest [" + loadTest.getName() + "]");
loadTest.getStatisticsModel().finish();
exportLog(loadTest);
exportStatistics(loadTest);
}
} catch (Exception e) {
SoapUI.logError(e);
log.error(e);
}
}
private void exportStatistics(WsdlLoadTest loadTest) throws IOException {
ExportStatisticsAction exportStatisticsAction = new ExportStatisticsAction(loadTest.getStatisticsModel());
String statisticsFileName = StringUtils.createFileName(loadTest.getName(), '_') + "-statistics.txt";
if (getOutputFolder() != null) {
ensureOutputFolder(loadTest);
statisticsFileName = getAbsoluteOutputFolder(loadTest) + File.separator + statisticsFileName;
}
int cnt = exportStatisticsAction.exportToFile(new File(statisticsFileName));
log.info("Exported " + cnt + " statistics to [" + statisticsFileName + "]");
}
private void exportLog(WsdlLoadTest loadTest) throws IOException {
// export log first
LoadTestLog loadTestLog = loadTest.getLoadTestLog();
ExportLoadTestLogAction exportLoadTestLogAction = new ExportLoadTestLogAction(loadTestLog, null);
String logFileName = StringUtils.createFileName(loadTest.getName(), '_') + "-log.txt";
if (getOutputFolder() != null) {
ensureOutputFolder(loadTest);
logFileName = getAbsoluteOutputFolder(loadTest) + File.separator + logFileName;
}
int cnt = exportLoadTestLogAction.exportToFile(new File(logFileName));
log.info("Exported " + cnt + " log items to [" + logFileName + "]");
int errorCnt = 0;
for (int c = 0; c < loadTestLog.getSize(); c++) {
LoadTestLogEntry entry = (LoadTestLogEntry) loadTestLog.getElementAt(c);
if (entry != null && entry.isError()) {
String entryFileName = StringUtils.createFileName(loadTest.getName(), '_') + "-error-" + errorCnt++
+ "-entry.txt";
if (getOutputFolder() != null) {
ensureOutputFolder(loadTest);
entryFileName = getAbsoluteOutputFolder(loadTest) + File.separator + entryFileName;
}
try {
entry.exportToFile(entryFileName);
} catch (Exception e) {
SoapUI.logError(e);
}
}
}
log.info("Exported " + errorCnt + " error results");
}
/**
* Sets the testcase to run
*
* @param testCase the testcase to run
*/
public void setTestCase(String testCase) {
this.testCase = testCase;
}
/**
* Sets the TestSuite to run. If not set all TestSuites in the specified
* project file are run
*
* @param testSuite the testSuite to run.
*/
public void setTestSuite(String testSuite) {
this.testSuite = testSuite;
}
public void afterLoadTest(LoadTestRunner loadTestRunner, LoadTestRunContext context) {
if (loadTestRunner.getStatus() == LoadTestRunner.Status.FAILED) {
failedTests.add(loadTestRunner);
}
}
public void afterTestCase(LoadTestRunner loadTestRunner, LoadTestRunContext context, TestCaseRunner testRunner,
TestCaseRunContext runContext) {
}
public void afterTestStep(LoadTestRunner loadTestRunner, LoadTestRunContext context, TestCaseRunner testRunner,
TestCaseRunContext runContext, TestStepResult testStepResult) {
super.afterStep(testRunner, runContext, testStepResult);
}
public void beforeLoadTest(LoadTestRunner loadTestRunner, LoadTestRunContext context) {
}
public void beforeTestCase(LoadTestRunner loadTestRunner, LoadTestRunContext context, TestCaseRunner testRunner,
TestCaseRunContext runContext) {
}
public void beforeTestStep(LoadTestRunner loadTestRunner, LoadTestRunContext context, TestCaseRunner testRunner,
TestCaseRunContext runContext, TestStep testStep) {
super.beforeStep(testRunner, runContext, testStep);
}
public void loadTestStarted(LoadTestRunner loadTestRunner, LoadTestRunContext context) {
}
public void loadTestStopped(LoadTestRunner loadTestRunner, LoadTestRunContext context) {
}
}