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

olamy pushed a commit to branch SUREFIRE-1643-junit5-parallel
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git

commit d7f380f3bee252e328855c635b74fe54c3fccc34
Author: Olivier Lamy <ol...@apache.org>
AuthorDate: Sun Mar 16 21:49:54 2025 +1000

    [SUREFIRE-1643] Make surefire junit5 supporting parallel execution and not 
mixing result when re running tests
    
    Signed-off-by: Olivier Lamy <ol...@apache.org>
---
 .../surefire/StartupReportConfiguration.java       |  2 +-
 .../surefire/report/StatelessXmlReporter.java      |  6 +--
 .../plugin/surefire/report/TestSetRunListener.java | 54 ++++++++++++++--------
 surefire-providers/surefire-junit-platform/pom.xml |  5 ++
 .../junitplatform/JUnitPlatformProvider.java       | 18 ++++++--
 5 files changed, 58 insertions(+), 27 deletions(-)

diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
index 89e6614f3..673ccd8a0 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/StartupReportConfiguration.java
@@ -220,7 +220,7 @@ public StatelessReportEventListener<WrappedReportEntry, 
TestSetStats> instantiat
         // In the in-plugin execution of parallel JUnit4.7 with rerun the map 
must be shared because reports and
         // listeners are in ThreadLocal, see 
Surefire1122ParallelAndFlakyTestsIT.
         Map<String, Deque<WrappedReportEntry>> testClassMethodRunHistory =
-                isForking ? new ConcurrentHashMap<String, 
Deque<WrappedReportEntry>>() : this.testClassMethodRunHistory;
+                isForking ? new ConcurrentHashMap<>() : 
this.testClassMethodRunHistory;
 
         DefaultStatelessReportMojoConfiguration xmlReporterConfig = new 
