Skip to content

Commit

Permalink
Move trimStackTrace() to Throwables.
Browse files Browse the repository at this point in the history
  • Loading branch information
kcooney committed Jan 23, 2017
1 parent 436788a commit b6f240b
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 153 deletions.
150 changes: 0 additions & 150 deletions src/main/java/org/junit/internal/StackTraces.java

This file was deleted.

141 changes: 141 additions & 0 deletions src/main/java/org/junit/internal/Throwables.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.AbstractList;
import java.util.Arrays;
import java.util.List;

/**
* Miscellaneous functions dealing with {@code Throwable}.
Expand Down Expand Up @@ -54,4 +57,142 @@ public static String getStacktrace(Throwable exception) {
exception.printStackTrace(writer);
return stringWriter.toString();
}

/**
* Trims the stack trace of the given exception. Stack trace elements that are
* below the test method are filtered out.
*
* @since 4.13
*/
public static void trimStackTrace(Throwable exception) {
if (exception == null) {
throw new NullPointerException("exception cannot be null");
}

Throwable nextException = exception;
do {
exception = nextException;
nextException = null;
List<StackTraceElement> stackTraceElements = Arrays.asList(exception.getStackTrace());
int originalStackTraceDepth = stackTraceElements.size();
int linesToInclude = originalStackTraceDepth;

State state = State.PROCESSING_OTHER_CODE;
for (StackTraceElement stackTraceElement : asReversedList(stackTraceElements)) {
state = state.processStackTraceElement(stackTraceElement);
if (state == State.DONE) {
if (linesToInclude >= originalStackTraceDepth) {
return;
}
StackTraceElement[] newStackTrace = stackTraceElements
.subList(0, linesToInclude)
.toArray(new StackTraceElement[linesToInclude]);
exception.setStackTrace(newStackTrace);
nextException = exception.getCause();
break;
}
linesToInclude--;
}
} while (nextException != null);
}

private static <T> List<T> asReversedList(final List<T> list) {
return new AbstractList<T>() {

@Override
public T get(int index) {
return list.get(list.size() - index - 1);
}

@Override
public int size() {
return list.size();
}
};
}

private enum State {
PROCESSING_OTHER_CODE {
@Override public State processLine(String methodName) {
if (isTestFrameworkMethod(methodName)) {
return PROCESSING_TEST_FRAMEWORK_CODE;
}
return this;
}
},
PROCESSING_TEST_FRAMEWORK_CODE {
@Override public State processLine(String methodName) {
if (isReflectionMethod(methodName)) {
return PROCESSING_REFLECTION_CODE;
} else if (isTestFrameworkMethod(methodName)) {
return this;
}
return PROCESSING_OTHER_CODE;
}
},
PROCESSING_REFLECTION_CODE {
@Override public State processLine(String methodName) {
if (isReflectionMethod(methodName)) {
return this;
} else if (isTestFrameworkMethod(methodName)) {
// This is here to handle TestCase.runBare() calling TestCase.runTest().
return PROCESSING_TEST_FRAMEWORK_CODE;
}
return DONE;
}
},
DONE {
@Override public State processLine(String methodName) {
return this;
}
};

/** Processes a stack trace element method name, possibly moving to a new state. */
protected abstract State processLine(String methodName);

/** Processes a stack trace element, possibly moving to a new state. */
public final State processStackTraceElement(StackTraceElement element) {
return processLine(element.getClassName() + "." + element.getMethodName() + "()");
}
}

private static final String[] TEST_FRAMEWORK_METHOD_NAME_PREFIXES = {
"org.junit.runner.",
"org.junit.runners.",
"org.junit.experimental.runners.",
"org.junit.internal.",
"junit.",
};

private static final String[] TEST_FRAMEWORK_TEST_METHOD_NAME_PREFIXES = {
"org.junit.internal.StackTracesTest",
};

private static boolean isTestFrameworkMethod(String methodName) {
return isMatchingMethod(methodName, TEST_FRAMEWORK_METHOD_NAME_PREFIXES) &&
!isMatchingMethod(methodName, TEST_FRAMEWORK_TEST_METHOD_NAME_PREFIXES);
}

private static final String[] REFLECTION_METHOD_NAME_PREFIXES = {
"sun.reflect.",
"java.lang.reflect.",
"org.junit.rules.RunRules.<init>(",
"org.junit.rules.RunRules.applyAll(", // calls TestRules
"org.junit.runners.BlockJUnit4ClassRunner.withMethodRules(", // calls MethodRules
"junit.framework.TestCase.runBare(", // runBare() directly calls setUp() and tearDown()
};

private static boolean isReflectionMethod(String methodName) {
return isMatchingMethod(methodName, REFLECTION_METHOD_NAME_PREFIXES);
}

private static boolean isMatchingMethod(String methodName, String[] methodNamePrefixes) {
for (String methodNamePrefix : methodNamePrefixes) {
if (methodName.startsWith(methodNamePrefix)) {
return true;
}
}

return false;
}
}
3 changes: 1 addition & 2 deletions src/main/java/org/junit/runner/notification/Failure.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.io.Serializable;

import org.junit.internal.StackTraces;
import org.junit.internal.Throwables;
import org.junit.runner.Description;

Expand Down Expand Up @@ -37,7 +36,7 @@ public Failure(Description description, Throwable thrownException) {
this.fThrownException = thrownException;
this.fDescription = description;
if (!disableStackTraceTrimming && thrownException != null) {
StackTraces.trimStackTrace(thrownException);
Throwables.trimStackTrace(thrownException);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.internal.StackTraces;
import org.junit.internal.Throwables;
import org.junit.rules.TestRule;
import org.junit.rules.Timeout;
Expand Down

0 comments on commit b6f240b

Please sign in to comment.