1 package org.apache.maven.surefire.report;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.List;
26
27 import org.apache.maven.shared.utils.StringUtils;
28
29
30
31
32 @SuppressWarnings( "ThrowableResultOfMethodCallIgnored" )
33 public class SmartStackTraceParser
34 {
35
36 private static final int MAX_LINE_LENGTH = 77;
37
38 private final SafeThrowable throwable;
39
40 private final StackTraceElement[] stackTrace;
41
42 private final String simpleName;
43
44 private String testClassName;
45
46 private final Class testClass;
47
48 private String testMethodName;
49
50 public SmartStackTraceParser( Class testClass, Throwable throwable )
51 {
52 this( testClass.getName(), throwable, null );
53 }
54
55 public SmartStackTraceParser( String testClassName, Throwable throwable, String testMethodName )
56 {
57 this.testMethodName = testMethodName;
58 this.testClassName = testClassName;
59 this.testClass = getClass( testClassName );
60 this.simpleName = this.testClassName.substring( this.testClassName.lastIndexOf( "." ) + 1 );
61 this.throwable = new SafeThrowable( throwable );
62 stackTrace = throwable.getStackTrace();
63 }
64
65 private static Class getClass( String name )
66 {
67 try
68 {
69 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
70 return classLoader != null ? classLoader.loadClass( name ) : null;
71 }
72 catch ( ClassNotFoundException e )
73 {
74 return null;
75 }
76 }
77
78 private static String getSimpleName( String className )
79 {
80 int i = className.lastIndexOf( "." );
81 return className.substring( i + 1 );
82 }
83
84 @SuppressWarnings( "ThrowableResultOfMethodCallIgnored" )
85 public String getString()
86 {
87 if ( testClass == null )
88 {
89 return throwable.getLocalizedMessage();
90 }
91
92 StringBuilder result = new StringBuilder();
93 List<StackTraceElement> stackTraceElements = focusOnClass( stackTrace, testClass );
94 Collections.reverse( stackTraceElements );
95 StackTraceElement stackTraceElement;
96 if ( stackTraceElements.isEmpty() )
97 {
98 result.append( simpleName );
99 if ( StringUtils.isNotEmpty( testMethodName ) )
100 {
101 result.append( "." ).append( testMethodName );
102 }
103 }
104 else
105 {
106 for ( int i = 0; i < stackTraceElements.size(); i++ )
107 {
108 stackTraceElement = stackTraceElements.get( i );
109 if ( i == 0 )
110 {
111 result.append( simpleName );
112 if ( !stackTraceElement.getClassName().equals( testClassName ) )
113 {
114 result.append( ">" );
115 }
116 else
117 {
118 result.append( "." );
119 }
120
121 }
122 if ( !stackTraceElement.getClassName().equals( testClassName ) )
123 {
124 result.append( getSimpleName( stackTraceElement.getClassName() ) );
125 result.append( "." );
126 }
127 result.append( stackTraceElement.getMethodName() ).append( ":" ).append(
128 stackTraceElement.getLineNumber() );
129 result.append( "->" );
130 }
131
132 if ( result.length() >= 2 )
133 {
134 result.deleteCharAt( result.length() - 1 );
135 result.deleteCharAt( result.length() - 1 );
136 }
137 }
138
139 Throwable target = throwable.getTarget();
140 if ( target instanceof AssertionError )
141 {
142 result.append( " " );
143 result.append( throwable.getMessage() );
144 }
145 else if ( "junit.framework.AssertionFailedError".equals( target.getClass().getName() )
146 || "junit.framework.ComparisonFailure".equals( target.getClass().getName() ) )
147 {
148 result.append( " " );
149 result.append( throwable.getMessage() );
150 }
151 else
152 {
153 result.append( rootIsInclass() ? " " : " ยป " );
154 result.append( getMinimalThrowableMiniMessage( target ) );
155 result.append( getTruncatedMessage( MAX_LINE_LENGTH - result.length() ) );
156 }
157 return result.toString();
158 }
159
160 private String getMinimalThrowableMiniMessage( Throwable throwable )
161 {
162 String name = throwable.getClass().getSimpleName();
163 if ( name.endsWith( "Exception" ) )
164 {
165 return StringUtils.chompLast( name, "Exception" );
166 }
167 if ( name.endsWith( "Error" ) )
168 {
169 return StringUtils.chompLast( name, "Error" );
170 }
171 return name;
172 }
173
174 private String getTruncatedMessage( int i )
175 {
176 if ( i < 0 )
177 {
178 return "";
179 }
180 String msg = throwable.getMessage();
181 if ( msg == null )
182 {
183 return "";
184 }
185 String substring = msg.substring( 0, Math.min( i, msg.length() ) );
186 if ( i < msg.length() )
187 {
188 return " " + substring + "...";
189 }
190 else
191 {
192 return " " + substring;
193 }
194 }
195
196 private boolean rootIsInclass()
197 {
198 return stackTrace.length > 0 && stackTrace[0].getClassName().equals( testClassName );
199 }
200
201 static List<StackTraceElement> focusOnClass( StackTraceElement[] stackTrace, Class clazz )
202 {
203 List<StackTraceElement> result = new ArrayList<StackTraceElement>();
204 for ( StackTraceElement element : stackTrace )
205 {
206 if ( element != null && isInSupers( clazz, element.getClassName() ) )
207 {
208 result.add( element );
209 }
210 }
211 return result;
212 }
213
214 private static boolean isInSupers( Class testClass, String lookFor )
215 {
216 if ( lookFor.startsWith( "junit.framework." ) )
217 {
218 return false;
219 }
220 while ( !testClass.getName().equals( lookFor ) && testClass.getSuperclass() != null )
221 {
222 testClass = testClass.getSuperclass();
223 }
224 return testClass.getName().equals( lookFor );
225 }
226
227 static Throwable findInnermostWithClass( Throwable t, String className )
228 {
229 Throwable match = t;
230 do
231 {
232 if ( containsClassName( t.getStackTrace(), className ) )
233 {
234 match = t;
235 }
236
237 t = t.getCause();
238
239 }
240 while ( t != null );
241 return match;
242 }
243
244 public static String innerMostWithFocusOnClass( Throwable t, String className )
245 {
246 Throwable innermost = findInnermostWithClass( t, className );
247 List<StackTraceElement> stackTraceElements = focusInsideClass( innermost.getStackTrace(), className );
248 String s = causeToString( innermost.getCause() );
249 return toString( t, stackTraceElements ) + s;
250 }
251
252 static List<StackTraceElement> focusInsideClass( StackTraceElement[] stackTrace, String className )
253 {
254 List<StackTraceElement> result = new ArrayList<StackTraceElement>();
255 boolean found = false;
256 for ( StackTraceElement element : stackTrace )
257 {
258 if ( !found )
259 {
260 result.add( element );
261 }
262
263 if ( className.equals( element.getClassName() ) )
264 {
265 if ( found )
266 {
267 result.add( element );
268 }
269 found = true;
270 }
271 else
272 {
273 if ( found )
274 {
275 break;
276 }
277 }
278 }
279 return result;
280 }
281
282 static boolean containsClassName( StackTraceElement[] stackTrace, String className )
283 {
284 for ( StackTraceElement element : stackTrace )
285 {
286 if ( className.equals( element.getClassName() ) )
287 {
288 return true;
289 }
290 }
291 return false;
292 }
293
294 public static String causeToString( Throwable cause )
295 {
296 StringBuilder resp = new StringBuilder();
297 while ( cause != null )
298 {
299 resp.append( "Caused by: " );
300 resp.append( toString( cause, Arrays.asList( cause.getStackTrace() ) ) );
301 cause = cause.getCause();
302 }
303 return resp.toString();
304 }
305
306 public static String toString( Throwable t, Iterable<StackTraceElement> elements )
307 {
308 StringBuilder result = new StringBuilder();
309 result.append( t.getClass().getName() );
310 result.append( ": " );
311 result.append( t.getMessage() );
312 result.append( "\n" );
313
314 for ( StackTraceElement element : elements )
315 {
316 result.append( "\tat " ).append( element.toString() );
317 result.append( "\n" );
318 }
319 return result.toString();
320 }
321 }