Skip to content

Commit 2bb2010

Browse files
authored
Stop attaching test reruns to final test report entries (#387)
* treat rerun entries as seperate test runs * Add changelog entry * fix a typo * remove debug code * fix flaky test on mac
1 parent 3f63686 commit 2bb2010

File tree

3 files changed

+87
-52
lines changed

3 files changed

+87
-52
lines changed

CHANGES.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ Release Notes
33

44
**3.1.0 (unreleased)**
55

6+
* Stop attaching test reruns to final test report entries (`#374 <https://github.com/pytest-dev/pytest-html/issues/374>`_)
7+
8+
* Thanks to `@VladimirPodolyan <https://github.com/VladimirPodolyan>`_ for reporting and `@gnikonorov <https://github.com/gnikonorov>`_ for the fix
9+
610
* Allow for report duration formatting (`#376 <https://github.com/pytest-dev/pytest-html/issues/376>`_)
711

812
* Thanks to `@brettnolan <https://github.com/brettnolan>`_ for reporting and `@gnikonorov <https://github.com/gnikonorov>`_ for the fix

src/pytest_html/plugin.py

Lines changed: 51 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ def __init__(self, outcome, report, logfile, config):
157157
if getattr(report, "when", "call") != "call":
158158
self.test_id = "::".join([report.nodeid, report.when])
159159
self.time = getattr(report, "duration", 0.0)
160-
self.formatted_time = getattr(report, "formatted_duration", 0.0)
160+
self.formatted_time = self._format_time(report)
161161
self.outcome = outcome
162162
self.additional_html = []
163163
self.links_html = []
@@ -283,6 +283,37 @@ def append_extra_html(self, extra, extra_index, test_index):
283283
)
284284
self.links_html.append(" ")
285285

286+
def _format_time(self, report):
287+
# parse the report duration into its display version and return
288+
# it to the caller
289+
duration = getattr(report, "duration", None)
290+
if duration is None:
291+
return ""
292+
293+
duration_formatter = getattr(report, "duration_formatter", None)
294+
string_duration = str(duration)
295+
if duration_formatter is None:
296+
if "." in string_duration:
297+
split_duration = string_duration.split(".")
298+
split_duration[1] = split_duration[1][0:2]
299+
300+
string_duration = ".".join(split_duration)
301+
302+
return string_duration
303+
else:
304+
# support %f, since time.strftime doesn't support it out of the box
305+
# keep a precision of 2 for legacy reasons
306+
formatted_milliseconds = "00"
307+
if "." in string_duration:
308+
milliseconds = string_duration.split(".")[1]
309+
formatted_milliseconds = milliseconds[0:2]
310+
311+
duration_formatter = duration_formatter.replace(
312+
"%f", formatted_milliseconds
313+
)
314+
duration_as_gmtime = time.gmtime(report.duration)
315+
return time.strftime(duration_formatter, duration_as_gmtime)
316+
286317
def _populate_html_log_div(self, log, report):
287318
if report.longrepr:
288319
# longreprtext is only filled out on failure by pytest
@@ -425,6 +456,10 @@ def append_failed(self, report):
425456
self.errors += 1
426457
self._appendrow("Error", report)
427458

459+
def append_rerun(self, report):
460+
self.rerun += 1
461+
self._appendrow("Rerun", report)
462+
428463
def append_skipped(self, report):
429464
if hasattr(report, "wasxfail"):
430465
self.xfailed += 1
@@ -433,11 +468,6 @@ def append_skipped(self, report):
433468
self.skipped += 1
434469
self._appendrow("Skipped", report)
435470

436-
def append_other(self, report):
437-
# For now, the only "other" the plugin give support is rerun
438-
self.rerun += 1
439-
self._appendrow("Rerun", report)
440-
441471
def _generate_report(self, session):
442472
suite_stop_time = time.time()
443473
suite_time_delta = suite_stop_time - self.suite_start_time
@@ -604,32 +634,6 @@ def generate_summary_item(self):
604634
unicode_doc = unicode_doc.encode("utf-8", errors="xmlcharrefreplace")
605635
return unicode_doc.decode("utf-8")
606636

