Skip to content

Commit 5e1c3d2

Browse files
authored
Propagate timestamps from CallInfo to TestReport objects (pytest-dev#10711)
This makes it possible to correlate pytest stages with external events, and also makes it readable when TestReports are exported externall (for example with pytest-reportlog). Closes pytest-dev#10710
1 parent 59e7d2b commit 5e1c3d2

File tree

4 files changed

+34
-0
lines changed

4 files changed

+34
-0
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ Ram Rachum
297297
Ran Benita
298298
Raphael Castaneda
299299
Raphael Pierzina
300+
Rafal Semik
300301
Raquel Alegre
301302
Ravi Chandra
302303
Robert Holt

changelog/10710.improvement.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added ``start`` and ``stop`` timestamps to ``TestReport`` objects.

src/_pytest/reports.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,8 @@ def __init__(
262262
when: "Literal['setup', 'call', 'teardown']",
263263
sections: Iterable[Tuple[str, str]] = (),
264264
duration: float = 0,
265+
start: float = 0,
266+
stop: float = 0,
265267
user_properties: Optional[Iterable[Tuple[str, object]]] = None,
266268
**extra,
267269
) -> None:
@@ -299,6 +301,11 @@ def __init__(
299301
#: Time it took to run just the test.
300302
self.duration: float = duration
301303

304+
#: The system time when the call started, in seconds since the epoch.
305+
self.start: float = start
306+
#: The system time when the call ended, in seconds since the epoch.
307+
self.stop: float = stop
308+
302309
self.__dict__.update(extra)
303310

304311
def __repr__(self) -> str:
@@ -317,6 +324,8 @@ def from_item_and_call(cls, item: Item, call: "CallInfo[None]") -> "TestReport":
317324
# Remove "collect" from the Literal type -- only for collection calls.
318325
assert when != "collect"
319326
duration = call.duration
327+
start = call.start
328+
stop = call.stop
320329
keywords = {x: 1 for x in item.keywords}
321330
excinfo = call.excinfo
322331
sections = []
@@ -361,6 +370,8 @@ def from_item_and_call(cls, item: Item, call: "CallInfo[None]") -> "TestReport":
361370
when,
362371
sections,
363372
duration,
373+
start,
374+
stop,
364375
user_properties=item.user_properties,
365376
)
366377

testing/test_reports.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from _pytest._code.code import ExceptionRepr
77
from _pytest.config import Config
88
from _pytest.pytester import Pytester
9+
from _pytest.python_api import approx
910
from _pytest.reports import CollectReport
1011
from _pytest.reports import TestReport
1112

@@ -415,6 +416,26 @@ def test_report_prevent_ConftestImportFailure_hiding_exception(
415416
result.stdout.fnmatch_lines(["E *Error: No module named 'unknown'"])
416417
result.stdout.no_fnmatch_line("ERROR - *ConftestImportFailure*")
417418

419+
def test_report_timestamps_match_duration(self, pytester: Pytester, mock_timing):
420+
reprec = pytester.inline_runsource(
421+
"""
422+
import pytest
423+
from _pytest import timing
424+
@pytest.fixture
425+
def fixture_():
426+
timing.sleep(5)
427+
yield
428+
timing.sleep(5)
429+
def test_1(fixture_): timing.sleep(10)
430+
"""
431+
)
432+
reports = reprec.getreports("pytest_runtest_logreport")
433+
assert len(reports) == 3
434+
for report in reports:
435+
data = report._to_json()
436+
loaded_report = TestReport._from_json(data)
437+
assert loaded_report.stop - loaded_report.start == approx(report.duration)
438+
418439

419440
class TestHooks:
420441
"""Test that the hooks are working correctly for plugins"""

0 commit comments

Comments
 (0)