summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNed Batchelder <ned@nedbatchelder.com>2009-10-20 11:59:35 -0400
committerNed Batchelder <ned@nedbatchelder.com>2009-10-20 11:59:35 -0400
commit7f53916e61839c18b3e06f56e898f1df55afed1d (patch)
treef39577426803b0cd4661fe732c3d02fb9cce14f3
parent832ee468e711e6d33fb49fb718e60a85c970a8ef (diff)
downloadpython-coveragepy-git-7f53916e61839c18b3e06f56e898f1df55afed1d.tar.gz
More exception flow testing. This stuff is kind of involved...
-rw-r--r--coverage/collector.py2
-rw-r--r--coverage/control.py5
-rw-r--r--coverage/parser.py21
-rw-r--r--test/test_arcs.py93
4 files changed, 108 insertions, 13 deletions
diff --git a/coverage/collector.py b/coverage/collector.py
index bc700279..36788438 100644
--- a/coverage/collector.py
+++ b/coverage/collector.py
@@ -70,6 +70,7 @@ class PyTracer:
# Record an executed line.
if self.cur_file_data is not None:
if self.arcs:
+ #print "lin", self.last_line, frame.f_lineno
self.cur_file_data[(self.last_line, frame.f_lineno)] = None
else:
self.cur_file_data[frame.f_lineno] = None
@@ -80,6 +81,7 @@ class PyTracer:
# Leaving this function, pop the filename stack.
self.cur_file_data, self.last_line = self.data_stack.pop()
elif event == 'exception':
+ #print "exc", self.last_line, frame.f_lineno
self.last_exc_back = frame.f_back
return self._trace
diff --git a/coverage/control.py b/coverage/control.py
index 5e857aa6..da773d3e 100644
--- a/coverage/control.py
+++ b/coverage/control.py
@@ -384,5 +384,8 @@ class Analysis:
def arcs_unpredicted(self):
possible = self.arc_possibilities()
executed = self.arcs_executed()
- unpredicted = [e for e in executed if e not in possible]
+ # Exclude arcs here which connect a line to itself. They can occur
+ # in executed data in some cases. This is where they can cause trouble,
+ # and here is where it's the least burden to remove them.
+ unpredicted = [e for e in executed if e not in possible and e[0] != e[1]]
return sorted(unpredicted)
diff --git a/coverage/parser.py b/coverage/parser.py
index 74490189..7b899d12 100644
--- a/coverage/parser.py
+++ b/coverage/parser.py
@@ -173,8 +173,13 @@ class CodeParser:
## Opcodes that guide the ByteParser.
+def _opcode(name):
+ """Return the opcode by name from the opcode module."""
+ return opcode.opmap[name]
+
def _opcode_set(*names):
- return set([opcode.opmap[name] for name in names])
+ """Return a set of opcodes by the names in `names`."""
+ return set([_opcode(name) for name in names])
# Opcodes that leave the code object.
OPS_CODE_END = _opcode_set('RETURN_VALUE')
@@ -191,12 +196,13 @@ OPS_PUSH_BLOCK = _opcode_set('SETUP_LOOP', 'SETUP_EXCEPT', 'SETUP_FINALLY')
# Opcodes that pop a block from the block stack.
OPS_POP_BLOCK = _opcode_set('POP_BLOCK')
-# Opcodes that break a loop.
-OPS_BREAK = _opcode_set('BREAK_LOOP')
-
# Opcodes that have a jump destination, but aren't really a jump.
OPS_NO_JUMP = _opcode_set('SETUP_EXCEPT', 'SETUP_FINALLY')
+# Individual opcodes we need below.
+OP_BREAK_LOOP = _opcode('BREAK_LOOP')
+OP_END_FINALLY = _opcode('END_FINALLY')
+
class ByteParser:
"""Parse byte codes to understand the structure of code."""
@@ -352,12 +358,15 @@ class ByteParser:
block_stack.pop()
elif bc.op in OPS_CHUNK_END:
# This opcode forces the end of the chunk.
- if bc.op in OPS_BREAK:
+ if bc.op == OP_BREAK_LOOP:
# A break is implicit: jump where the top of the
# block_stack points.
chunk.exits.add(block_stack[-1])
chunk = None
-
+ elif bc.op == OP_END_FINALLY:
+ if block_stack:
+ chunk.exits.add(block_stack[-1])
+
if chunks:
chunks[-1].length = bc.next_offset - chunks[-1].byte
for i in range(len(chunks)-1):
diff --git a/test/test_arcs.py b/test/test_arcs.py
index 0a7850a8..820ce8b1 100644
--- a/test/test_arcs.py
+++ b/test/test_arcs.py
@@ -72,7 +72,24 @@ class SimpleArcTest(CoverageTest):
assert a == 4
""",
arcz=".1 12 25 14 45 5.", arcz_missing="12 25")
-
+
+ def test_compact_if(self):
+ self.check_coverage("""\
+ a = 1
+ if len([]) == 0: a = 2
+ assert a == 2
+ """,
+ arcz=".1 12 23 3.", arcz_missing="")
+ self.check_coverage("""\
+ def fn(x):
+ if x % 2: return True
+ return False
+ a = fn(1)
+ assert a == True
+ """,
+ arcz=".1 14 45 5. .2 2. 23 3.", arcz_missing="23 3.")
+
+
class LoopArcTest(CoverageTest):
"""Arc-measuring tests involving loops."""
@@ -132,9 +149,11 @@ class LoopArcTest(CoverageTest):
""",
arcz=".1 12 23 34 45 25 56 51 67 17 7.", arcz_missing="17 25")
+
class ExceptionArcTest(CoverageTest):
+ """Arc-measuring tests involving exception handling."""
- def test_no_exception(self):
+ def test_try_except(self):
self.check_coverage("""\
a, b = 1, 1
try:
@@ -144,8 +163,6 @@ class ExceptionArcTest(CoverageTest):
assert a == 3 and b == 1
""",
arcz=".1 12 23 36 45 56 6.", arcz_missing="45 56")
-
- def test_raise(self):
self.check_coverage("""\
a, b = 1, 1
try:
@@ -159,7 +176,71 @@ class ExceptionArcTest(CoverageTest):
arcz=".1 12 23 34 58 67 78 8.",
arcz_missing="58", arcz_unpredicted="46")
- def xest_xx(self):
+ def test_hidden_raise(self):
+ self.check_coverage("""\
+ a, b = 1, 1
+ def oops(x):
+ if x % 2: raise Exception("odd")
+ try:
+ a = 5
+ oops(1)
+ a = 7
+ except:
+ b = 9
+ assert a == 5 and b == 9
+ """,
+ arcz=".1 12 .3 3. 24 45 56 67 7A 89 9A A.",
+ arcz_missing="67 7A", arcz_unpredicted="68")
+
+ def test_try_finally(self):
+ self.check_coverage("""\
+ a, c = 1, 1
+ try:
+ a = 3
+ finally:
+ c = 5
+ assert a == 3 and c == 5
+ """,
+ arcz=".1 12 23 35 56 6.", arcz_missing="")
self.check_coverage("""\
+ a, c, d = 1, 1, 1
+ try:
+ try:
+ a = 4
+ finally:
+ c = 6
+ except:
+ d = 8
+ assert a == 4 and c == 6 and d == 1 # 9
""",
- arcz="", arcz_missing="")
+ arcz=".1 12 23 34 46 67 78 89 69 9.",
+ arcz_missing="67 78 89", arcz_unpredicted="")
+ self.check_coverage("""\
+ a, c, d = 1, 1, 1
+ try:
+ try:
+ a = 4
+ raise Exception("Yikes!")
+ a = 6
+ finally:
+ c = 8
+ except:
+ d = 10 # A
+ assert a == 4 and c == 8 and d == 10 # B
+ """,
+ arcz=".1 12 23 34 45 68 89 8B 9A AB B.",
+ arcz_missing="68 8B", arcz_unpredicted="58")
+
+ if sys.hexversion >= 0x02050000:
+ def test_except_finally_no_exception(self):
+ self.check_coverage("""\
+ a, b, c = 1, 1, 1
+ try:
+ a = 3
+ except:
+ b = 5
+ finally:
+ c = 7
+ assert a == 3 and b == 1 and c == 7
+ """,
+ arcz=".1 12 23 45 37 57 78 8.", arcz_missing="45 57")