1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.tools;
14
15 import java.io.File;
16 import java.io.IOException;
17 import java.util.ArrayList;
18 import java.util.List;
19
20 import org.apache.commons.cli.CommandLine;
21
22 import com.eviware.soapui.SoapUI;
23 import com.eviware.soapui.impl.wsdl.WsdlProject;
24 import com.eviware.soapui.impl.wsdl.loadtest.WsdlLoadTest;
25 import com.eviware.soapui.impl.wsdl.loadtest.data.actions.ExportLoadTestLogAction;
26 import com.eviware.soapui.impl.wsdl.loadtest.data.actions.ExportStatisticsAction;
27 import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLog;
28 import com.eviware.soapui.impl.wsdl.loadtest.log.LoadTestLogEntry;
29 import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequest;
30 import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep;
31 import com.eviware.soapui.model.project.ProjectFactoryRegistry;
32 import com.eviware.soapui.model.testsuite.LoadTestRunContext;
33 import com.eviware.soapui.model.testsuite.LoadTestRunListener;
34 import com.eviware.soapui.model.testsuite.LoadTestRunner;
35 import com.eviware.soapui.model.testsuite.TestCase;
36 import com.eviware.soapui.model.testsuite.TestRunContext;
37 import com.eviware.soapui.model.testsuite.TestRunner;
38 import com.eviware.soapui.model.testsuite.TestStep;
39 import com.eviware.soapui.model.testsuite.TestStepResult;
40 import com.eviware.soapui.model.testsuite.TestSuite;
41 import com.eviware.soapui.settings.UISettings;
42 import com.eviware.soapui.support.SoapUIException;
43 import com.eviware.soapui.support.StringUtils;
44 import com.eviware.soapui.support.Tools;
45
46 /***
47 * Standalone test-runner used from maven-plugin, can also be used from command-line (see xdocs) or
48 * directly from other classes.
49 * <p>
50 * For standalone usage, set the project file (with setProjectFile) and other desired properties before
51 * calling run</p>
52 *
53 * @author Ole.Matzura
54 */
55
56 public class SoapUILoadTestRunner extends AbstractSoapUIRunner implements LoadTestRunListener
57 {
58 private String testSuite;
59 private String testCase;
60 private String endpoint;
61 private String domain;
62 private String password;
63 private String username;
64 private String host;
65 private String loadTest;
66 private boolean printReport;
67 private List<LoadTestRunner> failedTests = new ArrayList<LoadTestRunner>();
68 private int testCaseCount;
69 private int loadTestCount;
70 private int limit = -1;
71 private String wssPasswordType;
72 private long threadCount = -1;
73 private String projectPassword;
74
75 public static String TITLE = "soapUI " + SoapUI.SOAPUI_VERSION + " LoadTest Runner";
76
77 /***
78 * Runs the loadtests in the specified soapUI project file, see soapUI xdocs for details.
79 *
80 * @param args
81 * @throws Exception
82 */
83
84 public static void main( String [] args )
85 {
86 new SoapUILoadTestRunner().runFromCommandLine( args );
87 }
88
89 protected boolean processCommandLine( CommandLine cmd )
90 {
91 if( cmd.hasOption( "e"))
92 setEndpoint( cmd.getOptionValue( "e" ) );
93
94 if( cmd.hasOption( "s"))
95 setTestSuite( getCommandLineOptionSubstSpace( cmd, "s") );
96
97 if( cmd.hasOption( "c"))
98 setTestCase( cmd.getOptionValue( "c") );
99
100 if( cmd.hasOption( "l"))
101 setLoadTest( cmd.getOptionValue( "l") );
102
103 if( cmd.hasOption( "u"))
104 setUsername( cmd.getOptionValue( "u") );
105
106 if( cmd.hasOption( "p"))
107 setPassword( cmd.getOptionValue( "p") );
108
109 if( cmd.hasOption( "w"))
110 setWssPasswordType( cmd.getOptionValue( "w") );
111
112 if( cmd.hasOption( "d"))
113 setDomain( cmd.getOptionValue( "d") );
114
115 if( cmd.hasOption( "h"))
116 setHost( cmd.getOptionValue( "h") );
117
118 if( cmd.hasOption( "m"))
119 setLimit( Integer.parseInt( cmd.getOptionValue( "m") ));
120
121 if( cmd.hasOption( "n"))
122 setThreadCount( Integer.parseInt( cmd.getOptionValue( "n") ));
123
124 if( cmd.hasOption( "f"))
125 setOutputFolder( getCommandLineOptionSubstSpace( cmd, "f") );
126
127 if( cmd.hasOption( "t"))
128 setSettingsFile( getCommandLineOptionSubstSpace( cmd, "t" ) );
129
130 setPrintReport( cmd.hasOption( "r") );
131
132 if( cmd.hasOption( "x" ) ) {
133 setProjectPassword( cmd.getOptionValue("x"));
134 }
135
136 if( cmd.hasOption( "v" ) ) {
137 setSoapUISettingsPassword( cmd.getOptionValue("v"));
138 }
139
140 return true;
141 }
142
143 private void setProjectPassword(String projectPassword) {
144 this.projectPassword = projectPassword;
145 }
146
147 public void setLimit( int limit )
148 {
149 this.limit = limit;
150 }
151
152 public void setThreadCount( long threadCount )
153 {
154 this.threadCount = threadCount;
155 }
156
157 protected SoapUIOptions initCommandLineOptions()
158 {
159 SoapUIOptions options = new SoapUIOptions( "loadtestrunner" );
160 options.addOption( "e", true, "Sets the endpoint" );
161 options.addOption( "s", true, "Sets the testsuite" );
162 options.addOption( "c", true, "Sets the testcase" );
163 options.addOption( "l", true, "Sets the loadtest" );
164 options.addOption( "u", true, "Sets the username" );
165 options.addOption( "p", true, "Sets the password" );
166 options.addOption( "w", true, "Sets the WSS password type, either 'Text' or 'Digest'" );
167 options.addOption( "d", true, "Sets the domain" );
168 options.addOption( "h", true, "Sets the host" );
169 options.addOption( "m", true, "Overrides the LoadTest Limit" );
170 options.addOption( "n", true, "Overrides the LoadTest ThreadCount" );
171 options.addOption( "r", false, "Exports statistics and testlogs for each LoadTest run" );
172 options.addOption( "f", true, "Sets the output folder to export to" );
173 options.addOption( "t", true, "Sets the soapui-settings.xml file to use" );
174 options.addOption( "x", true, "Sets project password for decryption if project is encrypted" );
175 options.addOption( "v", true, "Sets password for soapui-settings.xml file");
176 return options;
177 }
178
179 public SoapUILoadTestRunner()
180 {
181 this( TITLE );
182 }
183
184 public SoapUILoadTestRunner( String title )
185 {
186 super( title );
187 }
188
189 public void setLoadTest(String loadTest)
190 {
191 this.loadTest = loadTest;
192 }
193
194 public void setPrintReport(boolean printReport)
195 {
196 this.printReport = printReport;
197 }
198
199 /***
200 * Sets the host to use by all test-requests, the existing endpoint port and path will be used
201 *
202 * @param host the host to use by all requests
203 */
204
205 public void setHost(String host)
206 {
207 this.host = host;
208 }
209
210 /***
211 * Sets the domain to use for any authentications
212 *
213 * @param domain the domain to use for any authentications
214 */
215
216 public void setDomain(String domain)
217 {
218 this.domain = domain;
219 }
220
221 /***
222 * Sets the password to use for any authentications
223 *
224 * @param domain the password to use for any authentications
225 */
226
227 public void setPassword(String password)
228 {
229 this.password = password;
230 }
231
232 /***
233 * Sets the WSS password-type to use for any authentications. Setting this will result
234 * in the addition of WS-Security UsernamePassword tokens to any outgoing request containing
235 * the specified username and password.
236 *
237 * @param wssPasswordType the wss-password type to use, either 'Text' or 'Digest'
238 */
239
240 public void setWssPasswordType( String wssPasswordType )
241 {
242 this.wssPasswordType = wssPasswordType;
243 }
244
245 /***
246 * Sets the username to use for any authentications
247 *
248 * @param domain the username to use for any authentications
249 */
250
251 public void setUsername(String username)
252 {
253 this.username = username;
254 }
255
256 /***
257 * Runs the testcases as configured with setXXX methods
258 *
259 * @throws Exception thrown if any tests fail
260 */
261
262 public boolean runRunner() throws Exception
263 {
264 if( SoapUI.getSettings().getBoolean( UISettings.DONT_DISABLE_GROOVY_LOG ))
265 {
266 initGroovyLog();
267 }
268
269 String projectFile = getProjectFile();
270
271
272 WsdlProject project = (WsdlProject) ProjectFactoryRegistry.getProjectFactory("wsdl").createNew(projectFile, getProjectPassword());
273
274 if( project.isDisabled() )
275 throw new Exception( "Failed to load soapUI project file [" + projectFile + "]" );
276 int suiteCount = 0;
277
278 for( int c = 0; c < project.getTestSuiteCount(); c++ )
279 {
280 if( testSuite == null ||
281 project.getTestSuiteAt( c ).getName().equalsIgnoreCase( testSuite ))
282 {
283 runSuite( project.getTestSuiteAt( c ));
284 suiteCount++;
285 }
286 }
287
288 if( suiteCount == 0 )
289 {
290 log.warn( "No test-suites matched argument [" + testSuite + "]" );
291 }
292 else if( testCaseCount == 0 )
293 {
294 log.warn( "No test-cases matched argument [" + testCase + "]" );
295 }
296 else if( loadTestCount == 0 )
297 {
298 log.warn( "No load-tests matched argument [" + loadTest + "]" );
299 }
300 else if( !failedTests.isEmpty() )
301 {
302 log.info( failedTests.size() + " load tests failed:" );
303 for( LoadTestRunner loadTestRunner : failedTests )
304 {
305 log.info( loadTestRunner.getLoadTest().getName() + ": " + loadTestRunner.getReason() );
306 }
307
308 throw new SoapUIException( "LoadTests failed" );
309 }
310
311 return true;
312 }
313
314 public String getProjectPassword() {
315 return projectPassword;
316 }
317
318 /***
319 * Run tests in the specified TestSuite
320 *
321 * @param suite the TestSuite to run
322 */
323
324 public void runSuite(TestSuite suite)
325 {
326 long start = System.currentTimeMillis();
327 for( int c = 0; c < suite.getTestCaseCount(); c++ )
328 {
329 String name = suite.getTestCaseAt( c ).getName();
330 if( testCase == null ||
331 name.equalsIgnoreCase( testCase ))
332 {
333 runTestCase( suite.getTestCaseAt( c ));
334 testCaseCount++;
335 }
336 else
337 log.info( "Skipping testcase [" + name + "], filter is [" + testCase + "]");
338 }
339 log.info( "soapUI suite [" + suite.getName() + "] finished in " + (System.currentTimeMillis()-start) + "ms" );
340 }
341
342 /***
343 * Runs the specified TestCase
344 *
345 * @param testCase the testcase to run
346 */
347
348 private void runTestCase(TestCase testCase)
349 {
350 for( int c = 0; c < testCase.getLoadTestCount(); c++ )
351 {
352 String name = testCase.getLoadTestAt( c ).getName();
353 if( loadTest == null || loadTest.equalsIgnoreCase( name ))
354 {
355 runWsdlLoadTest( (WsdlLoadTest) testCase.getLoadTestAt( c ));
356 loadTestCount++;
357 }
358 }
359 }
360
361 /***
362 * Runs the specified LoadTest
363 *
364 * @param loadTest the loadTest to run
365 */
366
367 private void runWsdlLoadTest(WsdlLoadTest loadTest)
368 {
369 try
370 {
371 log.info( "Running LoadTest [" + loadTest.getName() + "]" );
372 if( limit >= 0 )
373 {
374 log.info( "Overriding limit [" + loadTest.getTestLimit() + "] with specified [" + limit + "]" );
375 loadTest.setTestLimit( limit );
376 }
377
378 if( threadCount >= 0 )
379 {
380 log.info( "Overriding threadCount [" + loadTest.getThreadCount() + "] with specified [" + threadCount + "]" );
381 loadTest.setThreadCount( threadCount );
382 }
383
384 loadTest.addLoadTestRunListener(this);
385 LoadTestRunner runner = loadTest.run();
386
387
388 while (runner.getStatus() == LoadTestRunner.Status.RUNNING )
389 {
390 log.info( "LoadTest [" + loadTest.getName() + "] progress: " + runner.getProgress() + ", " +
391 runner.getRunningThreadCount() );
392 Thread.sleep(1000);
393 }
394
395 log.info( "LoadTest [" + loadTest.getName() + "] finished with status " + runner.getStatus().toString() );
396
397 if( printReport )
398 {
399 log.info( "Exporting log and statistics for LoadTest [" + loadTest.getName() + "]" );
400
401 loadTest.getStatisticsModel().finish();
402
403 exportLog( loadTest );
404 exportStatistics( loadTest );
405 }
406 }
407 catch (Exception e)
408 {
409 SoapUI.logError( e );
410 log.error( e );
411 }
412 }
413
414 private void exportStatistics(WsdlLoadTest loadTest) throws IOException
415 {
416 ExportStatisticsAction exportStatisticsAction = new ExportStatisticsAction( loadTest.getStatisticsModel() );
417 String statisticsFileName = StringUtils.createFileName( loadTest.getName(), '_' ) + "-statistics.txt";
418 if( getOutputFolder() != null )
419 {
420 ensureOutputFolder( loadTest );
421 statisticsFileName = getAbsoluteOutputFolder(loadTest) + File.separator + statisticsFileName;
422 }
423
424 int cnt = exportStatisticsAction.exportToFile( new File( statisticsFileName ));
425 log.info( "Exported " + cnt + " statistics to [" + statisticsFileName + "]" );
426 }
427
428 private void exportLog(WsdlLoadTest loadTest) throws IOException
429 {
430
431 LoadTestLog loadTestLog = loadTest.getLoadTestLog();
432 ExportLoadTestLogAction exportLoadTestLogAction = new ExportLoadTestLogAction(loadTestLog, null);
433 String logFileName = StringUtils.createFileName( loadTest.getName(), '_' ) + "-log.txt";
434 if( getOutputFolder() != null )
435 {
436 ensureOutputFolder( loadTest );
437 logFileName = getAbsoluteOutputFolder(loadTest) + File.separator + logFileName;
438 }
439
440 int cnt = exportLoadTestLogAction.exportToFile( new File( logFileName ));
441 log.info( "Exported " + cnt + " log items to [" + logFileName + "]" );
442
443 int errorCnt = 0;
444 for( int c = 0; c < loadTestLog.getSize(); c++ )
445 {
446 LoadTestLogEntry entry = (LoadTestLogEntry) loadTestLog.getElementAt( c );
447
448 if( entry != null && entry.isError() )
449 {
450 String entryFileName = StringUtils.createFileName( loadTest.getName(), '_' ) + "-error-" + errorCnt++ + "-entry.txt";
451 if( getOutputFolder() != null )
452 {
453 ensureOutputFolder( loadTest );
454 entryFileName = getAbsoluteOutputFolder(loadTest) + File.separator + entryFileName;
455 }
456
457 try
458 {
459 entry.exportToFile( entryFileName );
460 }
461 catch (Exception e)
462 {
463 SoapUI.logError( e );
464 }
465 }
466 }
467 log.info( "Exported " + errorCnt + " error results" );
468 }
469
470 /***
471 * Sets the testcase to run
472 *
473 * @param testCase the testcase to run
474 */
475
476 public void setTestCase(String testCase)
477 {
478 this.testCase = testCase;
479 }
480
481 /***
482 * Sets the endpoint to use for all test requests
483 *
484 * @param endpoint the endpoint to use for all test requests
485 */
486
487 public void setEndpoint(String endpoint)
488 {
489 this.endpoint = endpoint.trim();
490 }
491
492 /***
493 * Sets the TestSuite to run. If not set all TestSuites in the specified project file are run
494 *
495 * @param testSuite the testSuite to run.
496 */
497
498 public void setTestSuite(String testSuite)
499 {
500 this.testSuite = testSuite;
501 }
502
503 public void afterLoadTest(LoadTestRunner loadTestRunner, LoadTestRunContext context)
504 {
505 if( loadTestRunner.getStatus() == LoadTestRunner.Status.FAILED )
506 {
507 failedTests.add( loadTestRunner );
508 }
509 }
510
511 public void afterTestCase(LoadTestRunner loadTestRunner, LoadTestRunContext context, TestRunner testRunner, TestRunContext runContext)
512 {
513 }
514
515 public void afterTestStep(LoadTestRunner loadTestRunner, LoadTestRunContext context, TestRunner testRunner, TestRunContext runContext, TestStepResult testStepResult)
516 {
517 }
518
519 public void beforeLoadTest(LoadTestRunner loadTestRunner, LoadTestRunContext context)
520 {
521 }
522
523 public void beforeTestCase(LoadTestRunner loadTestRunner, LoadTestRunContext context, TestRunner testRunner, TestRunContext runContext)
524 {
525 }
526
527 public void beforeTestStep(LoadTestRunner loadTestRunner, LoadTestRunContext context, TestRunner testRunner, TestRunContext runContext, TestStep testStep)
528 {
529 if( testStep instanceof WsdlTestRequestStep )
530 {
531 WsdlTestRequestStep requestStep = (WsdlTestRequestStep) testStep;
532 if( endpoint != null && endpoint.length() > 0 )
533 {
534 requestStep.getTestRequest().setEndpoint( endpoint );
535 }
536
537 if( host != null && host.length() > 0 )
538 {
539 try
540 {
541 String ep = Tools.replaceHost( requestStep.getTestRequest().getEndpoint(), host );
542 requestStep.getTestRequest().setEndpoint( ep );
543 }
544 catch (Exception e)
545 {
546 log.error( "Failed to set host on endpoint", e );
547 }
548 }
549
550 if( username != null && username.length() > 0 )
551 {
552 requestStep.getTestRequest().setUsername( username );
553 }
554
555 if( password != null && password.length() > 0 )
556 {
557 requestStep.getTestRequest().setPassword( password );
558 }
559
560 if( domain != null && domain.length() > 0 )
561 {
562 requestStep.getTestRequest().setDomain( domain );
563 }
564
565 if( wssPasswordType != null && wssPasswordType.length() > 0 )
566 {
567 requestStep.getTestRequest().setWssPasswordType( wssPasswordType.equals( "Digest" ) ?
568 WsdlTestRequest.PW_TYPE_DIGEST : WsdlTestRequest.PW_TYPE_TEXT );
569 }
570 }
571 }
572
573 public void loadTestStarted(LoadTestRunner loadTestRunner, LoadTestRunContext context)
574 {
575 }
576
577 public void loadTestStopped(LoadTestRunner loadTestRunner, LoadTestRunContext context)
578 {
579 }
580 }