diff options
author | Ned Batchelder <ned@nedbatchelder.com> | 2021-05-31 19:37:53 -0400 |
---|---|---|
committer | Ned Batchelder <ned@nedbatchelder.com> | 2021-05-31 19:37:53 -0400 |
commit | 1157999dc5c6c3aa3129a7cd4bf249fe73598501 (patch) | |
tree | cba407bacb180fb8fcb46a1b5eaf7da2e0fd88fb | |
parent | 65d9e0edbca0dc893ca70fbf64969e2ddc5a3680 (diff) | |
download | python-coveragepy-git-1157999dc5c6c3aa3129a7cd4bf249fe73598501.tar.gz |
fix: --fail-under=100 could report 100 is less than 100.
Use the same rounding rules for the fail-under message that are used for totals
everywhere else, so that it won't say:
total of 100 is less than fail-under=100
-rw-r--r-- | CHANGES.rst | 5 | ||||
-rw-r--r-- | coverage/cmdline.py | 6 | ||||
-rw-r--r-- | coverage/results.py | 24 | ||||
-rw-r--r-- | tests/test_process.py | 16 | ||||
-rw-r--r-- | tests/test_results.py | 12 |
5 files changed, 53 insertions, 10 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 205ef0ab..b15d6298 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -35,11 +35,16 @@ Unreleased - Warnings generated by coverage.py are now real Python warnings. +- Using ``--fail-under=100`` with coverage near 100% could result in the + self-contradictory message :code:`total of 100 is less than fail-under=100`. + This bug (`issue 1168`_) is now fixed. + - The ``COVERAGE_DEBUG_FILE`` environment variable now accepts ``stdout`` and ``stderr`` to write to those destinations. .. _Django coverage plugin: https://pypi.org/project/django-coverage-plugin/ .. _issue 1150: https://github.com/nedbat/coveragepy/issues/1150 +.. _issue 1168: https://github.com/nedbat/coveragepy/issues/1168 .. _changes_56b1: diff --git a/coverage/cmdline.py b/coverage/cmdline.py index d1e8f283..697d2960 100644 --- a/coverage/cmdline.py +++ b/coverage/cmdline.py @@ -20,7 +20,7 @@ from coverage.data import line_counts from coverage.debug import info_formatter, info_header, short_stack from coverage.exceptions import BaseCoverageException, ExceptionDuringRun, NoSource from coverage.execfile import PyRunner -from coverage.results import should_fail_under +from coverage.results import Numbers, should_fail_under class Opts: @@ -655,8 +655,8 @@ class CoverageScript: fail_under = self.coverage.get_option("report:fail_under") precision = self.coverage.get_option("report:precision") if should_fail_under(total, fail_under, precision): - msg = "total of {total:.{p}f} is less than fail-under={fail_under:.{p}f}".format( - total=total, fail_under=fail_under, p=precision, + msg = "total of {total} is less than fail-under={fail_under:.{p}f}".format( + total=Numbers.display_covered(total), fail_under=fail_under, p=precision, ) print("Coverage failure:", msg) return FAIL_UNDER diff --git a/coverage/results.py b/coverage/results.py index c60ccac2..f7331b41 100644 --- a/coverage/results.py +++ b/coverage/results.py @@ -219,14 +219,24 @@ class Numbers(SimpleReprMixin): result in either "0" or "100". """ - pc = self.pc_covered - if 0 < pc < self._near0: - pc = self._near0 - elif self._near100 < pc < 100: - pc = self._near100 + return self.display_covered(self.pc_covered) + + @classmethod + def display_covered(cls, pc): + """Return a displayable total percentage, as a string. + + Note that "0" is only returned when the value is truly zero, and "100" + is only returned when the value is truly 100. Rounding can never + result in either "0" or "100". + + """ + if 0 < pc < cls._near0: + pc = cls._near0 + elif cls._near100 < pc < 100: + pc = cls._near100 else: - pc = round(pc, self._precision) - return "%.*f" % (self._precision, pc) + pc = round(pc, cls._precision) + return "%.*f" % (cls._precision, pc) @classmethod def pc_str_width(cls): diff --git a/tests/test_process.py b/tests/test_process.py index 9c695b2a..04a8a2d5 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -1249,6 +1249,22 @@ class FailUnderTest(CoverageTest): expected = "Coverage failure: total of 42.86 is less than fail-under=42.88" assert expected == self.last_line_squeezed(out) + def test_report_99p9_is_not_ok(self): + # A file with 99.99% coverage: + self.make_file("ninety_nine_plus.py", """\ + a = 1 + """ + """ + b = 2 + """ * 20000 + """ + if a > 3: + c = 4 + """) + self.run_command("coverage run --source=. ninety_nine_plus.py") + st, out = self.run_command_status("coverage report --fail-under=100") + assert st == 2 + expected = "Coverage failure: total of 99 is less than fail-under=100" + assert expected == self.last_line_squeezed(out) + class FailUnderNoFilesTest(CoverageTest): """Test that nothing to report results in an error exit status.""" diff --git a/tests/test_results.py b/tests/test_results.py index 5811b0c2..fa239e92 100644 --- a/tests/test_results.py +++ b/tests/test_results.py @@ -68,6 +68,18 @@ class NumbersTest(CoverageTest): assert n10000.pc_covered_str == "0.0" Numbers.set_precision(0) + @pytest.mark.parametrize("prec, pc, res", [ + (0, 47.87, "48"), + (1, 47.87, "47.9"), + (0, 99.995, "99"), + (2, 99.99995, "99.99"), + ]) + def test_display_covered(self, prec, pc, res): + # Numbers._precision is a global, which is bad. + Numbers.set_precision(prec) + assert Numbers.display_covered(pc) == res + Numbers.set_precision(0) + def test_covered_ratio(self): n = Numbers(n_files=1, n_statements=200, n_missing=47) assert n.ratio_covered == (153, 200) |