Skip to content

Commit 538ca96

Browse files
committed
refactor(test): reorg the parser tests in prep for moving more here
1 parent 24df7e9 commit 538ca96

File tree

1 file changed

+66
-66
lines changed

1 file changed

+66
-66
lines changed

tests/test_parser.py

+66-66
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,24 @@
1717
from tests.helpers import arcz_to_arcs, xfail_pypy38
1818

1919

20-
class PythonParserTest(CoverageTest):
20+
class PythonParserTestBase(CoverageTest):
2121
"""Tests for coverage.py's Python code parsing."""
2222

2323
run_in_temp_dir = False
2424

25-
def parse_source(self, text: str) -> PythonParser:
25+
def parse_text(self, text: str) -> PythonParser:
2626
"""Parse `text` as source, and return the `PythonParser` used."""
2727
text = textwrap.dedent(text)
2828
parser = PythonParser(text=text, exclude="nocover")
2929
parser.parse_source()
3030
return parser
3131

32+
33+
class PythonParserTest(PythonParserTestBase):
34+
"""Tests of coverage.parser."""
35+
3236
def test_exit_counts(self) -> None:
33-
parser = self.parse_source("""\
37+
parser = self.parse_text("""\
3438
# check some basic branch counting
3539
class Foo:
3640
def foo(self, a):
@@ -48,7 +52,7 @@ class Bar:
4852

4953
def test_generator_exit_counts(self) -> None:
5054
# https://github.com/nedbat/coveragepy/issues/324
51-
parser = self.parse_source("""\
55+
parser = self.parse_text("""\
5256
def gen(input):
5357
for n in inp:
5458
yield (i * 2 for i in range(n))
@@ -63,7 +67,7 @@ def gen(input):
6367
}
6468

6569
def test_try_except(self) -> None:
66-
parser = self.parse_source("""\
70+
parser = self.parse_text("""\
6771
try:
6872
a = 2
6973
except ValueError:
@@ -79,7 +83,7 @@ def test_try_except(self) -> None:
7983
}
8084

8185
def test_excluded_classes(self) -> None:
82-
parser = self.parse_source("""\
86+
parser = self.parse_text("""\
8387
class Foo:
8488
def __init__(self):
8589
pass
@@ -93,15 +97,15 @@ class Bar:
9397
}
9498

9599
def test_missing_branch_to_excluded_code(self) -> None:
96-
parser = self.parse_source("""\
100+
parser = self.parse_text("""\
97101
if fooey:
98102
a = 2
99103
else: # nocover
100104
a = 4
101105
b = 5
102106
""")
103107
assert parser.exit_counts() == { 1:1, 2:1, 5:1 }
104-
parser = self.parse_source("""\
108+
parser = self.parse_text("""\
105109
def foo():
106110
if fooey:
107111
a = 3
@@ -110,7 +114,7 @@ def foo():
110114
b = 6
111115
""")
112116
assert parser.exit_counts() == { 1:1, 2:2, 3:1, 5:1, 6:1 }
113-
parser = self.parse_source("""\
117+
parser = self.parse_text("""\
114118
def foo():
115119
if fooey:
116120
a = 3
@@ -126,7 +130,7 @@ def test_indentation_error(self) -> None:
126130
"'unindent does not match any outer indentation level.*' at line 3"
127131
)
128132
with pytest.raises(NotPython, match=msg):
129-
_ = self.parse_source("""\
133+
_ = self.parse_text("""\
130134
0 spaces
131135
2
132136
1
@@ -143,11 +147,58 @@ def test_token_error(self) -> None:
143147
+ r"' at line 1"
144148
)
145149
with pytest.raises(NotPython, match=msg):
146-
_ = self.parse_source("'''")
150+
_ = self.parse_text("'''")
151+
152+
def test_empty_decorated_function(self) -> None:
153+
parser = self.parse_text("""\
154+
def decorator(func):
155+
return func
156+
157+
@decorator
158+
def foo(self):
159+
'''Docstring'''
160+
161+
@decorator
162+
def bar(self):
163+
pass
164+
""")
165+
166+
expected_statements = {1, 2, 4, 5, 8, 9, 10}
167+
expected_arcs = set(arcz_to_arcs(".1 14 45 58 89 9. .2 2. -8A A-8"))
168+
expected_exits = {1: 1, 2: 1, 4: 1, 5: 1, 8: 1, 9: 1, 10: 1}
169+
170+
if env.PYBEHAVIOR.docstring_only_function:
171+
# 3.7 changed how functions with only docstrings are numbered.
172+
expected_arcs.update(set(arcz_to_arcs("-46 6-4")))
173+
expected_exits.update({6: 1})
174+
175+
if env.PYBEHAVIOR.trace_decorator_line_again:
176+
expected_arcs.update(set(arcz_to_arcs("54 98")))
177+
expected_exits.update({9: 2, 5: 2})
178+
179+
assert expected_statements == parser.statements
180+
assert expected_arcs == parser.arcs()
181+
assert expected_exits == parser.exit_counts()
182+
183+
def test_fuzzed_double_parse(self) -> None:
184+
# https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=50381
185+
# The second parse used to raise `TypeError: 'NoneType' object is not iterable`
186+
msg = (
187+
r"(EOF in multi-line statement)" # before 3.12.0b1
188+
+ r"|(unmatched ']')" # after 3.12.0b1
189+
)
190+
with pytest.raises(NotPython, match=msg):
191+
self.parse_text("]")
192+
with pytest.raises(NotPython, match=msg):
193+
self.parse_text("]")
194+
195+
196+
class ExclusionParserTest(PythonParserTestBase):
197+
"""Tests for the exclusion code in PythonParser."""
147198

148199
@xfail_pypy38
149200
def test_decorator_pragmas(self) -> None:
150-
parser = self.parse_source("""\
201+
parser = self.parse_text("""\
151202
# 1
152203
153204
@foo(3) # nocover
@@ -183,7 +234,7 @@ def func(x=25):
183234
def test_decorator_pragmas_with_colons(self) -> None:
184235
# A colon in a decorator expression would confuse the parser,
185236
# ending the exclusion of the decorated function.
186-
parser = self.parse_source("""\
237+
parser = self.parse_text("""\
187238
@decorate(X) # nocover
188239
@decorate("Hello"[2])
189240
def f():
@@ -199,7 +250,7 @@ def g():
199250
assert parser.statements == set()
200251

201252
def test_class_decorator_pragmas(self) -> None:
202-
parser = self.parse_source("""\
253+
parser = self.parse_text("""\
203254
class Foo(object):
204255
def __init__(self):
205256
self.x = 3
@@ -212,61 +263,10 @@ def __init__(self):
212263
assert parser.raw_statements == {1, 2, 3, 5, 6, 7, 8}
213264
assert parser.statements == {1, 2, 3}
214265

215-
def test_empty_decorated_function(self) -> None:
216-
parser = self.parse_source("""\
217-
def decorator(func):
218-
return func
219-
220-
@decorator
221-
def foo(self):
222-
'''Docstring'''
223-
224-
@decorator
225-
def bar(self):
226-
pass
227-
""")
228-
229-
expected_statements = {1, 2, 4, 5, 8, 9, 10}
230-
expected_arcs = set(arcz_to_arcs(".1 14 45 58 89 9. .2 2. -8A A-8"))
231-
expected_exits = {1: 1, 2: 1, 4: 1, 5: 1, 8: 1, 9: 1, 10: 1}
232-
233-
if env.PYBEHAVIOR.docstring_only_function:
234-
# 3.7 changed how functions with only docstrings are numbered.
235-
expected_arcs.update(set(arcz_to_arcs("-46 6-4")))
236-
expected_exits.update({6: 1})
237-
238-
if env.PYBEHAVIOR.trace_decorator_line_again:
239-
expected_arcs.update(set(arcz_to_arcs("54 98")))
240-
expected_exits.update({9: 2, 5: 2})
241266

242-
assert expected_statements == parser.statements
243-
assert expected_arcs == parser.arcs()
244-
assert expected_exits == parser.exit_counts()
245-
246-
def test_fuzzed_double_parse(self) -> None:
247-
# https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=50381
248-
# The second parse used to raise `TypeError: 'NoneType' object is not iterable`
249-
msg = (
250-
r"(EOF in multi-line statement)" # before 3.12.0b1
251-
+ r"|(unmatched ']')" # after 3.12.0b1
252-
)
253-
with pytest.raises(NotPython, match=msg):
254-
self.parse_source("]")
255-
with pytest.raises(NotPython, match=msg):
256-
self.parse_source("]")
257-
258-
259-
class ParserMissingArcDescriptionTest(CoverageTest):
267+
class ParserMissingArcDescriptionTest(PythonParserTestBase):
260268
"""Tests for PythonParser.missing_arc_description."""
261269

262-
run_in_temp_dir = False
263-
264-
def parse_text(self, source: str) -> PythonParser:
265-
"""Parse Python source, and return the parser object."""
266-
parser = PythonParser(text=textwrap.dedent(source))
267-
parser.parse_source()
268-
return parser
269-
270270
def test_missing_arc_description(self) -> None:
271271
# This code is never run, so the actual values don't matter.
272272
parser = self.parse_text("""\

0 commit comments

Comments
 (0)