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     private final static Logger log = Logger.getLogger(WsdlTestCaseRunner.class);
55  
56     private static int idCounter = 0;
57     private Timer timeoutTimer;
58     private TimeoutTimerTask timeoutTimerTask;
59  
60     public WsdlTestCaseRunner(WsdlTestCase testCase, StringToObjectMap properties)
61     {
62        this.testCase = testCase;
63        status = Status.INITIALIZED;
64        runContext = new WsdlTestRunContext(this, properties);
65        id = ++idCounter;
66     }
67  
68     public WsdlTestRunContext getRunContext()
69     {
70        return runContext;
71     }
72  
73     public void start(boolean async)
74     {
75        status = Status.RUNNING;
76        if (async)
77           future = threadPool.submit(this);
78        else
79           run();
80     }
81  
82     public void cancel(String reason)
83     {
84        if (status == Status.CANCELED || status == Status.FINISHED || status == Status.FAILED ||
85                runContext == null) return;
86        TestStep currentStep = runContext.getCurrentStep();
87        if (currentStep != null)
88           currentStep.cancel();
89        status = Status.CANCELED;
90        this.reason = reason;
91     }
92  
93     public void fail(String reason)
94     {
95        if (status == Status.CANCELED || status == Status.FAILED ||
96                runContext == null) return;
97        TestStep currentStep = runContext.getCurrentStep();
98        if (currentStep != null)
99           currentStep.cancel();
100       status = Status.FAILED;
101       this.reason = reason;
102    }
103 
104    public Status getStatus()
105    {
106       return status;
107    }
108 
109    public int getId()
110    {
111       return id;
112    }
113 
114    public void run()
115    {
116       int initCount = 0;
117 
118       try
119       {
120          gotoStepIndex = -1;
121          testStepResults.clear();
122 
123          listeners = testCase.getTestRunListeners();
124 
125          // create state for testcase if specified
126          if (testCase.getKeepSession())
127          {
128             runContext.setProperty(SubmitContext.HTTP_STATE_PROPERTY, new HttpState());
129          }
130 
131          testCase.runSetupScript(runContext, this);
132 
133          status = Status.RUNNING;
134          startTime = System.currentTimeMillis();
135 
136          if (testCase.getTimeout() > 0)
137          {
138             timeoutTimer = new Timer();
139             timeoutTimerTask = new TimeoutTimerTask();
140             timeoutTimer.schedule(timeoutTimerTask, testCase.getTimeout());
141          }
142 
143          for (int i = 0; i < listeners.length; i++)
144          {
145             listeners[i].beforeRun(this, runContext);
146             if (status != Status.RUNNING)
147                return;
148          }
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 = runContext.getCurrentStepIndex();
169 
170          for (currentStepIndex = 0; 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 runTestStep(TestStep testStep, boolean discard, boolean process)
248    {
249       for (int i = 0; i < listeners.length; i++)
250       {
251          listeners[i].beforeStep(this, runContext);
252          if (status == Status.CANCELED || status == Status.FAILED)
253             return null;
254       }
255 
256       TestStepResult stepResult = testStep.run(this, runContext);
257       testStepResults.add(stepResult);
258       resultCount++;
259       enforceMaxSize();
260 
261       for (int i = 0; i < listeners.length; i++)
262       {
263          listeners[i].afterStep(this, runContext, stepResult);
264       }
265 
266       // discard?
267       if (discard && stepResult.getStatus() == TestStepStatus.OK && testCase.getDiscardOkResults() &&
268               !stepResult.isDiscarded())
269       {
270          stepResult.discard();
271       }
272 
273       if (process && stepResult.getStatus() == TestStepStatus.FAILED)
274       {
275          if (testCase.getFailOnError())
276          {
277             error = stepResult.getError();
278             fail("Cancelling due to failed test step");
279          }
280          else
281          {
282             runContext.setProperty(TestRunner.Status.class.getName(), TestRunner.Status.FAILED);
283          }
284       }
285 
286       return stepResult;
287    }
288 
289    private void notifyAfterRun()
290    {
291       if (listeners == null || listeners.length == 0)
292          return;
293 
294       for (int i = 0; i < listeners.length; i++)
295       {
296          listeners[i].afterRun(this, runContext);
297       }
298    }
299 
300    public TestCase getTestCase()
301    {
302       return testCase;
303    }
304 
305    public synchronized Status waitUntilFinished()
306    {
307       if (future != null)
308       {
309          if (!future.isDone())
310          {
311             try
312             {
313                future.get();
314             }
315             catch (Exception e)
316             {
317                SoapUI.logError(e);
318             }
319          }
320       }
321       else
322          throw new RuntimeException("cannot wait on null future");
323 
324 
325       return getStatus();
326    }
327 
328    public long getTimeTaken()
329    {
330       long sum = 0;
331       for (int c = 0; c < testStepResults.size(); c++)
332       {
333          TestStepResult testStepResult = testStepResults.get(c);
334          if (testStepResult != null)
335             sum += testStepResult.getTimeTaken();
336       }
337 
338       return sum;
339    }
340 
341    public long getStartTime()
342    {
343       return startTime;
344    }
345 
346    public Throwable getError()
347    {
348       return error;
349    }
350 
351    public String getReason()
352    {
353       return reason == null ? error == null ? null : error.toString() : reason;
354    }
355 
356    public List<TestStepResult> getResults()
357    {
358       return testStepResults;
359    }
360 
361    public int getResultCount()
362    {
363       return resultCount;
364    }
365 
366    public void gotoStep(int index)
367    {
368       gotoStepIndex = index;
369    }
370 
371    private void enforceMaxSize()
372    {
373       int maxResults = testCase.getMaxResults();
374       if (maxResults < 1)
375          return;
376 
377       synchronized (this)
378       {
379          while (testStepResults.size() > maxResults)
380          {
381             testStepResults.remove(0);
382          }
383       }
384    }
385 
386    public void gotoStepByName(String stepName)
387    {
388       TestStep testStep = getTestCase().getTestStepByName(stepName);
389       if (testStep != null)
390          gotoStep(getTestCase().getIndexOfTestStep(testStep));
391    }
392 
393    private final class TimeoutTimerTask extends TimerTask
394    {
395       @Override
396       public void run()
397       {
398          fail("TestCase timed out");
399       }
400    }
401 }