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