Skip to content

Commit

Permalink
Add pytest_report_serialize and pytest_report_unserialize hooks
Browse files Browse the repository at this point in the history
These hooks will be used by pytest-xdist and pytest-subtests to
serialize and customize reports.
  • Loading branch information
nicoddemus committed Mar 20, 2019
1 parent 8ac8331 commit 8b683f6
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/_pytest/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ def directory_arg(path, optname):
"stepwise",
"warnings",
"logging",
"reports",
)


Expand Down
35 changes: 35 additions & 0 deletions src/_pytest/hookspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,41 @@ def pytest_runtest_logreport(report):
the respective phase of executing a test. """


@hookspec(firstresult=True)
def pytest_report_serialize(config, report):
"""
.. warning::
This hook is experimental and subject to change between pytest releases, even
bug fixes.
The intent is for this to be used by plugins maintained by the core-devs, such
as ``pytest-xdist``, ``pytest-subtests``, and as a replacement for the internal
'resultlog' plugin.
In the future it might become part of the public hook API.
Serializes the given report object into a data structure suitable for sending
over the wire, or converted to JSON.
"""


@hookspec(firstresult=True)
def pytest_report_unserialize(config, data):
"""
.. warning::
This hook is experimental and subject to change between pytest releases, even
bug fixes.
The intent is for this to be used by plugins maintained by the core-devs, such
as ``pytest-xdist``, ``pytest-subtests``, and as a replacement for the internal
'resultlog' plugin.
In the future it might become part of the public hook API.
Restores a report object previously serialized with pytest_report_serialize().;
"""


# -------------------------------------------------------------------------
# Fixture related hooks
# -------------------------------------------------------------------------
Expand Down
16 changes: 16 additions & 0 deletions src/_pytest/reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,3 +404,19 @@ def __init__(self, msg):

def toterminal(self, out):
out.line(self.longrepr, red=True)


def pytest_report_serialize(report):
if isinstance(report, (TestReport, CollectReport)):
data = report._to_json()
data["_report_type"] = report.__class__.__name__
return data


def pytest_report_unserialize(data):
if "_report_type" in data:
if data["_report_type"] == "TestReport":
return TestReport._from_json(data)
elif data["_report_type"] == "CollectReport":
return CollectReport._from_json(data)
assert "Unknown report_type unserialize data: {}".format(data["_report_type"])
50 changes: 50 additions & 0 deletions testing/test_reports.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,53 @@ def test_extended_report_deserialization(self, testdir):
assert newrep.skipped == rep.skipped
if rep.failed:
assert newrep.longrepr == str(rep.longrepr)


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

def test_test_report(self, testdir, pytestconfig):
testdir.makepyfile(
"""
import os
def test_a(): assert False
def test_b(): pass
"""
)
reprec = testdir.inline_run()
reports = reprec.getreports("pytest_runtest_logreport")
assert len(reports) == 6
for rep in reports:
data = pytestconfig.hook.pytest_report_serialize(
config=pytestconfig, report=rep
)
assert data["_report_type"] == "TestReport"
new_rep = pytestconfig.hook.pytest_report_unserialize(
config=pytestconfig, data=data
)
assert new_rep.nodeid == rep.nodeid
assert new_rep.when == rep.when
assert new_rep.outcome == rep.outcome

def test_collect_report(self, testdir, pytestconfig):
testdir.makepyfile(
"""
import os
def test_a(): assert False
def test_b(): pass
"""
)
reprec = testdir.inline_run()
reports = reprec.getreports("pytest_collectreport")
assert len(reports) == 2
for rep in reports:
data = pytestconfig.hook.pytest_report_serialize(
config=pytestconfig, report=rep
)
assert data["_report_type"] == "CollectReport"
new_rep = pytestconfig.hook.pytest_report_unserialize(
config=pytestconfig, data=data
)
assert new_rep.nodeid == rep.nodeid
assert new_rep.when == "collect"
assert new_rep.outcome == rep.outcome

0 comments on commit 8b683f6

Please sign in to comment.