View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2008 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by 
6    *  the Free Software Foundation.
7    *
8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
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 //		WsdlProject project = new WsdlProject( projectFile, getProjectPassword() );
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 			// wait for test to finish
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 		// export log first
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 }