@@ -161,7 +161,7 @@ public void testSetCompleted(WrappedReportEntry testSetReportEntry, TestSetStats
161161 OutputStreamWriter fw = getWriter (outputStream )) {
162162 XMLWriter ppw = new PrettyPrintXMLWriter (new PrintWriter (fw ), XML_INDENT , XML_NL , UTF_8 .name (), null );
163163
164- createTestSuiteElement (ppw , testSetReportEntry , testSetStats ); // TestSuite
164+ createTestSuiteElement (ppw , testSetReportEntry , classMethodStatistics ); // TestSuite
165165
166166 if (enablePropertiesElement ) {
167167 showProperties (ppw , testSetReportEntry .getSystemProperties ());
@@ -186,9 +186,9 @@ public void testSetCompleted(WrappedReportEntry testSetReportEntry, TestSetStats
186186 }
187187
188188 for (Entry <String , Map <String , List <WrappedReportEntry >>> statistics : classMethodStatistics .entrySet ()) {
189- for ( Entry <String , List <WrappedReportEntry >> thisMethodRuns :
190- statistics . getValue () .entrySet ()) {
191- serializeTestClass (outputStream , fw , ppw , thisMethodRuns .getValue ());
189+ Map <String , List <WrappedReportEntry >> methodStatistics = statistics . getValue ();
190+ for ( Entry < String , List < WrappedReportEntry >> thisMethodRuns : methodStatistics .entrySet ()) {
191+ serializeTestClass (outputStream , fw , ppw , thisMethodRuns .getValue (), methodStatistics );
192192 }
193193 }
194194
@@ -224,10 +224,14 @@ private Deque<WrappedReportEntry> aggregateCacheFromMultipleReruns(
224224 }
225225
226226 private void serializeTestClass (
227- OutputStream outputStream , OutputStreamWriter fw , XMLWriter ppw , List <WrappedReportEntry > methodEntries )
227+ OutputStream outputStream ,
228+ OutputStreamWriter fw ,
229+ XMLWriter ppw ,
230+ List <WrappedReportEntry > methodEntries ,
231+ Map <String , List <WrappedReportEntry >> methodStatistics )
228232 throws IOException {
229233 if (rerunFailingTestsCount > 0 ) {
230- serializeTestClassWithRerun (outputStream , fw , ppw , methodEntries );
234+ serializeTestClassWithRerun (outputStream , fw , ppw , methodEntries , methodStatistics );
231235 } else {
232236 // rerunFailingTestsCount is smaller than 1, but for some reasons a test could be run
233237 // for more than once
@@ -258,10 +262,18 @@ private void serializeTestClassWithoutRerun(
258262 }
259263
260264 private void serializeTestClassWithRerun (
261- OutputStream outputStream , OutputStreamWriter fw , XMLWriter ppw , List <WrappedReportEntry > methodEntries )
265+ OutputStream outputStream ,
266+ OutputStreamWriter fw ,
267+ XMLWriter ppw ,
268+ List <WrappedReportEntry > methodEntries ,
269+ Map <String , List <WrappedReportEntry >> methodStatistics )
262270 throws IOException {
263271 WrappedReportEntry firstMethodEntry = methodEntries .get (0 );
264- switch (getTestResultType (methodEntries )) {
272+
273+ TestResultType resultType =
274+ getTestResultTypeWithBeforeAllHandling (firstMethodEntry .getName (), methodEntries , methodStatistics );
275+
276+ switch (resultType ) {
265277 case SUCCESS :
266278 for (WrappedReportEntry methodEntry : methodEntries ) {
267279 if (methodEntry .getReportEntryType () == SUCCESS ) {
@@ -370,6 +382,40 @@ private TestResultType getTestResultType(List<WrappedReportEntry> methodEntryLis
370382 return DefaultReporterFactory .getTestResultType (testResultTypeList , rerunFailingTestsCount );
371383 }
372384
385+ /**
386+ * Determines the final result type for a test method, applying special handling for @BeforeAll failures.
387+ * If a @BeforeAll fails but any actual test methods succeed, it's classified as a FLAKE.
388+ *
389+ * @param methodName the name of the test method (null or "null" for @BeforeAll)
390+ * @param methodRuns the list of runs for this method
391+ * @param methodStatistics all method statistics for the test class
392+ * @return the final TestResultType
393+ */
394+ private TestResultType getTestResultTypeWithBeforeAllHandling (
395+ String methodName ,
396+ List <WrappedReportEntry > methodRuns ,
397+ Map <String , List <WrappedReportEntry >> methodStatistics ) {
398+ TestResultType resultType = getTestResultType (methodRuns );
399+
400+ // Special handling for @BeforeAll failures (null method name or method name is "null")
401+ // If @BeforeAll failed but any actual test methods succeeded, treat it as a flake
402+ if ((methodName == null || methodName .equals ("null" ))
403+ && (resultType == TestResultType .ERROR || resultType == TestResultType .FAILURE )) {
404+ // Check if any actual test methods (non-null and not "null" names) succeeded
405+ boolean hasSuccessfulTestMethods = methodStatistics .entrySet ().stream ()
406+ .filter (entry ->
407+ entry .getKey () != null && !entry .getKey ().equals ("null" )) // Only actual test methods
408+ .anyMatch (entry -> entry .getValue ().stream ()
409+ .anyMatch (reportEntry -> reportEntry .getReportEntryType () == SUCCESS ));
410+
411+ if (hasSuccessfulTestMethods ) {
412+ resultType = TestResultType .FLAKE ;
413+ }
414+ }
415+
416+ return resultType ;
417+ }
418+
373419 private Deque <WrappedReportEntry > getAddMethodRunHistoryMap (String testClassName ) {
374420 Deque <WrappedReportEntry > methodRunHistory = testClassMethodRunHistoryMap .get (testClassName );
375421 if (methodRunHistory == null ) {
@@ -420,7 +466,10 @@ private void startTestElement(XMLWriter ppw, WrappedReportEntry report) throws I
420466 }
421467 }
422468
423- private void createTestSuiteElement (XMLWriter ppw , WrappedReportEntry report , TestSetStats testSetStats )
469+ private void createTestSuiteElement (
470+ XMLWriter ppw ,
471+ WrappedReportEntry report ,
472+ Map <String , Map <String , List <WrappedReportEntry >>> classMethodStatistics )
424473 throws IOException {
425474 ppw .startElement ("testsuite" );
426475
@@ -441,13 +490,46 @@ private void createTestSuiteElement(XMLWriter ppw, WrappedReportEntry report, Te
441490 ppw .addAttribute ("time" , String .valueOf (report .getElapsed () / ONE_SECOND ));
442491 }
443492
444- ppw .addAttribute ("tests" , String .valueOf (testSetStats .getCompletedCount ()));
445-
446- ppw .addAttribute ("errors" , String .valueOf (testSetStats .getErrors ()));
447-
448- ppw .addAttribute ("skipped" , String .valueOf (testSetStats .getSkipped ()));
493+ // Count actual unique test methods and their final results from classMethodStatistics (accumulated across
494+ // reruns)
495+ int actualTestCount = 0 ;
496+ int errors = 0 ;
497+ int failures = 0 ;
498+ int skipped = 0 ;
499+ int flakes = 0 ;
500+
501+ for (Map <String , List <WrappedReportEntry >> methodStats : classMethodStatistics .values ()) {
502+ actualTestCount += methodStats .size ();
503+ for (Map .Entry <String , List <WrappedReportEntry >> methodEntry : methodStats .entrySet ()) {
504+ String methodName = methodEntry .getKey ();
505+ List <WrappedReportEntry > methodRuns = methodEntry .getValue ();
506+ TestResultType resultType = getTestResultTypeWithBeforeAllHandling (methodName , methodRuns , methodStats );
507+
508+ switch (resultType ) {
509+ case ERROR :
510+ errors ++;
511+ break ;
512+ case FAILURE :
513+ failures ++;
514+ break ;
515+ case SKIPPED :
516+ skipped ++;
517+ break ;
518+ case FLAKE :
519+ flakes ++;
520+ break ;
521+ case SUCCESS :
522+ default :
523+ break ;
524+ }
525+ }
526+ }
449527
450- ppw .addAttribute ("failures" , String .valueOf (testSetStats .getFailures ()));
528+ ppw .addAttribute ("tests" , String .valueOf (actualTestCount ));
529+ ppw .addAttribute ("errors" , String .valueOf (errors ));
530+ ppw .addAttribute ("skipped" , String .valueOf (skipped ));
531+ ppw .addAttribute ("failures" , String .valueOf (failures ));
532+ ppw .addAttribute ("flakes" , String .valueOf (flakes ));
451533 }
452534
453535 private static void getTestProblems (
0 commit comments