You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ti...@apache.org on 2018/12/23 16:17:51 UTC

[maven-surefire] 01/01: [SUREFIRE-1546] JUnit 5 runner does not honor JUnit 5 display names

This is an automated email from the ASF dual-hosted git repository.

tibordigana pushed a commit to branch SUREFIRE-1546-dispname
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git

commit c5e93878a0abc317c2b402c415adfacc435929fa
Author: Tibor17 <ti...@apache.org>
AuthorDate: Sat Dec 22 10:53:40 2018 +0100

    [SUREFIRE-1546] JUnit 5 runner does not honor JUnit 5 display names
---
 .../surefire/runorder/RunEntryStatisticsMap.java   |  20 +---
 .../util/internal/TestClassMethodNameUtils.java    |  21 ++--
 .../maven/surefire/its/JUnitPlatformEnginesIT.java |   3 +-
 .../test/java/junitplatform/DisplayNameTest.java   |  37 +++++++
 .../surefire/junitplatform/RunListenerAdapter.java | 114 +++++++++------------
 5 files changed, 97 insertions(+), 98 deletions(-)

diff --git a/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java b/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java
index 1a685dc..40d396b 100644
--- a/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java
+++ b/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java
@@ -19,7 +19,6 @@ package org.apache.maven.plugin.surefire.runorder;
  * under the License.
  */
 
-
 import org.apache.maven.surefire.report.ReportEntry;
 
 import java.io.BufferedReader;
@@ -36,12 +35,11 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 import static java.util.Collections.sort;
 import static org.apache.maven.plugin.surefire.runorder.RunEntryStatistics.fromReportEntry;
 import static org.apache.maven.plugin.surefire.runorder.RunEntryStatistics.fromString;
+import static org.apache.maven.surefire.util.internal.TestClassMethodNameUtils.extractClassName;
 
 /**
  * @author Kristian Rosenvold
@@ -50,7 +48,7 @@ public final class RunEntryStatisticsMap
 {
     private final Map<String, RunEntryStatistics> runEntryStatistics;
 
-    public RunEntryStatisticsMap( Map<String, RunEntryStatistics> runEntryStatistics )
+    private RunEntryStatisticsMap( Map<String, RunEntryStatistics> runEntryStatistics )
     {
         this.runEntryStatistics = new ConcurrentHashMap<>( runEntryStatistics );
     }
@@ -112,7 +110,7 @@ public final class RunEntryStatisticsMap
         }
     }
 
-    public RunEntryStatistics findOrCreate( ReportEntry reportEntry )
+    private RunEntryStatistics findOrCreate( ReportEntry reportEntry )
     {
         final RunEntryStatistics item = runEntryStatistics.get( reportEntry.getName() );
         return item != null ? item : fromReportEntry( reportEntry );
@@ -255,16 +253,4 @@ public final class RunEntryStatisticsMap
             return o.getMinSuccessRate() - o1.getMinSuccessRate();
         }
     }
-
-
-    private static final Pattern PARENS = Pattern.compile( "^" + "[^\\(\\)]+" //non-parens
-                                                               + "\\((" // then an open-paren (start matching a group)
-                                                               + "[^\\\\(\\\\)]+" //non-parens
-                                                               + ")\\)" + "$" ); // then a close-paren (end group match)
-
-    String extractClassName( String displayName )
-    {
-        Matcher m = PARENS.matcher( displayName );
-        return m.find() ? m.group( 1 ) : displayName;
-    }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/TestClassMethodNameUtils.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/TestClassMethodNameUtils.java
index 23e72e1..3371993 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/TestClassMethodNameUtils.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/internal/TestClassMethodNameUtils.java
@@ -19,9 +19,6 @@ package org.apache.maven.surefire.util.internal;
  * under the License.
  */
 
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 /**
  * JUnit Description parser.
  * Used by JUnit Version lower than 4.7.
@@ -31,12 +28,6 @@ import java.util.regex.Pattern;
  */
 public final class TestClassMethodNameUtils
 {
-    /**
-     * This pattern is verbatim copy from JUnit's code in class {@code Description}.
-     * Parsing class and method from junit description would provide identical result to JUnit internal parser.
-     */
-    private static final Pattern METHOD_CLASS_PATTERN = Pattern.compile( "([\\s\\S]*)\\((.*)\\)" );
-
     private TestClassMethodNameUtils()
     {
         throw new IllegalStateException( "no instantiable constructor" );
@@ -44,13 +35,17 @@ public final class TestClassMethodNameUtils
 
     public static String extractClassName( String displayName )
     {
-        Matcher m = METHOD_CLASS_PATTERN.matcher( displayName );
-        return m.matches() ? m.group( 2 ) : displayName;
+        if ( displayName.endsWith( ")" ) )
+        {
+            int paren = displayName.lastIndexOf( '(' );
+            return paren == -1 ? displayName : displayName.substring( paren + 1, displayName.length() - 1 );
+        }
+        return displayName;
     }
 
     public static String extractMethodName( String displayName )
     {
-        Matcher m = METHOD_CLASS_PATTERN.matcher( displayName );
-        return m.matches() ? m.group( 1 ) : displayName;
+        int paren = displayName.lastIndexOf( '(' );
+        return paren == -1 ? displayName : displayName.substring( 0, paren );
     }
 }
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java
index 332cbb9..3c07777 100644
--- a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformEnginesIT.java
@@ -63,6 +63,7 @@ public class JUnitPlatformEnginesIT
         args.add( new Object[] { "1.1.1", "5.1.1", "1.0.0", "1.0.0" } );
         args.add( new Object[] { "1.2.0", "5.2.0", "1.1.0", "1.0.0" } );
         args.add( new Object[] { "1.3.1", "5.3.1", "1.1.1", "1.0.0" } );
+        args.add( new Object[] { "1.3.2", "5.3.2", "1.1.1", "1.0.0" } );
         args.add( new Object[] { "1.4.0-SNAPSHOT", "5.4.0-SNAPSHOT", "1.1.1", "1.0.0" } );
         return args;
     }
