9
9
import re
10
10
import time
11
11
import warnings
12
+
12
13
from base64 import b64decode
13
14
from base64 import b64encode
15
+ from collections import defaultdict
14
16
from collections import OrderedDict
15
17
from functools import lru_cache
16
18
from html import escape
@@ -148,6 +150,7 @@ def __init__(self, logfile, config):
148
150
self .rerun = 0 if has_rerun else None
149
151
self .self_contained = config .getoption ("self_contained_html" )
150
152
self .config = config
153
+ self .reports = defaultdict (list )
151
154
152
155
class TestResult :
153
156
def __init__ (self , outcome , report , logfile , config ):
@@ -279,7 +282,12 @@ def append_extra_html(self, extra, extra_index, test_index):
279
282
def append_log_html (self , report , additional_html ):
280
283
log = html .div (class_ = "log" )
281
284
if report .longrepr :
282
- for line in report .longreprtext .splitlines ():
285
+ # longreprtext is only filled out on failure by pytest
286
+ # otherwise will be None.
287
+ # Use full_text if longreprtext is None-ish
288
+ # we added full_text elsewhere in this file.
289
+ text = report .longreprtext or report .full_text
290
+ for line in text .splitlines ():
283
291
separator = line .startswith ("_ " * 10 )
284
292
if separator :
285
293
log .append (line [:80 ])
@@ -620,15 +628,66 @@ def _save_report(self, report_content):
620
628
with open (style_path , "w" , encoding = "utf-8" ) as f :
621
629
f .write (self .style_css )
622
630
631
+ def _post_process_reports (self ):
632
+ for test_name , test_reports in self .reports .items ():
633
+ outcome = "passed"
634
+ wasxfail = False
635
+ failure_when = None
636
+ full_text = ""
637
+ extras = []
638
+ duration = 0.0
639
+
640
+ # in theory the last one should have all logs so we just go
641
+ # through them all to figure out the outcome, xfail, duration,
642
+ # extras, and when it swapped from pass
643
+ for test_report in test_reports :
644
+ full_text += test_report .longreprtext
645
+ extras .extend (getattr (test_report , "extra" , []))
646
+ duration += getattr (test_report , "duration" , 0.0 )
647
+
648
+ if (
649
+ test_report .outcome not in ("passed" , "rerun" )
650
+ and outcome == "passed"
651
+ ):
652
+ outcome = test_report .outcome
653
+ failure_when = test_report .when
654
+
655
+ if hasattr (test_report , "wasxfail" ):
656
+ wasxfail = True
657
+
658
+ if test_report .outcome == "rerun" :
659
+ self .append_other (test_report )
660
+
661
+ # the following test_report.<X> = settings come at the end of us
662
+ # looping through all test_reports that make up a single
663
+ # case.
664
+
665
+ # outcome on the right comes from the outcome of the various
666
+ # test_reports that make up this test case
667
+ # we are just carrying it over to the final report.
668
+ test_report .outcome = outcome
669
+ test_report .when = "call"
670
+ test_report .nodeid = test_name
671
+ test_report .longrepr = full_text
672
+ test_report .extra = extras
673
+ test_report .duration = duration
674
+
675
+ if wasxfail :
676
+ test_report .wasxfail = True
677
+
678
+ if test_report .outcome == "passed" :
679
+ self .append_passed (test_report )
680
+ elif test_report .outcome == "skipped" :
681
+ self .append_skipped (test_report )
682
+ elif test_report .outcome == "failed" :
683
+ test_report .when = failure_when
684
+ self .append_failed (test_report )
685
+
686
+ # we don't append other here since the only case supported
687
+ # for append_other is rerun, which is handled in the loop above
688
+
623
689
def pytest_runtest_logreport (self , report ):
624
- if report .passed :
625
- self .append_passed (report )
626
- elif report .failed :
627
- self .append_failed (report )
628
- elif report .skipped :
629
- self .append_skipped (report )
630
- else :
631
- self .append_other (report )
690
+ self .reports [report .nodeid ].append (report )
632
691
633
692
def pytest_collectreport (self , report ):
634
693
if report .failed :
@@ -638,6 +697,7 @@ def pytest_sessionstart(self, session):
638
697
self .suite_start_time = time .time ()
639
698
640
699
def pytest_sessionfinish (self , session ):
700
+ self ._post_process_reports ()
641
701
report_content = self ._generate_report (session )
642
702
self ._save_report (report_content )
643
703
0 commit comments