Commits

Ned Batchelder committed 50fbe53

Jumps to jumps could make loops look like they go places they really don't, so start new chunks for every absolute jump. Fixes issue #39.

Comments (0)

Files changed (4)

 
 - Settings are now read from a .coveragerc file.
 
+- Fixed a problem with nested loops having their branch possibilities
+  mischaracterized: `issue 39`_.
+
 - Added an AUTHORS.txt file.
 
+.. _issue 39: http://bitbucket.org/ned/coveragepy/issue/39
+
 
 Version 3.2, 5 December 2009
 ----------------------------

coverage/parser.py

     'BREAK_LOOP', 'CONTINUE_LOOP',
     )
 
+# Opcodes that unconditionally begin a new code chunk.  By starting new chunks
+# with unconditional jump instructions, we neatly deal with jumps to jumps
+# properly.
+OPS_CHUNK_BEGIN = _opcode_set('JUMP_ABSOLUTE', 'JUMP_FORWARD')
+
 # Opcodes that push a block on the block stack.
 OPS_PUSH_BLOCK = _opcode_set('SETUP_LOOP', 'SETUP_EXCEPT', 'SETUP_FINALLY')
 
         ult = penult = None
 
         for bc in ByteCodes(self.code.co_code):
-            # Maybe have to start a new block
+            # Maybe have to start a new chunk
             if bc.offset in bytes_lines_map:
+                # Start a new chunk for each source line number.
                 if chunk:
                     chunk.exits.add(bc.offset)
                 chunk = Chunk(bc.offset, bytes_lines_map[bc.offset])
                 chunks.append(chunk)
+            elif bc.op in OPS_CHUNK_BEGIN:
+                # Jumps deserve their own unnumbered chunk.  This fixes
+                # problems with jumps to jumps getting confused.
+                if chunk:
+                    chunk.exits.add(bc.offset)
+                chunk = Chunk(bc.offset)
+                chunks.append(chunk)
 
             if not chunk:
                 chunk = Chunk(bc.offset)
             --timid                 bool                [run].timid
 
 
-        --include=directory         [run].include
-        --include=filename
-        --include=module
-        --exclude=directory         [run].exclude
+        --include=directory *       [run].include
+        --include=filename *
+        --include=module *
+        --exclude=directory *       [run].exclude
 
 
 

test/test_arcs.py

             arcz_missing="27"   # while loop never exits naturally.
             )
 
+    def test_for_if_else_for(self):
+        self.check_coverage("""\
+            def branches_2(l):
+                if l:
+                    for e in l:
+                        a = 4
+                else:
+                    a = 6
+
+            def branches_3(l):
+                for x in l:
+                    if x:
+                        for e in l:
+                            a = 12
+                    else:
+                        a = 14
+            
+            branches_2([0,1])
+            branches_3([0,1])
+            """,
+            arcz=
+                ".1 18 8G GH H. "
+                ".2 23 34 43 26 3. 6. "
+                ".9 9A 9. AB BC CB B9 AE E9",
+            arcz_missing="26 6."
+            )
+
 
 class ExceptionArcTest(CoverageTest):
     """Arc-measuring tests involving exception handling."""