Skip to content

fix: Broken duration #753

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 43 additions & 11 deletions src/pytest_html/basereport.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import os
import re
import warnings
from collections import defaultdict
from pathlib import Path

import pytest
Expand All @@ -29,6 +30,7 @@ def __init__(self, report_path, config, report_data, template, css):
config.getini("max_asset_filename_length")
)

self._reports = defaultdict(dict)
self._report = report_data
self._report.title = self._report_path.name

Expand Down Expand Up @@ -204,15 +206,50 @@ def pytest_runtest_logreport(self, report):
DeprecationWarning,
)

# "reruns" makes this code a mess.
# We store each combination of when and outcome
# exactly once, unless that outcome is a "rerun"
# then we store all of them.
key = (report.when, report.outcome)
if report.outcome == "rerun":
if key not in self._reports[report.nodeid]:
self._reports[report.nodeid][key] = list()
self._reports[report.nodeid][key].append(report)
else:
self._reports[report.nodeid][key] = [report]

self._report.total_duration += report.duration

finished = report.when == "teardown" and report.outcome != "rerun"
if not finished:
return

# Calculate total duration for a single test.
# This is needed to add the "teardown" duration
# to tests total duration.
test_duration = 0
for key, reports in self._reports[report.nodeid].items():
_, outcome = key
if outcome != "rerun":
test_duration += reports[0].duration

for key, reports in self._reports[report.nodeid].items():
when, _ = key
for each in reports:
dur = test_duration if when == "call" else each.duration
self._process_report(each, dur)

self._generate_report()

def _process_report(self, report, duration):
outcome = _process_outcome(report)
try:
# hook returns as list for some reason
duration = self._config.hook.pytest_html_duration_format(
duration=report.duration
formatted_duration = self._config.hook.pytest_html_duration_format(
duration=duration
)[0]
except IndexError:
duration = _format_duration(report.duration)
self._report.total_duration += report.duration
formatted_duration = _format_duration(duration)

test_id = report.nodeid
if report.when != "call":
Expand All @@ -229,7 +266,7 @@ def pytest_runtest_logreport(self, report):
cells = [
f'<td class="col-result">{outcome}</td>',
f'<td class="col-testId">{test_id}</td>',
f'<td class="col-duration">{duration}</td>',
f'<td class="col-duration">{formatted_duration}</td>',
f'<td class="col-links">{_process_links(links)}</td>',
]
self._config.hook.pytest_html_results_table_row(report=report, cells=cells)
Expand All @@ -240,17 +277,12 @@ def pytest_runtest_logreport(self, report):
self._hydrate_data(data, cells)
data["resultsTableRow"] = cells

# don't count passed setups and teardowns
if not (report.when in ["setup", "teardown"] and report.outcome == "passed"):
self._report.outcomes = outcome

processed_logs = _process_logs(report)
self._config.hook.pytest_html_results_table_html(
report=report, data=processed_logs
)

if self._report.add_test(data, report, processed_logs):
self._generate_report()
self._report.add_test(data, report, outcome, processed_logs)


def _format_duration(duration):
Expand Down
6 changes: 2 additions & 4 deletions src/pytest_html/report_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def total_duration(self, duration):
def set_data(self, key, value):
self._data[key] = value

def add_test(self, test_data, report, logs):
def add_test(self, test_data, report, outcome, logs):
# regardless of pass or fail we must add teardown logging to "call"
if report.when == "teardown":
self.append_teardown_log(report)
Expand All @@ -137,10 +137,8 @@ def add_test(self, test_data, report, logs):
report.when in ["setup", "teardown"] and report.outcome != "passed"
):
test_data["log"] = _handle_ansi("\n".join(logs))
self.outcomes = outcome
self._data["tests"][report.nodeid].append(test_data)
return True

return False

def append_teardown_log(self, report):
log = []
Expand Down