This is an automated email from the ASF dual-hosted git repository.
olamy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
The following commit(s) were added to refs/heads/master by this push:
new fbff927de [Issue-3226] JUnit5Xml30StatelessReporter does not change
class name in report (#3347)
fbff927de is described below
commit fbff927de16aa597d77e0128bfdd9727b58e2f2f
Author: Nils Christian Ehmke <[email protected]>
AuthorDate: Sun May 24 23:03:06 2026 +0200
[Issue-3226] JUnit5Xml30StatelessReporter does not change class name in
report (#3347)
* Fix usePhrasedTestCaseClassName=false still using @DisplayName in XML
classname attribute
When phrasedClassName was false, the classname attribute in the XML report
incorrectly used sourceText (the @DisplayName value) whenever it was
non-null,
making the flag effectively a no-op for classes annotated with @DisplayName.
The false branch now consistently uses getSourceName(), matching the
existing
pattern for phrasedMethodName.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
* Closed the resources in StatelessXmlReporterTest.
* Fixed the integration tests.
* Replaced the original solution with a new field.
---------
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
---
.../surefire/report/StatelessXmlReporter.java | 2 +-
.../plugin/surefire/report/WrappedReportEntry.java | 10 +++
.../surefire/report/StatelessXmlReporterTest.java | 77 +++++++++++++++++++---
.../maven/surefire/api/report/ReportEntry.java | 11 ++++
.../surefire/api/report/SimpleReportEntry.java | 34 ++++++++++
.../surefire/junitplatform/RunListenerAdapter.java | 24 +++++--
6 files changed, 141 insertions(+), 17 deletions(-)
diff --git
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
index 20a577381..eb7554085 100644
---
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
+++
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporter.java
@@ -465,7 +465,7 @@ private void startTestElement(XMLWriter ppw,
WrappedReportEntry report) throws I
String className = phrasedClassName
? report.getReportSourceName(reportNameSuffix)
- : report.getSourceText() != null ? report.getSourceText() :
report.getSourceName(reportNameSuffix);
+ : report.getSourceQualifiedName(reportNameSuffix);
if (className != null) {
ppw.addAttribute("classname", extraEscapeAttribute(className));
}
diff --git
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
index 4c5f7de21..ceb92b40e 100644
---
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
+++
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/WrappedReportEntry.java
@@ -181,6 +181,16 @@ String getSourceName(String suffix) {
return isBlank(suffix) ? getSourceName() : getSourceName() + "(" +
suffix + ")";
}
+ String getSourceQualifiedName(String suffix) {
+ String qualifiedName = original.getSourceQualifiedName();
+ if (qualifiedName != null) {
+ return isBlank(suffix) ? qualifiedName : qualifiedName + "(" +
suffix + ")";
+ }
+ // Fall back to pre-existing behaviour: sourceText if set, else
sourceName (with suffix)
+ String sourceText = getSourceText();
+ return sourceText != null ? sourceText : getSourceName(suffix);
+ }
+
String getReportName() {
String name = getName();
String nameText = getNameText();
diff --git
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporterTest.java
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporterTest.java
index 7dadc62d0..994513339 100644
---
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporterTest.java
+++
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/StatelessXmlReporterTest.java
@@ -29,6 +29,7 @@
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.file.Path;
+import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
@@ -240,9 +241,11 @@ public void testAllFieldsSerialized() throws IOException {
false);
reporter.testSetCompleted(testSetReportEntry, stats);
- FileInputStream fileInputStream = new
FileInputStream(expectedReportFile);
-
- Xpp3Dom testSuite = Xpp3DomBuilder.build(new
InputStreamReader(fileInputStream, UTF_8));
+ Xpp3Dom testSuite;
+ try (FileInputStream fileInputStream = new
FileInputStream(expectedReportFile);
+ InputStreamReader reader = new
InputStreamReader(fileInputStream, UTF_8)) {
+ testSuite = Xpp3DomBuilder.build(reader);
+ }
assertEquals("testsuite", testSuite.getName());
Xpp3Dom properties = testSuite.getChild("properties");
assertEquals(System.getProperties().size(),
properties.getChildCount());
@@ -353,9 +356,11 @@ public void testOutputRerunFlakyFailure() throws
IOException {
reporter.testSetCompleted(testSetReportEntry, stats);
reporter.testSetCompleted(testSetReportEntry, rerunStats);
- FileInputStream fileInputStream = new
FileInputStream(expectedReportFile);
-
- Xpp3Dom testSuite = Xpp3DomBuilder.build(new
InputStreamReader(fileInputStream, UTF_8));
+ Xpp3Dom testSuite;
+ try (FileInputStream fileInputStream = new
FileInputStream(expectedReportFile);
+ InputStreamReader reader = new
InputStreamReader(fileInputStream, UTF_8)) {
+ testSuite = Xpp3DomBuilder.build(reader);
+ }
assertEquals("testsuite", testSuite.getName());
assertEquals("0.012", testSuite.getAttribute("time"));
Xpp3Dom properties = testSuite.getChild("properties");
@@ -477,9 +482,11 @@ public void testOutputRerunFlakyAssumption() throws
IOException {
reporter.testSetCompleted(testSetReportEntry, stats);
reporter.testSetCompleted(testSetReportEntry, rerunStats);
- FileInputStream fileInputStream = new
FileInputStream(expectedReportFile);
-
- Xpp3Dom testSuite = Xpp3DomBuilder.build(new
InputStreamReader(fileInputStream, UTF_8));
+ Xpp3Dom testSuite;
+ try (FileInputStream fileInputStream = new
FileInputStream(expectedReportFile);
+ InputStreamReader reader = new
InputStreamReader(fileInputStream, UTF_8)) {
+ testSuite = Xpp3DomBuilder.build(reader);
+ }
assertEquals("testsuite", testSuite.getName());
assertEquals("0.02", testSuite.getAttribute("time"));
@@ -654,6 +661,58 @@ public void
testReporterHandlesATestWithoutMessageAndWithEmptyStackTrace() {
reporter.testSetCompleted(testReport, stats);
}
+ @Test
+ public void testClassnameUsesActualClassNameWhenPhrasedClassNameDisabled()
throws IOException {
+ String actualClassName = "MyTest";
+ String displayName = "NewName";
+
+ ReportEntry reportEntry = new SimpleReportEntry(
+ NORMAL_RUN,
+ 0L,
+ actualClassName,
+ displayName,
+ actualClassName,
+ TEST_ONE,
+ null,
+ null,
+ 12,
+ null,
+ Collections.<String, String>emptyMap());
+ WrappedReportEntry testSetReportEntry =
+ new WrappedReportEntry(reportEntry, SUCCESS, 1771085631L, 12,
null, null, systemProps());
+ expectedReportFile = new File(reportDir, "TEST-" + actualClassName +
".xml");
+
+ stats.testSucceeded(testSetReportEntry);
+
+ StatelessXmlReporter reporter = new StatelessXmlReporter(
+ reportDir,
+ null,
+ false,
+ 0,
+ new ConcurrentHashMap<>(),
+ XSD,
+ "3.0.2",
+ false,
+ false,
+ false, // phrasedClassName = false
+ false,
+ true,
+ true,
+ false);
+ reporter.testSetCompleted(testSetReportEntry, stats);
+
+ Xpp3Dom testSuite;
+ try (FileInputStream fileInputStream = new
FileInputStream(expectedReportFile);
+ InputStreamReader reader = new
InputStreamReader(fileInputStream, UTF_8)) {
+ testSuite = Xpp3DomBuilder.build(reader);
+ }
+ Xpp3Dom testcase = testSuite.getChildren("testcase")[0];
+ assertEquals(
+ actualClassName,
+ testcase.getAttribute("classname"),
+ "classname should be the actual class name, not the
@DisplayName value");
+ }
+
private boolean defaultCharsetSupportsSpecialChar() {
// some charsets are not able to deal with \u0115 on both ways of the
conversion
return "\u0115\u00DC".equals(new String("\u0115\u00DC".getBytes()));
diff --git
a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ReportEntry.java
b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ReportEntry.java
index cb08ae917..e1d5d272a 100644
---
a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ReportEntry.java
+++
b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/ReportEntry.java
@@ -38,6 +38,17 @@ public interface ReportEntry {
*/
String getSourceText();
+ /**
+ * The actual qualified Java class name of the test (e.g. including {@code
$}-separated nested class names),
+ * independent of any display name. Returns {@code null} when the provider
has not set this value, in which case
+ * consumers should fall back to {@link #getSourceText()} / {@link
#getSourceName()}.
+ *
+ * @return qualified class name, or {@code null}
+ */
+ default String getSourceQualifiedName() {
+ return null;
+ }
+
/**
* The name of the test case.
*
diff --git
a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/SimpleReportEntry.java
b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/SimpleReportEntry.java
index 729ee9feb..f14d7f4ce 100644
---
a/surefire-api/src/main/java/org/apache/maven/surefire/api/report/SimpleReportEntry.java
+++
b/surefire-api/src/main/java/org/apache/maven/surefire/api/report/SimpleReportEntry.java
@@ -42,6 +42,8 @@ public class SimpleReportEntry implements TestSetReportEntry {
private final String sourceText;
+ private final String sourceQualifiedName;
+
private final String name;
private final String nameText;
@@ -122,10 +124,37 @@ public SimpleReportEntry(
Integer elapsed,
String message,
Map<String, String> systemProperties) {
+ this(
+ runMode,
+ testRunId,
+ source,
+ sourceText,
+ null,
+ name,
+ nameText,
+ stackTraceWriter,
+ elapsed,
+ message,
+ systemProperties);
+ }
+
+ public SimpleReportEntry(
+ @Nonnull RunMode runMode,
+ Long testRunId,
+ String source,
+ String sourceText,
+ String sourceQualifiedName,
+ String name,
+ String nameText,
+ StackTraceWriter stackTraceWriter,
+ Integer elapsed,
+ String message,
+ Map<String, String> systemProperties) {
this.runMode = runMode;
this.testRunId = testRunId;
this.source = source;
this.sourceText = sourceText;
+ this.sourceQualifiedName = sourceQualifiedName;
this.name = name;
this.nameText = nameText;
this.stackTraceWriter = stackTraceWriter;
@@ -230,6 +259,11 @@ public String getSourceText() {
return sourceText;
}
+ @Override
+ public String getSourceQualifiedName() {
+ return sourceQualifiedName;
+ }
+
@Override
public String getName() {
return name;
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 9c885e501..881a4a860 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
@@ -224,6 +224,7 @@ private SimpleReportEntry createReportEntry(
ResultDisplay classMethodName = toClassMethodName(testIdentifier);
String className = classMethodName.getClassName();
+ String qualifiedClassName = classMethodName.getQualifiedClassName();
String classText = classMethodName.getDisplayName();
if (Objects.equals(className, classText)) {
@@ -257,6 +258,7 @@ private SimpleReportEntry createReportEntry(
classMethodIndexer.indexClassMethod(className, methodName),
className,
classText,
+ qualifiedClassName,
methodName,
methodText,
stw,
@@ -395,8 +397,8 @@ private ResultDisplay toClassMethodName(TestIdentifier
testIdentifier) {
.findFirst()
.map(this::toClassMethodName)
.map(s -> new ResultDisplay(
- s.getClassName(), s.getDisplayName(), null, null,
s.getClassDisplayName()))
- .orElse(new ResultDisplay(realClassName, realClassName,
null, null, classDisplayName));
+ s.getClassName(), s.getDisplayName(), null, null,
s.getClassDisplayName(), null))
+ .orElse(new ResultDisplay(realClassName, realClassName,
null, null, classDisplayName, null));
String parentDisplay =
collectAllTestIdentifiersInHierarchy(testIdentifier)
.filter(identifier -> identifier
@@ -444,7 +446,8 @@ private ResultDisplay toClassMethodName(TestIdentifier
testIdentifier) {
source.getDisplayName(),
methodDesc,
methodDisp,
- classDisplayName);
+ classDisplayName,
+ realClassName);
} else if
(testSource.filter(ClassSource.class::isInstance).isPresent()) {
ClassSource classSource =
testSource.map(ClassSource.class::cast).get();
String className = classSource.getClassName();
@@ -454,12 +457,13 @@ private ResultDisplay toClassMethodName(TestIdentifier
testIdentifier) {
displayNameTagValue.orElse(className),
null,
null,
- classDisplayName); // != null ? classDisplayName :
className);
+ classDisplayName, // != null ? classDisplayName :
className);
+ className);
} else {
String source = testPlan.getParent(testIdentifier)
.map(TestIdentifier::getDisplayName)
.orElse(display);
- return new ResultDisplay(classLevelName.orElse(source), source,
display, display, classDisplayName);
+ return new ResultDisplay(classLevelName.orElse(source), source,
display, display, classDisplayName, null);
}
}
@@ -489,19 +493,21 @@ private Optional<String>
findDisplayNameTagValue(TestIdentifier testIdentifier)
}
private static class ResultDisplay {
- private String className, displayName, methodSignature,
methodDisplayName, classDisplayName;
+ private String className, displayName, methodSignature,
methodDisplayName, classDisplayName, qualifiedClassName;
ResultDisplay(
String className,
String displayName,
String methodSignature,
String methodDisplayName,
- String classDisplayName) {
+ String classDisplayName,
+ String qualifiedClassName) {
this.className = className;
this.displayName = displayName;
this.methodSignature = methodSignature;
this.methodDisplayName = methodDisplayName;
this.classDisplayName = classDisplayName;
+ this.qualifiedClassName = qualifiedClassName;
}
public String getClassName() {
@@ -523,6 +529,10 @@ public String getMethodDisplayName() {
public String getClassDisplayName() {
return classDisplayName;
}
+
+ public String getQualifiedClassName() {
+ return qualifiedClassName;
+ }
}
/**