Skip to content

Commit 3c6be5a

Browse files
committed
Simplify StackTraces by using StackTraceElement
1 parent 3488c76 commit 3c6be5a

File tree

1 file changed

+78
-83
lines changed

1 file changed

+78
-83
lines changed

src/main/java/org/junit/internal/StackTraces.java

+78-83
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
import java.io.StringWriter;
88
import java.util.AbstractList;
99
import java.util.ArrayList;
10+
import java.util.Arrays;
1011
import java.util.Collections;
1112
import java.util.List;
1213

1314
/**
1415
* Utility class for working with stack traces.
1516
*/
1617
public class StackTraces {
18+
1719
private StackTraces() {
1820
}
1921

@@ -24,34 +26,64 @@ private StackTraces() {
2426
* @return a trimmed stack trace, or the original trace if trimming wasn't possible
2527
*/
2628
public static String getTrimmedStackTrace(Throwable exception) {
27-
String fullTrace = getFullStackTrace(exception);
28-
BufferedReader reader = new BufferedReader(
29-
new StringReader(fullTrace.substring(exception.toString().length())));
29+
List<String> trimmedStackTraceLines = getTrimmedStackTraceLines(exception);
30+
if (trimmedStackTraceLines.isEmpty()) {
31+
return getFullStackTrace(exception);
32+
}
3033

31-
try {
32-
// Collect the stack trace lines for "exception" (but not the cause).
33-
List<String> stackTraceLines = new ArrayList<String>();
34-
List<String> causedByLines = new ArrayList<String>();
35-
collectStackTraceLines(reader, stackTraceLines, causedByLines);
34+
StringBuilder result = new StringBuilder(exception.toString());
35+
appendStackTraceLines(trimmedStackTraceLines, result);
36+
appendStackTraceLines(getCauseStackTraceLines(exception), result);
37+
return result.toString();
38+
}
3639

37-
if (stackTraceLines.isEmpty()) {
38-
// No stack trace?
39-
return fullTrace;
40-
}
41-
boolean hasCause = !causedByLines.isEmpty();
42-
stackTraceLines = trimStackTraceLines(stackTraceLines, hasCause);
43-
if (stackTraceLines.isEmpty()) {
44-
// Could not trim stack trace lines.
45-
return fullTrace;
40+
private static List<String> getTrimmedStackTraceLines(Throwable exception) {
41+
List<StackTraceElement> stackTraceElements = Arrays.asList(exception.getStackTrace());
42+
int linesToInclude = stackTraceElements.size();
43+
44+
State state = State.PROCESSING_OTHER_CODE;
45+
for (StackTraceElement stackTraceElement : asReversedList(stackTraceElements)) {
46+
state = state.processStackTraceElement(stackTraceElement);
47+
if (state == State.DONE) {
48+
List<String> trimmedLines = new ArrayList<String>(linesToInclude + 2);
49+
trimmedLines.add("");
50+
for (StackTraceElement each : stackTraceElements.subList(0, linesToInclude)) {
51+
trimmedLines.add("\tat " + each);
52+
}
53+
if (exception.getCause() != null) {
54+
trimmedLines.add("\t... " + (stackTraceElements.size() - trimmedLines.size()) + " trimmed");
55+
}
56+
return trimmedLines;
4657
}
58+
linesToInclude--;
59+
}
60+
return Collections.emptyList();
61+
}
4762

48-
StringBuilder trimmedTrace = new StringBuilder(exception.toString());
49-
appendStackTraceLines(stackTraceLines, trimmedTrace);
50-
appendStackTraceLines(causedByLines, trimmedTrace);
51-
return trimmedTrace.toString();
52-
} catch (IOException e) {
63+
private static List<String> getCauseStackTraceLines(Throwable exception) {
64+
if (exception.getCause() != null) {
65+
String fullTrace = getFullStackTrace(exception);
66+
BufferedReader reader = new BufferedReader(
67+
new StringReader(fullTrace.substring(exception.toString().length())));
68+
List<String> causedByLines = new ArrayList<String>();
69+
70+
try {
71+
String line;
72+
while ((line = reader.readLine()) != null) {
73+
if (line.startsWith("Caused by: ")) {
74+
causedByLines.add(line);
75+
while ((line = reader.readLine()) != null) {
76+
causedByLines.add(line);
77+
}
78+
return causedByLines;
79+
}
80+
}
81+
} catch (IOException e) {
82+
// We should never get here, because we are reading from a StringReader
83+
}
5384
}
54-
return fullTrace;
85+
86+
return Collections.emptyList();
5587
}
5688

5789
private static String getFullStackTrace(Throwable exception) {
@@ -61,51 +93,13 @@ private static String getFullStackTrace(Throwable exception) {
6193
return stringWriter.toString();
6294
}
6395

64-
private static void collectStackTraceLines(
65-
BufferedReader reader,
66-
List<String> stackTraceLines,
67-
List<String> remainingLines) throws IOException {
68-
String line;
69-
while ((line = reader.readLine()) != null) {
70-
if (line.startsWith("Caused by: ")) {
71-
remainingLines.add(line);
72-
while ((line = reader.readLine()) != null) {
73-
remainingLines.add(line);
74-
}
75-
return;
76-
}
77-
stackTraceLines.add(line);
78-
}
79-
}
80-
8196
private static void appendStackTraceLines(
8297
List<String> stackTraceLines, StringBuilder destBuilder) {
8398
for (String stackTraceLine : stackTraceLines) {
8499
destBuilder.append(String.format("%s%n", stackTraceLine));
85100
}
86101
}
87102

88-
private static List<String> trimStackTraceLines(
89-
List<String> stackTraceLines, boolean hasCause) {
90-
State state = State.PROCESSING_OTHER_CODE;
91-
int linesToInclude = stackTraceLines.size();
92-
for (String stackTraceLine : asReversedList(stackTraceLines)) {
93-
state = state.processLine(stackTraceLine);
94-
if (state == State.DONE) {
95-
List<String> trimmedLines = stackTraceLines.subList(0, linesToInclude);
96-
if (!hasCause) {
97-
return trimmedLines;
98-
}
99-
List<String> copy = new ArrayList<String>(trimmedLines.size() + 1);
100-
copy.addAll(trimmedLines);
101-
copy.add("\t... " + (stackTraceLines.size() - trimmedLines.size()) + " trimmed");
102-
return copy;
103-
}
104-
linesToInclude--;
105-
}
106-
return Collections.emptyList();
107-
}
108-
109103
private static <T> List<T> asReversedList(final List<T> list) {
110104
return new AbstractList<T>() {
111105

@@ -123,42 +117,47 @@ public int size() {
123117

124118
private enum State {
125119
PROCESSING_OTHER_CODE {
126-
@Override public State processLine(String line) {
127-
if (isTestFrameworkStackTraceLine(line)) {
120+
@Override public State processLine(String methodName) {
121+
if (isTestFrameworkMethod(methodName)) {
128122
return PROCESSING_TEST_FRAMEWORK_CODE;
129123
}
130124
return this;
131125
}
132126
},
133127
PROCESSING_TEST_FRAMEWORK_CODE {
134-
@Override public State processLine(String line) {
135-
if (isReflectionStackTraceLine(line)) {
128+
@Override public State processLine(String methodName) {
129+
if (isReflectionMethod(methodName)) {
136130
return PROCESSING_REFLECTION_CODE;
137-
} else if (isTestFrameworkStackTraceLine(line)) {
131+
} else if (isTestFrameworkMethod(methodName)) {
138132
return this;
139133
}
140134
return PROCESSING_OTHER_CODE;
141135
}
142136
},
143137
PROCESSING_REFLECTION_CODE {
144-
@Override public State processLine(String line) {
145-
if (isReflectionStackTraceLine(line)) {
138+
@Override public State processLine(String methodName) {
139+
if (isReflectionMethod(methodName)) {
146140
return this;
147-
} else if (isTestFrameworkStackTraceLine(line)) {
141+
} else if (isTestFrameworkMethod(methodName)) {
148142
// This is here to handle TestCase.runBare() calling TestCase.runTest().
149143
return PROCESSING_TEST_FRAMEWORK_CODE;
150144
}
151145
return DONE;
152146
}
153147
},
154148
DONE {
155-
@Override public State processLine(String line) {
149+
@Override public State processLine(String methodName) {
156150
return this;
157151
}
158152
};
159153

160-
/** Processes a stack trace line, possibly moving to a new state. */
161-
public abstract State processLine(String line);
154+
/** Processes a stack trace element method name, possibly moving to a new state. */
155+
protected abstract State processLine(String methodName);
156+
157+
/** Processes a stack trace element, possibly moving to a new state. */
158+
public final State processStackTraceElement(StackTraceElement element) {
159+
return processLine(element.getClassName() + "." + element.getMethodName() + "()");
160+
}
162161
}
163162

164163
private static final String[] TEST_FRAMEWORK_METHOD_NAME_PREFIXES = {
@@ -173,9 +172,9 @@ private enum State {
173172
"org.junit.internal.StackTracesTest",
174173
};
175174

176-
private static boolean isTestFrameworkStackTraceLine(String line) {
177-
return isMatchingStackTraceLine(line, TEST_FRAMEWORK_METHOD_NAME_PREFIXES) &&
178-
!isMatchingStackTraceLine(line, TEST_FRAMEWORK_TEST_METHOD_NAME_PREFIXES);
175+
private static boolean isTestFrameworkMethod(String methodName) {
176+
return isMatchingMethod(methodName, TEST_FRAMEWORK_METHOD_NAME_PREFIXES) &&
177+
!isMatchingMethod(methodName, TEST_FRAMEWORK_TEST_METHOD_NAME_PREFIXES);
179178
}
180179

181180
private static final String[] REFLECTION_METHOD_NAME_PREFIXES = {
@@ -187,17 +186,13 @@ private static boolean isTestFrameworkStackTraceLine(String line) {
187186
"junit.framework.TestCase.runBare(", // runBare() directly calls setUp() and tearDown()
188187
};
189188

190-
private static boolean isReflectionStackTraceLine(String line) {
191-
return isMatchingStackTraceLine(line, REFLECTION_METHOD_NAME_PREFIXES);
189+
private static boolean isReflectionMethod(String methodName) {
190+
return isMatchingMethod(methodName, REFLECTION_METHOD_NAME_PREFIXES);
192191
}
193192

194-
private static boolean isMatchingStackTraceLine(String line, String[] methodNamePrefixes) {
195-
if (!line.startsWith("\tat ")) {
196-
return false;
197-
}
198-
line = line.substring(4);
193+
private static boolean isMatchingMethod(String methodName, String[] methodNamePrefixes) {
199194
for (String methodNamePrefix : methodNamePrefixes) {
200-
if (line.startsWith(methodNamePrefix)) {
195+
if (methodName.startsWith(methodNamePrefix)) {
201196
return true;
202197
}
203198
}

0 commit comments

Comments
 (0)