Updated Branches:
  refs/heads/master 28e048818 -> 19939cb45

[SUREFIRE-992] Smartstacktaceparser sometimes incorrect

Fixed with unit test


Project: http://git-wip-us.apache.org/repos/asf/maven-surefire/repo
Commit: http://git-wip-us.apache.org/repos/asf/maven-surefire/commit/19939cb4
Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/19939cb4
Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/19939cb4

Branch: refs/heads/master
Commit: 19939cb45467c582da9b9bbb4afc436a75bb8ee6
Parents: 28e0488
Author: Kristian Rosenvold <krosenv...@apache.org>
Authored: Wed May 1 16:06:24 2013 +0200
Committer: Kristian Rosenvold <krosenv...@apache.org>
Committed: Wed May 1 16:12:03 2013 +0200

----------------------------------------------------------------------
 .../surefire/report/SmartStackTraceParser.java     |   62 +++++++++--
 .../maven/surefire/report/RunnableTestClass1.java  |   52 +++++++++
 .../maven/surefire/report/RunnableTestClass2.java  |   43 ++++++++
 .../surefire/report/SmartStackTraceParserTest.java |   85 +++++++++++++--
 4 files changed, 223 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/19939cb4/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/SmartStackTraceParser.java
----------------------------------------------------------------------
diff --git 
a/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/SmartStackTraceParser.java
 
