Skip to content

Commit

Permalink
fix: --fail-under=100 could report 100 is less than 100.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
nedbat committed May 31, 2021
1 parent 65d9e0e commit 1157999
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 10 deletions.
5 changes: 5 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 3 additions & 3 deletions coverage/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
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:
Expand Down Expand Up @@ -655,8 +655,8 @@ def command_line(self, argv):
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
Expand Down
24 changes: 17 additions & 7 deletions coverage/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,14 +219,24 @@ def pc_covered_str(self):
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):
Expand Down
16 changes: 16 additions & 0 deletions tests/test_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,22 @@ def test_report_42p86_is_not_ok(self):
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."""
Expand Down
12 changes: 12 additions & 0 deletions tests/test_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ def test_pc_covered_str_precision(self):
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)
Expand Down

0 comments on commit 1157999

Please sign in to comment.