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