summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2009-10-23 09:24:27 -0400
committerNed Batchelder <ned@nedbatchelder.com>2009-10-23 09:24:27 -0400
commit1839a5c6da5b2d0e0a0ca0e3a1dec7b7bd983adc (patch)
tree35634d7717909dc95a6d58e697c76f6b814dd352
parentb5264460669282d5085b243de25d8f46be5c7ae9 (diff)
downloadpython-coveragepy-git-1839a5c6da5b2d0e0a0ca0e3a1dec7b7bd983adc.tar.gz
Properly shift multiline references to the first line of the statement. This code is sloppy, I just want it to work first.
-rw-r--r--coverage/control.py18
-rw-r--r--coverage/parser.py44
-rw-r--r--test/test_arcs.py11
3 files changed, 49 insertions, 24 deletions
diff --git a/coverage/control.py b/coverage/control.py
index da773d3e..76e17f65 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -354,17 +354,8 @@ class Analysis:
# Identify missing statements.
self.missing = []
self.executed = self.coverage.data.executed_lines(self.filename)
- for line in self.statements:
- lines = line_map.get(line)
- if lines:
- for l in range(lines[0], lines[1]+1):
- if l in self.executed:
- break
- else:
- self.missing.append(line)
- else:
- if line not in self.executed:
- self.missing.append(line)
+ exec1 = self.parser._map_to_first_lines(self.executed)
+ self.missing = sorted(set(self.statements) - set(exec1))
def missing_formatted(self):
return format_lines(self.statements, self.missing)
@@ -373,7 +364,10 @@ class Analysis:
return self.parser.arc_info()
def arcs_executed(self):
- return self.coverage.data.executed_arcs(self.filename)
+ executed = self.coverage.data.executed_arcs(self.filename)
+ m2fl = self.parser._map_to_first_line
+ executed = [(m2fl(l1), m2fl(l2)) for (l1,l2) in executed]
+ return executed
def arcs_missing(self):
possible = self.arc_possibilities()
diff --git a/coverage/parser.py b/coverage/parser.py
index aab50fb7..18106e07 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -126,7 +126,15 @@ class CodeParser:
# Find the starts of the executable statements.
self.statement_starts.update(self.byte_parser._find_statements())
- def _map_to_first_line(self, lines, ignore=None):
+ def _map_to_first_line(self, line):
+ rng = self.multiline.get(line)
+ if rng:
+ first_line = rng[0]
+ else:
+ first_line = line
+ return first_line
+
+ def _map_to_first_lines(self, lines, ignore=None):
"""Map the line numbers in `lines` to the correct first line of the
statement.
@@ -140,15 +148,10 @@ class CodeParser:
for l in lines:
if l in ignore:
continue
- rng = self.multiline.get(l)
- if rng:
- new_l = rng[0]
- else:
- new_l = l
+ new_l = self._map_to_first_line(l)
if new_l not in ignore:
lset.add(new_l)
- lines = list(lset)
- return sorted(lines)
+ return sorted(lset)
def parse_source(self):
"""Parse source text to find executable lines, excluded lines, etc.
@@ -157,18 +160,23 @@ class CodeParser:
2) a sorted list of excluded line numbers, and 3) a dict mapping line
numbers to pairs (lo,hi) for multi-line statements.
+ Reported line numbers are normalized to the first line of multi-line
+ statements.
+
"""
self._raw_parse()
- excluded_lines = self._map_to_first_line(self.excluded)
+ excluded_lines = self._map_to_first_lines(self.excluded)
ignore = excluded_lines + list(self.docstrings)
- lines = self._map_to_first_line(self.statement_starts, ignore)
+ lines = self._map_to_first_lines(self.statement_starts, ignore)
return lines, excluded_lines, self.multiline
def arc_info(self):
"""Get information about the arcs available in the code."""
arcs = self.byte_parser._all_arcs()
+ m2fl = self._map_to_first_line
+ arcs = [(m2fl(l1), m2fl(l2)) for (l1,l2) in arcs]
return sorted(arcs)
## Opcodes that guide the ByteParser.
@@ -388,6 +396,13 @@ class ByteParser:
return chunks
def _arcs(self):
+ """Find the executable arcs in the code.
+
+ Returns a set of pairs, (from,to). From and to are integer line
+ numbers. If from is -1, then the arc is an entrance into the code
+ object. If to is -1, the arc is an exit from the code object.
+
+ """
chunks = self._split_into_chunks()
# A map from byte offsets to chunks jumped into.
@@ -457,9 +472,14 @@ class ByteParser:
return chunks
def _all_arcs(self):
- arcs = []
+ """Get the set of all arcs in this code object and its children.
+
+ See `_arcs` for details.
+
+ """
+ arcs = set()
for bp in self.child_parsers():
- arcs.extend(bp._arcs())
+ arcs.update(bp._arcs())
return arcs
diff --git a/test/test_arcs.py b/test/test_arcs.py
index fa660f8b..bc54429e 100644
--- a/test/test_arcs.py
+++ b/test/test_arcs.py
@@ -89,6 +89,17 @@ class SimpleArcTest(CoverageTest):
""",
arcz=".1 14 45 5. .2 2. 23 3.", arcz_missing="23 3.")
+ def test_multiline(self):
+ self.check_coverage("""\
+ a = (
+ 2 +
+ 3
+ )
+ b = \\
+ 6
+ """,
+ arcz=".1 15 5.", arcz_missing="")
+
class LoopArcTest(CoverageTest):
"""Arc-measuring tests involving loops."""