Skip to content

Commit a87457b

Browse files
committed
[SUREFIRE-2152] Handle JUnit5 templated-tests separately to set name/displayName
Ensures that all JUnit5 templated tests have a unique name that includes their invocation ID (index) This change also covers an issue of SUREFIRE-2087 where certain tests (namely, tests defined through a @testtemplate) still cause mixups of test invocations in relation with rerunFailingTestsCount, which can be mixed up by surefire if their name isn't unique for each distinct invocation. For an example description of this issue, see also: https://issues.apache.org/jira/browse/SUREFIRE-2087#comment-17690951
1 parent 208eae2 commit a87457b

File tree

1 file changed

+54
-0
lines changed
  • surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform

1 file changed

+54
-0
lines changed

surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/RunListenerAdapter.java

+54
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import java.util.Optional;
3434
import java.util.concurrent.ConcurrentHashMap;
3535
import java.util.concurrent.ConcurrentMap;
36+
import java.util.regex.Matcher;
37+
import java.util.regex.Pattern;
3638
import java.util.stream.Stream;
3739

3840
import org.apache.maven.surefire.api.report.OutputReportEntry;
@@ -60,6 +62,8 @@
6062
final class RunListenerAdapter
6163
implements TestExecutionListener, TestOutputReceiver<OutputReportEntry>, RunModeSetter
6264
{
65+
private static final Pattern COMMA_PATTERN = Pattern.compile( "," );
66+
6367
private final ClassMethodIndexer classMethodIndexer = new ClassMethodIndexer();
6468
private final ConcurrentMap<TestIdentifier, Long> testStartTime = new ConcurrentHashMap<>();
6569
private final ConcurrentMap<TestIdentifier, TestExecutionResult> failures = new ConcurrentHashMap<>();
@@ -362,6 +366,23 @@ private String[] toClassMethodName( TestIdentifier testIdentifier )
362366
// param || m()[1] | [1] <param>
363367
// param+displ || m()[1] | displ
364368

369+
// Override resulting methodDesc/methodDisp values again, for invocations of
370+
// JUnit5 templated-tests (such as @ParameterizedTest/@RepeatedTest)
371+
Integer templatedTestInvocationId = extractTemplatedInvocationId( testIdentifier );
372+
if ( templatedTestInvocationId != null )
373+
{
374+
String simpleClassNames = COMMA_PATTERN.splitAsStream( methodSource.getMethodParameterTypes() )
375+
.map( s -> s.substring( 1 + s.lastIndexOf( '.' ) ).trim() )
376+
.collect( joining( ", " ) );
377+
378+
String methodSignature = methodName + '(' + simpleClassNames + ')';
379+
380+
String invocationIdStr = "[" + templatedTestInvocationId + "]";
381+
382+
methodDesc = methodSignature + invocationIdStr;
383+
methodDisp = parentDisplay + display;
384+
}
385+
365386
return new String[] {source[0], source[1], methodDesc, methodDisp};
366387
}
367388
else if ( testSource.filter( ClassSource.class::isInstance ).isPresent() )
@@ -390,6 +411,39 @@ else if ( testSource.filter( ClassSource.class::isInstance ).isPresent() )
390411
}
391412
}
392413

414+
private static final Pattern TEST_TEMPLATE_INVOCATION_MATCHER =
415+
Pattern.compile( "\\[test-template-invocation:#([1-9][0-9]*)]" );
416+
417+
/**
418+
* If the given test-id defines an invocation of a templated-test (such as a specific
419+
* instance of a @ParameterizedTest or @RepeatedTest), returns the invocation-id of
420+
* that instance (1, 2, ...)
421+
*
422+
* <p>Returns null if the given test-id doesn't seem to be a templated-test invocation,
423+
* or if no invocation-id could be extracted.
424+
*/
425+
private Integer extractTemplatedInvocationId( TestIdentifier testId )
426+
{
427+
/*
428+
Note: with JUnit 5.8+, we could make this nicer using testId.getUniqueIdObject()
429+
430+
# Segment lastSegment = testId.getUniqueIdObject().getLastSegment();
431+
# if (lastSegment.getType().equals(TestTemplateInvocationTestDescriptor.SEGMENT_TYPE)) {
432+
# String invocationIdStr = lastSegment.getValue(); // #1, #2, ...
433+
# if (invocationIdStr.startsWith("#")) { // assuming always true
434+
# return Integer.valueOf(invocationIdStr.substring(1));
435+
# }
436+
# }
437+
*/
438+
Matcher m = TEST_TEMPLATE_INVOCATION_MATCHER.matcher( testId.getUniqueId() );
439+
if ( m.find() )
440+
{
441+
String group = m.group( 1 );
442+
return Integer.valueOf( group );
443+
}
444+
return null;
445+
}
446+
393447
/**
394448
* @return Map of tests that failed.
395449
*/

0 commit comments

Comments
 (0)