View Javadoc

1   // $Id: ExceptionHandler.java 351 2008-08-14 20:20:56Z jg_hamburg $
2   /********************************************************************************
3    * DDTUnit, a Datadriven Approach to Unit- and Moduletesting
4    * Copyright (c) 2004, Joerg and Kai Gellien
5    * All rights reserved.
6    * 
7    * The Software is provided under the terms of the Common Public License 1.0
8    * as provided with the distribution of DDTUnit in the file cpl-v10.html.
9    * Redistribution and use in source and binary forms, with or without 
10   * modification, are permitted provided that the following conditions
11   * are met:
12   * 
13   *     + Redistributions of source code must retain the above copyright 
14   *       notice, this list of conditions and the following disclaimer. 
15   *       
16   *     + Redistributions in binary form must reproduce the above 
17   *       copyright notice, this list of conditions and the following 
18   *       disclaimer in the documentation and/or other materials provided 
19   *       with the distribution. 
20   *       
21   *     + Neither the name of the authors or DDTUnit, nor the 
22   *       names of its contributors may be used to endorse or promote 
23   *       products derived from this software without specific prior 
24   *       written permission. 
25   * 
26   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
27   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
28   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
29   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 
30   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
31   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
32   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
33   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
34   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
35   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
36   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37   ********************************************************************************/
38  package junitx.ddtunit;
39  
40  import java.lang.reflect.InvocationTargetException;
41  import java.util.HashMap;
42  import java.util.Iterator;
43  import java.util.Map;
44  import java.util.Map.Entry;
45  
46  import junit.framework.AssertionFailedError;
47  import junitx.ddtunit.data.AssertObject;
48  import junitx.ddtunit.data.ExceptionAsserter;
49  import junitx.ddtunit.data.TypedObject;
50  import junitx.ddtunit.data.TypedObjectMap;
51  
52  import org.slf4j.Logger;
53  import org.slf4j.LoggerFactory;
54  
55  /**
56   * Handler for processing of exceptions caught during tst execution.
57   * 
58   * @author jg
59   */
60  class ExceptionHandler {
61  
62      private Logger log = LoggerFactory.getLogger(ExceptionHandler.class);
63  
64      private String methodName;
65  
66      private Map methodTestFailure;
67  
68      private Map methodTestError;
69  
70      private static final String LF = System.getProperty("line.separator");
71  
72      /**
73       * Instanciate ExceptionHandler
74       * 
75       * @param methodName
76       */
77      public ExceptionHandler(String methodName) {
78          this.methodName = methodName;
79          this.methodTestFailure = new HashMap();
80          this.methodTestError = new HashMap();
81      }
82  
83      /**
84       * Process caught exception of test
85       * 
86       * @param testId identifiing method test
87       * @param aThrowable
88       * @param assertMap containing infod about expected exceptions
89       * @throws Throwable uncaught exception
90       */
91      public void process(String testId, Throwable aThrowable,
92              TypedObjectMap assertMap) throws Throwable {
93          log.debug("process(" + methodName + ", " + testId + ", "
94                  + aThrowable.getMessage() + ") - START");
95          if (InvocationTargetException.class.isInstance(aThrowable)) {
96              InvocationTargetException myEx = (InvocationTargetException) aThrowable;
97              aThrowable.fillInStackTrace();
98              addErrorFailure(testId, myEx.getTargetException(), assertMap);
99          } else {
100             addErrorFailure(testId, aThrowable, assertMap);
101         }
102         log.debug("process(" + methodName + ", " + testId + ", "
103                 + aThrowable.getMessage() + ") - END");
104     }
105 
106     /**
107      * @param testId
108      * @param aThrowable
109      * @param assertMap containing all expected exception infos
110      */
111     private void addErrorFailure(String testId, Throwable aThrowable,
112             TypedObjectMap assertMap) throws Throwable {
113         if (!ignoreExpected(testId, aThrowable, assertMap)) {
114             if (AssertionFailedError.class.isInstance(aThrowable)) {
115                 AssertionFailedError assertError = (AssertionFailedError) aThrowable;
116                 this.methodTestFailure.put(testId, new DDTTestFailure(null,
117                         testId, assertError));
118                 throw assertError;
119             } else {
120                 this.methodTestError.put(testId, new DDTTestFailure(null,
121                         testId, aThrowable));
122                 throw aThrowable;
123             }
124         }
125     }
126 
127     /**
128      * Check if provided exception is expected to be thrown during test
129      * execution. <br/>If expected ignore this exception.
130      * 
131      * @param testId
132      * @param aThrowable
133      */
134     private boolean ignoreExpected(String testId, Throwable aThrowable,
135             TypedObjectMap assertMap) {
136         log.debug("filter(" + methodName + ", " + testId + ", "
137                 + aThrowable.getMessage() + ") - START");
138         boolean caughtExpectedException = false;
139 
140         if (assertMap != null) {
141             Throwable lastCaughtException = null;
142             for (Iterator iter = assertMap.entrySet().iterator(); iter
143                 .hasNext();) {
144                 Entry assertEntry = (Entry) iter.next();
145                 AssertObject assertObj = (AssertObject) assertEntry.getValue();
146                 if (ExceptionAsserter.class.isInstance(assertObj)) {
147                     ExceptionAsserter exAssert = (ExceptionAsserter) assertObj;
148                     try {
149                         exAssert.setActualObject(aThrowable);
150                         exAssert.validate(false);
151                         caughtExpectedException = true;
152                         break;
153                         // } catch (DDTException ex) {
154                         // if (!ex.getMessage().startsWith(
155                         // "Class types of actual (")) {
156                         // throw ex;
157                         // }
158                     } catch (Throwable ex) {
159                         // catch any exception thrown by assertion
160                         lastCaughtException = ex;
161                         log
162                             .debug(
163                                 "Exception caught during check on expected exception.",
164                                 lastCaughtException);
165                     }
166                 }
167             }
168             if (caughtExpectedException) {
169                 log.debug("Caught expected exception in test " + testId + ":"
170                         + aThrowable.getMessage());
171             }
172         }
173         log.debug("filter(" + methodName + ", " + testId + ", "
174                 + aThrowable.getMessage() + ") found: "
175                 + caughtExpectedException + "- END");
176         return caughtExpectedException;
177     }
178 
179     /**
180      * Summarize problems of method tests. <br/>If any errors were detected a
181      * method error will be thrown, <br/>if only assertion errors were detected
182      * only an assertion error will be thrown. <br/>First line contains summary
183      * statistics, following lines
184      * 
185      * @param testCount under testmethod
186      */
187     void summarizeProblems(int testCount) {
188         int errorCount = this.methodTestError.size();
189         int failureCount = this.methodTestFailure.size();
190         if (errorCount == 0 && failureCount == 0) {
191             return;
192         }
193         StringBuffer sb = new StringBuffer("method ").append(this.methodName)
194             .append(" - ");
195         sb.append("Total: " + testCount + ", Errors: " + errorCount
196                 + ", Failures: " + failureCount + LF);
197         Throwable testError = null;
198         if (errorCount > 0) {
199             testError = summariesErrors(sb, testError);
200         }
201         if (failureCount > 0) {
202             if (errorCount > 0) {
203                 sb.append(LF);
204             }
205             testError = summariesFailures(sb, testError);
206         }
207 
208         if (errorCount == 0) {
209             AssertionFailedError afEx = new AssertionFailedError(sb.toString());
210             afEx.setStackTrace(testError.getStackTrace());
211             throw afEx;
212         } else {
213             DDTException processError = new DDTException(sb.toString(),
214                     testError);
215             processError.setStackTrace(testError.getStackTrace());
216             throw processError;
217         }
218     }
219 
220     /**
221      * Collect all information of errors occured under one JUnit method by
222      * multiple xml testcases in one throwable and return it.
223      * 
224      * @param sb
225      * @param testError
226      * @return
227      */
228     private Throwable summariesFailures(StringBuffer sb, Throwable testError) {
229         DDTTestFailure testFailure;
230         for (Iterator iter = this.methodTestFailure.values().iterator(); iter
231             .hasNext();) {
232             testFailure = (DDTTestFailure) iter.next();
233             sb.append("F-(").append(testFailure.getMethodTest()).append(") ")
234                 .append(testFailure.exceptionMessage());
235             if (iter.hasNext()) {
236                 sb.append(LF);
237             }
238             if (testError == null) {
239                 testError = testFailure.thrownException();
240             }
241         }
242         return testError;
243     }
244 
245     /**
246      * Collect all information of errors occured under one JUnit method by
247      * multiple xml testcases in one throwable and return it.
248      * 
249      * @param sb
250      * @param testError
251      * @return throwable that contains summary of all occured errors/failures
252      *         during execution of testmethod
253      */
254     private Throwable summariesErrors(StringBuffer sb, Throwable testError) {
255         DDTTestFailure testFailure;
256         for (Iterator iter = this.methodTestError.values().iterator(); iter
257             .hasNext();) {
258             testFailure = (DDTTestFailure) iter.next();
259             sb.append("E-(").append(testFailure.getMethodTest()).append(") ")
260                 .append(testFailure.exceptionMessage());
261             if (iter.hasNext()) {
262                 sb.append(LF);
263             }
264             if (testError == null) {
265                 testError = testFailure.thrownException();
266             }
267         }
268         return testError;
269     }
270 
271     /**
272      * Check if an expected exception was not thrown during test execution. If
273      * an expected exception was defined but not raised throw an appropriate
274      * exception.
275      * 
276      * @param testId of executed test under testmethod.
277      * @param assertMap containing all assert information incl. expected
278      *        exceptions
279      */
280     public void checkOnExpectedException(String testId, TypedObjectMap assertMap) {
281         if (assertMap != null) {
282             int numberOfExpectedExceptions = 0;
283             Throwable firstException = null;
284             for (Iterator iter = assertMap.entrySet().iterator(); iter
285                 .hasNext();) {
286                 Entry assertEntry = (Entry) iter.next();
287                 String exceptKey = (String) assertEntry.getKey();
288                 TypedObject assertObj = assertMap.get(exceptKey);
289                 if (ExceptionAsserter.class.isInstance(assertObj)) {
290                     numberOfExpectedExceptions++;
291                     firstException = (Throwable) assertObj.getValue();
292                 }
293             }
294             if (numberOfExpectedExceptions > 0) {
295                 StringBuffer sb = new StringBuffer("There is/are ").append(
296                     numberOfExpectedExceptions).append(
297                     " expected exception(s) defined in test '").append(testId)
298                     .append("'");
299                 sb.append(LF).append(" (last as hint): ").append(
300                     firstException.getClass().getName());
301                 if (firstException.getMessage() != null) {
302                     sb.append(" - ").append(firstException.getMessage());
303                 }
304                 throw new AssertionFailedError(sb.toString());
305             }
306         }
307     }
308 }