607-
def _format_duration(self, report):
608-
# parse the report duration into its display version and return it to the caller
609-
duration_formatter = getattr(report, "duration_formatter", None)
610-
string_duration = str(report.duration)
611-
if duration_formatter is None:
612-
if "." in string_duration:
613-
split_duration = string_duration.split(".")
614-
split_duration[1] = split_duration[1][0:2]
615-
616-
string_duration = ".".join(split_duration)
617-
618-
return string_duration
619-
else:
620-
# support %f, since time.strftime doesn't support it out of the box
621-
# keep a precision of 2 for legacy reasons
622-
formatted_milliseconds = "00"
623-
if "." in string_duration:
624-
milliseconds = string_duration.split(".")[1]
625-
formatted_milliseconds = milliseconds[0:2]
626-
627-
duration_formatter = duration_formatter.replace(
628-
"%f", formatted_milliseconds
629-
)
630-
duration_as_gmtime = time.gmtime(report.duration)
631-
return time.strftime(duration_formatter, duration_as_gmtime)
632-
633637
def _generate_environment(self, config):
634638
if not hasattr(config, "_metadata") or config._metadata is None:
635639
return []
@@ -685,22 +689,23 @@ def _post_process_reports(self):
685689
# through them all to figure out the outcome, xfail, duration,
686690
# extras, and when it swapped from pass
687691
for test_report in test_reports:
688-
full_text += test_report.longreprtext
689-
extras.extend(getattr(test_report, "extra", []))
690-
duration += getattr(test_report, "duration", 0.0)
692+
if test_report.outcome == "rerun":
693+
# reruns are separate test runs for all intensive purposes
694+
self.append_rerun(test_report)
695+
else:
696+
full_text += test_report.longreprtext
697+
extras.extend(getattr(test_report, "extra", []))
698+
duration += getattr(test_report, "duration", 0.0)
691699

692-
if (
693-
test_report.outcome not in ("passed", "rerun")
694-
and outcome == "passed"
695-
):
696-
outcome = test_report.outcome
697-
failure_when = test_report.when
700+
if (
701+
test_report.outcome not in ("passed", "rerun")
702+
and outcome == "passed"
703+
):
704+
outcome = test_report.outcome
705+
failure_when = test_report.when
698706

699-
if hasattr(test_report, "wasxfail"):
700-
wasxfail = True
701-
702-
if test_report.outcome == "rerun":
703-
self.append_other(test_report)
707+
if hasattr(test_report, "wasxfail"):
708+
wasxfail = True
704709

705710
# the following test_report.<X> = settings come at the end of us
706711
# looping through all test_reports that make up a single
@@ -715,7 +720,6 @@ def _post_process_reports(self):
715720
test_report.longrepr = full_text
716721
test_report.extra = extras
717722
test_report.duration = duration
718-
test_report.formatted_duration = self._format_duration(test_report)
719723

720724
if wasxfail:
721725
test_report.wasxfail = True
@@ -728,9 +732,6 @@ def _post_process_reports(self):
728732
test_report.when = failure_when
729733
self.append_failed(test_report)
730734

731-
# we don't append other here since the only case supported
732-
# for append_other is rerun, which is handled in the loop above
733-
734735
def pytest_runtest_logreport(self, report):
735736
self.reports[report.nodeid].append(report)
736737

testing/test_pytest_html.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,17 +187,47 @@ def test_fail(self, testdir):
187187
assert "AssertionError" in html
188188

189189
def test_rerun(self, testdir):
190+
testdir.makeconftest(
191+
"""
192+
import pytest
193+
194+
@pytest.hookimpl(hookwrapper=True)
195+
def pytest_runtest_makereport(item, call):
196+
pytest_html = item.config.pluginmanager.getplugin("html")
197+
outcome = yield
198+
report = outcome.get_result()
199+
200+
extra = getattr(report, "extra", [])
201+
if report.when == "call":
202+
extra.append(pytest_html.extras.url("http://www.example.com/"))
203+
report.extra = extra
204+
"""
205+
)
206+
190207
testdir.makepyfile(
191208
"""
192209
import pytest
193-
@pytest.mark.flaky(reruns=5)
210+
import time
211+
212+
@pytest.mark.flaky(reruns=2)
194213
def test_example():
214+
time.sleep(1)
195215
assert False
196216
"""
197217
)
218+
198219
result, html = run(testdir)
199220
assert result.ret
200-
assert_results(html, passed=0, failed=1, rerun=5)
221+
assert_results(html, passed=0, failed=1, rerun=2)
222+
223+
expected_report_durations = r'<td class="col-duration">1.\d{2}</td>'
224+
assert len(re.findall(expected_report_durations, html)) == 3
225+
226+
expected_report_extras = (
227+
r'<td class="col-links"><a class="url" href="http://www.example.com/" '
228+
'target="_blank">URL</a> </td>'
229+
)
230+
assert len(re.findall(expected_report_extras, html)) == 3
201231

202232
def test_no_rerun(self, testdir):
203233
testdir.makepyfile("def test_pass(): pass")

0 commit comments

Comments
 (0)