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.FileOutputStream;
017: import java.io.PrintWriter;
018: import java.io.StringWriter;
019: import java.util.ArrayList;
020: import java.util.Arrays;
021: import java.util.HashMap;
022: import java.util.List;
023: import java.util.Map;
024:
025: import org.apache.commons.cli.CommandLine;
026:
027: import com.eviware.soapui.SoapUI;
028: import com.eviware.soapui.impl.wsdl.WsdlProject;
029: import com.eviware.soapui.impl.wsdl.support.assertions.Assertable.AssertionStatus;
030: import com.eviware.soapui.impl.wsdl.teststeps.WsdlMessageAssertion;
031: import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequest;
032: import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep;
033: import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStepResult;
034: import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestStepResult;
035: import com.eviware.soapui.model.iface.Attachment;
036: import com.eviware.soapui.model.support.PropertiesMap;
037: import com.eviware.soapui.model.testsuite.TestCase;
038: import com.eviware.soapui.model.testsuite.TestRunContext;
039: import com.eviware.soapui.model.testsuite.TestRunListener;
040: import com.eviware.soapui.model.testsuite.TestRunner;
041: import com.eviware.soapui.model.testsuite.TestStep;
042: import com.eviware.soapui.model.testsuite.TestStepResult;
043: import com.eviware.soapui.model.testsuite.TestSuite;
044: import com.eviware.soapui.model.testsuite.TestRunner.Status;
045: import com.eviware.soapui.model.testsuite.TestStepResult.TestStepStatus;
046: import com.eviware.soapui.model.testsuite.TestSuite.TestSuiteRunType;
047: import com.eviware.soapui.report.JUnitReportCollector;
048: import com.eviware.soapui.support.Tools;
049:
050: /**
051: * Standalone test-runner used from maven-plugin, can also be used from command-line (see xdocs) or
052: * directly from other classes.
053: * <p>
054: * For standalone usage, set the project file (with setProjectFile) and other desired properties before
055: * calling run</p>
056: *
057: * @author Ole.Matzura
058: */
059:
060: public class SoapUITestCaseRunner extends AbstractSoapUIRunner
061: implements TestRunListener {
062: public static final String TITLE = "soapUI "
063: + SoapUI.SOAPUI_VERSION + " TestCase Runner";
064:
065: private String testSuite;
066: private String testCase;
067: private List<WsdlMessageAssertion> assertions = new ArrayList<WsdlMessageAssertion>();
068: private Map<WsdlMessageAssertion, WsdlTestStepResult> assertionResults = new HashMap<WsdlMessageAssertion, WsdlTestStepResult>();
069: private List<TestCase> runningTests = new ArrayList<TestCase>();
070: private List<TestCase> failedTests = new ArrayList<TestCase>();
071: private String endpoint;
072: private String domain;
073: private String password;
074: private String username;
075: private String host;
076:
077: private int testSuiteCount;
078: private int testCaseCount;
079: private int testStepCount;
080: private int testAssertionCount;
081:
082: private boolean printReport;
083: private String outputFolder;
084: private boolean exportAll;
085: private boolean junitReport;
086: private int exportCount;
087: private JUnitReportCollector reportCollector;
088: private String wssPasswordType;
089: private WsdlProject project;
090:
091: /**
092: * Runs the tests in the specified soapUI project file, see soapUI xdocs for details.
093: *
094: * @param args
095: * @throws Exception
096: */
097:
098: @SuppressWarnings("static-access")
099: public static void main(String[] args) throws Exception {
100: new SoapUITestCaseRunner().runFromCommandLine(args);
101: }
102:
103: protected boolean processCommandLine(CommandLine cmd) {
104: if (cmd.hasOption("e"))
105: setEndpoint(cmd.getOptionValue("e"));
106:
107: if (cmd.hasOption("s"))
108: setTestSuite(cmd.getOptionValue("s"));
109:
110: if (cmd.hasOption("c"))
111: setTestCase(cmd.getOptionValue("c"));
112:
113: if (cmd.hasOption("u"))
114: setUsername(cmd.getOptionValue("u"));
115:
116: if (cmd.hasOption("p"))
117: setPassword(cmd.getOptionValue("p"));
118:
119: if (cmd.hasOption("w"))
120: setWssPasswordType(cmd.getOptionValue("w"));
121:
122: if (cmd.hasOption("d"))
123: setDomain(cmd.getOptionValue("d"));
124:
125: if (cmd.hasOption("h"))
126: setHost(cmd.getOptionValue("h"));
127:
128: if (cmd.hasOption("f"))
129: setOutputFolder(cmd.getOptionValue("f"));
130:
131: if (cmd.hasOption("t"))
132: SoapUI.initSettings(cmd.getOptionValue("t"));
133:
134: if (cmd.hasOption("i")) {
135: enableSwingUI();
136: }
137:
138: setPrintReport(cmd.hasOption("r"));
139: setExportAll(cmd.hasOption("a"));
140: setJUnitReport(cmd.hasOption("j"));
141:
142: return true;
143: }
144:
145: protected SoapUIOptions initCommandLineOptions() {
146: SoapUIOptions options = new SoapUIOptions("testrunner");
147: options.addOption("e", true, "Sets the endpoint");
148: options.addOption("s", true, "Sets the testsuite");
149: options.addOption("c", true, "Sets the testcase");
150: options.addOption("u", true, "Sets the username");
151: options.addOption("p", true, "Sets the password");
152: options
153: .addOption("w", true,
154: "Sets the WSS password type, either 'Text' or 'Digest'");
155: options.addOption("i", false, "Enables Swing UI for scripts");
156: options.addOption("d", true, "Sets the domain");
157: options.addOption("h", true, "Sets the host");
158: options.addOption("r", false, "Prints a small summary report");
159: options.addOption("f", true,
160: "Sets the output folder to export results to");
161: options.addOption("j", false,
162: "Sets the output to include JUnit XML reports");
163: options.addOption("a", false,
164: "Turns on exporting of all results");
165: options.addOption("t", true,
166: "Sets the soapui-settings.xml file to use");
167: return options;
168: }
169:
170: /**
171: * Add console appender to groovy log
172: */
173:
174: public void setExportAll(boolean exportAll) {
175: this .exportAll = exportAll;
176: }
177:
178: public void setJUnitReport(boolean junitReport) {
179: this .junitReport = junitReport;
180: if (junitReport)
181: reportCollector = new JUnitReportCollector();
182: }
183:
184: public void setOutputFolder(String outputFolder) {
185: this .outputFolder = outputFolder;
186: }
187:
188: public SoapUITestCaseRunner() {
189: super (SoapUITestCaseRunner.TITLE);
190: }
191:
192: public SoapUITestCaseRunner(String title) {
193: super (title);
194: }
195:
196: /**
197: * Controls if a short test summary should be printed after the test runs
198: *
199: * @param printReport a flag controlling if a summary should be printed
200: */
201:
202: public void setPrintReport(boolean printReport) {
203: this .printReport = printReport;
204: }
205:
206: /**
207: * Sets the host to use by all test-requests, the existing endpoint port and path will be used
208: *
209: * @param host the host to use by all requests
210: */
211:
212: public void setHost(String host) {
213: this .host = host;
214: }
215:
216: /**
217: * Sets the domain to use for any authentications
218: *
219: * @param domain the domain to use for any authentications
220: */
221:
222: public void setDomain(String domain) {
223: log.info("Setting domain to [" + domain + "]");
224: this .domain = domain;
225: }
226:
227: /**
228: * Sets the password to use for any authentications
229: *
230: * @param domain the password to use for any authentications
231: */
232:
233: public void setPassword(String password) {
234: log.info("Setting password to [" + password + "]");
235: this .password = password;
236: }
237:
238: /**
239: * Sets the WSS password-type to use for any authentications. Setting this will result
240: * in the addition of WS-Security UsernamePassword tokens to any outgoing request containing
241: * the specified username and password.
242: *
243: * @param wssPasswordType the wss-password type to use, either 'Text' or 'Digest'
244: */
245:
246: public void setWssPasswordType(String wssPasswordType) {
247: this .wssPasswordType = wssPasswordType;
248: }
249:
250: /**
251: * Sets the username to use for any authentications
252: *
253: * @param domain the username to use for any authentications
254: */
255:
256: public void setUsername(String username) {
257: log.info("Setting username to [" + username + "]");
258: this .username = username;
259: }
260:
261: /**
262: * Runs the testcases as configured with setXXX methods
263: *
264: * @throws Exception thrown if any tests fail
265: */
266:
267: public void run() throws Exception {
268: initGroovyLog();
269:
270: if (outputFolder != null) {
271: // ensure folder exists
272: File folder = new File(outputFolder);
273: if (!folder.exists())
274: folder.mkdirs();
275: }
276:
277: assertions.clear();
278:
279: String projectFile = getProjectFile();
280:
281: if (!new File(projectFile).exists())
282: throw new Exception("soapUI project file [" + projectFile
283: + "] not found");
284:
285: project = new WsdlProject(projectFile, null);
286: log.info("Running soapUI tests in project ["
287: + project.getName() + "]");
288:
289: long startTime = System.nanoTime();
290:
291: // start by listening to all testcases.. (since one testcase can call another)
292: for (int c = 0; c < project.getTestSuiteCount(); c++) {
293: TestSuite suite = project.getTestSuiteAt(c);
294: for (int i = 0; i < suite.getTestCaseCount(); i++) {
295: suite.getTestCaseAt(i).addTestRunListener(this );
296: if (junitReport)
297: suite.getTestCaseAt(i).addTestRunListener(
298: reportCollector);
299: }
300: }
301:
302: // now run tests..
303: for (int c = 0; c < project.getTestSuiteCount(); c++) {
304: if (testSuite == null
305: || project.getTestSuiteAt(c).getName()
306: .equalsIgnoreCase(testSuite)) {
307: runSuite(project.getTestSuiteAt(c));
308: testSuiteCount++;
309:
310: // wait for tests to finish if running in parallell mode
311: if (!runningTests.isEmpty())
312: log.info("Waiting for " + runningTests.size()
313: + " tests to finish");
314:
315: while (!runningTests.isEmpty()) {
316: Thread.sleep(100);
317: }
318: }
319: }
320:
321: long timeTaken = (System.nanoTime() - startTime) / 1000000;
322:
323: if (printReport) {
324: printReport(timeTaken);
325: }
326:
327: if (junitReport) {
328: exportJUnitReports(reportCollector, outputFolder);
329: }
330:
331: if (assertions.size() > 0 || failedTests.size() > 0) {
332: throwFailureException();
333: }
334: }
335:
336: protected void throwFailureException() throws Exception {
337: StringBuffer buf = new StringBuffer();
338:
339: for (int c = 0; c < assertions.size(); c++) {
340: WsdlMessageAssertion assertion = assertions.get(c);
341: WsdlTestRequest testRequest = ((WsdlTestRequest) assertion
342: .getAssertable());
343: failedTests.remove(testRequest.getTestCase());
344:
345: buf.append(assertion.getName() + " in ["
346: + testRequest.getName() + "] failed;\n");
347: buf.append(Arrays.toString(assertion.getErrors()) + "\n");
348:
349: WsdlTestStepResult result = assertionResults.get(assertion);
350: StringWriter stringWriter = new StringWriter();
351: PrintWriter writer = new PrintWriter(stringWriter);
352: result.writeTo(writer);
353: buf.append(stringWriter.toString());
354: }
355:
356: while (!failedTests.isEmpty()) {
357: buf.append("TestCase [" + failedTests.remove(0).getName()
358: + "] failed without assertions\n");
359: }
360:
361: throw new Exception(buf.toString());
362: }
363:
364: public void exportJUnitReports(JUnitReportCollector collector,
365: String folder) throws Exception {
366: collector.saveReports(folder == null ? "" : folder);
367: }
368:
369: public void printReport(long timeTaken) {
370: System.out.println();
371: System.out.println("SoapUI " + SoapUI.SOAPUI_VERSION
372: + " TestCaseRunner Summary");
373: System.out.println("-----------------------------");
374: System.out.println("Time Taken: " + timeTaken + "ms");
375: System.out.println("Total TestSuites: " + testSuiteCount);
376: System.out.println("Total TestCases: " + testCaseCount + " ("
377: + failedTests.size() + " failed)");
378: System.out.println("Total TestSteps: " + testStepCount);
379: System.out.println("Total Request Assertions: "
380: + testAssertionCount);
381: System.out.println("Total Failed Assertions: "
382: + assertions.size());
383: System.out.println("Total Exported Results: " + exportCount);
384: }
385:
386: /**
387: * Run tests in the specified TestSuite
388: *
389: * @param suite the TestSuite to run
390: */
391:
392: public void runSuite(TestSuite suite) {
393: log.info(("Running soapUI suite [" + suite.getName()
394: + "], runType = " + suite.getRunType()));
395: long start = System.currentTimeMillis();
396: for (int c = 0; c < suite.getTestCaseCount(); c++) {
397: String name = suite.getTestCaseAt(c).getName();
398: if (testCase == null || name.equalsIgnoreCase(testCase)) {
399: runTestCase(suite.getTestCaseAt(c));
400: } else {
401: log.info("Skipping testcase [" + name
402: + "], filter is [" + testCase + "]");
403: }
404: }
405: log.info("soapUI suite [" + suite.getName() + "] finished in "
406: + (System.currentTimeMillis() - start) + "ms");
407: }
408:
409: /**
410: * Runs the specified TestCase
411: *
412: * @param testCase the testcase to run
413: */
414:
415: private void runTestCase(TestCase testCase) {
416: runningTests.add(testCase);
417: testCase.run(PropertiesMap.EMPTY_MAP, testCase.getTestSuite()
418: .getRunType() == TestSuiteRunType.PARALLEL);
419: }
420:
421: /**
422: * Sets the testcase to run
423: *
424: * @param testCase the testcase to run
425: */
426:
427: public void setTestCase(String testCase) {
428: log.info("setting testCase to [" + testCase + "]");
429: this .testCase = testCase;
430: }
431:
432: /**
433: * Sets the endpoint to use for all test requests
434: *
435: * @param endpoint the endpoint to use for all test requests
436: */
437:
438: public void setEndpoint(String endpoint) {
439: log.info("setting test endpoint to [" + endpoint + "]");
440: this .endpoint = endpoint.trim();
441: }
442:
443: /**
444: * Sets the TestSuite to run. If not set all TestSuites in the specified project file are run
445: *
446: * @param testSuite the testSuite to run.
447: */
448:
449: public void setTestSuite(String testSuite) {
450: log.info("setting testSuite to [" + testSuite + "]");
451: this .testSuite = testSuite;
452: }
453:
454: public void beforeRun(TestRunner testRunner,
455: TestRunContext runContext) {
456: log.info("Running soapUI testcase ["
457: + testRunner.getTestCase().getName() + "]");
458: }
459:
460: public void beforeStep(TestRunner testRunner,
461: TestRunContext runContext) {
462: TestStep currentStep = runContext.getCurrentStep();
463: log.info("running step [" + currentStep.getName() + "]");
464:
465: if (currentStep instanceof WsdlTestRequestStep) {
466: WsdlTestRequestStep requestStep = (WsdlTestRequestStep) currentStep;
467: if (endpoint != null && endpoint.length() > 0) {
468: requestStep.getTestRequest().setEndpoint(endpoint);
469: }
470:
471: if (host != null && host.length() > 0) {
472: try {
473: String ep = Tools.replaceHost(requestStep
474: .getTestRequest().getEndpoint(), host);
475: requestStep.getTestRequest().setEndpoint(ep);
476: } catch (Exception e) {
477: log.error("Failed to set host on endpoint", e);
478: }
479: }
480:
481: if (username != null && username.length() > 0) {
482: requestStep.getTestRequest().setUsername(username);
483: }
484:
485: if (password != null && password.length() > 0) {
486: requestStep.getTestRequest().setPassword(password);
487: }
488:
489: if (domain != null && domain.length() > 0) {
490: requestStep.getTestRequest().setDomain(domain);
491: }
492:
493: if (wssPasswordType != null && wssPasswordType.length() > 0) {
494: requestStep
495: .getTestRequest()
496: .setWssPasswordType(
497: wssPasswordType.equals("Digest") ? WsdlTestRequest.PW_TYPE_DIGEST
498: : WsdlTestRequest.PW_TYPE_TEXT);
499: }
500: }
501: }
502:
503: public void afterStep(TestRunner testRunner,
504: TestRunContext runContext, TestStepResult result) {
505: TestStep currentStep = runContext.getCurrentStep();
506:
507: if (currentStep instanceof WsdlTestRequestStep) {
508: WsdlTestRequestStep requestStep = (WsdlTestRequestStep) currentStep;
509: for (int c = 0; c < requestStep.getAssertionCount(); c++) {
510: WsdlMessageAssertion assertion = requestStep
511: .getAssertionAt(c);
512: log.info("Assertion [" + assertion.getName()
513: + "] has status " + assertion.getStatus());
514: if (assertion.getStatus() == AssertionStatus.FAILED) {
515: log.info("ASSERTION FAILED -> "
516: + assertion.getErrors());
517: assertions.add(assertion);
518: assertionResults.put(assertion,
519: (WsdlTestStepResult) result);
520: }
521:
522: testAssertionCount++;
523: }
524: }
525:
526: String countPropertyName = currentStep.getName() + " run count";
527: Long count = (Long) runContext.getProperty(countPropertyName);
528: if (count == null) {
529: count = new Long(0);
530: }
531:
532: runContext.setProperty(countPropertyName, new Long(count
533: .longValue() + 1));
534:
535: if (result.getStatus() == TestStepStatus.FAILED || exportAll) {
536: try {
537: String nameBase = currentStep.getTestCase()
538: .getTestSuite().getName()
539: + "-"
540: + currentStep.getTestCase().getName()
541: + "-"
542: + currentStep.getName()
543: + "-"
544: + count.longValue() + "-" + result.getStatus();
545:
546: String fileName = nameBase + ".txt";
547: if (outputFolder != null) {
548: fileName = outputFolder + File.separator + fileName;
549: }
550:
551: if (result.getStatus() == TestStepStatus.FAILED)
552: log.error(currentStep.getName()
553: + " failed, exporting to [" + fileName
554: + "]");
555:
556: PrintWriter writer = new PrintWriter(fileName);
557: result.writeTo(writer);
558: writer.close();
559:
560: // write attachments
561: if (result instanceof WsdlTestRequestStepResult) {
562: Attachment[] attachments = ((WsdlTestRequestStepResult) result)
563: .getResponseAttachments();
564: if (attachments != null && attachments.length > 0) {
565: for (int c = 0; c < attachments.length; c++) {
566: fileName = nameBase + "-attachment-"
567: + (c + 1) + ".";
568:
569: Attachment attachment = attachments[c];
570: String contentType = attachment
571: .getContentType();
572: if (!"application/octet-stream"
573: .equals(contentType)
574: && contentType != null
575: && contentType.indexOf('/') != -1) {
576: fileName += contentType
577: .substring(contentType
578: .lastIndexOf('/') + 1);
579: } else {
580: fileName += "dat";
581: }
582:
583: if (outputFolder != null) {
584: fileName = outputFolder
585: + File.separator + fileName;
586: }
587:
588: FileOutputStream outFile = new FileOutputStream(
589: fileName);
590: Tools.writeAll(outFile, attachment
591: .getInputStream());
592: outFile.close();
593: }
594: }
595: }
596:
597: exportCount++;
598: } catch (Exception e) {
599: log.error("Error saving failed result: " + e, e);
600: }
601: }
602:
603: testStepCount++;
604: }
605:
606: public void afterRun(TestRunner testRunner,
607: TestRunContext runContext) {
608: log.info("Finished running soapUI testcase ["
609: + testRunner.getTestCase().getName()
610: + "], time taken: " + testRunner.getTimeTaken()
611: + "ms, status: " + testRunner.getStatus());
612:
613: if (testRunner.getStatus() == Status.FAILED) {
614: failedTests.add(testRunner.getTestCase());
615: }
616:
617: runningTests.remove(testRunner.getTestCase());
618:
619: testCaseCount++;
620: }
621:
622: protected WsdlProject getProject() {
623: return project;
624: }
625: }
|