diff --git a/changelog/3829.feature.rst b/changelog/3829.feature.rst new file mode 100644 index 00000000000..d3bfdb8e67e --- /dev/null +++ b/changelog/3829.feature.rst @@ -0,0 +1 @@ +Added the ``count`` option to ``console_output_style`` to enable displaying the progress as a count instead of a percentage. diff --git a/doc/en/reference.rst b/doc/en/reference.rst index fe9e87042f9..9b0c0bc71f3 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -916,6 +916,7 @@ passed multiple times. The expected format is ``name=value``. For example:: * ``classic``: classic pytest output. * ``progress``: like classic pytest output, but with a progress indicator. + * ``count``: like progress, but shows progress as the number of tests completed instead of a percent. The default is ``progress``, but you can fallback to ``classic`` if you prefer or the new mode is causing unexpected problems: diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 7dd2edd6f9d..1251b1479ed 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -254,7 +254,7 @@ def _determine_show_progress_info(self): # do not show progress if we are showing fixture setup/teardown if self.config.getoption("setupshow"): return False - return self.config.getini("console_output_style") == "progress" + return self.config.getini("console_output_style") in ("progress", "count") def hasopt(self, char): char = {"xfailed": "x", "skipped": "s"}.get(char, char) @@ -404,6 +404,12 @@ def pytest_runtest_logreport(self, report): self.currentfspath = -2 def pytest_runtest_logfinish(self, nodeid): + if self.config.getini("console_output_style") == "count": + num_tests = self._session.testscollected + progress_length = len(" [{}/{}]".format(str(num_tests), str(num_tests))) + else: + progress_length = len(" [100%]") + if self.verbosity <= 0 and self._show_progress_info: self._progress_nodeids_reported.add(nodeid) last_item = ( @@ -413,23 +419,29 @@ def pytest_runtest_logfinish(self, nodeid): self._write_progress_information_filling_space() else: past_edge = ( - self._tw.chars_on_current_line + self._PROGRESS_LENGTH + 1 + self._tw.chars_on_current_line + progress_length + 1 >= self._screen_width ) if past_edge: msg = self._get_progress_information_message() self._tw.write(msg + "\n", cyan=True) - _PROGRESS_LENGTH = len(" [100%]") - def _get_progress_information_message(self): if self.config.getoption("capture") == "no": return "" collected = self._session.testscollected - if collected: - progress = len(self._progress_nodeids_reported) * 100 // collected - return " [{:3d}%]".format(progress) - return " [100%]" + if self.config.getini("console_output_style") == "count": + if collected: + progress = self._progress_nodeids_reported + counter_format = "{{:{}d}}".format(len(str(collected))) + format_string = " [{}/{{}}]".format(counter_format) + return format_string.format(len(progress), collected) + return " [ {} / {} ]".format(collected, collected) + else: + if collected: + progress = len(self._progress_nodeids_reported) * 100 // collected + return " [{:3d}%]".format(progress) + return " [100%]" def _write_progress_information_filling_space(self): msg = self._get_progress_information_message() diff --git a/testing/test_terminal.py b/testing/test_terminal.py index a9da27980c0..1a1d20c95c0 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1143,6 +1143,22 @@ def test_normal(self, many_tests_files, testdir): ] ) + def test_count(self, many_tests_files, testdir): + testdir.makeini( + """ + [pytest] + console_output_style = count + """ + ) + output = testdir.runpytest() + output.stdout.re_match_lines( + [ + r"test_bar.py \.{10} \s+ \[10/20\]", + r"test_foo.py \.{5} \s+ \[15/20\]", + r"test_foobar.py \.{5} \s+ \[20/20\]", + ] + ) + def test_verbose(self, many_tests_files, testdir): output = testdir.runpytest("-v") output.stdout.re_match_lines( @@ -1153,11 +1169,38 @@ def test_verbose(self, many_tests_files, testdir): ] ) + def test_verbose_count(self, many_tests_files, testdir): + testdir.makeini( + """ + [pytest] + console_output_style = count + """ + ) + output = testdir.runpytest("-v") + output.stdout.re_match_lines( + [ + r"test_bar.py::test_bar\[0\] PASSED \s+ \[ 1/20\]", + r"test_foo.py::test_foo\[4\] PASSED \s+ \[15/20\]", + r"test_foobar.py::test_foobar\[4\] PASSED \s+ \[20/20\]", + ] + ) + def test_xdist_normal(self, many_tests_files, testdir): pytest.importorskip("xdist") output = testdir.runpytest("-n2") output.stdout.re_match_lines([r"\.{20} \s+ \[100%\]"]) + def test_xdist_normal_count(self, many_tests_files, testdir): + pytest.importorskip("xdist") + testdir.makeini( + """ + [pytest] + console_output_style = count + """ + ) + output = testdir.runpytest("-n2") + output.stdout.re_match_lines([r"\.{20} \s+ \[20/20\]"]) + def test_xdist_verbose(self, many_tests_files, testdir): pytest.importorskip("xdist") output = testdir.runpytest("-n2", "-v")