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

tibordigana 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 b4a53de9b Properly parse flake failures in beforeAll stages
b4a53de9b is described below

commit b4a53de9b3a9ce02f306f02b064f4f58070678f4
Author: Jakub Stejskal <[email protected]>
AuthorDate: Wed Oct 8 16:48:44 2025 +0200

    Properly parse flake failures in beforeAll stages
    
    Signed-off-by: Jakub Stejskal <[email protected]>
    
    # Conflicts:
    #       
maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
    #       
maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java
---
 .../surefire/report/DefaultReporterFactory.java    | 102 +++++++++++++++++++
 .../surefire/report/StatelessXmlReporter.java      | 112 ++++++++++++++++++---
 .../report/DefaultReporterFactoryTest.java         |  48 +++++++--
 .../its/JUnitPlatformFailingBeforeAllRerunIT.java  |  64 ++++++++++++
 .../pom.xml                                        |  93 +++++++++++++++++
 .../test/java/junitplatform/AlwaysFailingTest.java |  22 ++++
 .../java/junitplatform/FlakyFirstTimeTest.java     |  57 +++++++++++
 .../src/test/java/junitplatform/PassingTest.java   |  19 ++++
 8 files changed, 496 insertions(+), 21 deletions(-)

diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
index f95c061ab..54ed44813 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactory.java
@@ -41,6 +41,7 @@
 import 
org.apache.maven.surefire.extensions.StatelessTestsetInfoConsoleReportEventListener;
 import 
org.apache.maven.surefire.extensions.StatelessTestsetInfoFileReportEventListener;
 import org.apache.maven.surefire.report.RunStatistics;
+import org.apache.maven.surefire.shared.utils.StringUtils;
 import org.apache.maven.surefire.shared.utils.logging.MessageBuilder;
 
 import static org.apache.maven.plugin.surefire.log.api.Level.resolveLevel;