DefaultStatelessReportMojoConfiguration(
                 resolveReportsDirectory(forkNumber),
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 7724b1210..aab9d665b 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
@@ -20,12 +20,12 @@
 
 import java.io.BufferedOutputStream;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.FilterOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
+import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Deque;
 import java.util.LinkedHashMap;
@@ -140,7 +140,7 @@ public StatelessXmlReporter(
         this.rerunFailingTestsCount = rerunFailingTestsCount;
         this.testClassMethodRunHistoryMap = testClassMethodRunHistoryMap;
         this.xsdSchemaLocation = xsdSchemaLocation;
-        this.xsdVersion = xsdVersion;
+        this.xsdVersion = xsdVersion;SUREFIRE-1643
         this.phrasedFileName = phrasedFileName;
         this.phrasedSuiteName = phrasedSuiteName;
         this.phrasedClassName = phrasedClassName;
@@ -392,7 +392,7 @@ private OutputStream getOutputStream(WrappedReportEntry 
testSetReportEntry) thro
         reportFile.delete();
         //noinspection ResultOfMethodCallIgnored
         reportDir.mkdirs();
-        return new BufferedOutputStream(new FileOutputStream(reportFile), 64 * 
1024);
+        return new 
BufferedOutputStream(Files.newOutputStream(reportFile.toPath()), 64 * 1024);
     }
 
     private static OutputStreamWriter getWriter(OutputStream fos) {
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
index 5e90fb055..716fca87e 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/TestSetRunListener.java
@@ -22,7 +22,9 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
 
 import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
 import org.apache.maven.surefire.api.report.ReportEntry;
@@ -48,7 +50,7 @@
 public class TestSetRunListener implements 
TestReportListener<TestOutputReportEntry> {
     private final Queue<TestMethodStats> testMethodStats = new 
ConcurrentLinkedQueue<>();
 
-    private final TestSetStats detailsForThis;
+    private final ConcurrentMap<String, TestSetStats> detailsForThis = new 
ConcurrentHashMap<>();
 
     private final ConsoleOutputReportEventListener testOutputReceiver;
 
@@ -64,6 +66,10 @@ public class TestSetRunListener implements 
TestReportListener<TestOutputReportEn
 
     private final Object lock;
 
+    private final boolean trimStackTrace;
+
+    private final boolean isPlainFormat;
+
     private Utf8RecodingDeferredFileOutputStream testStdOut = 
initDeferred("stdout");
 
     private Utf8RecodingDeferredFileOutputStream testStdErr = 
initDeferred("stderr");
@@ -85,7 +91,9 @@ public TestSetRunListener(
         this.simpleXMLReporter = simpleXMLReporter;
         this.testOutputReceiver = testOutputReceiver;
         this.briefOrPlainFormat = briefOrPlainFormat;
-        detailsForThis = new TestSetStats(trimStackTrace, isPlainFormat);
+        this.trimStackTrace = trimStackTrace;
+        this.isPlainFormat = isPlainFormat;
+        // detailsForThis = new TestSetStats(trimStackTrace, isPlainFormat);
         this.lock = lock;
     }
 
@@ -166,7 +174,9 @@ public void writeTestOutput(TestOutputReportEntry 
reportEntry) {
 
     @Override
     public void testSetStarting(TestSetReportEntry report) {
-        detailsForThis.testSetStart();
+        detailsForThis
+                .computeIfAbsent(report.getSourceName(), s -> new 
TestSetStats(trimStackTrace, isPlainFormat))
+                .testSetStart();
         consoleReporter.testSetStarting(report);
         testOutputReceiver.testSetStarting(report);
     }
@@ -187,20 +197,20 @@ private void clearCapture() {
     @Override
     public void testSetCompleted(TestSetReportEntry report) {
         final WrappedReportEntry wrap = wrapTestSet(report);
-        final List<String> testResults =
-                briefOrPlainFormat ? detailsForThis.getTestResults() : 
Collections.<String>emptyList();
-        fileReporter.testSetCompleted(wrap, detailsForThis, testResults);
-        simpleXMLReporter.testSetCompleted(wrap, detailsForThis);
+        TestSetStats testSetStats = detailsForThis.get(report.getSourceName());
+        final List<String> testResults = briefOrPlainFormat ? 
testSetStats.getTestResults() : Collections.emptyList();
+        fileReporter.testSetCompleted(wrap, testSetStats, testResults);
+        simpleXMLReporter.testSetCompleted(wrap, testSetStats);
         statisticsReporter.testSetCompleted();
-        consoleReporter.testSetCompleted(wrap, detailsForThis, testResults);
+        consoleReporter.testSetCompleted(wrap, testSetStats, testResults);
         testOutputReceiver.testSetCompleted(wrap);
         consoleReporter.reset();
 
         wrap.getStdout().free();
         wrap.getStdErr().free();
 
-        addTestMethodStats();
-        detailsForThis.reset();
+        addTestMethodStats(report);
+        testSetStats.reset();
         clearCapture();
     }
 
@@ -210,13 +220,15 @@ public void testSetCompleted(TestSetReportEntry report) {
 
     @Override
     public void testStarting(ReportEntry report) {
-        detailsForThis.testStart();
+        detailsForThis
+                .computeIfAbsent(report.getSourceName(), s -> new 
TestSetStats(trimStackTrace, isPlainFormat))
+                .testSetStart();
     }
 
     @Override
     public void testSucceeded(ReportEntry reportEntry) {
         WrappedReportEntry wrapped = wrap(reportEntry, SUCCESS);
-        detailsForThis.testSucceeded(wrapped);
+        detailsForThis.get(reportEntry.getSourceName()).testSucceeded(wrapped);
         statisticsReporter.testSucceeded(reportEntry);
         clearCapture();
     }
@@ -224,7 +236,7 @@ public void testSucceeded(ReportEntry reportEntry) {
     @Override
     public void testError(ReportEntry reportEntry) {
         WrappedReportEntry wrapped = wrap(reportEntry, ERROR);
-        detailsForThis.testError(wrapped);
+        detailsForThis.get(reportEntry.getSourceName()).testError(wrapped);
         statisticsReporter.testError(reportEntry);
         clearCapture();
     }
@@ -232,7 +244,7 @@ public void testError(ReportEntry reportEntry) {
     @Override
     public void testFailed(ReportEntry reportEntry) {
         WrappedReportEntry wrapped = wrap(reportEntry, FAILURE);
-        detailsForThis.testFailure(wrapped);
+        detailsForThis.get(reportEntry.getSourceName()).testFailure(wrapped);
         statisticsReporter.testFailed(reportEntry);
         clearCapture();
     }
@@ -244,7 +256,7 @@ public void testFailed(ReportEntry reportEntry) {
     @Override
     public void testSkipped(ReportEntry reportEntry) {
         WrappedReportEntry wrapped = wrap(reportEntry, SKIPPED);
-        detailsForThis.testSkipped(wrapped);
+        detailsForThis.get(reportEntry.getSourceName()).testSkipped(wrapped);
         statisticsReporter.testSkipped(reportEntry);
         clearCapture();
     }
@@ -263,7 +275,8 @@ private WrappedReportEntry wrap(ReportEntry other, 
ReportEntryType reportEntryTy
         int estimatedElapsed = 0;
         if (reportEntryType != SKIPPED) {
             Integer etime = other.getElapsed();
-            estimatedElapsed = etime == null ? 
detailsForThis.getElapsedSinceLastStart() : etime;
+            estimatedElapsed =
+                    etime == null ? 
detailsForThis.get(other.getSourceName()).getElapsedSinceLastStart() : etime;
         }
 
         return new WrappedReportEntry(other, reportEntryType, 
estimatedElapsed, testStdOut, testStdErr);
@@ -273,7 +286,9 @@ private WrappedReportEntry wrapTestSet(TestSetReportEntry 
other) {
         return new WrappedReportEntry(
                 other,
                 null,
-                other.getElapsed() != null ? other.getElapsed() : 
detailsForThis.getElapsedSinceTestSetStart(),
+                other.getElapsed() != null
+                        ? other.getElapsed()
+                        : 
detailsForThis.get(other.getSourceName()).getElapsedSinceTestSetStart(),
                 testStdOut,
                 testStdErr,
                 other.getSystemProperties());
@@ -283,8 +298,9 @@ public void close() {
         testOutputReceiver.close();
     }
 
-    private void addTestMethodStats() {
-        for (WrappedReportEntry reportEntry : 
detailsForThis.getReportEntries()) {
+    private void addTestMethodStats(TestSetReportEntry report) {
+        for (WrappedReportEntry reportEntry :
+                detailsForThis.get(report.getSourceName()).getReportEntries()) 
{
             TestMethodStats methodStats = new TestMethodStats(
                     reportEntry.getClassMethodName(),
                     reportEntry.getReportEntryType(),
diff --git a/surefire-providers/surefire-junit-platform/pom.xml 
b/surefire-providers/surefire-junit-platform/pom.xml
index b850fc855..a6c1da758 100644
--- a/surefire-providers/surefire-junit-platform/pom.xml
+++ b/surefire-providers/surefire-junit-platform/pom.xml
@@ -104,6 +104,11 @@
       <artifactId>junit-platform-launcher</artifactId>
       <version>${junitPlatformVersion}</version>
     </dependency>
+    <dependency>
+      <groupId>org.junit.platform</groupId>
+      <artifactId>junit-platform-reporting</artifactId>
+      <version>${junitPlatformVersion}</version>
+    </dependency>
     <dependency>
       <groupId>org.junit.jupiter</groupId>
       <artifactId>junit-jupiter-engine</artifactId>
diff --git 
a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
 
b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
index 5e8255490..13dd3be31 100644
--- 
a/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
+++ 
b/surefire-providers/surefire-junit-platform/src/main/java/org/apache/maven/surefire/junitplatform/JUnitPlatformProvider.java
@@ -18,7 +18,9 @@
  */
 package org.apache.maven.surefire.junitplatform;
 
+import java.io.File;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.io.StringReader;
 import java.io.UncheckedIOException;
 import java.util.ArrayList;
@@ -46,8 +48,10 @@
 import org.junit.platform.launcher.Launcher;
 import org.junit.platform.launcher.LauncherDiscoveryRequest;
 import org.junit.platform.launcher.TagFilter;
+import org.junit.platform.launcher.TestExecutionListener;
 import org.junit.platform.launcher.TestIdentifier;
 import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
+import 
org.junit.platform.reporting.legacy.xml.LegacyXmlReportGeneratingListener;
 
 import static java.util.Arrays.stream;
 import static java.util.Collections.emptyMap;
@@ -172,6 +176,13 @@ private void invokeAllTests(TestsToRun testsToRun, 
RunListenerAdapter adapter) {
     }
 
     private void execute(TestsToRun testsToRun, RunListenerAdapter adapter) {
+
+        TestExecutionListener[] testExecutionListeners = new 
TestExecutionListener[] {
+            adapter,
+            new LegacyXmlReportGeneratingListener(
+                    new File("target", "junit-platform").toPath(), new 
PrintWriter(System.out))
+        };
+
         if (testsToRun.allowEagerReading()) {
             List<DiscoverySelector> selectors = new ArrayList<>();
             testsToRun.iterator().forEachRemaining(c -> 
selectors.add(selectClass(c.getName())));
@@ -180,15 +191,14 @@ private void execute(TestsToRun testsToRun, 
RunListenerAdapter adapter) {
                     .filters(filters)
                     .configurationParameters(configurationParameters)
                     .selectors(selectors);
-
-            launcher.execute(builder.build(), adapter);
+            launcher.execute(builder.build(), testExecutionListeners);
         } else {
             testsToRun.iterator().forEachRemaining(c -> {
                 LauncherDiscoveryRequestBuilder builder = request()
                         .filters(filters)
                         .configurationParameters(configurationParameters)
                         .selectors(selectClass(c.getName()));
-                launcher.execute(builder.build(), adapter);
+                launcher.execute(builder.build(), testExecutionListeners);
             });
         }
     }
@@ -237,7 +247,7 @@ private Filter<?>[] newFilters() {
                 .map(EngineFilter::excludeEngines)
                 .ifPresent(filters::add);
 
-        return filters.toArray(new Filter<?>[filters.size()]);
+        return filters.toArray(new Filter<?>[0]);
     }
 
     Filter<?>[] getFilters() {

Reply via email to