@@ -565,6 +565,10 @@ func (r *Runner) parseTestResults(filePaths []string) ([]reports.TestResult, err
565565 log .Warn ().Str ("parent test" , parentTestKey ).Msg ("expected parent test not found" )
566566 }
567567 }
568+
569+ // Zero out parent test's failures if they are purely caused by subtest fails
570+ zeroOutParentFailsIfSubtestOnlyFails (testDetails , testsWithSubTests )
571+
568572 for _ , result := range testDetails {
569573 if result .Runs > expectedRuns { // Panics can introduce double-counting test failures, this is a correction for it
570574 if result .Panic {
@@ -595,6 +599,87 @@ func (r *Runner) parseTestResults(filePaths []string) ([]reports.TestResult, err
595599 return results , nil
596600}
597601
602+ // zeroOutParentFailsIfSubtestOnlyFails scans through parent tests in `testDetails`.
603+ // If a parent test's failures only reference subtests (no genuine parent-level lines),
604+ // we zero out the parent's failures, so the parent is considered passed.
605+ func zeroOutParentFailsIfSubtestOnlyFails (
606+ testDetails map [string ]* reports.TestResult ,
607+ testsWithSubTests map [string ][]string ,
608+ ) {
609+ for parentTestKey , subTests := range testsWithSubTests {
610+ parentResult , ok := testDetails [parentTestKey ]
611+ if ! ok {
612+ continue
613+ }
614+ // If the parent has zero failures, no need to adjust.
615+ if parentResult .Failures == 0 {
616+ continue
617+ }
618+
619+ parentHasOwnFailure := false
620+
621+ // For each run's fail lines
622+ for _ , failLines := range parentResult .FailedOutputs {
623+ for _ , line := range failLines {
624+
625+ // 1) If line doesn't even mention parent test name (e.g. "example_tests_test.go:315: This sub-subtest fails"),
626+ // skip it. It's presumably sub-subtest detail.
627+ if ! strings .Contains (line , parentResult .TestName ) {
628+ continue
629+ }
630+
631+ // 2) If it's the summary line, e.g. "--- FAIL: TestNestedSubtests (0.00s)",
632+ // skip it. It's not a genuine parent-level fail for your purposes.
633+ if strings .HasPrefix (line , "--- FAIL: " + parentResult .TestName + " (" ) {
634+ continue
635+ }
636+
637+ // 3) Check if it references "TestParent/SubtestName"
638+ // e.g. "TestNestedSubtests/Level1"
639+ isSubtestLine := false
640+ for _ , subName := range subTests {
641+ expected := parentResult .TestName + "/" + subName
642+ if strings .Contains (line , expected ) {
643+ isSubtestLine = true
644+ break
645+ }
646+ }
647+
648+ // If we *still* can’t identify it as purely subtest-based => it's a genuine parent-level fail.
649+ if ! isSubtestLine {
650+ parentHasOwnFailure = true
651+ break
652+ }
653+ }
654+ if parentHasOwnFailure {
655+ break
656+ }
657+ }
658+
659+ // If the parent has no genuine failure lines, zero out its fail
660+ if ! parentHasOwnFailure {
661+ parentResult .Runs -= parentResult .Failures
662+ parentResult .Failures = 0
663+
664+ // Adjust successes if needed
665+ if parentResult .Runs < parentResult .Successes {
666+ parentResult .Successes = parentResult .Runs
667+ }
668+
669+ // Recompute pass ratio
670+ if parentResult .Runs > 0 {
671+ parentResult .PassRatio = float64 (parentResult .Successes ) / float64 (parentResult .Runs )
672+ } else {
673+ // If no runs remain, we consider it "passed"
674+ parentResult .PassRatio = 1
675+ }
676+
677+ // Clear the parent's failed outputs
678+ parentResult .FailedOutputs = make (map [string ][]string )
679+ }
680+ }
681+ }
682+
598683// attributePanicToTest properly attributes panics to the test that caused them.
599684func attributePanicToTest (panicPackage string , panicEntries []entry ) (test string , timeout bool , err error ) {
600685 regexSanitizePanicPackage := filepath .Base (panicPackage )
0 commit comments