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; + } }