summaryrefslogtreecommitdiff
path: root/testing/test_resultlog.py
blob: 8fc93d25c7d29e236b342241e66e93cdd6e1f65e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
import os
from io import StringIO

import _pytest._code
import pytest
from _pytest.resultlog import pytest_configure
from _pytest.resultlog import pytest_unconfigure
from _pytest.resultlog import ResultLog
from _pytest.resultlog import resultlog_key

pytestmark = pytest.mark.filterwarnings("ignore:--result-log is deprecated")


def test_write_log_entry():
    reslog = ResultLog(None, None)
    reslog.logfile = StringIO()
    reslog.write_log_entry("name", ".", "")
    entry = reslog.logfile.getvalue()
    assert entry[-1] == "\n"
    entry_lines = entry.splitlines()
    assert len(entry_lines) == 1
    assert entry_lines[0] == ". name"

    reslog.logfile = StringIO()
    reslog.write_log_entry("name", "s", "Skipped")
    entry = reslog.logfile.getvalue()
    assert entry[-1] == "\n"
    entry_lines = entry.splitlines()
    assert len(entry_lines) == 2
    assert entry_lines[0] == "s name"
    assert entry_lines[1] == " Skipped"

    reslog.logfile = StringIO()
    reslog.write_log_entry("name", "s", "Skipped\n")
    entry = reslog.logfile.getvalue()
    assert entry[-1] == "\n"
    entry_lines = entry.splitlines()
    assert len(entry_lines) == 2
    assert entry_lines[0] == "s name"
    assert entry_lines[1] == " Skipped"

    reslog.logfile = StringIO()
    longrepr = " tb1\n tb 2\nE tb3\nSome Error"
    reslog.write_log_entry("name", "F", longrepr)
    entry = reslog.logfile.getvalue()
    assert entry[-1] == "\n"
    entry_lines = entry.splitlines()
    assert len(entry_lines) == 5
    assert entry_lines[0] == "F name"
    assert entry_lines[1:] == [" " + line for line in longrepr.splitlines()]


class TestWithFunctionIntegration:
    # XXX (hpk) i think that the resultlog plugin should
    # provide a Parser object so that one can remain
    # ignorant regarding formatting details.
    def getresultlog(self, testdir, arg):
        resultlog = testdir.tmpdir.join("resultlog")
        testdir.plugins.append("resultlog")
        args = ["--resultlog=%s" % resultlog] + [arg]
        testdir.runpytest(*args)
        return [x for x in resultlog.readlines(cr=0) if x]

    def test_collection_report(self, testdir):
        ok = testdir.makepyfile(test_collection_ok="")
        fail = testdir.makepyfile(test_collection_fail="XXX")
        lines = self.getresultlog(testdir, ok)
        assert not lines

        lines = self.getresultlog(testdir, fail)
        assert lines
        assert lines[0].startswith("F ")
        assert lines[0].endswith("test_collection_fail.py"), lines[0]
        for x in lines[1:]:
            assert x.startswith(" ")
        assert "XXX" in "".join(lines[1:])

    def test_log_test_outcomes(self, testdir):
        mod = testdir.makepyfile(
            test_mod="""
            import pytest
            def test_pass(): pass
            def test_skip(): pytest.skip("hello")
            def test_fail(): raise ValueError("FAIL")

            @pytest.mark.xfail
            def test_xfail(): raise ValueError("XFAIL")
            @pytest.mark.xfail
            def test_xpass(): pass

        """
        )
        lines = self.getresultlog(testdir, mod)
        assert len(lines) >= 3
        assert lines[0].startswith(". ")
        assert lines[0].endswith("test_pass")
        assert lines[1].startswith("s "), lines[1]
        assert lines[1].endswith("test_skip")
        assert lines[2].find("hello") != -1

        assert lines[3].startswith("F ")
        assert lines[3].endswith("test_fail")
        tb = "".join(lines[4:8])
        assert tb.find('raise ValueError("FAIL")') != -1

        assert lines[8].startswith("x ")
        tb = "".join(lines[8:14])
        assert tb.find('raise ValueError("XFAIL")') != -1

        assert lines[14].startswith("X ")
        assert len(lines) == 15

    @pytest.mark.parametrize("style", ("native", "long", "short"))
    def test_internal_exception(self, style):
        # they are produced for example by a teardown failing
        # at the end of the run or a failing hook invocation
        try:
            raise ValueError
        except ValueError:
            excinfo = _pytest._code.ExceptionInfo.from_current()
        reslog = ResultLog(None, StringIO())
        reslog.pytest_internalerror(excinfo.getrepr(style=style))
        entry = reslog.logfile.getvalue()
        entry_lines = entry.splitlines()

        assert entry_lines[0].startswith("! ")
        if style != "native":
            assert os.path.basename(__file__)[:-9] in entry_lines[0]  # .pyc/class
        assert entry_lines[-1][0] == " "
        assert "ValueError" in entry


