1# -*- coding: utf-8 -*-
2import pytest
3from _pytest.pathlib import Path
4from _pytest.reports import CollectReport
5from _pytest.reports import TestReport
6
7
8class TestReportSerialization(object):
9    def test_xdist_longrepr_to_str_issue_241(self, testdir):
10        """
11        Regarding issue pytest-xdist#241
12
13        This test came originally from test_remote.py in xdist (ca03269).
14        """
15        testdir.makepyfile(
16            """
17            def test_a(): assert False
18            def test_b(): pass
19        """
20        )
21        reprec = testdir.inline_run()
22        reports = reprec.getreports("pytest_runtest_logreport")
23        assert len(reports) == 6
24        test_a_call = reports[1]
25        assert test_a_call.when == "call"
26        assert test_a_call.outcome == "failed"
27        assert test_a_call._to_json()["longrepr"]["reprtraceback"]["style"] == "long"
28        test_b_call = reports[4]
29        assert test_b_call.when == "call"
30        assert test_b_call.outcome == "passed"
31        assert test_b_call._to_json()["longrepr"] is None
32
33    def test_xdist_report_longrepr_reprcrash_130(self, testdir):
34        """Regarding issue pytest-xdist#130
35
36        This test came originally from test_remote.py in xdist (ca03269).
37        """
38        reprec = testdir.inline_runsource(
39            """
40                    def test_fail():
41                        assert False, 'Expected Message'
42                """
43        )
44        reports = reprec.getreports("pytest_runtest_logreport")
45        assert len(reports) == 3
46        rep = reports[1]
47        added_section = ("Failure Metadata", str("metadata metadata"), "*")
48        rep.longrepr.sections.append(added_section)
49        d = rep._to_json()
50        a = TestReport._from_json(d)
51        # Check assembled == rep
52        assert a.__dict__.keys() == rep.__dict__.keys()
53        for key in rep.__dict__.keys():
54            if key != "longrepr":
55                assert getattr(a, key) == getattr(rep, key)
56        assert rep.longrepr.reprcrash.lineno == a.longrepr.reprcrash.lineno
57        assert rep.longrepr.reprcrash.message == a.longrepr.reprcrash.message
58        assert rep.longrepr.reprcrash.path == a.longrepr.reprcrash.path
59        assert rep.longrepr.reprtraceback.entrysep == a.longrepr.reprtraceback.entrysep
60        assert (
61            rep.longrepr.reprtraceback.extraline == a.longrepr.reprtraceback.extraline
62        )
63        assert rep.longrepr.reprtraceback.style == a.longrepr.reprtraceback.style
64        assert rep.longrepr.sections == a.longrepr.sections
65        # Missing section attribute PR171
66        assert added_section in a.longrepr.sections
67
68    def test_reprentries_serialization_170(self, testdir):
69        """Regarding issue pytest-xdist#170
70
71        This test came originally from test_remote.py in xdist (ca03269).
72        """
73        from _pytest._code.code import ReprEntry
74
75        reprec = testdir.inline_runsource(
76            """
77                            def test_repr_entry():
78                                x = 0
79                                assert x
80                        """,
81            "--showlocals",
82        )
83        reports = reprec.getreports("pytest_runtest_logreport")
84        assert len(reports) == 3
85        rep = reports[1]
86        d = rep._to_json()
87        a = TestReport._from_json(d)
88
89        rep_entries = rep.longrepr.reprtraceback.reprentries
90        a_entries = a.longrepr.reprtraceback.reprentries
91        for i in range(len(a_entries)):
92            assert isinstance(rep_entries[i], ReprEntry)
93            assert rep_entries[i].lines == a_entries[i].lines
94            assert rep_entries[i].reprfileloc.lineno == a_entries[i].reprfileloc.lineno
95            assert (
96                rep_entries[i].reprfileloc.message == a_entries[i].reprfileloc.message
97            )
98            assert rep_entries[i].reprfileloc.path == a_entries[i].reprfileloc.path
99            assert rep_entries[i].reprfuncargs.args == a_entries[i].reprfuncargs.args
100            assert rep_entries[i].reprlocals.lines == a_entries[i].reprlocals.lines
101            assert rep_entries[i].style == a_entries[i].style
102
103    def test_reprentries_serialization_196(self, testdir):
104        """Regarding issue pytest-xdist#196
105
106        This test came originally from test_remote.py in xdist (ca03269).
107        """
108        from _pytest._code.code import ReprEntryNative
109
110        reprec = testdir.inline_runsource(
111            """
112                            def test_repr_entry_native():
113                                x = 0
114                                assert x
115                        """,
116            "--tb=native",
117        )
118        reports = reprec.getreports("pytest_runtest_logreport")
119        assert len(reports) == 3
120        rep = reports[1]
121        d = rep._to_json()
122        a = TestReport._from_json(d)
123
124        rep_entries = rep.longrepr.reprtraceback.reprentries
125        a_entries = a.longrepr.reprtraceback.reprentries
126        for i in range(len(a_entries)):
127            assert isinstance(rep_entries[i], ReprEntryNative)
128            assert rep_entries[i].lines == a_entries[i].lines
129
130    def test_itemreport_outcomes(self, testdir):
131        """
132        This test came originally from test_remote.py in xdist (ca03269).
133        """
134        reprec = testdir.inline_runsource(
135            """
136            import py
137            def test_pass(): pass
138            def test_fail(): 0/0
139            @py.test.mark.skipif("True")
140            def test_skip(): pass
141            def test_skip_imperative():
142                py.test.skip("hello")
143            @py.test.mark.xfail("True")
144            def test_xfail(): 0/0
145            def test_xfail_imperative():
146                py.test.xfail("hello")
147        """
148        )
149        reports = reprec.getreports("pytest_runtest_logreport")
150        assert len(reports) == 17  # with setup/teardown "passed" reports
151        for rep in reports:
152            d = rep._to_json()
153            newrep = TestReport._from_json(d)
154            assert newrep.passed == rep.passed
155            assert newrep.failed == rep.failed
156            assert newrep.skipped == rep.skipped
157            if newrep.skipped and not hasattr(newrep, "wasxfail"):
158                assert len(newrep.longrepr) == 3
159            assert newrep.outcome == rep.outcome
160            assert newrep.when == rep.when
161            assert newrep.keywords == rep.keywords
162            if rep.failed:
163                assert newrep.longreprtext == rep.longreprtext
164
165    def test_collectreport_passed(self, testdir):
166        """This test came originally from test_remote.py in xdist (ca03269)."""
167        reprec = testdir.inline_runsource("def test_func(): pass")
168        reports = reprec.getreports("pytest_collectreport")
169        for rep in reports:
170            d = rep._to_json()
171            newrep = CollectReport._from_json(d)
172            assert newrep.passed == rep.passed
173            assert newrep.failed == rep.failed
174            assert newrep.skipped == rep.skipped
175
176    def test_collectreport_fail(self, testdir):
177        """This test came originally from test_remote.py in xdist (ca03269)."""
178        reprec = testdir.inline_runsource("qwe abc")
179        reports = reprec.getreports("pytest_collectreport")
180        assert reports
181        for rep in reports:
182            d = rep._to_json()
183            newrep = CollectReport._from_json(d)
184            assert newrep.passed == rep.passed
185            assert newrep.failed == rep.failed
186            assert newrep.skipped == rep.skipped
187            if rep.failed:
188                assert newrep.longrepr == str(rep.longrepr)
189
190    def test_extended_report_deserialization(self, testdir):
191        """This test came originally from test_remote.py in xdist (ca03269)."""
192        reprec = testdir.inline_runsource("qwe abc")
193        reports = reprec.getreports("pytest_collectreport")
194        assert reports
195        for rep in reports:
196            rep.extra = True
197            d = rep._to_json()
198            newrep = CollectReport._from_json(d)
199            assert newrep.extra
200            assert newrep.passed == rep.passed
201            assert newrep.failed == rep.failed
202            assert newrep.skipped == rep.skipped
203            if rep.failed:
204                assert newrep.longrepr == str(rep.longrepr)
205
206    def test_paths_support(self, testdir):
207        """Report attributes which are py.path or pathlib objects should become strings."""
208        testdir.makepyfile(
209            """
210            def test_a():
211                assert False
212        """
213        )
214        reprec = testdir.inline_run()
215        reports = reprec.getreports("pytest_runtest_logreport")
216        assert len(reports) == 3
217        test_a_call = reports[1]
218        test_a_call.path1 = testdir.tmpdir
219        test_a_call.path2 = Path(testdir.tmpdir)
220        data = test_a_call._to_json()
221        assert data["path1"] == str(testdir.tmpdir)
222        assert data["path2"] == str(testdir.tmpdir)
223
224    def test_unserialization_failure(self, testdir):
225        """Check handling of failure during unserialization of report types."""
226        testdir.makepyfile(
227            """
228            def test_a():
229                assert False
230        """
231        )
232        reprec = testdir.inline_run()
233        reports = reprec.getreports("pytest_runtest_logreport")
234        assert len(reports) == 3
235        test_a_call = reports[1]
236        data = test_a_call._to_json()
237        entry = data["longrepr"]["reprtraceback"]["reprentries"][0]
238        assert entry["type"] == "ReprEntry"
239
240        entry["type"] = "Unknown"
241        with pytest.raises(
242            RuntimeError, match="INTERNALERROR: Unknown entry type returned: Unknown"
243        ):
244            TestReport._from_json(data)
245
246
247class TestHooks:
248    """Test that the hooks are working correctly for plugins"""
249
250    def test_test_report(self, testdir, pytestconfig):
251        testdir.makepyfile(
252            """
253            def test_a(): assert False
254            def test_b(): pass
255        """
256        )
257        reprec = testdir.inline_run()
258        reports = reprec.getreports("pytest_runtest_logreport")
259        assert len(reports) == 6
260        for rep in reports:
261            data = pytestconfig.hook.pytest_report_to_serializable(
262                config=pytestconfig, report=rep
263            )
264            assert data["_report_type"] == "TestReport"
265            new_rep = pytestconfig.hook.pytest_report_from_serializable(
266                config=pytestconfig, data=data
267            )
268            assert new_rep.nodeid == rep.nodeid
269            assert new_rep.when == rep.when
270            assert new_rep.outcome == rep.outcome
271
272    def test_collect_report(self, testdir, pytestconfig):
273        testdir.makepyfile(
274            """
275            def test_a(): assert False
276            def test_b(): pass
277        """
278        )
279        reprec = testdir.inline_run()
280        reports = reprec.getreports("pytest_collectreport")
281        assert len(reports) == 2
282        for rep in reports:
283            data = pytestconfig.hook.pytest_report_to_serializable(
284                config=pytestconfig, report=rep
285            )
286            assert data["_report_type"] == "CollectReport"
287            new_rep = pytestconfig.hook.pytest_report_from_serializable(
288                config=pytestconfig, data=data
289            )
290            assert new_rep.nodeid == rep.nodeid
291            assert new_rep.when == "collect"
292            assert new_rep.outcome == rep.outcome
293
294    @pytest.mark.parametrize(
295        "hook_name", ["pytest_runtest_logreport", "pytest_collectreport"]
296    )
297    def test_invalid_report_types(self, testdir, pytestconfig, hook_name):
298        testdir.makepyfile(
299            """
300            def test_a(): pass
301            """
302        )
303        reprec = testdir.inline_run()
304        reports = reprec.getreports(hook_name)
305        assert reports
306        rep = reports[0]
307        data = pytestconfig.hook.pytest_report_to_serializable(
308            config=pytestconfig, report=rep
309        )
310        data["_report_type"] = "Unknown"
311        with pytest.raises(AssertionError):
312            _ = pytestconfig.hook.pytest_report_from_serializable(
313                config=pytestconfig, data=data
314            )
315