summaryrefslogtreecommitdiff
path: root/coverage
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2009-10-16 07:25:56 -0400
committerNed Batchelder <ned@nedbatchelder.com>2009-10-16 07:25:56 -0400
commit9ca8f380e28b3bd315708da4d0c879549f9e3e42 (patch)
tree8fbc0925eb2bd741a62a829c4cb41dc2414cc105 /coverage
parent86f2a554175d899a227c726e9a894f8cfcba985e (diff)
downloadpython-coveragepy-9ca8f380e28b3bd315708da4d0c879549f9e3e42.tar.gz
Start unit testing the arc measurement. In arcs, -1 means enter or exit.
Diffstat (limited to 'coverage')
-rw-r--r--coverage/collector.py6
-rw-r--r--coverage/control.py35
-rw-r--r--coverage/data.py4
-rw-r--r--coverage/parser.py26
4 files changed, 42 insertions, 29 deletions
diff --git a/coverage/collector.py b/coverage/collector.py
index aadbab7..bc70027 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -49,7 +49,7 @@ class PyTracer:
if frame == self.last_exc_back:
# Someone forgot a return event.
if self.arcs and self.cur_file_data:
- self.cur_file_data[(self.last_line, 0)] = None
+ self.cur_file_data[(self.last_line, -1)] = None
self.cur_file_data, self.last_line = self.data_stack.pop()
self.last_exc_back = None
@@ -65,7 +65,7 @@ class PyTracer:
self.cur_file_data = self.data[tracename]
else:
self.cur_file_data = None
- self.last_line = 0
+ self.last_line = -1
elif event == 'line':
# Record an executed line.
if self.cur_file_data is not None:
@@ -76,7 +76,7 @@ class PyTracer:
self.last_line = frame.f_lineno
elif event == 'return':
if self.arcs and self.cur_file_data:
- self.cur_file_data[(self.last_line, 0)] = None
+ self.cur_file_data[(self.last_line, -1)] = None
# Leaving this function, pop the filename stack.
self.cur_file_data, self.last_line = self.data_stack.pop()
elif event == 'exception':
diff --git a/coverage/control.py b/coverage/control.py
index ce79cd3..952897d 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -3,7 +3,7 @@
import os, socket
from coverage.annotate import AnnotateReporter
-from coverage.backward import string_class
+from coverage.backward import string_class, sorted # pylint: disable-msg=W0622
from coverage.codeunit import code_unit_factory, CodeUnit
from coverage.collector import Collector
from coverage.data import CoverageData
@@ -330,45 +330,54 @@ class Analysis:
"""The results of analyzing a code unit."""
def __init__(self, cov, code_unit):
+ self.coverage = cov
self.code_unit = code_unit
from coverage.parser import CodeParser
- filename = code_unit.filename
- ext = os.path.splitext(filename)[1]
+ self.filename = self.code_unit.filename
+ ext = os.path.splitext(self.filename)[1]
source = None
if ext == '.py':
- if not os.path.exists(filename):
- source = cov.file_locator.get_zip_data(filename)
+ if not os.path.exists(self.filename):
+ source = self.coverage.file_locator.get_zip_data(self.filename)
if not source:
raise CoverageException(
- "No source for code '%s'." % code_unit.filename
+ "No source for code '%s'." % self.filename
)
self.parser = CodeParser(
- text=source, filename=filename, exclude=cov.exclude_re
+ text=source, filename=self.filename, exclude=self.coverage.exclude_re
)
self.statements, self.excluded, line_map = self.parser.parse_source()
# Identify missing statements.
self.missing = []
- execed = cov.data.executed_lines(filename)
+ 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 execed:
+ if l in self.executed:
break
else:
self.missing.append(line)
else:
- if line not in execed:
+ if line not in self.executed:
self.missing.append(line)
- self.filename = self.code_unit.filename
-
def missing_formatted(self):
return format_lines(self.statements, self.missing)
- def arc_info(self):
+ def arc_possibilities(self):
return self.parser.arc_info()
+
+ def arcs_executed(self):
+ return self.coverage.data.executed_arcs(self.filename)
+
+ def arcs_missing(self):
+ possible = self.arc_possibilities()
+ executed = self.arcs_executed()
+ missing = [p for p in possible if p not in executed]
+ return sorted(missing)
+
diff --git a/coverage/data.py b/coverage/data.py
index 26dc5aa..0c1fc3a 100644
--- a/coverage/data.py
+++ b/coverage/data.py
@@ -198,6 +198,10 @@ class CoverageData:
"""
return self.lines.get(filename) or {}
+ def executed_arcs(self, filename):
+ """A map containing all the arcs executed in `filename`."""
+ return self.arcs.get(filename) or {}
+
def summary(self, fullpath=False):
"""Return a dict summarizing the coverage data.
diff --git a/coverage/parser.py b/coverage/parser.py
index ba28102..9460f08 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -2,7 +2,7 @@
import glob, opcode, os, re, sys, token, tokenize
-from coverage.backward import set, StringIO # pylint: disable-msg=W0622
+from coverage.backward import set, sorted, StringIO # pylint: disable-msg=W0622
from coverage.bytecode import ByteCodes, CodeObjects
from coverage.misc import nice_pair, CoverageException
@@ -148,8 +148,7 @@ class CodeParser:
if new_l not in ignore:
lset.add(new_l)
lines = list(lset)
- lines.sort()
- return lines
+ return sorted(lines)
def parse_source(self):
"""Parse source text to find executable lines, excluded lines, etc.
@@ -169,7 +168,8 @@ class CodeParser:
def arc_info(self):
"""Get information about the arcs available in the code."""
- return self.byte_parser._all_arcs()
+ arcs = self.byte_parser._all_arcs()
+ return sorted(arcs)
class ByteParser:
@@ -281,7 +281,7 @@ class ByteParser:
class Chunk(object):
"""A sequence of bytecodes with exits to other bytecodes.
- An exit of None means the chunk can leave the code block (return).
+ An exit of -1 means the chunk can leave the code block (return).
"""
def __init__(self, byte, line=0):
@@ -313,7 +313,7 @@ class ByteParser:
chunk.exits.add(bc.jump_to)
if bc.op in self._code_enders:
- chunk.exits.add(None)
+ chunk.exits.add(-1)
if bc.op in self._chunk_enders:
chunk = None
@@ -332,12 +332,12 @@ class ByteParser:
byte_chunks = dict([(c.byte, c) for c in chunks])
# Build a map from byte offsets to actual lines reached.
- byte_lines = {None:[None]}
+ byte_lines = {-1:[-1]}
bytes_to_add = set([c.byte for c in chunks])
while bytes_to_add:
byte_to_add = bytes_to_add.pop()
- if byte_to_add in byte_lines or byte_to_add is None:
+ if byte_to_add in byte_lines or byte_to_add == -1:
continue
# Which lines does this chunk lead to?
@@ -365,8 +365,8 @@ class ByteParser:
lines.add(ch.line)
else:
for ex in ch.exits:
- if ex is None:
- lines.add(None)
+ if ex == -1:
+ lines.add(-1)
elif ex not in bytes_considered:
bytes_to_consider.append(ex)
@@ -382,7 +382,7 @@ class ByteParser:
for exit_line in byte_lines[ex]:
arcs.add((chunk.line, exit_line))
for line in byte_lines[0]:
- arcs.add((None, line))
+ arcs.add((-1, line))
return arcs
@@ -472,9 +472,9 @@ class AdHocMain(object):
arc_chars = {}
if options.chunks:
for lfrom, lto in sorted(arcs):
- if lfrom is None:
+ if lfrom == -1:
arc_chars[lto] = arc_chars.get(lto, '') + 'v'
- elif lto is None:
+ elif lto == -1:
arc_chars[lfrom] = arc_chars.get(lfrom, '') + '^'
else:
if lfrom == lto-1: