package fitnesse.slim;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.PrintStream;
import java.io.StringWriter;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.regex.Pattern;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
public class StackTraceEnricherTest {
private static final String JUNIT_JAR_PATTERN = "[junit";
private static final String RT_JAR = "rt.jar";
private static final String COMMONS_LANG_VERSION = "2.6";
private static final Pattern COMMONS_LANG_JAR = Pattern.compile("commons-lang(-2.6)?.jar");
private Throwable exception;
private Throwable exceptionWithCause;
private StackTraceEnricher enricher;
@Before
public void setUp() {
exception = createException();
exceptionWithCause = createIllegalArgumentExceptionWithCause();
enricher = new StackTraceEnricher();
}
private Exception createException() {
Exception exception = null;
try {
org.apache.commons.lang.StringUtils.getLevenshteinDistance(null, null);
fail("StringUtils.getLevenshteinDistance() changed its contract, expected IllegalArgumentException");
} catch (IllegalArgumentException iae) {
if (iae.getStackTrace() == null || iae.getStackTrace().length == 0) {
iae.fillInStackTrace();
}
exception = iae;
}
return exception;
}
private IllegalArgumentException createIllegalArgumentExceptionWithCause() {
IllegalArgumentException exception = null;
try {
throwIllegalArgumentExceptionWithIOExceptionCause();
} catch (IllegalArgumentException iae) {
exception = iae;
}
return exception;
}
private void throwException() throws Exception {
throw createException();
}
private void throwIllegalArgumentExceptionWithIOExceptionCause() {
try {
throwException();
fail("No Exception was thrown.");
} catch (Exception e) {
throw new IllegalArgumentException("Custom IllegalArgumentException message", e);
}
}
private StackTraceElement getCommonsLangStackTraceElement(Throwable exception) {
StackTraceElement javaLangElement = null;
for (StackTraceElement element : exception.getStackTrace()) {
if (element.getClassName().startsWith("org.apache.commons.lang")) {
javaLangElement = element;
break;
}
}
if (javaLangElement == null) {
fail("Unable to find a org.apache.commons.lang class in the stack trace.");
}
return javaLangElement;
}
@Test
public void shouldWriteEnrichedStackTraceToWriter() throws Exception {
StringWriter writer = new StringWriter();
enricher.printStackTrace(exception, writer);
assertTrue("JUnit jar " + JUNIT_JAR_PATTERN + " not found in stack trace written to writer",
writer.toString().contains(JUNIT_JAR_PATTERN));
}
@Test
public void shouldWriteEnrichedStackTraceToOutputStream() throws Exception {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
enricher.printStackTrace(exception, outputStream);
String output = new String(outputStream.toByteArray());
assertTrue("JUnit jar " + JUNIT_JAR_PATTERN + " not found in stack trace written to output stream",
output.contains(JUNIT_JAR_PATTERN));
}
@Test
public void shouldReturnEnrichedStackTraceAsString() {
assertTrue("JUnit jar " + JUNIT_JAR_PATTERN + " not found in stack trace as String",
enricher.getStackTraceAsString(exception).contains(JUNIT_JAR_PATTERN));
}
@Test
public void shouldParseRtJar() {
assertTrue("Java RT jar not properly determined.", enricher.getStackTraceAsString(exception).contains(RT_JAR));
}
@Test
public void shouldAddVersionWhenAvailable() {
String stackTraceAsString = enricher.getStackTraceAsString(exception);
String fragment = ".jar:2.6";
assertTrue(String.format("Version not added for commons-lang.jar. Did not find '%s' in \n%s", fragment, stackTraceAsString),
stackTraceAsString.contains(fragment));
}
@Test
public void shouldGetVersionForClassInJarWithVersion() {
assertTrue("Version not retrieved for org.apache.commons.lang.ArrayUtils",
enricher.getVersion(org.apache.commons.lang.ArrayUtils.class).contains(COMMONS_LANG_VERSION));
}
@Test
public void shouldGetVersionForStackTraceElementInJarWithVersion() {
StackTraceElement commonsLangElement = getCommonsLangStackTraceElement(exception);
assertTrue("Version not retrieved for " + commonsLangElement.getClassName(), enricher.getVersion(commonsLangElement).contains(COMMONS_LANG_VERSION));
}
@Test
public void shouldGetLocationForClassInJarWithVersion() {
assertEquals("Location not retrieved for java.lang.reflect.Method",
enricher.getLocation(java.lang.reflect.Method.class), RT_JAR);
}
@Test
public void shouldGetLocationForStackTraceElementInJarWithVersion() {
StackTraceElement commonsLangElement = getCommonsLangStackTraceElement(exception);
assertTrue("Version not retrieved for " + commonsLangElement.getClassName(),
COMMONS_LANG_JAR.matcher(enricher.getLocation(commonsLangElement)).matches());
}
@Test
public void shouldParseDirectories() throws Exception {
String classPath = System.getProperty("java.class.path");
String testLocation = enricher.getLocation(this.getClass());
if (!testLocation.contains(".jar")) {
assertContainsPath("Location of unit test (" + testLocation + ") not found on the classpath (" + classPath + ").",
testLocation, classPath);
} else {
// TODO Find a way to test this if the unit test is executed from a jar.
System.err.println("Unit test executed from jar file, no stable method available for testing the parsing of " +
"directory locations.");
}
}
public void assertContainsPath(String message, String path, String paths) throws URISyntaxException {
File testFile;
if (path.startsWith("file:/")) {
testFile = new File(new URI(path));
} else {
testFile = new File(path);
}
String[] parts = paths.split(System.getProperty("path.separator"));
for (String part : parts) {
if (testFile.equals(new File(part))) {
return;
}
}
fail(message);
}
@Test
public void shouldWriteToSystemErrByDefault() {
PrintStream originalErrStream = System.err;
try {
ByteArrayOutputStream out = new ByteArrayOutputStream();
System.setErr(new PrintStream(out));
enricher.printStackTrace(exception);
String output = new String(out.toByteArray());
assertTrue("JUnit jar " + JUNIT_JAR_PATTERN + " not found in stack trace written to default System.err stream",
output.contains(JUNIT_JAR_PATTERN));
} finally {
System.setErr(originalErrStream);
}
}
@Test
public void shouldParseCauseExceptions() {
String parsedString = enricher.getStackTraceAsString(exceptionWithCause);
assertTrue("No IllegalArgumentException found as cause in stacktrace.",
parsedString.contains("\nCaused by: java.lang.IllegalArgumentException:"));
}
}