From 0d79061432639308ecf3e3c2e838080567c84e20 Mon Sep 17 00:00:00 2001 From: MarcoGorelli Date: Wed, 30 Oct 2019 20:49:20 +0000 Subject: [PATCH] Color percentage indicator according to color of final line indicate current outcome/status with color of percentage indicator Fix type annotation, refactor _write_progress_information_filling_space Keep code in _get_main_color as similar as possible to how it was before Write test Make black-compliant Fix error in newly introduced test_collecterror Make tests more readable by using constants and f-strings Remove accidentally added monkeypatch Make Python 3.5-compatible, add changelog entry Add newline at the end of changelog file --- changelog/6097.improvement.rst | 1 + src/_pytest/terminal.py | 21 ++++++++++++---- testing/test_terminal.py | 45 ++++++++++++++++++++++++++++++++-- 3 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 changelog/6097.improvement.rst diff --git a/changelog/6097.improvement.rst b/changelog/6097.improvement.rst new file mode 100644 index 000000000..32eb84906 --- /dev/null +++ b/changelog/6097.improvement.rst @@ -0,0 +1 @@ +The "[XXX%]" indicator in the test summary is colored according to the final (new) multi-colored line's main color. diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index c3663e695..d26df2d2c 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -15,6 +15,7 @@ from typing import List from typing import Mapping from typing import Optional from typing import Set +from typing import Tuple import attr import pluggy @@ -458,18 +459,20 @@ class TerminalReporter: else: progress_length = len(" [100%]") + main_color, _ = _get_main_color(self.stats) + self._progress_nodeids_reported.add(nodeid) is_last_item = ( len(self._progress_nodeids_reported) == self._session.testscollected ) if is_last_item: - self._write_progress_information_filling_space() + self._write_progress_information_filling_space(color=main_color) else: w = self._width_of_current_line past_edge = w + progress_length + 1 >= self._screen_width if past_edge: msg = self._get_progress_information_message() - self._tw.write(msg + "\n", cyan=True) + self._tw.write(msg + "\n", **{main_color: True}) def _get_progress_information_message(self): collected = self._session.testscollected @@ -486,11 +489,13 @@ class TerminalReporter: return " [{:3d}%]".format(progress) return " [100%]" - def _write_progress_information_filling_space(self): + def _write_progress_information_filling_space(self, color=None): + if not color: + color, _ = _get_main_color(self.stats) msg = self._get_progress_information_message() w = self._width_of_current_line fill = self._tw.fullwidth - w - 1 - self.write(msg.rjust(fill), cyan=True) + self.write(msg.rjust(fill), **{color: True}) @property def _width_of_current_line(self): @@ -1075,7 +1080,7 @@ def _make_plural(count, noun): return count, noun + "s" if count != 1 else noun -def build_summary_stats_line(stats): +def _get_main_color(stats) -> Tuple[str, List[str]]: known_types = ( "failed passed skipped deselected xfailed xpassed warnings error".split() ) @@ -1096,6 +1101,12 @@ def build_summary_stats_line(stats): else: main_color = "yellow" + return main_color, known_types + + +def build_summary_stats_line(stats): + main_color, known_types = _get_main_color(stats) + parts = [] for key in known_types: reports = stats.get(key, None) diff --git a/testing/test_terminal.py b/testing/test_terminal.py index e77102667..02de45ff9 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -21,6 +21,10 @@ from _pytest.terminal import getreportopt from _pytest.terminal import TerminalReporter DistInfo = collections.namedtuple("DistInfo", ["project_name", "version"]) +RED = r"\x1b\[31m" +GREEN = r"\x1b\[32m" +YELLOW = r"\x1b\[33m" +RESET = r"\x1b\[0m" class Option: @@ -1494,6 +1498,43 @@ class TestProgressOutputStyle: ] ) + def test_colored_progress(self, testdir, monkeypatch): + monkeypatch.setenv("PY_COLORS", "1") + testdir.makepyfile( + 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() + """, + ) + output = testdir.runpytest() + output.stdout.re_match_lines( + [ + r"test_bar.py ({green}\.{reset}){{10}}{green} \s+ \[ 50%\]{reset}".format( + green=GREEN, reset=RESET + ), + r"test_foo.py ({green}\.{reset}){{5}}{yellow} \s+ \[ 75%\]{reset}".format( + green=GREEN, reset=RESET, yellow=YELLOW + ), + r"test_foobar.py ({red}F{reset}){{5}}{red} \s+ \[100%\]{reset}".format( + reset=RESET, red=RED + ), + ] + ) + def test_count(self, many_tests_files, testdir): testdir.makeini( """ @@ -1781,13 +1822,13 @@ def test_collecterror(testdir): result = testdir.runpytest("-ra", str(p1)) result.stdout.fnmatch_lines( [ - "collected 0 items / 1 errors", + "collected 0 items / 1 error", "*= ERRORS =*", "*_ ERROR collecting test_collecterror.py _*", "E SyntaxError: *", "*= short test summary info =*", "ERROR test_collecterror.py", - "*! Interrupted: 1 errors during collection !*", + "*! Interrupted: 1 error during collection !*", "*= 1 error in *", ] )