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