From 19db169ce9f4632a187fc63d0e58771bb254f229 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Tue, 19 Dec 2023 18:33:36 -0500 Subject: [PATCH] refactor(test): move parser tests from check_coverage to parse --- tests/test_coverage.py | 460 --------------------------------------- tests/test_parser.py | 477 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 475 insertions(+), 462 deletions(-) diff --git a/tests/test_coverage.py b/tests/test_coverage.py index 1752d5a6e..96639c072 100644 --- a/tests/test_coverage.py +++ b/tests/test_coverage.py @@ -1334,16 +1334,6 @@ def test_default(self) -> None: [1,3,5,7] ) - def test_simple(self) -> None: - self.check_coverage("""\ - a = 1; b = 2 - - if len([]): - a = 4 # -cc - """, - [1,3], "", excludes=['-cc'] - ) - def test_two_excludes(self) -> None: self.check_coverage("""\ a = 1; b = 2 @@ -1357,72 +1347,6 @@ def test_two_excludes(self) -> None: [1,3,5,7], "5", excludes=['-cc', '-xx'] ) - def test_excluding_if_suite(self) -> None: - self.check_coverage("""\ - a = 1; b = 2 - - if len([]): # not-here - a = 4 - b = 5 - c = 6 - assert a == 1 and b == 2 - """, - [1,7], "", excludes=['not-here'] - ) - - def test_excluding_if_but_not_else_suite(self) -> None: - self.check_coverage("""\ - a = 1; b = 2 - - if len([]): # not-here - a = 4 - b = 5 - c = 6 - else: - a = 8 - b = 9 - assert a == 8 and b == 9 - """, - [1,8,9,10], "", excludes=['not-here'] - ) - - def test_excluding_else_suite(self) -> None: - self.check_coverage("""\ - a = 1; b = 2 - - if 1==1: - a = 4 - b = 5 - c = 6 - else: #pragma: NO COVER - a = 8 - b = 9 - assert a == 4 and b == 5 and c == 6 - """, - [1,3,4,5,6,10], "", excludes=['#pragma: NO COVER'] - ) - self.check_coverage("""\ - a = 1; b = 2 - - if 1==1: - a = 4 - b = 5 - c = 6 - - # Lots of comments to confuse the else handler. - # more. - - else: #pragma: NO COVER - - # Comments here too. - - a = 8 - b = 9 - assert a == 4 and b == 5 and c == 6 - """, - [1,3,4,5,6,17], "", excludes=['#pragma: NO COVER'] - ) - def test_excluding_elif_suites(self) -> None: self.check_coverage("""\ a = 1; b = 2 @@ -1442,144 +1366,7 @@ def test_excluding_elif_suites(self) -> None: [1,3,4,5,6,11,12,13], "11-12", excludes=['#pragma: NO COVER'] ) - def test_excluding_oneline_if(self) -> None: - self.check_coverage("""\ - def foo(): - a = 2 - if len([]): x = 3 # no cover - b = 4 - - foo() - """, - [1,2,4,6], "", excludes=["no cover"] - ) - - def test_excluding_a_colon_not_a_suite(self) -> None: - self.check_coverage("""\ - def foo(): - l = list(range(10)) - a = l[:3] # no cover - b = 4 - - foo() - """, - [1,2,4,6], "", excludes=["no cover"] - ) - - def test_excluding_for_suite(self) -> None: - self.check_coverage("""\ - a = 0 - for i in [1,2,3,4,5]: #pragma: NO COVER - a += i - assert a == 15 - """, - [1,4], "", excludes=['#pragma: NO COVER'] - ) - self.check_coverage("""\ - a = 0 - for i in [1, - 2,3,4, - 5]: #pragma: NO COVER - a += i - assert a == 15 - """, - [1,6], "", excludes=['#pragma: NO COVER'] - ) - self.check_coverage("""\ - a = 0 - for i in [1,2,3,4,5 - ]: #pragma: NO COVER - a += i - break - a = 99 - assert a == 1 - """, - [1,7], "", excludes=['#pragma: NO COVER'] - ) - - def test_excluding_for_else(self) -> None: - self.check_coverage("""\ - a = 0 - for i in range(5): - a += i+1 - break - else: #pragma: NO COVER - a = 123 - assert a == 1 - """, - [1,2,3,4,7], "", excludes=['#pragma: NO COVER'] - ) - - def test_excluding_while(self) -> None: - self.check_coverage("""\ - a = 3; b = 0 - while a*b: #pragma: NO COVER - b += 1 - break - assert a == 3 and b == 0 - """, - [1,5], "", excludes=['#pragma: NO COVER'] - ) - self.check_coverage("""\ - a = 3; b = 0 - while ( - a*b - ): #pragma: NO COVER - b += 1 - break - assert a == 3 and b == 0 - """, - [1,7], "", excludes=['#pragma: NO COVER'] - ) - - def test_excluding_while_else(self) -> None: - self.check_coverage("""\ - a = 3; b = 0 - while a: - b += 1 - break - else: #pragma: NO COVER - b = 123 - assert a == 3 and b == 1 - """, - [1,2,3,4,7], "", excludes=['#pragma: NO COVER'] - ) - def test_excluding_try_except(self) -> None: - self.check_coverage("""\ - a = 0 - try: - a = 1 - except: #pragma: NO COVER - a = 99 - assert a == 1 - """, - [1,2,3,6], "", excludes=['#pragma: NO COVER'] - ) - self.check_coverage("""\ - a = 0 - try: - a = 1 - raise Exception("foo") - except: - a = 99 - assert a == 99 - """, - [1,2,3,4,5,6,7], "", excludes=['#pragma: NO COVER'] - ) - self.check_coverage("""\ - a = 0 - try: - a = 1 - raise Exception("foo") - except ImportError: #pragma: NO COVER - a = 99 - except: - a = 123 - assert a == 123 - """, - [1,2,3,4,7,8,9], "", excludes=['#pragma: NO COVER'] - ) self.check_coverage("""\ a = 0 try: @@ -1619,253 +1406,6 @@ def test_excluding_try_except_stranded_else(self) -> None: arcz_missing=arcz_missing, ) - def test_excluding_if_pass(self) -> None: - # From a comment on the coverage.py page by Michael McNeil Forbes: - self.check_coverage("""\ - def f(): - if False: # pragma: no cover - pass # This line still reported as missing - if False: # pragma: no cover - x = 1 # Now it is skipped. - - f() - """, - [1,7], "", excludes=["no cover"] - ) - - def test_excluding_function(self) -> None: - self.check_coverage("""\ - def fn(foo): #pragma: NO COVER - a = 1 - b = 2 - c = 3 - - x = 1 - assert x == 1 - """, - [6,7], "", excludes=['#pragma: NO COVER'] - ) - self.check_coverage("""\ - a = 0 - def very_long_function_to_exclude_name(very_long_argument1, - very_long_argument2): - pass - assert a == 0 - """, - [1,5], "", excludes=['function_to_exclude'] - ) - self.check_coverage("""\ - a = 0 - def very_long_function_to_exclude_name( - very_long_argument1, - very_long_argument2 - ): - pass - assert a == 0 - """, - [1,7], "", excludes=['function_to_exclude'] - ) - self.check_coverage("""\ - def my_func( - super_long_input_argument_0=0, - super_long_input_argument_1=1, - super_long_input_argument_2=2): - pass - - def my_func_2(super_long_input_argument_0=0, super_long_input_argument_1=1, super_long_input_argument_2=2): - pass - """, - [], "", excludes=['my_func'] - ) - self.check_coverage("""\ - def my_func( - super_long_input_argument_0=0, - super_long_input_argument_1=1, - super_long_input_argument_2=2): - pass - - def my_func_2(super_long_input_argument_0=0, super_long_input_argument_1=1, super_long_input_argument_2=2): - pass - """, - [1,5], "5", excludes=['my_func_2'] - ) - self.check_coverage("""\ - def my_func ( - super_long_input_argument_0=0, - super_long_input_argument_1=1, - super_long_input_argument_2=2): - pass - - def my_func_2 (super_long_input_argument_0=0, super_long_input_argument_1=1, super_long_input_argument_2=2): - pass - """, - [1,5], "5", excludes=['my_func_2'] - ) - self.check_coverage("""\ - def my_func ( - super_long_input_argument_0=0, - super_long_input_argument_1=1, - super_long_input_argument_2=2): - pass - - def my_func_2 (super_long_input_argument_0=0, super_long_input_argument_1=1, super_long_input_argument_2=2): - pass - """, - [], "5", excludes=['my_func'] - ) - self.check_coverage("""\ - def my_func \ - ( - super_long_input_argument_0=0, - super_long_input_argument_1=1 - ): - pass - - def my_func_2(super_long_input_argument_0=0, super_long_input_argument_1=1, super_long_input_argument_2=2): - pass - """, - [1,5], "5", excludes=['my_func_2'] - ) - self.check_coverage("""\ - def my_func \ - ( - super_long_input_argument_0=0, - super_long_input_argument_1=1 - ): - pass - - def my_func_2(super_long_input_argument_0=0, super_long_input_argument_1=1, super_long_input_argument_2=2): - pass - """, - [], "5", excludes=['my_func'] - ) - - def test_excluding_bug1713(self) -> None: - if env.PYVERSION >= (3, 10): - self.check_coverage("""\ - print("1") - - def hello_3(a): # pragma: no cover - match a: - case ("5" - | "6"): - print("7") - case "8": - print("9") - - print("11") - """, - [1, 11], - ) - self.check_coverage("""\ - print("1") - - def hello_3(a): # no thanks - if ("4" or - "5"): - print("6") - else: - print("8") - - print("10") - """, - [1, 10], "", excludes=["no thanks"], - ) - self.check_coverage("""\ - print(1) - - def func(a, b): - if a == 4: # pragma: no cover - func5() - if b: - print(7) - func8() - - print(10) - """, - [1, 3, 10] - ) - self.check_coverage("""\ - class Foo: # pragma: no cover - def greet(self): - print("hello world") - """, - [] - ) - - def test_excluding_method(self) -> None: - self.check_coverage("""\ - class Fooey: - def __init__(self): - self.a = 1 - - def foo(self): #pragma: NO COVER - return self.a - - x = Fooey() - assert x.a == 1 - """, - [1,2,3,8,9], "", excludes=['#pragma: NO COVER'] - ) - self.check_coverage("""\ - class Fooey: - def __init__(self): - self.a = 1 - - def very_long_method_to_exclude_name( - very_long_argument1, - very_long_argument2 - ): - pass - - x = Fooey() - assert x.a == 1 - """, - [1,2,3,11,12], "", excludes=['method_to_exclude'] - ) - - def test_excluding_class(self) -> None: - self.check_coverage("""\ - class Fooey: #pragma: NO COVER - def __init__(self): - self.a = 1 - - def foo(self): - return self.a - - x = 1 - assert x == 1 - """, - [8,9], "", excludes=['#pragma: NO COVER'] - ) - - def test_excludes_non_ascii(self) -> None: - self.check_coverage("""\ - # coding: utf-8 - a = 1; b = 2 - - if len([]): - a = 5 # ✘cover - """, - [2, 4], "", excludes=['✘cover'] - ) - - def test_formfeed(self) -> None: - # https://github.com/nedbat/coveragepy/issues/461 - self.check_coverage("""\ - x = 1 - assert len([]) == 0, ( - "This won't happen %s" % ("hello",) - ) - \f - x = 6 - assert len([]) == 0, ( - "This won't happen %s" % ("hello",) - ) - """, - [1, 6], "", excludes=['assert'], - ) - def test_excluded_comprehension_branches(self) -> None: # https://github.com/nedbat/coveragepy/issues/1271 self.check_coverage("""\ diff --git a/tests/test_parser.py b/tests/test_parser.py index 0b160c879..09f02f75d 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -22,10 +22,10 @@ class PythonParserTestBase(CoverageTest): run_in_temp_dir = False - def parse_text(self, text: str) -> PythonParser: + def parse_text(self, text: str, exclude: str = "nocover") -> PythonParser: """Parse `text` as source, and return the `PythonParser` used.""" text = textwrap.dedent(text) - parser = PythonParser(text=text, exclude="nocover") + parser = PythonParser(text=text, exclude=exclude) parser.parse_source() return parser @@ -196,6 +196,479 @@ def test_fuzzed_double_parse(self) -> None: class ExclusionParserTest(PythonParserTestBase): """Tests for the exclusion code in PythonParser.""" + def test_simple(self) -> None: + parser = self.parse_text("""\ + a = 1; b = 2 + + if len([]): + a = 4 # nocover + """, + ) + assert parser.statements == {1,3} + + def test_excluding_if_suite(self) -> None: + parser = self.parse_text("""\ + a = 1; b = 2 + + if len([]): # nocover + a = 4 + b = 5 + c = 6 + assert a == 1 and b == 2 + """, + ) + assert parser.statements == {1,7} + + def test_excluding_if_but_not_else_suite(self) -> None: + parser = self.parse_text("""\ + a = 1; b = 2 + + if len([]): # nocover + a = 4 + b = 5 + c = 6 + else: + a = 8 + b = 9 + assert a == 8 and b == 9 + """, + ) + assert parser.statements == {1,8,9,10} + + def test_excluding_else_suite(self) -> None: + parser = self.parse_text("""\ + a = 1; b = 2 + + if 1==1: + a = 4 + b = 5 + c = 6 + else: # nocover + a = 8 + b = 9 + assert a == 4 and b == 5 and c == 6 + """, + ) + assert parser.statements == {1,3,4,5,6,10} + parser = self.parse_text("""\ + a = 1; b = 2 + + if 1==1: + a = 4 + b = 5 + c = 6 + + # Lots of comments to confuse the else handler. + # more. + + else: # nocover + + # Comments here too. + + a = 8 + b = 9 + assert a == 4 and b == 5 and c == 6 + """, + ) + assert parser.statements == {1,3,4,5,6,17} + + def test_excluding_oneline_if(self) -> None: + parser = self.parse_text("""\ + def foo(): + a = 2 + if len([]): x = 3 # nocover + b = 4 + + foo() + """, + ) + assert parser.statements == {1,2,4,6} + + def test_excluding_a_colon_not_a_suite(self) -> None: + parser = self.parse_text("""\ + def foo(): + l = list(range(10)) + a = l[:3] # nocover + b = 4 + + foo() + """, + ) + assert parser.statements == {1,2,4,6} + + def test_excluding_for_suite(self) -> None: + parser = self.parse_text("""\ + a = 0 + for i in [1,2,3,4,5]: # nocover + a += i + assert a == 15 + """, + ) + assert parser.statements == {1,4} + parser = self.parse_text("""\ + a = 0 + for i in [1, + 2,3,4, + 5]: # nocover + a += i + assert a == 15 + """, + ) + assert parser.statements == {1,6} + parser = self.parse_text("""\ + a = 0 + for i in [1,2,3,4,5 + ]: # nocover + a += i + break + a = 99 + assert a == 1 + """, + ) + assert parser.statements == {1,7} + + def test_excluding_for_else(self) -> None: + parser = self.parse_text("""\ + a = 0 + for i in range(5): + a += i+1 + break + else: # nocover + a = 123 + assert a == 1 + """, + ) + assert parser.statements == {1,2,3,4,7} + + def test_excluding_while(self) -> None: + parser = self.parse_text("""\ + a = 3; b = 0 + while a*b: # nocover + b += 1 + break + assert a == 3 and b == 0 + """, + ) + assert parser.statements == {1,5} + parser = self.parse_text("""\ + a = 3; b = 0 + while ( + a*b + ): # nocover + b += 1 + break + assert a == 3 and b == 0 + """, + ) + assert parser.statements == {1,7} + + def test_excluding_while_else(self) -> None: + parser = self.parse_text("""\ + a = 3; b = 0 + while a: + b += 1 + break + else: # nocover + b = 123 + assert a == 3 and b == 1 + """, + ) + assert parser.statements == {1,2,3,4,7} + + def test_excluding_try_except(self) -> None: + parser = self.parse_text("""\ + a = 0 + try: + a = 1 + except: # nocover + a = 99 + assert a == 1 + """, + ) + assert parser.statements == {1,2,3,6} + parser = self.parse_text("""\ + a = 0 + try: + a = 1 + raise Exception("foo") + except: + a = 99 + assert a == 99 + """, + ) + assert parser.statements == {1,2,3,4,5,6,7} + parser = self.parse_text("""\ + a = 0 + try: + a = 1 + raise Exception("foo") + except ImportError: # nocover + a = 99 + except: + a = 123 + assert a == 123 + """, + ) + assert parser.statements == {1,2,3,4,7,8,9} + + def test_excluding_if_pass(self) -> None: + # From a comment on the coverage.py page by Michael McNeil Forbes: + parser = self.parse_text("""\ + def f(): + if False: # pragma: nocover + pass # This line still reported as missing + if False: # pragma: nocover + x = 1 # Now it is skipped. + + f() + """, + ) + assert parser.statements == {1,7} + + def test_excluding_function(self) -> None: + parser = self.parse_text("""\ + def fn(foo): # nocover + a = 1 + b = 2 + c = 3 + + x = 1 + assert x == 1 + """, + ) + assert parser.statements == {6,7} + parser = self.parse_text("""\ + a = 0 + def very_long_function_to_exclude_name(very_long_argument1, + very_long_argument2): + pass + assert a == 0 + """, + exclude="function_to_exclude", + ) + assert parser.statements == {1,5} + parser = self.parse_text("""\ + a = 0 + def very_long_function_to_exclude_name( + very_long_argument1, + very_long_argument2 + ): + pass + assert a == 0 + """, + exclude="function_to_exclude", + ) + assert parser.statements == {1,7} + parser = self.parse_text("""\ + def my_func( + super_long_input_argument_0=0, + super_long_input_argument_1=1, + super_long_input_argument_2=2): + pass + + def my_func_2(super_long_input_argument_0=0, super_long_input_argument_1=1, super_long_input_argument_2=2): + pass + """, + exclude="my_func", + ) + assert parser.statements == set() + parser = self.parse_text("""\ + def my_func( + super_long_input_argument_0=0, + super_long_input_argument_1=1, + super_long_input_argument_2=2): + pass + + def my_func_2(super_long_input_argument_0=0, super_long_input_argument_1=1, super_long_input_argument_2=2): + pass + """, + exclude="my_func_2", + ) + assert parser.statements == {1,5} + parser = self.parse_text("""\ + def my_func ( + super_long_input_argument_0=0, + super_long_input_argument_1=1, + super_long_input_argument_2=2): + pass + + def my_func_2 (super_long_input_argument_0=0, super_long_input_argument_1=1, super_long_input_argument_2=2): + pass + """, + exclude="my_func_2", + ) + assert parser.statements == {1,5} + parser = self.parse_text("""\ + def my_func ( + super_long_input_argument_0=0, + super_long_input_argument_1=1, + super_long_input_argument_2=2): + pass + + def my_func_2 (super_long_input_argument_0=0, super_long_input_argument_1=1, super_long_input_argument_2=2): + pass + """, + exclude="my_func", + ) + assert parser.statements == set() + parser = self.parse_text("""\ + def my_func \ + ( + super_long_input_argument_0=0, + super_long_input_argument_1=1 + ): + pass + + def my_func_2(super_long_input_argument_0=0, super_long_input_argument_1=1, super_long_input_argument_2=2): + pass + """, + exclude="my_func_2", + ) + assert parser.statements == {1,5} + parser = self.parse_text("""\ + def my_func \ + ( + super_long_input_argument_0=0, + super_long_input_argument_1=1 + ): + pass + + def my_func_2(super_long_input_argument_0=0, super_long_input_argument_1=1, super_long_input_argument_2=2): + pass + """, + exclude="my_func", + ) + assert parser.statements == set() + + def test_excluding_bug1713(self) -> None: + if env.PYVERSION >= (3, 10): + parser = self.parse_text("""\ + print("1") + + def hello_3(a): # pragma: nocover + match a: + case ("5" + | "6"): + print("7") + case "8": + print("9") + + print("11") + """, + ) + assert parser.statements == {1, 11} + parser = self.parse_text("""\ + print("1") + + def hello_3(a): # nocover + if ("4" or + "5"): + print("6") + else: + print("8") + + print("10") + """, + ) + assert parser.statements == {1, 10} + parser = self.parse_text("""\ + print(1) + + def func(a, b): + if a == 4: # nocover + func5() + if b: + print(7) + func8() + + print(10) + """, + ) + assert parser.statements == {1, 3, 10} + parser = self.parse_text("""\ + class Foo: # pragma: nocover + def greet(self): + print("hello world") + """, + ) + assert parser.statements == set() + + def test_excluding_method(self) -> None: + parser = self.parse_text("""\ + class Fooey: + def __init__(self): + self.a = 1 + + def foo(self): # nocover + return self.a + + x = Fooey() + assert x.a == 1 + """, + ) + assert parser.statements == {1,2,3,8,9} + parser = self.parse_text("""\ + class Fooey: + def __init__(self): + self.a = 1 + + def very_long_method_to_exclude_name( + very_long_argument1, + very_long_argument2 + ): + pass + + x = Fooey() + assert x.a == 1 + """, + exclude="method_to_exclude", + ) + assert parser.statements == {1,2,3,11,12} + + def test_excluding_class(self) -> None: + parser = self.parse_text("""\ + class Fooey: # nocover + def __init__(self): + self.a = 1 + + def foo(self): + return self.a + + x = 1 + assert x == 1 + """, + ) + assert parser.statements == {8,9} + + def test_excludes_non_ascii(self) -> None: + parser = self.parse_text("""\ + # coding: utf-8 + a = 1; b = 2 + + if len([]): + a = 5 # ✘cover + """, + exclude="✘cover", + ) + assert parser.statements == {2, 4} + + def test_formfeed(self) -> None: + # https://github.com/nedbat/coveragepy/issues/461 + parser = self.parse_text("""\ + x = 1 + assert len([]) == 0, ( + "This won't happen %s" % ("hello",) + ) + \f + x = 6 + assert len([]) == 0, ( + "This won't happen %s" % ("hello",) + ) + """, + exclude="assert", + ) + assert parser.statements == {1, 6} + @xfail_pypy38 def test_decorator_pragmas(self) -> None: parser = self.parse_text("""\