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.Collections;
24  import java.util.List;
25  import org.apache.maven.shared.utils.StringUtils;
26  
27  /**
28   * @author Kristian Rosenvold
29   */
30  @SuppressWarnings( "ThrowableResultOfMethodCallIgnored" )
31  public class SmartStackTraceParser
32  {
33  
34      private final SafeThrowable throwable;
35  
36      private final StackTraceElement[] stackTrace;
37  
38      private final String simpleName;
39  
40      private String testClassName;
41  
42      private final Class testClass;
43  
44      public SmartStackTraceParser( Class testClass, Throwable throwable )
45      {
46          this( testClass.getName(), throwable );
47      }
48  
49      public SmartStackTraceParser( String testClassName, Throwable throwable )
50      {
51          this.testClassName = testClassName;
52          this.testClass = getClass( testClassName );
53          this.simpleName = this.testClassName.substring( this.testClassName.lastIndexOf( "." ) + 1 );
54          this.throwable = new SafeThrowable( throwable );
55          stackTrace = throwable.getStackTrace();
56      }
57  
58      private static Class getClass( String name )
59      {
60          try
61          {
62              return Thread.currentThread().getContextClassLoader().loadClass( name );
63          }
64          catch ( ClassNotFoundException e )
65          {
66              return null;
67          }
68      }
69  
70      private static String getSimpleName( String className )
71      {
72          int i = className.lastIndexOf( "." );
73          return className.substring( i + 1 );
74      }
75  
76      @SuppressWarnings( "ThrowableResultOfMethodCallIgnored" )
77      public String getString()
78      {
79          if ( testClass == null )
80          {
81              return throwable.getLocalizedMessage();
82          }
83  
84          StringBuilder result = new StringBuilder();
85          List<StackTraceElement> stackTraceElements = focusOnClass( stackTrace, testClass );
86          Collections.reverse( stackTraceElements );
87          StackTraceElement stackTraceElement;
88          if (stackTrace.length == 0)
89          {
90              result.append( simpleName );
91              result.append("XX");
92          }
93          for ( int i = 0; i < stackTraceElements.size(); i++ )
94          {
95              stackTraceElement = stackTraceElements.get( i );
96              if ( i == 0 )
97              {
98                  result.append( simpleName );
99                  if ( !stackTraceElement.getClassName().equals( testClassName ) )
100                 {
101                     result.append( ">" );
102                 }
103                 else
104                 {
105                     result.append( "." );
106                 }
107 
108             }
109             if ( !stackTraceElement.getClassName().equals( testClassName ) )
110             {
111                 result.append( getSimpleName( stackTraceElement.getClassName() ) ); // Add the name of the superclas
112                 result.append( "." );
113             }
114             result.append( stackTraceElement.getMethodName() ).append( ":" ).append( stackTraceElement.getLineNumber() );
115             result.append( "->" );
116         }
117 
118         if ( result.length() >= 2 )
119         {
120             result.deleteCharAt( result.length() - 1 );
121             result.deleteCharAt( result.length() - 1 );
122         }
123 
124         Throwable target = throwable.getTarget();
125         if ( target instanceof AssertionError )
126         {
127             result.append( " " );
128             result.append( throwable.getMessage() );
129         }
130         else if ( "junit.framework.AssertionFailedError".equals( target.getClass().getName() )
131             || "junit.framework.ComparisonFailure".equals( target.getClass().getName() ) )
132         {
133             result.append( " " );
134             result.append( throwable.getMessage() );
135         }
136         else
137         {
138             result.append( rootIsInclass() ? " " : " ยป " );
139             result.append( getMinimalThrowableMiniMessage( target ) );
140             result.append( getTruncatedMessage( 77 - result.length() ) );
141         }
142         return result.toString();
143     }
144 
145     private String getMinimalThrowableMiniMessage( Throwable throwable )
146     {
147         String name = throwable.getClass().getSimpleName();
148         if ( name.endsWith( "Exception" ) )
149         {
150             return StringUtils.chompLast( name, "Exception" );
151         }
152         if ( name.endsWith( "Error" ) )
153         {
154             return StringUtils.chompLast( name, "Error" );
155         }
156         return name;
157     }
158 
159     private String getTruncatedMessage( int i )
160     {
161         if ( i < 0 )
162         {
163             return "";
164         }
165         String msg = throwable.getMessage();
166         if ( msg == null )
167         {
168             return "";
169         }
170         String substring = msg.substring( 0, Math.min( i, msg.length() ) );
171         if ( i < msg.length() )
172         {
173             return " " + substring + "...";
174         }
175         else
176         {
177             return " " + substring;
178         }
179     }
180 
181     private boolean rootIsInclass()
182     {
183         return  stackTrace.length > 0 && stackTrace[0].getClassName().equals( testClassName );
184     }
185 
186     static List<StackTraceElement> focusOnClass( StackTraceElement[] stackTrace, Class clazz )
187     {
188         List<StackTraceElement> result = new ArrayList<StackTraceElement>();
189         for ( StackTraceElement element : stackTrace )
190         {
191             if ( element != null && isInSupers( clazz, element.getClassName() ) )
192             {
193                 result.add( element );
194             }
195         }
196         return result;
197     }
198 
199     private static boolean isInSupers( Class testClass, String lookFor )
200     {
201         if ( lookFor.startsWith( "junit.framework." ) )
202         {
203             return false;
204         }
205         while ( !testClass.getName().equals( lookFor ) && testClass.getSuperclass() != null )
206         {
207             testClass = testClass.getSuperclass();
208         }
209         return testClass.getName().equals( lookFor );
210     }
211 
212     private static Throwable findInnermost( Throwable t )
213     {
214         Throwable real = t;
215         while ( real.getCause() != null )
216         {
217             real = real.getCause();
218         }
219         return real;
220     }
221 
222     public static String innerMostWithFocusOnClass( Throwable t, String className )
223     {
224         List<StackTraceElement> stackTraceElements = focusInsideClass( findInnermost( t ).getStackTrace(), className );
225         return toString( t, stackTraceElements );
226     }
227 
228     static List<StackTraceElement> focusInsideClass( StackTraceElement[] stackTrace, String className )
229     {
230         List<StackTraceElement> result = new ArrayList<StackTraceElement>();
231         boolean found = false;
232         for ( StackTraceElement element : stackTrace )
233         {
234             if ( !found )
235             {
236                 result.add( element );
237             }
238 
239             if ( className.equals( element.getClassName() ) )
240             {
241                 if ( found )
242                 {
243                     result.add( element );
244                 }
245                 found = true;
246             }
247             else
248             {
249                 if ( found )
250                 {
251                     break;
252                 }
253             }
254         }
255         return result;
256     }
257 
258     public static String toString( Throwable t, List<StackTraceElement> elements )
259     {
260         StringBuilder result = new StringBuilder();
261         result.append( t.getClass().getName() );
262         result.append( ": " );
263         result.append( t.getMessage() );
264         result.append( "\n" );
265 
266         for ( StackTraceElement element : elements )
267         {
268             result.append( "\tat " ).append( element.toString() );
269             result.append( "\n" );
270         }
271         return result.toString();
272     }
273 }