Show reason for skipped test in verbose mode
This commit is contained in:
parent
810b878ef8
commit
612f157dbd
|
@ -0,0 +1 @@
|
||||||
|
Verbose mode now shows the reason that a test was skipped in the test's terminal line after the "SKIPPED", "XFAIL" or "XPASS".
|
|
@ -13,6 +13,7 @@ from functools import partial
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
from typing import cast
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
from typing import List
|
from typing import List
|
||||||
|
@ -545,6 +546,16 @@ class TerminalReporter:
|
||||||
line = self._locationline(rep.nodeid, *rep.location)
|
line = self._locationline(rep.nodeid, *rep.location)
|
||||||
if not running_xdist:
|
if not running_xdist:
|
||||||
self.write_ensure_prefix(line, word, **markup)
|
self.write_ensure_prefix(line, word, **markup)
|
||||||
|
if rep.skipped or hasattr(report, "wasxfail"):
|
||||||
|
available_width = (
|
||||||
|
(self._tw.fullwidth - self._tw.width_of_current_line)
|
||||||
|
- len(" [100%]")
|
||||||
|
- 1
|
||||||
|
)
|
||||||
|
reason = _get_raw_skip_reason(rep)
|
||||||
|
reason_ = _format_trimmed(" ({})", reason, available_width)
|
||||||
|
if reason_ is not None:
|
||||||
|
self._tw.write(reason_)
|
||||||
if self._show_progress_info:
|
if self._show_progress_info:
|
||||||
self._write_progress_information_filling_space()
|
self._write_progress_information_filling_space()
|
||||||
else:
|
else:
|
||||||
|
@ -1249,6 +1260,31 @@ def _get_pos(config: Config, rep: BaseReport):
|
||||||
return nodeid
|
return nodeid
|
||||||
|
|
||||||
|
|
||||||
|
def _format_trimmed(format: str, msg: str, available_width: int) -> Optional[str]:
|
||||||
|
"""Format msg into format, ellipsizing it if doesn't fit in available_width.
|
||||||
|
|
||||||
|
Returns None if even the ellipsis can't fit.
|
||||||
|
"""
|
||||||
|
# Only use the first line.
|
||||||
|
i = msg.find("\n")
|
||||||
|
if i != -1:
|
||||||
|
msg = msg[:i]
|
||||||
|
|
||||||
|
ellipsis = "..."
|
||||||
|
format_width = wcswidth(format.format(""))
|
||||||
|
if format_width + len(ellipsis) > available_width:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if format_width + wcswidth(msg) > available_width:
|
||||||
|
available_width -= len(ellipsis)
|
||||||
|
msg = msg[:available_width]
|
||||||
|
while format_width + wcswidth(msg) > available_width:
|
||||||
|
msg = msg[:-1]
|
||||||
|
msg += ellipsis
|
||||||
|
|
||||||
|
return format.format(msg)
|
||||||
|
|
||||||
|
|
||||||
def _get_line_with_reprcrash_message(
|
def _get_line_with_reprcrash_message(
|
||||||
config: Config, rep: BaseReport, termwidth: int
|
config: Config, rep: BaseReport, termwidth: int
|
||||||
) -> str:
|
) -> str:
|
||||||
|
@ -1257,11 +1293,7 @@ def _get_line_with_reprcrash_message(
|
||||||
pos = _get_pos(config, rep)
|
pos = _get_pos(config, rep)
|
||||||
|
|
||||||
line = f"{verbose_word} {pos}"
|
line = f"{verbose_word} {pos}"
|
||||||
len_line = wcswidth(line)
|
line_width = wcswidth(line)
|
||||||
ellipsis, len_ellipsis = "...", 3
|
|
||||||
if len_line > termwidth - len_ellipsis:
|
|
||||||
# No space for an additional message.
|
|
||||||
return line
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Type ignored intentionally -- possible AttributeError expected.
|
# Type ignored intentionally -- possible AttributeError expected.
|
||||||
|
@ -1269,22 +1301,11 @@ def _get_line_with_reprcrash_message(
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
# Only use the first line.
|
available_width = termwidth - line_width
|
||||||
i = msg.find("\n")
|
msg = _format_trimmed(" - {}", msg, available_width)
|
||||||
if i != -1:
|
if msg is not None:
|
||||||
msg = msg[:i]
|
line += msg
|
||||||
len_msg = wcswidth(msg)
|
|
||||||
|
|
||||||
sep, len_sep = " - ", 3
|
|
||||||
max_len_msg = termwidth - len_line - len_sep
|
|
||||||
if max_len_msg >= len_ellipsis:
|
|
||||||
if len_msg > max_len_msg:
|
|
||||||
max_len_msg -= len_ellipsis
|
|
||||||
msg = msg[:max_len_msg]
|
|
||||||
while wcswidth(msg) > max_len_msg:
|
|
||||||
msg = msg[:-1]
|
|
||||||
msg += ellipsis
|
|
||||||
line += sep + msg
|
|
||||||
return line
|
return line
|
||||||
|
|
||||||
|
|
||||||
|
@ -1361,3 +1382,22 @@ def format_session_duration(seconds: float) -> str:
|
||||||
else:
|
else:
|
||||||
dt = datetime.timedelta(seconds=int(seconds))
|
dt = datetime.timedelta(seconds=int(seconds))
|
||||||
return f"{seconds:.2f}s ({dt})"
|
return f"{seconds:.2f}s ({dt})"
|
||||||
|
|
||||||
|
|
||||||
|
def _get_raw_skip_reason(report: TestReport) -> str:
|
||||||
|
"""Get the reason string of a skip/xfail/xpass test report.
|
||||||
|
|
||||||
|
The string is just the part given by the user.
|
||||||
|
"""
|
||||||
|
if hasattr(report, "wasxfail"):
|
||||||
|
reason = cast(str, report.wasxfail)
|
||||||
|
if reason.startswith("reason: "):
|
||||||
|
reason = reason[len("reason: ") :]
|
||||||
|
return reason
|
||||||
|
else:
|
||||||
|
assert report.skipped
|
||||||
|
assert isinstance(report.longrepr, tuple)
|
||||||
|
_, _, reason = report.longrepr
|
||||||
|
if reason.startswith("Skipped: "):
|
||||||
|
reason = reason[len("Skipped: ") :]
|
||||||
|
return reason
|
||||||
|
|
|
@ -5,6 +5,7 @@ import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from types import SimpleNamespace
|
||||||
from typing import cast
|
from typing import cast
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import List
|
from typing import List
|
||||||
|
@ -23,8 +24,11 @@ from _pytest.monkeypatch import MonkeyPatch
|
||||||
from _pytest.pytester import Pytester
|
from _pytest.pytester import Pytester
|
||||||
from _pytest.reports import BaseReport
|
from _pytest.reports import BaseReport
|
||||||
from _pytest.reports import CollectReport
|
from _pytest.reports import CollectReport
|
||||||
|
from _pytest.reports import TestReport
|
||||||
from _pytest.terminal import _folded_skips
|
from _pytest.terminal import _folded_skips
|
||||||
|
from _pytest.terminal import _format_trimmed
|
||||||
from _pytest.terminal import _get_line_with_reprcrash_message
|
from _pytest.terminal import _get_line_with_reprcrash_message
|
||||||
|
from _pytest.terminal import _get_raw_skip_reason
|
||||||
from _pytest.terminal import _plugin_nameversions
|
from _pytest.terminal import _plugin_nameversions
|
||||||
from _pytest.terminal import getreportopt
|
from _pytest.terminal import getreportopt
|
||||||
from _pytest.terminal import TerminalReporter
|
from _pytest.terminal import TerminalReporter
|
||||||
|
@ -342,6 +346,33 @@ class TestTerminal:
|
||||||
color_mapping.format_for_fnmatch(["*{red}FOO{reset}*"])
|
color_mapping.format_for_fnmatch(["*{red}FOO{reset}*"])
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
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) *",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestCollectonly:
|
class TestCollectonly:
|
||||||
def test_collectonly_basic(self, pytester: Pytester) -> None:
|
def test_collectonly_basic(self, pytester: Pytester) -> None:
|
||||||
|
@ -2345,3 +2376,27 @@ class TestCodeHighlight:
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
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 ...) "
|
||||||
|
|
Loading…
Reference in New Issue