001: /*
002: * soapUI, copyright (C) 2004-2007 eviware.com
003: *
004: * soapUI is free software; you can redistribute it and/or modify it under the
005: * terms of version 2.1 of the GNU Lesser General Public License as published by
006: * the Free Software Foundation.
007: *
008: * soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
009: * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
010: * See the GNU Lesser General Public License for more details at gnu.org.
011: */
012:
013: package com.eviware.soapui.tools;
014:
015: import java.io.File;
016: import java.io.IOException;
017: import java.util.ArrayList;
018: import java.util.List;
019:
020: import org.apache.commons.cli.CommandLine;
021:
022: import com.eviware.soapui.SoapUI;
023: import com.eviware.soapui.impl.wsdl.WsdlProject;
024: import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTest;
025: import com.eviware.soapui.impl.wsdl.loadtest.data.actions.ExportLoadTestLogAction;
026: import com.eviware.soapui.impl.wsdl.loadtest.data.actions.ExportStatisticsAction;
027: import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLog;
028: import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLogEntry;
029: import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequest;
030: import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep;
031: import com.eviware.soapui.model.testsuite.LoadTestRunContext;
032: import com.eviware.soapui.model.testsuite.LoadTestRunListener;
033: import com.eviware.soapui.model.testsuite.LoadTestRunner;
034: import com.eviware.soapui.model.testsuite.TestCase;
035: import com.eviware.soapui.model.testsuite.TestRunContext;
036: import com.eviware.soapui.model.testsuite.TestRunner;
037: import com.eviware.soapui.model.testsuite.TestStep;
038: import com.eviware.soapui.model.testsuite.TestStepResult;
039: import com.eviware.soapui.model.testsuite.TestSuite;
040: import com.eviware.soapui.settings.UISettings;
041: import com.eviware.soapui.support.SoapUIException;
042: import com.eviware.soapui.support.Tools;
043:
044: /**
045: * Standalone test-runner used from maven-plugin, can also be used from command-line (see xdocs) or
046: * directly from other classes.
047: * <p>
048: * For standalone usage, set the project file (with setProjectFile) and other desired properties before
049: * calling run</p>
050: *
051: * @author Ole.Matzura
052: */
053:
054: public class SoapUILoadTestRunner extends AbstractSoapUIRunner
055: implements LoadTestRunListener {
056: private String testSuite;
057: private String testCase;
058: private String endpoint;
059:
060: private String domain;
061: private String password;
062: private String username;
063: private String host;
064: private String loadTest;
065: private boolean printReport;
066: private String outputFolder;
067: private List<LoadTestRunner> failedTests = new ArrayList<LoadTestRunner>();
068: private int testCaseCount;
069: private int loadTestCount;
070: private String wssPasswordType;
071:
072: public static String TITLE = "soapUI " + SoapUI.SOAPUI_VERSION
073: + " LoadTest Runner";
074:
075: /**
076: * Runs the loadtests in the specified soapUI project file, see soapUI xdocs for details.
077: *
078: * @param args
079: * @throws Exception
080: */
081:
082: public static void main(String[] args) {
083: new SoapUILoadTestRunner().runFromCommandLine(args);
084: }
085:
086: protected boolean processCommandLine(CommandLine cmd) {
087: if (cmd.hasOption("e"))
088: setEndpoint(cmd.getOptionValue("e"));
089:
090: if (cmd.hasOption("s"))
091: setTestSuite(cmd.getOptionValue("s"));
092:
093: if (cmd.hasOption("c"))
094: setTestCase(cmd.getOptionValue("c"));
095:
096: if (cmd.hasOption("l"))
097: setLoadTest(cmd.getOptionValue("l"));
098:
099: if (cmd.hasOption("u"))
100: setUsername(cmd.getOptionValue("u"));
101:
102: if (cmd.hasOption("p"))
103: setPassword(cmd.getOptionValue("p"));
104:
105: if (cmd.hasOption("w"))
106: setWssPasswordType(cmd.getOptionValue("w"));
107:
108: if (cmd.hasOption("d"))
109: setDomain(cmd.getOptionValue("d"));
110:
111: if (cmd.hasOption("h"))
112: setHost(cmd.getOptionValue("h"));
113:
114: if (cmd.hasOption("f"))
115: setOutputFolder(cmd.getOptionValue("f"));
116:
117: if (cmd.hasOption("t"))
118: SoapUI.initSettings(cmd.getOptionValue("t"));
119:
120: setPrintReport(cmd.hasOption("r"));
121:
122: return true;
123: }
124:
125: protected SoapUIOptions initCommandLineOptions() {
126: SoapUIOptions options = new SoapUIOptions("loadtestrunner");
127: options.addOption("e", true, "Sets the endpoint");
128: options.addOption("s", true, "Sets the testsuite");
129: options.addOption("c", true, "Sets the testcase");
130: options.addOption("l", true, "Sets the loadtest");
131: options.addOption("u", true, "Sets the username");
132: options.addOption("p", true, "Sets the password");
133: options
134: .addOption("w", true,
135: "Sets the WSS password type, either 'Text' or 'Digest'");
136: options.addOption("d", true, "Sets the domain");
137: options.addOption("h", true, "Sets the host");
138: options
139: .addOption("r", false,
140: "Exports statistics and testlogs for each loadtest run");
141: options.addOption("f", true,
142: "Sets the output folder to export to");
143: options.addOption("t", true,
144: "Sets the soapui-settings.xml file to use");
145: return options;
146: }
147:
148: public SoapUILoadTestRunner() {
149: super (TITLE);
150: }
151:
152: public SoapUILoadTestRunner(String title) {
153: super (title);
154: }
155:
156: public void setLoadTest(String loadTest) {
157: this .loadTest = loadTest;
158: }
159:
160: public void setOutputFolder(String outputFolder) {
161: this .outputFolder = outputFolder;
162: }
163:
164: public void setPrintReport(boolean printReport) {
165: this .printReport = printReport;
166: }
167:
168: /**
169: * Sets the host to use by all test-requests, the existing endpoint port and path will be used
170: *
171: * @param host the host to use by all requests
172: */
173:
174: public void setHost(String host) {
175: this .host = host;
176: }
177:
178: /**
179: * Sets the domain to use for any authentications
180: *
181: * @param domain the domain to use for any authentications
182: */
183:
184: public void setDomain(String domain) {
185: log.info("Setting domain to [" + domain + "]");
186: this .domain = domain;
187: }
188:
189: /**
190: * Sets the password to use for any authentications
191: *
192: * @param domain the password to use for any authentications
193: */
194:
195: public void setPassword(String password) {
196: log.info("Setting password to [" + password + "]");
197: this .password = password;
198: }
199:
200: /**
201: * Sets the WSS password-type to use for any authentications. Setting this will result
202: * in the addition of WS-Security UsernamePassword tokens to any outgoing request containing
203: * the specified username and password.
204: *
205: * @param wssPasswordType the wss-password type to use, either 'Text' or 'Digest'
206: */
207:
208: public void setWssPasswordType(String wssPasswordType) {
209: this .wssPasswordType = wssPasswordType;
210: }
211:
212: /**
213: * Sets the username to use for any authentications
214: *
215: * @param domain the username to use for any authentications
216: */
217:
218: public void setUsername(String username) {
219: log.info("Setting username to [" + username + "]");
220: this .username = username;
221: }
222:
223: /**
224: * Runs the testcases as configured with setXXX methods
225: *
226: * @throws Exception thrown if any tests fail
227: */
228:
229: public void run() throws Exception {
230: if (SoapUI.getSettings().getBoolean(
231: UISettings.DONT_DISABLE_GROOVY_LOG)) {
232: initGroovyLog();
233: }
234:
235: String projectFile = getProjectFile();
236:
237: if (!new File(projectFile).exists())
238: throw new SoapUIException("soapUI project file ["
239: + projectFile + "] not found");
240:
241: WsdlProject project = new WsdlProject(projectFile, null);
242: int suiteCount = 0;
243:
244: for (int c = 0; c < project.getTestSuiteCount(); c++) {
245: if (testSuite == null
246: || project.getTestSuiteAt(c).getName()
247: .equalsIgnoreCase(testSuite)) {
248: runSuite(project.getTestSuiteAt(c));
249: suiteCount++;
250: }
251: }
252:
253: if (suiteCount == 0) {
254: log.warn("No test-suites matched argument [" + testSuite
255: + "]");
256: } else if (testCaseCount == 0) {
257: log.warn("No test-cases matched argument [" + testCase
258: + "]");
259: } else if (loadTestCount == 0) {
260: log.warn("No load-tests matched argument [" + loadTest
261: + "]");
262: } else if (!failedTests.isEmpty()) {
263: log.info(failedTests.size() + " load tests failed:");
264: for (LoadTestRunner loadTestRunner : failedTests) {
265: log.info(loadTestRunner.getLoadTest().getName() + ": "
266: + loadTestRunner.getReason());
267: }
268:
269: throw new SoapUIException("LoadTests failed");
270: }
271: }
272:
273: /**
274: * Run tests in the specified TestSuite
275: *
276: * @param suite the TestSuite to run
277: */
278:
279: public void runSuite(TestSuite suite) {
280: long start = System.currentTimeMillis();
281: for (int c = 0; c < suite.getTestCaseCount(); c++) {
282: String name = suite.getTestCaseAt(c).getName();
283: if (testCase == null || name.equalsIgnoreCase(testCase)) {
284: runTestCase(suite.getTestCaseAt(c));
285: testCaseCount++;
286: } else
287: log.info("Skipping testcase [" + name
288: + "], filter is [" + testCase + "]");
289: }
290: log.info("soapUI suite [" + suite.getName() + "] finished in "
291: + (System.currentTimeMillis() - start) + "ms");
292: }
293:
294: /**
295: * Runs the specified TestCase
296: *
297: * @param testCase the testcase to run
298: */
299:
300: private void runTestCase(TestCase testCase) {
301: for (int c = 0; c < testCase.getLoadTestCount(); c++) {
302: String name = testCase.getLoadTestAt(c).getName();
303: if (loadTest == null || loadTest.equalsIgnoreCase(name)) {
304: runWsdlLoadTest((WsdlLoadTest) testCase
305: .getLoadTestAt(c));
306: loadTestCount++;
307: }
308: }
309: }
310:
311: /**
312: * Runs the specified LoadTest
313: *
314: * @param loadTest the loadTest to run
315: */
316:
317: private void runWsdlLoadTest(WsdlLoadTest loadTest) {
318: try {
319: log.info("Running LoadTest [" + loadTest.getName() + "]");
320: loadTest.addLoadTestRunListener(this );
321: LoadTestRunner runner = loadTest.run();
322:
323: // wait for test to finish
324: while (runner.getStatus() == LoadTestRunner.Status.RUNNING) {
325: log.info("LoadTest [" + loadTest.getName()
326: + "] progress: " + runner.getProgress() + ", "
327: + runner.getRunningThreadCount());
328: Thread.sleep(1000);
329: }
330:
331: log.info("LoadTest [" + loadTest.getName()
332: + "] finished with status "
333: + runner.getStatus().toString());
334:
335: if (printReport) {
336: log.info("Exporting log and statistics for LoadTest ["
337: + loadTest.getName() + "]");
338:
339: loadTest.getStatisticsModel().finish();
340:
341: exportLog(loadTest);
342: exportStatistics(loadTest);
343: }
344: } catch (Exception e) {
345: SoapUI.logError(e);
346: log.error(e);
347: }
348: }
349:
350: private void exportStatistics(WsdlLoadTest loadTest)
351: throws IOException {
352: ExportStatisticsAction exportStatisticsAction = new ExportStatisticsAction(
353: loadTest.getStatisticsModel());
354: String statisticsFileName = loadTest.getName()
355: + "-statistics.txt";
356: if (outputFolder != null) {
357: ensureOutputFolder();
358: statisticsFileName = outputFolder + File.separator
359: + statisticsFileName;
360: }
361:
362: int cnt = exportStatisticsAction.exportToFile(new File(
363: statisticsFileName));
364: log.info("Exported " + cnt + " statistics to ["
365: + statisticsFileName + "]");
366: }
367:
368: private void exportLog(WsdlLoadTest loadTest) throws IOException {
369: // export log first
370: LoadTestLog loadTestLog = loadTest.getLoadTestLog();
371: ExportLoadTestLogAction exportLoadTestLogAction = new ExportLoadTestLogAction(
372: loadTestLog);
373: String logFileName = loadTest.getName() + "-log.txt";
374: if (outputFolder != null) {
375: ensureOutputFolder();
376: logFileName = outputFolder + File.separator + logFileName;
377: }
378:
379: int cnt = exportLoadTestLogAction.exportToFile(new File(
380: logFileName));
381: log.info("Exported " + cnt + " log items to [" + logFileName
382: + "]");
383:
384: int errorCnt = 0;
385: for (int c = 0; c < loadTestLog.getSize(); c++) {
386: LoadTestLogEntry entry = (LoadTestLogEntry) loadTestLog
387: .getElementAt(c);
388:
389: if (entry != null && entry.isError()) {
390: String entryFileName = loadTest.getName() + "-error-"
391: + errorCnt++ + "-entry.txt";
392: if (outputFolder != null) {
393: ensureOutputFolder();
394: entryFileName = outputFolder + File.separator
395: + entryFileName;
396: }
397:
398: try {
399: entry.exportToFile(entryFileName);
400: } catch (Exception e) {
401: SoapUI.logError(e);
402: }
403: }
404: }
405: log.info("Exported " + errorCnt + " error results");
406: }
407:
408: private void ensureOutputFolder() {
409: File folder = new File(outputFolder);
410: if (!folder.exists())
411: folder.mkdirs();
412: }
413:
414: /**
415: * Sets the testcase to run
416: *
417: * @param testCase the testcase to run
418: */
419:
420: public void setTestCase(String testCase) {
421: log.info("setting testCase to [" + testCase + "]");
422: this .testCase = testCase;
423: }
424:
425: /**
426: * Sets the endpoint to use for all test requests
427: *
428: * @param endpoint the endpoint to use for all test requests
429: */
430:
431: public void setEndpoint(String endpoint) {
432: log.info("setting test endpoint to [" + endpoint + "]");
433: this .endpoint = endpoint.trim();
434: }
435:
436: /**
437: * Sets the TestSuite to run. If not set all TestSuites in the specified project file are run
438: *
439: * @param testSuite the testSuite to run.
440: */
441:
442: public void setTestSuite(String testSuite) {
443: log.info("setting testSuite to [" + testSuite + "]");
444: this .testSuite = testSuite;
445: }
446:
447: public void afterLoadTest(LoadTestRunner loadTestRunner,
448: LoadTestRunContext context) {
449: if (loadTestRunner.getStatus() == LoadTestRunner.Status.FAILED) {
450: failedTests.add(loadTestRunner);
451: }
452: }
453:
454: public void afterTestCase(LoadTestRunner loadTestRunner,
455: LoadTestRunContext context, TestRunner testRunner,
456: TestRunContext runContext) {
457: }
458:
459: public void afterTestStep(LoadTestRunner loadTestRunner,
460: LoadTestRunContext context, TestRunner testRunner,
461: TestRunContext runContext, TestStepResult testStepResult) {
462: }
463:
464: public void beforeLoadTest(LoadTestRunner loadTestRunner,
465: LoadTestRunContext context) {
466: }
467:
468: public void beforeTestCase(LoadTestRunner loadTestRunner,
469: LoadTestRunContext context, TestRunner testRunner,
470: TestRunContext runContext) {
471: }
472:
473: public void beforeTestStep(LoadTestRunner loadTestRunner,
474: LoadTestRunContext context, TestRunner testRunner,
475: TestRunContext runContext, TestStep testStep) {
476: if (testStep instanceof WsdlTestRequestStep) {
477: WsdlTestRequestStep requestStep = (WsdlTestRequestStep) testStep;
478: if (endpoint != null && endpoint.length() > 0) {
479: requestStep.getTestRequest().setEndpoint(endpoint);
480: }
481:
482: if (host != null && host.length() > 0) {
483: try {
484: String ep = Tools.replaceHost(requestStep
485: .getTestRequest().getEndpoint(), host);
486: requestStep.getTestRequest().setEndpoint(ep);
487: } catch (Exception e) {
488: log.error("Failed to set host on endpoint", e);
489: }
490: }
491:
492: if (username != null && username.length() > 0) {
493: requestStep.getTestRequest().setUsername(username);
494: }
495:
496: if (password != null && password.length() > 0) {
497: requestStep.getTestRequest().setPassword(password);
498: }
499:
500: if (domain != null && domain.length() > 0) {
501: requestStep.getTestRequest().setDomain(domain);
502: }
503:
504: if (wssPasswordType != null && wssPasswordType.length() > 0) {
505: requestStep
506: .getTestRequest()
507: .setWssPasswordType(
508: wssPasswordType.equals("Digest") ? WsdlTestRequest.PW_TYPE_DIGEST
509: : WsdlTestRequest.PW_TYPE_TEXT);
510: }
511: }
512: }
513:
514: public void loadTestStarted(LoadTestRunner loadTestRunner,
515: LoadTestRunContext context) {
516: }
517:
518: public void loadTestStopped(LoadTestRunner loadTestRunner,
519: LoadTestRunContext context) {
520: }
521: }
|