From 90189c05e6e8716b6cc6f78cf3e1e98f9fa045c1 Mon Sep 17 00:00:00 2001 From: Mickie Betz Date: Tue, 14 Apr 2015 16:53:30 -0400 Subject: Including generator yield statements when doing end of code calculations issue 324 --- tests/test_parser.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'tests') diff --git a/tests/test_parser.py b/tests/test_parser.py index 244d4c70..a39820e6 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -34,6 +34,19 @@ class PythonParserTest(CoverageTest): 2:1, 3:1, 4:2, 5:1, 7:1, 9:1, 10:1 }) + def test_generator_exit_counts(self): + parser = self.parse_source("""\ + # generators yield lines should only have one exit count + def gen(input): + for n in inp: + yield (i * 2 for i in range(n)) + + list(gen([1,2,3])) + """) + self.assertEqual(parser.exit_counts(), { + 2:1, 3:2, 4:1, 6:1 + }) + def test_try_except(self): parser = self.parse_source("""\ try: -- cgit v1.2.1 From 13db3dfa8471333b4410e217fab3e80502075846 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 18 Apr 2015 09:29:43 -0400 Subject: Tweaks to Mickie's changes. --- tests/test_parser.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/test_parser.py b/tests/test_parser.py index a39820e6..04c345ec 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -35,8 +35,9 @@ class PythonParserTest(CoverageTest): }) def test_generator_exit_counts(self): + # Generators' yield lines should only have one exit count. + # https://bitbucket.org/ned/coveragepy/issue/324/yield-in-loop-confuses-branch-coverage parser = self.parse_source("""\ - # generators yield lines should only have one exit count def gen(input): for n in inp: yield (i * 2 for i in range(n)) @@ -44,7 +45,7 @@ class PythonParserTest(CoverageTest): list(gen([1,2,3])) """) self.assertEqual(parser.exit_counts(), { - 2:1, 3:2, 4:1, 6:1 + 1:1, 2:2, 3:1, 5:1 }) def test_try_except(self): -- cgit v1.2.1 From de4cfde7b1f7b3d3bee11a26b4c1bb3ae598259c Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Mon, 20 Apr 2015 12:15:37 -0400 Subject: Fix branch coverage for yield statements. #308 #324 Turns out the "call" and "return" trace events are really "start frame" and "end frame". They happen not only when functions are entered and left, but when generators yield and resume. We aren't interested in arcs into and out of yield statements, so the trace functions look more closely to see what's really happening, and record an arc in human-friendly terms. Thanks for Mickie Betz for pushing on this bug, although her code is no longer here. :( --- tests/test_arcs.py | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test_parser.py | 6 ++-- 2 files changed, 91 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/test_arcs.py b/tests/test_arcs.py index d3717a88..1098dbdf 100644 --- a/tests/test_arcs.py +++ b/tests/test_arcs.py @@ -558,6 +558,93 @@ class ExceptionArcTest(CoverageTest): arcz_missing="67 7B", arcz_unpredicted="68") +class YieldTest(CoverageTest): + """Arc tests for generators.""" + + def test_yield_in_loop(self): + self.check_coverage("""\ + def gen(inp): + for n in inp: + yield n + + list(gen([1,2,3])) + """, + arcz=".1 .2 23 2. 32 15 5.", + arcz_missing="", + arcz_unpredicted="") + + def test_padded_yield_in_loop(self): + self.check_coverage("""\ + def gen(inp): + i = 2 + for n in inp: + i = 4 + yield n + i = 6 + i = 7 + + list(gen([1,2,3])) + """, + arcz=".1 19 9. .2 23 34 45 56 63 37 7.", + arcz_missing="", + arcz_unpredicted="") + + def test_bug_308(self): + self.check_coverage("""\ + def run(): + for i in range(10): + yield lambda: i + + for f in run(): + print(f()) + """, + arcz=".1 15 56 65 5. .2 23 32 2. .3 3-3", + arcz_missing="", + arcz_unpredicted="") + + self.check_coverage("""\ + def run(): + yield lambda: 100 + for i in range(10): + yield lambda: i + + for f in run(): + print(f()) + """, + arcz=".1 16 67 76 6. .2 23 34 43 3. 2-2 .4 4-4", + arcz_missing="", + arcz_unpredicted="") + + self.check_coverage("""\ + def run(): + yield lambda: 100 # no branch miss + + for f in run(): + print(f()) + """, + arcz=".1 14 45 54 4. .2 2. 2-2", + arcz_missing="", + arcz_unpredicted="") + + def test_bug_324(self): + # This code is tricky: the list() call pulls all the values from gen(), + # but each of them is a generator itself that is never iterated. As a + # result, the generator expression on line 3 is never entered or run. + self.check_coverage("""\ + def gen(inp): + for n in inp: + yield (i * 2 for i in range(n)) + + list(gen([1,2,3])) + """, + arcz= + ".1 15 5. " # The module level + ".2 23 32 2. " # The gen() function + ".3 3-3", # The generator expression + arcz_missing=".3 3-3", + arcz_unpredicted="") + + class MiscArcTest(CoverageTest): """Miscellaneous arc-measuring tests.""" diff --git a/tests/test_parser.py b/tests/test_parser.py index 04c345ec..81916a98 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -35,7 +35,6 @@ class PythonParserTest(CoverageTest): }) def test_generator_exit_counts(self): - # Generators' yield lines should only have one exit count. # https://bitbucket.org/ned/coveragepy/issue/324/yield-in-loop-confuses-branch-coverage parser = self.parse_source("""\ def gen(input): @@ -45,7 +44,10 @@ class PythonParserTest(CoverageTest): list(gen([1,2,3])) """) self.assertEqual(parser.exit_counts(), { - 1:1, 2:2, 3:1, 5:1 + 1:1, # def -> list + 2:2, # for -> yield; for -> exit + 3:2, # yield -> for; genexp exit + 5:1, # list -> exit }) def test_try_except(self): -- cgit v1.2.1