Skip to content

Commit 34120f6

Browse files
committed
refactor test discovery
1 parent 9d10fcd commit 34120f6

15 files changed

+175
-303
lines changed

mypy/myunit/__init__.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -105,30 +105,19 @@ def fail() -> None:
105105
raise AssertionFailure()
106106

107107

108-
class TestCase:
109-
def __init__(self, name: str, suite: 'Optional[Suite]' = None,
110-
func: Optional[Callable[[], None]] = None) -> None:
111-
self.func = func
108+
class ProtoTestCase:
109+
def __init__(self, name: str) -> None:
112110
self.name = name
113-
self.suite = suite
114111
self.old_cwd = None # type: Optional[str]
115112
self.tmpdir = None # type: Optional[tempfile.TemporaryDirectory[str]]
116113

117-
def run(self) -> None:
118-
if self.func:
119-
self.func()
120-
121114
def set_up(self) -> None:
122115
self.old_cwd = os.getcwd()
123116
self.tmpdir = tempfile.TemporaryDirectory(prefix='mypy-test-')
124117
os.chdir(self.tmpdir.name)
125118
os.mkdir('tmp')
126-
if self.suite:
127-
self.suite.set_up()
128119

129120
def tear_down(self) -> None:
130-
if self.suite:
131-
self.suite.tear_down()
132121
assert self.old_cwd is not None and self.tmpdir is not None, \
133122
"test was not properly set up"
134123
os.chdir(self.old_cwd)
@@ -140,6 +129,28 @@ def tear_down(self) -> None:
140129
self.tmpdir = None
141130

142131

132+
class TestCase(ProtoTestCase):
133+
def __init__(self, name: str, suite: 'Optional[Suite]' = None,
134+
func: Optional[Callable[[], None]] = None) -> None:
135+
super().__init__(name)
136+
self.func = func
137+
self.suite = suite
138+
139+
def run(self) -> None:
140+
if self.func:
141+
self.func()
142+
143+
def set_up(self) -> None:
144+
super().set_up()
145+
if self.suite:
146+
self.suite.set_up()
147+
148+
def tear_down(self) -> None:
149+
if self.suite:
150+
self.suite.tear_down()
151+
super().tear_down()
152+
153+
143154
class Suite:
144155
def __init__(self) -> None:
145156
self.prefix = typename(type(self)) + '.'

mypy/test/data.py

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,33 @@
88
import shutil
99

1010
import pytest # type: ignore # no pytest in typeshed
11-
from typing import Callable, List, Tuple, Set, Optional, Iterator, Any, Dict
12-
13-
from mypy.myunit import TestCase, SkipTestCaseException
11+
from typing import List, Tuple, Set, Optional, Iterator, Any, Dict
12+
import typing
1413

14+
from mypy.myunit import ProtoTestCase
15+
from mypy.test.config import test_data_prefix
1516

1617
root_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..'))
1718

1819

