001: // $Id: ExceptionHandler.java 256 2006-10-23 21:56:10Z jg_hamburg $
002: /********************************************************************************
003: * DDTUnit, a Datadriven Approach to Unit- and Moduletesting
004: * Copyright (c) 2004, Joerg and Kai Gellien
005: * All rights reserved.
006: *
007: * The Software is provided under the terms of the Common Public License 1.0
008: * as provided with the distribution of DDTUnit in the file cpl-v10.html.
009: * Redistribution and use in source and binary forms, with or without
010: * modification, are permitted provided that the following conditions
011: * are met:
012: *
013: * + Redistributions of source code must retain the above copyright
014: * notice, this list of conditions and the following disclaimer.
015: *
016: * + Redistributions in binary form must reproduce the above
017: * copyright notice, this list of conditions and the following
018: * disclaimer in the documentation and/or other materials provided
019: * with the distribution.
020: *
021: * + Neither the name of the authors or DDTUnit, nor the
022: * names of its contributors may be used to endorse or promote
023: * products derived from this software without specific prior
024: * written permission.
025: *
026: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
027: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
028: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
029: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
030: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
031: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
032: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
033: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
036: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
037: ********************************************************************************/package junitx.ddtunit;
038:
039: import java.lang.reflect.InvocationTargetException;
040: import java.util.HashMap;
041: import java.util.Iterator;
042: import java.util.Map;
043: import java.util.Map.Entry;
044:
045: import junit.framework.AssertionFailedError;
046: import junitx.ddtunit.data.AssertObject;
047: import junitx.ddtunit.data.ExceptionAsserter;
048: import junitx.ddtunit.data.TypedObject;
049: import junitx.ddtunit.data.TypedObjectMap;
050:
051: import org.apache.log4j.Logger;
052:
053: /**
054: * Handler for processing of exceptions caught during tst execution.
055: *
056: * @author jg
057: */
058: class ExceptionHandler {
059:
060: private Logger log = Logger.getLogger(ExceptionHandler.class);
061:
062: private String methodName;
063:
064: private Map methodTestFailure;
065:
066: private Map methodTestError;
067:
068: private static final String LF = System
069: .getProperty("line.separator");
070:
071: /**
072: * Instanciate ExceptionHandler
073: *
074: * @param methodName
075: */
076: public ExceptionHandler(String methodName) {
077: this .methodName = methodName;
078: this .methodTestFailure = new HashMap();
079: this .methodTestError = new HashMap();
080: }
081:
082: /**
083: * Process caught exception of test
084: *
085: * @param testId identifiing method test
086: * @param aThrowable
087: * @param assertMap containing infod about expected exceptions
088: * @throws Throwable uncaught exception
089: */
090: public void process(String testId, Throwable aThrowable,
091: TypedObjectMap assertMap) throws Throwable {
092: log.debug("process(" + methodName + ", " + testId + ", "
093: + aThrowable.getMessage() + ") - START");
094: if (InvocationTargetException.class.isInstance(aThrowable)) {
095: InvocationTargetException myEx = (InvocationTargetException) aThrowable;
096: aThrowable.fillInStackTrace();
097: addErrorFailure(testId, myEx.getTargetException(),
098: assertMap);
099: } 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(
117: null, testId, assertError));
118: throw assertError;
119: } else {
120: this .methodTestError.put(testId, new DDTTestFailure(
121: null, 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
146: .getValue();
147: if (ExceptionAsserter.class.isInstance(assertObj)) {
148: ExceptionAsserter exAssert = (ExceptionAsserter) assertObj;
149: try {
150: exAssert.setActualObject(aThrowable);
151: exAssert.validate(false);
152: caughtExpectedException = true;
153: break;
154: // } catch (DDTException ex) {
155: // if (!ex.getMessage().startsWith(
156: // "Class types of actual (")) {
157: // throw ex;
158: // }
159: } catch (Throwable ex) {
160: // catch any exception thrown by assertion
161: lastCaughtException = ex;
162: log
163: .debug(
164: "Exception caught during check on expected exception.",
165: lastCaughtException);
166: }
167: }
168: }
169: if (caughtExpectedException) {
170: log.debug("Caught expected exception in test " + testId
171: + ":" + aThrowable.getMessage());
172: }
173: }
174: log.debug("filter(" + methodName + ", " + testId + ", "
175: + aThrowable.getMessage() + ") found: "
176: + caughtExpectedException + "- END");
177: return caughtExpectedException;
178: }
179:
180: /**
181: * Summarize problems of method tests. <br/>If any errors were detected a
182: * method error will be thrown, <br/>if only assertion errors were detected
183: * only an assertion error will be thrown. <br/>First line contains summary
184: * statistics, following lines
185: *
186: * @param testCount under testmethod
187: */
188: void summarizeProblems(int testCount) {
189: int errorCount = this .methodTestError.size();
190: int failureCount = this .methodTestFailure.size();
191: if (errorCount == 0 && failureCount == 0) {
192: return;
193: }
194: StringBuffer sb = new StringBuffer("method ").append(
195: this .methodName).append(" - ");
196: sb.append("Total: " + testCount + ", Errors: " + errorCount
197: + ", Failures: " + failureCount + LF);
198: Throwable testError = null;
199: if (errorCount > 0) {
200: testError = summariesErrors(sb, testError);
201: }
202: if (failureCount > 0) {
203: if (errorCount > 0) {
204: sb.append(LF);
205: }
206: testError = summariesFailures(sb, testError);
207: }
208:
209: if (errorCount == 0) {
210: AssertionFailedError afEx = new AssertionFailedError(sb
211: .toString());
212: afEx.setStackTrace(testError.getStackTrace());
213: throw afEx;
214: } else {
215: DDTException processError = new DDTException(sb.toString(),
216: testError);
217: processError.setStackTrace(testError.getStackTrace());
218: throw processError;
219: }
220: }
221:
222: /**
223: * Collect all information of errors occured under one JUnit method by
224: * multiple xml testcases in one throwable and return it.
225: *
226: * @param sb
227: * @param testError
228: * @return
229: */
230: private Throwable summariesFailures(StringBuffer sb,
231: Throwable testError) {
232: DDTTestFailure testFailure;
233: for (Iterator iter = this .methodTestFailure.values().iterator(); iter
234: .hasNext();) {
235: testFailure = (DDTTestFailure) iter.next();
236: sb.append("F-(").append(testFailure.getMethodTest())
237: .append(") ")
238: .append(testFailure.exceptionMessage());
239: if (iter.hasNext()) {
240: sb.append(LF);
241: }
242: if (testError == null) {
243: testError = testFailure.thrownException();
244: }
245: }
246: return testError;
247: }
248:
249: /**
250: * Collect all information of errors occured under one JUnit method by
251: * multiple xml testcases in one throwable and return it.
252: *
253: * @param sb
254: * @param testError
255: * @return throwable that contains summary of all occured errors/failures
256: * during execution of testmethod
257: */
258: private Throwable summariesErrors(StringBuffer sb,
259: Throwable testError) {
260: DDTTestFailure testFailure;
261: for (Iterator iter = this .methodTestError.values().iterator(); iter
262: .hasNext();) {
263: testFailure = (DDTTestFailure) iter.next();
264: sb.append("E-(").append(testFailure.getMethodTest())
265: .append(") ")
266: .append(testFailure.exceptionMessage());
267: if (iter.hasNext()) {
268: sb.append(LF);
269: }
270: if (testError == null) {
271: testError = testFailure.thrownException();
272: }
273: }
274: return testError;
275: }
276:
277: /**
278: * Check if an expected exception was not thrown during test execution. If
279: * an expected exception was defined but not raised throw an appropriate
280: * exception.
281: *
282: * @param testId of executed test under testmethod.
283: * @param assertMap containing all assert information incl. expected
284: * exceptions
285: */
286: public void checkOnExpectedException(String testId,
287: TypedObjectMap assertMap) {
288: if (assertMap != null) {
289: int numberOfExpectedExceptions = 0;
290: Throwable firstException = null;
291: for (Iterator iter = assertMap.entrySet().iterator(); iter
292: .hasNext();) {
293: Entry assertEntry = (Entry) iter.next();
294: String exceptKey = (String) assertEntry.getKey();
295: TypedObject assertObj = assertMap.get(exceptKey);
296: if (ExceptionAsserter.class.isInstance(assertObj)) {
297: numberOfExpectedExceptions++;
298: firstException = (Throwable) assertObj.getValue();
299: }
300: }
301: if (numberOfExpectedExceptions > 0) {
302: StringBuffer sb = new StringBuffer("There is/are ")
303: .append(numberOfExpectedExceptions)
304: .append(
305: " expected exception(s) defined in test '")
306: .append(testId).append("'");
307: sb.append(LF).append(" (last as hint): ").append(
308: firstException.getClass().getName());
309: if (firstException.getMessage() != null) {
310: sb.append(" - ")
311: .append(firstException.getMessage());
312: }
313: throw new AssertionFailedError(sb.toString());
314: }
315: }
316: }
317: }
|