summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2012-11-19 21:58:40 -0500
committerNed Batchelder <ned@nedbatchelder.com>2012-11-19 21:58:40 -0500
commitdc438780abf10e5257bb177728066e16201e3916 (patch)
tree172dc6b0d7929fb0fade8562e2e7734c985de532
parentc405d15412a1e8f2d14ee0ecea9c5d4fe55641de (diff)
downloadpython-coveragepy-git-dc438780abf10e5257bb177728066e16201e3916.tar.gz
Branch coverage is computed more accurately, #156.
-rw-r--r--CHANGES.txt6
-rw-r--r--coverage/html.py3
-rw-r--r--coverage/htmlfiles/index.html4
-rw-r--r--coverage/htmlfiles/pyfile.html2
-rw-r--r--coverage/results.py17
-rw-r--r--coverage/summary.py2
-rw-r--r--test/test_api.py3
-rw-r--r--test/test_summary.py36
8 files changed, 53 insertions, 20 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index e129a910..695b4155 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -27,6 +27,11 @@ Version 3.6b1
- Embarrassingly, the `[xml] output=' setting in the .coveragerc file simply
didn't work. Now it does.
+- Coverage percentage metrics are computed slightly differently under branch
+ coverage. This means that completely unexecuted files will now correctly
+ have 0% coverage, fixing `issue 156`_. This also means that your total
+ coverage numbers will be lower in general if you are using branch coverage.
+
- When installing, now in addition to creating a "coverage" command, two new
aliases are also installed. A "coverage2" or "coverage3" command will be
created, depending on whether you are installing in Python 2.x or 3.x.
@@ -69,6 +74,7 @@ Version 3.6b1
.. _issue 139: https://bitbucket.org/ned/coveragepy/issue/139/easy-check-for-a-certain-coverage-in-tests
.. _issue 143: https://bitbucket.org/ned/coveragepy/issue/143/omit-doesnt-seem-to-work-in-coverage
.. _issue 153: https://bitbucket.org/ned/coveragepy/issue/153/non-existent-filename-triggers
+.. _issue 156: https://bitbucket.org/ned/coveragepy/issue/156/a-completely-unexecuted-file-shows-14
.. _issue 163: https://bitbucket.org/ned/coveragepy/issue/163/problem-with-include-and-omit-filename
.. _issue 171: https://bitbucket.org/ned/coveragepy/issue/171/how-to-contribute-and-run-tests
.. _issue 193: https://bitbucket.org/ned/coveragepy/issue/193/unicodedecodeerror-on-htmlpy
diff --git a/coverage/html.py b/coverage/html.py
index 6a6c648e..ed8920f2 100644
--- a/coverage/html.py
+++ b/coverage/html.py
@@ -162,7 +162,6 @@ class HtmlReporter(Reporter):
nums = analysis.numbers
missing_branch_arcs = analysis.missing_branch_arcs()
- n_par = 0 # accumulated below.
arcs = self.arcs
# These classes determine which lines are highlighted by default.
@@ -187,7 +186,6 @@ class HtmlReporter(Reporter):
line_class.append(c_mis)
elif self.arcs and lineno in missing_branch_arcs:
line_class.append(c_par)
- n_par += 1
annlines = []
for b in missing_branch_arcs[lineno]:
if b < 0:
@@ -234,7 +232,6 @@ class HtmlReporter(Reporter):
# Save this file's information for the index file.
index_info = {
'nums': nums,
- 'par': n_par,
'html_filename': html_filename,
'name': cu.name,
}
diff --git a/coverage/htmlfiles/index.html b/coverage/htmlfiles/index.html
index 5a7c8c2e..c649a83c 100644
--- a/coverage/htmlfiles/index.html
+++ b/coverage/htmlfiles/index.html
@@ -69,7 +69,7 @@
<td>{{totals.n_excluded}}</td>
{% if arcs %}
<td>{{totals.n_branches}}</td>
- <td>{{totals.n_missing_branches}}</td>
+ <td>{{totals.n_partial_branches}}</td>
{% endif %}
<td class='right'>{{totals.pc_covered_str}}%</td>
</tr>
@@ -83,7 +83,7 @@
<td>{{file.nums.n_excluded}}</td>
{% if arcs %}
<td>{{file.nums.n_branches}}</td>
- <td>{{file.nums.n_missing_branches}}</td>
+ <td>{{file.nums.n_partial_branches}}</td>
{% endif %}
<td class='right'>{{file.nums.pc_covered_str}}%</td>
</tr>
diff --git a/coverage/htmlfiles/pyfile.html b/coverage/htmlfiles/pyfile.html
index 490fad86..525939f8 100644
--- a/coverage/htmlfiles/pyfile.html
+++ b/coverage/htmlfiles/pyfile.html
@@ -32,7 +32,7 @@
<span class='{{c_mis}} shortkey_m button_toggle_mis'>{{nums.n_missing}} missing</span>
<span class='{{c_exc}} shortkey_x button_toggle_exc'>{{nums.n_excluded}} excluded</span>
{% if arcs %}
- <span class='{{c_par}} shortkey_p button_toggle_par'>{{n_par}} partial</span>
+ <span class='{{c_par}} shortkey_p button_toggle_par'>{{nums.n_partial_branches}} partial</span>
{% endif %}
</h2>
</div>
diff --git a/coverage/results.py b/coverage/results.py
index b39966ca..ae22e1c3 100644
--- a/coverage/results.py
+++ b/coverage/results.py
@@ -41,11 +41,12 @@ class Analysis(object):
)
n_branches = self.total_branches()
mba = self.missing_branch_arcs()
- n_missing_branches = sum(
+ n_partial_branches = sum(
[len(v) for k,v in iitems(mba) if k not in self.missing]
)
+ n_missing_branches = sum([len(v) for k,v in iitems(mba)])
else:
- n_branches = n_missing_branches = 0
+ n_branches = n_partial_branches = n_missing_branches = 0
self.no_branch = set()
self.numbers = Numbers(
@@ -54,6 +55,7 @@ class Analysis(object):
n_excluded=len(self.excluded),
n_missing=len(self.missing),
n_branches=n_branches,
+ n_partial_branches=n_partial_branches,
n_missing_branches=n_missing_branches,
)
@@ -166,13 +168,14 @@ class Numbers(object):
_near100 = 99.0
def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0,
- n_branches=0, n_missing_branches=0
+ n_branches=0, n_partial_branches=0, n_missing_branches=0
):
self.n_files = n_files
self.n_statements = n_statements
self.n_excluded = n_excluded
self.n_missing = n_missing
self.n_branches = n_branches
+ self.n_partial_branches = n_partial_branches
self.n_missing_branches = n_missing_branches
def set_precision(cls, precision):
@@ -236,8 +239,12 @@ class Numbers(object):
nums.n_excluded = self.n_excluded + other.n_excluded
nums.n_missing = self.n_missing + other.n_missing
nums.n_branches = self.n_branches + other.n_branches
- nums.n_missing_branches = (self.n_missing_branches +
- other.n_missing_branches)
+ nums.n_partial_branches = (
+ self.n_partial_branches + other.n_partial_branches
+ )
+ nums.n_missing_branches = (
+ self.n_missing_branches + other.n_missing_branches
+ )
return nums
def __radd__(self, other):
diff --git a/coverage/summary.py b/coverage/summary.py
index 03648e5f..4b1cd14e 100644
--- a/coverage/summary.py
+++ b/coverage/summary.py
@@ -29,7 +29,7 @@ class SummaryReporter(Reporter):
header = (fmt_name % "Name") + " Stmts Miss"
fmt_coverage = fmt_name + "%6d %6d"
if self.branches:
- header += " Branch BrPart"
+ header += " Branch BrMiss"
fmt_coverage += " %6d %6d"
width100 = Numbers.pc_str_width()
header += "%*s" % (width100+4, "Cover")
diff --git a/test/test_api.py b/test/test_api.py
index e2ebc656..c15db5ed 100644
--- a/test/test_api.py
+++ b/test/test_api.py
@@ -511,4 +511,5 @@ class AnalysisTest(CoverageTest):
self.assertEqual(nums.n_excluded, 1)
self.assertEqual(nums.n_missing, 3)
self.assertEqual(nums.n_branches, 2)
- self.assertEqual(nums.n_missing_branches, 0)
+ self.assertEqual(nums.n_partial_branches, 0)
+ self.assertEqual(nums.n_missing_branches, 2)
diff --git a/test/test_summary.py b/test/test_summary.py
index 644aa9dd..933bcbe5 100644
--- a/test/test_summary.py
+++ b/test/test_summary.py
@@ -118,7 +118,7 @@ class SummaryTest(CoverageTest):
self.assertEqual(out, 'x\n')
report = self.report_from_command("coverage report")
- # Name Stmts Miss Branch BrPart Cover
+ # Name Stmts Miss Branch BrMiss Cover
# --------------------------------------------
# mybranch 5 0 2 1 85%
@@ -179,18 +179,40 @@ class SummaryTest(CoverageTest):
self.assertEqual(self.line_count(report), 2)
+ def get_report(self, cov):
+ """Get the report from `cov`, and canonicalize it."""
+ repout = StringIO()
+ cov.report(file=repout, show_missing=False)
+ report = repout.getvalue().replace('\\', '/')
+ report = re.sub(r" +", " ", report)
+ return report
+
+ def test_bug_156_file_not_run_should_be_zero(self):
+ # https://bitbucket.org/ned/coveragepy/issue/156
+ self.make_file("mybranch.py", """\
+ def branch(x):
+ if x:
+ print("x")
+ return x
+ branch(1)
+ """)
+ self.make_file("main.py", """\
+ print("y")
+ """)
+ cov = coverage.coverage(branch=True, source=["."])
+ cov.start()
+ import main
+ cov.stop()
+ report = self.get_report(cov).splitlines()
+ self.assertIn("mybranch 5 5 2 2 0%", report)
+
def run_TheCode_and_report_it(self):
"""A helper for the next few tests."""
cov = coverage.coverage()
cov.start()
import TheCode # pylint: disable=F0401,W0612
cov.stop()
-
- repout = StringIO()
- cov.report(file=repout, show_missing=False)
- report = repout.getvalue().replace('\\', '/')
- report = re.sub(r"\s+", " ", report)
- return report
+ return self.get_report(cov)
def test_bug_203_mixed_case_listed_twice_with_rc(self):
self.make_file("TheCode.py", "a = 1\n")