Coverage for tests / test_parser.py: 100.000%
337 statements
« prev ^ index » next coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000
« prev ^ index » next coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000
1# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
2# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
4"""Tests for coverage.py's code parsing."""
6from __future__ import annotations 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
8import ast 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
9import re 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
10import textwrap 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
11from unittest import mock 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
13import pytest 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
15from coverage import env 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
16from coverage.exceptions import NoSource, NotPython 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
17from coverage.parser import PythonParser, is_constant_test_expr 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
19from tests.coveragetest import CoverageTest 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
20from tests.helpers import arcz_to_arcs 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
23class PythonParserTestBase(CoverageTest): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
24 """Tests for coverage.py's Python code parsing."""
26 run_in_temp_dir = False 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
28 def parse_text(self, text: str, exclude: str = "nocover") -> PythonParser: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
29 """Parse `text` as source, and return the `PythonParser` used."""
30 text = textwrap.dedent(text) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
31 parser = PythonParser(text=text, exclude=exclude) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
32 parser.parse_source() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
33 return parser 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
36class PythonParserTest(PythonParserTestBase): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
37 """Tests of coverage.parser."""
39 def test_exit_counts(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
40 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
41 # check some basic branch counting
42 class Foo:
43 def foo(self, a):
44 if a:
45 return 5
46 else:
47 return 7
49 class Bar:
50 pass
51 """)
52 assert parser.exit_counts() == { 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
53 2: 1,
54 3: 1,
55 4: 2,
56 5: 1,
57 7: 1,
58 9: 1,
59 10: 1,
60 }
62 def test_try_except(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
63 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
64 try:
65 a = 2
66 except ValueError:
67 a = 4
68 except ZeroDivideError:
69 a = 6
70 except:
71 a = 8
72 b = 9
73 """)
74 assert parser.exit_counts() == { 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
75 1: 1,
76 2: 1,
77 3: 1,
78 4: 1,
79 5: 1,
80 6: 1,
81 7: 1,
82 8: 1,
83 9: 1,
84 }
86 def test_excluded_classes(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
87 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
88 class Foo:
89 def __init__(self):
90 pass
92 if len([]): # nocover
93 class Bar:
94 pass
95 """)
96 assert parser.exit_counts() == {2: 1, 3: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
98 def test_missing_branch_to_excluded_code(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
99 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
100 if fooey:
101 a = 2
102 else: # nocover
103 a = 4
104 b = 5
105 """)
106 assert parser.exit_counts() == {1: 1, 2: 1, 5: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
107 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
108 def foo():
109 if fooey:
110 a = 3
111 else:
112 a = 5
113 b = 6
114 """)
115 assert parser.exit_counts() == {1: 1, 2: 2, 3: 1, 5: 1, 6: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
116 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
117 def foo():
118 if fooey:
119 a = 3
120 else: # nocover
121 a = 5
122 b = 6
123 """)
124 assert parser.exit_counts() == {1: 1, 2: 1, 3: 1, 6: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
126 @pytest.mark.parametrize( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
127 "text",
128 [
129 pytest.param("0 spaces\n 2\n 1", id="bad_indent"),
130 pytest.param("'''", id="string_eof"),
131 pytest.param("$hello", id="dollar"),
132 # on 3.10 this passes ast.parse but fails on tokenize.generate_tokens
133 pytest.param(
134 "\r'\\\n'''",
135 id="leading_newline_eof",
136 marks=[
137 pytest.mark.skipif(env.PYVERSION >= (3, 12), reason="parses fine in 3.12"),
138 ],
139 ),
140 ],
141 )
142 def test_not_python(self, text: str) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
143 msg = r"Couldn't parse '<code>' as Python source: ['\"].*['\"] at line \d+" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
144 with pytest.raises(NotPython, match=msg): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
145 _ = self.parse_text(text) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
147 def test_empty_decorated_function(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
148 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
149 def decorator(func):
150 return func
152 @decorator
153 def foo(self):
154 '''Docstring'''
156 @decorator
157 def bar(self):
158 pass
159 """)
161 assert parser.statements == {1, 2, 4, 5, 8, 9, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
162 assert parser.arcs() == set(arcz_to_arcs("14 45 58 89 9. 2. A-8")) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
163 assert parser.exit_counts() == {1: 1, 2: 1, 4: 1, 5: 1, 8: 1, 9: 1, 10: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
165 def test_nested_context_managers(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
166 # https://github.com/coveragepy/coveragepy/issues/1876
167 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
168 a = 1
169 with suppress(ValueError):
170 with suppress(ValueError):
171 x = 4
172 with suppress(ValueError):
173 x = 6
174 with suppress(ValueError):
175 x = 8
176 a = 9
177 """)
179 one_nine = set(range(1, 10)) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
180 assert parser.statements == one_nine 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
181 assert parser.exit_counts() == dict.fromkeys(one_nine, 1) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
183 def test_module_docstrings(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
184 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
185 '''The docstring on line 1'''
186 a = 2
187 """)
188 assert {2} == parser.statements 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
190 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
191 # Docstring is not line 1
192 '''The docstring on line 2'''
193 a = 3
194 """)
195 assert {3} == parser.statements 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
197 def test_fuzzed_double_parse(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
198 # https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=50381
199 # The second parse used to raise `TypeError: 'NoneType' object is not iterable`
200 msg = ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
201 r"(EOF in multi-line statement)" # before 3.12.0b1
202 + r"|(unmatched ']')" # after 3.12.0b1
203 )
204 with pytest.raises(NotPython, match=msg): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
205 self.parse_text("]") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
206 with pytest.raises(NotPython, match=msg): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
207 self.parse_text("]") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
209 def test_bug_1891(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
210 # These examples exercise code paths I thought were impossible.
211 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
212 res = siblings(
213 'configure',
214 **ca,
215 )
216 """)
217 assert parser.exit_counts() == {1: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
218 # In conftest.py, we silence the SyntaxWarning this code causes. If
219 # we remove this code, we can probably remove that warning.
220 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
221 def g2():
222 try:
223 return 2
224 finally:
225 return 3
226 """)
227 assert parser.exit_counts() == {1: 1, 2: 1, 3: 1, 5: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
229 def test_fuzzed_weirdness(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
230 # This used to cause a `min of empty sequence` error:
231 self.parse_text("\r\\\n\n\t\t\\\n\n") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
234class ExclusionParserTest(PythonParserTestBase): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
235 """Tests for the exclusion code in PythonParser."""
237 def test_simple(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
238 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
239 """\
240 a = 1; b = 2
242 if len([]):
243 a = 4 # nocover
244 """,
245 )
246 assert parser.statements == {1, 3} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
248 def test_excluding_if_suite(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
249 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
250 """\
251 a = 1; b = 2
253 if len([]): # nocover
254 a = 4
255 b = 5
256 c = 6
257 assert a == 1 and b == 2
258 """,
259 )
260 assert parser.statements == {1, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
262 def test_excluding_if_but_not_else_suite(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
263 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
264 """\
265 a = 1; b = 2
267 if len([]): # nocover
268 a = 4
269 b = 5
270 c = 6
271 else:
272 a = 8
273 b = 9
274 assert a == 8 and b == 9
275 """,
276 )
277 assert parser.statements == {1, 8, 9, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
279 def test_excluding_else_suite(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
280 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
281 """\
282 a = 1; b = 2
284 if 1==1:
285 a = 4
286 b = 5
287 c = 6
288 else: # nocover
289 a = 8
290 b = 9
291 assert a == 4 and b == 5 and c == 6
292 """,
293 )
294 assert parser.statements == {1, 3, 4, 5, 6, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
295 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
296 """\
297 a = 1; b = 2
299 if 1==1:
300 a = 4
301 b = 5
302 c = 6
304 # Lots of comments to confuse the else handler.
305 # more.
307 else: # nocover
309 # Comments here too.
311 a = 8
312 b = 9
313 assert a == 4 and b == 5 and c == 6
314 """,
315 )
316 assert parser.statements == {1, 3, 4, 5, 6, 17} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
318 def test_excluding_oneline_if(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
319 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
320 """\
321 def foo():
322 a = 2
323 if len([]): x = 3 # nocover
324 b = 4
326 foo()
327 """,
328 )
329 assert parser.statements == {1, 2, 4, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
331 def test_excluding_a_colon_not_a_suite(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
332 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
333 """\
334 def foo():
335 l = list(range(10))
336 a = l[:3] # nocover
337 b = 4
339 foo()
340 """,
341 )
342 assert parser.statements == {1, 2, 4, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
344 def test_excluding_for_suite(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
345 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
346 """\
347 a = 0
348 for i in [1,2,3,4,5]: # nocover
349 a += i
350 assert a == 15
351 """,
352 )
353 assert parser.statements == {1, 4} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
354 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
355 """\
356 a = 0
357 for i in [1,
358 2,3,4,
359 5]: # nocover
360 a += i
361 assert a == 15
362 """,
363 )
364 assert parser.statements == {1, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
365 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
366 """\
367 a = 0
368 for i in [1,2,3,4,5
369 ]: # nocover
370 a += i
371 break
372 a = 99
373 assert a == 1
374 """,
375 )
376 assert parser.statements == {1, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
378 def test_excluding_for_else(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
379 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
380 """\
381 a = 0
382 for i in range(5):
383 a += i+1
384 break
385 else: # nocover
386 a = 123
387 assert a == 1
388 """,
389 )
390 assert parser.statements == {1, 2, 3, 4, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
392 def test_excluding_while(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
393 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
394 """\
395 a = 3; b = 0
396 while a*b: # nocover
397 b += 1
398 break
399 assert a == 3 and b == 0
400 """,
401 )
402 assert parser.statements == {1, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
403 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
404 """\
405 a = 3; b = 0
406 while (
407 a*b
408 ): # nocover
409 b += 1
410 break
411 assert a == 3 and b == 0
412 """,
413 )
414 assert parser.statements == {1, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
416 def test_excluding_while_else(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
417 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
418 """\
419 a = 3; b = 0
420 while a:
421 b += 1
422 break
423 else: # nocover
424 b = 123
425 assert a == 3 and b == 1
426 """,
427 )
428 assert parser.statements == {1, 2, 3, 4, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
430 def test_excluding_try_except(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
431 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
432 """\
433 a = 0
434 try:
435 a = 1
436 except: # nocover
437 a = 99
438 assert a == 1
439 """,
440 )
441 assert parser.statements == {1, 2, 3, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
442 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
443 """\
444 a = 0
445 try:
446 a = 1
447 raise Exception("foo")
448 except:
449 a = 99
450 assert a == 99
451 """,
452 )
453 assert parser.statements == {1, 2, 3, 4, 5, 6, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
454 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
455 """\
456 a = 0
457 try:
458 a = 1
459 raise Exception("foo")
460 except ImportError: # nocover
461 a = 99
462 except:
463 a = 123
464 assert a == 123
465 """,
466 )
467 assert parser.statements == {1, 2, 3, 4, 7, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
469 def test_excluding_if_pass(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
470 # From a comment on the coverage.py page by Michael McNeil Forbes:
471 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
472 """\
473 def f():
474 if False: # pragma: nocover
475 pass # This line still reported as missing
476 if False: # pragma: nocover
477 x = 1 # Now it is skipped.
479 f()
480 """,
481 )
482 assert parser.statements == {1, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
484 def test_multiline_if_no_branch(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
485 # From https://github.com/coveragepy/coveragepy/issues/754
486 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
487 """\
488 if (this_is_a_verylong_boolean_expression == True # pragma: no branch
489 and another_long_expression and here_another_expression):
490 do_something()
491 """,
492 )
493 parser2 = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
494 """\
495 if this_is_a_verylong_boolean_expression == True and another_long_expression \\
496 and here_another_expression: # pragma: no branch
497 do_something()
498 """,
499 )
500 assert parser.statements == parser2.statements == {1, 3} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
501 pragma_re = ".*pragma: no branch.*" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
502 assert parser.lines_matching(pragma_re) == parser2.lines_matching(pragma_re) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
504 def test_excluding_function(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
505 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
506 """\
507 def fn(foo): # nocover
508 a = 1
509 b = 2
510 c = 3
512 x = 1
513 assert x == 1
514 """,
515 )
516 assert parser.statements == {6, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
517 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
518 """\
519 a = 0
520 def very_long_function_to_exclude_name(very_long_argument1,
521 very_long_argument2):
522 pass
523 assert a == 0
524 """,
525 exclude="function_to_exclude",
526 )
527 assert parser.statements == {1, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
528 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
529 """\
530 a = 0
531 def very_long_function_to_exclude_name(
532 very_long_argument1,
533 very_long_argument2
534 ):
535 pass
536 assert a == 0
537 """,
538 exclude="function_to_exclude",
539 )
540 assert parser.statements == {1, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
541 # linters and formatters can't agree about whether strings are too
542 # long, so format in the long variable names that make them too long.
543 long_arg = "super_long_input_argument" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
544 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
545 f"""\
546 def my_func(
547 {long_arg}_0=0,
548 {long_arg}_1=1,
549 {long_arg}_2=2):
550 pass
552 def my_func_2({long_arg}_0=0, {long_arg}_1=1, {long_arg}_2=2):
553 pass
554 """,
555 exclude="my_func",
556 )
557 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
558 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
559 f"""\
560 def my_func(
561 {long_arg}_0=0,
562 {long_arg}_1=1,
563 {long_arg}_2=2):
564 pass
566 def my_func_2({long_arg}_0=0, {long_arg}_1=1, {long_arg}_2=2):
567 pass
568 """,
569 exclude="my_func_2",
570 )
571 assert parser.statements == {1, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
572 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
573 f"""\
574 def my_func (
575 {long_arg}_0=0,
576 {long_arg}_1=1,
577 {long_arg}_2=2):
578 pass
580 def my_func_2 ({long_arg}_0=0, {long_arg}_1=1, {long_arg}_2=2):
581 pass
582 """,
583 exclude="my_func_2",
584 )
585 assert parser.statements == {1, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
586 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
587 f"""\
588 def my_func (
589 {long_arg}_0=0,
590 {long_arg}_1=1,
591 {long_arg}_2=2):
592 pass
594 def my_func_2 ({long_arg}_0=0, {long_arg}_1=1, {long_arg}_2=2):
595 pass
596 """,
597 exclude="my_func",
598 )
599 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
600 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
601 f"""\
602 def my_func \
603 (
604 {long_arg}_0=0,
605 {long_arg}_1=1
606 ):
607 pass
609 def my_func_2({long_arg}_0=0, {long_arg}_1=1, {long_arg}_2=2):
610 pass
611 """,
612 exclude="my_func_2",
613 )
614 assert parser.statements == {1, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
615 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
616 f"""\
617 def my_func \
618 (
619 {long_arg}_0=0,
620 {long_arg}_1=1
621 ):
622 pass
624 def my_func_2({long_arg}_0=0, {long_arg}_1=1, {long_arg}_2=2):
625 pass
626 """,
627 exclude="my_func",
628 )
629 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
631 def test_excluding_bug1713(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
632 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
633 """\
634 print("1")
636 def hello_3(a): # pragma: nocover
637 match a:
638 case ("5"
639 | "6"):
640 print("7")
641 case "8":
642 print("9")
644 print("11")
645 """,
646 )
647 assert parser.statements == {1, 11} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
648 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
649 """\
650 print("1")
652 def hello_3(a): # nocover
653 if ("4" or
654 "5"):
655 print("6")
656 else:
657 print("8")
659 print("10")
660 """,
661 )
662 assert parser.statements == {1, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
663 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
664 """\
665 print(1)
667 def func(a, b):
668 if a == 4: # nocover
669 func5()
670 if b:
671 print(7)
672 func8()
674 print(10)
675 """,
676 )
677 assert parser.statements == {1, 3, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
678 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
679 """\
680 class Foo: # pragma: nocover
681 def greet(self):
682 print("hello world")
683 """,
684 )
685 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
687 def test_excluding_method(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
688 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
689 """\
690 class Fooey:
691 def __init__(self):
692 self.a = 1
694 def foo(self): # nocover
695 return self.a
697 x = Fooey()
698 assert x.a == 1
699 """,
700 )
701 assert parser.statements == {1, 2, 3, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
702 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
703 """\
704 class Fooey:
705 def __init__(self):
706 self.a = 1
708 def very_long_method_to_exclude_name(
709 very_long_argument1,
710 very_long_argument2
711 ):
712 pass
714 x = Fooey()
715 assert x.a == 1
716 """,
717 exclude="method_to_exclude",
718 )
719 assert parser.statements == {1, 2, 3, 11, 12} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
721 def test_excluding_class(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
722 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
723 """\
724 class Fooey: # nocover
725 def __init__(self):
726 self.a = 1
728 def foo(self):
729 return self.a
731 x = 1
732 assert x == 1
733 """,
734 )
735 assert parser.statements == {8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
737 def test_excludes_non_ascii(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
738 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
739 """\
740 # coding: utf-8
741 a = 1; b = 2
743 if len([]):
744 a = 5 # ✘cover
745 """,
746 exclude="✘cover",
747 )
748 assert parser.statements == {2, 4} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
750 def test_no_exclude_at_all(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
751 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
752 """\
753 def foo():
754 if fooey:
755 a = 3
756 else:
757 a = 5
758 b = 6
759 """,
760 exclude="",
761 )
762 assert parser.exit_counts() == {1: 1, 2: 2, 3: 1, 5: 1, 6: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
764 def test_formfeed(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
765 # https://github.com/coveragepy/coveragepy/issues/461
766 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
767 """\
768 x = 1
769 assert len([]) == 0, (
770 "This won't happen %s" % ("hello",)
771 )
772 \f
773 x = 6
774 assert len([]) == 0, (
775 "This won't happen %s" % ("hello",)
776 )
777 """,
778 exclude="assert",
779 )
780 assert parser.statements == {1, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
782 def test_decorator_pragmas(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
783 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
784 # 1
786 @foo(3) # nocover
787 @bar
788 def func(x, y=5):
789 return 6
791 class Foo: # this is the only statement.
792 '''9'''
793 @foo # nocover
794 def __init__(self):
795 '''12'''
796 return 13
798 @foo( # nocover
799 16,
800 17,
801 )
802 def meth(self):
803 return 20
805 @foo( # nocover
806 23
807 )
808 def func(x=25):
809 return 26
810 """)
811 raw_statements = {3, 4, 5, 6, 8, 9, 10, 11, 13, 15, 16, 17, 19, 20, 22, 23, 25, 26} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
812 assert parser.raw_statements == raw_statements 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
813 assert parser.statements == {8} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
815 def test_decorator_pragmas_with_colons(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
816 # A colon in a decorator expression would confuse the parser,
817 # ending the exclusion of the decorated function.
818 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
819 @decorate(X) # nocover
820 @decorate("Hello"[2])
821 def f():
822 x = 4
824 @decorate(X) # nocover
825 @decorate("Hello"[:7])
826 def g():
827 x = 9
828 """)
829 raw_statements = {1, 2, 3, 4, 6, 7, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
830 assert parser.raw_statements == raw_statements 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
831 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
833 def test_class_decorator_pragmas(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
834 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
835 class Foo(object):
836 def __init__(self):
837 self.x = 3
839 @foo # nocover
840 class Bar(object):
841 def __init__(self):
842 self.x = 8
843 """)
844 assert parser.raw_statements == {1, 2, 3, 5, 6, 7, 8} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
845 assert parser.statements == {1, 2, 3} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
847 def test_over_exclusion_bug1779(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
848 # https://github.com/coveragepy/coveragepy/issues/1779
849 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
850 import abc
852 class MyProtocol: # nocover 3
853 @abc.abstractmethod # nocover 4
854 def my_method(self) -> int:
855 ... # 6
857 def function() -> int:
858 return 9
859 """)
860 assert parser.raw_statements == {1, 3, 4, 5, 6, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
861 assert parser.statements == {1, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
863 def test_multiline_exclusion_single_line(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
864 regex = r"print\('.*'\)" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
865 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
866 """\
867 def foo():
868 print('Hello, world!')
869 """,
870 regex,
871 )
872 assert parser.lines_matching(regex) == {2} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
873 assert parser.raw_statements == {1, 2} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
874 assert parser.statements == {1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
876 def test_multiline_exclusion_suite(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
877 # A multi-line exclusion that matches a colon line still excludes the entire block.
878 regex = r"if T:\n\s+print\('Hello, world!'\)" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
879 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
880 """\
881 def foo():
882 if T:
883 print('Hello, world!')
884 print('This is a multiline regex test.')
885 a = 5
886 """,
887 regex,
888 )
889 assert parser.lines_matching(regex) == {2, 3} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
890 assert parser.raw_statements == {1, 2, 3, 4, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
891 assert parser.statements == {1, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
893 def test_multiline_exclusion_no_match(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
894 regex = r"nonexistent" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
895 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
896 """\
897 def foo():
898 print('Hello, world!')
899 """,
900 regex,
901 )
902 assert parser.lines_matching(regex) == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
903 assert parser.raw_statements == {1, 2} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
904 assert parser.statements == {1, 2} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
906 def test_multiline_exclusion_no_source(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
907 regex = r"anything" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
908 parser = PythonParser(text="", filename="dummy.py", exclude=regex) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
909 assert parser.lines_matching(regex) == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
910 assert parser.raw_statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
911 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
913 def test_multiline_exclusion_all_lines_must_match(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
914 # https://github.com/coveragepy/coveragepy/issues/996
915 regex = r"except ValueError:\n\s*print\('false'\)" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
916 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
917 """\
918 try:
919 a = 2
920 print('false')
921 except ValueError:
922 print('false')
923 except ValueError:
924 print('something else')
925 except IndexError:
926 print('false')
927 """,
928 regex,
929 )
930 assert parser.lines_matching(regex) == {4, 5} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
931 assert parser.raw_statements == {1, 2, 3, 4, 5, 6, 7, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
932 assert parser.statements == {1, 2, 3, 6, 7, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
934 def test_multiline_exclusion_multiple_matches(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
935 regex = r"print\('.*'\)\n\s+. = \d" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
936 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
937 """\
938 def foo():
939 print('Hello, world!')
940 a = 5
941 def bar():
942 print('Hello again!')
943 b = 6
944 """,
945 regex,
946 )
947 assert parser.lines_matching(regex) == {2, 3, 5, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
948 assert parser.raw_statements == {1, 2, 3, 4, 5, 6} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
949 assert parser.statements == {1, 4} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
951 def test_multiline_exclusion_suite2(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
952 regex = r"print\('Hello, world!'\)\n\s+if T:" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
953 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
954 """\
955 def foo():
956 print('Hello, world!')
957 if T:
958 print('This is a test.')
959 """,
960 regex,
961 )
962 assert parser.lines_matching(regex) == {2, 3} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
963 assert parser.raw_statements == {1, 2, 3, 4} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
964 assert parser.statements == {1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
966 def test_multiline_exclusion_match_all(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
967 regex = ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
968 r"def foo\(\):\n\s+print\('Hello, world!'\)\n"
969 + r"\s+if T:\n\s+print\('This is a test\.'\)"
970 )
971 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
972 """\
973 def foo():
974 print('Hello, world!')
975 if T:
976 print('This is a test.')
977 """,
978 regex,
979 )
980 assert parser.lines_matching(regex) == {1, 2, 3, 4} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
981 assert parser.raw_statements == {1, 2, 3, 4} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
982 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
984 def test_multiline_exclusion_block(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
985 # https://github.com/coveragepy/coveragepy/issues/1803
986 regex = "# no cover: start(?s:.)*?# no cover: stop" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
987 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
988 """\
989 a = my_function1()
990 if debug:
991 msg = "blah blah"
992 # no cover: start
993 log_message(msg, a)
994 b = my_function2()
995 # no cover: stop
996 count_this()
997 # no cover: start
998 but_not_this()
999 # no cover: stop
1000 """,
1001 regex,
1002 )
1003 assert parser.lines_matching(regex) == {4, 5, 6, 7, 9, 10, 11} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1004 assert parser.raw_statements == {1, 2, 3, 5, 6, 8, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1005 assert parser.statements == {1, 2, 3, 8} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1007 def test_multiline_exclusion_block2(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1008 # https://github.com/coveragepy/coveragepy/issues/1797
1009 regex = r"case _:\n\s+assert_never\(" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1010 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1011 """\
1012 match something:
1013 case type_1():
1014 logic_1()
1015 case type_2():
1016 logic_2()
1017 case _:
1018 assert_never(something)
1019 match something:
1020 case type_1():
1021 logic_1()
1022 case type_2():
1023 logic_2()
1024 case _:
1025 print("Default case")
1026 """,
1027 regex,
1028 )
1029 assert parser.lines_matching(regex) == {6, 7} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1030 assert parser.raw_statements == {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1031 assert parser.statements == {1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 14} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1033 def test_multiline_exclusion_block3(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1034 # https://github.com/coveragepy/coveragepy/issues/1741
1035 # This will only work if there's exactly one return statement in the rest of the function
1036 regex = r"# no cover: to return(?s:.)*?return" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1037 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1038 """\
1039 def my_function(args, j):
1040 if args.command == Commands.CMD.value:
1041 return cmd_handler(j, args)
1042 # no cover: to return
1043 print(f"Command '{args.command}' was not handled.", file=sys.stderr)
1044 parser.print_help(file=sys.stderr)
1046 return os.EX_USAGE
1047 print("not excluded")
1048 """,
1049 regex,
1050 )
1051 assert parser.lines_matching(regex) == {4, 5, 6, 7, 8} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1052 assert parser.raw_statements == {1, 2, 3, 5, 6, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1053 assert parser.statements == {1, 2, 3, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1055 def test_multiline_exclusion_whole_source(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1056 # https://github.com/coveragepy/coveragepy/issues/118
1057 regex = r"\A(?s:.*# pragma: exclude file.*)\Z" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1058 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1059 """\
1060 import coverage
1061 # pragma: exclude file
1062 def the_void() -> None:
1063 if "py" not in __file__:
1064 print("Not a Python file.")
1065 print("Everything here is excluded.")
1067 return
1068 print("Excluded too")
1069 """,
1070 regex,
1071 )
1072 assert parser.lines_matching(regex) == {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1073 assert parser.raw_statements == {1, 3, 4, 5, 6, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1074 assert parser.statements == set() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1076 def test_multiline_exclusion_from_marker(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1077 # https://github.com/coveragepy/coveragepy/issues/118
1078 regex = r"# pragma: rest of file(?s:.)*\Z" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1079 parser = self.parse_text( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1080 """\
1081 import coverage
1082 # pragma: rest of file
1083 def the_void() -> None:
1084 if "py" not in __file__:
1085 print("Not a Python file.")
1086 print("Everything here is excluded.")
1088 return
1089 print("Excluded too")
1090 """,
1091 regex,
1092 )
1093 assert parser.lines_matching(regex) == {2, 3, 4, 5, 6, 7, 8, 9, 10} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1094 assert parser.raw_statements == {1, 3, 4, 5, 6, 8, 9} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1095 assert parser.statements == {1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1098class ParserMissingArcDescriptionTest(PythonParserTestBase): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1099 """Tests for PythonParser.missing_arc_description."""
1101 def test_missing_arc_description(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1102 # This code is never run, so the actual values don't matter.
1103 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1104 if x:
1105 print(2)
1106 print(3)
1108 def func5():
1109 for x in range(6):
1110 if x == 7:
1111 break
1113 def func10():
1114 while something(11):
1115 thing(12)
1116 more_stuff(13)
1117 """)
1118 expected = "line 1 didn't jump to line 2 because the condition on line 1 was never true" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1119 assert expected == parser.missing_arc_description(1, 2) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1120 expected = "line 1 didn't jump to line 3 because the condition on line 1 was always true" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1121 assert expected == parser.missing_arc_description(1, 3) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1122 expected = ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1123 "line 6 didn't return from function 'func5' "
1124 + "because the loop on line 6 didn't complete"
1125 )
1126 assert expected == parser.missing_arc_description(6, -5) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1127 expected = "line 6 didn't jump to line 7 because the loop on line 6 never started" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1128 assert expected == parser.missing_arc_description(6, 7) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1129 expected = "line 11 didn't jump to line 12 because the condition on line 11 was never true" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1130 assert expected == parser.missing_arc_description(11, 12) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1131 expected = ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1132 "line 11 didn't jump to line 13 " + "because the condition on line 11 was always true"
1133 )
1134 assert expected == parser.missing_arc_description(11, 13) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1136 def test_missing_arc_descriptions_for_exceptions(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1137 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1138 try:
1139 pass
1140 except ZeroDivideError:
1141 print("whoops")
1142 except ValueError:
1143 print("yikes")
1144 """)
1145 expected = ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1146 "line 3 didn't jump to line 4 " + "because the exception caught by line 3 didn't happen"
1147 )
1148 assert expected == parser.missing_arc_description(3, 4) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1149 expected = ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1150 "line 5 didn't jump to line 6 " + "because the exception caught by line 5 didn't happen"
1151 )
1152 assert expected == parser.missing_arc_description(5, 6) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1155class MatchCaseMissingArcDescriptionTest(PythonParserTestBase): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1156 """Missing arc descriptions for match/case."""
1158 def test_match_case(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1159 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1160 match command.split():
1161 case ["go", direction] if direction in "nesw": # 2
1162 match = f"go: {direction}"
1163 case ["go", _]: # 4
1164 match = "no go"
1165 print(match) # 6
1166 """)
1167 assert parser.missing_arc_description(2, 3) == ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1168 "line 2 didn't jump to line 3 because the pattern on line 2 never matched"
1169 )
1170 assert parser.missing_arc_description(2, 4) == ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1171 "line 2 didn't jump to line 4 because the pattern on line 2 always matched"
1172 )
1173 assert parser.missing_arc_description(4, 6) == ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1174 "line 4 didn't jump to line 6 because the pattern on line 4 always matched"
1175 )
1177 def test_final_wildcard(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1178 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1179 match command.split():
1180 case ["go", direction] if direction in "nesw": # 2
1181 match = f"go: {direction}"
1182 case _: # 4
1183 match = "no go"
1184 print(match) # 6
1185 """)
1186 assert parser.missing_arc_description(2, 3) == ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1187 "line 2 didn't jump to line 3 because the pattern on line 2 never matched"
1188 )
1189 assert parser.missing_arc_description(2, 4) == ( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1190 "line 2 didn't jump to line 4 because the pattern on line 2 always matched"
1191 )
1192 # 4-6 isn't a possible arc, so the description is generic.
1193 assert parser.missing_arc_description(4, 6) == "line 4 didn't jump to line 6" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1195 def test_missing_arc_descriptions_bug1775(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1196 # Bug: the `if x == 2:` line was marked partial with a message of:
1197 # line 6 didn't return from function 'func', because the return on line 4 wasn't executed
1198 # At some point it changed to "didn't jump to the function exit," which
1199 # is close enough. These situations are hard to describe precisely.
1200 parser = self.parse_text("""\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1201 def func():
1202 x = 2
1203 try:
1204 return 4
1205 finally:
1206 if x == 2: # line 6
1207 print("x is 2")
1209 func()
1210 """)
1211 assert parser.missing_arc_description(6, -1) == "line 6 didn't jump to the function exit" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1214class ParserFileTest(CoverageTest): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1215 """Tests for coverage.py's code parsing from files."""
1217 def parse_file(self, filename: str) -> PythonParser: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1218 """Parse `text` as source, and return the `PythonParser` used."""
1219 parser = PythonParser(filename=filename, exclude="nocover") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1220 parser.parse_source() 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1221 return parser 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1223 @pytest.mark.parametrize( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1224 "slug, newline",
1225 [
1226 ("unix", "\n"),
1227 ("dos", "\r\n"),
1228 ("mac", "\r"),
1229 ],
1230 )
1231 def test_line_endings(self, slug: str, newline: str) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1232 text = """\ 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1233 # check some basic branch counting
1234 class Foo:
1235 def foo(self, a):
1236 if a:
1237 return 5
1238 else:
1239 return 7
1241 class Bar:
1242 pass
1243 """
1244 counts = {2: 1, 3: 1, 4: 2, 5: 1, 7: 1, 9: 1, 10: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1245 fname = slug + ".py" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1246 self.make_file(fname, text, newline=newline) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1247 parser = self.parse_file(fname) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1248 assert parser.exit_counts() == counts, f"Wrong for {fname!r}" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1250 def test_encoding(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1251 self.make_file( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1252 "encoded.py",
1253 """\
1254 coverage = "\xe7\xf6v\xear\xe3g\xe9"
1255 """,
1256 )
1257 parser = self.parse_file("encoded.py") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1258 assert parser.exit_counts() == {1: 1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1260 def test_missing_line_ending(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1261 # Test that the set of statements is the same even if a final
1262 # multi-line statement has no final newline.
1263 # https://github.com/coveragepy/coveragepy/issues/293
1265 self.make_file( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1266 "normal.py",
1267 """\
1268 out, err = some_module.some_function(
1269 ["my data", "-c", "pass"],
1270 arg1=some_module.NAME,
1271 arg2=some_module.OTHER_NAME).function()
1272 """,
1273 )
1275 parser = self.parse_file("normal.py") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1276 assert parser.statements == {1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1278 self.make_file( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1279 "abrupt.py",
1280 """\
1281 out, err = some_module.some_function(
1282 ["my data", "-c", "pass"],
1283 arg1=some_module.NAME,
1284 arg2=some_module.OTHER_NAME).function()""",
1285 ) # no final newline.
1287 # Double-check that some test helper wasn't being helpful.
1288 with open("abrupt.py", encoding="utf-8") as f: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1289 assert f.read()[-1] == ")" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1291 parser = self.parse_file("abrupt.py") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1292 assert parser.statements == {1} 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1294 def test_os_error(self) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1295 self.make_file("cant-read.py", "BOOM!") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1296 msg = "No source for code: 'cant-read.py': Fake!" 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1297 with pytest.raises(NoSource, match=re.escape(msg)): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1298 with mock.patch("coverage.python.read_python_source", side_effect=OSError("Fake!")): 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1299 PythonParser(filename="cant-read.py") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1302@pytest.mark.parametrize( 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1303 ["expr", "ret"],
1304 [
1305 ("True", (True, True)),
1306 ("False", (True, False)),
1307 ("1", (True, True)),
1308 ("0", (True, False)),
1309 ("__debug__", (True, True)),
1310 ("not __debug__", (True, False)),
1311 ("not(__debug__)", (True, False)),
1312 ("-__debug__", (False, False)),
1313 ("__debug__ or True", (True, True)),
1314 ("__debug__ + True", (False, False)),
1315 ("x", (False, False)),
1316 ("__debug__ or debug", (False, False)),
1317 ],
1318)
1319def test_is_constant_test_expr(expr: str, ret: tuple[bool, bool]) -> None: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1320 node = ast.parse(expr, mode="eval").body 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1321 print(ast.dump(node, indent=4)) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()
1322 assert is_constant_test_expr(node) == ret 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%'()