summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2009-11-08 13:45:20 -0500
committerNed Batchelder <ned@nedbatchelder.com>2009-11-08 13:45:20 -0500
commit824ca447ec8a5f803bc4bf6aa0d80cc90f1cf3df (patch)
treed4b979ab12279e51d6ef3c5066b2ec2ec91bacf5
parent68cb3e52214c74cec6b94283972db7162f28c8cf (diff)
downloadpython-coveragepy-git-824ca447ec8a5f803bc4bf6aa0d80cc90f1cf3df.tar.gz
Added branch stats to results, and the summary report includes them.
-rw-r--r--TODO.txt1
-rw-r--r--coverage/results.py33
-rw-r--r--coverage/summary.py38
-rw-r--r--test/test_summary.py60
4 files changed, 95 insertions, 37 deletions
diff --git a/TODO.txt b/TODO.txt
index ca99056d..bdedd44f 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -8,6 +8,7 @@ Coverage TODO
- while TRUE claims to be partial?
+ Analysis class should do rolling up of stats also (actually Numbers)
- Update docs for --branch.
+- self.coverage.data.has_arcs is ugly.
* Speed
diff --git a/coverage/results.py b/coverage/results.py
index 2374d3af..138782e6 100644
--- a/coverage/results.py
+++ b/coverage/results.py
@@ -35,11 +35,20 @@ class Analysis(object):
exec1 = self.parser.first_lines(executed)
self.missing = sorted(set(self.statements) - set(exec1))
+ if self.coverage.data.has_arcs():
+ n_branches = self.total_branches()
+ mba = self.missing_branch_arcs()
+ n_missing_branches = sum([len(v) for v in mba.values()])
+ else:
+ n_branches = n_missing_branches = 0
+
self.numbers = Numbers(
n_files=1,
n_statements=len(self.statements),
n_excluded=len(self.excluded),
n_missing=len(self.missing),
+ n_branches=n_branches,
+ n_missing_branches=n_missing_branches,
)
def missing_formatted(self):
@@ -95,6 +104,15 @@ class Analysis(object):
return [l1 for l1,count in exit_counts.items() if count > 1]
+ def total_branches(self):
+ exit_counts = {}
+ for l1,l2 in self.arc_possibilities():
+ if l1 not in exit_counts:
+ exit_counts[l1] = 0
+ exit_counts[l1] += 1
+
+ return sum([count for count in exit_counts.values() if count > 1])
+
def missing_branch_arcs(self):
"""Return arcs that weren't executed from branch lines.
@@ -119,21 +137,30 @@ class Numbers(object):
up statistics across files.
"""
- def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0):
+ def __init__(self, n_files=0, n_statements=0, n_excluded=0, n_missing=0,
+ n_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_missing_branches = n_missing_branches
def _get_n_executed(self):
"""Returns the number of executed statements."""
return self.n_statements - self.n_missing
n_executed = property(_get_n_executed)
+ def _get_n_executed_branches(self):
+ """Returns the number of executed branches."""
+ return self.n_branches - self.n_missing_branches
+ n_executed_branches = property(_get_n_executed_branches)
+
def _get_pc_covered(self):
"""Returns a single percentage value for coverage."""
if self.n_statements > 0:
- pc_cov = 100.0 * self.n_executed / self.n_statements
+ pc_cov = 100.0 * (self.n_executed + self.n_executed_branches) / (self.n_statements + self.n_branches)
else:
pc_cov = 100.0
return pc_cov
@@ -145,6 +172,8 @@ class Numbers(object):
nums.n_statements = self.n_statements + other.n_statements
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
return nums
def __radd__(self, other):
diff --git a/coverage/summary.py b/coverage/summary.py
index 00e4af87..e0e9eba7 100644
--- a/coverage/summary.py
+++ b/coverage/summary.py
@@ -12,6 +12,7 @@ class SummaryReporter(Reporter):
def __init__(self, coverage, show_missing=True, ignore_errors=False):
super(SummaryReporter, self).__init__(coverage, ignore_errors)
self.show_missing = show_missing
+ self.branches = coverage.data.has_arcs()
def report(self, morfs, omit_prefixes=None, outfile=None):
"""Writes a report summarizing coverage statistics per module."""
@@ -22,12 +23,19 @@ class SummaryReporter(Reporter):
max_name = max([len(cu.name) for cu in self.code_units] + [5])
fmt_name = "%%- %ds " % max_name
fmt_err = "%s %s: %s\n"
- header = fmt_name % "Name" + " Stmts Exec Cover\n"
- fmt_coverage = fmt_name + "% 6d % 6d % 5d%%\n"
+ header = (fmt_name % "Name") + " Stmts Exec"
+ fmt_coverage = fmt_name + "%6d %6d"
+ if self.branches:
+ header += " Branch BrExec"
+ fmt_coverage += " %6d %6d"
+ header += " Cover"
+ fmt_coverage += " %5d%%"
if self.show_missing:
- header = header.replace("\n", " Missing\n")
- fmt_coverage = fmt_coverage.replace("\n", " %s\n")
- rule = "-" * (len(header)-1) + "\n"
+ header += " Missing"
+ fmt_coverage += " %s"
+ rule = "-" * len(header) + "\n"
+ header += "\n"
+ fmt_coverage += "\n"
if not outfile:
outfile = sys.stdout
@@ -42,12 +50,12 @@ class SummaryReporter(Reporter):
try:
analysis = self.coverage._analyze(cu)
nums = analysis.numbers
- args = (
- cu.name, nums.n_statements, nums.n_executed,
- nums.pc_covered
- )
+ args = (cu.name, nums.n_statements, nums.n_executed)
+ if self.branches:
+ args += (nums.n_branches, nums.n_executed_branches)
+ args += (nums.pc_covered,)
if self.show_missing:
- args = args + (analysis.missing_formatted(),)
+ args += (analysis.missing_formatted(),)
outfile.write(fmt_coverage % args)
total += nums
except KeyboardInterrupt: #pragma: no cover
@@ -59,10 +67,10 @@ class SummaryReporter(Reporter):
if total.n_files > 1:
outfile.write(rule)
- args = (
- "TOTAL", total.n_statements, total.n_executed,
- total.pc_covered
- )
+ args = ("TOTAL", total.n_statements, total.n_executed)
+ if self.branches:
+ args += (total.n_branches, total.n_executed_branches)
+ args += (total.pc_covered,)
if self.show_missing:
- args = args + ("",)
+ args += ("",)
outfile.write(fmt_coverage % args)
diff --git a/test/test_summary.py b/test/test_summary.py
index 1612777d..bbf5f026 100644
--- a/test/test_summary.py
+++ b/test/test_summary.py
@@ -36,7 +36,7 @@ class SummaryTest(CoverageTest):
def test_report(self):
out = self.run_command("coverage -x mycode.py")
self.assertEqual(out, 'done\n')
- report1 = self.report_from_command("coverage -r")
+ report = self.report_from_command("coverage -r")
# Name Stmts Exec Cover
# ---------------------------------------------------------------------
@@ -46,41 +46,61 @@ class SummaryTest(CoverageTest):
# ---------------------------------------------------------------------
# TOTAL 8 8 100%
- self.assert_("/coverage/__init__/" not in report1)
- self.assert_("/test/modules/covmod1 " in report1)
- self.assert_("/test/zipmods.zip/covmodzip1 " in report1)
- self.assert_("mycode " in report1)
- self.assertEqual(self.last_line_squeezed(report1), "TOTAL 8 8 100%")
+ self.assert_("/coverage/__init__/" not in report)
+ self.assert_("/test/modules/covmod1 " in report)
+ self.assert_("/test/zipmods.zip/covmodzip1 " in report)
+ self.assert_("mycode " in report)
+ self.assertEqual(self.last_line_squeezed(report), "TOTAL 8 8 100%")
def test_report_just_one(self):
# Try reporting just one module
self.run_command("coverage -x mycode.py")
- report2 = self.report_from_command("coverage -r mycode.py")
+ report = self.report_from_command("coverage -r mycode.py")
# Name Stmts Exec Cover
# ----------------------------
# mycode 4 4 100%
- self.assertEqual(self.line_count(report2), 3)
- self.assert_("/coverage/" not in report2)
- self.assert_("/test/modules/covmod1 " not in report2)
- self.assert_("/test/zipmods.zip/covmodzip1 " not in report2)
- self.assert_("mycode " in report2)
- self.assertEqual(self.last_line_squeezed(report2), "mycode 4 4 100%")
+ self.assertEqual(self.line_count(report), 3)
+ self.assert_("/coverage/" not in report)
+ self.assert_("/test/modules/covmod1 " not in report)
+ self.assert_("/test/zipmods.zip/covmodzip1 " not in report)
+ self.assert_("mycode " in report)
+ self.assertEqual(self.last_line_squeezed(report), "mycode 4 4 100%")
def test_report_omitting(self):
# Try reporting while omitting some modules
prefix = os.path.split(__file__)[0]
self.run_command("coverage -x mycode.py")
- report3 = self.report_from_command("coverage -r -o %s" % prefix)
+ report = self.report_from_command("coverage -r -o %s" % prefix)
# Name Stmts Exec Cover
# ----------------------------
# mycode 4 4 100%
- self.assertEqual(self.line_count(report3), 3)
- self.assert_("/coverage/" not in report3)
- self.assert_("/test/modules/covmod1 " not in report3)
- self.assert_("/test/zipmods.zip/covmodzip1 " not in report3)
- self.assert_("mycode " in report3)
- self.assertEqual(self.last_line_squeezed(report3), "mycode 4 4 100%")
+ self.assertEqual(self.line_count(report), 3)
+ self.assert_("/coverage/" not in report)
+ self.assert_("/test/modules/covmod1 " not in report)
+ self.assert_("/test/zipmods.zip/covmodzip1 " not in report)
+ self.assert_("mycode " in report)
+ self.assertEqual(self.last_line_squeezed(report), "mycode 4 4 100%")
+
+ def test_report_branches(self):
+ self.make_file("mybranch.py", """\
+ def branch(x):
+ if x:
+ print("x")
+ return x
+ branch(1)
+ """)
+ out = self.run_command("coverage run --branch mybranch.py")
+ self.assertEqual(out, 'x\n')
+ report = self.report_from_command("coverage -r")
+
+ # Name Stmts Exec Branch BrExec Cover
+ # --------------------------------------------
+ # mybranch 5 5 4 3 88%
+
+ self.assertEqual(self.line_count(report), 3)
+ self.assert_("mybranch " in report)
+ self.assertEqual(self.last_line_squeezed(report), "mybranch 5 5 4 3 88%")