def test_generic(testdir, LineMatcher):
    testdir.plugins.append("resultlog")
    testdir.makepyfile(
        """
        import pytest
        def test_pass():
            pass
        def test_fail():
            assert 0
        def test_skip():
            pytest.skip("")
        @pytest.mark.xfail
        def test_xfail():
            assert 0
        @pytest.mark.xfail(run=False)
        def test_xfail_norun():
            assert 0
    """
    )
    testdir.runpytest("--resultlog=result.log")
    lines = testdir.tmpdir.join("result.log").readlines(cr=0)
    LineMatcher(lines).fnmatch_lines(
        [
            ". *:test_pass",
            "F *:test_fail",
            "s *:test_skip",
            "x *:test_xfail",
            "x *:test_xfail_norun",
        ]
    )


def test_makedir_for_resultlog(testdir, LineMatcher):
    """--resultlog should automatically create directories for the log file"""
    testdir.plugins.append("resultlog")
    testdir.makepyfile(
        """
        import pytest
        def test_pass():
            pass
    """
    )
    testdir.runpytest("--resultlog=path/to/result.log")
    lines = testdir.tmpdir.join("path/to/result.log").readlines(cr=0)
    LineMatcher(lines).fnmatch_lines([". *:test_pass"])


def test_no_resultlog_on_workers(testdir):
    config = testdir.parseconfig("-p", "resultlog", "--resultlog=resultlog")

    assert resultlog_key not in config._store
    pytest_configure(config)
    assert resultlog_key in config._store
    pytest_unconfigure(config)
    assert resultlog_key not in config._store

    config.workerinput = {}
    pytest_configure(config)
    assert resultlog_key not in config._store
    pytest_unconfigure(config)
    assert resultlog_key not in config._store


def test_unknown_teststatus(testdir):
    """Ensure resultlog correctly handles unknown status from pytest_report_teststatus

    Inspired on pytest-rerunfailures.
    """
    testdir.makepyfile(
        """
        def test():
            assert 0
    """
    )
    testdir.makeconftest(
        """
        import pytest

        def pytest_report_teststatus(report):
            if report.outcome == 'rerun':
                return "rerun", "r", "RERUN"

        @pytest.hookimpl(hookwrapper=True)
        def pytest_runtest_makereport():
            res = yield
            report = res.get_result()
            if report.when == "call":
                report.outcome = 'rerun'
    """
    )
    result = testdir.runpytest("--resultlog=result.log")
    result.stdout.fnmatch_lines(
        ["test_unknown_teststatus.py r *[[]100%[]]", "* 1 rerun *"]
    )

    lines = testdir.tmpdir.join("result.log").readlines(cr=0)
    assert lines[0] == "r test_unknown_teststatus.py::test"


def test_failure_issue380(testdir):
    testdir.makeconftest(
        """
        import pytest
        class MyCollector(pytest.File):
            def collect(self):
                raise ValueError()
            def repr_failure(self, excinfo):
                return "somestring"
        def pytest_collect_file(path, parent):
            return MyCollector(parent=parent, fspath=path)
    """
    )
    testdir.makepyfile(
        """
        def test_func():
            pass
    """
    )
    result = testdir.runpytest("--resultlog=log")
    assert result.ret == 2