package org.rapidbeans.exception;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.LineNumberReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.Date;
import junit.framework.Assert;
import org.junit.Test;
/**
* Unit Test if the UniversalException
*
* @author Martin Bluemel
*/
public class UniversalExceptionTest {
private static final String BLA_BLA = "bla bla";
private static final int NUM11 = 11;
private static final String TEST_ERROR_MESSAGE = "test error message";
private static final String TEST_MESSAGE = "test message";
private static final String NL = getLineBreak();
private static final String TAB = "\t";
private static final int FIRST_LINE = 185;
/**
* My tiny little test exception.
*
* @author Martin Bluemel
*/
private static final class MyTinyLittleException extends RuntimeException {
private static final long serialVersionUID = 1L;
private final int x;
private final String detailedMessage;
/**
* Constructor.
*
* @param message
* the message
* @param code
* the code
* @param details
* the details
*/
public MyTinyLittleException(String message, int code, String details) {
super(message);
this.x = code;
this.detailedMessage = details;
}
/**
* @return the detailed message.
*/
// this method is used by means of reflection
@SuppressWarnings("unused")
public String getDetailedMessage() {
return this.detailedMessage;
}
/**
* @return the error code.
*/
// this method is used by means of reflection
@SuppressWarnings("unused")
public int getX() {
return this.x;
}
/**
* @return the null argument.
*/
// this method is used by means of reflection
@SuppressWarnings("unused")
public String getNullExArg() {
return null;
}
/**
* Throws always a RuntimeException in order to provoke an
* InvocationTargetException
*
* @return nothing
*/
// this method is used by means of reflection
@SuppressWarnings("unused")
public String getTestInvocationTargetException() {
throw new RuntimeException("xxx");
}
/**
* @return nothing.
*/
// this method is used by means of reflection
@SuppressWarnings("unused")
public String get() {
return null;
}
/**
* @return nothing.
*/
// this method is used by means of reflection
@SuppressWarnings("unused")
public String getY(final String x) {
return null;
}
/**
* @return nothing.
*/
// this method is used by means of reflection
@SuppressWarnings("unused")
public String geta() {
return null;
}
}
/**
* test method 1.
*/
private void f1() {
f2();
}
/**
* test method 2.
*/
private void f2() {
f3();
}
/**
* test method 3.
*/
private void f3() {
f4();
}
/**
* test method 4.
*/
private void f4() {
f5();
}
/**
* test method 5.
*/
private void f5() {
f6();
}
/**
* test method 6.
*/
private void f6() {
throw new RuntimeException(TEST_MESSAGE);
}
/**
* test method.
*
* @throws UniversalException
* for test reasons
*/
private void s01_1() throws UniversalException {
try {
s01_2();
} catch (RuntimeException rex) {
throw new UniversalException(rex);
}
}
/**
* test method.
*/
private void s01_2() {
s01_3();
}
/**
* test method.
*/
private void s01_3() {
try {
s01_4();
} catch (RuntimeException e) {
throw new RuntimeException(TEST_MESSAGE, e);
}
}
/**
* test method.
*/
private void s01_4() {
s01_5();
}
/**
* test method.
*/
private void s01_5() {
s01_6();
}
/**
* test method.
*/
private void s01_6() {
throw new MyTinyLittleException(TEST_ERROR_MESSAGE, NUM11, BLA_BLA);
}
/**
* test method.
*/
@Test
public void testRuntimeException() {
try {
f1();
} catch (RuntimeException e) {
Assert.assertEquals(TEST_MESSAGE, e.getMessage());
// Assert.assertEquals(25, e.getStackTrace().length);
Assert.assertEquals("f6", e.getStackTrace()[0].getMethodName());
Assert.assertEquals("f1", e.getStackTrace()[5].getMethodName());
Assert.assertEquals("testRuntimeException", e.getStackTrace()[6].getMethodName());
}
}
/**
* Test if the UniversalException has a correct time stamp.
*/
@Test
public void testTimeStamp() {
UniversalException sex = new UniversalException(TEST_MESSAGE);
final Date currentTime = new Date();
final Date ts = sex.getTimeStamp();
// asserting the current time in milliseconds is a bit dangerous
if (!(currentTime.toString().equals(ts.toString()))) {
System.out.println("WARNING: exception time stamp: " + currentTime.toString()
+ " differs from current time " + ts.toString());
}
// assert immutability
final long time = ts.getTime();
ts.setTime(0);
Assert.assertEquals(0, ts.getTime());
Assert.assertEquals(time, sex.getTimeStamp().getTime());
}
/**
* test test correct message.
*/
@Test
public void testMessage() {
UniversalException uniEx = new UniversalException(TEST_MESSAGE);
Assert.assertEquals(TEST_MESSAGE, uniEx.getMessage());
}
/**
* Test a wrapped exception message.
*/
@Test
public void testMessageWrapped() {
RuntimeException rex = new RuntimeException(TEST_MESSAGE);
UniversalException uniEx = new UniversalException(rex);
Assert.assertEquals("wrapped java.lang.RuntimeException: test message", uniEx.getMessage());
}
/**
* Test an explicitly wrapped message.
*/
@Test
public void testMessageWrappedWithMessageExplicitely() {
RuntimeException rex = new RuntimeException(TEST_MESSAGE);
UniversalException uniEx = new UniversalException(BLA_BLA, rex);
Assert.assertEquals(BLA_BLA, uniEx.getMessage());
}
/**
* Test the stack trace and cause chain.
*
* @throws IOException
* in case of IO problems
*/
@Test
public void testPrintStackTraceAndCauseChain() throws IOException {
try {
s01_1();
} catch (UniversalException uniEx) {
UniversalException rex = (UniversalException) uniEx.getCause();
Assert.assertEquals("java.lang.RuntimeException", rex.getOriginalClassname());
Assert.assertEquals(TEST_MESSAGE, rex.getMessage());
UniversalException mex = (UniversalException) rex.getCause();
Assert.assertEquals("org.rapidbeans.exception.UniversalExceptionTest$MyTinyLittleException",
mex.getOriginalClassname());
Assert.assertEquals(TEST_ERROR_MESSAGE, mex.getMessage());
Assert.assertEquals(BLA_BLA, mex.getProperty("detailedMessage"));
Assert.assertEquals("11", mex.getProperty("x"));
Assert.assertNull(mex.getCause());
Assert.assertNull(mex.getProperty("class"));
Assert.assertNull(mex.getProperty("cause"));
Assert.assertNull(mex.getProperty("message"));
Assert.assertNull(mex.getProperty("localizedMessage"));
Assert.assertNull(mex.getProperty("stackTrace"));
Assert.assertEquals(getStackTraceExpectedCauseChain(uniEx.getTimeStamp().toString()),
filterEnvironment(printStackTraceWriter(uniEx)));
}
}
/**
* Test wrapping an UniversalException within an UniversalException.
*/
@Test
public void testWrapUniversal() {
UniversalException uniEx2 = new UniversalException(TEST_MESSAGE);
RuntimeException rex = new RuntimeException(uniEx2);
UniversalException uniEx = new UniversalException(rex);
Assert.assertEquals("wrapped java.lang.RuntimeException: " + "org.rapidbeans.exception.UniversalException: "
+ "test message", uniEx.getMessage());
}
@Test
public void testPrintStackTrace() {
UniversalException uniEx = new UniversalException("asdf");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(bos);
uniEx.printStackTrace(ps);
}
/**
* Filter specific things from a stack trace to ease comparison.
*
* @param s
* the string to filter
* @return the filtered string
* @throws IOException
* in case of IO problems
*/
private String filterEnvironment(String s) throws IOException {
StringBuffer sb = new StringBuffer();
LineNumberReader rd = new LineNumberReader(new StringReader(s));
String line;
while ((line = rd.readLine()) != null) {
boolean append = true;
if (line.startsWith("\t...")) {
append = false;
}
if (append && line.startsWith("\tat ") && (!line.startsWith("\tat org.rapidbeans"))) {
append = false;
}
if (append) {
sb.append(line + NL);
}
}
return sb.toString();
}
/**
* Convert a stack trace into a string using a writer.
*
* @param ex
* the exception to print the stack trace from
* @return a string containing the stack trace
*/
private String printStackTraceWriter(final Exception ex) {
CharArrayWriter caw = new CharArrayWriter();
PrintWriter pw = new PrintWriter(caw);
ex.printStackTrace(pw);
pw.close();
return caw.toString();
}
/**
* The test fixture for the stack trace string.
*
* @param timeStamp
* the time stamp
* @return the string
*/
private static String getStackTraceExpectedCauseChain(final String timeStamp) {
return "org.rapidbeans.exception.UniversalException:" + " wrapped java.lang.RuntimeException: test message"
+ NL
+ TAB
+ "time stamp: "
+ timeStamp
+ NL
+ TAB
+ "at org.rapidbeans.exception.UniversalExceptionTest"
+ ".s01_1(UniversalExceptionTest.java:"
+ Integer.toString(FIRST_LINE)
+ ")"
+ NL
+ TAB
+ "at org.rapidbeans.exception.UniversalExceptionTest."
+ "testPrintStackTraceAndCauseChain(UniversalExceptionTest.java:"
+ Integer.toString(FIRST_LINE + 123)
+ ")"
+ NL
+ "Caused by: java.lang.RuntimeException: test message"
+ NL
+ TAB
+ "at org.rapidbeans.exception."
+ "UniversalExceptionTest.s01_3("
+ "UniversalExceptionTest.java:"
+ Integer.toString(FIRST_LINE + 18)
+ ")"
+ NL
+ TAB
+ "at org.rapidbeans.exception."
+ "UniversalExceptionTest.s01_2("
+ "UniversalExceptionTest.java:"
+ Integer.toString(FIRST_LINE + 8)
+ ")"
+ NL
+ TAB
+ "at org.rapidbeans.exception."
+ "UniversalExceptionTest.s01_1("
+ "UniversalExceptionTest.java:"
+ Integer.toString(FIRST_LINE - 2)
+ ")"
+ NL
+ "Caused by: org.rapidbeans.exception."
+ "UniversalExceptionTest$MyTinyLittleException: test error message"
+ NL
+ TAB
+ "testInvocationTargetException = \"<problems to retrieve exception property testInvocationTargetException>\""
+ NL + TAB + "nullExArg = \"<null>\"" + NL + TAB + "detailedMessage = \"bla bla\"" + NL + TAB
+ "x = \"11\"" + NL + TAB + "at org.rapidbeans.exception." + "UniversalExceptionTest.s01_6("
+ "UniversalExceptionTest.java:" + Integer.toString(FIRST_LINE + 40) + ")" + NL + TAB
+ "at org.rapidbeans.exception." + "UniversalExceptionTest.s01_5(" + "UniversalExceptionTest.java:"
+ Integer.toString(FIRST_LINE + 33) + ")" + NL + TAB + "at org.rapidbeans.exception."
+ "UniversalExceptionTest.s01_4(" + "UniversalExceptionTest.java:" + Integer.toString(FIRST_LINE + 26)
+ ")" + NL + TAB + "at org.rapidbeans.exception." + "UniversalExceptionTest.s01_3("
+ "UniversalExceptionTest.java:" + Integer.toString(FIRST_LINE + 16) + ")" + NL;
}
/**
* @return the OS platform specific line break.
*/
private static String getLineBreak() {
if (System.getProperty("os.name").toLowerCase().startsWith("windows")) {
return "\r\n";
} else {
return "\n";
}
}
}