View Javadoc

1   package org.apache.maven.surefire.report;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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   * @author Kristian Rosenvold
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() ) ); // Add the name of the superclas
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 }