View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2007 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.impl.wsdl.testcase;
14  
15  import java.util.ArrayList;
16  import java.util.Collections;
17  import java.util.List;
18  import java.util.Timer;
19  import java.util.TimerTask;
20  import java.util.concurrent.ExecutorService;
21  import java.util.concurrent.Executors;
22  import java.util.concurrent.Future;
23  
24  import org.apache.commons.httpclient.HttpState;
25  import org.apache.log4j.Logger;
26  
27  import com.eviware.soapui.SoapUI;
28  import com.eviware.soapui.impl.wsdl.teststeps.WsdlTestStep;
29  import com.eviware.soapui.model.iface.SubmitContext;
30  import com.eviware.soapui.model.testsuite.TestCase;
31  import com.eviware.soapui.model.testsuite.TestRunListener;
32  import com.eviware.soapui.model.testsuite.TestRunner;
33  import com.eviware.soapui.model.testsuite.TestStep;
34  import com.eviware.soapui.model.testsuite.TestStepResult;
35  import com.eviware.soapui.model.testsuite.TestStepResult.TestStepStatus;
36  import com.eviware.soapui.support.UISupport;
37  import com.eviware.soapui.support.types.StringToObjectMap;
38  
39  /***
40   * WSDL TestCase Runner - runs all steps in a testcase and collects performance data
41   * 
42   * @author Ole.Matzura
43   */
44  
45  public class WsdlTestCaseRunner implements Runnable, TestRunner
46  {
47  	private TestRunListener [] listeners;
48  	private final WsdlTestCase testCase;
49  	private Status status;
50  	private Throwable error;
51  	private WsdlTestRunContext runContext;
52  	private List<TestStepResult> testStepResults = new ArrayList<TestStepResult>();
53  	private int gotoStepIndex;
54  	private long startTime;
55  	private String reason;
56  	private volatile Future<?> future;
57  	private int id;
58  	private final static ExecutorService threadPool = Executors.newCachedThreadPool();
59  	private final static Logger log = Logger.getLogger( WsdlTestCaseRunner.class );
60  	
61  	private static int idCounter = 0;
62  	private Timer timeoutTimer;
63  	private TimeoutTimerTask timeoutTimerTask;
64  	
65  	public WsdlTestCaseRunner( WsdlTestCase testCase, StringToObjectMap properties )
66  	{
67  		this.testCase = testCase;
68  		status = Status.INITIALIZED;
69  		runContext = new WsdlTestRunContext( this, properties );
70  		id = ++idCounter;
71  	}
72  	
73  	public WsdlTestRunContext getRunContext()
74  	{
75  		return runContext;
76  	}
77  
78  	public void start( boolean async ) 
79  	{
80  		status = Status.RUNNING;
81  		if( async )
82  			future = threadPool.submit(this);
83  		else 
84  			run();
85  	}
86  	
87  	public void cancel( String reason )
88  	{
89  		if( status == Status.CANCELED || status == Status.FINISHED || status == Status.FAILED ||
90  				runContext == null ) return;
91  		TestStep currentStep = runContext.getCurrentStep();
92  		if( currentStep != null )
93  			currentStep.cancel();
94  		status = Status.CANCELED;
95  		this.reason = reason;
96  	}
97  
98  	public void fail( String reason )
99  	{
100 		if( status == Status.CANCELED || status == Status.FAILED ||
101 				runContext == null ) return;
102 		TestStep currentStep = runContext.getCurrentStep();
103 		if( currentStep != null )
104 			currentStep.cancel();
105 		status = Status.FAILED;
106 		this.reason = reason;
107 	}
108 	
109 	public Status getStatus()
110 	{
111 		return status;
112 	}
113 
114 	public int getId()
115 	{
116 		return id;
117 	}
118 
119 	public void run() 
120 	{
121 		int initCount = 0;
122 		
123 		try 
124 		{
125 			gotoStepIndex = -1;
126 			testStepResults.clear();
127 			
128 			listeners = testCase.getTestRunListeners();
129 			
130 			// create state for testcase if specified
131 			if( testCase.getKeepSession() )
132 			{
133 			   runContext.setProperty( SubmitContext.HTTP_STATE_PROPERTY, new HttpState() );	
134 			}
135 
136 			testCase.runSetupScript( runContext, this );
137 			
138 			status = Status.RUNNING;
139 			startTime = System.currentTimeMillis();
140 			
141 			if( testCase.getTimeout() > 0 )
142 			{
143 				timeoutTimer = new Timer();
144 				timeoutTimerTask = new TimeoutTimerTask();
145 				timeoutTimer.schedule( timeoutTimerTask, testCase.getTimeout() );
146 			}
147 			
148 			for (int i = 0; i < listeners.length; i++) 
149 			{
150 				listeners[i].beforeRun( this, runContext );
151 				if( status != Status.RUNNING )
152 					return;
153 			}
154 			
155 			for( ; initCount < testCase.getTestStepCount() && status == Status.RUNNING; initCount++ )
156 			{
157 				WsdlTestStep testStep = testCase.getTestStepAt( initCount );
158 				if( testStep.isDisabled() )
159 					continue;
160 				
161 				try
162 				{
163 					testStep.prepare( this, runContext );
164 				}
165 				catch( Exception e )
166 				{
167 					status = Status.FAILED;
168 					throw new Exception( "Failed to prepare testStep [" + testStep.getName() + "]; " + e.toString() );
169 				}
170 			}
171 			
172 			int currentStepIndex = runContext.getCurrentStepIndex();
173 
174 			for ( currentStepIndex = 0; status == Status.RUNNING && currentStepIndex < testCase.getTestStepCount(); currentStepIndex++) 
175 			{
176 				TestStep currentStep = runContext.getCurrentStep();
177 				if( !currentStep.isDisabled() )
178 				{
179 					for (int i = 0; i < listeners.length; i++) {
180 						listeners[i].beforeStep(this, runContext );
181 						if( status == Status.CANCELED || status == Status.FAILED )
182 							return;
183 					}
184 					
185 					TestStepResult stepResult = currentStep.run( this, runContext );
186 					testStepResults.add( stepResult );
187 					
188 					for (int i = 0; i < listeners.length; i++) 
189 					{
190 						listeners[i].afterStep( this, runContext, stepResult );
191 					}
192 					
193 					// discard?
194 					if( stepResult.getStatus() == TestStepStatus.OK && testCase.getDiscardOkResults() && 
195 						 !stepResult.isDiscarded()	)
196 					{
197 						stepResult.discard();
198 					}
199 	
200 					if( stepResult.getStatus() == TestStepStatus.FAILED )
201 					{
202 						if( testCase.getFailOnError()  )
203 						{
204 							error = stepResult.getError();
205 							fail( "Cancelling due to failed test step" );
206 						}
207 						else
208 						{
209 							runContext.setProperty( TestRunner.Status.class.getName(), TestRunner.Status.FAILED );
210 						}
211 					}
212 					
213 					if( status == Status.CANCELED || status == Status.FAILED )
214 						return;
215 	
216 					if( gotoStepIndex != -1 )
217 					{
218 						currentStepIndex = gotoStepIndex-1;
219 						gotoStepIndex = -1;
220 					}
221 				}
222 				
223 				runContext.setCurrentStep( currentStepIndex+1 );
224 			}
225 			
226 			if( runContext.getProperty( TestRunner.Status.class.getName() ) == TestRunner.Status.FAILED &&
227 				 testCase.getFailTestCaseOnErrors() )
228 			{
229 				fail( "Failing due to failed test step" );
230 			}
231 		} 
232 		catch (Throwable t) 
233 		{
234 			log.error( "Exception during TestCase Execution", t );
235 
236 			if( t instanceof OutOfMemoryError && 
237 			    UISupport.confirm( "Exit now without saving?", "Out of Memory Error" ) )
238 			{
239 				System.exit( 0 );
240 			}
241 			
242 			status = Status.FAILED;
243 			error = t;
244 			reason = t.toString();
245 		}
246 		finally
247 		{
248 			if( timeoutTimer != null )
249 			{
250 				timeoutTimer.cancel();
251 			}
252 			
253 			if( status == Status.RUNNING )
254 			{
255 				status = Status.FINISHED;
256 			}
257 			
258 			for( int c = 0; c < initCount; c++ )
259 			{
260 				WsdlTestStep testStep = testCase.getTestStepAt( c );
261 				if( !testStep.isDisabled() )
262 					testStep.finish( this, runContext );
263 			}
264 			
265 			notifyAfterRun();
266 			
267 			try
268 			{
269 				testCase.runTearDownScript( runContext, this );
270 			}
271 			catch( Exception e )
272 			{
273 				SoapUI.logError( e );
274 			}
275 			
276 			runContext.clear();
277 			listeners = null;
278 		}
279 	}
280 
281 	private void notifyAfterRun()
282 	{
283 		if( listeners == null || listeners.length == 0 )
284 			return;
285 		
286 		for (int i = 0; i < listeners.length; i++) 
287 		{
288 			listeners[i].afterRun( this, runContext );
289 		}
290 	}
291 
292 	public TestCase getTestCase() {
293 		return testCase;
294 	}
295 
296 	public synchronized Status waitUntilFinished()
297 	{
298 		if (future != null)
299 		{
300 			if (!future.isDone())
301 			{
302 				try
303 				{
304 					future.get();
305 				}
306 				catch (Exception e)
307 				{
308 					SoapUI.logError( e );
309 				}
310 			}
311 		}
312 		else
313 			throw new RuntimeException("cannot wait on null future");
314 
315 		
316 		return getStatus();
317 	}
318 	
319 	public long getTimeTaken()
320 	{
321 		long sum = 0;
322 		for( int c = 0; c < testStepResults.size(); c++ )
323 		{
324 			TestStepResult testStepResult = testStepResults.get( c );
325 			if( testStepResult != null )
326 				sum += testStepResult.getTimeTaken();
327 		}
328 		
329 		return sum;
330 	}
331 	
332 	public long getStartTime()
333 	{
334 		return startTime;
335 	}
336 
337 	public Throwable getError()
338 	{
339 		return error;
340 	}
341 	
342 	public String getReason()
343 	{
344 		return reason == null ? error == null ? null : error.toString() : reason;
345 	}
346 
347 	public List<TestStepResult> getResults()
348 	{
349 		return Collections.unmodifiableList( testStepResults );
350 	}
351 
352 	public void gotoStep(int index)
353 	{
354 		gotoStepIndex  = index;
355 	}
356 
357 	public void gotoStepByName(String stepName)
358 	{
359 		TestStep testStep = getTestCase().getTestStepByName( stepName );
360 		if( testStep != null )
361 			gotoStep( getTestCase().getIndexOfTestStep( testStep));
362 	}
363 	
364 	private final class TimeoutTimerTask extends TimerTask
365 	{
366 		@Override
367 		public void run()
368 		{
369 			fail( "TestCase timed out" );
370 		}
371 	}
372 }