summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt5
-rw-r--r--TODO.txt2
-rw-r--r--coverage/parser.py28
-rw-r--r--test/test_parser.py36
4 files changed, 62 insertions, 9 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index d630b76b..8f934252 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -7,9 +7,12 @@ Version 3.2b2
- Fixed some problems syntax coloring sources with line continuations and
source with tabs: `issue 30`_ and `issue 31`_.
-
+
+- Classes are no longer incorrectly marked as branches: `issue 32`_.
+
.. _issue 30: http://bitbucket.org/ned/coveragepy/issue/30
.. _issue 31: http://bitbucket.org/ned/coveragepy/issue/31
+.. _issue 32: http://bitbucket.org/ned/coveragepy/issue/32
Version 3.2b1, 10 November 2009
diff --git a/TODO.txt b/TODO.txt
index c5d1e9c5..01e886c1 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -174,7 +174,7 @@ x Why can't you specify execute (-x) and report (-r) in the same invocation?
+ Switch to a real test runner, like nose.
+ Test both the C trace function and the Python trace function.
-- parser.py has no direct tests.
++ parser.py has no direct tests.
- Tests about the .coverage file.
+ Tests about the --long-form of arguments.
+ Tests about overriding the .coverage filename.
diff --git a/coverage/parser.py b/coverage/parser.py
index 760e4e47..1e8b7792 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -45,6 +45,9 @@ class CodeParser(object):
# The line numbers of docstring lines.
self.docstrings = set()
+ # The line numbers of class definitions.
+ self.classdefs = set()
+
# A dict mapping line numbers to (lo,hi) for multi-line statements.
self.multiline = {}
@@ -94,6 +97,11 @@ class CodeParser(object):
indent += 1
elif toktype == token.DEDENT:
indent -= 1
+ elif toktype == token.NAME and ttext == 'class':
+ # Class definitions look like branches in the byte code, so
+ # we need to exclude them. The simplest way is to note the
+ # lines with the 'class' keyword.
+ self.classdefs.add(slineno)
elif toktype == token.OP and ttext == ':':
if not excluding and elineno in self.excluded:
# Start excluding a suite. We trigger off of the colon
@@ -203,7 +211,7 @@ class CodeParser(object):
"""
excluded_lines = self.first_lines(self.excluded)
exit_counts = {}
- for l1,l2 in self.arcs():
+ for l1, l2 in self.arcs():
if l1 == -1:
continue
if l1 in excluded_lines:
@@ -212,6 +220,10 @@ class CodeParser(object):
exit_counts[l1] = 0
exit_counts[l1] += 1
+ # Class definitions have one extra exit, so remove one for each:
+ for l in self.classdefs:
+ exit_counts[l] -= 1
+
return exit_counts
exit_counts = expensive(exit_counts)
@@ -604,7 +616,7 @@ class AdHocMain(object):
else:
print("Chunks: %r" % chunks)
arcs = bp._all_arcs()
- print("Arcs: %r" % arcs)
+ print("Arcs: %r" % sorted(arcs))
if options.source or options.tokens:
cp = CodeParser(filename=filename, exclude=r"no\s*cover")
@@ -624,13 +636,15 @@ class AdHocMain(object):
m0 = m1 = m2 = m3 = a = ' '
if lineno in cp.statement_starts:
m0 = '-'
- if lineno in cp.docstrings:
- m1 = '"'
- if lineno in cp.excluded:
- m2 = 'x'
exits = exit_counts.get(lineno, 0)
if exits > 1:
- m3 = str(exits)
+ m1 = str(exits)
+ if lineno in cp.docstrings:
+ m2 = '"'
+ if lineno in cp.classdefs:
+ m2 = 'C'
+ if lineno in cp.excluded:
+ m3 = 'x'
a = arc_chars.get(lineno, '').ljust(arc_width)
print("%4d %s%s%s%s%s %s" %
(lineno, m0, m1, m2, m3, a, ltext)
diff --git a/test/test_parser.py b/test/test_parser.py
new file mode 100644
index 00000000..5a66f873
--- /dev/null
+++ b/test/test_parser.py
@@ -0,0 +1,36 @@
+"""Tests for Coverage.py's code parsing."""
+
+import os, sys, textwrap
+
+sys.path.insert(0, os.path.split(__file__)[0]) # Force relative import for Py3k
+from coveragetest import CoverageTest
+
+from coverage.parser import CodeParser
+
+
+class ParserTest(CoverageTest):
+ """Tests for Coverage.py's code parsing."""
+
+ def parse_source(self, text):
+ """Parse `text` as source, and return the `CodeParser` used."""
+ text = textwrap.dedent(text)
+ cp = CodeParser(text)
+ cp.parse_source()
+ return cp
+
+ def test_exit_counts(self):
+ cp = self.parse_source("""\
+ # check some basic branch counting
+ class Foo:
+ def foo(self, a):
+ if a:
+ return 5
+ else:
+ return 7
+
+ class Bar:
+ pass
+ """)
+ self.assertEqual(cp.exit_counts(), {
+ 2:1, 3:1, 4:2, 5:1, 7:1, 9:1, 10:1
+ })