Merge pull request #7208 from CarycaKatarzyna/issue2044

Issue 2044 - show skipping reason in verbose mode
This commit is contained in:
Ran Benita 2020-12-09 10:11:47 +02:00 committed by GitHub
commit 902739cfc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 116 additions and 20 deletions

View File

@ -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".

View File

@ -13,6 +13,7 @@ from functools import partial
from pathlib import Path
from typing import Any
from typing import Callable
from typing import cast
from typing import Dict
from typing import Generator
from typing import List
@ -545,6 +546,16 @@ class TerminalReporter:
line = self._locationline(rep.nodeid, *rep.location)
if not running_xdist:
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:
self._write_progress_information_filling_space()
else:
@ -1249,6 +1260,31 @@ def _get_pos(config: Config, rep: BaseReport):
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(
config: Config, rep: BaseReport, termwidth: int
) -> str:
@ -1257,11 +1293,7 @@ def _get_line_with_reprcrash_message(
pos = _get_pos(config, rep)
line = f"{verbose_word} {pos}"
len_line = wcswidth(line)
ellipsis, len_ellipsis = "...", 3
if len_line > termwidth - len_ellipsis:
# No space for an additional message.
return line
line_width = wcswidth(line)
try:
# Type ignored intentionally -- possible AttributeError expected.
@ -1269,22 +1301,11 @@ def _get_line_with_reprcrash_message(
except AttributeError:
pass
else:
# Only use the first line.
i = msg.find("\n")
if i != -1:
msg = msg[:i]
len_msg = wcswidth(msg)
available_width = termwidth - line_width
msg = _format_trimmed(" - {}", msg, available_width)
if msg is not None:
line += 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
@ -1361,3 +1382,22 @@ def format_session_duration(seconds: float) -> str:
else:
dt = datetime.timedelta(seconds=int(seconds))
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

View File

@ -5,6 +5,7 @@ import sys
import textwrap
from io import StringIO
from pathlib import Path
from types import SimpleNamespace
from typing import cast
from typing import Dict
from typing import List
@ -23,8 +24,11 @@ from _pytest.monkeypatch import MonkeyPatch
from _pytest.pytester import Pytester
from _pytest.reports import BaseReport
from _pytest.reports import CollectReport
from _pytest.reports import TestReport
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_raw_skip_reason
from _pytest.terminal import _plugin_nameversions
from _pytest.terminal import getreportopt
from _pytest.terminal import TerminalReporter
@ -342,6 +346,33 @@ class TestTerminal:
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:
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 ...) "