2020-07-18 17:35:13 +08:00
|
|
|
"""Terminal reporting of the full testing process."""
|
2015-08-17 15:10:01 +08:00
|
|
|
import collections
|
2018-08-27 04:12:55 +08:00
|
|
|
import os
|
2009-07-17 22:19:19 +08:00
|
|
|
import sys
|
2018-08-24 00:06:17 +08:00
|
|
|
import textwrap
|
2019-10-26 20:47:48 +08:00
|
|
|
from io import StringIO
|
2020-10-03 23:08:14 +08:00
|
|
|
from pathlib import Path
|
2020-05-10 09:09:09 +08:00
|
|
|
from types import SimpleNamespace
|
2020-05-01 19:40:17 +08:00
|
|
|
from typing import cast
|
2020-01-25 21:49:48 +08:00
|
|
|
from typing import Dict
|
|
|
|
from typing import List
|
|
|
|
from typing import Tuple
|
2009-07-17 22:19:19 +08:00
|
|
|
|
2017-08-25 23:46:55 +08:00
|
|
|
import pluggy
|
2018-10-25 15:01:29 +08:00
|
|
|
|
2020-01-06 22:09:09 +08:00
|
|
|
import _pytest.config
|
2020-05-26 19:59:16 +08:00
|
|
|
import _pytest.terminal
|
2015-11-27 22:43:01 +08:00
|
|
|
import pytest
|
2020-05-26 19:59:16 +08:00
|
|
|
from _pytest._io.wcwidth import wcswidth
|
2020-05-01 19:40:17 +08:00
|
|
|
from _pytest.config import Config
|
2020-02-11 05:43:30 +08:00
|
|
|
from _pytest.config import ExitCode
|
2020-09-29 14:29:27 +08:00
|
|
|
from _pytest.monkeypatch import MonkeyPatch
|
2020-12-09 04:20:02 +08:00
|
|
|
from _pytest.pytester import Pytester
|
2019-03-07 05:54:45 +08:00
|
|
|
from _pytest.reports import BaseReport
|
2020-05-01 19:40:17 +08:00
|
|
|
from _pytest.reports import CollectReport
|
2020-05-10 09:09:09 +08:00
|
|
|
from _pytest.reports import TestReport
|
2019-04-08 01:36:29 +08:00
|
|
|
from _pytest.terminal import _folded_skips
|
2020-05-10 09:09:09 +08:00
|
|
|
from _pytest.terminal import _format_trimmed
|
2019-04-17 21:30:34 +08:00
|
|
|
from _pytest.terminal import _get_line_with_reprcrash_message
|
2020-05-10 09:09:09 +08:00
|
|
|
from _pytest.terminal import _get_raw_skip_reason
|
2018-10-25 15:01:29 +08:00
|
|
|
from _pytest.terminal import _plugin_nameversions
|
|
|
|
from _pytest.terminal import getreportopt
|
|
|
|
from _pytest.terminal import TerminalReporter
|
2009-07-17 22:19:19 +08:00
|
|
|
|
2018-05-23 22:48:46 +08:00
|
|
|
DistInfo = collections.namedtuple("DistInfo", ["project_name", "version"])
|
2020-01-18 19:43:38 +08:00
|
|
|
|
2015-08-17 15:10:01 +08:00
|
|
|
|
2020-01-28 22:52:46 +08:00
|
|
|
TRANS_FNMATCH = str.maketrans({"[": "[[]", "]": "[]]"})
|
2015-08-17 15:10:01 +08:00
|
|
|
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
class Option:
|
2019-11-04 04:01:37 +08:00
|
|
|
def __init__(self, verbosity=0):
|
2019-03-22 14:45:43 +08:00
|
|
|
self.verbosity = verbosity
|
2010-07-07 18:41:15 +08:00
|
|
|
|
|
|
|
@property
|
|
|
|
def args(self):
|
2017-11-04 23:17:20 +08:00
|
|
|
values = []
|
2019-03-22 14:45:43 +08:00
|
|
|
values.append("--verbosity=%d" % self.verbosity)
|
2017-11-04 23:17:20 +08:00
|
|
|
return values
|
2009-07-18 00:07:37 +08:00
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
2018-05-23 22:48:46 +08:00
|
|
|
@pytest.fixture(
|
2019-11-04 04:01:37 +08:00
|
|
|
params=[Option(verbosity=0), Option(verbosity=1), Option(verbosity=-1)],
|
|
|
|
ids=["default", "verbose", "quiet"],
|
2018-05-23 22:48:46 +08:00
|
|
|
)
|
2018-02-22 01:54:39 +08:00
|
|
|
def option(request):
|
|
|
|
return request.param
|
2010-07-07 18:41:15 +08:00
|
|
|
|
2009-07-18 00:07:37 +08:00
|
|
|
|
2018-05-23 22:48:46 +08:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"input,expected",
|
|
|
|
[
|
|
|
|
([DistInfo(project_name="test", version=1)], ["test-1"]),
|
|
|
|
([DistInfo(project_name="pytest-test", version=1)], ["test-1"]),
|
|
|
|
(
|
|
|
|
[
|
|
|
|
DistInfo(project_name="test", version=1),
|
|
|
|
DistInfo(project_name="test", version=1),
|
|
|
|
],
|
|
|
|
["test-1"],
|
|
|
|
),
|
|
|
|
],
|
|
|
|
ids=["normal", "prefix-strip", "deduplicate"],
|
|
|
|
)
|
2015-08-17 15:10:01 +08:00
|
|
|
def test_plugin_nameversion(input, expected):
|
|
|
|
pluginlist = [(None, x) for x in input]
|
|
|
|
result = _plugin_nameversions(pluginlist)
|
|
|
|
assert result == expected
|
|
|
|
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
class TestTerminal:
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_pass_skip_fail(self, pytester: Pytester, option) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2010-11-18 05:12:16 +08:00
|
|
|
import pytest
|
2009-07-17 22:19:19 +08:00
|
|
|
def test_ok():
|
|
|
|
pass
|
|
|
|
def test_skip():
|
2010-11-18 05:12:16 +08:00
|
|
|
pytest.skip("xx")
|
2009-07-17 22:19:19 +08:00
|
|
|
def test_func():
|
|
|
|
assert 0
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest(*option.args)
|
2019-03-22 14:45:43 +08:00
|
|
|
if option.verbosity > 0:
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"*test_pass_skip_fail.py::test_ok PASS*",
|
|
|
|
"*test_pass_skip_fail.py::test_skip SKIP*",
|
|
|
|
"*test_pass_skip_fail.py::test_func FAIL*",
|
|
|
|
]
|
|
|
|
)
|
2019-03-22 14:45:43 +08:00
|
|
|
elif option.verbosity == 0:
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["*test_pass_skip_fail.py .sF*"])
|
2019-03-22 14:45:43 +08:00
|
|
|
else:
|
|
|
|
result.stdout.fnmatch_lines([".sF*"])
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[" def test_func():", "> assert 0", "E assert 0"]
|
|
|
|
)
|
2009-07-17 22:19:19 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_internalerror(self, pytester: Pytester, linecomp) -> None:
|
|
|
|
modcol = pytester.getmodulecol("def test_one(): pass")
|
2009-07-17 22:19:19 +08:00
|
|
|
rep = TerminalReporter(modcol.config, file=linecomp.stringio)
|
2018-11-23 02:05:10 +08:00
|
|
|
with pytest.raises(ValueError) as excinfo:
|
|
|
|
raise ValueError("hello")
|
2009-07-17 22:19:19 +08:00
|
|
|
rep.pytest_internalerror(excinfo.getrepr())
|
2018-05-23 22:48:46 +08:00
|
|
|
linecomp.assert_contains_lines(["INTERNALERROR> *ValueError*hello*"])
|
2009-07-17 22:19:19 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_writeline(self, pytester: Pytester, linecomp) -> None:
|
|
|
|
modcol = pytester.getmodulecol("def test_one(): pass")
|
2009-07-17 22:19:19 +08:00
|
|
|
rep = TerminalReporter(modcol.config, file=linecomp.stringio)
|
2015-02-27 04:56:44 +08:00
|
|
|
rep.write_fspath_result(modcol.nodeid, ".")
|
2009-07-17 22:19:19 +08:00
|
|
|
rep.write_line("hello world")
|
2018-05-23 22:48:46 +08:00
|
|
|
lines = linecomp.stringio.getvalue().split("\n")
|
2009-07-17 22:19:19 +08:00
|
|
|
assert not lines[0]
|
2015-02-27 04:56:44 +08:00
|
|
|
assert lines[1].endswith(modcol.name + " .")
|
2009-07-17 22:19:19 +08:00
|
|
|
assert lines[2] == "hello world"
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_show_runtest_logstart(self, pytester: Pytester, linecomp) -> None:
|
|
|
|
item = pytester.getitem("def test_func(): pass")
|
2009-07-17 22:19:19 +08:00
|
|
|
tr = TerminalReporter(item.config, file=linecomp.stringio)
|
|
|
|
item.config.pluginmanager.register(tr)
|
2010-11-05 06:21:23 +08:00
|
|
|
location = item.reportinfo()
|
2018-05-23 22:48:46 +08:00
|
|
|
tr.config.hook.pytest_runtest_logstart(
|
2021-01-18 04:20:29 +08:00
|
|
|
nodeid=item.nodeid, location=location, fspath=str(item.path)
|
2018-05-23 22:48:46 +08:00
|
|
|
)
|
|
|
|
linecomp.assert_contains_lines(["*test_show_runtest_logstart.py*"])
|
2009-07-17 22:19:19 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_runtest_location_shown_before_test_starts(
|
|
|
|
self, pytester: Pytester
|
|
|
|
) -> None:
|
2020-10-25 08:03:20 +08:00
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2010-09-26 22:23:45 +08:00
|
|
|
def test_1():
|
|
|
|
import time
|
|
|
|
time.sleep(20)
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-10-25 08:03:20 +08:00
|
|
|
child = pytester.spawn_pytest("")
|
2010-09-26 22:23:45 +08:00
|
|
|
child.expect(".*test_runtest_location.*py")
|
|
|
|
child.sendeof()
|
|
|
|
child.kill(15)
|
2009-07-17 22:19:19 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_report_collect_after_half_a_second(
|
|
|
|
self, pytester: Pytester, monkeypatch: MonkeyPatch
|
|
|
|
) -> None:
|
2019-03-21 01:24:25 +08:00
|
|
|
"""Test for "collecting" being updated after 0.5s"""
|
|
|
|
|
2020-10-25 08:03:20 +08:00
|
|
|
pytester.makepyfile(
|
2019-03-21 01:24:25 +08:00
|
|
|
**{
|
|
|
|
"test1.py": """
|
|
|
|
import _pytest.terminal
|
|
|
|
|
|
|
|
_pytest.terminal.REPORT_COLLECTING_RESOLUTION = 0
|
|
|
|
|
|
|
|
def test_1():
|
|
|
|
pass
|
|
|
|
""",
|
|
|
|
"test2.py": "def test_2(): pass",
|
|
|
|
}
|
|
|
|
)
|
2019-11-02 20:13:33 +08:00
|
|
|
# Explicitly test colored output.
|
2020-10-25 08:03:20 +08:00
|
|
|
monkeypatch.setenv("PY_COLORS", "1")
|
2019-03-21 01:24:25 +08:00
|
|
|
|
2020-10-25 08:03:20 +08:00
|
|
|
child = pytester.spawn_pytest("-v test1.py test2.py")
|
2019-03-21 01:24:25 +08:00
|
|
|
child.expect(r"collecting \.\.\.")
|
|
|
|
child.expect(r"collecting 1 item")
|
|
|
|
child.expect(r"collecting 2 items")
|
|
|
|
child.expect(r"collected 2 items")
|
|
|
|
rest = child.read().decode("utf8")
|
2019-04-06 23:09:48 +08:00
|
|
|
assert "= \x1b[32m\x1b[1m2 passed\x1b[0m\x1b[32m in" in rest
|
2019-03-21 01:24:25 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_itemreport_subclasses_show_subclassed_file(
|
|
|
|
self, pytester: Pytester
|
|
|
|
) -> None:
|
|
|
|
pytester.makepyfile(
|
2020-02-21 22:24:12 +08:00
|
|
|
**{
|
|
|
|
"tests/test_p1": """
|
2017-02-17 02:41:51 +08:00
|
|
|
class BaseTests(object):
|
2020-02-21 22:24:12 +08:00
|
|
|
fail = False
|
|
|
|
|
2009-08-17 22:45:52 +08:00
|
|
|
def test_p1(self):
|
2020-02-21 22:24:12 +08:00
|
|
|
if self.fail: assert 0
|
|
|
|
""",
|
|
|
|
"tests/test_p2": """
|
2009-08-17 22:45:52 +08:00
|
|
|
from test_p1 import BaseTests
|
2020-02-21 22:24:12 +08:00
|
|
|
|
|
|
|
class TestMore(BaseTests): pass
|
|
|
|
""",
|
|
|
|
"tests/test_p3.py": """
|
|
|
|
from test_p1 import BaseTests
|
|
|
|
|
|
|
|
BaseTests.fail = True
|
|
|
|
|
|
|
|
class TestMore(BaseTests): pass
|
|
|
|
""",
|
|
|
|
}
|
2018-05-23 22:48:46 +08:00
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("tests/test_p2.py", "--rootdir=tests")
|
2020-02-21 22:24:12 +08:00
|
|
|
result.stdout.fnmatch_lines(["tests/test_p2.py .*", "=* 1 passed in *"])
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("-vv", "-rA", "tests/test_p2.py", "--rootdir=tests")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
2020-02-21 22:24:12 +08:00
|
|
|
[
|
|
|
|
"tests/test_p2.py::TestMore::test_p1 <- test_p1.py PASSED *",
|
|
|
|
"*= short test summary info =*",
|
|
|
|
"PASSED tests/test_p2.py::TestMore::test_p1",
|
|
|
|
]
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("-vv", "-rA", "tests/test_p3.py", "--rootdir=tests")
|
2020-02-21 22:24:12 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"tests/test_p3.py::TestMore::test_p1 <- test_p1.py FAILED *",
|
|
|
|
"*_ TestMore.test_p1 _*",
|
|
|
|
" def test_p1(self):",
|
|
|
|
"> if self.fail: assert 0",
|
|
|
|
"E assert 0",
|
|
|
|
"",
|
|
|
|
"tests/test_p1.py:5: AssertionError",
|
|
|
|
"*= short test summary info =*",
|
|
|
|
"FAILED tests/test_p3.py::TestMore::test_p1 - assert 0",
|
|
|
|
"*= 1 failed in *",
|
|
|
|
]
|
2018-05-23 22:48:46 +08:00
|
|
|
)
|
2009-08-17 22:45:52 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_itemreport_directclasses_not_shown_as_subclasses(
|
|
|
|
self, pytester: Pytester
|
|
|
|
) -> None:
|
|
|
|
a = pytester.mkpydir("a123")
|
|
|
|
a.joinpath("test_hello123.py").write_text(
|
2018-08-24 00:06:17 +08:00
|
|
|
textwrap.dedent(
|
|
|
|
"""\
|
|
|
|
class TestClass(object):
|
|
|
|
def test_method(self):
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("-vv")
|
2011-03-08 20:37:00 +08:00
|
|
|
assert result.ret == 0
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["*a123/test_hello123.py*PASS*"])
|
2019-10-06 01:18:51 +08:00
|
|
|
result.stdout.no_fnmatch_line("* <- *")
|
2011-03-08 20:37:00 +08:00
|
|
|
|
2019-11-04 04:01:37 +08:00
|
|
|
@pytest.mark.parametrize("fulltrace", ("", "--fulltrace"))
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_keyboard_interrupt(self, pytester: Pytester, fulltrace) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2009-07-17 22:19:19 +08:00
|
|
|
def test_foobar():
|
|
|
|
assert 0
|
|
|
|
def test_spamegg():
|
2010-11-18 05:12:16 +08:00
|
|
|
import py; pytest.skip('skip me please!')
|
2009-07-17 22:19:19 +08:00
|
|
|
def test_interrupt_me():
|
|
|
|
raise KeyboardInterrupt # simulating the user
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2009-07-18 00:07:37 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest(fulltrace, no_reraise_ctrlc=True)
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
" def test_foobar():",
|
|
|
|
"> assert 0",
|
|
|
|
"E assert 0",
|
|
|
|
"*_keyboard_interrupt.py:6: KeyboardInterrupt*",
|
|
|
|
]
|
|
|
|
)
|
2019-11-04 04:01:37 +08:00
|
|
|
if fulltrace:
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
["*raise KeyboardInterrupt # simulating the user*"]
|
|
|
|
)
|
2016-02-11 02:54:10 +08:00
|
|
|
else:
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
2019-07-28 03:06:29 +08:00
|
|
|
["(to show a full traceback on KeyboardInterrupt use --full-trace)"]
|
2018-05-23 22:48:46 +08:00
|
|
|
)
|
|
|
|
result.stdout.fnmatch_lines(["*KeyboardInterrupt*"])
|
2010-05-25 22:52:09 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_keyboard_in_sessionstart(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makeconftest(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2011-07-08 03:24:09 +08:00
|
|
|
def pytest_sessionstart():
|
|
|
|
raise KeyboardInterrupt
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2011-07-08 03:24:09 +08:00
|
|
|
def test_foobar():
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2011-07-08 03:24:09 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest(no_reraise_ctrlc=True)
|
2011-07-08 03:24:09 +08:00
|
|
|
assert result.ret == 2
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["*KeyboardInterrupt*"])
|
2010-01-13 04:43:25 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_collect_single_item(self, pytester: Pytester) -> None:
|
2017-06-04 05:42:26 +08:00
|
|
|
"""Use singular 'item' when reporting a single test item"""
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2017-06-04 05:42:26 +08:00
|
|
|
def test_foobar():
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["collected 1 item"])
|
2017-06-04 05:42:26 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_rewrite(self, pytester: Pytester, monkeypatch) -> None:
|
|
|
|
config = pytester.parseconfig()
|
2019-10-26 20:47:48 +08:00
|
|
|
f = StringIO()
|
2018-05-23 22:48:46 +08:00
|
|
|
monkeypatch.setattr(f, "isatty", lambda *args: True)
|
2017-08-04 07:54:33 +08:00
|
|
|
tr = TerminalReporter(config, f)
|
2017-11-24 05:26:57 +08:00
|
|
|
tr._tw.fullwidth = 10
|
2018-05-23 22:48:46 +08:00
|
|
|
tr.write("hello")
|
|
|
|
tr.rewrite("hey", erase=True)
|
|
|
|
assert f.getvalue() == "hello" + "\r" + "hey" + (6 * " ")
|
2017-08-04 07:54:33 +08:00
|
|
|
|
2020-04-04 19:00:15 +08:00
|
|
|
def test_report_teststatus_explicit_markup(
|
2020-12-09 04:20:02 +08:00
|
|
|
self, monkeypatch: MonkeyPatch, pytester: Pytester, color_mapping
|
2020-04-04 19:00:15 +08:00
|
|
|
) -> None:
|
|
|
|
"""Test that TerminalReporter handles markup explicitly provided by
|
|
|
|
a pytest_report_teststatus hook."""
|
2020-12-09 04:20:02 +08:00
|
|
|
monkeypatch.setenv("PY_COLORS", "1")
|
|
|
|
pytester.makeconftest(
|
2020-04-04 19:00:15 +08:00
|
|
|
"""
|
|
|
|
def pytest_report_teststatus(report):
|
|
|
|
return 'foo', 'F', ('FOO', {'red': True})
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2020-04-04 19:00:15 +08:00
|
|
|
"""
|
|
|
|
def test_foobar():
|
|
|
|
pass
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("-v")
|
2020-04-04 19:00:15 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
color_mapping.format_for_fnmatch(["*{red}FOO{reset}*"])
|
|
|
|
)
|
|
|
|
|
2020-05-10 09:09:09 +08:00
|
|
|
def test_verbose_skip_reason(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
|
|
|
"""
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
@pytest.mark.skip(reason="123")
|
|
|
|
def test_1():
|
|
|
|
pass
|
|
|
|
|
|
|
|
@pytest.mark.xfail(reason="456")
|
|
|
|
def test_2():
|
|
|
|
pass
|
|
|
|
|
|
|
|
@pytest.mark.xfail(reason="789")
|
|
|
|
def test_3():
|
|
|
|
assert False
|
2020-12-13 04:09:00 +08:00
|
|
|
|
|
|
|
@pytest.mark.xfail(reason="")
|
|
|
|
def test_4():
|
|
|
|
assert False
|
2020-12-16 02:16:28 +08:00
|
|
|
|
|
|
|
@pytest.mark.skip
|
|
|
|
def test_5():
|
|
|
|
pass
|
|
|
|
|
|
|
|
@pytest.mark.xfail
|
|
|
|
def test_6():
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_7():
|
|
|
|
pytest.skip()
|
|
|
|
|
|
|
|
def test_8():
|
|
|
|
pytest.skip("888 is great")
|
|
|
|
|
|
|
|
def test_9():
|
|
|
|
pytest.xfail()
|
|
|
|
|
|
|
|
def test_10():
|
|
|
|
pytest.xfail("It's 🕙 o'clock")
|
2020-05-10 09:09:09 +08:00
|
|
|
"""
|
|
|
|
)
|
|
|
|
result = pytester.runpytest("-v")
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"test_verbose_skip_reason.py::test_1 SKIPPED (123) *",
|
|
|
|
"test_verbose_skip_reason.py::test_2 XPASS (456) *",
|
|
|
|
"test_verbose_skip_reason.py::test_3 XFAIL (789) *",
|
2020-12-13 04:09:00 +08:00
|
|
|
"test_verbose_skip_reason.py::test_4 XFAIL *",
|
2020-12-16 02:16:28 +08:00
|
|
|
"test_verbose_skip_reason.py::test_5 SKIPPED (unconditional skip) *",
|
|
|
|
"test_verbose_skip_reason.py::test_6 XPASS *",
|
|
|
|
"test_verbose_skip_reason.py::test_7 SKIPPED *",
|
|
|
|
"test_verbose_skip_reason.py::test_8 SKIPPED (888 is great) *",
|
|
|
|
"test_verbose_skip_reason.py::test_9 XFAIL *",
|
|
|
|
"test_verbose_skip_reason.py::test_10 XFAIL (It's 🕙 o'clock) *",
|
2020-05-10 09:09:09 +08:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2009-07-17 22:19:19 +08:00
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
class TestCollectonly:
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_collectonly_basic(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2009-07-17 22:19:19 +08:00
|
|
|
def test_func():
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--collect-only")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
2018-11-26 01:33:18 +08:00
|
|
|
["<Module test_collectonly_basic.py>", " <Function test_func>"]
|
2018-05-23 22:48:46 +08:00
|
|
|
)
|
2009-07-17 22:19:19 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_collectonly_skipped_module(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2010-11-18 05:12:16 +08:00
|
|
|
import pytest
|
2011-03-07 01:32:00 +08:00
|
|
|
pytest.skip("hello")
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--collect-only", "-rs")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["*ERROR collecting*"])
|
2009-07-17 22:19:19 +08:00
|
|
|
|
2020-04-10 03:47:12 +08:00
|
|
|
def test_collectonly_displays_test_description(
|
2020-12-09 04:20:02 +08:00
|
|
|
self, pytester: Pytester, dummy_yaml_custom_test
|
2020-04-10 03:47:12 +08:00
|
|
|
) -> None:
|
|
|
|
"""Used dummy_yaml_custom_test for an Item without ``obj``."""
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2018-12-30 14:46:46 +08:00
|
|
|
"""
|
|
|
|
def test_with_description():
|
2020-04-10 03:47:12 +08:00
|
|
|
''' This test has a description.
|
|
|
|
|
|
|
|
more1.
|
|
|
|
more2.'''
|
|
|
|
"""
|
2018-12-30 14:46:46 +08:00
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--collect-only", "--verbose")
|
2020-04-10 03:47:12 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"<YamlFile test1.yaml>",
|
|
|
|
" <YamlItem test1.yaml>",
|
|
|
|
"<Module test_collectonly_displays_test_description.py>",
|
|
|
|
" <Function test_with_description>",
|
|
|
|
" This test has a description.",
|
|
|
|
" ",
|
|
|
|
" more1.",
|
|
|
|
" more2.",
|
|
|
|
],
|
|
|
|
consecutive=True,
|
|
|
|
)
|
2018-12-30 14:46:46 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_collectonly_failed_module(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile("""raise ValueError(0)""")
|
|
|
|
result = pytester.runpytest("--collect-only")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["*raise ValueError*", "*1 error*"])
|
2009-07-17 22:19:19 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_collectonly_fatal(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makeconftest(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2009-07-17 22:19:19 +08:00
|
|
|
def pytest_collectstart(collector):
|
2010-07-27 03:15:15 +08:00
|
|
|
assert 0, "urgs"
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--collect-only")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["*INTERNAL*args*"])
|
2009-07-17 22:19:19 +08:00
|
|
|
assert result.ret == 3
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_collectonly_simple(self, pytester: Pytester) -> None:
|
|
|
|
p = pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2009-08-03 21:27:26 +08:00
|
|
|
def test_func1():
|
|
|
|
pass
|
2017-02-17 02:41:51 +08:00
|
|
|
class TestClass(object):
|
2009-08-03 21:27:26 +08:00
|
|
|
def test_method(self):
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--collect-only", p)
|
2017-07-17 07:25:09 +08:00
|
|
|
# assert stderr.startswith("inserting into sys.path")
|
2009-08-03 21:27:26 +08:00
|
|
|
assert result.ret == 0
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
2018-11-26 01:33:18 +08:00
|
|
|
"*<Module *.py>",
|
|
|
|
"* <Function test_func1>",
|
|
|
|
"* <Class TestClass>",
|
|
|
|
"* <Function test_method>",
|
2018-05-23 22:48:46 +08:00
|
|
|
]
|
|
|
|
)
|
2009-08-03 21:27:26 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_collectonly_error(self, pytester: Pytester) -> None:
|
|
|
|
p = pytester.makepyfile("import Errlkjqweqwe")
|
|
|
|
result = pytester.runpytest("--collect-only", p)
|
2016-06-20 21:05:50 +08:00
|
|
|
assert result.ret == 2
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
2018-08-24 00:06:17 +08:00
|
|
|
textwrap.dedent(
|
|
|
|
"""\
|
|
|
|
*ERROR*
|
|
|
|
*ImportError*
|
|
|
|
*No module named *Errlk*
|
|
|
|
*1 error*
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
).strip()
|
|
|
|
)
|
2009-08-03 21:27:26 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_collectonly_missing_path(self, pytester: Pytester) -> None:
|
2020-07-18 17:35:13 +08:00
|
|
|
"""Issue 115: failure in parseargs will cause session not to
|
|
|
|
have the items attribute."""
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--collect-only", "uhm_missing_path")
|
2012-05-18 05:11:23 +08:00
|
|
|
assert result.ret == 4
|
2020-08-15 21:31:17 +08:00
|
|
|
result.stderr.fnmatch_lines(
|
|
|
|
["*ERROR: file or directory not found: uhm_missing_path"]
|
|
|
|
)
|
2012-02-03 23:33:32 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_collectonly_quiet(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile("def test_foo(): pass")
|
|
|
|
result = pytester.runpytest("--collect-only", "-q")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["*test_foo*"])
|
2012-02-03 23:56:06 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_collectonly_more_quiet(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(test_fun="def test_foo(): pass")
|
|
|
|
result = pytester.runpytest("--collect-only", "-qq")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["*test_fun.py: 1*"])
|
2012-02-03 23:56:06 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_collect_only_summary_status(self, pytester: Pytester) -> None:
|
2020-11-08 22:45:10 +08:00
|
|
|
"""Custom status depending on test selection using -k or -m. #7701."""
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2020-11-08 22:45:10 +08:00
|
|
|
test_collect_foo="""
|
|
|
|
def test_foo(): pass
|
|
|
|
""",
|
|
|
|
test_collect_bar="""
|
|
|
|
def test_foobar(): pass
|
|
|
|
def test_bar(): pass
|
|
|
|
""",
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--collect-only")
|
2020-11-08 22:45:10 +08:00
|
|
|
result.stdout.fnmatch_lines("*== 3 tests collected in * ==*")
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--collect-only", "test_collect_foo.py")
|
2020-11-08 22:45:10 +08:00
|
|
|
result.stdout.fnmatch_lines("*== 1 test collected in * ==*")
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--collect-only", "-k", "foo")
|
2020-11-08 22:45:10 +08:00
|
|
|
result.stdout.fnmatch_lines("*== 2/3 tests collected (1 deselected) in * ==*")
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--collect-only", "-k", "test_bar")
|
2020-11-08 22:45:10 +08:00
|
|
|
result.stdout.fnmatch_lines("*== 1/3 tests collected (2 deselected) in * ==*")
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--collect-only", "-k", "invalid")
|
2020-11-08 22:45:10 +08:00
|
|
|
result.stdout.fnmatch_lines("*== no tests collected (3 deselected) in * ==*")
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.mkdir("no_tests_here")
|
|
|
|
result = pytester.runpytest("--collect-only", "no_tests_here")
|
2020-11-08 22:45:10 +08:00
|
|
|
result.stdout.fnmatch_lines("*== no tests collected in * ==*")
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2020-11-08 22:45:10 +08:00
|
|
|
test_contains_error="""
|
|
|
|
raise RuntimeError
|
|
|
|
""",
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--collect-only")
|
2020-11-08 22:45:10 +08:00
|
|
|
result.stdout.fnmatch_lines("*== 3 tests collected, 1 error in * ==*")
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--collect-only", "-k", "foo")
|
2020-11-08 22:45:10 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
"*== 2/3 tests collected (1 deselected), 1 error in * ==*"
|
|
|
|
)
|
|
|
|
|
2009-08-03 21:27:26 +08:00
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
class TestFixtureReporting:
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_setup_fixture_error(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2009-07-31 20:22:02 +08:00
|
|
|
def setup_function(function):
|
2018-11-22 16:15:14 +08:00
|
|
|
print("setup func")
|
2009-07-31 20:22:02 +08:00
|
|
|
assert 0
|
|
|
|
def test_nada():
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"*ERROR at setup of test_nada*",
|
|
|
|
"*setup_function(function):*",
|
|
|
|
"*setup func*",
|
|
|
|
"*assert 0*",
|
|
|
|
"*1 error*",
|
|
|
|
]
|
|
|
|
)
|
2009-07-31 20:22:02 +08:00
|
|
|
assert result.ret != 0
|
2010-07-27 03:15:15 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_teardown_fixture_error(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2009-07-31 20:22:02 +08:00
|
|
|
def test_nada():
|
|
|
|
pass
|
|
|
|
def teardown_function(function):
|
2018-11-22 16:15:14 +08:00
|
|
|
print("teardown func")
|
2009-07-31 20:22:02 +08:00
|
|
|
assert 0
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"*ERROR at teardown*",
|
|
|
|
"*teardown_function(function):*",
|
|
|
|
"*assert 0*",
|
|
|
|
"*Captured stdout*",
|
|
|
|
"*teardown func*",
|
|
|
|
"*1 passed*1 error*",
|
|
|
|
]
|
|
|
|
)
|
2009-07-31 20:22:02 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_teardown_fixture_error_and_test_failure(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2009-07-31 20:22:02 +08:00
|
|
|
def test_fail():
|
|
|
|
assert 0, "failingfunc"
|
|
|
|
|
|
|
|
def teardown_function(function):
|
2018-11-22 16:15:14 +08:00
|
|
|
print("teardown func")
|
2009-07-31 20:22:02 +08:00
|
|
|
assert False
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"*ERROR at teardown of test_fail*",
|
|
|
|
"*teardown_function(function):*",
|
|
|
|
"*assert False*",
|
|
|
|
"*Captured stdout*",
|
|
|
|
"*teardown func*",
|
|
|
|
"*test_fail*",
|
|
|
|
"*def test_fail():",
|
|
|
|
"*failingfunc*",
|
|
|
|
"*1 failed*1 error*",
|
|
|
|
]
|
|
|
|
)
|
2009-08-03 21:27:26 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_setup_teardown_output_and_test_failure(self, pytester: Pytester) -> None:
|
2020-07-18 17:35:13 +08:00
|
|
|
"""Test for issue #442."""
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2016-10-30 16:46:08 +08:00
|
|
|
def setup_function(function):
|
2018-11-22 16:15:14 +08:00
|
|
|
print("setup func")
|
2016-10-30 16:46:08 +08:00
|
|
|
|
|
|
|
def test_fail():
|
|
|
|
assert 0, "failingfunc"
|
|
|
|
|
|
|
|
def teardown_function(function):
|
2018-11-22 16:15:14 +08:00
|
|
|
print("teardown func")
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"*test_fail*",
|
|
|
|
"*def test_fail():",
|
|
|
|
"*failingfunc*",
|
|
|
|
"*Captured stdout setup*",
|
|
|
|
"*setup func*",
|
|
|
|
"*Captured stdout teardown*",
|
|
|
|
"*teardown func*",
|
|
|
|
"*1 failed*",
|
|
|
|
]
|
|
|
|
)
|
2016-10-30 16:46:08 +08:00
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
class TestTerminalFunctional:
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_deselected(self, pytester: Pytester) -> None:
|
|
|
|
testpath = pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2009-08-03 21:27:26 +08:00
|
|
|
def test_one():
|
|
|
|
pass
|
|
|
|
def test_two():
|
|
|
|
pass
|
|
|
|
def test_three():
|
|
|
|
pass
|
|
|
|
"""
|
2018-05-23 22:48:46 +08:00
|
|
|
)
|
2021-11-15 05:39:56 +08:00
|
|
|
result = pytester.runpytest(
|
|
|
|
"-Wignore::pytest.PytestRemovedIn7Warning", "-k", "test_two:", testpath
|
|
|
|
)
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
2019-01-24 07:08:43 +08:00
|
|
|
["collected 3 items / 1 deselected / 2 selected", "*test_deselected.py ..*"]
|
2018-05-23 22:48:46 +08:00
|
|
|
)
|
2009-08-03 21:27:26 +08:00
|
|
|
assert result.ret == 0
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_deselected_with_hookwrapper(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makeconftest(
|
2019-04-15 01:54:18 +08:00
|
|
|
"""
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
@pytest.hookimpl(hookwrapper=True)
|
|
|
|
def pytest_collection_modifyitems(config, items):
|
|
|
|
yield
|
|
|
|
deselected = items.pop()
|
|
|
|
config.hook.pytest_deselected(items=[deselected])
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
testpath = pytester.makepyfile(
|
2019-04-15 01:54:18 +08:00
|
|
|
"""
|
|
|
|
def test_one():
|
|
|
|
pass
|
|
|
|
def test_two():
|
|
|
|
pass
|
|
|
|
def test_three():
|
|
|
|
pass
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest(testpath)
|
2019-04-15 01:54:18 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"collected 3 items / 1 deselected / 2 selected",
|
|
|
|
"*= 2 passed, 1 deselected in*",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
assert result.ret == 0
|
2009-08-03 21:27:26 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_show_deselected_items_using_markexpr_before_test_execution(
|
|
|
|
self, pytester: Pytester
|
|
|
|
) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-08-27 04:12:55 +08:00
|
|
|
test_show_deselected="""
|
2018-02-13 21:46:11 +08:00
|
|
|
import pytest
|
|
|
|
|
|
|
|
@pytest.mark.foo
|
|
|
|
def test_foobar():
|
|
|
|
pass
|
|
|
|
|
|
|
|
@pytest.mark.bar
|
|
|
|
def test_bar():
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_pass():
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("-m", "not foo")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
2019-01-24 07:08:43 +08:00
|
|
|
"collected 3 items / 1 deselected / 2 selected",
|
2018-08-27 04:12:55 +08:00
|
|
|
"*test_show_deselected.py ..*",
|
2018-05-23 22:48:46 +08:00
|
|
|
"*= 2 passed, 1 deselected in * =*",
|
|
|
|
]
|
|
|
|
)
|
2019-10-06 01:18:51 +08:00
|
|
|
result.stdout.no_fnmatch_line("*= 1 deselected =*")
|
2018-02-13 21:46:11 +08:00
|
|
|
assert result.ret == 0
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_no_skip_summary_if_failure(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2010-11-18 05:12:16 +08:00
|
|
|
import pytest
|
2009-08-03 21:27:26 +08:00
|
|
|
def test_ok():
|
|
|
|
pass
|
|
|
|
def test_fail():
|
|
|
|
assert 0
|
|
|
|
def test_skip():
|
2010-11-18 05:12:16 +08:00
|
|
|
pytest.skip("dontshow")
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2009-08-03 21:27:26 +08:00
|
|
|
assert result.stdout.str().find("skip test summary") == -1
|
|
|
|
assert result.ret == 1
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_passes(self, pytester: Pytester) -> None:
|
|
|
|
p1 = pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2009-08-03 21:27:26 +08:00
|
|
|
def test_passes():
|
|
|
|
pass
|
2017-02-17 02:41:51 +08:00
|
|
|
class TestClass(object):
|
2009-08-03 21:27:26 +08:00
|
|
|
def test_method(self):
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
old = p1.parent
|
|
|
|
pytester.chdir()
|
2009-08-03 21:27:26 +08:00
|
|
|
try:
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2009-08-03 21:27:26 +08:00
|
|
|
finally:
|
2020-12-09 04:20:02 +08:00
|
|
|
os.chdir(old)
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["test_passes.py ..*", "* 2 pass*"])
|
2009-08-03 21:27:26 +08:00
|
|
|
assert result.ret == 0
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_header_trailer_info(
|
|
|
|
self, monkeypatch: MonkeyPatch, pytester: Pytester, request
|
|
|
|
) -> None:
|
|
|
|
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2009-08-03 21:27:26 +08:00
|
|
|
def test_passes():
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2017-12-27 11:47:26 +08:00
|
|
|
verinfo = ".".join(map(str, sys.version_info[:3]))
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"*===== test session starts ====*",
|
2021-10-16 06:57:12 +08:00
|
|
|
"platform %s -- Python %s*pytest-%s**pluggy-%s"
|
2018-05-23 22:48:46 +08:00
|
|
|
% (
|
|
|
|
sys.platform,
|
|
|
|
verinfo,
|
|
|
|
pytest.__version__,
|
|
|
|
pluggy.__version__,
|
|
|
|
),
|
|
|
|
"*test_header_trailer_info.py .*",
|
2019-08-10 20:14:57 +08:00
|
|
|
"=* 1 passed*in *.[0-9][0-9]s *=",
|
2018-05-23 22:48:46 +08:00
|
|
|
]
|
|
|
|
)
|
2018-12-03 12:09:04 +08:00
|
|
|
if request.config.pluginmanager.list_plugin_distinfo():
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["plugins: *"])
|
2009-08-03 21:27:26 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_no_header_trailer_info(
|
|
|
|
self, monkeypatch: MonkeyPatch, pytester: Pytester, request
|
|
|
|
) -> None:
|
|
|
|
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
|
|
|
|
pytester.makepyfile(
|
2020-05-26 01:29:18 +08:00
|
|
|
"""
|
|
|
|
def test_passes():
|
|
|
|
pass
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--no-header")
|
2020-05-26 01:29:18 +08:00
|
|
|
verinfo = ".".join(map(str, sys.version_info[:3]))
|
|
|
|
result.stdout.no_fnmatch_line(
|
2021-10-16 06:57:12 +08:00
|
|
|
"platform %s -- Python %s*pytest-%s**pluggy-%s"
|
2020-05-26 01:36:57 +08:00
|
|
|
% (
|
|
|
|
sys.platform,
|
|
|
|
verinfo,
|
|
|
|
pytest.__version__,
|
|
|
|
pluggy.__version__,
|
|
|
|
)
|
2020-05-26 01:29:18 +08:00
|
|
|
)
|
|
|
|
if request.config.pluginmanager.list_plugin_distinfo():
|
|
|
|
result.stdout.no_fnmatch_line("plugins: *")
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_header(self, pytester: Pytester) -> None:
|
|
|
|
pytester.path.joinpath("tests").mkdir()
|
|
|
|
pytester.path.joinpath("gui").mkdir()
|
2019-03-02 22:31:09 +08:00
|
|
|
|
|
|
|
# no ini file
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2019-03-02 22:31:09 +08:00
|
|
|
result.stdout.fnmatch_lines(["rootdir: *test_header0"])
|
|
|
|
|
2020-06-08 21:03:10 +08:00
|
|
|
# with configfile
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makeini("""[pytest]""")
|
|
|
|
result = pytester.runpytest()
|
2020-06-08 21:03:10 +08:00
|
|
|
result.stdout.fnmatch_lines(["rootdir: *test_header0, configfile: tox.ini"])
|
2019-03-02 22:17:43 +08:00
|
|
|
|
2019-03-02 22:31:09 +08:00
|
|
|
# with testpaths option, and not passing anything in the command-line
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makeini(
|
2019-03-02 22:17:43 +08:00
|
|
|
"""
|
|
|
|
[pytest]
|
|
|
|
testpaths = tests gui
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2019-03-02 22:17:43 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
2020-06-08 21:03:10 +08:00
|
|
|
["rootdir: *test_header0, configfile: tox.ini, testpaths: tests, gui"]
|
2019-03-02 22:17:43 +08:00
|
|
|
)
|
|
|
|
|
2019-03-02 22:31:09 +08:00
|
|
|
# with testpaths option, passing directory in command-line: do not show testpaths then
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("tests")
|
2020-06-08 21:03:10 +08:00
|
|
|
result.stdout.fnmatch_lines(["rootdir: *test_header0, configfile: tox.ini"])
|
2019-03-02 22:17:43 +08:00
|
|
|
|
2020-09-29 14:29:27 +08:00
|
|
|
def test_header_absolute_testpath(
|
2020-12-09 04:20:02 +08:00
|
|
|
self, pytester: Pytester, monkeypatch: MonkeyPatch
|
2020-09-29 14:29:27 +08:00
|
|
|
) -> None:
|
|
|
|
"""Regresstion test for #7814."""
|
2020-12-09 04:20:02 +08:00
|
|
|
tests = pytester.path.joinpath("tests")
|
|
|
|
tests.mkdir()
|
|
|
|
pytester.makepyprojecttoml(
|
2020-09-29 14:29:27 +08:00
|
|
|
"""
|
|
|
|
[tool.pytest.ini_options]
|
|
|
|
testpaths = ['{}']
|
|
|
|
""".format(
|
|
|
|
tests
|
|
|
|
)
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2020-09-29 14:29:27 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"rootdir: *absolute_testpath0, configfile: pyproject.toml, testpaths: {}".format(
|
|
|
|
tests
|
|
|
|
)
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_no_header(self, pytester: Pytester) -> None:
|
|
|
|
pytester.path.joinpath("tests").mkdir()
|
|
|
|
pytester.path.joinpath("gui").mkdir()
|
2020-05-26 01:29:18 +08:00
|
|
|
|
|
|
|
# with testpaths option, and not passing anything in the command-line
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makeini(
|
2020-05-26 01:29:18 +08:00
|
|
|
"""
|
|
|
|
[pytest]
|
|
|
|
testpaths = tests gui
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--no-header")
|
2020-05-26 01:29:18 +08:00
|
|
|
result.stdout.no_fnmatch_line(
|
|
|
|
"rootdir: *test_header0, inifile: tox.ini, testpaths: tests, gui"
|
|
|
|
)
|
|
|
|
|
|
|
|
# with testpaths option, passing directory in command-line: do not show testpaths then
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("tests", "--no-header")
|
2020-05-26 01:29:18 +08:00
|
|
|
result.stdout.no_fnmatch_line("rootdir: *test_header0, inifile: tox.ini")
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_no_summary(self, pytester: Pytester) -> None:
|
|
|
|
p1 = pytester.makepyfile(
|
2020-05-26 01:29:18 +08:00
|
|
|
"""
|
|
|
|
def test_no_summary():
|
|
|
|
assert false
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest(p1, "--no-summary")
|
2020-05-26 01:29:18 +08:00
|
|
|
result.stdout.no_fnmatch_line("*= FAILURES =*")
|
2020-05-26 02:07:10 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_showlocals(self, pytester: Pytester) -> None:
|
|
|
|
p1 = pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2009-08-03 21:27:26 +08:00
|
|
|
def test_showlocals():
|
|
|
|
x = 3
|
2010-07-27 03:15:15 +08:00
|
|
|
y = "x" * 5000
|
2009-08-03 21:27:26 +08:00
|
|
|
assert 0
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest(p1, "-l")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
# "_ _ * Locals *",
|
|
|
|
"x* = 3",
|
|
|
|
"y* = 'xxxxxx*",
|
|
|
|
]
|
|
|
|
)
|
2009-08-03 21:27:26 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_showlocals_short(self, pytester: Pytester) -> None:
|
|
|
|
p1 = pytester.makepyfile(
|
2019-12-31 01:54:42 +08:00
|
|
|
"""
|
|
|
|
def test_showlocals_short():
|
|
|
|
x = 3
|
|
|
|
y = "xxxx"
|
|
|
|
assert 0
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest(p1, "-l", "--tb=short")
|
2019-12-31 01:54:42 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"test_showlocals_short.py:*",
|
|
|
|
" assert 0",
|
|
|
|
"E assert 0",
|
|
|
|
" x = 3",
|
|
|
|
" y = 'xxxx'",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2018-08-28 16:26:18 +08:00
|
|
|
@pytest.fixture
|
2020-12-09 04:20:02 +08:00
|
|
|
def verbose_testfile(self, pytester: Pytester) -> Path:
|
|
|
|
return pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2010-11-18 05:12:16 +08:00
|
|
|
import pytest
|
2009-08-03 21:27:26 +08:00
|
|
|
def test_fail():
|
|
|
|
raise ValueError()
|
|
|
|
def test_pass():
|
|
|
|
pass
|
2017-02-17 02:41:51 +08:00
|
|
|
class TestClass(object):
|
2009-08-03 21:27:26 +08:00
|
|
|
def test_skip(self):
|
2010-11-18 05:12:16 +08:00
|
|
|
pytest.skip("hello")
|
2009-08-03 21:27:26 +08:00
|
|
|
def test_gen():
|
|
|
|
def check(x):
|
|
|
|
assert x == 1
|
|
|
|
yield check, 0
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2018-08-28 16:26:18 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_verbose_reporting(self, verbose_testfile, pytester: Pytester) -> None:
|
|
|
|
result = pytester.runpytest(
|
2018-08-28 16:26:18 +08:00
|
|
|
verbose_testfile, "-v", "-Walways::pytest.PytestWarning"
|
|
|
|
)
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"*test_verbose_reporting.py::test_fail *FAIL*",
|
|
|
|
"*test_verbose_reporting.py::test_pass *PASS*",
|
|
|
|
"*test_verbose_reporting.py::TestClass::test_skip *SKIP*",
|
2019-01-23 13:26:30 +08:00
|
|
|
"*test_verbose_reporting.py::test_gen *XFAIL*",
|
2018-05-23 22:48:46 +08:00
|
|
|
]
|
|
|
|
)
|
2009-08-03 21:27:26 +08:00
|
|
|
assert result.ret == 1
|
2011-03-08 20:37:00 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_verbose_reporting_xdist(
|
|
|
|
self,
|
|
|
|
verbose_testfile,
|
|
|
|
monkeypatch: MonkeyPatch,
|
|
|
|
pytester: Pytester,
|
|
|
|
pytestconfig,
|
|
|
|
) -> None:
|
2015-04-26 02:17:32 +08:00
|
|
|
if not pytestconfig.pluginmanager.get_plugin("xdist"):
|
2015-04-22 19:31:46 +08:00
|
|
|
pytest.skip("xdist plugin not installed")
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD")
|
|
|
|
result = pytester.runpytest(
|
2018-08-28 16:26:18 +08:00
|
|
|
verbose_testfile, "-v", "-n 1", "-Walways::pytest.PytestWarning"
|
|
|
|
)
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
["*FAIL*test_verbose_reporting_xdist.py::test_fail*"]
|
|
|
|
)
|
2010-01-13 23:00:33 +08:00
|
|
|
assert result.ret == 1
|
2009-08-03 21:27:26 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_quiet_reporting(self, pytester: Pytester) -> None:
|
|
|
|
p1 = pytester.makepyfile("def test_pass(): pass")
|
|
|
|
result = pytester.runpytest(p1, "-q")
|
2010-11-01 02:51:16 +08:00
|
|
|
s = result.stdout.str()
|
2018-05-23 22:48:46 +08:00
|
|
|
assert "test session starts" not in s
|
2020-12-09 04:20:02 +08:00
|
|
|
assert p1.name not in s
|
2010-11-01 02:51:16 +08:00
|
|
|
assert "===" not in s
|
2013-07-06 21:43:59 +08:00
|
|
|
assert "passed" in s
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_more_quiet_reporting(self, pytester: Pytester) -> None:
|
|
|
|
p1 = pytester.makepyfile("def test_pass(): pass")
|
|
|
|
result = pytester.runpytest(p1, "-qq")
|
2013-07-06 21:43:59 +08:00
|
|
|
s = result.stdout.str()
|
2018-05-23 22:48:46 +08:00
|
|
|
assert "test session starts" not in s
|
2020-12-09 04:20:02 +08:00
|
|
|
assert p1.name not in s
|
2013-07-06 21:43:59 +08:00
|
|
|
assert "===" not in s
|
|
|
|
assert "passed" not in s
|
2017-07-27 21:34:49 +08:00
|
|
|
|
2019-02-06 07:29:30 +08:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"params", [(), ("--collect-only",)], ids=["no-params", "collect-only"]
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_report_collectionfinish_hook(self, pytester: Pytester, params) -> None:
|
|
|
|
pytester.makeconftest(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2021-03-15 04:20:53 +08:00
|
|
|
def pytest_report_collectionfinish(config, startpath, items):
|
|
|
|
return [f'hello from hook: {len(items)} items']
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2017-07-27 21:34:49 +08:00
|
|
|
import pytest
|
|
|
|
@pytest.mark.parametrize('i', range(3))
|
|
|
|
def test(i):
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest(*params)
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["collected 3 items", "hello from hook: 3 items"])
|
2013-07-06 21:43:59 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_summary_f_alias(self, pytester: Pytester) -> None:
|
2019-12-12 18:48:07 +08:00
|
|
|
"""Test that 'f' and 'F' report chars are aliases and don't show up twice in the summary (#6334)"""
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2019-12-12 18:41:23 +08:00
|
|
|
"""
|
|
|
|
def test():
|
|
|
|
assert False
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("-rfF")
|
2019-12-12 18:41:23 +08:00
|
|
|
expected = "FAILED test_summary_f_alias.py::test - assert False"
|
|
|
|
result.stdout.fnmatch_lines([expected])
|
|
|
|
assert result.stdout.lines.count(expected) == 1
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_summary_s_alias(self, pytester: Pytester) -> None:
|
2019-12-12 18:48:07 +08:00
|
|
|
"""Test that 's' and 'S' report chars are aliases and don't show up twice in the summary"""
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2019-12-12 18:48:07 +08:00
|
|
|
"""
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
@pytest.mark.skip
|
|
|
|
def test():
|
|
|
|
pass
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("-rsS")
|
2019-12-12 18:48:07 +08:00
|
|
|
expected = "SKIPPED [1] test_summary_s_alias.py:3: unconditional skip"
|
|
|
|
result.stdout.fnmatch_lines([expected])
|
|
|
|
assert result.stdout.lines.count(expected) == 1
|
|
|
|
|
2010-11-01 02:51:16 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_fail_extra_reporting(pytester: Pytester, monkeypatch) -> None:
|
2019-03-30 00:59:02 +08:00
|
|
|
monkeypatch.setenv("COLUMNS", "80")
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile("def test_this(): assert 0, 'this_failed' * 100")
|
|
|
|
result = pytester.runpytest("-rN")
|
2019-10-06 01:18:51 +08:00
|
|
|
result.stdout.no_fnmatch_line("*short test summary*")
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2019-03-30 00:59:02 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"*test summary*",
|
2019-04-05 18:17:08 +08:00
|
|
|
"FAILED test_fail_extra_reporting.py::test_this - AssertionError: this_failedt...",
|
2019-03-30 00:59:02 +08:00
|
|
|
]
|
|
|
|
)
|
2010-05-06 01:50:59 +08:00
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_fail_reporting_on_pass(pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile("def test_this(): assert 1")
|
|
|
|
result = pytester.runpytest("-rf")
|
2019-10-06 01:18:51 +08:00
|
|
|
result.stdout.no_fnmatch_line("*short test summary*")
|
2009-10-17 23:42:40 +08:00
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_pass_extra_reporting(pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile("def test_this(): assert 1")
|
|
|
|
result = pytester.runpytest()
|
2019-10-06 01:18:51 +08:00
|
|
|
result.stdout.no_fnmatch_line("*short test summary*")
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("-rp")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["*test summary*", "PASS*test_pass_extra_reporting*"])
|
2015-12-09 09:54:23 +08:00
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_pass_reporting_on_fail(pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile("def test_this(): assert 0")
|
|
|
|
result = pytester.runpytest("-rp")
|
2019-10-06 01:18:51 +08:00
|
|
|
result.stdout.no_fnmatch_line("*short test summary*")
|
2015-12-09 09:54:23 +08:00
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_pass_output_reporting(pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2020-01-12 04:41:38 +08:00
|
|
|
def setup_module():
|
|
|
|
print("setup_module")
|
|
|
|
|
|
|
|
def teardown_module():
|
|
|
|
print("teardown_module")
|
|
|
|
|
2018-09-26 21:44:00 +08:00
|
|
|
def test_pass_has_output():
|
2015-12-09 09:54:23 +08:00
|
|
|
print("Four score and seven years ago...")
|
2020-01-12 04:41:38 +08:00
|
|
|
|
2018-09-26 21:44:00 +08:00
|
|
|
def test_pass_no_output():
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2018-09-26 21:44:00 +08:00
|
|
|
s = result.stdout.str()
|
|
|
|
assert "test_pass_has_output" not in s
|
|
|
|
assert "Four score and seven years ago..." not in s
|
|
|
|
assert "test_pass_no_output" not in s
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("-rPp")
|
2018-09-26 21:44:00 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
2019-04-14 21:13:15 +08:00
|
|
|
[
|
|
|
|
"*= PASSES =*",
|
|
|
|
"*_ test_pass_has_output _*",
|
2020-01-12 04:41:38 +08:00
|
|
|
"*- Captured stdout setup -*",
|
|
|
|
"setup_module",
|
2019-04-14 21:13:15 +08:00
|
|
|
"*- Captured stdout call -*",
|
|
|
|
"Four score and seven years ago...",
|
2020-01-12 04:41:38 +08:00
|
|
|
"*- Captured stdout teardown -*",
|
|
|
|
"teardown_module",
|
2019-04-14 21:13:15 +08:00
|
|
|
"*= short test summary info =*",
|
|
|
|
"PASSED test_pass_output_reporting.py::test_pass_has_output",
|
|
|
|
"PASSED test_pass_output_reporting.py::test_pass_no_output",
|
|
|
|
"*= 2 passed in *",
|
|
|
|
]
|
2018-09-26 21:44:00 +08:00
|
|
|
)
|
2015-12-09 09:54:23 +08:00
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_color_yes(pytester: Pytester, color_mapping) -> None:
|
|
|
|
p1 = pytester.makepyfile(
|
2020-01-18 20:08:38 +08:00
|
|
|
"""
|
|
|
|
def fail():
|
|
|
|
assert 0
|
|
|
|
|
|
|
|
def test_this():
|
|
|
|
fail()
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--color=yes", str(p1))
|
2020-01-18 20:08:38 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
2020-02-12 19:32:37 +08:00
|
|
|
color_mapping.format_for_fnmatch(
|
|
|
|
[
|
2020-01-18 20:08:38 +08:00
|
|
|
"{bold}=*= test session starts =*={reset}",
|
|
|
|
"collected 1 item",
|
|
|
|
"",
|
|
|
|
"test_color_yes.py {red}F{reset}{red} * [100%]{reset}",
|
|
|
|
"",
|
|
|
|
"=*= FAILURES =*=",
|
|
|
|
"{red}{bold}_*_ test_this _*_{reset}",
|
|
|
|
"",
|
2020-02-12 19:32:37 +08:00
|
|
|
" {kw}def{hl-reset} {function}test_this{hl-reset}():",
|
|
|
|
"> fail()",
|
2020-01-18 20:08:38 +08:00
|
|
|
"",
|
|
|
|
"{bold}{red}test_color_yes.py{reset}:5: ",
|
|
|
|
"_ _ * _ _*",
|
|
|
|
"",
|
2020-02-12 19:32:37 +08:00
|
|
|
" {kw}def{hl-reset} {function}fail{hl-reset}():",
|
|
|
|
"> {kw}assert{hl-reset} {number}0{hl-reset}",
|
2020-01-18 20:08:38 +08:00
|
|
|
"{bold}{red}E assert 0{reset}",
|
|
|
|
"",
|
|
|
|
"{bold}{red}test_color_yes.py{reset}:2: AssertionError",
|
|
|
|
"{red}=*= {red}{bold}1 failed{reset}{red} in *s{reset}{red} =*={reset}",
|
|
|
|
]
|
2020-02-12 19:32:37 +08:00
|
|
|
)
|
2020-01-18 20:08:38 +08:00
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--color=yes", "--tb=short", str(p1))
|
2020-01-18 20:08:38 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
2020-02-12 19:32:37 +08:00
|
|
|
color_mapping.format_for_fnmatch(
|
|
|
|
[
|
2020-01-18 20:08:38 +08:00
|
|
|
"{bold}=*= test session starts =*={reset}",
|
|
|
|
"collected 1 item",
|
|
|
|
"",
|
|
|
|
"test_color_yes.py {red}F{reset}{red} * [100%]{reset}",
|
|
|
|
"",
|
|
|
|
"=*= FAILURES =*=",
|
|
|
|
"{red}{bold}_*_ test_this _*_{reset}",
|
|
|
|
"{bold}{red}test_color_yes.py{reset}:5: in test_this",
|
2020-02-12 19:32:37 +08:00
|
|
|
" fail()",
|
2020-01-18 20:08:38 +08:00
|
|
|
"{bold}{red}test_color_yes.py{reset}:2: in fail",
|
2020-02-12 19:32:37 +08:00
|
|
|
" {kw}assert{hl-reset} {number}0{hl-reset}",
|
2020-01-18 20:08:38 +08:00
|
|
|
"{bold}{red}E assert 0{reset}",
|
|
|
|
"{red}=*= {red}{bold}1 failed{reset}{red} in *s{reset}{red} =*={reset}",
|
|
|
|
]
|
2020-02-12 19:32:37 +08:00
|
|
|
)
|
2020-01-18 20:08:38 +08:00
|
|
|
)
|
2013-12-07 03:49:48 +08:00
|
|
|
|
2016-02-21 00:21:42 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_color_no(pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile("def test_this(): assert 1")
|
|
|
|
result = pytester.runpytest("--color=no")
|
2018-05-23 22:48:46 +08:00
|
|
|
assert "test session starts" in result.stdout.str()
|
2019-10-06 01:18:51 +08:00
|
|
|
result.stdout.no_fnmatch_line("*\x1b[1m*")
|
2013-12-07 03:49:48 +08:00
|
|
|
|
2016-02-21 00:21:42 +08:00
|
|
|
|
2018-05-23 22:48:46 +08:00
|
|
|
@pytest.mark.parametrize("verbose", [True, False])
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_color_yes_collection_on_non_atty(pytester: Pytester, verbose) -> None:
|
2020-07-18 17:35:13 +08:00
|
|
|
"""#1397: Skip collect progress report when working on non-terminals."""
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2016-02-21 00:21:42 +08:00
|
|
|
import pytest
|
|
|
|
@pytest.mark.parametrize('i', range(10))
|
|
|
|
def test_this(i):
|
|
|
|
assert 1
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
|
|
|
args = ["--color=yes"]
|
2016-02-21 00:21:42 +08:00
|
|
|
if verbose:
|
2018-05-23 22:48:46 +08:00
|
|
|
args.append("-vv")
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest(*args)
|
2018-05-23 22:48:46 +08:00
|
|
|
assert "test session starts" in result.stdout.str()
|
|
|
|
assert "\x1b[1m" in result.stdout.str()
|
2019-10-06 01:18:51 +08:00
|
|
|
result.stdout.no_fnmatch_line("*collecting 10 items*")
|
2016-02-21 00:21:42 +08:00
|
|
|
if verbose:
|
2018-05-23 22:48:46 +08:00
|
|
|
assert "collecting ..." in result.stdout.str()
|
|
|
|
assert "collected 10 items" in result.stdout.str()
|
2016-02-21 00:21:42 +08:00
|
|
|
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
def test_getreportopt() -> None:
|
2020-01-21 20:39:34 +08:00
|
|
|
from _pytest.terminal import _REPORTCHARS_DEFAULT
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
class FakeConfig:
|
2019-06-03 06:32:00 +08:00
|
|
|
class Option:
|
2020-01-21 20:39:34 +08:00
|
|
|
reportchars = _REPORTCHARS_DEFAULT
|
|
|
|
disable_warnings = False
|
2018-05-23 22:48:46 +08:00
|
|
|
|
2018-02-22 01:54:39 +08:00
|
|
|
option = Option()
|
2018-05-23 22:48:46 +08:00
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
config = cast(Config, FakeConfig())
|
2010-05-06 01:50:59 +08:00
|
|
|
|
2020-01-21 20:39:34 +08:00
|
|
|
assert _REPORTCHARS_DEFAULT == "fE"
|
|
|
|
|
|
|
|
# Default.
|
|
|
|
assert getreportopt(config) == "wfE"
|
|
|
|
|
2010-11-01 06:28:31 +08:00
|
|
|
config.option.reportchars = "sf"
|
2020-01-21 20:39:34 +08:00
|
|
|
assert getreportopt(config) == "wsf"
|
2010-05-06 01:50:59 +08:00
|
|
|
|
2016-06-26 00:16:13 +08:00
|
|
|
config.option.reportchars = "sfxw"
|
2020-01-21 20:39:34 +08:00
|
|
|
assert getreportopt(config) == "sfxw"
|
|
|
|
|
|
|
|
config.option.reportchars = "a"
|
|
|
|
assert getreportopt(config) == "wsxXEf"
|
|
|
|
|
|
|
|
config.option.reportchars = "N"
|
|
|
|
assert getreportopt(config) == "w"
|
|
|
|
|
|
|
|
config.option.reportchars = "NwfE"
|
|
|
|
assert getreportopt(config) == "wfE"
|
|
|
|
|
|
|
|
config.option.reportchars = "NfENx"
|
|
|
|
assert getreportopt(config) == "wx"
|
2009-10-17 23:42:40 +08:00
|
|
|
|
2019-04-08 00:04:09 +08:00
|
|
|
# Now with --disable-warnings.
|
2020-01-21 20:39:34 +08:00
|
|
|
config.option.disable_warnings = True
|
2019-04-08 00:04:09 +08:00
|
|
|
config.option.reportchars = "a"
|
2020-01-21 20:39:34 +08:00
|
|
|
assert getreportopt(config) == "sxXEf"
|
2019-04-08 00:04:09 +08:00
|
|
|
|
|
|
|
config.option.reportchars = "sfx"
|
2020-01-21 20:39:34 +08:00
|
|
|
assert getreportopt(config) == "sfx"
|
2016-06-26 00:16:13 +08:00
|
|
|
|
|
|
|
config.option.reportchars = "sfxw"
|
2020-01-21 20:39:34 +08:00
|
|
|
assert getreportopt(config) == "sfx"
|
2016-06-26 00:16:13 +08:00
|
|
|
|
2019-04-08 00:04:09 +08:00
|
|
|
config.option.reportchars = "a"
|
2020-01-21 20:39:34 +08:00
|
|
|
assert getreportopt(config) == "sxXEf"
|
2019-04-08 00:04:09 +08:00
|
|
|
|
2019-04-08 00:03:06 +08:00
|
|
|
config.option.reportchars = "A"
|
2020-01-21 20:39:34 +08:00
|
|
|
assert getreportopt(config) == "PpsxXEf"
|
|
|
|
|
|
|
|
config.option.reportchars = "AN"
|
|
|
|
assert getreportopt(config) == ""
|
|
|
|
|
|
|
|
config.option.reportchars = "NwfE"
|
|
|
|
assert getreportopt(config) == "fE"
|
2019-04-08 00:03:06 +08:00
|
|
|
|
2016-06-26 00:16:13 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_terminalreporter_reportopt_addopts(pytester: Pytester) -> None:
|
|
|
|
pytester.makeini("[pytest]\naddopts=-rs")
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2016-07-12 09:03:53 +08:00
|
|
|
import pytest
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def tr(request):
|
2009-10-17 23:42:40 +08:00
|
|
|
tr = request.config.pluginmanager.getplugin("terminalreporter")
|
|
|
|
return tr
|
|
|
|
def test_opt(tr):
|
|
|
|
assert tr.hasopt('skipped')
|
|
|
|
assert not tr.hasopt('qwe')
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["*1 passed*"])
|
2010-01-18 06:23:02 +08:00
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_tbstyle_short(pytester: Pytester) -> None:
|
|
|
|
p = pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2016-07-12 09:03:53 +08:00
|
|
|
import pytest
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def arg(request):
|
2010-05-22 22:18:24 +08:00
|
|
|
return 42
|
|
|
|
def test_opt(arg):
|
|
|
|
x = 0
|
|
|
|
assert x
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--tb=short")
|
2010-05-22 22:18:24 +08:00
|
|
|
s = result.stdout.str()
|
2018-05-23 22:48:46 +08:00
|
|
|
assert "arg = 42" not in s
|
|
|
|
assert "x = 0" not in s
|
2020-12-09 04:20:02 +08:00
|
|
|
result.stdout.fnmatch_lines(["*%s:8*" % p.name, " assert x", "E assert*"])
|
|
|
|
result = pytester.runpytest()
|
2010-05-22 22:18:24 +08:00
|
|
|
s = result.stdout.str()
|
2018-05-23 22:48:46 +08:00
|
|
|
assert "x = 0" in s
|
|
|
|
assert "assert x" in s
|
2010-05-22 22:18:24 +08:00
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_traceconfig(pytester: Pytester) -> None:
|
|
|
|
result = pytester.runpytest("--traceconfig")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["*active plugins*"])
|
2019-06-07 18:58:51 +08:00
|
|
|
assert result.ret == ExitCode.NO_TESTS_COLLECTED
|
2010-05-22 22:18:24 +08:00
|
|
|
|
2010-07-07 18:41:15 +08:00
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
class TestGenericReporting:
|
2020-07-18 17:35:13 +08:00
|
|
|
"""Test class which can be subclassed with a different option provider to
|
|
|
|
run e.g. distributed tests."""
|
2017-07-17 07:25:09 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_collect_fail(self, pytester: Pytester, option) -> None:
|
|
|
|
pytester.makepyfile("import xyz\n")
|
|
|
|
result = pytester.runpytest(*option.args)
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
["ImportError while importing*", "*No module named *xyz*", "*1 error*"]
|
|
|
|
)
|
2010-07-07 18:41:15 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_maxfailures(self, pytester: Pytester, option) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2010-07-07 18:41:15 +08:00
|
|
|
def test_1():
|
|
|
|
assert 0
|
|
|
|
def test_2():
|
|
|
|
assert 0
|
|
|
|
def test_3():
|
|
|
|
assert 0
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--maxfail=2", *option.args)
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
2019-11-13 22:55:11 +08:00
|
|
|
[
|
|
|
|
"*def test_1():*",
|
|
|
|
"*def test_2():*",
|
|
|
|
"*! stopping after 2 failures !*",
|
|
|
|
"*2 failed*",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_maxfailures_with_interrupted(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2019-11-13 22:55:11 +08:00
|
|
|
"""
|
|
|
|
def test(request):
|
|
|
|
request.session.shouldstop = "session_interrupted"
|
|
|
|
assert 0
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--maxfail=1", "-ra")
|
2019-11-13 22:55:11 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"*= short test summary info =*",
|
|
|
|
"FAILED *",
|
|
|
|
"*! stopping after 1 failures !*",
|
|
|
|
"*! session_interrupted !*",
|
|
|
|
"*= 1 failed in*",
|
|
|
|
]
|
2018-05-23 22:48:46 +08:00
|
|
|
)
|
2010-07-07 18:41:15 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_tb_option(self, pytester: Pytester, option) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2010-11-18 05:12:16 +08:00
|
|
|
import pytest
|
2010-07-07 18:41:15 +08:00
|
|
|
def g():
|
|
|
|
raise IndexError
|
|
|
|
def test_func():
|
2018-11-22 16:15:14 +08:00
|
|
|
print(6*7)
|
2010-07-07 18:41:15 +08:00
|
|
|
g() # --calling--
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2010-07-07 18:41:15 +08:00
|
|
|
for tbopt in ["long", "short", "no"]:
|
2018-05-23 22:48:46 +08:00
|
|
|
print("testing --tb=%s..." % tbopt)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("-rN", "--tb=%s" % tbopt)
|
2010-07-07 18:41:15 +08:00
|
|
|
s = result.stdout.str()
|
|
|
|
if tbopt == "long":
|
2018-11-22 16:15:14 +08:00
|
|
|
assert "print(6*7)" in s
|
2010-07-07 18:41:15 +08:00
|
|
|
else:
|
2018-11-22 16:15:14 +08:00
|
|
|
assert "print(6*7)" not in s
|
2010-07-07 18:41:15 +08:00
|
|
|
if tbopt != "no":
|
2018-05-23 22:48:46 +08:00
|
|
|
assert "--calling--" in s
|
|
|
|
assert "IndexError" in s
|
2010-07-07 18:41:15 +08:00
|
|
|
else:
|
2018-05-23 22:48:46 +08:00
|
|
|
assert "FAILURES" not in s
|
|
|
|
assert "--calling--" not in s
|
|
|
|
assert "IndexError" not in s
|
2010-07-07 18:41:15 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_tb_crashline(self, pytester: Pytester, option) -> None:
|
|
|
|
p = pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2010-11-18 05:12:16 +08:00
|
|
|
import pytest
|
2010-07-07 18:41:15 +08:00
|
|
|
def g():
|
|
|
|
raise IndexError
|
|
|
|
def test_func1():
|
2018-11-22 16:15:14 +08:00
|
|
|
print(6*7)
|
2010-07-07 18:41:15 +08:00
|
|
|
g() # --calling--
|
|
|
|
def test_func2():
|
|
|
|
assert 0, "hello"
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--tb=line")
|
|
|
|
bn = p.name
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
["*%s:3: IndexError*" % bn, "*%s:8: AssertionError: hello*" % bn]
|
|
|
|
)
|
2010-07-07 18:41:15 +08:00
|
|
|
s = result.stdout.str()
|
|
|
|
assert "def test_func2" not in s
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_pytest_report_header(self, pytester: Pytester, option) -> None:
|
|
|
|
pytester.makeconftest(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2012-06-21 17:07:22 +08:00
|
|
|
def pytest_sessionstart(session):
|
|
|
|
session.config._somevalue = 42
|
2010-07-07 18:41:15 +08:00
|
|
|
def pytest_report_header(config):
|
2012-06-21 17:07:22 +08:00
|
|
|
return "hello: %s" % config._somevalue
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.mkdir("a").joinpath("conftest.py").write_text(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2021-03-15 04:20:53 +08:00
|
|
|
def pytest_report_header(config, startpath):
|
2020-12-15 00:16:14 +08:00
|
|
|
return ["line1", str(startpath)]
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("a")
|
|
|
|
result.stdout.fnmatch_lines(["*hello: 42*", "line1", str(pytester.path)])
|
2010-10-05 23:21:50 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_show_capture(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2018-02-07 04:38:51 +08:00
|
|
|
import sys
|
2018-02-18 19:42:25 +08:00
|
|
|
import logging
|
2018-02-01 03:36:28 +08:00
|
|
|
def test_one():
|
2018-02-07 04:38:51 +08:00
|
|
|
sys.stdout.write('!This is stdout!')
|
|
|
|
sys.stderr.write('!This is stderr!')
|
2018-02-18 19:42:25 +08:00
|
|
|
logging.warning('!This is a warning log msg!')
|
2018-02-01 03:36:28 +08:00
|
|
|
assert False, 'Something failed'
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2018-02-01 03:36:28 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--tb=short")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"!This is stdout!",
|
|
|
|
"!This is stderr!",
|
|
|
|
"*WARNING*!This is a warning log msg!",
|
|
|
|
]
|
|
|
|
)
|
2018-02-18 19:42:25 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--show-capture=all", "--tb=short")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"!This is stdout!",
|
|
|
|
"!This is stderr!",
|
|
|
|
"*WARNING*!This is a warning log msg!",
|
|
|
|
]
|
|
|
|
)
|
2018-02-18 19:42:25 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
stdout = pytester.runpytest("--show-capture=stdout", "--tb=short").stdout.str()
|
2018-02-18 19:42:25 +08:00
|
|
|
assert "!This is stderr!" not in stdout
|
|
|
|
assert "!This is stdout!" in stdout
|
|
|
|
assert "!This is a warning log msg!" not in stdout
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
stdout = pytester.runpytest("--show-capture=stderr", "--tb=short").stdout.str()
|
2018-02-18 19:42:25 +08:00
|
|
|
assert "!This is stdout!" not in stdout
|
|
|
|
assert "!This is stderr!" in stdout
|
|
|
|
assert "!This is a warning log msg!" not in stdout
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
stdout = pytester.runpytest("--show-capture=log", "--tb=short").stdout.str()
|
2018-02-18 19:42:25 +08:00
|
|
|
assert "!This is stdout!" not in stdout
|
|
|
|
assert "!This is stderr!" not in stdout
|
|
|
|
assert "!This is a warning log msg!" in stdout
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
stdout = pytester.runpytest("--show-capture=no", "--tb=short").stdout.str()
|
2018-02-18 19:42:25 +08:00
|
|
|
assert "!This is stdout!" not in stdout
|
|
|
|
assert "!This is stderr!" not in stdout
|
|
|
|
assert "!This is a warning log msg!" not in stdout
|
2018-02-08 21:21:22 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_show_capture_with_teardown_logs(self, pytester: Pytester) -> None:
|
2018-08-21 03:25:01 +08:00
|
|
|
"""Ensure that the capturing of teardown logs honor --show-capture setting"""
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2018-08-21 03:25:01 +08:00
|
|
|
"""
|
|
|
|
import logging
|
|
|
|
import sys
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
@pytest.fixture(scope="function", autouse="True")
|
|
|
|
def hook_each_test(request):
|
|
|
|
yield
|
|
|
|
sys.stdout.write("!stdout!")
|
|
|
|
sys.stderr.write("!stderr!")
|
|
|
|
logging.warning("!log!")
|
|
|
|
|
|
|
|
def test_func():
|
|
|
|
assert False
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--show-capture=stdout", "--tb=short").stdout.str()
|
2018-08-21 03:25:01 +08:00
|
|
|
assert "!stdout!" in result
|
|
|
|
assert "!stderr!" not in result
|
|
|
|
assert "!log!" not in result
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--show-capture=stderr", "--tb=short").stdout.str()
|
2018-08-21 03:25:01 +08:00
|
|
|
assert "!stdout!" not in result
|
|
|
|
assert "!stderr!" in result
|
|
|
|
assert "!log!" not in result
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--show-capture=log", "--tb=short").stdout.str()
|
2018-08-21 03:25:01 +08:00
|
|
|
assert "!stdout!" not in result
|
|
|
|
assert "!stderr!" not in result
|
|
|
|
assert "!log!" in result
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--show-capture=no", "--tb=short").stdout.str()
|
2018-08-21 03:25:01 +08:00
|
|
|
assert "!stdout!" not in result
|
|
|
|
assert "!stderr!" not in result
|
|
|
|
assert "!log!" not in result
|
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
2010-11-18 05:12:16 +08:00
|
|
|
@pytest.mark.xfail("not hasattr(os, 'dup')")
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_fdopen_kept_alive_issue124(pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2010-10-05 23:21:50 +08:00
|
|
|
import os, sys
|
|
|
|
k = []
|
|
|
|
def test_open_file_and_keep_alive(capfd):
|
|
|
|
stdout = os.fdopen(1, 'w', 1)
|
|
|
|
k.append(stdout)
|
|
|
|
|
|
|
|
def test_close_kept_alive_file():
|
|
|
|
stdout = k.pop()
|
|
|
|
stdout.close()
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(["*2 passed*"])
|
2013-05-08 23:01:20 +08:00
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_tbstyle_native_setup_error(pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2013-05-08 23:01:20 +08:00
|
|
|
import pytest
|
|
|
|
@pytest.fixture
|
|
|
|
def setup_error_fixture():
|
|
|
|
raise Exception("error in exception")
|
2013-09-27 15:49:39 +08:00
|
|
|
|
2013-05-08 23:01:20 +08:00
|
|
|
def test_error_fixture(setup_error_fixture):
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--tb=native")
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
['*File *test_tbstyle_native_setup_error.py", line *, in setup_error_fixture*']
|
|
|
|
)
|
2013-09-27 21:48:03 +08:00
|
|
|
|
2017-07-17 07:25:09 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_terminal_summary(pytester: Pytester) -> None:
|
|
|
|
pytester.makeconftest(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2016-08-14 21:02:35 +08:00
|
|
|
def pytest_terminal_summary(terminalreporter, exitstatus):
|
2013-09-27 21:48:03 +08:00
|
|
|
w = terminalreporter
|
|
|
|
w.section("hello")
|
|
|
|
w.line("world")
|
2016-08-14 21:02:35 +08:00
|
|
|
w.line("exitstatus: {0}".format(exitstatus))
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
"""
|
2013-09-27 21:48:03 +08:00
|
|
|
*==== hello ====*
|
|
|
|
world
|
2016-08-14 21:02:35 +08:00
|
|
|
exitstatus: 5
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2015-07-03 01:03:05 +08:00
|
|
|
|
2016-01-04 10:07:45 +08:00
|
|
|
|
2021-05-04 20:27:21 +08:00
|
|
|
@pytest.mark.filterwarnings("default::UserWarning")
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_terminal_summary_warnings_are_displayed(pytester: Pytester) -> None:
|
2016-01-04 10:07:45 +08:00
|
|
|
"""Test that warnings emitted during pytest_terminal_summary are displayed.
|
|
|
|
(#1305).
|
|
|
|
"""
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makeconftest(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2018-09-02 04:10:26 +08:00
|
|
|
import warnings
|
2016-01-04 10:07:45 +08:00
|
|
|
def pytest_terminal_summary(terminalreporter):
|
2018-09-02 04:10:26 +08:00
|
|
|
warnings.warn(UserWarning('internal warning'))
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2018-11-16 00:05:56 +08:00
|
|
|
"""
|
|
|
|
def test_failure():
|
|
|
|
import warnings
|
|
|
|
warnings.warn("warning_from_" + "test")
|
|
|
|
assert 0
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("-ra")
|
2018-06-11 01:51:36 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
2018-11-16 00:05:56 +08:00
|
|
|
[
|
|
|
|
"*= warnings summary =*",
|
|
|
|
"*warning_from_test*",
|
|
|
|
"*= short test summary info =*",
|
|
|
|
"*= warnings summary (final) =*",
|
|
|
|
"*conftest.py:3:*internal warning",
|
|
|
|
"*== 1 failed, 2 warnings in *",
|
|
|
|
]
|
2018-06-11 01:51:36 +08:00
|
|
|
)
|
2019-10-06 01:18:51 +08:00
|
|
|
result.stdout.no_fnmatch_line("*None*")
|
2018-11-24 05:39:05 +08:00
|
|
|
stdout = result.stdout.str()
|
|
|
|
assert stdout.count("warning_from_test") == 1
|
|
|
|
assert stdout.count("=== warnings summary ") == 2
|
|
|
|
|
|
|
|
|
2021-05-04 20:27:21 +08:00
|
|
|
@pytest.mark.filterwarnings("default::UserWarning")
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_terminal_summary_warnings_header_once(pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-11-24 05:39:05 +08:00
|
|
|
"""
|
|
|
|
def test_failure():
|
|
|
|
import warnings
|
|
|
|
warnings.warn("warning_from_" + "test")
|
|
|
|
assert 0
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("-ra")
|
2018-11-24 05:39:05 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"*= warnings summary =*",
|
|
|
|
"*warning_from_test*",
|
|
|
|
"*= short test summary info =*",
|
2019-10-27 23:02:37 +08:00
|
|
|
"*== 1 failed, 1 warning in *",
|
2018-11-24 05:39:05 +08:00
|
|
|
]
|
|
|
|
)
|
2019-10-06 01:18:51 +08:00
|
|
|
result.stdout.no_fnmatch_line("*None*")
|
2018-11-24 05:39:05 +08:00
|
|
|
stdout = result.stdout.str()
|
|
|
|
assert stdout.count("warning_from_test") == 1
|
|
|
|
assert stdout.count("=== warnings summary ") == 1
|
2018-05-23 22:48:46 +08:00
|
|
|
|
|
|
|
|
2020-05-26 01:29:18 +08:00
|
|
|
@pytest.mark.filterwarnings("default")
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_terminal_no_summary_warnings_header_once(pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2020-05-26 01:29:18 +08:00
|
|
|
"""
|
|
|
|
def test_failure():
|
|
|
|
import warnings
|
|
|
|
warnings.warn("warning_from_" + "test")
|
|
|
|
assert 0
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--no-summary")
|
2020-05-26 01:29:18 +08:00
|
|
|
result.stdout.no_fnmatch_line("*= warnings summary =*")
|
|
|
|
result.stdout.no_fnmatch_line("*= short test summary info =*")
|
|
|
|
|
|
|
|
|
2020-01-06 22:09:09 +08:00
|
|
|
@pytest.fixture(scope="session")
|
2020-01-25 21:49:48 +08:00
|
|
|
def tr() -> TerminalReporter:
|
2020-01-06 22:09:09 +08:00
|
|
|
config = _pytest.config._prepareconfig()
|
|
|
|
return TerminalReporter(config)
|
|
|
|
|
|
|
|
|
2018-05-23 22:48:46 +08:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"exp_color, exp_line, stats_arg",
|
|
|
|
[
|
|
|
|
# The method under test only cares about the length of each
|
|
|
|
# dict value, not the actual contents, so tuples of anything
|
|
|
|
# suffice
|
|
|
|
# Important statuses -- the highest priority of these always wins
|
2020-08-01 18:06:13 +08:00
|
|
|
("red", [("1 failed", {"bold": True, "red": True})], {"failed": [1]}),
|
2019-04-06 23:09:48 +08:00
|
|
|
(
|
|
|
|
"red",
|
|
|
|
[
|
|
|
|
("1 failed", {"bold": True, "red": True}),
|
|
|
|
("1 passed", {"bold": False, "green": True}),
|
|
|
|
],
|
2020-08-01 18:06:13 +08:00
|
|
|
{"failed": [1], "passed": [1]},
|
2019-04-06 23:09:48 +08:00
|
|
|
),
|
2020-08-01 18:06:13 +08:00
|
|
|
("red", [("1 error", {"bold": True, "red": True})], {"error": [1]}),
|
|
|
|
("red", [("2 errors", {"bold": True, "red": True})], {"error": [1, 2]}),
|
2019-04-06 23:09:48 +08:00
|
|
|
(
|
|
|
|
"red",
|
|
|
|
[
|
|
|
|
("1 passed", {"bold": False, "green": True}),
|
|
|
|
("1 error", {"bold": True, "red": True}),
|
|
|
|
],
|
2020-08-01 18:06:13 +08:00
|
|
|
{"error": [1], "passed": [1]},
|
2019-04-06 23:09:48 +08:00
|
|
|
),
|
2018-05-23 22:48:46 +08:00
|
|
|
# (a status that's not known to the code)
|
2020-08-01 18:06:13 +08:00
|
|
|
("yellow", [("1 weird", {"bold": True, "yellow": True})], {"weird": [1]}),
|
2019-04-06 23:09:48 +08:00
|
|
|
(
|
|
|
|
"yellow",
|
|
|
|
[
|
|
|
|
("1 passed", {"bold": False, "green": True}),
|
|
|
|
("1 weird", {"bold": True, "yellow": True}),
|
|
|
|
],
|
2020-08-01 18:06:13 +08:00
|
|
|
{"weird": [1], "passed": [1]},
|
2019-04-06 23:09:48 +08:00
|
|
|
),
|
2020-08-01 18:06:13 +08:00
|
|
|
("yellow", [("1 warning", {"bold": True, "yellow": True})], {"warnings": [1]}),
|
2019-04-06 23:09:48 +08:00
|
|
|
(
|
|
|
|
"yellow",
|
|
|
|
[
|
|
|
|
("1 passed", {"bold": False, "green": True}),
|
2019-10-27 23:02:37 +08:00
|
|
|
("1 warning", {"bold": True, "yellow": True}),
|
2019-04-06 23:09:48 +08:00
|
|
|
],
|
2020-08-01 18:06:13 +08:00
|
|
|
{"warnings": [1], "passed": [1]},
|
2019-04-06 23:09:48 +08:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"green",
|
|
|
|
[("5 passed", {"bold": True, "green": True})],
|
2020-08-01 18:06:13 +08:00
|
|
|
{"passed": [1, 2, 3, 4, 5]},
|
2019-04-06 23:09:48 +08:00
|
|
|
),
|
2018-05-23 22:48:46 +08:00
|
|
|
# "Boring" statuses. These have no effect on the color of the summary
|
|
|
|
# line. Thus, if *every* test has a boring status, the summary line stays
|
|
|
|
# at its default color, i.e. yellow, to warn the user that the test run
|
|
|
|
# produced no useful information
|
2020-08-01 18:06:13 +08:00
|
|
|
("yellow", [("1 skipped", {"bold": True, "yellow": True})], {"skipped": [1]}),
|
2019-04-06 23:09:48 +08:00
|
|
|
(
|
|
|
|
"green",
|
|
|
|
[
|
|
|
|
("1 passed", {"bold": True, "green": True}),
|
|
|
|
("1 skipped", {"bold": False, "yellow": True}),
|
|
|
|
],
|
2020-08-01 18:06:13 +08:00
|
|
|
{"skipped": [1], "passed": [1]},
|
2019-04-06 23:09:48 +08:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"yellow",
|
|
|
|
[("1 deselected", {"bold": True, "yellow": True})],
|
2020-08-01 18:06:13 +08:00
|
|
|
{"deselected": [1]},
|
2019-04-06 23:09:48 +08:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"green",
|
|
|
|
[
|
|
|
|
("1 passed", {"bold": True, "green": True}),
|
|
|
|
("1 deselected", {"bold": False, "yellow": True}),
|
|
|
|
],
|
2020-08-01 18:06:13 +08:00
|
|
|
{"deselected": [1], "passed": [1]},
|
2019-04-06 23:09:48 +08:00
|
|
|
),
|
2020-08-01 18:06:13 +08:00
|
|
|
("yellow", [("1 xfailed", {"bold": True, "yellow": True})], {"xfailed": [1]}),
|
2019-04-06 23:09:48 +08:00
|
|
|
(
|
|
|
|
"green",
|
|
|
|
[
|
|
|
|
("1 passed", {"bold": True, "green": True}),
|
|
|
|
("1 xfailed", {"bold": False, "yellow": True}),
|
|
|
|
],
|
2020-08-01 18:06:13 +08:00
|
|
|
{"xfailed": [1], "passed": [1]},
|
2019-04-06 23:09:48 +08:00
|
|
|
),
|
2020-08-01 18:06:13 +08:00
|
|
|
("yellow", [("1 xpassed", {"bold": True, "yellow": True})], {"xpassed": [1]}),
|
2019-04-06 23:09:48 +08:00
|
|
|
(
|
2020-01-06 22:09:09 +08:00
|
|
|
"yellow",
|
2019-04-06 23:09:48 +08:00
|
|
|
[
|
2020-01-06 22:09:09 +08:00
|
|
|
("1 passed", {"bold": False, "green": True}),
|
|
|
|
("1 xpassed", {"bold": True, "yellow": True}),
|
2019-04-06 23:09:48 +08:00
|
|
|
],
|
2020-08-01 18:06:13 +08:00
|
|
|
{"xpassed": [1], "passed": [1]},
|
2019-04-06 23:09:48 +08:00
|
|
|
),
|
2018-05-23 22:48:46 +08:00
|
|
|
# Likewise if no tests were found at all
|
2019-04-06 23:09:48 +08:00
|
|
|
("yellow", [("no tests ran", {"yellow": True})], {}),
|
2018-05-23 22:48:46 +08:00
|
|
|
# Test the empty-key special case
|
2020-08-01 18:06:13 +08:00
|
|
|
("yellow", [("no tests ran", {"yellow": True})], {"": [1]}),
|
2019-04-06 23:09:48 +08:00
|
|
|
(
|
|
|
|
"green",
|
|
|
|
[("1 passed", {"bold": True, "green": True})],
|
2020-08-01 18:06:13 +08:00
|
|
|
{"": [1], "passed": [1]},
|
2019-04-06 23:09:48 +08:00
|
|
|
),
|
2018-05-23 22:48:46 +08:00
|
|
|
# A couple more complex combinations
|
|
|
|
(
|
|
|
|
"red",
|
2019-04-06 23:09:48 +08:00
|
|
|
[
|
|
|
|
("1 failed", {"bold": True, "red": True}),
|
|
|
|
("2 passed", {"bold": False, "green": True}),
|
|
|
|
("3 xfailed", {"bold": False, "yellow": True}),
|
|
|
|
],
|
2020-08-01 18:06:13 +08:00
|
|
|
{"passed": [1, 2], "failed": [1], "xfailed": [1, 2, 3]},
|
2018-05-23 22:48:46 +08:00
|
|
|
),
|
|
|
|
(
|
|
|
|
"green",
|
2019-04-06 23:09:48 +08:00
|
|
|
[
|
|
|
|
("1 passed", {"bold": True, "green": True}),
|
|
|
|
("2 skipped", {"bold": False, "yellow": True}),
|
|
|
|
("3 deselected", {"bold": False, "yellow": True}),
|
|
|
|
("2 xfailed", {"bold": False, "yellow": True}),
|
|
|
|
],
|
2018-05-23 22:48:46 +08:00
|
|
|
{
|
2020-08-01 18:06:13 +08:00
|
|
|
"passed": [1],
|
|
|
|
"skipped": [1, 2],
|
|
|
|
"deselected": [1, 2, 3],
|
|
|
|
"xfailed": [1, 2],
|
2018-05-23 22:48:46 +08:00
|
|
|
},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
2020-01-25 21:49:48 +08:00
|
|
|
def test_summary_stats(
|
|
|
|
tr: TerminalReporter,
|
|
|
|
exp_line: List[Tuple[str, Dict[str, bool]]],
|
|
|
|
exp_color: str,
|
2020-08-01 18:06:13 +08:00
|
|
|
stats_arg: Dict[str, List[object]],
|
2020-01-25 21:49:48 +08:00
|
|
|
) -> None:
|
2020-01-06 22:09:09 +08:00
|
|
|
tr.stats = stats_arg
|
|
|
|
|
|
|
|
# Fake "_is_last_item" to be True.
|
|
|
|
class fake_session:
|
|
|
|
testscollected = 0
|
|
|
|
|
2020-07-10 14:44:14 +08:00
|
|
|
tr._session = fake_session # type: ignore[assignment]
|
2020-01-06 22:09:09 +08:00
|
|
|
assert tr._is_last_item
|
|
|
|
|
|
|
|
# Reset cache.
|
|
|
|
tr._main_color = None
|
|
|
|
|
2015-07-01 06:48:49 +08:00
|
|
|
print("Based on stats: %s" % stats_arg)
|
2020-10-03 04:16:22 +08:00
|
|
|
print(f'Expect summary: "{exp_line}"; with color "{exp_color}"')
|
2020-01-06 22:09:09 +08:00
|
|
|
(line, color) = tr.build_summary_stats_line()
|
2020-10-03 04:16:22 +08:00
|
|
|
print(f'Actually got: "{line}"; with color "{color}"')
|
2015-07-03 01:03:05 +08:00
|
|
|
assert line == exp_line
|
|
|
|
assert color == exp_color
|
2017-03-06 03:44:13 +08:00
|
|
|
|
|
|
|
|
2020-01-06 22:09:09 +08:00
|
|
|
def test_skip_counting_towards_summary(tr):
|
2019-03-07 05:54:45 +08:00
|
|
|
class DummyReport(BaseReport):
|
|
|
|
count_towards_summary = True
|
|
|
|
|
|
|
|
r1 = DummyReport()
|
|
|
|
r2 = DummyReport()
|
2020-01-06 22:09:09 +08:00
|
|
|
tr.stats = {"failed": (r1, r2)}
|
|
|
|
tr._main_color = None
|
|
|
|
res = tr.build_summary_stats_line()
|
2019-04-06 23:09:48 +08:00
|
|
|
assert res == ([("2 failed", {"bold": True, "red": True})], "red")
|
2019-03-07 05:54:45 +08:00
|
|
|
|
|
|
|
r1.count_towards_summary = False
|
2020-01-06 22:09:09 +08:00
|
|
|
tr.stats = {"failed": (r1, r2)}
|
|
|
|
tr._main_color = None
|
|
|
|
res = tr.build_summary_stats_line()
|
2019-04-06 23:09:48 +08:00
|
|
|
assert res == ([("1 failed", {"bold": True, "red": True})], "red")
|
2019-03-07 05:54:45 +08:00
|
|
|
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
class TestClassicOutputStyle:
|
2018-08-27 04:12:55 +08:00
|
|
|
"""Ensure classic output style works as expected (#3883)"""
|
|
|
|
|
|
|
|
@pytest.fixture
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_files(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-08-27 04:12:55 +08:00
|
|
|
**{
|
|
|
|
"test_one.py": "def test_one(): pass",
|
|
|
|
"test_two.py": "def test_two(): assert 0",
|
|
|
|
"sub/test_three.py": """
|
|
|
|
def test_three_1(): pass
|
|
|
|
def test_three_2(): assert 0
|
|
|
|
def test_three_3(): pass
|
|
|
|
""",
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_normal_verbosity(self, pytester: Pytester, test_files) -> None:
|
|
|
|
result = pytester.runpytest("-o", "console_output_style=classic")
|
2018-08-27 04:12:55 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"test_one.py .",
|
|
|
|
"test_two.py F",
|
2020-10-03 04:16:22 +08:00
|
|
|
f"sub{os.sep}test_three.py .F.",
|
2018-08-27 04:12:55 +08:00
|
|
|
"*2 failed, 3 passed in*",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_verbose(self, pytester: Pytester, test_files) -> None:
|
|
|
|
result = pytester.runpytest("-o", "console_output_style=classic", "-v")
|
2018-08-27 04:12:55 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
|
|
|
"test_one.py::test_one PASSED",
|
|
|
|
"test_two.py::test_two FAILED",
|
2020-10-03 04:16:22 +08:00
|
|
|
f"sub{os.sep}test_three.py::test_three_1 PASSED",
|
|
|
|
f"sub{os.sep}test_three.py::test_three_2 FAILED",
|
|
|
|
f"sub{os.sep}test_three.py::test_three_3 PASSED",
|
2018-08-27 04:12:55 +08:00
|
|
|
"*2 failed, 3 passed in*",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_quiet(self, pytester: Pytester, test_files) -> None:
|
|
|
|
result = pytester.runpytest("-o", "console_output_style=classic", "-q")
|
2018-08-27 04:12:55 +08:00
|
|
|
result.stdout.fnmatch_lines([".F.F.", "*2 failed, 3 passed in*"])
|
|
|
|
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
class TestProgressOutputStyle:
|
2017-11-22 06:43:12 +08:00
|
|
|
@pytest.fixture
|
2020-12-09 04:20:02 +08:00
|
|
|
def many_tests_files(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makepyfile(
|
2017-11-22 06:43:12 +08:00
|
|
|
test_bar="""
|
|
|
|
import pytest
|
|
|
|
@pytest.mark.parametrize('i', range(10))
|
|
|
|
def test_bar(i): pass
|
|
|
|
""",
|
|
|
|
test_foo="""
|
|
|
|
import pytest
|
|
|
|
@pytest.mark.parametrize('i', range(5))
|
|
|
|
def test_foo(i): pass
|
|
|
|
""",
|
|
|
|
test_foobar="""
|
|
|
|
import pytest
|
|
|
|
@pytest.mark.parametrize('i', range(5))
|
|
|
|
def test_foobar(i): pass
|
|
|
|
""",
|
|
|
|
)
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_zero_tests_collected(self, pytester: Pytester) -> None:
|
2017-11-29 08:42:52 +08:00
|
|
|
"""Some plugins (testmon for example) might issue pytest_runtest_logreport without any tests being
|
|
|
|
actually collected (#2971)."""
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makeconftest(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2017-11-29 08:42:52 +08:00
|
|
|
def pytest_collection_modifyitems(items, config):
|
|
|
|
from _pytest.runner import CollectReport
|
|
|
|
for node_id in ('nodeid1', 'nodeid2'):
|
|
|
|
rep = CollectReport(node_id, 'passed', None, None)
|
|
|
|
rep.when = 'passed'
|
|
|
|
rep.duration = 0.1
|
|
|
|
config.hook.pytest_runtest_logreport(report=rep)
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
output = pytester.runpytest()
|
2019-10-06 01:18:51 +08:00
|
|
|
output.stdout.no_fnmatch_line("*ZeroDivisionError*")
|
2018-05-23 22:48:46 +08:00
|
|
|
output.stdout.fnmatch_lines(["=* 2 passed in *="])
|
2017-11-29 08:42:52 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_normal(self, many_tests_files, pytester: Pytester) -> None:
|
|
|
|
output = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
output.stdout.re_match_lines(
|
|
|
|
[
|
|
|
|
r"test_bar.py \.{10} \s+ \[ 50%\]",
|
|
|
|
r"test_foo.py \.{5} \s+ \[ 75%\]",
|
|
|
|
r"test_foobar.py \.{5} \s+ \[100%\]",
|
|
|
|
]
|
|
|
|
)
|
2017-11-22 06:43:12 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_colored_progress(
|
|
|
|
self, pytester: Pytester, monkeypatch, color_mapping
|
|
|
|
) -> None:
|
2019-10-31 04:49:20 +08:00
|
|
|
monkeypatch.setenv("PY_COLORS", "1")
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makepyfile(
|
2020-01-06 22:09:09 +08:00
|
|
|
test_axfail="""
|
|
|
|
import pytest
|
|
|
|
@pytest.mark.xfail
|
|
|
|
def test_axfail(): assert 0
|
|
|
|
""",
|
2019-10-31 04:49:20 +08:00
|
|
|
test_bar="""
|
|
|
|
import pytest
|
|
|
|
@pytest.mark.parametrize('i', range(10))
|
|
|
|
def test_bar(i): pass
|
|
|
|
""",
|
|
|
|
test_foo="""
|
|
|
|
import pytest
|
|
|
|
import warnings
|
|
|
|
@pytest.mark.parametrize('i', range(5))
|
|
|
|
def test_foo(i):
|
|
|
|
warnings.warn(DeprecationWarning("collection"))
|
|
|
|
pass
|
|
|
|
""",
|
|
|
|
test_foobar="""
|
|
|
|
import pytest
|
|
|
|
@pytest.mark.parametrize('i', range(5))
|
|
|
|
def test_foobar(i): raise ValueError()
|
|
|
|
""",
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest()
|
2020-01-18 19:43:38 +08:00
|
|
|
result.stdout.re_match_lines(
|
2020-02-12 19:32:37 +08:00
|
|
|
color_mapping.format_for_rematch(
|
|
|
|
[
|
2020-01-06 22:09:09 +08:00
|
|
|
r"test_axfail.py {yellow}x{reset}{green} \s+ \[ 4%\]{reset}",
|
|
|
|
r"test_bar.py ({green}\.{reset}){{10}}{green} \s+ \[ 52%\]{reset}",
|
|
|
|
r"test_foo.py ({green}\.{reset}){{5}}{yellow} \s+ \[ 76%\]{reset}",
|
2020-01-18 19:43:38 +08:00
|
|
|
r"test_foobar.py ({red}F{reset}){{5}}{red} \s+ \[100%\]{reset}",
|
|
|
|
]
|
2020-02-12 19:32:37 +08:00
|
|
|
)
|
2019-10-31 04:49:20 +08:00
|
|
|
)
|
|
|
|
|
2020-01-06 22:09:09 +08:00
|
|
|
# Only xfail should have yellow progress indicator.
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("test_axfail.py")
|
2020-01-06 22:09:09 +08:00
|
|
|
result.stdout.re_match_lines(
|
2020-02-16 01:46:29 +08:00
|
|
|
color_mapping.format_for_rematch(
|
|
|
|
[
|
2020-01-06 22:09:09 +08:00
|
|
|
r"test_axfail.py {yellow}x{reset}{yellow} \s+ \[100%\]{reset}",
|
|
|
|
r"^{yellow}=+ ({yellow}{bold}|{bold}{yellow})1 xfailed{reset}{yellow} in ",
|
|
|
|
]
|
2020-02-16 01:46:29 +08:00
|
|
|
)
|
2020-01-06 22:09:09 +08:00
|
|
|
)
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_count(self, many_tests_files, pytester: Pytester) -> None:
|
|
|
|
pytester.makeini(
|
2018-08-24 13:56:25 +08:00
|
|
|
"""
|
|
|
|
[pytest]
|
2018-08-27 10:21:00 +08:00
|
|
|
console_output_style = count
|
2018-08-24 14:00:02 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
output = pytester.runpytest()
|
2018-08-24 13:56:25 +08:00
|
|
|
output.stdout.re_match_lines(
|
|
|
|
[
|
2018-08-28 11:23:17 +08:00
|
|
|
r"test_bar.py \.{10} \s+ \[10/20\]",
|
|
|
|
r"test_foo.py \.{5} \s+ \[15/20\]",
|
|
|
|
r"test_foobar.py \.{5} \s+ \[20/20\]",
|
2018-08-24 13:56:25 +08:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_verbose(self, many_tests_files, pytester: Pytester) -> None:
|
|
|
|
output = pytester.runpytest("-v")
|
2018-05-23 22:48:46 +08:00
|
|
|
output.stdout.re_match_lines(
|
|
|
|
[
|
|
|
|
r"test_bar.py::test_bar\[0\] PASSED \s+ \[ 5%\]",
|
|
|
|
r"test_foo.py::test_foo\[4\] PASSED \s+ \[ 75%\]",
|
|
|
|
r"test_foobar.py::test_foobar\[4\] PASSED \s+ \[100%\]",
|
|
|
|
]
|
|
|
|
)
|
2017-11-22 06:43:12 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_verbose_count(self, many_tests_files, pytester: Pytester) -> None:
|
|
|
|
pytester.makeini(
|
2018-08-24 13:56:25 +08:00
|
|
|
"""
|
|
|
|
[pytest]
|
2018-08-27 10:21:00 +08:00
|
|
|
console_output_style = count
|
2018-08-24 14:00:02 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
output = pytester.runpytest("-v")
|
2018-08-24 13:56:25 +08:00
|
|
|
output.stdout.re_match_lines(
|
|
|
|
[
|
2018-08-28 11:23:17 +08:00
|
|
|
r"test_bar.py::test_bar\[0\] PASSED \s+ \[ 1/20\]",
|
|
|
|
r"test_foo.py::test_foo\[4\] PASSED \s+ \[15/20\]",
|
|
|
|
r"test_foobar.py::test_foobar\[4\] PASSED \s+ \[20/20\]",
|
2018-08-24 13:56:25 +08:00
|
|
|
]
|
|
|
|
)
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_xdist_normal(
|
|
|
|
self, many_tests_files, pytester: Pytester, monkeypatch
|
|
|
|
) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
pytest.importorskip("xdist")
|
2018-12-09 18:53:41 +08:00
|
|
|
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False)
|
2020-12-09 04:20:02 +08:00
|
|
|
output = pytester.runpytest("-n2")
|
2018-05-23 22:48:46 +08:00
|
|
|
output.stdout.re_match_lines([r"\.{20} \s+ \[100%\]"])
|
2017-11-22 06:43:12 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_xdist_normal_count(
|
|
|
|
self, many_tests_files, pytester: Pytester, monkeypatch
|
|
|
|
) -> None:
|
2018-08-24 13:56:25 +08:00
|
|
|
pytest.importorskip("xdist")
|
2018-12-09 18:53:41 +08:00
|
|
|
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False)
|
2020-12-09 04:20:02 +08:00
|
|
|
pytester.makeini(
|
2018-08-26 13:18:29 +08:00
|
|
|
"""
|
|
|
|
[pytest]
|
2018-08-27 10:21:00 +08:00
|
|
|
console_output_style = count
|
2018-08-26 13:18:29 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
output = pytester.runpytest("-n2")
|
2018-08-28 11:23:17 +08:00
|
|
|
output.stdout.re_match_lines([r"\.{20} \s+ \[20/20\]"])
|
2018-08-24 13:56:25 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_xdist_verbose(
|
|
|
|
self, many_tests_files, pytester: Pytester, monkeypatch
|
|
|
|
) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
pytest.importorskip("xdist")
|
2018-12-09 18:53:41 +08:00
|
|
|
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False)
|
2020-12-09 04:20:02 +08:00
|
|
|
output = pytester.runpytest("-n2", "-v")
|
2018-05-23 22:48:46 +08:00
|
|
|
output.stdout.re_match_lines_random(
|
|
|
|
[
|
|
|
|
r"\[gw\d\] \[\s*\d+%\] PASSED test_bar.py::test_bar\[1\]",
|
|
|
|
r"\[gw\d\] \[\s*\d+%\] PASSED test_foo.py::test_foo\[1\]",
|
|
|
|
r"\[gw\d\] \[\s*\d+%\] PASSED test_foobar.py::test_foobar\[1\]",
|
|
|
|
]
|
|
|
|
)
|
2020-02-15 08:22:01 +08:00
|
|
|
output.stdout.fnmatch_lines_random(
|
|
|
|
[
|
|
|
|
line.translate(TRANS_FNMATCH)
|
|
|
|
for line in [
|
|
|
|
"test_bar.py::test_bar[0] ",
|
|
|
|
"test_foo.py::test_foo[0] ",
|
|
|
|
"test_foobar.py::test_foobar[0] ",
|
|
|
|
"[gw?] [ 5%] PASSED test_*[?] ",
|
|
|
|
"[gw?] [ 10%] PASSED test_*[?] ",
|
|
|
|
"[gw?] [ 55%] PASSED test_*[?] ",
|
|
|
|
"[gw?] [ 60%] PASSED test_*[?] ",
|
|
|
|
"[gw?] [ 95%] PASSED test_*[?] ",
|
|
|
|
"[gw?] [100%] PASSED test_*[?] ",
|
|
|
|
]
|
|
|
|
]
|
|
|
|
)
|
2017-12-16 20:52:30 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_capture_no(self, many_tests_files, pytester: Pytester) -> None:
|
|
|
|
output = pytester.runpytest("-s")
|
2018-05-23 22:48:46 +08:00
|
|
|
output.stdout.re_match_lines(
|
|
|
|
[r"test_bar.py \.{10}", r"test_foo.py \.{5}", r"test_foobar.py \.{5}"]
|
|
|
|
)
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
output = pytester.runpytest("--capture=no")
|
2019-10-06 01:18:51 +08:00
|
|
|
output.stdout.no_fnmatch_line("*%]*")
|
2018-01-12 06:22:18 +08:00
|
|
|
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
class TestProgressWithTeardown:
|
2018-01-12 06:22:18 +08:00
|
|
|
"""Ensure we show the correct percentages for tests that fail during teardown (#3088)"""
|
|
|
|
|
|
|
|
@pytest.fixture
|
2020-12-09 04:20:02 +08:00
|
|
|
def contest_with_teardown_fixture(self, pytester: Pytester) -> None:
|
|
|
|
pytester.makeconftest(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2018-01-12 06:22:18 +08:00
|
|
|
import pytest
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def fail_teardown():
|
|
|
|
yield
|
|
|
|
assert False
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2018-01-12 06:22:18 +08:00
|
|
|
|
|
|
|
@pytest.fixture
|
2020-12-09 04:20:02 +08:00
|
|
|
def many_files(self, pytester: Pytester, contest_with_teardown_fixture) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
test_bar="""
|
2018-01-12 06:22:18 +08:00
|
|
|
import pytest
|
|
|
|
@pytest.mark.parametrize('i', range(5))
|
|
|
|
def test_bar(fail_teardown, i):
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
""",
|
|
|
|
test_foo="""
|
2018-01-12 06:22:18 +08:00
|
|
|
import pytest
|
|
|
|
@pytest.mark.parametrize('i', range(15))
|
|
|
|
def test_foo(fail_teardown, i):
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
""",
|
2018-01-12 06:22:18 +08:00
|
|
|
)
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_teardown_simple(
|
|
|
|
self, pytester: Pytester, contest_with_teardown_fixture
|
|
|
|
) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2018-01-12 06:22:18 +08:00
|
|
|
def test_foo(fail_teardown):
|
|
|
|
pass
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
output = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
output.stdout.re_match_lines([r"test_teardown_simple.py \.E\s+\[100%\]"])
|
2018-01-12 06:22:18 +08:00
|
|
|
|
2018-05-23 22:48:46 +08:00
|
|
|
def test_teardown_with_test_also_failing(
|
2020-12-09 04:20:02 +08:00
|
|
|
self, pytester: Pytester, contest_with_teardown_fixture
|
|
|
|
) -> None:
|
|
|
|
pytester.makepyfile(
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
2018-01-12 06:22:18 +08:00
|
|
|
def test_foo(fail_teardown):
|
2019-12-12 18:41:23 +08:00
|
|
|
assert 0
|
2018-05-23 22:48:46 +08:00
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
output = pytester.runpytest("-rfE")
|
2018-05-23 22:48:46 +08:00
|
|
|
output.stdout.re_match_lines(
|
2019-12-12 18:41:23 +08:00
|
|
|
[
|
|
|
|
r"test_teardown_with_test_also_failing.py FE\s+\[100%\]",
|
|
|
|
"FAILED test_teardown_with_test_also_failing.py::test_foo - assert 0",
|
|
|
|
"ERROR test_teardown_with_test_also_failing.py::test_foo - assert False",
|
|
|
|
]
|
2018-05-23 22:48:46 +08:00
|
|
|
)
|
2018-01-12 06:22:18 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_teardown_many(self, pytester: Pytester, many_files) -> None:
|
|
|
|
output = pytester.runpytest()
|
2018-05-23 22:48:46 +08:00
|
|
|
output.stdout.re_match_lines(
|
|
|
|
[r"test_bar.py (\.E){5}\s+\[ 25%\]", r"test_foo.py (\.E){15}\s+\[100%\]"]
|
|
|
|
)
|
2018-01-12 06:22:18 +08:00
|
|
|
|
2020-02-12 19:32:37 +08:00
|
|
|
def test_teardown_many_verbose(
|
2020-12-09 04:20:02 +08:00
|
|
|
self, pytester: Pytester, many_files, color_mapping
|
2020-02-12 19:32:37 +08:00
|
|
|
) -> None:
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("-v")
|
2020-01-28 22:52:46 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
2020-02-12 19:32:37 +08:00
|
|
|
color_mapping.format_for_fnmatch(
|
|
|
|
[
|
2020-01-28 22:52:46 +08:00
|
|
|
"test_bar.py::test_bar[0] PASSED * [ 5%]",
|
|
|
|
"test_bar.py::test_bar[0] ERROR * [ 5%]",
|
|
|
|
"test_bar.py::test_bar[4] PASSED * [ 25%]",
|
|
|
|
"test_foo.py::test_foo[14] PASSED * [100%]",
|
|
|
|
"test_foo.py::test_foo[14] ERROR * [100%]",
|
|
|
|
"=* 20 passed, 20 errors in *",
|
|
|
|
]
|
2020-02-12 19:32:37 +08:00
|
|
|
)
|
2018-05-23 22:48:46 +08:00
|
|
|
)
|
2018-01-12 06:22:18 +08:00
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_xdist_normal(self, many_files, pytester: Pytester, monkeypatch) -> None:
|
2018-05-23 22:48:46 +08:00
|
|
|
pytest.importorskip("xdist")
|
2018-12-09 18:53:41 +08:00
|
|
|
monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False)
|
2020-12-09 04:20:02 +08:00
|
|
|
output = pytester.runpytest("-n2")
|
2018-05-23 22:48:46 +08:00
|
|
|
output.stdout.re_match_lines([r"[\.E]{40} \s+ \[100%\]"])
|
2019-04-08 00:21:37 +08:00
|
|
|
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
def test_skip_reasons_folding() -> None:
|
2019-04-08 00:21:37 +08:00
|
|
|
path = "xyz"
|
|
|
|
lineno = 3
|
|
|
|
message = "justso"
|
|
|
|
longrepr = (path, lineno, message)
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
class X:
|
2019-04-08 00:21:37 +08:00
|
|
|
pass
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
ev1 = cast(CollectReport, X())
|
2019-04-08 00:21:37 +08:00
|
|
|
ev1.when = "execute"
|
2020-12-30 20:37:00 +08:00
|
|
|
ev1.skipped = True # type: ignore[misc]
|
2019-04-08 00:21:37 +08:00
|
|
|
ev1.longrepr = longrepr
|
|
|
|
|
2020-05-01 19:40:17 +08:00
|
|
|
ev2 = cast(CollectReport, X())
|
2019-04-08 00:21:37 +08:00
|
|
|
ev2.when = "execute"
|
|
|
|
ev2.longrepr = longrepr
|
2020-12-30 20:37:00 +08:00
|
|
|
ev2.skipped = True # type: ignore[misc]
|
2019-04-08 00:21:37 +08:00
|
|
|
|
|
|
|
# ev3 might be a collection report
|
2020-05-01 19:40:17 +08:00
|
|
|
ev3 = cast(CollectReport, X())
|
2019-04-08 00:21:37 +08:00
|
|
|
ev3.when = "collect"
|
|
|
|
ev3.longrepr = longrepr
|
2020-12-30 20:37:00 +08:00
|
|
|
ev3.skipped = True # type: ignore[misc]
|
2019-04-08 00:21:37 +08:00
|
|
|
|
2020-08-06 19:46:24 +08:00
|
|
|
values = _folded_skips(Path.cwd(), [ev1, ev2, ev3])
|
2019-04-08 00:21:37 +08:00
|
|
|
assert len(values) == 1
|
2020-05-01 19:40:17 +08:00
|
|
|
num, fspath, lineno_, reason = values[0]
|
2019-04-08 00:21:37 +08:00
|
|
|
assert num == 3
|
|
|
|
assert fspath == path
|
2020-05-01 19:40:17 +08:00
|
|
|
assert lineno_ == lineno
|
2019-04-08 00:21:37 +08:00
|
|
|
assert reason == message
|
2019-04-17 21:30:34 +08:00
|
|
|
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_line_with_reprcrash(monkeypatch: MonkeyPatch) -> None:
|
2019-04-17 21:30:34 +08:00
|
|
|
mocked_verbose_word = "FAILED"
|
|
|
|
|
|
|
|
mocked_pos = "some::nodeid"
|
|
|
|
|
|
|
|
def mock_get_pos(*args):
|
|
|
|
return mocked_pos
|
|
|
|
|
|
|
|
monkeypatch.setattr(_pytest.terminal, "_get_pos", mock_get_pos)
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
class config:
|
2019-04-17 21:30:34 +08:00
|
|
|
pass
|
|
|
|
|
2019-06-03 06:32:00 +08:00
|
|
|
class rep:
|
2019-04-17 21:30:34 +08:00
|
|
|
def _get_verbose_word(self, *args):
|
|
|
|
return mocked_verbose_word
|
|
|
|
|
|
|
|
class longrepr:
|
|
|
|
class reprcrash:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def check(msg, width, expected):
|
|
|
|
__tracebackhide__ = True
|
|
|
|
if msg:
|
2020-05-01 19:40:17 +08:00
|
|
|
rep.longrepr.reprcrash.message = msg # type: ignore
|
|
|
|
actual = _get_line_with_reprcrash_message(config, rep(), width) # type: ignore
|
2019-04-17 21:30:34 +08:00
|
|
|
|
|
|
|
assert actual == expected
|
2020-10-03 04:16:22 +08:00
|
|
|
if actual != f"{mocked_verbose_word} {mocked_pos}":
|
2019-04-17 21:30:34 +08:00
|
|
|
assert len(actual) <= width
|
|
|
|
assert wcswidth(actual) <= width
|
|
|
|
|
|
|
|
# AttributeError with message
|
|
|
|
check(None, 80, "FAILED some::nodeid")
|
|
|
|
|
|
|
|
check("msg", 80, "FAILED some::nodeid - msg")
|
|
|
|
check("msg", 3, "FAILED some::nodeid")
|
|
|
|
|
|
|
|
check("msg", 24, "FAILED some::nodeid")
|
|
|
|
check("msg", 25, "FAILED some::nodeid - msg")
|
|
|
|
|
|
|
|
check("some longer msg", 24, "FAILED some::nodeid")
|
|
|
|
check("some longer msg", 25, "FAILED some::nodeid - ...")
|
|
|
|
check("some longer msg", 26, "FAILED some::nodeid - s...")
|
|
|
|
|
|
|
|
check("some\nmessage", 25, "FAILED some::nodeid - ...")
|
|
|
|
check("some\nmessage", 26, "FAILED some::nodeid - some")
|
|
|
|
check("some\nmessage", 80, "FAILED some::nodeid - some")
|
|
|
|
|
|
|
|
# Test unicode safety.
|
2020-05-26 19:59:16 +08:00
|
|
|
check("🉐🉐🉐🉐🉐\n2nd line", 25, "FAILED some::nodeid - ...")
|
|
|
|
check("🉐🉐🉐🉐🉐\n2nd line", 26, "FAILED some::nodeid - ...")
|
|
|
|
check("🉐🉐🉐🉐🉐\n2nd line", 27, "FAILED some::nodeid - 🉐...")
|
|
|
|
check("🉐🉐🉐🉐🉐\n2nd line", 28, "FAILED some::nodeid - 🉐...")
|
|
|
|
check("🉐🉐🉐🉐🉐\n2nd line", 29, "FAILED some::nodeid - 🉐🉐...")
|
2019-04-17 21:30:34 +08:00
|
|
|
|
|
|
|
# NOTE: constructed, not sure if this is supported.
|
2020-05-26 19:59:16 +08:00
|
|
|
mocked_pos = "nodeid::🉐::withunicode"
|
|
|
|
check("🉐🉐🉐🉐🉐\n2nd line", 29, "FAILED nodeid::🉐::withunicode")
|
|
|
|
check("🉐🉐🉐🉐🉐\n2nd line", 40, "FAILED nodeid::🉐::withunicode - 🉐🉐...")
|
|
|
|
check("🉐🉐🉐🉐🉐\n2nd line", 41, "FAILED nodeid::🉐::withunicode - 🉐🉐...")
|
|
|
|
check("🉐🉐🉐🉐🉐\n2nd line", 42, "FAILED nodeid::🉐::withunicode - 🉐🉐🉐...")
|
|
|
|
check("🉐🉐🉐🉐🉐\n2nd line", 80, "FAILED nodeid::🉐::withunicode - 🉐🉐🉐🉐🉐")
|
2019-08-10 20:14:57 +08:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"seconds, expected",
|
|
|
|
[
|
|
|
|
(10.0, "10.00s"),
|
|
|
|
(10.34, "10.34s"),
|
|
|
|
(59.99, "59.99s"),
|
|
|
|
(60.55, "60.55s (0:01:00)"),
|
|
|
|
(123.55, "123.55s (0:02:03)"),
|
|
|
|
(60 * 60 + 0.5, "3600.50s (1:00:00)"),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
def test_format_session_duration(seconds, expected):
|
|
|
|
from _pytest.terminal import format_session_duration
|
|
|
|
|
|
|
|
assert format_session_duration(seconds) == expected
|
2019-10-25 11:59:10 +08:00
|
|
|
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_collecterror(pytester: Pytester) -> None:
|
|
|
|
p1 = pytester.makepyfile("raise SyntaxError()")
|
|
|
|
result = pytester.runpytest("-ra", str(p1))
|
2019-10-25 11:59:10 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
[
|
2019-11-04 00:48:06 +08:00
|
|
|
"collected 0 items / 1 error",
|
2019-10-25 11:59:10 +08:00
|
|
|
"*= ERRORS =*",
|
|
|
|
"*_ ERROR collecting test_collecterror.py _*",
|
|
|
|
"E SyntaxError: *",
|
|
|
|
"*= short test summary info =*",
|
|
|
|
"ERROR test_collecterror.py",
|
2019-11-04 00:48:06 +08:00
|
|
|
"*! Interrupted: 1 error during collection !*",
|
2019-10-25 11:59:10 +08:00
|
|
|
"*= 1 error in *",
|
|
|
|
]
|
|
|
|
)
|
2020-01-26 23:41:17 +08:00
|
|
|
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_no_summary_collecterror(pytester: Pytester) -> None:
|
|
|
|
p1 = pytester.makepyfile("raise SyntaxError()")
|
|
|
|
result = pytester.runpytest("-ra", "--no-summary", str(p1))
|
2020-05-26 01:29:18 +08:00
|
|
|
result.stdout.no_fnmatch_line("*= ERRORS =*")
|
|
|
|
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_via_exec(pytester: Pytester) -> None:
|
|
|
|
p1 = pytester.makepyfile("exec('def test_via_exec(): pass')")
|
|
|
|
result = pytester.runpytest(str(p1), "-vv")
|
2020-01-26 23:41:17 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
["test_via_exec.py::test_via_exec <- <string> PASSED*", "*= 1 passed in *"]
|
|
|
|
)
|
2020-02-12 19:32:37 +08:00
|
|
|
|
|
|
|
|
|
|
|
class TestCodeHighlight:
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_code_highlight_simple(self, pytester: Pytester, color_mapping) -> None:
|
|
|
|
pytester.makepyfile(
|
2020-02-12 19:32:37 +08:00
|
|
|
"""
|
|
|
|
def test_foo():
|
|
|
|
assert 1 == 10
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--color=yes")
|
2020-02-12 19:32:37 +08:00
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
color_mapping.format_for_fnmatch(
|
|
|
|
[
|
|
|
|
" {kw}def{hl-reset} {function}test_foo{hl-reset}():",
|
|
|
|
"> {kw}assert{hl-reset} {number}1{hl-reset} == {number}10{hl-reset}",
|
|
|
|
"{bold}{red}E assert 1 == 10{reset}",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
2020-12-09 04:20:02 +08:00
|
|
|
def test_code_highlight_continuation(
|
|
|
|
self, pytester: Pytester, color_mapping
|
|
|
|
) -> None:
|
|
|
|
pytester.makepyfile(
|
2020-02-12 19:32:37 +08:00
|
|
|
"""
|
|
|
|
def test_foo():
|
|
|
|
print('''
|
|
|
|
'''); assert 0
|
|
|
|
"""
|
|
|
|
)
|
2020-12-09 04:20:02 +08:00
|
|
|
result = pytester.runpytest("--color=yes")
|
2020-02-12 19:32:37 +08:00
|
|
|
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
color_mapping.format_for_fnmatch(
|
|
|
|
[
|
|
|
|
" {kw}def{hl-reset} {function}test_foo{hl-reset}():",
|
|
|
|
" {print}print{hl-reset}({str}'''{hl-reset}{str}{hl-reset}",
|
|
|
|
"> {str} {hl-reset}{str}'''{hl-reset}); {kw}assert{hl-reset} {number}0{hl-reset}",
|
|
|
|
"{bold}{red}E assert 0{reset}",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
)
|
2020-05-10 09:09:09 +08:00
|
|
|
|
2021-06-11 23:04:27 +08:00
|
|
|
def test_code_highlight_custom_theme(
|
|
|
|
self, pytester: Pytester, color_mapping, monkeypatch: MonkeyPatch
|
|
|
|
) -> None:
|
|
|
|
pytester.makepyfile(
|
|
|
|
"""
|
|
|
|
def test_foo():
|
|
|
|
assert 1 == 10
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
monkeypatch.setenv("PYTEST_THEME", "solarized-dark")
|
|
|
|
monkeypatch.setenv("PYTEST_THEME_MODE", "dark")
|
|
|
|
result = pytester.runpytest("--color=yes")
|
|
|
|
result.stdout.fnmatch_lines(
|
|
|
|
color_mapping.format_for_fnmatch(
|
|
|
|
[
|
|
|
|
" {kw}def{hl-reset} {function}test_foo{hl-reset}():",
|
|
|
|
"> {kw}assert{hl-reset} {number}1{hl-reset} == {number}10{hl-reset}",
|
|
|
|
"{bold}{red}E assert 1 == 10{reset}",
|
|
|
|
]
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_code_highlight_invalid_theme(
|
|
|
|
self, pytester: Pytester, color_mapping, monkeypatch: MonkeyPatch
|
|
|
|
) -> None:
|
|
|
|
pytester.makepyfile(
|
|
|
|
"""
|
|
|
|
def test_foo():
|
|
|
|
assert 1 == 10
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
monkeypatch.setenv("PYTEST_THEME", "invalid")
|
|
|
|
result = pytester.runpytest_subprocess("--color=yes")
|
|
|
|
result.stderr.fnmatch_lines(
|
|
|
|
"ERROR: PYTEST_THEME environment variable had an invalid value: 'invalid'. "
|
|
|
|
"Only valid pygment styles are allowed."
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_code_highlight_invalid_theme_mode(
|
|
|
|
self, pytester: Pytester, color_mapping, monkeypatch: MonkeyPatch
|
|
|
|
) -> None:
|
|
|
|
pytester.makepyfile(
|
|
|
|
"""
|
|
|
|
def test_foo():
|
|
|
|
assert 1 == 10
|
|
|
|
"""
|
|
|
|
)
|
|
|
|
monkeypatch.setenv("PYTEST_THEME_MODE", "invalid")
|
|
|
|
result = pytester.runpytest_subprocess("--color=yes")
|
|
|
|
result.stderr.fnmatch_lines(
|
|
|
|
"ERROR: PYTEST_THEME_MODE environment variable had an invalid value: 'invalid'. "
|
|
|
|
"The only allowed values are 'dark' and 'light'."
|
|
|
|
)
|
|
|
|
|
2020-05-10 09:09:09 +08:00
|
|
|
|
|
|
|
def test_raw_skip_reason_skipped() -> None:
|
|
|
|
report = SimpleNamespace()
|
|
|
|
report.skipped = True
|
|
|
|
report.longrepr = ("xyz", 3, "Skipped: Just so")
|
|
|
|
|
|
|
|
reason = _get_raw_skip_reason(cast(TestReport, report))
|
|
|
|
assert reason == "Just so"
|
|
|
|
|
|
|
|
|
|
|
|
def test_raw_skip_reason_xfail() -> None:
|
|
|
|
report = SimpleNamespace()
|
|
|
|
report.wasxfail = "reason: To everything there is a season"
|
|
|
|
|
|
|
|
reason = _get_raw_skip_reason(cast(TestReport, report))
|
|
|
|
assert reason == "To everything there is a season"
|
|
|
|
|
|
|
|
|
|
|
|
def test_format_trimmed() -> None:
|
|
|
|
msg = "unconditional skip"
|
|
|
|
|
|
|
|
assert _format_trimmed(" ({}) ", msg, len(msg) + 4) == " (unconditional skip) "
|
|
|
|
assert _format_trimmed(" ({}) ", msg, len(msg) + 3) == " (unconditional ...) "
|