parent
90d2adf0e8
commit
69fb79e741
|
@ -35,6 +35,7 @@ from _pytest import nodes
|
||||||
from _pytest import timing
|
from _pytest import timing
|
||||||
from _pytest._code import ExceptionInfo
|
from _pytest._code import ExceptionInfo
|
||||||
from _pytest._code.code import ExceptionRepr
|
from _pytest._code.code import ExceptionRepr
|
||||||
|
from _pytest._io import TerminalWriter
|
||||||
from _pytest._io.wcwidth import wcswidth
|
from _pytest._io.wcwidth import wcswidth
|
||||||
from _pytest.compat import final
|
from _pytest.compat import final
|
||||||
from _pytest.config import _PluggyPlugin
|
from _pytest.config import _PluggyPlugin
|
||||||
|
@ -1074,33 +1075,43 @@ class TerminalReporter:
|
||||||
if not self.reportchars:
|
if not self.reportchars:
|
||||||
return
|
return
|
||||||
|
|
||||||
def show_simple(stat, lines: List[str]) -> None:
|
def show_simple(lines: List[str], *, stat: str) -> None:
|
||||||
failed = self.stats.get(stat, [])
|
failed = self.stats.get(stat, [])
|
||||||
if not failed:
|
if not failed:
|
||||||
return
|
return
|
||||||
termwidth = self._tw.fullwidth
|
|
||||||
config = self.config
|
config = self.config
|
||||||
for rep in failed:
|
for rep in failed:
|
||||||
line = _get_line_with_reprcrash_message(config, rep, termwidth)
|
color = _color_for_type.get(stat, _color_for_type_default)
|
||||||
|
line = _get_line_with_reprcrash_message(
|
||||||
|
config, rep, self._tw, {color: True}
|
||||||
|
)
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
|
|
||||||
def show_xfailed(lines: List[str]) -> None:
|
def show_xfailed(lines: List[str]) -> None:
|
||||||
xfailed = self.stats.get("xfailed", [])
|
xfailed = self.stats.get("xfailed", [])
|
||||||
for rep in xfailed:
|
for rep in xfailed:
|
||||||
verbose_word = rep._get_verbose_word(self.config)
|
verbose_word = rep._get_verbose_word(self.config)
|
||||||
pos = _get_pos(self.config, rep)
|
markup_word = self._tw.markup(
|
||||||
lines.append(f"{verbose_word} {pos}")
|
verbose_word, **{_color_for_type["warnings"]: True}
|
||||||
|
)
|
||||||
|
nodeid = _get_node_id_with_markup(self._tw, self.config, rep)
|
||||||
|
line = f"{markup_word} {nodeid}"
|
||||||
reason = rep.wasxfail
|
reason = rep.wasxfail
|
||||||
if reason:
|
if reason:
|
||||||
lines.append(" " + str(reason))
|
line += " - " + str(reason)
|
||||||
|
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
def show_xpassed(lines: List[str]) -> None:
|
def show_xpassed(lines: List[str]) -> None:
|
||||||
xpassed = self.stats.get("xpassed", [])
|
xpassed = self.stats.get("xpassed", [])
|
||||||
for rep in xpassed:
|
for rep in xpassed:
|
||||||
verbose_word = rep._get_verbose_word(self.config)
|
verbose_word = rep._get_verbose_word(self.config)
|
||||||
pos = _get_pos(self.config, rep)
|
markup_word = self._tw.markup(
|
||||||
|
verbose_word, **{_color_for_type["warnings"]: True}
|
||||||
|
)
|
||||||
|
nodeid = _get_node_id_with_markup(self._tw, self.config, rep)
|
||||||
reason = rep.wasxfail
|
reason = rep.wasxfail
|
||||||
lines.append(f"{verbose_word} {pos} {reason}")
|
lines.append(f"{markup_word} {nodeid} {reason}")
|
||||||
|
|
||||||
def show_skipped(lines: List[str]) -> None:
|
def show_skipped(lines: List[str]) -> None:
|
||||||
skipped: List[CollectReport] = self.stats.get("skipped", [])
|
skipped: List[CollectReport] = self.stats.get("skipped", [])
|
||||||
|
@ -1108,24 +1119,27 @@ class TerminalReporter:
|
||||||
if not fskips:
|
if not fskips:
|
||||||
return
|
return
|
||||||
verbose_word = skipped[0]._get_verbose_word(self.config)
|
verbose_word = skipped[0]._get_verbose_word(self.config)
|
||||||
|
markup_word = self._tw.markup(
|
||||||
|
verbose_word, **{_color_for_type["warnings"]: True}
|
||||||
|
)
|
||||||
|
prefix = "Skipped: "
|
||||||
for num, fspath, lineno, reason in fskips:
|
for num, fspath, lineno, reason in fskips:
|
||||||
if reason.startswith("Skipped: "):
|
if reason.startswith(prefix):
|
||||||
reason = reason[9:]
|
reason = reason[len(prefix) :]
|
||||||
if lineno is not None:
|
if lineno is not None:
|
||||||
lines.append(
|
lines.append(
|
||||||
"%s [%d] %s:%d: %s"
|
"%s [%d] %s:%d: %s" % (markup_word, num, fspath, lineno, reason)
|
||||||
% (verbose_word, num, fspath, lineno, reason)
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
lines.append("%s [%d] %s: %s" % (verbose_word, num, fspath, reason))
|
lines.append("%s [%d] %s: %s" % (markup_word, num, fspath, reason))
|
||||||
|
|
||||||
REPORTCHAR_ACTIONS: Mapping[str, Callable[[List[str]], None]] = {
|
REPORTCHAR_ACTIONS: Mapping[str, Callable[[List[str]], None]] = {
|
||||||
"x": show_xfailed,
|
"x": show_xfailed,
|
||||||
"X": show_xpassed,
|
"X": show_xpassed,
|
||||||
"f": partial(show_simple, "failed"),
|
"f": partial(show_simple, stat="failed"),
|
||||||
"s": show_skipped,
|
"s": show_skipped,
|
||||||
"p": partial(show_simple, "passed"),
|
"p": partial(show_simple, stat="passed"),
|
||||||
"E": partial(show_simple, "error"),
|
"E": partial(show_simple, stat="error"),
|
||||||
}
|
}
|
||||||
|
|
||||||
lines: List[str] = []
|
lines: List[str] = []
|
||||||
|
@ -1135,7 +1149,7 @@ class TerminalReporter:
|
||||||
action(lines)
|
action(lines)
|
||||||
|
|
||||||
if lines:
|
if lines:
|
||||||
self.write_sep("=", "short test summary info")
|
self.write_sep("=", "short test summary info", cyan=True, bold=True)
|
||||||
for line in lines:
|
for line in lines:
|
||||||
self.write_line(line)
|
self.write_line(line)
|
||||||
|
|
||||||
|
@ -1249,9 +1263,14 @@ class TerminalReporter:
|
||||||
return parts, main_color
|
return parts, main_color
|
||||||
|
|
||||||
|
|
||||||
def _get_pos(config: Config, rep: BaseReport):
|
def _get_node_id_with_markup(tw: TerminalWriter, config: Config, rep: BaseReport):
|
||||||
nodeid = config.cwd_relative_nodeid(rep.nodeid)
|
nodeid = config.cwd_relative_nodeid(rep.nodeid)
|
||||||
return nodeid
|
path, *parts = nodeid.split("::")
|
||||||
|
if parts:
|
||||||
|
parts_markup = tw.markup("::".join(parts), bold=True)
|
||||||
|
return path + "::" + parts_markup
|
||||||
|
else:
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
def _format_trimmed(format: str, msg: str, available_width: int) -> Optional[str]:
|
def _format_trimmed(format: str, msg: str, available_width: int) -> Optional[str]:
|
||||||
|
@ -1280,13 +1299,14 @@ def _format_trimmed(format: str, msg: str, available_width: int) -> Optional[str
|
||||||
|
|
||||||
|
|
||||||
def _get_line_with_reprcrash_message(
|
def _get_line_with_reprcrash_message(
|
||||||
config: Config, rep: BaseReport, termwidth: int
|
config: Config, rep: BaseReport, tw: TerminalWriter, word_markup: Dict[str, bool]
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Get summary line for a report, trying to add reprcrash message."""
|
"""Get summary line for a report, trying to add reprcrash message."""
|
||||||
verbose_word = rep._get_verbose_word(config)
|
verbose_word = rep._get_verbose_word(config)
|
||||||
pos = _get_pos(config, rep)
|
word = tw.markup(verbose_word, **word_markup)
|
||||||
|
node = _get_node_id_with_markup(tw, config, rep)
|
||||||
|
|
||||||
line = f"{verbose_word} {pos}"
|
line = f"{word} {node}"
|
||||||
line_width = wcswidth(line)
|
line_width = wcswidth(line)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -1295,7 +1315,7 @@ def _get_line_with_reprcrash_message(
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
available_width = termwidth - line_width
|
available_width = tw.fullwidth - line_width
|
||||||
msg = _format_trimmed(" - {}", msg, available_width)
|
msg = _format_trimmed(" - {}", msg, available_width)
|
||||||
if msg is not None:
|
if msg is not None:
|
||||||
line += msg
|
line += msg
|
||||||
|
|
|
@ -441,10 +441,8 @@ class TestXFail:
|
||||||
result = pytester.runpytest(p, "-rx")
|
result = pytester.runpytest(p, "-rx")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
[
|
[
|
||||||
"*test_one*test_this*",
|
"*test_one*test_this - reason: *NOTRUN* noway",
|
||||||
"*NOTRUN*noway",
|
"*test_one*test_this_true - reason: *NOTRUN* condition: True",
|
||||||
"*test_one*test_this_true*",
|
|
||||||
"*NOTRUN*condition:*True*",
|
|
||||||
"*1 passed*",
|
"*1 passed*",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -461,9 +459,7 @@ class TestXFail:
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(p, "-rx")
|
result = pytester.runpytest(p, "-rx")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(["*test_one*test_this*NOTRUN*hello", "*1 xfailed*"])
|
||||||
["*test_one*test_this*", "*NOTRUN*hello", "*1 xfailed*"]
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_xfail_xpass(self, pytester: Pytester) -> None:
|
def test_xfail_xpass(self, pytester: Pytester) -> None:
|
||||||
p = pytester.makepyfile(
|
p = pytester.makepyfile(
|
||||||
|
@ -489,7 +485,7 @@ class TestXFail:
|
||||||
result = pytester.runpytest(p)
|
result = pytester.runpytest(p)
|
||||||
result.stdout.fnmatch_lines(["*1 xfailed*"])
|
result.stdout.fnmatch_lines(["*1 xfailed*"])
|
||||||
result = pytester.runpytest(p, "-rx")
|
result = pytester.runpytest(p, "-rx")
|
||||||
result.stdout.fnmatch_lines(["*XFAIL*test_this*", "*reason:*hello*"])
|
result.stdout.fnmatch_lines(["*XFAIL*test_this*reason:*hello*"])
|
||||||
result = pytester.runpytest(p, "--runxfail")
|
result = pytester.runpytest(p, "--runxfail")
|
||||||
result.stdout.fnmatch_lines(["*1 pass*"])
|
result.stdout.fnmatch_lines(["*1 pass*"])
|
||||||
|
|
||||||
|
@ -507,7 +503,7 @@ class TestXFail:
|
||||||
result = pytester.runpytest(p)
|
result = pytester.runpytest(p)
|
||||||
result.stdout.fnmatch_lines(["*1 xfailed*"])
|
result.stdout.fnmatch_lines(["*1 xfailed*"])
|
||||||
result = pytester.runpytest(p, "-rx")
|
result = pytester.runpytest(p, "-rx")
|
||||||
result.stdout.fnmatch_lines(["*XFAIL*test_this*", "*reason:*hello*"])
|
result.stdout.fnmatch_lines(["*XFAIL*test_this*reason:*hello*"])
|
||||||
result = pytester.runpytest(p, "--runxfail")
|
result = pytester.runpytest(p, "--runxfail")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
"""
|
"""
|
||||||
|
@ -543,7 +539,7 @@ class TestXFail:
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(p, "-rxX")
|
result = pytester.runpytest(p, "-rxX")
|
||||||
result.stdout.fnmatch_lines(["*XFAIL*test_this*", "*NOTRUN*"])
|
result.stdout.fnmatch_lines(["*XFAIL*test_this*NOTRUN*"])
|
||||||
|
|
||||||
def test_dynamic_xfail_set_during_funcarg_setup(self, pytester: Pytester) -> None:
|
def test_dynamic_xfail_set_during_funcarg_setup(self, pytester: Pytester) -> None:
|
||||||
p = pytester.makepyfile(
|
p = pytester.makepyfile(
|
||||||
|
@ -622,7 +618,7 @@ class TestXFail:
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest(p, "-rxX")
|
result = pytester.runpytest(p, "-rxX")
|
||||||
result.stdout.fnmatch_lines(["*XFAIL*", "*unsupported feature*"])
|
result.stdout.fnmatch_lines(["*XFAIL*unsupported feature*"])
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
@pytest.mark.parametrize("strict", [True, False])
|
@pytest.mark.parametrize("strict", [True, False])
|
||||||
|
@ -1185,7 +1181,7 @@ def test_xfail_skipif_with_globals(pytester: Pytester) -> None:
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = pytester.runpytest("-rsx")
|
result = pytester.runpytest("-rsx")
|
||||||
result.stdout.fnmatch_lines(["*SKIP*x == 3*", "*XFAIL*test_boolean*", "*x == 3*"])
|
result.stdout.fnmatch_lines(["*SKIP*x == 3*", "*XFAIL*test_boolean*x == 3*"])
|
||||||
|
|
||||||
|
|
||||||
def test_default_markers(pytester: Pytester) -> None:
|
def test_default_markers(pytester: Pytester) -> None:
|
||||||
|
@ -1297,8 +1293,7 @@ class TestBooleanCondition:
|
||||||
result = pytester.runpytest("-rxs")
|
result = pytester.runpytest("-rxs")
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
"""
|
"""
|
||||||
*XFAIL*
|
*XFAIL*True123*
|
||||||
*True123*
|
|
||||||
*1 xfail*
|
*1 xfail*
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
|
|
@ -2319,7 +2319,7 @@ def test_line_with_reprcrash(monkeypatch: MonkeyPatch) -> None:
|
||||||
def mock_get_pos(*args):
|
def mock_get_pos(*args):
|
||||||
return mocked_pos
|
return mocked_pos
|
||||||
|
|
||||||
monkeypatch.setattr(_pytest.terminal, "_get_pos", mock_get_pos)
|
monkeypatch.setattr(_pytest.terminal, "_get_node_id_with_markup", mock_get_pos)
|
||||||
|
|
||||||
class config:
|
class config:
|
||||||
pass
|
pass
|
||||||
|
@ -2333,10 +2333,16 @@ def test_line_with_reprcrash(monkeypatch: MonkeyPatch) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def check(msg, width, expected):
|
def check(msg, width, expected):
|
||||||
|
class DummyTerminalWriter:
|
||||||
|
fullwidth = width
|
||||||
|
|
||||||
|
def markup(self, word: str, **markup: str):
|
||||||
|
return word
|
||||||
|
|
||||||
__tracebackhide__ = True
|
__tracebackhide__ = True
|
||||||
if msg:
|
if msg:
|
||||||
rep.longrepr.reprcrash.message = msg # type: ignore
|
rep.longrepr.reprcrash.message = msg # type: ignore
|
||||||
actual = _get_line_with_reprcrash_message(config, rep(), width) # type: ignore
|
actual = _get_line_with_reprcrash_message(config, rep(), DummyTerminalWriter(), {}) # type: ignore
|
||||||
|
|
||||||
assert actual == expected
|
assert actual == expected
|
||||||
if actual != f"{mocked_verbose_word} {mocked_pos}":
|
if actual != f"{mocked_verbose_word} {mocked_pos}":
|
||||||
|
|
Loading…
Reference in New Issue