@@ -72,6 +73,9 @@ public class DefaultReporterFactory implements 
ReporterFactory, ReportsMerger {
 
     private RunStatistics globalStats = new RunStatistics();
 
+    // from "<testclass>.<testmethod>" -> statistics about all the runs for 
success tests
+    private Map<String, List<TestMethodStats>> successTests;
+
     // from "<testclass>.<testmethod>" -> statistics about all the runs for 
flaky tests
     private Map<String, List<TestMethodStats>> flakyTests;
 
@@ -243,6 +247,7 @@ static TestResultType 
getTestResultType(List<ReportEntryType> reportEntries, int
      */
     private void mergeTestHistoryResult() {
         globalStats = new RunStatistics();
+        successTests = new TreeMap<>();
         flakyTests = new TreeMap<>();
         failedTests = new TreeMap<>();
         errorTests = new TreeMap<>();
@@ -265,10 +270,40 @@ private void mergeTestHistoryResult() {
 
         // Update globalStatistics by iterating through mergedTestHistoryResult
         int completedCount = 0, skipped = 0;
+        Map<String, List<TestMethodStats>> beforeAllFailures = new HashMap<>();
 
         for (Map.Entry<String, List<TestMethodStats>> entry : 
mergedTestHistoryResult.entrySet()) {
             List<TestMethodStats> testMethodStats = entry.getValue();
             String testClassMethodName = entry.getKey();
+
+            // Handle @BeforeAll failures (null, empty, or ends with ".null" 
method names)
+            // But only if they actually failed (ERROR or FAILURE), not if 
they were skipped
+            if ((StringUtils.isBlank(testClassMethodName) || 
testClassMethodName.endsWith(".null"))
+                    && (testClassMethodName == null || 
!testClassMethodName.contains("$"))) {
+
+                // Check if this is actually a failure/error (not skipped or 
success)
+                boolean isActualFailure = testMethodStats.stream()
+                        .anyMatch(stat -> stat.getResultType() == 
ReportEntryType.ERROR
+                                || stat.getResultType() == 
ReportEntryType.FAILURE);
+
+                if (isActualFailure) {
+                    // Extract class name from the test class method name
+                    String className = 
extractClassNameFromMethodName(testClassMethodName);
+                    if (className != null) {
+                        if (beforeAllFailures.containsKey(className)) {
+                            List<TestMethodStats> previousMethodStats = 
beforeAllFailures.get(className);
+                            previousMethodStats.addAll(testMethodStats);
+                            beforeAllFailures.put(className, 
previousMethodStats);
+                        } else {
+                            beforeAllFailures.put(className, new 
ArrayList<>(testMethodStats));
+                        }
+                    }
+                    // Skip normal processing of @BeforeAll failures because 
it needs special care
+                    continue;
+                }
+                // If it's skipped or success with null method name, fall 
through to normal processing
+            }
+
             completedCount++;
 
             List<ReportEntryType> resultTypes = new ArrayList<>();
@@ -286,6 +321,7 @@ private void mergeTestHistoryResult() {
                         }
                     }
                     completedCount += successCount - 1;
+                    successTests.put(testClassMethodName, testMethodStats);
                     break;
                 case SKIPPED:
                     skipped++;
@@ -304,6 +340,37 @@ private void mergeTestHistoryResult() {
             }
         }
 
+        // Loop over all success tests and find those that are passed flakes 
for beforeAll failures
+        for (Map.Entry<String, List<TestMethodStats>> entry : 
successTests.entrySet()) {
+            List<TestMethodStats> testMethodStats = entry.getValue();
+            String testClassMethodName = entry.getKey();
+            // If current test belong to class that failed during beforeAll 
store that info to proper log info
+            String className = 
extractClassNameFromMethodName(testClassMethodName);
+            if (beforeAllFailures.containsKey(className)) {
+                List<TestMethodStats> previousMethodStats = 
beforeAllFailures.get(className);
+                previousMethodStats.addAll(testMethodStats);
+                beforeAllFailures.put(className, previousMethodStats);
+            }
+        }
+
+        // Process @BeforeAll failures after we know which classes have 
successful tests
+        for (Map.Entry<String, List<TestMethodStats>> entry : 
beforeAllFailures.entrySet()) {
+            String className = entry.getKey();
+            List<TestMethodStats> testMethodStats = entry.getValue();
+            String classNameKey = className + ".<beforeAll>";
+
+            if (reportConfiguration.getRerunFailingTestsCount() > 0
+                    && testMethodStats.stream()
+                            .anyMatch(methodStats -> 
methodStats.getTestClassMethodName() != null
+                                    && 
!methodStats.getTestClassMethodName().isEmpty()
+                                    && 
methodStats.getResultType().equals(ReportEntryType.SUCCESS))) {
+                flakyTests.put(classNameKey, testMethodStats);
+            } else {
+                errorTests.put(classNameKey, testMethodStats);
+                completedCount++;
+            }
+        }
+
         globalStats.set(completedCount, errorTests.size(), failedTests.size(), 
skipped, flakyTests.size());
     }
 
@@ -362,6 +429,41 @@ boolean printTestFailures(TestResultType type) {
         return printed;
     }
 