1920
def parse_test_cases(
2021
path: str,
21-
perform: Optional[Callable[['DataDrivenTestCase'], None]],
2222
base_path: str = '.',
2323
optional_out: bool = False,
24-
include_path: Optional[str] = None,
2524
native_sep: bool = False) -> List['DataDrivenTestCase']:
2625
"""Parse a file with test case descriptions.
2726
2827
Return an array of test cases.
2928
30-
NB this function and DataDrivenTestCase are shared between the
29+
NB: this function and DataDrivenTestCase were shared between the
3130
myunit and pytest codepaths -- if something looks redundant,
3231
that's likely the reason.
3332
"""
3433
if native_sep:
3534
join = os.path.join
3635
else:
3736
join = posixpath.join # type: ignore
38-
if not include_path:
39-
include_path = os.path.dirname(path)
37+
include_path = os.path.dirname(path)
4038
with open(path, encoding='utf-8') as f:
4139
lst = f.readlines()
4240
for i in range(len(lst)):
@@ -167,7 +165,7 @@ def parse_test_cases(
167165
arg0 = p[i0].arg
168166
assert arg0 is not None
169167
tc = DataDrivenTestCase(arg0, input, tcout, tcout2, path,
170-
p[i0].line, lastline, perform,
168+
p[i0].line, lastline,
171169
files, output_files, stale_modules,
172170
rechecked_modules, deleted_paths, native_sep)
173171
out.append(tc)
@@ -179,7 +177,7 @@ def parse_test_cases(
179177
return out
180178

181179

182-
class DataDrivenTestCase(TestCase):
180+
class DataDrivenTestCase(ProtoTestCase):
183181
input = None # type: List[str]
184182
output = None # type: List[str] # Output for the first pass
185183
output2 = None # type: Dict[int, List[str]] # Output for runs 2+, indexed by run number
@@ -202,7 +200,6 @@ def __init__(self,
202200
file: str,
203201
line: int,
204202
lastline: int,
205-
perform: Optional[Callable[['DataDrivenTestCase'], None]],
206203
files: List[Tuple[str, str]],
207204
output_files: List[Tuple[str, str]],
208205
expected_stale_modules: Dict[int, Set[str]],
@@ -217,7 +214,6 @@ def __init__(self,
217214
self.lastline = lastline
218215
self.file = file
219216
self.line = line
220-
self.perform = perform
221217
self.files = files
222218
self.output_files = output_files
223219
self.expected_stale_modules = expected_stale_modules
@@ -268,13 +264,6 @@ def add_dirs(self, dir: str) -> List[str]:
268264
os.mkdir(dir)
269265
return dirs
270266

271-
def run(self) -> None:
272-
if self.name.endswith('-skip'):
273-
raise SkipTestCaseException()
274-
else:
275-
assert self.perform is not None, 'Tests without `perform` should not be `run`'
276-
self.perform(self)
277-
278267
def tear_down(self) -> None:
279268
# First remove files.
280269
for is_dir, path in reversed(self.clean_up):
@@ -507,13 +496,37 @@ def pytest_addoption(parser: Any) -> None:
507496
def pytest_pycollect_makeitem(collector: Any, name: str, obj: Any) -> Any:
508497
if not isinstance(obj, type) or not issubclass(obj, DataSuite):
509498
return None
499+
if obj is DataSuite:
500+
return None
510501
return MypyDataSuite(name, parent=collector)
511502

512503

513504
class MypyDataSuite(pytest.Class): # type: ignore # inheriting from Any
514505
def collect(self) -> Iterator['MypyDataCase']:
515-
for case in self.obj.cases():
516-
yield MypyDataCase(case.name, self, case)
506+
cls = self.obj
507+
for f in cls.files:
508+
for case in parse_test_cases(os.path.join(test_data_prefix, f),
509+
base_path=cls.base_path,
510+
optional_out=cls.optional_out,
511+
native_sep=cls.native_sep):
512+
if cls.require_stable and not has_stable_flags(case):
513+
continue
514+
if cls.require_incremental and not is_incremental(case):
515+
continue
516+
yield MypyDataCase(case.name, self, case)
517+
518+
519+
def is_incremental(testcase: DataDrivenTestCase) -> bool:
520+
return 'incremental' in testcase.name.lower() or 'incremental' in testcase.file
521+
522+
523+
def has_stable_flags(testcase: DataDrivenTestCase) -> bool:
524+
if any(re.match(r'# flags[2-9]:', line) for line in testcase.input):
525+
return False
526+
for filename, contents in testcase.files:
527+
if os.path.basename(filename).startswith('mypy.ini.'):
528+
return False
529+
return True
517530

518531

519532
class MypyDataCase(pytest.Item): # type: ignore # inheriting from Any
@@ -556,12 +569,15 @@ def repr_failure(self, excinfo: Any) -> str:
556569

557570

558571
class DataSuite:
572+
files = None # type: typing.ClassVar[List[str]]
573+
base_path = '.' # type: typing.ClassVar[str]
574+
optional_out = False # type: typing.ClassVar[bool]
575+
native_sep = False # type: typing.ClassVar[bool]
576+
require_stable = False # type: typing.ClassVar[bool]
577+
require_incremental = False # type: typing.ClassVar[bool]
578+
559579
def __init__(self, *, update_data: bool) -> None:
560580
self.update_data = update_data
561581

562-
@classmethod
563-
def cases(cls) -> List[DataDrivenTestCase]:
564-
return []
565-
566582
def run_case(self, testcase: DataDrivenTestCase) -> None:
567583
raise NotImplementedError

mypy/test/testcheck.py

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,16 @@
33
import os
44
import re
55
import shutil
6-
import sys
7-
import time
8-
import typed_ast
96

107
from typing import Dict, List, Optional, Set, Tuple
8+
import typing
119

1210
from mypy import build, defaults
1311
from mypy.main import process_options
1412
from mypy.build import BuildSource, find_module_clear_caches
1513
from mypy.myunit import AssertionFailure
16-
from mypy.test.config import test_temp_dir, test_data_prefix
17-
from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite
14+
from mypy.test.config import test_temp_dir
15+
from mypy.test.data import DataDrivenTestCase, DataSuite
1816
from mypy.test.helpers import (
1917
assert_string_arrays_equal, normalize_error_messages,
2018
retry_on_error, testcase_pyversion, update_testcase_output,
@@ -25,7 +23,7 @@
2523
from mypy import experiments
2624

2725
# List of files that contain test case descriptions.
28-
files = [
26+
typecheck_files = [
2927
'check-basic.test',
3028
'check-callable.test',
3129
'check-classes.test',
@@ -83,14 +81,9 @@
8381

8482

8583
class TypeCheckSuite(DataSuite):
86-
87-
@classmethod
88-
def cases(cls) -> List[DataDrivenTestCase]:
89-
c = [] # type: List[DataDrivenTestCase]
90-
for f in files:
91-
c += parse_test_cases(os.path.join(test_data_prefix, f),
92-
None, test_temp_dir, True)
93-
return c
84+
files = typecheck_files # type: typing.ClassVar[List[str]]
85+
base_path = test_temp_dir # type: typing.ClassVar[str]
86+
optional_out = True # type: typing.ClassVar[bool]
9487

9588
def run_case(self, testcase: DataDrivenTestCase) -> None:
9689
incremental = ('incremental' in testcase.name.lower()

mypy/test/testcmdline.py

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99
import subprocess
1010
import sys
1111

12-
from typing import Tuple, List, Dict, Set
12+
from typing import List
13+
import typing
1314

14-
from mypy.myunit import Suite, SkipTestCaseException, AssertionFailure
15-
from mypy.test.config import test_data_prefix, test_temp_dir
15+
from mypy.myunit import AssertionFailure
16+
from mypy.test.config import test_temp_dir
1617
from mypy.test.data import fix_cobertura_filename
17-
from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite
18+
from mypy.test.data import DataDrivenTestCase, DataSuite
1819
from mypy.test.helpers import assert_string_arrays_equal, normalize_error_messages
1920
from mypy.version import __version__, base_version
2021

@@ -28,24 +29,17 @@
2829
]
2930

3031

31-
class PythonEvaluationSuite(DataSuite):
32-
33-
@classmethod
34-
def cases(cls) -> List[DataDrivenTestCase]:
35-
c = [] # type: List[DataDrivenTestCase]
36-
for f in cmdline_files:
37-
c += parse_test_cases(os.path.join(test_data_prefix, f),
38-
test_python_evaluation,
39-
base_path=test_temp_dir,
40-
optional_out=True,
41-
native_sep=True)
42-
return c
32+
class PythonCmdlineSuite(DataSuite):
33+
files = cmdline_files # type: typing.ClassVar[List[str]]
34+
base_path = test_temp_dir # type: typing.ClassVar[str]
35+
optional_out = True # type: typing.ClassVar[bool]
36+
native_sep = True # type: typing.ClassVar[bool]
4337

4438
def run_case(self, testcase: DataDrivenTestCase) -> None:
45-
test_python_evaluation(testcase)
39+
test_python_cmdline(testcase)
4640

4741

48-
def test_python_evaluation(testcase: DataDrivenTestCase) -> None:
42+
def test_python_cmdline(testcase: DataDrivenTestCase) -> None:
4943
assert testcase.old_cwd is not None, "test was not properly set up"
5044
# Write the program to a file.
5145
program = '_program.py'

mypy/test/testdeps.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,24 @@
22

33
import os
44
from typing import List, Tuple, Dict, Optional
5+
import typing
56

67
from mypy import build
78
from mypy.build import BuildSource
89
from mypy.errors import CompileError
910
from mypy.nodes import MypyFile, Expression
1011
from mypy.options import Options
1112
from mypy.server.deps import get_dependencies
12-
from mypy.test.config import test_temp_dir, test_data_prefix
13-
from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite
13+
from mypy.test.config import test_temp_dir
14+
from mypy.test.data import DataDrivenTestCase, DataSuite
1415
from mypy.test.helpers import assert_string_arrays_equal
1516
from mypy.types import Type
1617

17-
files = [
18-
'deps.test'
19-
]
20-
2118

2219
class GetDependenciesSuite(DataSuite):
23-
24-
@classmethod
25-
def cases(cls) -> List[DataDrivenTestCase]:
26-
c = [] # type: List[DataDrivenTestCase]
27-
for f in files:
28-
c += parse_test_cases(os.path.join(test_data_prefix, f),
29-
None, test_temp_dir, True)
30-
return c
20+
files = ['deps.test'] # type: typing.ClassVar[List[str]]
21+
base_path = test_temp_dir # type: typing.ClassVar[str]
22+
optional_out = True # type: typing.ClassVar[bool]
3123

3224
def run_case(self, testcase: DataDrivenTestCase) -> None:
3325
src = '\n'.join(testcase.input)

mypy/test/testdiff.py

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,32 +2,23 @@
22

33
import os
44
from typing import List, Tuple, Dict, Optional
5+
import typing
56

67
from mypy import build
78
from mypy.build import BuildSource
89
from mypy.errors import CompileError
910
from mypy.nodes import MypyFile
1011
from mypy.options import Options
1112
from mypy.server.astdiff import compare_symbol_tables
12-
from mypy.test.config import test_temp_dir, test_data_prefix
13-
from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite
13+
from mypy.test.config import test_temp_dir
14+
from mypy.test.data import DataDrivenTestCase, DataSuite
1415
from mypy.test.helpers import assert_string_arrays_equal
1516

1617

17-
files = [
18-
'diff.test'
19-
]
20-
21-
2218
class ASTDiffSuite(DataSuite):
23-
24-
@classmethod
25-
def cases(cls) -> List[DataDrivenTestCase]:
26-
c = [] # type: List[DataDrivenTestCase]
27-
for f in files:
28-
c += parse_test_cases(os.path.join(test_data_prefix, f),
29-
None, test_temp_dir, True)
30-
return c
19+
files = ['diff.test'] # type: typing.ClassVar[List[str]]
20+
base_path = test_temp_dir # type: typing.ClassVar[str]
21+
optional_out = True # type: typing.ClassVar[bool]
3122

3223
def run_case(self, testcase: DataDrivenTestCase) -> None:
3324
first_src = '\n'.join(testcase.input)

0 commit comments

Comments
 (0)