| # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 |
| # For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt |
| |
| """Tests for coverage.py's arc measurement.""" |
| |
| from __future__ import annotations |
| |
| import pytest |
| |
| from tests.coveragetest import CoverageTest |
| from tests.helpers import assert_count_equal |
| |
| import coverage |
| from coverage.data import sorted_lines |
| from coverage.files import abs_file |
| |
| |
| class SimpleArcTest(CoverageTest): |
| """Tests for coverage.py's arc measurement.""" |
| |
| def test_simple_sequence(self) -> None: |
| self.check_coverage( |
| """\ |
| a = 1 |
| b = 2 |
| """, |
| branchz="", |
| ) |
| self.check_coverage( |
| """\ |
| a = 1 |
| |
| b = 3 |
| """, |
| branchz="", |
| ) |
| self.check_coverage( |
| """\ |
| |
| a = 2 |
| b = 3 |
| |
| c = 5 |
| """, |
| branchz="", |
| ) |
| |
| def test_function_def(self) -> None: |
| self.check_coverage( |
| """\ |
| def foo(): |
| a = 2 |
| |
| foo() |
| """, |
| branchz="", |
| ) |
| |
| def test_if(self) -> None: |
| self.check_coverage( |
| """\ |
| a = 1 |
| if len([]) == 0: |
| a = 3 |
| assert a == 3 |
| """, |
| branchz="23 24", |
| branchz_missing="24", |
| ) |
| self.check_coverage( |
| """\ |
| a = 1 |
| if len([]) == 1: |
| a = 3 |
| assert a == 1 |
| """, |
| branchz="23 24", |
| branchz_missing="23", |
| ) |
| |
| def test_if_else(self) -> None: |
| self.check_coverage( |
| """\ |
| if len([]) == 0: |
| a = 2 |
| else: |
| a = 4 |
| assert a == 2 |
| """, |
| branchz="12 14", |
| branchz_missing="14", |
| ) |
| self.check_coverage( |
| """\ |
| if len([]) == 1: |
| a = 2 |
| else: |
| a = 4 |
| assert a == 4 |
| """, |
| branchz="12 14", |
| branchz_missing="12", |
| ) |
| |
| def test_compact_if(self) -> None: |
| self.check_coverage( |
| """\ |
| a = 1 |
| if len([]) == 0: a = 2 |
| assert a == 2 |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| self.check_coverage( |
| """\ |
| def fn(x): |
| if x % 2: return True |
| return False |
| a = fn(1) |
| assert a is True |
| """, |
| branchz="2. 23", |
| branchz_missing="23", |
| ) |
| |
| def test_multiline(self) -> None: |
| self.check_coverage( |
| """\ |
| a = ( |
| 2 + |
| 3 |
| ) |
| b = \\ |
| 6 |
| """, |
| branchz="", |
| ) |
| |
| def test_if_return(self) -> None: |
| self.check_coverage( |
| """\ |
| def if_ret(a): |
| if a: |
| return 3 |
| b = 4 |
| return 5 |
| x = if_ret(0) + if_ret(1) |
| assert x == 8 |
| """, |
| branchz="23 24", |
| branchz_missing="", |
| ) |
| |
| def test_dont_confuse_exit_and_else(self) -> None: |
| self.check_coverage( |
| """\ |
| def foo(): |
| if foo: |
| a = 3 |
| else: |
| a = 5 |
| return a |
| assert foo() == 3 # 7 |
| """, |
| branchz="23 25", |
| branchz_missing="25", |
| ) |
| self.check_coverage( |
| """\ |
| def foo(): |
| if foo: |
| a = 3 |
| else: |
| a = 5 |
| foo() # 6 |
| """, |
| branchz="23 25", |
| branchz_missing="25", |
| ) |
| |
| def test_bug_1184(self) -> None: |
| self.check_coverage( |
| """\ |
| def foo(x): |
| if x: |
| try: |
| 1/(x - 1) |
| except ZeroDivisionError: |
| pass |
| return x # 7 |
| |
| for i in range(3): # 9 |
| foo(i) |
| """, |
| branchz="23 27 9A 9.", |
| branchz_missing="", |
| ) |
| |
| def test_bug_1991(self) -> None: |
| # A bytecode was missing a line number, causing a KeyError in sysmon.py. |
| self.check_coverage( |
| """\ |
| def func(x, y): |
| for size in (x or ()) if y else (): |
| print(size) |
| |
| func([5], True) |
| """, |
| branchz="2-1 23", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "5\n" |
| |
| def test_bug_576(self) -> None: |
| self.check_coverage( |
| """\ |
| foo = True |
| |
| if foo == True: |
| foo = False |
| else: # pragma: no cover |
| pass |
| |
| if foo == False: |
| foo = True |
| else: # pragma: no cover |
| None |
| |
| print("Done") |
| """, |
| branchz="34 36 89 8B", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "Done\n" |
| |
| |
| class WithTest(CoverageTest): |
| """Arc-measuring tests involving context managers.""" |
| |
| def test_with(self) -> None: |
| self.check_coverage( |
| """\ |
| def example(): |
| with open("test", "w", encoding="utf-8") as f: |
| f.write("3") |
| a = 4 |
| |
| example() |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_with_return(self) -> None: |
| self.check_coverage( |
| """\ |
| def example(): |
| with open("test", "w", encoding="utf-8") as f: |
| f.write("3") |
| return 4 |
| |
| example() |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_bug_146(self) -> None: |
| # https://github.com/nedbat/coveragepy/issues/146 |
| self.check_coverage( |
| """\ |
| for i in range(2): |
| with open("test", "w", encoding="utf-8") as f: |
| print(3) |
| print(4) |
| print(5) |
| """, |
| branchz="12 15", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "3\n4\n3\n4\n5\n" |
| |
| def test_nested_with_return(self) -> None: |
| self.check_coverage( |
| """\ |
| def example(x): |
| with open("test", "w", encoding="utf-8") as f2: |
| a = 3 |
| with open("test2", "w", encoding="utf-8") as f4: |
| f2.write("5") |
| return 6 |
| |
| example(8) |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_break_through_with(self) -> None: |
| self.check_coverage( |
| """\ |
| for i in range(1+1): |
| with open("test", "w", encoding="utf-8") as f: |
| print(3) |
| break |
| print(5) |
| """, |
| branchz="12 15", |
| branchz_missing="15", |
| ) |
| |
| def test_continue_through_with(self) -> None: |
| self.check_coverage( |
| """\ |
| for i in range(1+1): |
| with open("test", "w", encoding="utf-8") as f: |
| print(3) |
| continue |
| print(5) |
| """, |
| branchz="12 15", |
| branchz_missing="", |
| ) |
| |
| # https://github.com/nedbat/coveragepy/issues/1270 |
| def test_raise_through_with(self) -> None: |
| cov = self.check_coverage( |
| """\ |
| from contextlib import nullcontext |
| def f(x): |
| with nullcontext(): |
| print(4) |
| raise Exception("Boo6") |
| print(6) |
| try: |
| f(8) |
| except Exception: |
| print("oops 10") |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| expected = "line 3 didn't jump to the function exit" |
| assert self.get_missing_arc_description(cov, 3, -2) == expected |
| |
| def test_untaken_if_through_with(self) -> None: |
| cov = self.check_coverage( |
| """\ |
| from contextlib import nullcontext |
| def f(x): |
| with nullcontext(): |
| print(4) |
| if x == 5: |
| print(6) |
| print(7) |
| f(8) |
| """, |
| branchz="56 57", |
| branchz_missing="56", |
| ) |
| assert self.stdout() == "4\n7\n" |
| expected = "line 3 didn't jump to the function exit" |
| assert self.get_missing_arc_description(cov, 3, -2) == expected |
| |
| def test_untaken_raise_through_with(self) -> None: |
| cov = self.check_coverage( |
| """\ |
| from contextlib import nullcontext |
| def f(x): |
| with nullcontext(): |
| print(4) |
| if x == 5: |
| raise Exception("Boo6") |
| print(7) |
| try: |
| f(9) |
| except Exception: |
| print("oops 11") |
| """, |
| branchz="56 57", |
| branchz_missing="56", |
| ) |
| assert self.stdout() == "4\n7\n" |
| expected = "line 3 didn't jump to the function exit" |
| assert self.get_missing_arc_description(cov, 3, -2) == expected |
| |
| def test_leaving_module(self) -> None: |
| cov = self.check_coverage( |
| """\ |
| print(a := 1) |
| if a == 1: |
| print(3) |
| """, |
| branchz="2. 23", |
| branchz_missing="2.", |
| ) |
| assert self.stdout() == "1\n3\n" |
| expected = "line 2 didn't exit the module because the condition on line 2 was always true" |
| assert self.get_missing_arc_description(cov, 2, -1) == expected |
| |
| def test_with_with_lambda(self) -> None: |
| self.check_coverage( |
| """\ |
| from contextlib import nullcontext |
| with nullcontext(lambda x: 2): |
| print(3) |
| print(4) |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_multiline_with(self) -> None: |
| # https://github.com/nedbat/coveragepy/issues/1880 |
| self.check_coverage( |
| """\ |
| import contextlib, itertools |
| nums = itertools.count() |
| with ( |
| contextlib.nullcontext() as x, |
| ): |
| while next(nums) < 6: |
| y = 7 |
| z = 8 |
| """, |
| branchz="67 68", |
| branchz_missing="", |
| ) |
| |
| def test_multi_multiline_with(self) -> None: |
| # https://github.com/nedbat/coveragepy/issues/1880 |
| self.check_coverage( |
| """\ |
| import contextlib, itertools |
| nums = itertools.count() |
| with ( |
| contextlib.nullcontext() as x, |
| contextlib.nullcontext() as y, |
| contextlib.nullcontext() as z, |
| ): |
| while next(nums) < 8: |
| y = 9 |
| z = 10 |
| """, |
| branchz="89 8A", |
| branchz_missing="", |
| ) |
| |
| def test_multi_multiline_with_backslash(self) -> None: |
| # https://github.com/nedbat/coveragepy/issues/1880 |
| self.check_coverage( |
| """\ |
| import contextlib, itertools |
| nums = itertools.count() |
| with contextlib.nullcontext() as x, \\ |
| contextlib.nullcontext() as y, \\ |
| contextlib.nullcontext() as z: |
| while next(nums) < 6: |
| y = 7 |
| z = 8 |
| """, |
| branchz="67 68", |
| branchz_missing="", |
| ) |
| |
| |
| class LoopArcTest(CoverageTest): |
| """Arc-measuring tests involving loops.""" |
| |
| def test_loop(self) -> None: |
| self.check_coverage( |
| """\ |
| for i in range(10): |
| a = i |
| assert a == 9 |
| """, |
| branchz="12 13", |
| branchz_missing="", |
| ) |
| self.check_coverage( |
| """\ |
| a = -1 |
| for i in range(0): |
| a = i |
| assert a == -1 |
| """, |
| branchz="23 24", |
| branchz_missing="23", |
| ) |
| |
| def test_nested_loop(self) -> None: |
| self.check_coverage( |
| """\ |
| for i in range(3): |
| for j in range(3): |
| a = i + j |
| assert a == 4 |
| """, |
| branchz="12 14 23 21", |
| branchz_missing="", |
| ) |
| |
| def test_break(self) -> None: |
| self.check_coverage( |
| """\ |
| for i in range(10): |
| a = i |
| break # 3 |
| a = 99 |
| assert a == 0 # 5 |
| """, |
| branchz="12 15", |
| branchz_missing="15", |
| ) |
| |
| def test_continue(self) -> None: |
| self.check_coverage( |
| """\ |
| for i in range(10): |
| a = i |
| continue # 3 |
| a = 99 |
| assert a == 9 # 5 |
| """, |
| branchz="12 15", |
| branchz_missing="", |
| ) |
| |
| def test_nested_breaks(self) -> None: |
| self.check_coverage( |
| """\ |
| for i in range(3): |
| for j in range(3): |
| a = i + j |
| break # 4 |
| if i == 2: |
| break |
| assert a == 2 and i == 2 # 7 |
| """, |
| branchz="12 17 23 25 51 56", |
| branchz_missing="17 25", |
| ) |
| |
| def test_if_1(self) -> None: |
| self.check_coverage( |
| """\ |
| a = 1 |
| if not not 1: |
| a = 3 |
| else: |
| a = 5 |
| assert a == 3 |
| """, |
| lines=[1, 2, 3, 6], |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_while_1(self) -> None: |
| # With "while 1", the loop knows it's constant. |
| self.check_coverage( |
| """\ |
| a, i = 1, 0 |
| while 1: |
| if i >= 3: |
| a = 4 |
| break |
| i += 1 |
| assert a == 4 and i == 3 |
| """, |
| branchz="34 36", |
| branchz_missing="", |
| ) |
| |
| def test_while_true(self) -> None: |
| self.check_coverage( |
| """\ |
| a, i = 1, 0 |
| while True: |
| if i >= 3: |
| a = 4 |
| break |
| i += 1 |
| assert a == 4 and i == 3 |
| """, |
| lines=[1, 2, 3, 4, 5, 6, 7], |
| branchz="34 36", |
| branchz_missing="", |
| ) |
| |
| def test_while_false(self) -> None: |
| self.check_coverage( |
| """\ |
| a, i = 1, 0 |
| while False: |
| 1/0 |
| assert a == 1 and i == 0 |
| """, |
| lines=[1, 2, 4], |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_while_not_false(self) -> None: |
| self.check_coverage( |
| """\ |
| a, i = 1, 0 |
| while not False: |
| if i >= 3: |
| a = 4 |
| break |
| i += 1 |
| assert a == 4 and i == 3 |
| """, |
| branchz="34 36", |
| branchz_missing="", |
| ) |
| |
| def test_zero_coverage_while_loop(self) -> None: |
| # https://github.com/nedbat/coveragepy/issues/502 |
| self.make_file("main.py", "print('done')") |
| self.make_file( |
| "zero.py", |
| """\ |
| def method(self): |
| while True: |
| return 1 |
| """, |
| ) |
| cov = coverage.Coverage(source=["."], branch=True) |
| self.start_import_stop(cov, "main") |
| assert self.stdout() == "done\n" |
| expected = "zero.py 3 3 0 0 0% 1-3" |
| report = self.get_report(cov, show_missing=True) |
| squeezed = self.squeezed_lines(report) |
| assert expected in squeezed[3] |
| |
| def test_bug_496_continue_in_constant_while(self) -> None: |
| # https://github.com/nedbat/coveragepy/issues/496 |
| self.check_coverage( |
| """\ |
| up = iter('ta') |
| while True: |
| char = next(up) |
| if char == 't': |
| continue |
| i = "line 6" |
| break |
| """, |
| branchz="45 46", |
| branchz_missing="", |
| ) |
| |
| def test_missing_while_body(self) -> None: |
| self.check_coverage( |
| """\ |
| a = 3; b = 0 |
| if 0: |
| while a > 0: |
| a -= 1 |
| assert a == 3 and b == 0 |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_for_if_else_for(self) -> None: |
| 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]) |
| """, |
| branchz="23 26 34 3. 9A 9-8 AB AE BC B9", |
| branchz_missing="26", |
| ) |
| |
| def test_for_else(self) -> None: |
| self.check_coverage( |
| """\ |
| def forelse(seq): |
| for n in seq: |
| if n > 5: |
| break |
| else: |
| print('None of the values were greater than 5') |
| print('Done') |
| forelse([1,2]) |
| """, |
| branchz="23 26 34 32", |
| branchz_missing="34", |
| ) |
| self.check_coverage( |
| """\ |
| def forelse(seq): |
| for n in seq: |
| if n > 5: |
| break |
| else: |
| print('None of the values were greater than 5') |
| print('Done') |
| forelse([1,6]) |
| """, |
| branchz="23 26 34 32", |
| branchz_missing="26", |
| ) |
| |
| def test_split_for(self) -> None: |
| self.check_coverage( |
| """\ |
| a = 0 |
| for (i |
| ) in [1,2,3,4,5]: |
| a += i |
| assert a == 15 |
| """, |
| lines=[1, 2, 4, 5], |
| branchz="24 25", |
| ) |
| |
| def test_while_else(self) -> None: |
| self.check_coverage( |
| """\ |
| def whileelse(seq): |
| while seq: |
| n = seq.pop() |
| if n > 4: |
| break |
| else: |
| n = 99 |
| return n |
| assert whileelse([1, 2]) == 99 |
| """, |
| branchz="23 27 45 42", |
| branchz_missing="45", |
| ) |
| self.check_coverage( |
| """\ |
| def whileelse(seq): |
| while seq: |
| n = seq.pop() |
| if n > 4: |
| break |
| else: |
| n = 99 |
| return n |
| assert whileelse([1, 5]) == 5 |
| """, |
| branchz="23 27 45 42", |
| branchz_missing="27 42", |
| ) |
| |
| def test_confusing_for_loop_bug_175(self) -> None: |
| self.check_coverage( |
| """\ |
| o = [(1,2), (3,4)] |
| o = [a for a in o] |
| for tup in o: |
| x = tup[0] |
| y = tup[1] |
| """, |
| branchz="34 3.", |
| branchz_missing="", |
| ) |
| self.check_coverage( |
| """\ |
| o = [(1,2), (3,4)] |
| for tup in [a for a in o]: |
| x = tup[0] |
| y = tup[1] |
| """, |
| branchz="23 2.", |
| branchz_missing="", |
| ) |
| |
| def test_incorrect_loop_exit_bug_1175(self) -> None: |
| self.check_coverage( |
| """\ |
| def wrong_loop(x): |
| if x: |
| for i in [3, 33]: |
| print(i+4) |
| else: |
| pass |
| |
| wrong_loop(8) |
| """, |
| branchz="23 26 34 3.", |
| branchz_missing="26", |
| ) |
| |
| # https://bugs.python.org/issue44672 |
| def test_incorrect_if_bug_1175(self) -> None: |
| self.check_coverage( |
| """\ |
| def wrong_loop(x): |
| if x: |
| if x: |
| print(4) |
| else: |
| pass |
| |
| wrong_loop(8) |
| """, |
| branchz="23 26 34 3.", |
| branchz_missing="26 3.", |
| ) |
| |
| def test_generator_expression(self) -> None: |
| # Generator expression: |
| self.check_coverage( |
| """\ |
| o = ((1,2), (3,4)) |
| o = (a for a in o) |
| for tup in o: |
| x = tup[0] |
| y = tup[1] |
| """, |
| branchz="34 3.", |
| branchz_missing="", |
| ) |
| |
| def test_generator_expression_another_way(self) -> None: |
| # https://bugs.python.org/issue44450 |
| # Generator expression: |
| self.check_coverage( |
| """\ |
| o = ((1,2), (3,4)) |
| o = (a for |
| a in |
| o) |
| for tup in o: |
| x = tup[0] |
| y = tup[1] |
| """, |
| branchz="56 5.", |
| branchz_missing="", |
| ) |
| |
| def test_other_comprehensions(self) -> None: |
| # Set comprehension: |
| self.check_coverage( |
| """\ |
| o = ((1,2), (3,4)) |
| o = {a for a in o} |
| for tup in o: |
| x = tup[0] |
| y = tup[1] |
| """, |
| branchz="34 3.", |
| branchz_missing="", |
| ) |
| # Dict comprehension: |
| self.check_coverage( |
| """\ |
| o = ((1,2), (3,4)) |
| o = {a:1 for a in o} |
| for tup in o: |
| x = tup[0] |
| y = tup[1] |
| """, |
| branchz="34 3.", |
| branchz_missing="", |
| ) |
| |
| def test_multiline_dict_comp(self) -> None: |
| # Multiline dict comp: |
| self.check_coverage( |
| """\ |
| # comment |
| d = \\ |
| { |
| i: |
| str(i) |
| for |
| i |
| in |
| range(9) |
| } |
| x = 11 |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| # Multi dict comp: |
| self.check_coverage( |
| """\ |
| # comment |
| d = \\ |
| { |
| (i, j): |
| str(i+j) |
| for |
| i |
| in |
| range(9) |
| for |
| j |
| in |
| range(13) |
| } |
| x = 15 |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| |
| class ExceptionArcTest(CoverageTest): |
| """Arc-measuring tests involving exception handling.""" |
| |
| def test_try_except(self) -> None: |
| self.check_coverage( |
| """\ |
| a, b = 1, 1 |
| try: |
| a = 3 |
| except: |
| b = 5 |
| assert a == 3 and b == 1 |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_raise_followed_by_statement(self) -> None: |
| self.check_coverage( |
| """\ |
| a, b = 1, 1 |
| try: |
| a = 3 |
| raise Exception("Yikes!") |
| a = 5 |
| except: |
| b = 7 |
| assert a == 3 and b == 7 |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_hidden_raise(self) -> None: |
| self.check_coverage( |
| """\ |
| a, b = 1, 1 |
| def oops(x): |
| if x % 2: |
| raise Exception("odd") |
| try: |
| a = 6 |
| oops(1) |
| a = 8 |
| except: |
| b = 10 |
| assert a == 6 and b == 10 |
| """, |
| branchz="34 3-2", |
| branchz_missing="3-2", |
| ) |
| |
| def test_except_with_type(self) -> None: |
| self.check_coverage( |
| """\ |
| a, b = 1, 1 |
| def oops(x): |
| if x % 2: |
| raise ValueError("odd") |
| def try_it(x): |
| try: |
| a = 7 |
| oops(x) |
| a = 9 |
| except ValueError: |
| b = 11 |
| return a |
| assert try_it(0) == 9 # C |
| assert try_it(1) == 7 # D |
| """, |
| branchz="34 3-2", |
| branchz_missing="", |
| ) |
| |
| def test_try_finally(self) -> None: |
| self.check_coverage( |
| """\ |
| a, c = 1, 1 |
| try: |
| a = 3 |
| finally: |
| c = 5 |
| assert a == 3 and c == 5 |
| """, |
| branchz="", |
| ) |
| 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 |
| """, |
| branchz="", |
| ) |
| self.check_coverage( |
| """\ |
| a, c, d = 1, 1, 1 |
| try: |
| try: |
| a = 4 |
| raise Exception("Yikes!") |
| # line 6 |
| finally: |
| c = 8 |
| except: |
| d = 10 # A |
| assert a == 4 and c == 8 and d == 10 # B |
| """, |
| branchz="", |
| ) |
| |
| def test_finally_in_loop(self) -> None: |
| self.check_coverage( |
| """\ |
| a, c, d, i = 1, 1, 1, 99 |
| try: |
| for i in range(5): |
| try: |
| a = 5 |
| if i > 0: |
| raise Exception("Yikes!") |
| a = 8 |
| finally: |
| c = 10 |
| except: |
| d = 12 # C |
| assert a == 5 and c == 10 and d == 12 # D |
| """, |
| branchz="34 3D 67 68", |
| branchz_missing="3D", |
| ) |
| self.check_coverage( |
| """\ |
| a, c, d, i = 1, 1, 1, 99 |
| try: |
| for i in range(5): |
| try: |
| a = 5 |
| if i > 10: |
| raise Exception("Yikes!") |
| a = 8 |
| finally: |
| c = 10 |
| except: |
| d = 12 # C |
| assert a == 8 and c == 10 and d == 1 # D |
| """, |
| branchz="34 3D 67 68", |
| branchz_missing="67", |
| ) |
| |
| def test_break_through_finally(self) -> None: |
| self.check_coverage( |
| """\ |
| a, c, d, i = 1, 1, 1, 99 |
| try: |
| for i in range(3): |
| try: |
| a = 5 |
| if i > 0: |
| break |
| a = 8 |
| finally: |
| c = 10 |
| except: |
| d = 12 # C |
| assert a == 5 and c == 10 and d == 1 # D |
| """, |
| branchz="34 3D 67 68", |
| branchz_missing="3D", |
| ) |
| |
| def test_break_continue_without_finally(self) -> None: |
| self.check_coverage( |
| """\ |
| a, c, d, i = 1, 1, 1, 99 |
| try: |
| for i in range(3): |
| try: |
| a = 5 |
| if i > 0: |
| break |
| continue |
| except: |
| c = 10 |
| except: |
| d = 12 # C |
| assert a == 5 and c == 1 and d == 1 # D |
| """, |
| branchz="34 3D 67 68", |
| branchz_missing="3D", |
| ) |
| |
| def test_continue_through_finally(self) -> None: |
| self.check_coverage( |
| """\ |
| a, b, c, d, i = 1, 1, 1, 1, 99 |
| try: |
| for i in range(3): |
| try: |
| a = 5 |
| if i > 0: |
| continue |
| b = 8 |
| finally: |
| c = 10 |
| except: |
| d = 12 # C |
| assert (a, b, c, d) == (5, 8, 10, 1) # D |
| """, |
| branchz="34 3D 67 68", |
| branchz_missing="", |
| ) |
| |
| def test_finally_in_loop_bug_92(self) -> None: |
| self.check_coverage( |
| """\ |
| for i in range(5): |
| try: |
| j = 3 |
| finally: |
| f = 5 |
| g = 6 |
| h = 7 |
| """, |
| branchz="12 17", |
| branchz_missing="", |
| ) |
| |
| def test_bug_212(self) -> None: |
| # "except Exception as e" is crucial here. |
| # Bug 212 said that the "if exc" line was incorrectly marked as only |
| # partially covered. |
| self.check_coverage( |
| """\ |
| def b(exc): |
| try: |
| while "no peephole".upper(): |
| raise Exception(exc) # 4 |
| except Exception as e: |
| if exc != 'expected': |
| raise |
| q = 8 |
| |
| b('expected') |
| try: |
| b('unexpected') # C |
| except: |
| pass |
| """, |
| branchz="34 3-1 67 68", |
| branchz_missing="3-1", |
| ) |
| |
| def test_except_finally(self) -> None: |
| 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 |
| """, |
| branchz="", |
| ) |
| self.check_coverage( |
| """\ |
| a, b, c = 1, 1, 1 |
| def oops(x): |
| if x % 2: raise Exception("odd") |
| try: |
| a = 5 |
| oops(1) |
| a = 7 |
| except: |
| b = 9 |
| finally: |
| c = 11 |
| assert a == 5 and b == 9 and c == 11 |
| """, |
| branchz="", |
| ) |
| |
| def test_multiple_except_clauses(self) -> None: |
| self.check_coverage( |
| """\ |
| a, b, c = 1, 1, 1 |
| try: |
| a = 3 |
| except ValueError: |
| b = 5 |
| except IndexError: |
| a = 7 |
| finally: |
| c = 9 |
| assert a == 3 and b == 1 and c == 9 |
| """, |
| branchz="", |
| ) |
| self.check_coverage( |
| """\ |
| a, b, c = 1, 1, 1 |
| try: |
| a = int("xyz") # ValueError |
| except ValueError: |
| b = 5 |
| except IndexError: |
| a = 7 |
| finally: |
| c = 9 |
| assert a == 1 and b == 5 and c == 9 |
| """, |
| branchz="", |
| ) |
| self.check_coverage( |
| """\ |
| a, b, c = 1, 1, 1 |
| try: |
| a = [1][3] # IndexError |
| except ValueError: |
| b = 5 |
| except IndexError: |
| a = 7 |
| finally: |
| c = 9 |
| assert a == 7 and b == 1 and c == 9 |
| """, |
| branchz="", |
| ) |
| self.check_coverage( |
| """\ |
| a, b, c = 1, 1, 1 |
| try: |
| try: |
| a = 4/0 # ZeroDivisionError |
| except ValueError: |
| b = 6 |
| except IndexError: |
| a = 8 |
| finally: |
| c = 10 |
| except ZeroDivisionError: |
| pass |
| assert a == 1 and b == 1 and c == 10 |
| """, |
| branchz="", |
| ) |
| |
| def test_return_finally(self) -> None: |
| self.check_coverage( |
| """\ |
| a = [1] |
| def check_token(data): |
| if data: |
| try: |
| return 5 |
| finally: |
| a.append(7) |
| return 8 |
| assert check_token(False) == 8 |
| assert a == [1] |
| assert check_token(True) == 5 |
| assert a == [1, 7] |
| """, |
| branchz="34 38", |
| branchz_missing="", |
| ) |
| |
| def test_except_jump_finally(self) -> None: |
| self.check_coverage( |
| """\ |
| def func(x): |
| a = f = g = 2 |
| try: |
| for i in range(4): |
| try: |
| 6/0 |
| except ZeroDivisionError: |
| if x == 'break': |
| a = 9 |
| break |
| elif x == 'continue': |
| a = 12 |
| continue |
| elif x == 'return': |
| a = 15 # F |
| return a, f, g, i # G |
| elif x == 'raise': # H |
| a = 18 # I |
| raise ValueError() # J |
| finally: |
| f = 21 # L |
| except ValueError: # M |
| g = 23 # N |
| return a, f, g, i # O |
| |
| assert func('break') == (9, 21, 2, 0) # Q |
| assert func('continue') == (12, 21, 2, 3) # R |
| assert func('return') == (15, 2, 2, 0) # S |
| assert func('raise') == (18, 21, 23, 0) # T |
| assert func('other') == (2, 21, 2, 3) # U 30 |
| """, |
| branchz="45 4O 89 8B BC BE EF EH HI HL", |
| branchz_missing="", |
| ) |
| |
| def test_else_jump_finally(self) -> None: |
| self.check_coverage( |
| """\ |
| def func(x): |
| a = f = g = 2 |
| try: |
| for i in range(4): |
| try: |
| b = 6 |
| except ZeroDivisionError: |
| pass |
| else: |
| if x == 'break': |
| a = 11 |
| break |
| elif x == 'continue': |
| a = 14 |
| continue |
| elif x == 'return': |
| a = 17 # H |
| return a, f, g, i # I |
| elif x == 'raise': # J |
| a = 20 # K |
| raise ValueError() # L |
| finally: |
| f = 23 # N |
| except ValueError: # O |
| g = 25 # P |
| return a, f, g, i # Q |
| |
| assert func('break') == (11, 23, 2, 0) # S |
| assert func('continue') == (14, 23, 2, 3) # T |
| assert func('return') == (17, 2, 2, 0) # U |
| assert func('raise') == (20, 23, 25, 0) # V |
| assert func('other') == (2, 23, 2, 3) # W 32 |
| """, |
| branchz="45 4Q AB AD DE DG GH GJ JK JN", |
| branchz_missing="", |
| ) |
| |
| |
| class YieldTest(CoverageTest): |
| """Arc tests for generators.""" |
| |
| def test_yield_in_loop(self) -> None: |
| self.check_coverage( |
| """\ |
| def gen(inp): |
| for n in inp: |
| yield n |
| |
| list(gen([1,2,3])) |
| """, |
| branchz="23 2-1", |
| branchz_missing="", |
| ) |
| |
| def test_padded_yield_in_loop(self) -> None: |
| 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])) |
| """, |
| branchz="34 37", |
| branchz_missing="", |
| ) |
| |
| def test_bug_308(self) -> None: |
| self.check_coverage( |
| """\ |
| def run(): |
| for i in range(10): |
| yield lambda: i |
| |
| for f in run(): |
| print(f()) |
| """, |
| branchz="23 2. 56 5.", |
| branchz_missing="", |
| ) |
| self.check_coverage( |
| """\ |
| def run(): |
| yield lambda: 100 |
| for i in range(10): |
| yield lambda: i |
| |
| for f in run(): |
| print(f()) |
| """, |
| branchz="34 3. 67 6.", |
| branchz_missing="", |
| ) |
| self.check_coverage( |
| """\ |
| def run(): |
| yield lambda: 100 # no branch miss |
| |
| for f in run(): |
| print(f()) |
| """, |
| branchz="45 4.", |
| branchz_missing="", |
| ) |
| |
| def test_bug_324(self) -> None: |
| # 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])) |
| """, |
| branchz="23 2.", |
| branchz_missing="", |
| ) |
| |
| def test_coroutines(self) -> None: |
| self.check_coverage( |
| """\ |
| def double_inputs(): |
| while len([1]): # avoid compiler differences |
| x = yield |
| x *= 2 |
| yield x |
| |
| gen = double_inputs() |
| next(gen) |
| print(gen.send(10)) |
| next(gen) |
| print(gen.send(6)) |
| """, |
| branchz="23 2-1", |
| branchz_missing="2-1", |
| ) |
| assert self.stdout() == "20\n12\n" |
| |
| def test_yield_from(self) -> None: |
| self.check_coverage( |
| """\ |
| def gen(inp): |
| i = 2 |
| for n in inp: |
| i = 4 |
| yield from range(3) |
| i = 6 |
| i = 7 |
| |
| list(gen([1,2,3])) |
| """, |
| branchz="34 37", |
| branchz_missing="", |
| ) |
| |
| def test_abandoned_yield(self) -> None: |
| # https://github.com/nedbat/coveragepy/issues/440 |
| self.check_coverage( |
| """\ |
| def gen(): |
| print(2) |
| yield 3 |
| print(4) |
| |
| print(next(gen())) |
| """, |
| lines=[1, 2, 3, 4, 6], |
| missing="4", |
| branchz="", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "2\n3\n" |
| |
| |
| class MatchCaseTest(CoverageTest): |
| """Tests of match-case.""" |
| |
| def test_match_case_with_default(self) -> None: |
| self.check_coverage( |
| """\ |
| for command in ["huh", "go home", "go n"]: |
| match command.split(): |
| case ["go", direction] if direction in "nesw": |
| match = f"go: {direction}" |
| case ["go", _]: |
| match = "no go" |
| case _: |
| match = "default" |
| print(match) |
| """, |
| branchz="12 1-1 34 35 56 57", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "default\nno go\ngo: n\n" |
| |
| def test_match_case_with_named_default(self) -> None: |
| self.check_coverage( |
| """\ |
| for command in ["huh", "go home", "go n"]: |
| match command.split(): |
| case ["go", direction] if direction in "nesw": |
| match = f"go: {direction}" |
| case ["go", _]: |
| match = "no go" |
| case _ as value: |
| match = "default" |
| print(match) |
| """, |
| branchz="12 1-1 34 35 56 57", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "default\nno go\ngo: n\n" |
| |
| def test_match_case_with_wildcard(self) -> None: |
| self.check_coverage( |
| """\ |
| for command in ["huh", "go home", "go n"]: |
| match command.split(): |
| case ["go", direction] if direction in "nesw": |
| match = f"go: {direction}" |
| case ["go", _]: |
| match = "no go" |
| case x: |
| match = f"default: {x}" |
| print(match) |
| """, |
| branchz="12 1-1 34 35 56 57", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "default: ['huh']\nno go\ngo: n\n" |
| |
| def test_match_case_without_wildcard(self) -> None: |
| self.check_coverage( |
| """\ |
| match = None |
| for command in ["huh", "go home", "go n"]: |
| match command.split(): |
| case ["go", direction] if direction in "nesw": |
| match = f"go: {direction}" |
| case ["go", _]: |
| match = "no go" |
| print(match) |
| """, |
| branchz="23 2-1 45 46 67 68", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "None\nno go\ngo: n\n" |
| |
| def test_absurd_wildcards(self) -> None: |
| # https://github.com/nedbat/coveragepy/issues/1421 |
| self.check_coverage( |
| """\ |
| def absurd(x): |
| match x: |
| case (3 | 99 | (999 | _)): |
| print("default") |
| absurd(5) |
| """, |
| # No branches because 3 always matches. |
| branchz="", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "default\n" |
| self.check_coverage( |
| """\ |
| def absurd(x): |
| match x: |
| case (3 | 99 | 999 as y): |
| print("not default") |
| absurd(5) |
| """, |
| branchz="34 3-1", |
| branchz_missing="34", |
| ) |
| assert self.stdout() == "" |
| self.check_coverage( |
| """\ |
| def absurd(x): |
| match x: |
| case (3 | 17 as y): |
| print("not default") |
| case 7: # 5 |
| print("also not default") |
| absurd(7) |
| """, |
| branchz="34 35 56 5-1", |
| branchz_missing="34 5-1", |
| ) |
| assert self.stdout() == "also not default\n" |
| self.check_coverage( |
| """\ |
| def absurd(x): |
| match x: |
| case 3: |
| print("not default") |
| case _ if x == 7: # 5 |
| print("also not default") |
| absurd(7) |
| """, |
| branchz="34 35 56 5-1", |
| branchz_missing="34 5-1", |
| ) |
| assert self.stdout() == "also not default\n" |
| |
| def test_split_match_case(self) -> None: |
| self.check_coverage( |
| """\ |
| def foo(x): |
| match x: |
| case ( |
| 1 |
| | 2 |
| ): |
| return "output: 1 or 2" |
| case _: |
| return "output: other" |
| |
| print(foo(1)) |
| print(foo(2)) |
| print(foo(3)) |
| """, |
| lines=[1, 2, 3, 7, 8, 9, 11, 12, 13], |
| missing="", |
| branchz="37 38", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "output: 1 or 2\noutput: 1 or 2\noutput: other\n" |
| |
| |
| class OptimizedIfTest(CoverageTest): |
| """Tests of if statements being optimized away.""" |
| |
| def test_optimized_away_if_0(self) -> None: |
| self.check_coverage( |
| """\ |
| a = 1 |
| if len([2]): |
| c = 3 |
| if 0: |
| if len([5]): |
| d = 6 |
| else: |
| e = 8 |
| f = 9 |
| """, |
| lines=[1, 2, 3, 4, 8, 9], |
| branchz="23 24", |
| branchz_missing="24", |
| ) |
| |
| def test_optimized_away_if_1(self) -> None: |
| self.check_coverage( |
| """\ |
| a = 1 |
| if len([2]): |
| c = 3 |
| if 1: |
| if len([5]): |
| d = 6 |
| else: |
| e = 8 |
| f = 9 |
| """, |
| lines=[1, 2, 3, 4, 5, 6, 9], |
| branchz="23 24 56 59", |
| branchz_missing="24 59", |
| ) |
| |
| def test_optimized_away_if_1_no_else(self) -> None: |
| self.check_coverage( |
| """\ |
| a = 1 |
| if 1: |
| b = 3 |
| c = 4 |
| d = 5 |
| """, |
| lines=[1, 2, 3, 4, 5], |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_optimized_if_nested(self) -> None: |
| self.check_coverage( |
| """\ |
| a = 1 |
| if 0: |
| if 0: |
| b = 4 |
| else: |
| c = 6 |
| else: |
| if 0: |
| d = 9 |
| else: |
| if 0: e = 11 |
| f = 12 |
| if 0: g = 13 |
| h = 14 |
| i = 15 |
| """, |
| lines=[1, 2, 8, 11, 12, 13, 14, 15], |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_dunder_debug(self) -> None: |
| # Since some of our tests use __debug__, let's make sure it is true as |
| # we expect |
| assert __debug__ |
| # Check that executed code has __debug__ |
| self.check_coverage( |
| """\ |
| assert __debug__, "assert __debug__" |
| """, |
| ) |
| # Check that if it didn't have debug, it would let us know. |
| with pytest.raises(AssertionError): |
| self.check_coverage( |
| """\ |
| assert not __debug__, "assert not __debug__" |
| """, |
| ) |
| |
| def test_if_debug(self) -> None: |
| self.check_coverage( |
| """\ |
| for value in [True, False]: |
| if value: |
| if __debug__: |
| x = 4 |
| else: |
| x = 6 |
| """, |
| branchz="12 1. 23 26", |
| branchz_missing="", |
| ) |
| |
| def test_if_not_debug(self) -> None: |
| self.check_coverage( |
| """\ |
| lines = set() |
| for value in [True, False]: |
| if value: |
| if not __debug__: |
| lines.add(5) |
| else: |
| lines.add(7) |
| assert lines == {7} |
| """, |
| branchz="23 28 34 37", |
| ) |
| |
| |
| class MiscArcTest(CoverageTest): |
| """Miscellaneous arc-measuring tests.""" |
| |
| def test_dict_literal(self) -> None: |
| self.check_coverage( |
| """\ |
| d = { |
| 'a': 2, |
| 'b': 3, |
| 'c': { |
| 'd': 5, |
| 'e': 6, |
| } |
| } |
| assert d |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| self.check_coverage( |
| """\ |
| d = \\ |
| { 'a': 2, |
| 'b': 3, |
| 'c': { |
| 'd': 5, |
| 'e': 6, |
| } |
| } |
| assert d |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_unpacked_literals(self) -> None: |
| self.check_coverage( |
| """\ |
| d = { |
| 'a': 2, |
| 'b': 3, |
| } |
| weird = { |
| **d, |
| **{'c': 7}, |
| 'd': 8, |
| } |
| assert weird['b'] == 3 |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| self.check_coverage( |
| """\ |
| l = [ |
| 2, |
| 3, |
| ] |
| weird = [ |
| *l, |
| *[7], |
| 8, |
| ] |
| assert weird[1] == 3 |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| @pytest.mark.parametrize("n", [10, 50, 100, 500, 1000, 2000, 10000]) |
| def test_pathologically_long_code_object(self, n: int) -> None: |
| # https://github.com/nedbat/coveragepy/issues/359 |
| # Long code objects sometimes cause problems. Originally, it was |
| # due to EXTENDED_ARG bytes codes. Then it showed a mistake in |
| # line-number packing. |
| code = ( |
| """\ |
| data = [ |
| """ |
| + "".join( |
| f"""\ |
| [ |
| {i}, {i}, {i}, {i}, {i}, {i}, {i}, {i}, {i}, {i}], |
| """ |
| for i in range(n) |
| ) |
| + """\ |
| ] |
| |
| print(len(data)) |
| """ |
| ) |
| self.check_coverage(code, branchz="") |
| assert self.stdout() == f"{n}\n" |
| |
| def test_partial_generators(self) -> None: |
| # https://github.com/nedbat/coveragepy/issues/475 |
| # Line 2 is executed completely. |
| # Line 3 is started but not finished, because zip ends before it finishes. |
| # Line 4 is never started. |
| self.check_coverage( |
| """\ |
| def f(a, b): |
| c = (i for i in a) # 2 |
| d = (j for j in b) # 3 |
| e = (k for k in b) # 4 |
| return dict(zip(c, d)) |
| |
| f(['a', 'b'], [1, 2, 3]) |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| |
| class DecoratorArcTest(CoverageTest): |
| """Tests of arcs with decorators.""" |
| |
| def test_function_decorator(self) -> None: |
| self.check_coverage( |
| """\ |
| def decorator(arg): |
| def _dec(f): |
| return f |
| return _dec |
| |
| @decorator(6) |
| @decorator( |
| len([8]), |
| ) |
| def my_function( |
| a=len([11]), |
| ): |
| x = 13 |
| a = 14 |
| my_function() |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_class_decorator(self) -> None: |
| self.check_coverage( |
| """\ |
| def decorator(arg): |
| def _dec(c): |
| return c |
| return _dec |
| |
| @decorator(6) |
| @decorator( |
| len([8]), |
| ) |
| class MyObject( |
| object |
| ): |
| X = 13 |
| a = 14 |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_bug_466a(self) -> None: |
| # A bad interaction between decorators and multi-line list assignments, |
| # believe it or not...! |
| # This example makes more sense when considered in tandem with 466b below. |
| self.check_coverage( |
| """\ |
| class Parser(object): |
| |
| @classmethod |
| def parse(cls): |
| formats = [ 5 ] |
| |
| |
| return None |
| |
| Parser.parse() |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_bug_466b(self) -> None: |
| # A bad interaction between decorators and multi-line list assignments, |
| # believe it or not...! |
| self.check_coverage( |
| """\ |
| class Parser(object): |
| |
| @classmethod |
| def parse(cls): |
| formats = [ |
| 6, |
| ] |
| return None |
| |
| Parser.parse() |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| |
| class LambdaArcTest(CoverageTest): |
| """Tests of lambdas""" |
| |
| def test_multiline_lambda(self) -> None: |
| self.check_coverage( |
| """\ |
| fn = (lambda x: |
| x + 2 |
| ) |
| assert fn(4) == 6 |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| self.check_coverage( |
| """\ |
| |
| fn = \\ |
| ( |
| lambda |
| x: |
| x |
| + |
| 8 |
| ) |
| assert fn(10) == 18 |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_unused_lambdas_are_confusing_bug_90(self) -> None: |
| self.check_coverage( |
| """\ |
| a = 1 |
| fn = lambda x: x |
| b = 3 |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_raise_with_lambda_looks_like_partial_branch(self) -> None: |
| self.check_coverage( |
| """\ |
| def ouch(fn): |
| 2/0 |
| a = b = c = d = 3 |
| try: |
| a = ouch(lambda: 5) |
| if a: |
| b = 7 |
| except ZeroDivisionError: |
| c = 9 |
| d = 10 |
| assert (a, b, c, d) == (3, 3, 9, 10) |
| """, |
| lines=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], |
| missing="6-7", |
| branchz="67 6A", |
| branchz_missing="67 6A", |
| ) |
| |
| def test_lambda_in_dict(self) -> None: |
| self.check_coverage( |
| """\ |
| x = 1 |
| x = 2 |
| d = { |
| 4: lambda: [], |
| 5: lambda: [], |
| 6: lambda: [], |
| 7: lambda: [], |
| } |
| |
| for k, v in d.items(): # 10 |
| if k & 1: |
| v() |
| """, |
| branchz="AB A. BA BC", |
| branchz_missing="", |
| ) |
| |
| |
| class AsyncTest(CoverageTest): |
| """Tests of the new async and await keywords in Python 3.5""" |
| |
| def test_async(self) -> None: |
| self.check_coverage( |
| """\ |
| import asyncio |
| |
| async def compute(x, y): # 3 |
| print(f"Compute {x} + {y} ...") |
| await asyncio.sleep(0.001) |
| return x + y # 6 |
| |
| async def print_sum(x, y): # 8 |
| result = (0 + |
| await compute(x, y) # A |
| ) |
| print(f"{x} + {y} = {result}") |
| |
| loop = asyncio.new_event_loop() # E |
| loop.run_until_complete(print_sum(1, 2)) |
| loop.close() # G |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "Compute 1 + 2 ...\n1 + 2 = 3\n" |
| |
| def test_async_for(self) -> None: |
| self.check_coverage( |
| """\ |
| import asyncio |
| |
| class AsyncIteratorWrapper: # 3 |
| def __init__(self, obj): # 4 |
| self._it = iter(obj) |
| |
| def __aiter__(self): # 7 |
| return self |
| |
| async def __anext__(self): # A |
| try: |
| return next(self._it) |
| except StopIteration: |
| raise StopAsyncIteration |
| |
| async def doit(): # G |
| async for letter in AsyncIteratorWrapper("abc"): |
| print(letter) |
| print(".") |
| |
| loop = asyncio.new_event_loop() # L |
| loop.run_until_complete(doit()) |
| loop.close() |
| """, |
| branchz="HI HJ", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "a\nb\nc\n.\n" |
| |
| def test_async_with(self) -> None: |
| self.check_coverage( |
| """\ |
| async def go(): |
| async with x: |
| pass |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| def test_async_decorator(self) -> None: |
| self.check_coverage( |
| """\ |
| def wrap(f): # 1 |
| return f |
| |
| @wrap # 4 |
| async def go(): |
| return |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| |
| # https://github.com/nedbat/coveragepy/issues/1158 |
| def test_bug_1158(self) -> None: |
| self.check_coverage( |
| """\ |
| import asyncio |
| |
| async def async_gen(): |
| yield 4 |
| |
| async def async_test(): |
| global a |
| a = 8 |
| async for i in async_gen(): |
| print(i + 10) |
| else: |
| a = 12 |
| |
| asyncio.run(async_test()) |
| assert a == 12 |
| """, |
| branchz="9A 9C", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "14\n" |
| |
| # https://github.com/nedbat/coveragepy/issues/1176 |
| # https://bugs.python.org/issue44622 |
| def test_bug_1176(self) -> None: |
| self.check_coverage( |
| """\ |
| import asyncio |
| |
| async def async_gen(): |
| yield 4 |
| |
| async def async_test(): |
| async for i in async_gen(): |
| print(i + 8) |
| |
| asyncio.run(async_test()) |
| """, |
| branchz="78 7-6", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "12\n" |
| |
| # https://github.com/nedbat/coveragepy/issues/1205 |
| def test_bug_1205(self) -> None: |
| self.check_coverage( |
| """\ |
| def func(): |
| if T(2): |
| if T(3): |
| if F(4): |
| if X(5): |
| return 6 |
| else: |
| return 8 |
| elif X(9) and Y: |
| return 10 |
| |
| T, F = (lambda _: True), (lambda _: False) |
| func() |
| """, |
| branchz="23 29 34 38 45 4. 56 5. 9A 9.", |
| branchz_missing="29 38 45 56 5. 9A 9.", |
| ) |
| |
| def test_bug_1999(self) -> None: |
| self.check_coverage( |
| """\ |
| import asyncio |
| |
| async def async_range(number): |
| for n in range(number): |
| yield n |
| |
| async def do_something(number): |
| try: |
| async for n in async_range(number): |
| print(n) |
| finally: |
| print("Done.") |
| |
| asyncio.run(do_something(3)) |
| """, |
| branchz="4-3 45 9A 9C", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "0\n1\n2\nDone.\n" |
| |
| |
| class AnnotationTest(CoverageTest): |
| """Tests using type annotations.""" |
| |
| def test_annotations(self) -> None: |
| self.check_coverage( |
| """\ |
| def f(x:str, y:int) -> str: |
| a:int = 2 |
| return f"{x}, {y}, {a}, 3" |
| print(f("x", 4)) |
| """, |
| branchz="", |
| branchz_missing="", |
| ) |
| assert self.stdout() == "x, 4, 2, 3\n" |
| |
| |
| class ExcludeTest(CoverageTest): |
| """Tests of exclusions to indicate known partial branches.""" |
| |
| def test_default(self) -> None: |
| # A number of forms of pragma comment are accepted. |
| self.check_coverage( |
| """\ |
| a = 1 |
| if a: #pragma: no branch |
| b = 3 |
| c = 4 |
| if c: # pragma NOBRANCH |
| d = 6 |
| e = 7 |
| if e:#\tpragma:\tno branch |
| f = 9 |
| import typing |
| if typing.TYPE_CHECKING: # only for mypy |
| g = 12 |
| else: |
| h = 14 |
| """, |
| lines=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 14], |
| missing="", |
| branchz="23 24 56 57 89 8A BC BE", |
| branchz_missing="", |
| ) |
| |
| def test_custom_pragmas(self) -> None: |
| self.check_coverage( |
| """\ |
| a = 1 |
| while a: # [only some] |
| c = 3 |
| break |
| assert c == 5-2 |
| """, |
| lines=[1, 2, 3, 4, 5], |
| partials=["only some"], |
| branchz="23 25", |
| branchz_missing="", |
| ) |
| |
| |
| class LineDataTest(CoverageTest): |
| """Tests that line_data gives us what we expect.""" |
| |
| def test_branch(self) -> None: |
| cov = coverage.Coverage(branch=True) |
| |
| self.make_file( |
| "fun1.py", |
| """\ |
| def fun1(x): |
| if x == 1: |
| return |
| |
| fun1(3) |
| """, |
| ) |
| |
| self.start_import_stop(cov, "fun1") |
| |
| data = cov.get_data() |
| fun1_lines = sorted_lines(data, abs_file("fun1.py")) |
| assert_count_equal(fun1_lines, [1, 2, 5]) |