@@ -88,7 +89,7 @@ public class JUnitPlatformEnginesIT
                 .sysProp( "jupiter.version", jupiter )
                 .debugLogging()
                 .executeTest()
-                .verifyErrorFree( 1 );
+                .verifyErrorFree( 2 );
 
         String testClasspath = "[DEBUG] test(compact) classpath:"
                 + "  test-classes"
diff --git a/surefire-its/src/test/resources/junit-platform/src/test/java/junitplatform/DisplayNameTest.java b/surefire-its/src/test/resources/junit-platform/src/test/java/junitplatform/DisplayNameTest.java
new file mode 100644
index 0000000..f47dfa1
--- /dev/null
+++ b/surefire-its/src/test/resources/junit-platform/src/test/java/junitplatform/DisplayNameTest.java
@@ -0,0 +1,37 @@
+package junitplatform_1_0_0;
+
+/*
+ * 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.
+ */
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.util.logging.Logger;
+
+class DisplayNameTest
+{
+    private static final Logger LOGGER = Logger.getLogger( DisplayNameTest.class.getName() );
+
+    @Test
+    @DisplayName( "Custom test name containing spaces" )
+    void test()
+    {
+        LOGGER.warning( "some warning from test" );
+    }
+}
diff --git a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java
index 85227f3..c2810f1 100644
--- a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java
+++ b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java
@@ -20,6 +20,9 @@ package org.apache.maven.surefire.junitplatform;
  */
 
 import static org.apache.maven.surefire.report.SimpleReportEntry.ignored;
+import static org.apache.maven.surefire.report.SimpleReportEntry.withException;
+import static org.apache.maven.surefire.util.internal.TestClassMethodNameUtils.extractClassName;
+import static org.apache.maven.surefire.util.internal.TestClassMethodNameUtils.extractMethodName;
 import static org.junit.platform.engine.TestExecutionResult.Status.ABORTED;
 import static org.junit.platform.engine.TestExecutionResult.Status.FAILED;
 
