Skip to content

Commit

Permalink
Implement showing summary words colored
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoddemus committed Apr 21, 2022
1 parent 28e8c85 commit bcaff45
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 36 deletions.
59 changes: 37 additions & 22 deletions src/_pytest/terminal.py
Expand Up @@ -35,6 +35,7 @@
from _pytest import timing
from _pytest._code import ExceptionInfo
from _pytest._code.code import ExceptionRepr
from _pytest._io import TerminalWriter
from _pytest._io.wcwidth import wcswidth
from _pytest.compat import final
from _pytest.config import _PluggyPlugin
Expand Down Expand Up @@ -1074,58 +1075,71 @@ def short_test_summary(self) -> None:
if not self.reportchars:
return

def show_simple(stat, lines: List[str]) -> None:
def show_simple(lines: List[str], *, stat: str) -> None:
failed = self.stats.get(stat, [])
if not failed:
return
termwidth = self._tw.fullwidth
config = self.config
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)

def show_xfailed(lines: List[str]) -> None:
xfailed = self.stats.get("xfailed", [])
for rep in xfailed:
verbose_word = rep._get_verbose_word(self.config)
pos = _get_pos(self.config, rep)
lines.append(f"{verbose_word} {pos}")
markup_word = self._tw.markup(
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
if reason:
lines.append(" " + str(reason))
line += " - " + str(reason)

lines.append(line)

def show_xpassed(lines: List[str]) -> None:
xpassed = self.stats.get("xpassed", [])
for rep in xpassed:
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
lines.append(f"{verbose_word} {pos} {reason}")
lines.append(f"{markup_word} {nodeid} {reason}")

def show_skipped(lines: List[str]) -> None:
skipped: List[CollectReport] = self.stats.get("skipped", [])
fskips = _folded_skips(self.startpath, skipped) if skipped else []
if not fskips:
return
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:
if reason.startswith("Skipped: "):
reason = reason[9:]
if reason.startswith(prefix):
reason = reason[len(prefix) :]
if lineno is not None:
lines.append(
"%s [%d] %s:%d: %s"
% (verbose_word, num, fspath, lineno, reason)
"%s [%d] %s:%d: %s" % (markup_word, num, fspath, lineno, reason)
)
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]] = {
"x": show_xfailed,
"X": show_xpassed,
"f": partial(show_simple, "failed"),
"f": partial(show_simple, stat="failed"),
"s": show_skipped,
"p": partial(show_simple, "passed"),
"E": partial(show_simple, "error"),
"p": partial(show_simple, stat="passed"),
"E": partial(show_simple, stat="error"),
}

lines: List[str] = []
Expand All @@ -1135,7 +1149,7 @@ def show_skipped(lines: List[str]) -> None:
action(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:
self.write_line(line)

Expand Down Expand Up @@ -1249,7 +1263,7 @@ def _build_collect_only_summary_stats_line(
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)
return nodeid

Expand Down Expand Up @@ -1280,13 +1294,14 @@ def _format_trimmed(format: str, msg: str, available_width: int) -> Optional[str


def _get_line_with_reprcrash_message(
config: Config, rep: BaseReport, termwidth: int
config: Config, rep: BaseReport, tw: TerminalWriter, word_markup: Dict[str, bool]
) -> str:
"""Get summary line for a report, trying to add reprcrash message."""
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)

try:
Expand All @@ -1295,7 +1310,7 @@ def _get_line_with_reprcrash_message(
except AttributeError:
pass
else:
available_width = termwidth - line_width
available_width = tw.fullwidth - line_width
msg = _format_trimmed(" - {}", msg, available_width)
if msg is not None:
line += msg
Expand Down
20 changes: 8 additions & 12 deletions testing/test_skipping.py
Expand Up @@ -441,10 +441,8 @@ def test_this_false():
result = pytester.runpytest(p, "-rx")
result.stdout.fnmatch_lines(
[
"*test_one*test_this*",
"*NOTRUN*noway",
"*test_one*test_this_true*",
"*NOTRUN*condition:*True*",
"*test_one*test_this - reason: *NOTRUN* noway",
"*test_one*test_this_true - reason: *NOTRUN* condition: True",
"*1 passed*",
]
)
Expand All @@ -461,9 +459,7 @@ def setup_module(mod):
"""
)
result = pytester.runpytest(p, "-rx")
result.stdout.fnmatch_lines(
["*test_one*test_this*", "*NOTRUN*hello", "*1 xfailed*"]
)
result.stdout.fnmatch_lines(["*test_one*test_this*NOTRUN*hello", "*1 xfailed*"])

def test_xfail_xpass(self, pytester: Pytester) -> None:
p = pytester.makepyfile(
Expand All @@ -489,7 +485,7 @@ def test_this():
result = pytester.runpytest(p)
result.stdout.fnmatch_lines(["*1 xfailed*"])
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.stdout.fnmatch_lines(["*1 pass*"])

Expand All @@ -507,7 +503,7 @@ def test_this():
result = pytester.runpytest(p)
result.stdout.fnmatch_lines(["*1 xfailed*"])
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.stdout.fnmatch_lines(
"""
Expand Down Expand Up @@ -543,7 +539,7 @@ def test_this(arg):
"""
)
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:
p = pytester.makepyfile(
Expand Down Expand Up @@ -622,7 +618,7 @@ def test_foo():
"""
)
result = pytester.runpytest(p, "-rxX")
result.stdout.fnmatch_lines(["*XFAIL*", "*unsupported feature*"])
result.stdout.fnmatch_lines(["*XFAIL*unsupported feature*"])
assert result.ret == 0

@pytest.mark.parametrize("strict", [True, False])
Expand Down Expand Up @@ -1185,7 +1181,7 @@ def test_boolean():
"""
)
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:
Expand Down
10 changes: 8 additions & 2 deletions testing/test_terminal.py
Expand Up @@ -2319,7 +2319,7 @@ def test_line_with_reprcrash(monkeypatch: MonkeyPatch) -> None:
def mock_get_pos(*args):
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:
pass
Expand All @@ -2333,10 +2333,16 @@ class reprcrash:
pass

def check(msg, width, expected):
class DummyTerminalWriter:
fullwidth = width

def markup(self, word: str, **markup: str):
return word

__tracebackhide__ = True
if msg:
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
if actual != f"{mocked_verbose_word} {mocked_pos}":
Expand Down

0 comments on commit bcaff45

Please sign in to comment.