+    /**
+     * Extract class name from test class method name like 
"com.example.TestClass.methodName"
+     */
+    private static String extractClassNameFromMethodName(String 
testClassMethodName) {
+        if (StringUtils.isBlank(testClassMethodName)) {
+            return null;
+        }
+        int lastDotIndex = testClassMethodName.lastIndexOf('.');
+        if (lastDotIndex > 0) {
+            return testClassMethodName.substring(0, lastDotIndex);
+        }
+        return null;
+    }
+
+    /**
+     * Extract class name from stack trace when method name is null (e.g., 
@BeforeAll failures)
+     */
+    private static String extractClassNameFromStackTrace(TestMethodStats 
stats) {
+        if (stats.getStackTraceWriter() == null) {
+            return null;
+        }
+        String stackTrace = 
stats.getStackTraceWriter().smartTrimmedStackTrace();
+        if (stackTrace == null || stackTrace.isEmpty()) {
+            return null;
+        }
+
+        // Strip everything after the first whitespace character
+        int firstWhitespace = stackTrace.indexOf(' ');
+        if (firstWhitespace > 0) {
+            stackTrace = stackTrace.substring(0, firstWhitespace);
+        }
+
+        return extractClassNameFromMethodName(stackTrace);
+    }
+
     // Describe the result of a given test
     enum TestResultType {
         ERROR("Errors: "),
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 05b5059de..f925a7c25 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
@@ -161,7 +161,7 @@ public void testSetCompleted(WrappedReportEntry 
testSetReportEntry, TestSetStats
                 OutputStreamWriter fw = getWriter(outputStream)) {
             XMLWriter ppw = new PrettyPrintXMLWriter(new PrintWriter(fw), 
XML_INDENT, XML_NL, UTF_8.name(), null);
 
-            createTestSuiteElement(ppw, testSetReportEntry, testSetStats); // 
TestSuite
+            createTestSuiteElement(ppw, testSetReportEntry, 
classMethodStatistics); // TestSuite
 
             if (enablePropertiesElement) {
                 showProperties(ppw, testSetReportEntry.getSystemProperties());
@@ -186,9 +186,9 @@ public void testSetCompleted(WrappedReportEntry 
testSetReportEntry, TestSetStats
             }
 
             for (Entry<String, Map<String, List<WrappedReportEntry>>> 
statistics : classMethodStatistics.entrySet()) {
-                for (Entry<String, List<WrappedReportEntry>> thisMethodRuns :
-                        statistics.getValue().entrySet()) {
-                    serializeTestClass(outputStream, fw, ppw, 
thisMethodRuns.getValue());
+                Map<String, List<WrappedReportEntry>> methodStatistics = 
statistics.getValue();
+                for (Entry<String, List<WrappedReportEntry>> thisMethodRuns : 
methodStatistics.entrySet()) {
+                    serializeTestClass(outputStream, fw, ppw, 
thisMethodRuns.getValue(), methodStatistics);
                 }
             }
 
@@ -224,10 +224,14 @@ private Deque<WrappedReportEntry> 
aggregateCacheFromMultipleReruns(
     }
 
     private void serializeTestClass(
-            OutputStream outputStream, OutputStreamWriter fw, XMLWriter ppw, 
List<WrappedReportEntry> methodEntries)
+            OutputStream outputStream,
+            OutputStreamWriter fw,
+            XMLWriter ppw,
+            List<WrappedReportEntry> methodEntries,
+            Map<String, List<WrappedReportEntry>> methodStatistics)
             throws IOException {
         if (rerunFailingTestsCount > 0) {
-            serializeTestClassWithRerun(outputStream, fw, ppw, methodEntries);
+            serializeTestClassWithRerun(outputStream, fw, ppw, methodEntries, 
methodStatistics);
         } else {
             // rerunFailingTestsCount is smaller than 1, but for some reasons 
a test could be run
             // for more than once
@@ -258,10 +262,18 @@ private void serializeTestClassWithoutRerun(
     }
 
     private void serializeTestClassWithRerun(
-            OutputStream outputStream, OutputStreamWriter fw, XMLWriter ppw, 
List<WrappedReportEntry> methodEntries)
+            OutputStream outputStream,
+            OutputStreamWriter fw,
+            XMLWriter ppw,
+            List<WrappedReportEntry> methodEntries,
+            Map<String, List<WrappedReportEntry>> methodStatistics)
             throws IOException {
         WrappedReportEntry firstMethodEntry = methodEntries.get(0);
-        switch (getTestResultType(methodEntries)) {
+
+        TestResultType resultType =
+                
getTestResultTypeWithBeforeAllHandling(firstMethodEntry.getName(), 
methodEntries, methodStatistics);
+
+        switch (resultType) {
             case SUCCESS:
                 for (WrappedReportEntry methodEntry : methodEntries) {
                     if (methodEntry.getReportEntryType() == SUCCESS) {
@@ -370,6 +382,40 @@ private TestResultType 
getTestResultType(List<WrappedReportEntry> methodEntryLis
         return DefaultReporterFactory.getTestResultType(testResultTypeList, 
rerunFailingTestsCount);
     }
 
+    /**
+     * Determines the final result type for a test method, applying special 
handling for @BeforeAll failures.
+     * If a @BeforeAll fails but any actual test methods succeed, it's 
classified as a FLAKE.
+     *
+     * @param methodName the name of the test method (null or "null" for 
@BeforeAll)
+     * @param methodRuns the list of runs for this method
+     * @param methodStatistics all method statistics for the test class
+     * @return the final TestResultType
+     */
+    private TestResultType getTestResultTypeWithBeforeAllHandling(
+            String methodName,
+            List<WrappedReportEntry> methodRuns,
+            Map<String, List<WrappedReportEntry>> methodStatistics) {
+        TestResultType resultType = getTestResultType(methodRuns);
+
+        // Special handling for @BeforeAll failures (null method name or 
method name is "null")
+        // If @BeforeAll failed but any actual test methods succeeded, treat 
it as a flake
+        if ((methodName == null || methodName.equals("null"))
+                && (resultType == TestResultType.ERROR || resultType == 
TestResultType.FAILURE)) {
+            // Check if any actual test methods (non-null and not "null" 
names) succeeded
+            boolean hasSuccessfulTestMethods = 
methodStatistics.entrySet().stream()
+                    .filter(entry ->
+                            entry.getKey() != null && 
!entry.getKey().equals("null")) // Only actual test methods
+                    .anyMatch(entry -> entry.getValue().stream()
+                            .anyMatch(reportEntry -> 
reportEntry.getReportEntryType() == SUCCESS));
+
+            if (hasSuccessfulTestMethods) {
+                resultType = TestResultType.FLAKE;
+            }
+        }
+
+        return resultType;
+    }
+
     private Deque<WrappedReportEntry> getAddMethodRunHistoryMap(String 
testClassName) {
         Deque<WrappedReportEntry> methodRunHistory = 
testClassMethodRunHistoryMap.get(testClassName);
         if (methodRunHistory == null) {
@@ -420,7 +466,10 @@ private void startTestElement(XMLWriter ppw, 
WrappedReportEntry report) throws I
         }
     }
 
-    private void createTestSuiteElement(XMLWriter ppw, WrappedReportEntry 
report, TestSetStats testSetStats)
+    private void createTestSuiteElement(
+            XMLWriter ppw,
+            WrappedReportEntry report,
+            Map<String, Map<String, List<WrappedReportEntry>>> 
classMethodStatistics)
             throws IOException {
         ppw.startElement("testsuite");
 
@@ -441,13 +490,46 @@ private void createTestSuiteElement(XMLWriter ppw, 
WrappedReportEntry report, Te
             ppw.addAttribute("time", String.valueOf(report.getElapsed() / 
ONE_SECOND));
         }
 
-        ppw.addAttribute("tests", 
String.valueOf(testSetStats.getCompletedCount()));
-
-        ppw.addAttribute("errors", String.valueOf(testSetStats.getErrors()));
-
-        ppw.addAttribute("skipped", String.valueOf(testSetStats.getSkipped()));
+        // Count actual unique test methods and their final results from 
classMethodStatistics (accumulated across
+        // reruns)
+        int actualTestCount = 0;
+        int errors = 0;
+        int failures = 0;
+        int skipped = 0;
+        int flakes = 0;
+
+        for (Map<String, List<WrappedReportEntry>> methodStats : 
classMethodStatistics.values()) {
+            actualTestCount += methodStats.size();
+            for (Map.Entry<String, List<WrappedReportEntry>> methodEntry : 
methodStats.entrySet()) {
+                String methodName = methodEntry.getKey();
+                List<WrappedReportEntry> methodRuns = methodEntry.getValue();
+                TestResultType resultType = 
getTestResultTypeWithBeforeAllHandling(methodName, methodRuns, methodStats);
+
+                switch (resultType) {
+                    case ERROR:
+                        errors++;
+                        break;
+                    case FAILURE:
+                        failures++;
+                        break;
+                    case SKIPPED:
+                        skipped++;
+                        break;
+                    case FLAKE:
+                        flakes++;
+                        break;
+                    case SUCCESS:
+                    default:
+                        break;
+                }
+            }
+        }
 
-        ppw.addAttribute("failures", 
String.valueOf(testSetStats.getFailures()));
+        ppw.addAttribute("tests", String.valueOf(actualTestCount));
+        ppw.addAttribute("errors", String.valueOf(errors));
+        ppw.addAttribute("skipped", String.valueOf(skipped));
+        ppw.addAttribute("failures", String.valueOf(failures));
+        ppw.addAttribute("flakes", String.valueOf(flakes));
     }
 
     private static void getTestProblems(
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java
index 86c23c82a..12ad8db8c 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/report/DefaultReporterFactoryTest.java
@@ -68,6 +68,12 @@ public class DefaultReporterFactoryTest extends TestCase {
 
     private static final String ERROR = "error";
 
+    private static final String TEST_BEFORE_ALL_FLAKE = 
"com.example.FlakyClass";
+
+    private static final String TEST_BEFORE_ALL_ERROR = 
"com.example.AlwaysFailClass";
+
+    private static final String TEST_ERROR_SUFFIX = "-- Time elapsed: 292.2 s 
<<< ERROR!";
+
     public void testMergeTestHistoryResult() throws Exception {
         MessageUtils.setColorEnabled(false);
         File target = new File(System.getProperty("user.dir"), "target");
@@ -97,7 +103,7 @@ public void testMergeTestHistoryResult() throws Exception {
 
         DefaultReporterFactory factory = new 
DefaultReporterFactory(reportConfig, reporter);
 
-        // First run, four tests failed and one passed
+        // First run, four tests failed and one passed, plus @BeforeAll failure
         Queue<TestMethodStats> firstRunStats = new ArrayDeque<>();
         firstRunStats.add(new TestMethodStats(TEST_ONE, ReportEntryType.ERROR, 
new DummyStackTraceWriter(ERROR)));
         firstRunStats.add(new TestMethodStats(TEST_TWO, ReportEntryType.ERROR, 
new DummyStackTraceWriter(ERROR)));
@@ -106,6 +112,11 @@ public void testMergeTestHistoryResult() throws Exception {
         firstRunStats.add(
                 new TestMethodStats(TEST_FOUR, ReportEntryType.FAILURE, new 
DummyStackTraceWriter(ASSERTION_FAIL)));
         firstRunStats.add(new TestMethodStats(TEST_FIVE, 
ReportEntryType.SUCCESS, null));
+        // @BeforeAll failure for a test class that will eventually succeed
+        firstRunStats.add(new TestMethodStats(
+                TEST_BEFORE_ALL_FLAKE + ".null",
+                ReportEntryType.ERROR,
+                new DummyStackTraceWriter(TEST_BEFORE_ALL_FLAKE + ".null " + 
TEST_ERROR_SUFFIX)));
 
         // Second run, two tests passed
         Queue<TestMethodStats> secondRunStats = new ArrayDeque<>();
@@ -114,11 +125,23 @@ public void testMergeTestHistoryResult() throws Exception 
{
         secondRunStats.add(new TestMethodStats(TEST_TWO, 
ReportEntryType.SUCCESS, null));
         secondRunStats.add(new TestMethodStats(TEST_THREE, 
ReportEntryType.ERROR, new DummyStackTraceWriter(ERROR)));
         secondRunStats.add(new TestMethodStats(TEST_FOUR, 
ReportEntryType.SUCCESS, null));
+        // Successful test from the class that had @BeforeAll failure
+        secondRunStats.add(new TestMethodStats(TEST_BEFORE_ALL_FLAKE + 
".testSucceed", ReportEntryType.SUCCESS, null));
+        // @BeforeAll failure for a different class that will stay as error
+        secondRunStats.add(new TestMethodStats(
+                TEST_BEFORE_ALL_ERROR + ".null",
+                ReportEntryType.ERROR,
+                new DummyStackTraceWriter(TEST_BEFORE_ALL_ERROR + ".null " + 
TEST_ERROR_SUFFIX)));
 
         // Third run, another test passed
         Queue<TestMethodStats> thirdRunStats = new ArrayDeque<>();
         thirdRunStats.add(new TestMethodStats(TEST_ONE, 
ReportEntryType.SUCCESS, null));
         thirdRunStats.add(new TestMethodStats(TEST_THREE, 
ReportEntryType.ERROR, new DummyStackTraceWriter(ERROR)));
+        // Another @BeforeAll failure for the always-failing class
+        thirdRunStats.add(new TestMethodStats(
+                TEST_BEFORE_ALL_ERROR + ".null",
+                ReportEntryType.ERROR,
+                new DummyStackTraceWriter(TEST_BEFORE_ALL_ERROR + ".null")));
 
         TestSetRunListener firstRunListener = mock(TestSetRunListener.class);
         TestSetRunListener secondRunListener = mock(TestSetRunListener.class);
@@ -134,17 +157,21 @@ public void testMergeTestHistoryResult() throws Exception 
{
         invokeMethod(factory, "mergeTestHistoryResult");
         RunStatistics mergedStatistics = factory.getGlobalRunStatistics();
 
-        // Only TEST_THREE is a failing test, other three are flaky tests
-        assertEquals(5, mergedStatistics.getCompletedCount());
-        assertEquals(1, mergedStatistics.getErrors());
+        // TEST_THREE and AlwaysFailClass.null are failing tests, regular 
tests + FlakyClass.null are flaky
+        assertEquals(7, mergedStatistics.getCompletedCount());
+        assertEquals(2, mergedStatistics.getErrors());
         assertEquals(0, mergedStatistics.getFailures());
-        assertEquals(3, mergedStatistics.getFlakes());
+        assertEquals(4, mergedStatistics.getFlakes());
         assertEquals(0, mergedStatistics.getSkipped());
 
         // Now test the result will be printed out correctly
         factory.printTestFailures(TestResultType.FLAKE);
         String[] expectedFlakeOutput = {
             "Flakes: ",
+            TEST_BEFORE_ALL_FLAKE + ".<beforeAll>",
+            "  Run 1: " + TEST_BEFORE_ALL_FLAKE + ".null " + TEST_ERROR_SUFFIX,
+            "  Run 2: PASS",
+            "",
             TEST_FOUR,
             "  Run 1: " + ASSERTION_FAIL,
             "  Run 2: PASS",
@@ -164,7 +191,16 @@ public void testMergeTestHistoryResult() throws Exception {
         reporter.reset();
         factory.printTestFailures(TestResultType.ERROR);
         String[] expectedFailureOutput = {
-            "Errors: ", TEST_THREE, "  Run 1: " + ASSERTION_FAIL, "  Run 2: " 
+ ERROR, "  Run 3: " + ERROR, ""
+            "Errors: ",
+            TEST_BEFORE_ALL_ERROR + ".<beforeAll>",
+            "  Run 1: " + TEST_BEFORE_ALL_ERROR + ".null " + TEST_ERROR_SUFFIX,
+            "  Run 2: " + TEST_BEFORE_ALL_ERROR + ".null",
+            "",
+            TEST_THREE,
+            "  Run 1: " + ASSERTION_FAIL,
+            "  Run 2: " + ERROR,
+            "  Run 3: " + ERROR,
+            ""
         };
         assertEquals(asList(expectedFailureOutput), reporter.getMessages());
 
diff --git 
a/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformFailingBeforeAllRerunIT.java
 
b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformFailingBeforeAllRerunIT.java
new file mode 100644
index 000000000..c2a049f3f
--- /dev/null
+++ 
b/surefire-its/src/test/java/org/apache/maven/surefire/its/JUnitPlatformFailingBeforeAllRerunIT.java
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+package org.apache.maven.surefire.its;
+
+import org.apache.maven.surefire.its.fixture.OutputValidator;
+import org.apache.maven.surefire.its.fixture.SurefireJUnit4IntegrationTestCase;
+import org.junit.Test;
+
+/**
+ * Integration tests for JUnit Platform @BeforeAll failures with rerun 
functionality.
+ * Tests various scenarios where @BeforeAll lifecycle methods fail and are 
rerun.
+ */
+public class JUnitPlatformFailingBeforeAllRerunIT extends 
SurefireJUnit4IntegrationTestCase {
+    private static final String VERSION = "5.9.1";
+
+    private static final String TEST_PROJECT_BASE = 
"junit-platform-rerun-failing-before-all";
+
+    @Test
+    public void testBeforeAllFailures() {
+        // Test that @BeforeAll failures are properly handled when they 
succeed on rerun
+        OutputValidator outputValidator = unpack(TEST_PROJECT_BASE)
+                .setJUnitVersion(VERSION)
+                .maven()
+                .debugLogging()
+                .addGoal("-Dsurefire.rerunFailingTestsCount=3")
+                .withFailure()
+                .executeTest()
+                .assertTestSuiteResults(7, 1, 0, 0, 4);
+
+        // Verify the @BeforeAll is reported as a flake with proper formatting
+        
outputValidator.verifyTextInLog("junitplatform.FlakyFirstTimeTest.<beforeAll>");
+        outputValidator.verifyTextInLog("Run 1: FlakyFirstTimeTest.setup:53 
IllegalArgument");
+        outputValidator.verifyTextInLog("Run 2: PASS");
+
+        // Verify XML report doesn't contain error testcase with empty name
+        outputValidator
+                
.getSurefireReportsXmlFile("TEST-junitplatform.FlakyFirstTimeTest.xml")
+                .assertContainsText("tests=\"4\" errors=\"0\"")
+                .assertContainsText("name=\"testFailingTestOne\"")
+                .assertContainsText("name=\"testErrorTestOne\"")
+                .assertContainsText("name=\"testPassingTest\"");
+
+        // Verify @BeforeAll is reported as error
+        outputValidator.verifyTextInLog("Errors:");
+        
outputValidator.verifyTextInLog("junitplatform.AlwaysFailingTest.<beforeAll>");
+        outputValidator.verifyTextInLog("Run 3: AlwaysFailingTest.setup:15 
IllegalArgument BeforeAll always fails");
+    }
+}
diff --git 
a/surefire-its/src/test/resources/junit-platform-rerun-failing-before-all/pom.xml
 
b/surefire-its/src/test/resources/junit-platform-rerun-failing-before-all/pom.xml
new file mode 100644
index 000000000..702d04f4f
--- /dev/null
+++ 
b/surefire-its/src/test/resources/junit-platform-rerun-failing-before-all/pom.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.maven.plugins.surefire</groupId>
+    <artifactId>junit-platform-rerun-failing-before-all</artifactId>
+    <version>1.0-SNAPSHOT</version>
+    <name>Test for rerun failing tests with BeforeAll in JUnit 
5/Platform</name>
+
+    <properties>
+        <maven.compiler.source>1.8</maven.compiler.source>
+        <maven.compiler.target>1.8</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-api</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-params</artifactId>
+            <version>${junit.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>${surefire.version}</version>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>**/ParametersTest.java</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <profiles>
+        <profile>
+            <id>parameters</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-surefire-plugin</artifactId>
+                        <configuration>
+                            <excludes>
+                                <exclude>**/PassingTest.java</exclude>
+                                <exclude>**/FlakyFirstTimeTest.java</exclude>
+                            </excludes>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+</project>
diff --git 
a/surefire-its/src/test/resources/junit-platform-rerun-failing-before-all/src/test/java/junitplatform/AlwaysFailingTest.java
 
b/surefire-its/src/test/resources/junit-platform-rerun-failing-before-all/src/test/java/junitplatform/AlwaysFailingTest.java
new file mode 100644
index 000000000..511026a39
--- /dev/null
+++ 
b/surefire-its/src/test/resources/junit-platform-rerun-failing-before-all/src/test/java/junitplatform/AlwaysFailingTest.java
@@ -0,0 +1,22 @@
+package junitplatform;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test class with @BeforeAll that always fails.
+ * Used to test scenario where @BeforeAll never succeeds even with reruns.
+ */
+public class AlwaysFailingTest {
+
+    @BeforeAll
+    static void setup() {
+        System.out.println("Error beforeAll in AlwaysFailingTest");
+        throw new IllegalArgumentException("BeforeAll always fails");
+    }
+
+    @Test
+    public void testThatNeverRuns() {
+        System.out.println("This test never runs because beforeAll always 
fails");
+    }
+}
\ No newline at end of file
diff --git 
a/surefire-its/src/test/resources/junit-platform-rerun-failing-before-all/src/test/java/junitplatform/FlakyFirstTimeTest.java
 
b/surefire-its/src/test/resources/junit-platform-rerun-failing-before-all/src/test/java/junitplatform/FlakyFirstTimeTest.java
new file mode 100644
index 000000000..fe2e48e79
--- /dev/null
+++ 
b/surefire-its/src/test/resources/junit-platform-rerun-failing-before-all/src/test/java/junitplatform/FlakyFirstTimeTest.java
@@ -0,0 +1,57 @@
+package junitplatform;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+
+public class FlakyFirstTimeTest
+{
+    private static int failingCount = 0;
+
+    private static int errorCount = 0;
+    private static int errorBeforeAllCount = 0;
+
+
+    @Test
+    public void testFailingTestOne()
+    {
+        System.out.println( "Failing test" );
+        // This test will fail with only one retry, but will pass with two
+        if ( failingCount < 1 )
+        {
+            failingCount++;
+            fail( "Failing test" );
+        }
+    }
+
+    @Test
+    public void testErrorTestOne() throws Exception
+    {
+        System.out.println( "Error test" );
+        // This test will error out with only one retry, but will pass with two
+        if ( errorCount < 2 )
+        {
+            errorCount++;
+            throw new IllegalArgumentException( "..." );
+        }
+    }
+
+    @Test
+    public void testPassingTest() throws Exception
+    {
+        System.out.println( "Passing test" );
+    }
+
+    @BeforeAll
+    static void setup() {
+        if ( errorBeforeAllCount < 1 )
+        {
+            System.out.println( "Error beforeAll" );
+            errorBeforeAllCount++;
+            throw new IllegalArgumentException( "..." );
+        }
+        System.out.println( "Passing beforeAll" );
+    }
+}
diff --git 
a/surefire-its/src/test/resources/junit-platform-rerun-failing-before-all/src/test/java/junitplatform/PassingTest.java
 
b/surefire-its/src/test/resources/junit-platform-rerun-failing-before-all/src/test/java/junitplatform/PassingTest.java
new file mode 100644
index 000000000..cc48ef4fb
--- /dev/null
+++ 
b/surefire-its/src/test/resources/junit-platform-rerun-failing-before-all/src/test/java/junitplatform/PassingTest.java
@@ -0,0 +1,19 @@
+package junitplatform;
+
+import org.junit.jupiter.api.Test;
+
+
+public class PassingTest
+{
+    @Test
+    public void testPassingTestOne()
+    {
+        System.out.println( "Passing test one" );
+    }
+
+    @Test
+    public void testPassingTestTwo() throws Exception
+    {
+        System.out.println( "Passing test two" );
+    }
+}


Reply via email to