b/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/SmartStackTraceParser.java
index ec67df8..2d8f64e 100644
--- 
a/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/SmartStackTraceParser.java
+++ 
b/surefire-providers/common-java5/src/main/java/org/apache/maven/surefire/report/SmartStackTraceParser.java
@@ -19,9 +19,13 @@ package org.apache.maven.surefire.report;
  * under the License.
  */
 
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+
 import org.apache.maven.shared.utils.StringUtils;
 
 /**
@@ -85,10 +89,10 @@ public class SmartStackTraceParser
         List<StackTraceElement> stackTraceElements = focusOnClass( stackTrace, 
testClass );
         Collections.reverse( stackTraceElements );
         StackTraceElement stackTraceElement;
-        if (stackTrace.length == 0)
+        if ( stackTrace.length == 0 )
         {
             result.append( simpleName );
-            result.append("XX");
+            result.append( "XX" );
         }
         for ( int i = 0; i < stackTraceElements.size(); i++ )
         {
@@ -111,7 +115,8 @@ public class SmartStackTraceParser
                 result.append( getSimpleName( stackTraceElement.getClassName() 
) ); // Add the name of the superclas
                 result.append( "." );
             }
-            result.append( stackTraceElement.getMethodName() ).append( ":" 
).append( stackTraceElement.getLineNumber() );
+            result.append( stackTraceElement.getMethodName() ).append( ":" 
).append(
+                stackTraceElement.getLineNumber() );
             result.append( "->" );
         }
 
@@ -180,7 +185,7 @@ public class SmartStackTraceParser
 
     private boolean rootIsInclass()
     {
-        return  stackTrace.length > 0 && stackTrace[0].getClassName().equals( 
testClassName );
+        return stackTrace.length > 0 && stackTrace[0].getClassName().equals( 
testClassName );
     }
 
     static List<StackTraceElement> focusOnClass( StackTraceElement[] 
stackTrace, Class clazz )
@@ -209,20 +214,29 @@ public class SmartStackTraceParser
         return testClass.getName().equals( lookFor );
     }
 
-    private static Throwable findInnermost( Throwable t )
+    static Throwable findInnermostWithClass( Throwable t, String className )
     {
-        Throwable real = t;
-        while ( real.getCause() != null )
+        Throwable match = t;
+        do
         {
-            real = real.getCause();
+            if ( containsClassName( t.getStackTrace(), className ) )
+            {
+                match = t;
+            }
+
+            t = t.getCause();
+
         }
-        return real;
+        while ( t != null );
+        return match;
     }
 
     public static String innerMostWithFocusOnClass( Throwable t, String 
className )
     {
-        List<StackTraceElement> stackTraceElements = focusInsideClass( 
findInnermost( t ).getStackTrace(), className );
-        return toString( t, stackTraceElements );
+        Throwable innermost = findInnermostWithClass( t, className );
+        List<StackTraceElement> stackTraceElements = focusInsideClass( 
innermost.getStackTrace(), className );
+        String s = causeToString( innermost.getCause() );
+        return toString( t, stackTraceElements ) + s;
     }
 
     static List<StackTraceElement> focusInsideClass( StackTraceElement[] 
stackTrace, String className )
@@ -255,7 +269,31 @@ public class SmartStackTraceParser
         return result;
     }
 
-    public static String toString( Throwable t, List<StackTraceElement> 
elements )
+    static boolean containsClassName( StackTraceElement[] stackTrace, String 
className )
+    {
+        for ( StackTraceElement element : stackTrace )
+        {
+            if ( className.equals( element.getClassName() ) )
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static String causeToString( Throwable cause )
+    {
+        StringBuilder resp = new StringBuilder();
+        while ( cause != null )
+        {
+            resp.append( "Caused by: " );
+            resp.append( toString( cause, Arrays.asList( cause.getStackTrace() 
) ) );
+            cause = cause.getCause();
+        }
+        return resp.toString();
+    }
+
+    public static String toString( Throwable t, Iterable<StackTraceElement> 
elements )
     {
         StringBuilder result = new StringBuilder();
         result.append( t.getClass().getName() );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/19939cb4/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/RunnableTestClass1.java
----------------------------------------------------------------------
diff --git 
a/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/RunnableTestClass1.java
 
b/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/RunnableTestClass1.java
new file mode 100644
index 0000000..58ed78d
--- /dev/null
+++ 
b/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/RunnableTestClass1.java
@@ -0,0 +1,52 @@
+package org.apache.maven.surefire.report;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+class RunnableTestClass1
+    implements Callable<Object>
+
+{
+    public Object call()
+        throws Exception
+    {
+        doSomethingThatThrows();
+        return "yo";
+    }
+
+    private void doSomethingThatThrows()
+        throws ExecutionException
+    {
+        RunnableTestClass2 rt2 = new RunnableTestClass2();
+        FutureTask<Object> futureTask = new FutureTask<Object>( rt2 );
+        new Thread( futureTask ).start();
+        try
+        {
+            futureTask.get();
+        }
+        catch ( InterruptedException e )
+        {
+            throw new RuntimeException();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/19939cb4/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/RunnableTestClass2.java
----------------------------------------------------------------------
diff --git 
a/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/RunnableTestClass2.java
 
b/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/RunnableTestClass2.java
new file mode 100644
index 0000000..8a13b0e
--- /dev/null
+++ 
b/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/RunnableTestClass2.java
@@ -0,0 +1,43 @@
+package org.apache.maven.surefire.report;
+
+import java.util.concurrent.Callable;
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+class RunnableTestClass2
+    implements Callable<Object>
+
+{
+
+    public Object call()
+        throws Exception
+    {
+        InnerRunnableTestClass.cThrows();
+        return null;  //To change body of implemented methods use File | 
Settings | File Templates.
+    }
+
+    static class InnerRunnableTestClass
+    {
+        public static void cThrows()
+            throws Exception
+        {
+            throw new Exception( "Hey ho, hey ho, a throwable we throw!" );
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/19939cb4/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/SmartStackTraceParserTest.java
----------------------------------------------------------------------
diff --git 
a/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/SmartStackTraceParserTest.java
 
b/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/SmartStackTraceParserTest.java
index 8880ed0..d35fedf 100644
--- 
a/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/SmartStackTraceParserTest.java
+++ 
b/surefire-providers/common-java5/src/test/java/org/apache/maven/surefire/report/SmartStackTraceParserTest.java
@@ -21,12 +21,17 @@ package org.apache.maven.surefire.report;
 
 import java.lang.reflect.Field;
 import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
 
 import junit.framework.Assert;
 import junit.framework.AssertionFailedError;
 import junit.framework.ComparisonFailure;
 import junit.framework.TestCase;
 
+import static 
org.apache.maven.surefire.report.SmartStackTraceParser.findInnermostWithClass;
+import static 
org.apache.maven.surefire.report.SmartStackTraceParser.focusInsideClass;
+
 @SuppressWarnings( "ThrowableResultOfMethodCallIgnored" )
 public class SmartStackTraceParserTest
     extends TestCase
@@ -167,8 +172,7 @@ public class SmartStackTraceParserTest
         Throwable aThrownException = getAThrownException();
 
         List<StackTraceElement> innerMost =
-            SmartStackTraceParser.focusInsideClass( 
aThrownException.getCause().getStackTrace(),
-                                                    
TestClass1.InnerBTestClass.class.getName() );
+            focusInsideClass( aThrownException.getCause().getStackTrace(), 
TestClass1.InnerBTestClass.class.getName() );
         assertEquals( 3, innerMost.size() );
         StackTraceElement inner = innerMost.get( 0 );
         assertEquals( TestClass2.InnerCTestClass.class.getName(), 
inner.getClassName() );
@@ -213,7 +217,7 @@ public class SmartStackTraceParserTest
         catch ( Throwable t )
         {
             List<StackTraceElement> stackTraceElements =
-                SmartStackTraceParser.focusInsideClass( t.getStackTrace(), 
InnerATestClass.class.getName() );
+                focusInsideClass( t.getStackTrace(), 
InnerATestClass.class.getName() );
             assertNotNull( stackTraceElements );
             assertEquals( 5, stackTraceElements.size() );
             StackTraceElement innerMost = stackTraceElements.get( 0 );
@@ -231,7 +235,7 @@ public class SmartStackTraceParserTest
     }
 
     public void testNullElementInStackTrace()
-            throws Exception
+        throws Exception
     {
         ATestClass aTestClass = new ATestClass();
         try
@@ -241,13 +245,80 @@ public class SmartStackTraceParserTest
         catch ( AssertionError e )
         {
             SmartStackTraceParser smartStackTraceParser = new 
SmartStackTraceParser( ATestClass.class, e );
-            Field stackTrace = 
SmartStackTraceParser.class.getDeclaredField("stackTrace");
-            stackTrace.setAccessible(true);
-            stackTrace.set(smartStackTraceParser, new StackTraceElement[0]);
+            Field stackTrace = SmartStackTraceParser.class.getDeclaredField( 
"stackTrace" );
+            stackTrace.setAccessible( true );
+            stackTrace.set( smartStackTraceParser, new StackTraceElement[0] );
             String res = smartStackTraceParser.getString();
             assertEquals( "ATestClass X is not Z", res );
         }
 
     }
 
+    public void testSingleNestedWithThread()
+    {
+        ExecutionException e = getSingleNested();
+        String name = this.getClass().getName();
+        Throwable focus = findInnermostWithClass( e, name );
+        assertEquals( e, focus );
+        List<StackTraceElement> stackTraceElements = focusInsideClass( 
focus.getStackTrace(), name );
+        assertEquals( stackTraceElements.get( stackTraceElements.size() - 1 
).getClassName(), name );
+    }
+
+
+    public void testDoubleNestedWithThread()
+    {
+        ExecutionException e = getDoubleNestedException();
+
+        String name = this.getClass().getName();
+        Throwable focus = findInnermostWithClass( e, name );
+        assertEquals( e, focus );
+        List<StackTraceElement> stackTraceElements = focusInsideClass( 
focus.getStackTrace(), name );
+        assertEquals( stackTraceElements.get( stackTraceElements.size() - 1 
).getClassName(), name );
+
+        name = "org.apache.maven.surefire.report.RunnableTestClass1";
+        focus = findInnermostWithClass( e, name );
+        assertEquals( e.getCause(), focus );
+        stackTraceElements = focusInsideClass( focus.getStackTrace(), name );
+        assertEquals( stackTraceElements.get( stackTraceElements.size() - 1 
).getClassName(), name );
+
+    }
+
+    public ExecutionException getSingleNested()
+    {
+        FutureTask<Object> futureTask = new FutureTask<Object>( new 
RunnableTestClass2() );
+        new Thread( futureTask ).start();
+        try
+        {
+            futureTask.get();
+        }
+        catch ( InterruptedException e )
+        {
+            fail();
+        }
+        catch ( ExecutionException e )
+        {
+            return e;
+        }
+        fail();
+        return null;
+    }
+
+    private ExecutionException getDoubleNestedException()
+    {
+        FutureTask<Object> futureTask = new FutureTask<Object>( new 
RunnableTestClass1() );
+        new Thread( futureTask ).start();
+        try
+        {
+            futureTask.get();
+        }
+        catch ( InterruptedException e )
+        {
+            fail();
+        }
+        catch ( ExecutionException e )
+        {
+            return e;
+        }
+        return null;
+    }
 }

Reply via email to