@@ -38,7 +41,6 @@ import org.junit.platform.engine.support.descriptor.MethodSource;
 import org.junit.platform.launcher.TestExecutionListener;
 import org.junit.platform.launcher.TestIdentifier;
 import org.junit.platform.launcher.TestPlan;
-import org.junit.platform.launcher.listeners.LegacyReportingUtils;
 
 /**
  * @since 2.22.0
@@ -46,12 +48,11 @@ import org.junit.platform.launcher.listeners.LegacyReportingUtils;
 final class RunListenerAdapter
     implements TestExecutionListener
 {
-
     private final RunListener runListener;
 
-    private TestPlan testPlan;
+    private final Set<TestIdentifier> testSetNodes = ConcurrentHashMap.newKeySet();
 
-    private Set<TestIdentifier> testSetNodes = ConcurrentHashMap.newKeySet();
+    private volatile TestPlan testPlan;
 
     RunListenerAdapter( RunListener runListener )
     {
@@ -89,14 +90,15 @@ final class RunListenerAdapter
     public void executionSkipped( TestIdentifier testIdentifier, String reason )
     {
         ensureTestSetStarted( testIdentifier );
-        String source = getLegacyReportingClassName( testIdentifier );
-        runListener.testSkipped( ignored( source, getLegacyReportingName( testIdentifier ), reason ) );
+        String[] classMethodName = toClassMethodName( testIdentifier );
+        String className = classMethodName[0];
+        String methodName = classMethodName[1];
+        runListener.testSkipped( ignored( className, methodName, reason ) );
         completeTestSetIfNecessary( testIdentifier );
     }
 
     @Override
-    public void executionFinished(
-                    TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
+    public void executionFinished( TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
     {
         if ( testExecutionResult.getStatus() == ABORTED )
         {
@@ -121,17 +123,11 @@ final class RunListenerAdapter
 
     private void ensureTestSetStarted( TestIdentifier testIdentifier )
     {
-        if ( isTestSetStarted( testIdentifier ) )
-        {
-            return;
-        }
-        if ( testIdentifier.isTest() )
-        {
-            startTestSet( testPlan.getParent( testIdentifier ).orElse( testIdentifier ) );
-        }
-        else
+        if ( !isTestSetStarted( testIdentifier ) )
         {
-            startTestSet( testIdentifier );
+            startTestSet( testIdentifier.isTest()
+                    ? testPlan.getParent( testIdentifier ).orElse( testIdentifier )
+                    : testIdentifier );
         }
     }
 
@@ -185,8 +181,10 @@ final class RunListenerAdapter
 
     private SimpleReportEntry createTestSetReportEntry( TestIdentifier testIdentifier )
     {
-        return new SimpleReportEntry(
-                        JUnitPlatformProvider.class.getName(), testIdentifier.getLegacyReportingName() );
+        String[] classMethodName = toClassMethodName( testIdentifier );
+        String className = classMethodName[0];
+        String methodName = classMethodName[1];
+        return new SimpleReportEntry( className, methodName );
     }
 
     private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier )
@@ -194,79 +192,61 @@ final class RunListenerAdapter
         return createReportEntry( testIdentifier, (StackTraceWriter) null );
     }
 
-    private SimpleReportEntry createReportEntry(
-                    TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
-    {
-        return createReportEntry(
-                        testIdentifier, getStackTraceWriter( testIdentifier, testExecutionResult ) );
-    }
-
-    private SimpleReportEntry createReportEntry(
-                    TestIdentifier testIdentifier, StackTraceWriter stackTraceWriter )
-    {
-        String source = getLegacyReportingClassName( testIdentifier );
-        String name = getLegacyReportingName( testIdentifier );
-
-        return SimpleReportEntry.withException( source, name, stackTraceWriter );
-    }
-
-    private String getLegacyReportingName( TestIdentifier testIdentifier )
+    private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier,
+                                                 TestExecutionResult testExecutionResult )
     {
-        // Surefire cuts off the name at the first '(' character. Thus, we have to pick a different
-        // character to represent parentheses. "()" are removed entirely to maximize compatibility with
-        // existing reporting tools because in the old days test methods used to not have parameters.
-        return testIdentifier
-                        .getLegacyReportingName()
-                        .replace( "()", "" )
-                        .replace( '(', '{' )
-                        .replace( ')', '}' );
+        return createReportEntry( testIdentifier, toStackTraceWriter( testIdentifier, testExecutionResult ) );
     }
 
-    private String getLegacyReportingClassName( TestIdentifier testIdentifier )
+    private SimpleReportEntry createReportEntry( TestIdentifier testIdentifier, StackTraceWriter stackTraceWriter )
     {
-        return LegacyReportingUtils.getClassName( testPlan, testIdentifier );
+        String[] classMethodName = toClassMethodName( testIdentifier );
+        String className = classMethodName[0];
+        String methodName = classMethodName[1];
+        return withException( className, methodName, stackTraceWriter );
     }
 
-    private StackTraceWriter getStackTraceWriter(
-                    TestIdentifier testIdentifier, TestExecutionResult testExecutionResult )
+    private StackTraceWriter toStackTraceWriter( TestIdentifier testIdentifier,
+                                                 TestExecutionResult testExecutionResult )
     {
         Optional<Throwable> throwable = testExecutionResult.getThrowable();
         if ( testExecutionResult.getStatus() == FAILED )
         {
             // Failed tests must have a StackTraceWriter, otherwise Surefire will fail
-            return getStackTraceWriter( testIdentifier, throwable.orElse( null ) );
+            return toStackTraceWriter( testIdentifier, throwable.orElse( null ) );
         }
-        return throwable.map( t -> getStackTraceWriter( testIdentifier, t ) ).orElse( null );
+        return throwable.map( t -> toStackTraceWriter( testIdentifier, t ) )
+                .orElse( null );
     }
 
-    private StackTraceWriter getStackTraceWriter( TestIdentifier testIdentifier, Throwable throwable )
+    private StackTraceWriter toStackTraceWriter( TestIdentifier testIdentifier, Throwable throwable )
     {
-        String className = getClassName( testIdentifier );
-        String methodName = getMethodName( testIdentifier ).orElse( "" );
+        String[] classMethodName = toClassMethodName( testIdentifier );
+        String className = classMethodName[0];
+        String methodName = classMethodName[1];
         return new PojoStackTraceWriter( className, methodName, throwable );
     }
 
-    private String getClassName( TestIdentifier testIdentifier )
+    private String[] toClassMethodName( TestIdentifier testIdentifier )
     {
+        String methodDisplayName = testIdentifier.getDisplayName();
         TestSource testSource = testIdentifier.getSource().orElse( null );
         if ( testSource instanceof ClassSource )
         {
-            return ( (ClassSource) testSource ).getJavaClass().getName();
+            ClassSource classSource = (ClassSource) testSource;
+            return new String[] { classSource.getClassName(), methodDisplayName };
         }
-        if ( testSource instanceof MethodSource )
+        else if ( testSource instanceof MethodSource )
         {
-            return ( (MethodSource) testSource ).getClassName();
+            MethodSource methodSource = (MethodSource) testSource;
+            return new String[] { methodSource.getClassName(), methodDisplayName };
         }
-        return testPlan.getParent( testIdentifier ).map( this::getClassName ).orElse( "" );
-    }
-
-    private Optional<String> getMethodName( TestIdentifier testIdentifier )
-    {
-        TestSource testSource = testIdentifier.getSource().orElse( null );
-        if ( testSource instanceof MethodSource )
+        else
         {
-            return Optional.of( ( (MethodSource) testSource ).getMethodName() );
+            String description = testIdentifier.getLegacyReportingName();
+            return testPlan.getParent( testIdentifier )
+                    .map( this::toClassMethodName )
+                    .orElse( new String[] { extractClassName( description ), extractMethodName( description ) } );
         }
-        return Optional.empty();
     }
 }