From 34120f68c75ed8c87dab051c744c2c7c9902ec30 Mon Sep 17 00:00:00 2001 From: elazar Date: Wed, 20 Sep 2017 01:20:33 +0300 Subject: [PATCH 01/14] refactor test discovery --- mypy/myunit/__init__.py | 37 +++++++++++++------- mypy/test/data.py | 66 ++++++++++++++++++++++-------------- mypy/test/testcheck.py | 21 ++++-------- mypy/test/testcmdline.py | 30 +++++++--------- mypy/test/testdeps.py | 20 ++++------- mypy/test/testdiff.py | 21 ++++-------- mypy/test/testdmypy.py | 54 +++++++++-------------------- mypy/test/testfinegrained.py | 16 +++------ mypy/test/testmerge.py | 22 ++++-------- mypy/test/testparse.py | 29 ++++------------ mypy/test/testpythoneval.py | 27 +++++---------- mypy/test/testsemanal.py | 61 ++++++++------------------------- mypy/test/teststubgen.py | 18 +++------- mypy/test/testtransform.py | 36 ++++++++------------ mypy/test/testtypegen.py | 20 ++++------- 15 files changed, 175 insertions(+), 303 deletions(-) diff --git a/mypy/myunit/__init__.py b/mypy/myunit/__init__.py index 92ba802530eb..62efcee6da00 100644 --- a/mypy/myunit/__init__.py +++ b/mypy/myunit/__init__.py @@ -105,30 +105,19 @@ def fail() -> None: raise AssertionFailure() -class TestCase: - def __init__(self, name: str, suite: 'Optional[Suite]' = None, - func: Optional[Callable[[], None]] = None) -> None: - self.func = func +class ProtoTestCase: + def __init__(self, name: str) -> None: self.name = name - self.suite = suite self.old_cwd = None # type: Optional[str] self.tmpdir = None # type: Optional[tempfile.TemporaryDirectory[str]] - def run(self) -> None: - if self.func: - self.func() - def set_up(self) -> None: self.old_cwd = os.getcwd() self.tmpdir = tempfile.TemporaryDirectory(prefix='mypy-test-') os.chdir(self.tmpdir.name) os.mkdir('tmp') - if self.suite: - self.suite.set_up() def tear_down(self) -> None: - if self.suite: - self.suite.tear_down() assert self.old_cwd is not None and self.tmpdir is not None, \ "test was not properly set up" os.chdir(self.old_cwd) @@ -140,6 +129,28 @@ def tear_down(self) -> None: self.tmpdir = None +class TestCase(ProtoTestCase): + def __init__(self, name: str, suite: 'Optional[Suite]' = None, + func: Optional[Callable[[], None]] = None) -> None: + super().__init__(name) + self.func = func + self.suite = suite + + def run(self) -> None: + if self.func: + self.func() + + def set_up(self) -> None: + super().set_up() + if self.suite: + self.suite.set_up() + + def tear_down(self) -> None: + if self.suite: + self.suite.tear_down() + super().tear_down() + + class Suite: def __init__(self) -> None: self.prefix = typename(type(self)) + '.' diff --git a/mypy/test/data.py b/mypy/test/data.py index a080edea38de..07dff5454a53 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -8,26 +8,25 @@ import shutil import pytest # type: ignore # no pytest in typeshed -from typing import Callable, List, Tuple, Set, Optional, Iterator, Any, Dict - -from mypy.myunit import TestCase, SkipTestCaseException +from typing import List, Tuple, Set, Optional, Iterator, Any, Dict +import typing +from mypy.myunit import ProtoTestCase +from mypy.test.config import test_data_prefix root_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..')) def parse_test_cases( path: str, - perform: Optional[Callable[['DataDrivenTestCase'], None]], base_path: str = '.', optional_out: bool = False, - include_path: Optional[str] = None, native_sep: bool = False) -> List['DataDrivenTestCase']: """Parse a file with test case descriptions. Return an array of test cases. - NB this function and DataDrivenTestCase are shared between the + NB: this function and DataDrivenTestCase were shared between the myunit and pytest codepaths -- if something looks redundant, that's likely the reason. """ @@ -35,8 +34,7 @@ def parse_test_cases( join = os.path.join else: join = posixpath.join # type: ignore - if not include_path: - include_path = os.path.dirname(path) + include_path = os.path.dirname(path) with open(path, encoding='utf-8') as f: lst = f.readlines() for i in range(len(lst)): @@ -167,7 +165,7 @@ def parse_test_cases( arg0 = p[i0].arg assert arg0 is not None tc = DataDrivenTestCase(arg0, input, tcout, tcout2, path, - p[i0].line, lastline, perform, + p[i0].line, lastline, files, output_files, stale_modules, rechecked_modules, deleted_paths, native_sep) out.append(tc) @@ -179,7 +177,7 @@ def parse_test_cases( return out -class DataDrivenTestCase(TestCase): +class DataDrivenTestCase(ProtoTestCase): input = None # type: List[str] output = None # type: List[str] # Output for the first pass output2 = None # type: Dict[int, List[str]] # Output for runs 2+, indexed by run number @@ -202,7 +200,6 @@ def __init__(self, file: str, line: int, lastline: int, - perform: Optional[Callable[['DataDrivenTestCase'], None]], files: List[Tuple[str, str]], output_files: List[Tuple[str, str]], expected_stale_modules: Dict[int, Set[str]], @@ -217,7 +214,6 @@ def __init__(self, self.lastline = lastline self.file = file self.line = line - self.perform = perform self.files = files self.output_files = output_files self.expected_stale_modules = expected_stale_modules @@ -268,13 +264,6 @@ def add_dirs(self, dir: str) -> List[str]: os.mkdir(dir) return dirs - def run(self) -> None: - if self.name.endswith('-skip'): - raise SkipTestCaseException() - else: - assert self.perform is not None, 'Tests without `perform` should not be `run`' - self.perform(self) - def tear_down(self) -> None: # First remove files. for is_dir, path in reversed(self.clean_up): @@ -507,13 +496,37 @@ def pytest_addoption(parser: Any) -> None: def pytest_pycollect_makeitem(collector: Any, name: str, obj: Any) -> Any: if not isinstance(obj, type) or not issubclass(obj, DataSuite): return None + if obj is DataSuite: + return None return MypyDataSuite(name, parent=collector) class MypyDataSuite(pytest.Class): # type: ignore # inheriting from Any def collect(self) -> Iterator['MypyDataCase']: - for case in self.obj.cases(): - yield MypyDataCase(case.name, self, case) + cls = self.obj + for f in cls.files: + for case in parse_test_cases(os.path.join(test_data_prefix, f), + base_path=cls.base_path, + optional_out=cls.optional_out, + native_sep=cls.native_sep): + if cls.require_stable and not has_stable_flags(case): + continue + if cls.require_incremental and not is_incremental(case): + continue + yield MypyDataCase(case.name, self, case) + + +def is_incremental(testcase: DataDrivenTestCase) -> bool: + return 'incremental' in testcase.name.lower() or 'incremental' in testcase.file + + +def has_stable_flags(testcase: DataDrivenTestCase) -> bool: + if any(re.match(r'# flags[2-9]:', line) for line in testcase.input): + return False + for filename, contents in testcase.files: + if os.path.basename(filename).startswith('mypy.ini.'): + return False + return True class MypyDataCase(pytest.Item): # type: ignore # inheriting from Any @@ -556,12 +569,15 @@ def repr_failure(self, excinfo: Any) -> str: class DataSuite: + files = None # type: typing.ClassVar[List[str]] + base_path = '.' # type: typing.ClassVar[str] + optional_out = False # type: typing.ClassVar[bool] + native_sep = False # type: typing.ClassVar[bool] + require_stable = False # type: typing.ClassVar[bool] + require_incremental = False # type: typing.ClassVar[bool] + def __init__(self, *, update_data: bool) -> None: self.update_data = update_data - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - return [] - def run_case(self, testcase: DataDrivenTestCase) -> None: raise NotImplementedError diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index 7a4a6f289a7b..d96e07b1a76d 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -3,18 +3,16 @@ import os import re import shutil -import sys -import time -import typed_ast from typing import Dict, List, Optional, Set, Tuple +import typing from mypy import build, defaults from mypy.main import process_options from mypy.build import BuildSource, find_module_clear_caches from mypy.myunit import AssertionFailure -from mypy.test.config import test_temp_dir, test_data_prefix -from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite +from mypy.test.config import test_temp_dir +from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.helpers import ( assert_string_arrays_equal, normalize_error_messages, retry_on_error, testcase_pyversion, update_testcase_output, @@ -25,7 +23,7 @@ from mypy import experiments # List of files that contain test case descriptions. -files = [ +typecheck_files = [ 'check-basic.test', 'check-callable.test', 'check-classes.test', @@ -83,14 +81,9 @@ class TypeCheckSuite(DataSuite): - - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - c = [] # type: List[DataDrivenTestCase] - for f in files: - c += parse_test_cases(os.path.join(test_data_prefix, f), - None, test_temp_dir, True) - return c + files = typecheck_files # type: typing.ClassVar[List[str]] + base_path = test_temp_dir # type: typing.ClassVar[str] + optional_out = True # type: typing.ClassVar[bool] def run_case(self, testcase: DataDrivenTestCase) -> None: incremental = ('incremental' in testcase.name.lower() diff --git a/mypy/test/testcmdline.py b/mypy/test/testcmdline.py index 26f1780559e1..f8434ef08b62 100644 --- a/mypy/test/testcmdline.py +++ b/mypy/test/testcmdline.py @@ -9,12 +9,13 @@ import subprocess import sys -from typing import Tuple, List, Dict, Set +from typing import List +import typing -from mypy.myunit import Suite, SkipTestCaseException, AssertionFailure -from mypy.test.config import test_data_prefix, test_temp_dir +from mypy.myunit import AssertionFailure +from mypy.test.config import test_temp_dir from mypy.test.data import fix_cobertura_filename -from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite +from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.helpers import assert_string_arrays_equal, normalize_error_messages from mypy.version import __version__, base_version @@ -28,24 +29,17 @@ ] -class PythonEvaluationSuite(DataSuite): - - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - c = [] # type: List[DataDrivenTestCase] - for f in cmdline_files: - c += parse_test_cases(os.path.join(test_data_prefix, f), - test_python_evaluation, - base_path=test_temp_dir, - optional_out=True, - native_sep=True) - return c +class PythonCmdlineSuite(DataSuite): + files = cmdline_files # type: typing.ClassVar[List[str]] + base_path = test_temp_dir # type: typing.ClassVar[str] + optional_out = True # type: typing.ClassVar[bool] + native_sep = True # type: typing.ClassVar[bool] def run_case(self, testcase: DataDrivenTestCase) -> None: - test_python_evaluation(testcase) + test_python_cmdline(testcase) -def test_python_evaluation(testcase: DataDrivenTestCase) -> None: +def test_python_cmdline(testcase: DataDrivenTestCase) -> None: assert testcase.old_cwd is not None, "test was not properly set up" # Write the program to a file. program = '_program.py' diff --git a/mypy/test/testdeps.py b/mypy/test/testdeps.py index d6580c40d65b..b9cf09116198 100644 --- a/mypy/test/testdeps.py +++ b/mypy/test/testdeps.py @@ -2,6 +2,7 @@ import os from typing import List, Tuple, Dict, Optional +import typing from mypy import build from mypy.build import BuildSource @@ -9,25 +10,16 @@ from mypy.nodes import MypyFile, Expression from mypy.options import Options from mypy.server.deps import get_dependencies -from mypy.test.config import test_temp_dir, test_data_prefix -from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite +from mypy.test.config import test_temp_dir +from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.helpers import assert_string_arrays_equal from mypy.types import Type -files = [ - 'deps.test' -] - class GetDependenciesSuite(DataSuite): - - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - c = [] # type: List[DataDrivenTestCase] - for f in files: - c += parse_test_cases(os.path.join(test_data_prefix, f), - None, test_temp_dir, True) - return c + files = ['deps.test'] # type: typing.ClassVar[List[str]] + base_path = test_temp_dir # type: typing.ClassVar[str] + optional_out = True # type: typing.ClassVar[bool] def run_case(self, testcase: DataDrivenTestCase) -> None: src = '\n'.join(testcase.input) diff --git a/mypy/test/testdiff.py b/mypy/test/testdiff.py index 3f9d2345e787..8c2a72efa63a 100644 --- a/mypy/test/testdiff.py +++ b/mypy/test/testdiff.py @@ -2,6 +2,7 @@ import os from typing import List, Tuple, Dict, Optional +import typing from mypy import build from mypy.build import BuildSource @@ -9,25 +10,15 @@ from mypy.nodes import MypyFile from mypy.options import Options from mypy.server.astdiff import compare_symbol_tables -from mypy.test.config import test_temp_dir, test_data_prefix -from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite +from mypy.test.config import test_temp_dir +from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.helpers import assert_string_arrays_equal -files = [ - 'diff.test' -] - - class ASTDiffSuite(DataSuite): - - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - c = [] # type: List[DataDrivenTestCase] - for f in files: - c += parse_test_cases(os.path.join(test_data_prefix, f), - None, test_temp_dir, True) - return c + files = ['diff.test'] # type: typing.ClassVar[List[str]] + base_path = test_temp_dir # type: typing.ClassVar[str] + optional_out = True # type: typing.ClassVar[bool] def run_case(self, testcase: DataDrivenTestCase) -> None: first_src = '\n'.join(testcase.input) diff --git a/mypy/test/testdmypy.py b/mypy/test/testdmypy.py index 1f483e1bc315..37ad9a6de1ac 100644 --- a/mypy/test/testdmypy.py +++ b/mypy/test/testdmypy.py @@ -4,52 +4,43 @@ import re import shutil import sys -import time -import typed_ast from typing import Dict, List, Optional, Set, Tuple +import typing from mypy import build from mypy import defaults from mypy.main import process_options from mypy.myunit import AssertionFailure -from mypy.test.config import test_temp_dir, test_data_prefix -from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite +from mypy.test.config import test_temp_dir +from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.helpers import ( assert_string_arrays_equal, normalize_error_messages, retry_on_error, testcase_pyversion, update_testcase_output, ) -from mypy.errors import CompileError from mypy.options import Options -from mypy import experiments from mypy import dmypy # List of files that contain test case descriptions. -files = [ - 'check-enum.test', - 'check-incremental.test', - 'check-newtype.test', -] +if sys.platform != 'win32': + dmypy_files = [ + 'check-enum.test', + 'check-incremental.test', + 'check-newtype.test', + ] +else: + dmypy_files = [] class TypeCheckSuite(DataSuite): - - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - if sys.platform == 'win32': - return [] # Nothing here works on Windows. - c = [] # type: List[DataDrivenTestCase] - for f in files: - tc = parse_test_cases(os.path.join(test_data_prefix, f), - None, test_temp_dir, True) - c += [case for case in tc - if cls.has_stable_flags(case) and cls.is_incremental(case)] - return c + files = dmypy_files # type: typing.ClassVar[List[str]] + base_path = test_temp_dir # type: typing.ClassVar[str] + optional_out = True # type: typing.ClassVar[bool] + require_stable = True # type: typing.ClassVar[bool] + require_incremental = True # type: typing.ClassVar[bool] def run_case(self, testcase: DataDrivenTestCase) -> None: - assert self.is_incremental(testcase), "Testcase is not incremental" - assert self.has_stable_flags(testcase), "Testcase has varying flags" # All tests run once with a cold cache, then at least once # with a warm cache and maybe changed files. Expected output # is specified separately for each run. @@ -67,19 +58,6 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: for step in range(1, num_steps + 1): self.run_case_once(testcase, step) - @classmethod - def is_incremental(cls, testcase: DataDrivenTestCase) -> bool: - return 'incremental' in testcase.name.lower() or 'incremental' in testcase.file - - @classmethod - def has_stable_flags(cls, testcase: DataDrivenTestCase) -> bool: - if any(re.match(r'# flags[2-9]:', line) for line in testcase.input): - return False - for filename, contents in testcase.files: - if os.path.basename(filename).startswith('mypy.ini.'): - return False - return True - def clear_cache(self) -> None: dn = defaults.CACHE_DIR if os.path.exists(dn): diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index 7e442d545566..83f72ec8f074 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -11,6 +11,7 @@ import re import shutil from typing import List, Tuple, Dict +import typing from mypy import build from mypy.build import BuildManager, BuildSource, Graph @@ -29,19 +30,10 @@ from mypy.util import short_type -files = [ - 'fine-grained.test' -] - - class FineGrainedSuite(DataSuite): - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - c = [] # type: List[DataDrivenTestCase] - for f in files: - c += parse_test_cases(os.path.join(test_data_prefix, f), - None, test_temp_dir, True) - return c + files = ['fine-grained.test'] # type: typing.ClassVar[List[str]] + base_path = test_temp_dir # type: typing.ClassVar[str] + optional_out = True # type: typing.ClassVar[bool] def run_case(self, testcase: DataDrivenTestCase) -> None: main_src = '\n'.join(testcase.input) diff --git a/mypy/test/testmerge.py b/mypy/test/testmerge.py index 4694865d284a..a9608964dc91 100644 --- a/mypy/test/testmerge.py +++ b/mypy/test/testmerge.py @@ -3,6 +3,7 @@ import os import shutil from typing import List, Tuple, Dict, Optional +import typing from mypy import build from mypy.build import BuildManager, BuildSource, State @@ -15,19 +16,14 @@ from mypy.server.subexpr import get_subexpressions from mypy.server.update import build_incremental_step, replace_modules_with_new_variants from mypy.strconv import StrConv, indent -from mypy.test.config import test_temp_dir, test_data_prefix -from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite +from mypy.test.config import test_temp_dir +from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.helpers import assert_string_arrays_equal from mypy.test.testtypegen import ignore_node from mypy.types import TypeStrVisitor, Type from mypy.util import short_type, IdMapper -files = [ - 'merge.test' -] - - # Which data structures to dump in a test case? SYMTABLE = 'SYMTABLE' TYPEINFO = ' TYPEINFO' @@ -36,6 +32,10 @@ class ASTMergeSuite(DataSuite): + files = ['merge.test'] # type: typing.ClassVar[List[str]] + base_path = test_temp_dir # type: typing.ClassVar[str] + optional_out = True # type: typing.ClassVar[bool] + def __init__(self, *, update_data: bool) -> None: super().__init__(update_data=update_data) self.str_conv = StrConv(show_ids=True) @@ -43,14 +43,6 @@ def __init__(self, *, update_data: bool) -> None: self.id_mapper = self.str_conv.id_mapper # type: IdMapper self.type_str_conv = TypeStrVisitor(self.id_mapper) - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - c = [] # type: List[DataDrivenTestCase] - for f in files: - c += parse_test_cases(os.path.join(test_data_prefix, f), - None, test_temp_dir, True) - return c - def run_case(self, testcase: DataDrivenTestCase) -> None: name = testcase.name # We use the test case name to decide which data structures to dump. diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py index ef9632a34372..633b5fc9d411 100644 --- a/mypy/test/testparse.py +++ b/mypy/test/testparse.py @@ -1,31 +1,19 @@ """Tests for the mypy parser.""" - -import os.path - from typing import List +import typing from mypy import defaults -from mypy.myunit import Suite, AssertionFailure +from mypy.myunit import AssertionFailure from mypy.test.helpers import assert_string_arrays_equal -from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite -from mypy.test import config +from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.parse import parse from mypy.errors import CompileError from mypy.options import Options class ParserSuite(DataSuite): - parse_files = ['parse.test', - 'parse-python2.test'] - - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - # The test case descriptions are stored in data files. - c = [] # type: List[DataDrivenTestCase] - for f in cls.parse_files: - c += parse_test_cases( - os.path.join(config.test_data_prefix, f), test_parser) - return c + files = ['parse.test', + 'parse-python2.test'] # type: typing.ClassVar[List[str]] def run_case(self, testcase: DataDrivenTestCase) -> None: test_parser(testcase) @@ -62,12 +50,7 @@ def test_parser(testcase: DataDrivenTestCase) -> None: class ParseErrorSuite(DataSuite): - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - # Test case descriptions are in an external file. - return parse_test_cases(os.path.join(config.test_data_prefix, - 'parse-errors.test'), - test_parse_error) + files = ['parse-errors.test'] # type: typing.ClassVar[List[str]] def run_case(self, testcase: DataDrivenTestCase) -> None: test_parse_error(testcase) diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index be32252a21b5..37481771397f 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -18,36 +18,25 @@ import pytest # type: ignore # no pytest in typeshed from typing import Dict, List, Tuple, Optional +import typing -from mypy.test.config import test_data_prefix, test_temp_dir -from mypy.test.data import DataDrivenTestCase, parse_test_cases, DataSuite +from mypy.test.config import test_temp_dir +from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.helpers import assert_string_arrays_equal from mypy.util import try_find_python2_interpreter from mypy import api -# Files which contain test case descriptions. -python_eval_files = ['pythoneval.test', - 'python2eval.test'] - -python_34_eval_files = ['pythoneval-asyncio.test'] - # Path to Python 3 interpreter python3_path = sys.executable program_re = re.compile(r'\b_program.py\b') class PythonEvaluationSuite(DataSuite): - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - c = [] # type: List[DataDrivenTestCase] - for f in python_eval_files: - c += parse_test_cases(os.path.join(test_data_prefix, f), - test_python_evaluation, test_temp_dir, True) - if sys.version_info.major == 3 and sys.version_info.minor >= 4: - for f in python_34_eval_files: - c += parse_test_cases(os.path.join(test_data_prefix, f), - test_python_evaluation, test_temp_dir, True) - return c + files = ['pythoneval.test', 'python2eval.test'] # type: typing.ClassVar[List[str]] + if sys.version_info.major == 3 and sys.version_info.minor >= 4: + files += ['pythoneval-asyncio.test'] + base_path = test_temp_dir # type: typing.ClassVar[str] + optional_out = True # type: typing.ClassVar[bool] def run_case(self, testcase: DataDrivenTestCase) -> None: test_python_evaluation(testcase) diff --git a/mypy/test/testsemanal.py b/mypy/test/testsemanal.py index 8ea7c1d5cc9f..f4242cc5fb2d 100644 --- a/mypy/test/testsemanal.py +++ b/mypy/test/testsemanal.py @@ -3,14 +3,15 @@ import os.path from typing import Dict, List +import typing from mypy import build from mypy.build import BuildSource from mypy.test.helpers import ( assert_string_arrays_equal, normalize_error_messages, testfile_pyversion, ) -from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite -from mypy.test.config import test_data_prefix, test_temp_dir +from mypy.test.data import DataDrivenTestCase, DataSuite +from mypy.test.config import test_temp_dir from mypy.errors import CompileError from mypy.nodes import TypeInfo from mypy.options import Options @@ -42,16 +43,10 @@ def get_semanal_options() -> Options: class SemAnalSuite(DataSuite): - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - c = [] # type: List[DataDrivenTestCase] - for f in semanal_files: - c += parse_test_cases(os.path.join(test_data_prefix, f), - test_semanal, - base_path=test_temp_dir, - optional_out=True, - native_sep=True) - return c + files = semanal_files # type: typing.ClassVar[List[str]] + base_path = test_temp_dir # type: typing.ClassVar[str] + optional_out = True # type: typing.ClassVar[bool] + native_sep = True # type: typing.ClassVar[bool] def run_case(self, testcase: DataDrivenTestCase) -> None: test_semanal(testcase) @@ -101,19 +96,10 @@ def test_semanal(testcase: DataDrivenTestCase) -> None: # Semantic analyzer error test cases -# Paths to files containing test case descriptions. -semanal_error_files = ['semanal-errors.test'] - - class SemAnalErrorSuite(DataSuite): - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - # Read test cases from test case description files. - c = [] # type: List[DataDrivenTestCase] - for f in semanal_error_files: - c += parse_test_cases(os.path.join(test_data_prefix, f), - test_semanal_error, test_temp_dir, optional_out=True) - return c + files = ['semanal-errors.test'] # type: typing.ClassVar[List[str]] + base_path = test_temp_dir # type: typing.ClassVar[str] + optional_out = True # type: typing.ClassVar[bool] def run_case(self, testcase: DataDrivenTestCase) -> None: test_semanal_error(testcase) @@ -140,18 +126,9 @@ def test_semanal_error(testcase: DataDrivenTestCase) -> None: # SymbolNode table export test cases -# Test case descriptions -semanal_symtable_files = ['semanal-symtable.test'] - - class SemAnalSymtableSuite(DataSuite): - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - c = [] # type: List[DataDrivenTestCase] - for f in semanal_symtable_files: - c += parse_test_cases(os.path.join(test_data_prefix, f), - None, test_temp_dir) - return c + files = ['semanal-symtable.test'] # type: typing.ClassVar[List[str]] + base_path = test_temp_dir # type: typing.ClassVar[str] def run_case(self, testcase: DataDrivenTestCase) -> None: """Perform a test case.""" @@ -179,19 +156,9 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: # Type info export test cases - -semanal_typeinfo_files = ['semanal-typeinfo.test'] - - class SemAnalTypeInfoSuite(DataSuite): - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - """Test case descriptions""" - c = [] # type: List[DataDrivenTestCase] - for f in semanal_typeinfo_files: - c += parse_test_cases(os.path.join(test_data_prefix, f), - None, test_temp_dir) - return c + files = ['semanal-typeinfo.test'] # type: typing.ClassVar[List[str]] + base_path = test_temp_dir # type: typing.ClassVar[str] def run_case(self, testcase: DataDrivenTestCase) -> None: """Perform a test case.""" diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 539080c387f9..3386f6ddb24a 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -1,21 +1,18 @@ import glob import importlib import os.path -import random import shutil import sys import tempfile -import time import re from types import ModuleType from typing import List, Tuple +import typing -from mypy.myunit import Suite, AssertionFailure, assert_equal +from mypy.myunit import Suite, assert_equal from mypy.test.helpers import assert_string_arrays_equal -from mypy.test.data import DataSuite, parse_test_cases, DataDrivenTestCase -from mypy.test import config -from mypy.parse import parse +from mypy.test.data import DataSuite, DataDrivenTestCase from mypy.errors import CompileError from mypy.stubgen import generate_stub, generate_stub_for_module, parse_options, Options from mypy.stubgenc import generate_c_type_stub, infer_method_sig @@ -96,14 +93,7 @@ def test_infer_sig_from_docstring(self) -> None: class StubgenPythonSuite(DataSuite): - test_data_files = ['stubgen.test'] - - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - c = [] # type: List[DataDrivenTestCase] - for path in cls.test_data_files: - c += parse_test_cases(os.path.join(config.test_data_prefix, path), test_stubgen) - return c + files = ['stubgen.test'] # type: typing.ClassVar[List[str]] def run_case(self, testcase: DataDrivenTestCase) -> None: test_stubgen(testcase) diff --git a/mypy/test/testtransform.py b/mypy/test/testtransform.py index 852b96721d40..c884356085ee 100644 --- a/mypy/test/testtransform.py +++ b/mypy/test/testtransform.py @@ -1,16 +1,16 @@ """Identity AST transform test cases""" import os.path - -from typing import Dict, List +from typing import List +import typing from mypy import build from mypy.build import BuildSource from mypy.test.helpers import ( assert_string_arrays_equal, testfile_pyversion, normalize_error_messages ) -from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite -from mypy.test.config import test_data_prefix, test_temp_dir +from mypy.test.data import DataDrivenTestCase, DataSuite +from mypy.test.config import test_temp_dir from mypy.errors import CompileError from mypy.treetransform import TransformVisitor from mypy.types import Type @@ -19,24 +19,16 @@ class TransformSuite(DataSuite): # Reuse semantic analysis test cases. - transform_files = ['semanal-basic.test', - 'semanal-expressions.test', - 'semanal-classes.test', - 'semanal-types.test', - 'semanal-modules.test', - 'semanal-statements.test', - 'semanal-abstractclasses.test', - 'semanal-python2.test'] - - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - c = [] # type: List[DataDrivenTestCase] - for f in cls.transform_files: - c += parse_test_cases(os.path.join(test_data_prefix, f), - test_transform, - base_path=test_temp_dir, - native_sep=True) - return c + files = ['semanal-basic.test', + 'semanal-expressions.test', + 'semanal-classes.test', + 'semanal-types.test', + 'semanal-modules.test', + 'semanal-statements.test', + 'semanal-abstractclasses.test', + 'semanal-python2.test'] # type: typing.ClassVar[List[str]] + base_path = test_temp_dir # type: typing.ClassVar[str] + native_sep = True # type: typing.ClassVar[bool] def run_case(self, testcase: DataDrivenTestCase) -> None: test_transform(testcase) diff --git a/mypy/test/testtypegen.py b/mypy/test/testtypegen.py index 7376d51913ef..65dc25ada334 100644 --- a/mypy/test/testtypegen.py +++ b/mypy/test/testtypegen.py @@ -1,14 +1,14 @@ """Test cases for the type checker: exporting inferred types""" -import os.path import re from typing import Set, List +import typing from mypy import build from mypy.build import BuildSource -from mypy.test import config -from mypy.test.data import parse_test_cases, DataDrivenTestCase, DataSuite +from mypy.test.config import test_temp_dir +from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.helpers import assert_string_arrays_equal from mypy.util import short_type from mypy.nodes import ( @@ -20,16 +20,8 @@ class TypeExportSuite(DataSuite): - # List of files that contain test case descriptions. - files = ['typexport-basic.test'] - - @classmethod - def cases(cls) -> List[DataDrivenTestCase]: - c = [] # type: List[DataDrivenTestCase] - for f in cls.files: - c += parse_test_cases(os.path.join(config.test_data_prefix, f), - None, config.test_temp_dir) - return c + files = ['typexport-basic.test'] # type: typing.ClassVar[List[str]] + base_path = test_temp_dir # type: typing.ClassVar[str] def run_case(self, testcase: DataDrivenTestCase) -> None: try: @@ -44,7 +36,7 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: options.show_traceback = True result = build.build(sources=[BuildSource('main', None, src)], options=options, - alt_lib_path=config.test_temp_dir) + alt_lib_path=test_temp_dir) a = result.errors map = result.types nodes = map.keys() From 3d89f93e723b75f216721645031ffbabf175e8a6 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Thu, 23 Nov 2017 22:57:44 +0200 Subject: [PATCH 02/14] import test_temp_dir --- mypy/test/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/test/data.py b/mypy/test/data.py index a8642b59605b..79d2f660351e 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -12,7 +12,7 @@ import typing from mypy.myunit import ProtoTestCase -from mypy.test.config import test_data_prefix +from mypy.test.config import test_data_prefix, test_temp_dir root_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..')) From 0ce79aba664a5a219004beb8cd95dfc5e24f3769 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Fri, 1 Dec 2017 12:58:47 +0200 Subject: [PATCH 03/14] remove python3.3 related check --- mypy/test/testpythoneval.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index 37481771397f..4eadd1109f97 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -32,9 +32,9 @@ class PythonEvaluationSuite(DataSuite): - files = ['pythoneval.test', 'python2eval.test'] # type: typing.ClassVar[List[str]] - if sys.version_info.major == 3 and sys.version_info.minor >= 4: - files += ['pythoneval-asyncio.test'] + files = ['pythoneval.test', + 'python2eval.test', + 'pythoneval-asyncio.test'] # type: typing.ClassVar[List[str]] base_path = test_temp_dir # type: typing.ClassVar[str] optional_out = True # type: typing.ClassVar[bool] From cdbfe1b5043bc9affbce2119e92210f15a0a3778 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Fri, 1 Dec 2017 13:10:38 +0200 Subject: [PATCH 04/14] rename ProtoTestCase to BaseTestCase --- mypy/myunit/__init__.py | 4 ++-- mypy/test/data.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mypy/myunit/__init__.py b/mypy/myunit/__init__.py index 62efcee6da00..c0420a00653f 100644 --- a/mypy/myunit/__init__.py +++ b/mypy/myunit/__init__.py @@ -105,7 +105,7 @@ def fail() -> None: raise AssertionFailure() -class ProtoTestCase: +class BaseTestCase: def __init__(self, name: str) -> None: self.name = name self.old_cwd = None # type: Optional[str] @@ -129,7 +129,7 @@ def tear_down(self) -> None: self.tmpdir = None -class TestCase(ProtoTestCase): +class TestCase(BaseTestCase): def __init__(self, name: str, suite: 'Optional[Suite]' = None, func: Optional[Callable[[], None]] = None) -> None: super().__init__(name) diff --git a/mypy/test/data.py b/mypy/test/data.py index 79d2f660351e..9c2daaff03bf 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -11,7 +11,7 @@ from typing import Callable, List, Tuple, Set, Optional, Iterator, Any, Dict, NamedTuple, Union import typing -from mypy.myunit import ProtoTestCase +from mypy.myunit import BaseTestCase from mypy.test.config import test_data_prefix, test_temp_dir root_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..')) @@ -188,7 +188,7 @@ def parse_test_cases( return out -class DataDrivenTestCase(ProtoTestCase): +class DataDrivenTestCase(BaseTestCase): input = None # type: List[str] output = None # type: List[str] # Output for the first pass output2 = None # type: Dict[int, List[str]] # Output for runs 2+, indexed by run number From 73b56a6f5769ddc7a2d99823a42990a4fc593a20 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Fri, 1 Dec 2017 14:23:39 +0200 Subject: [PATCH 05/14] cleanup and document myunit classes --- mypy/myunit/__init__.py | 76 +++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/mypy/myunit/__init__.py b/mypy/myunit/__init__.py index c0420a00653f..c72b0f96cf55 100644 --- a/mypy/myunit/__init__.py +++ b/mypy/myunit/__init__.py @@ -1,13 +1,12 @@ import importlib import os import sys -import re import tempfile import time import traceback -from typing import List, Tuple, Any, Callable, Union, cast, Optional -from types import TracebackType +from typing import List, Tuple, Any, Callable, Union, cast, Optional, Iterable +from types import TracebackType, MethodType # TODO remove global state @@ -106,6 +105,9 @@ def fail() -> None: class BaseTestCase: + """Common base class for _MyUnitTestCase and DataDrivenTestCase. + + Handles temporary folder creation and deletion""" def __init__(self, name: str) -> None: self.name = name self.old_cwd = None # type: Optional[str] @@ -129,57 +131,50 @@ def tear_down(self) -> None: self.tmpdir = None -class TestCase(BaseTestCase): - def __init__(self, name: str, suite: 'Optional[Suite]' = None, - func: Optional[Callable[[], None]] = None) -> None: +class _MyUnitTestCase(BaseTestCase): + """A concrete, myunit-specific test case, a wrapper around a method to run.""" + + def __init__(self, name: str, suite: 'Suite', run: Callable[[], None]) -> None: super().__init__(name) - self.func = func + self.run = run self.suite = suite - def run(self) -> None: - if self.func: - self.func() - def set_up(self) -> None: super().set_up() - if self.suite: - self.suite.set_up() + self.suite.set_up() def tear_down(self) -> None: - if self.suite: - self.suite.tear_down() + self.suite.tear_down() # No-op super().tear_down() class Suite: - def __init__(self) -> None: - self.prefix = typename(type(self)) + '.' - # Each test case is either a TestCase object or (str, function). - self._test_cases = [] # type: List[Any] - self.init() + """Abstract class for myunit test suites - node in the tree whose leaves are _MyUnitTestCases. - def set_up(self) -> None: - pass + The children `cases` are looked up during __init__, looking for attributes named test_* + they are either no-arg methods or of a pair (name, Suite).""" - def tear_down(self) -> None: - pass + cases = None # type: Iterable[Union[_MyUnitTestCase, Tuple[str, Suite]]] - def init(self) -> None: + def __init__(self) -> None: + self.prefix = typename(type(self)) + '.' + self.cases = [] for m in dir(self): - if m.startswith('test'): + if m.startswith('test_'): t = getattr(self, m) if isinstance(t, Suite): - self.add_test((m + '.', t)) + self.cases.append((m + '.', t)) else: - self.add_test(TestCase(m, self, getattr(self, m))) + assert isinstance(t, MethodType) + self.cases.append(_MyUnitTestCase(m, self, t)) - def add_test(self, test: Union[TestCase, - Tuple[str, Callable[[], None]], - Tuple[str, 'Suite']]) -> None: - self._test_cases.append(test) + def set_up(self) -> None: + """Set up fixtures""" + pass - def cases(self) -> List[Any]: - return self._test_cases[:] + def tear_down(self) -> None: + # This method is not overridden in practice + pass def skip(self) -> None: raise SkipTestCaseException() @@ -261,10 +256,11 @@ def main(args: Optional[List[str]] = None) -> None: sys.exit(1) -def run_test_recursive(test: Any, num_total: int, num_fail: int, num_skip: int, +def run_test_recursive(test: Union[_MyUnitTestCase, Tuple[str, Suite], ListSuite], + num_total: int, num_fail: int, num_skip: int, prefix: str, depth: int) -> Tuple[int, int, int]: - """The first argument may be TestCase, Suite or (str, Suite).""" - if isinstance(test, TestCase): + """The first argument may be _MyUnitTestCase, Suite or (str, Suite).""" + if isinstance(test, _MyUnitTestCase): name = prefix + test.name for pattern in patterns: if match_pattern(name, pattern): @@ -286,7 +282,7 @@ def run_test_recursive(test: Any, num_total: int, num_fail: int, num_skip: int, suite = test suite_prefix = test.prefix - for stest in suite.cases(): + for stest in suite.cases: new_prefix = prefix if depth > 0: new_prefix = prefix + suite_prefix @@ -295,14 +291,14 @@ def run_test_recursive(test: Any, num_total: int, num_fail: int, num_skip: int, return num_total, num_fail, num_skip -def run_single_test(name: str, test: Any) -> Tuple[bool, bool]: +def run_single_test(name: str, test: _MyUnitTestCase) -> Tuple[bool, bool]: if is_verbose: sys.stderr.write(name) sys.stderr.flush() time0 = time.time() test.set_up() # FIX: check exceptions - exc_traceback = None # type: Any + exc_traceback = None # type: TracebackType try: test.run() except BaseException as e: From f255c4b0b867c7fec085a35008d3d57c39eb6e5e Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Fri, 1 Dec 2017 15:30:10 +0200 Subject: [PATCH 06/14] document pytest magic --- mypy/test/data.py | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/mypy/test/data.py b/mypy/test/data.py index 9c2daaff03bf..0337c826a724 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -546,17 +546,25 @@ def pytest_addoption(parser: Any) -> None: # This function name is special to pytest. See # http://doc.pytest.org/en/latest/writing_plugins.html#collection-hooks -def pytest_pycollect_makeitem(collector: Any, name: str, obj: Any) -> Any: - if not isinstance(obj, type) or not issubclass(obj, DataSuite): - return None - if obj is DataSuite: - return None - return MypyDataSuite(name, parent=collector) +def pytest_pycollect_makeitem(collector: pytest.Collector, name: str, + obj: object) -> 'Optional[pytest.Class]': # type: ignore + """Called by pytest on each object in modules configured in conftest.py files""" + + if isinstance(obj, type): + # Only classes derived from DataSuite contain test cases, not the DataSuite class itself + if issubclass(obj, DataSuite) and obj is not DataSuite: + # Non-None result means this obj is a test case + # result.collect will be called, with self.obj being obj + return MypyDataSuite(name, parent=collector) + return None class MypyDataSuite(pytest.Class): # type: ignore # inheriting from Any - def collect(self) -> Iterator['MypyDataCase']: - cls = self.obj + def collect(self) -> Iterator[pytest.Item]: # type: ignore + """Called by pytest on each of the object returned from pytest_pycollect_makeitem""" + + # obj is the object for which pytest_pycollect_makeitem returned self. + cls = self.obj # type: DataSuite for f in cls.files: for case in parse_test_cases(os.path.join(test_data_prefix, f), base_path=cls.base_path, @@ -583,29 +591,29 @@ def has_stable_flags(testcase: DataDrivenTestCase) -> bool: class MypyDataCase(pytest.Item): # type: ignore # inheriting from Any - def __init__(self, name: str, parent: MypyDataSuite, obj: DataDrivenTestCase) -> None: + def __init__(self, name: str, parent: MypyDataSuite, case: DataDrivenTestCase) -> None: self.skip = False if name.endswith('-skip'): self.skip = True name = name[:-len('-skip')] super().__init__(name, parent) - self.obj = obj + self.case = case def runtest(self) -> None: if self.skip: pytest.skip() update_data = self.config.getoption('--update-data', False) - self.parent.obj(update_data=update_data).run_case(self.obj) + self.parent.obj(update_data=update_data).run_case(self.case) def setup(self) -> None: - self.obj.set_up() + self.case.set_up() def teardown(self) -> None: - self.obj.tear_down() + self.case.tear_down() def reportinfo(self) -> Tuple[str, int, str]: - return self.obj.file, self.obj.line, self.obj.name + return self.case.file, self.case.line, self.case.name def repr_failure(self, excinfo: Any) -> str: if excinfo.errisinstance(SystemExit): @@ -618,7 +626,7 @@ def repr_failure(self, excinfo: Any) -> str: self.parent._prunetraceback(excinfo) excrepr = excinfo.getrepr(style='short') - return "data: {}:{}:\n{}".format(self.obj.file, self.obj.line, excrepr) + return "data: {}:{}:\n{}".format(self.case.file, self.case.line, excrepr) class DataSuite: From a5c6939dd35230833b87b4f84735379b118e818c Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Fri, 1 Dec 2017 16:11:03 +0200 Subject: [PATCH 07/14] uniform naming (setup/teardown); document DataDrivenTestCase --- mypy/myunit/__init__.py | 18 +++++++++--------- mypy/test/data.py | 25 ++++++++++++++++--------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/mypy/myunit/__init__.py b/mypy/myunit/__init__.py index c72b0f96cf55..79fda5d3b777 100644 --- a/mypy/myunit/__init__.py +++ b/mypy/myunit/__init__.py @@ -113,13 +113,13 @@ def __init__(self, name: str) -> None: self.old_cwd = None # type: Optional[str] self.tmpdir = None # type: Optional[tempfile.TemporaryDirectory[str]] - def set_up(self) -> None: + def setup(self) -> None: self.old_cwd = os.getcwd() self.tmpdir = tempfile.TemporaryDirectory(prefix='mypy-test-') os.chdir(self.tmpdir.name) os.mkdir('tmp') - def tear_down(self) -> None: + def teardown(self) -> None: assert self.old_cwd is not None and self.tmpdir is not None, \ "test was not properly set up" os.chdir(self.old_cwd) @@ -139,13 +139,13 @@ def __init__(self, name: str, suite: 'Suite', run: Callable[[], None]) -> None: self.run = run self.suite = suite - def set_up(self) -> None: - super().set_up() + def setup(self) -> None: + super().setup() self.suite.set_up() - def tear_down(self) -> None: + def teardown(self) -> None: self.suite.tear_down() # No-op - super().tear_down() + super().teardown() class Suite: @@ -297,8 +297,8 @@ def run_single_test(name: str, test: _MyUnitTestCase) -> Tuple[bool, bool]: sys.stderr.flush() time0 = time.time() - test.set_up() # FIX: check exceptions - exc_traceback = None # type: TracebackType + test.setup() # FIX: check exceptions + exc_traceback = None # type: Optional[TracebackType] try: test.run() except BaseException as e: @@ -306,7 +306,7 @@ def run_single_test(name: str, test: _MyUnitTestCase) -> Tuple[bool, bool]: raise exc_type, exc_value, exc_traceback = sys.exc_info() finally: - test.tear_down() + test.teardown() times.append((time.time() - time0, name)) if exc_traceback: diff --git a/mypy/test/data.py b/mypy/test/data.py index 0337c826a724..af7fa11a032e 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -189,6 +189,11 @@ def parse_test_cases( class DataDrivenTestCase(BaseTestCase): + """Holds parsed data and handles directory setup and teardown for MypyDataCase.""" + + # TODO: rename to ParsedTestCase or merge with MypyDataCase (yet avoid multiple inheritance) + # TODO: only create files on setup, not during parsing + input = None # type: List[str] output = None # type: List[str] # Output for the first pass output2 = None # type: Dict[int, List[str]] # Output for runs 2+, indexed by run number @@ -233,8 +238,8 @@ def __init__(self, self.deleted_paths = deleted_paths self.native_sep = native_sep - def set_up(self) -> None: - super().set_up() + def setup(self) -> None: + super().setup() encountered_files = set() self.clean_up = [] for paths in self.deleted_paths.values(): @@ -276,7 +281,7 @@ def add_dirs(self, dir: str) -> List[str]: os.mkdir(dir) return dirs - def tear_down(self) -> None: + def teardown(self) -> None: # First remove files. for is_dir, path in reversed(self.clean_up): if not is_dir: @@ -310,7 +315,7 @@ def tear_down(self) -> None: if path.startswith(test_temp_dir + '/') and os.path.isdir(path): shutil.rmtree(path) raise - super().tear_down() + super().teardown() def find_steps(self) -> List[List[FileOperation]]: """Return a list of descriptions of file operations for each incremental step. @@ -546,9 +551,11 @@ def pytest_addoption(parser: Any) -> None: # This function name is special to pytest. See # http://doc.pytest.org/en/latest/writing_plugins.html#collection-hooks -def pytest_pycollect_makeitem(collector: pytest.Collector, name: str, - obj: object) -> 'Optional[pytest.Class]': # type: ignore - """Called by pytest on each object in modules configured in conftest.py files""" +def pytest_pycollect_makeitem(collector: Any, name: str, + obj: object) -> 'Optional[Any]': + """Called by pytest on each object in modules configured in conftest.py files. + + collector is pytest.Collector, returns Optional[pytest.Class]""" if isinstance(obj, type): # Only classes derived from DataSuite contain test cases, not the DataSuite class itself @@ -607,10 +614,10 @@ def runtest(self) -> None: self.parent.obj(update_data=update_data).run_case(self.case) def setup(self) -> None: - self.case.set_up() + self.case.setup() def teardown(self) -> None: - self.case.tear_down() + self.case.teardown() def reportinfo(self) -> Tuple[str, int, str]: return self.case.file, self.case.line, self.case.name From b5c1067e4ff46a1498891e79177c111ca16bd12e Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Sat, 2 Dec 2017 20:29:33 +0200 Subject: [PATCH 08/14] assert is_incremental and has_stable_flags --- mypy/test/data.py | 6 ------ mypy/test/testdmypy.py | 7 ++++--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/mypy/test/data.py b/mypy/test/data.py index af7fa11a032e..d8fa78114f0f 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -577,10 +577,6 @@ def collect(self) -> Iterator[pytest.Item]: # type: ignore base_path=cls.base_path, optional_out=cls.optional_out, native_sep=cls.native_sep): - if cls.require_stable and not has_stable_flags(case): - continue - if cls.require_incremental and not is_incremental(case): - continue yield MypyDataCase(case.name, self, case) @@ -641,8 +637,6 @@ class DataSuite: base_path = '.' # type: typing.ClassVar[str] optional_out = False # type: typing.ClassVar[bool] native_sep = False # type: typing.ClassVar[bool] - require_stable = False # type: typing.ClassVar[bool] - require_incremental = False # type: typing.ClassVar[bool] def __init__(self, *, update_data: bool) -> None: self.update_data = update_data diff --git a/mypy/test/testdmypy.py b/mypy/test/testdmypy.py index 37ad9a6de1ac..0fd375783c09 100644 --- a/mypy/test/testdmypy.py +++ b/mypy/test/testdmypy.py @@ -13,7 +13,7 @@ from mypy.main import process_options from mypy.myunit import AssertionFailure from mypy.test.config import test_temp_dir -from mypy.test.data import DataDrivenTestCase, DataSuite +from mypy.test.data import DataDrivenTestCase, DataSuite, has_stable_flags, is_incremental from mypy.test.helpers import ( assert_string_arrays_equal, normalize_error_messages, retry_on_error, testcase_pyversion, update_testcase_output, @@ -37,10 +37,11 @@ class TypeCheckSuite(DataSuite): files = dmypy_files # type: typing.ClassVar[List[str]] base_path = test_temp_dir # type: typing.ClassVar[str] optional_out = True # type: typing.ClassVar[bool] - require_stable = True # type: typing.ClassVar[bool] - require_incremental = True # type: typing.ClassVar[bool] def run_case(self, testcase: DataDrivenTestCase) -> None: + assert has_stable_flags(testcase) + assert is_incremental(testcase) + # All tests run once with a cold cache, then at least once # with a warm cache and maybe changed files. Expected output # is specified separately for each run. From 9e12fec05539b731f64d067cec1ef62965eb4f44 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Sat, 2 Dec 2017 20:37:15 +0200 Subject: [PATCH 09/14] avoid using ClassVar --- mypy/test/config.py | 1 - mypy/test/data.py | 10 +++++----- mypy/test/testargs.py | 1 - mypy/test/testcheck.py | 7 +++---- mypy/test/testcmdline.py | 9 ++++----- mypy/test/testdeps.py | 7 +++---- mypy/test/testdiff.py | 7 +++---- mypy/test/testdmypy.py | 7 +++---- mypy/test/testfinegrained.py | 7 +++---- mypy/test/testmerge.py | 7 +++---- mypy/test/testparse.py | 5 ++--- mypy/test/testpythoneval.py | 7 +++---- mypy/test/testsemanal.py | 23 +++++++++++------------ mypy/test/teststubgen.py | 3 +-- mypy/test/testtransform.py | 7 +++---- mypy/test/testtypegen.py | 5 ++--- 16 files changed, 49 insertions(+), 64 deletions(-) diff --git a/mypy/test/config.py b/mypy/test/config.py index 681f86664d4a..744ba3d76aba 100644 --- a/mypy/test/config.py +++ b/mypy/test/config.py @@ -1,7 +1,6 @@ import os import os.path -import typing this_file_dir = os.path.dirname(os.path.realpath(__file__)) diff --git a/mypy/test/data.py b/mypy/test/data.py index d8fa78114f0f..2b39256b9586 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -9,7 +9,6 @@ import pytest # type: ignore # no pytest in typeshed from typing import Callable, List, Tuple, Set, Optional, Iterator, Any, Dict, NamedTuple, Union -import typing from mypy.myunit import BaseTestCase from mypy.test.config import test_data_prefix, test_temp_dir @@ -633,10 +632,11 @@ def repr_failure(self, excinfo: Any) -> str: class DataSuite: - files = None # type: typing.ClassVar[List[str]] - base_path = '.' # type: typing.ClassVar[str] - optional_out = False # type: typing.ClassVar[bool] - native_sep = False # type: typing.ClassVar[bool] + # option fields - class variables + files = None # type: List[str] + base_path = '.' # type: str + optional_out = False # type: bool + native_sep = False # type: bool def __init__(self, *, update_data: bool) -> None: self.update_data = update_data diff --git a/mypy/test/testargs.py b/mypy/test/testargs.py index 4e27e37a7e45..edee17b90ffb 100644 --- a/mypy/test/testargs.py +++ b/mypy/test/testargs.py @@ -5,7 +5,6 @@ object it creates. """ -import typing from mypy.myunit import Suite, assert_equal from mypy.options import Options, BuildType from mypy.main import process_options diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index d96e07b1a76d..d5f99f2dba77 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -5,7 +5,6 @@ import shutil from typing import Dict, List, Optional, Set, Tuple -import typing from mypy import build, defaults from mypy.main import process_options @@ -81,9 +80,9 @@ class TypeCheckSuite(DataSuite): - files = typecheck_files # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] - optional_out = True # type: typing.ClassVar[bool] + files = typecheck_files # type: List[str] + base_path = test_temp_dir # type: str + optional_out = True # type: bool def run_case(self, testcase: DataDrivenTestCase) -> None: incremental = ('incremental' in testcase.name.lower() diff --git a/mypy/test/testcmdline.py b/mypy/test/testcmdline.py index f8434ef08b62..8d43ce54916b 100644 --- a/mypy/test/testcmdline.py +++ b/mypy/test/testcmdline.py @@ -10,7 +10,6 @@ import sys from typing import List -import typing from mypy.myunit import AssertionFailure from mypy.test.config import test_temp_dir @@ -30,10 +29,10 @@ class PythonCmdlineSuite(DataSuite): - files = cmdline_files # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] - optional_out = True # type: typing.ClassVar[bool] - native_sep = True # type: typing.ClassVar[bool] + files = cmdline_files # type: List[str] + base_path = test_temp_dir # type: str + optional_out = True # type: bool + native_sep = True # type: bool def run_case(self, testcase: DataDrivenTestCase) -> None: test_python_cmdline(testcase) diff --git a/mypy/test/testdeps.py b/mypy/test/testdeps.py index b05097eea4e6..5894f263e1a8 100644 --- a/mypy/test/testdeps.py +++ b/mypy/test/testdeps.py @@ -2,7 +2,6 @@ import os from typing import List, Tuple, Dict, Optional -import typing from mypy import build, defaults from mypy.build import BuildSource @@ -24,9 +23,9 @@ class GetDependenciesSuite(DataSuite): 'deps-expressions.test', 'deps-statements.test', 'deps-classes.test', - ] # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] - optional_out = True # type: typing.ClassVar[bool] + ] # type: List[str] + base_path = test_temp_dir # type: str + optional_out = True # type: bool def run_case(self, testcase: DataDrivenTestCase) -> None: src = '\n'.join(testcase.input) diff --git a/mypy/test/testdiff.py b/mypy/test/testdiff.py index 7c75ee13bf7b..779807c182e0 100644 --- a/mypy/test/testdiff.py +++ b/mypy/test/testdiff.py @@ -2,7 +2,6 @@ import os from typing import List, Tuple, Dict, Optional -import typing from mypy import build from mypy.build import BuildSource @@ -16,9 +15,9 @@ class ASTDiffSuite(DataSuite): - files = ['diff.test'] # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] - optional_out = True # type: typing.ClassVar[bool] + files = ['diff.test'] # type: List[str] + base_path = test_temp_dir # type: str + optional_out = True # type: bool def run_case(self, testcase: DataDrivenTestCase) -> None: first_src = '\n'.join(testcase.input) diff --git a/mypy/test/testdmypy.py b/mypy/test/testdmypy.py index 0fd375783c09..529b33bce7e5 100644 --- a/mypy/test/testdmypy.py +++ b/mypy/test/testdmypy.py @@ -6,7 +6,6 @@ import sys from typing import Dict, List, Optional, Set, Tuple -import typing from mypy import build from mypy import defaults @@ -34,9 +33,9 @@ class TypeCheckSuite(DataSuite): - files = dmypy_files # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] - optional_out = True # type: typing.ClassVar[bool] + files = dmypy_files # type: List[str] + base_path = test_temp_dir # type: str + optional_out = True # type: bool def run_case(self, testcase: DataDrivenTestCase) -> None: assert has_stable_flags(testcase) diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index 211118d87ebb..ac19de89ad58 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -12,7 +12,6 @@ import shutil from typing import List, Tuple, Dict, Optional, Set -import typing from mypy import build from mypy.build import BuildManager, BuildSource, Graph @@ -37,9 +36,9 @@ class FineGrainedSuite(DataSuite): 'fine-grained-cycles.test', 'fine-grained-blockers.test', 'fine-grained-modules.test', - ] # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] - optional_out = True # type: typing.ClassVar[bool] + ] # type: List[str] + base_path = test_temp_dir # type: str + optional_out = True # type: bool def run_case(self, testcase: DataDrivenTestCase) -> None: main_src = '\n'.join(testcase.input) diff --git a/mypy/test/testmerge.py b/mypy/test/testmerge.py index d5d3b1ba9907..6970409dfd1e 100644 --- a/mypy/test/testmerge.py +++ b/mypy/test/testmerge.py @@ -3,7 +3,6 @@ import os import shutil from typing import List, Tuple, Dict, Optional -import typing from mypy import build from mypy.build import BuildManager, BuildSource, State @@ -35,9 +34,9 @@ class ASTMergeSuite(DataSuite): - files = ['merge.test'] # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] - optional_out = True # type: typing.ClassVar[bool] + files = ['merge.test'] # type: List[str] + base_path = test_temp_dir # type: str + optional_out = True # type: bool def __init__(self, *, update_data: bool) -> None: super().__init__(update_data=update_data) diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py index 633b5fc9d411..38ad25f53ff3 100644 --- a/mypy/test/testparse.py +++ b/mypy/test/testparse.py @@ -1,6 +1,5 @@ """Tests for the mypy parser.""" from typing import List -import typing from mypy import defaults from mypy.myunit import AssertionFailure @@ -13,7 +12,7 @@ class ParserSuite(DataSuite): files = ['parse.test', - 'parse-python2.test'] # type: typing.ClassVar[List[str]] + 'parse-python2.test'] # type: List[str] def run_case(self, testcase: DataDrivenTestCase) -> None: test_parser(testcase) @@ -50,7 +49,7 @@ def test_parser(testcase: DataDrivenTestCase) -> None: class ParseErrorSuite(DataSuite): - files = ['parse-errors.test'] # type: typing.ClassVar[List[str]] + files = ['parse-errors.test'] # type: List[str] def run_case(self, testcase: DataDrivenTestCase) -> None: test_parse_error(testcase) diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index 4eadd1109f97..1e318c0710fd 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -18,7 +18,6 @@ import pytest # type: ignore # no pytest in typeshed from typing import Dict, List, Tuple, Optional -import typing from mypy.test.config import test_temp_dir from mypy.test.data import DataDrivenTestCase, DataSuite @@ -34,9 +33,9 @@ class PythonEvaluationSuite(DataSuite): files = ['pythoneval.test', 'python2eval.test', - 'pythoneval-asyncio.test'] # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] - optional_out = True # type: typing.ClassVar[bool] + 'pythoneval-asyncio.test'] # type: List[str] + base_path = test_temp_dir # type: str + optional_out = True # type: bool def run_case(self, testcase: DataDrivenTestCase) -> None: test_python_evaluation(testcase) diff --git a/mypy/test/testsemanal.py b/mypy/test/testsemanal.py index f4242cc5fb2d..0a3786752230 100644 --- a/mypy/test/testsemanal.py +++ b/mypy/test/testsemanal.py @@ -3,7 +3,6 @@ import os.path from typing import Dict, List -import typing from mypy import build from mypy.build import BuildSource @@ -43,10 +42,10 @@ def get_semanal_options() -> Options: class SemAnalSuite(DataSuite): - files = semanal_files # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] - optional_out = True # type: typing.ClassVar[bool] - native_sep = True # type: typing.ClassVar[bool] + files = semanal_files # type: List[str] + base_path = test_temp_dir # type: str + optional_out = True # type: bool + native_sep = True # type: bool def run_case(self, testcase: DataDrivenTestCase) -> None: test_semanal(testcase) @@ -97,9 +96,9 @@ def test_semanal(testcase: DataDrivenTestCase) -> None: # Semantic analyzer error test cases class SemAnalErrorSuite(DataSuite): - files = ['semanal-errors.test'] # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] - optional_out = True # type: typing.ClassVar[bool] + files = ['semanal-errors.test'] # type: List[str] + base_path = test_temp_dir # type: str + optional_out = True # type: bool def run_case(self, testcase: DataDrivenTestCase) -> None: test_semanal_error(testcase) @@ -127,8 +126,8 @@ def test_semanal_error(testcase: DataDrivenTestCase) -> None: # SymbolNode table export test cases class SemAnalSymtableSuite(DataSuite): - files = ['semanal-symtable.test'] # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] + files = ['semanal-symtable.test'] # type: List[str] + base_path = test_temp_dir # type: str def run_case(self, testcase: DataDrivenTestCase) -> None: """Perform a test case.""" @@ -157,8 +156,8 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: # Type info export test cases class SemAnalTypeInfoSuite(DataSuite): - files = ['semanal-typeinfo.test'] # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] + files = ['semanal-typeinfo.test'] # type: List[str] + base_path = test_temp_dir # type: str def run_case(self, testcase: DataDrivenTestCase) -> None: """Perform a test case.""" diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 3386f6ddb24a..e3c52a029535 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -8,7 +8,6 @@ from types import ModuleType from typing import List, Tuple -import typing from mypy.myunit import Suite, assert_equal from mypy.test.helpers import assert_string_arrays_equal @@ -93,7 +92,7 @@ def test_infer_sig_from_docstring(self) -> None: class StubgenPythonSuite(DataSuite): - files = ['stubgen.test'] # type: typing.ClassVar[List[str]] + files = ['stubgen.test'] # type: List[str] def run_case(self, testcase: DataDrivenTestCase) -> None: test_stubgen(testcase) diff --git a/mypy/test/testtransform.py b/mypy/test/testtransform.py index c884356085ee..0d366fd04e7d 100644 --- a/mypy/test/testtransform.py +++ b/mypy/test/testtransform.py @@ -2,7 +2,6 @@ import os.path from typing import List -import typing from mypy import build from mypy.build import BuildSource @@ -26,9 +25,9 @@ class TransformSuite(DataSuite): 'semanal-modules.test', 'semanal-statements.test', 'semanal-abstractclasses.test', - 'semanal-python2.test'] # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] - native_sep = True # type: typing.ClassVar[bool] + 'semanal-python2.test'] # type: List[str] + base_path = test_temp_dir # type: str + native_sep = True # type: bool def run_case(self, testcase: DataDrivenTestCase) -> None: test_transform(testcase) diff --git a/mypy/test/testtypegen.py b/mypy/test/testtypegen.py index 65dc25ada334..6d5a154b67be 100644 --- a/mypy/test/testtypegen.py +++ b/mypy/test/testtypegen.py @@ -3,7 +3,6 @@ import re from typing import Set, List -import typing from mypy import build from mypy.build import BuildSource @@ -20,8 +19,8 @@ class TypeExportSuite(DataSuite): - files = ['typexport-basic.test'] # type: typing.ClassVar[List[str]] - base_path = test_temp_dir # type: typing.ClassVar[str] + files = ['typexport-basic.test'] # type: List[str] + base_path = test_temp_dir # type: str def run_case(self, testcase: DataDrivenTestCase) -> None: try: From a8c61ba7859d2903610c63b28537eae86ae98fec Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Sat, 2 Dec 2017 20:59:26 +0200 Subject: [PATCH 10/14] add filter method to DataSuite for dmypy --- mypy/test/config.py | 3 --- mypy/test/data.py | 21 ++++++++++++++------- mypy/test/testdmypy.py | 8 ++++++-- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/mypy/test/config.py b/mypy/test/config.py index 744ba3d76aba..5dbe791e593a 100644 --- a/mypy/test/config.py +++ b/mypy/test/config.py @@ -1,8 +1,5 @@ -import os import os.path - - this_file_dir = os.path.dirname(os.path.realpath(__file__)) PREFIX = os.path.dirname(os.path.dirname(this_file_dir)) diff --git a/mypy/test/data.py b/mypy/test/data.py index 2b39256b9586..dc4ad74d7312 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -6,9 +6,10 @@ import re from os import remove, rmdir import shutil +from abc import abstractmethod import pytest # type: ignore # no pytest in typeshed -from typing import Callable, List, Tuple, Set, Optional, Iterator, Any, Dict, NamedTuple, Union +from typing import List, Tuple, Set, Optional, Iterator, Any, Dict, NamedTuple, Union from mypy.myunit import BaseTestCase from mypy.test.config import test_data_prefix, test_temp_dir @@ -570,13 +571,14 @@ def collect(self) -> Iterator[pytest.Item]: # type: ignore """Called by pytest on each of the object returned from pytest_pycollect_makeitem""" # obj is the object for which pytest_pycollect_makeitem returned self. - cls = self.obj # type: DataSuite - for f in cls.files: + suite = self.obj # type: DataSuite + for f in suite.files: for case in parse_test_cases(os.path.join(test_data_prefix, f), - base_path=cls.base_path, - optional_out=cls.optional_out, - native_sep=cls.native_sep): - yield MypyDataCase(case.name, self, case) + base_path=suite.base_path, + optional_out=suite.optional_out, + native_sep=suite.native_sep): + if suite.filter(case): + yield MypyDataCase(case.name, self, case) def is_incremental(testcase: DataDrivenTestCase) -> bool: @@ -641,5 +643,10 @@ class DataSuite: def __init__(self, *, update_data: bool) -> None: self.update_data = update_data + @abstractmethod def run_case(self, testcase: DataDrivenTestCase) -> None: raise NotImplementedError + + @classmethod + def filter(cls, testcase: DataDrivenTestCase) -> bool: + return True diff --git a/mypy/test/testdmypy.py b/mypy/test/testdmypy.py index 529b33bce7e5..6cd1650ae7c9 100644 --- a/mypy/test/testdmypy.py +++ b/mypy/test/testdmypy.py @@ -37,9 +37,13 @@ class TypeCheckSuite(DataSuite): base_path = test_temp_dir # type: str optional_out = True # type: bool + @classmethod + def filter(cls, testcase: DataDrivenTestCase) -> bool: + return has_stable_flags(testcase) and is_incremental(testcase) + def run_case(self, testcase: DataDrivenTestCase) -> None: - assert has_stable_flags(testcase) - assert is_incremental(testcase) + assert has_stable_flags(testcase), "Testcase is not incremental" + assert is_incremental(testcase), "Testcase has varying flags" # All tests run once with a cold cache, then at least once # with a warm cache and maybe changed files. Expected output From 8eb0fede2e2b579a4626a66e333cdbfb7d178579 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Tue, 12 Dec 2017 16:59:23 +0200 Subject: [PATCH 11/14] consistent setup/teardown; multiline docstrings --- mypy/myunit/__init__.py | 14 ++++++++------ mypy/test/data.py | 9 +++++---- mypy/test/testsubtypes.py | 2 +- mypy/test/testtypes.py | 6 +++--- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/mypy/myunit/__init__.py b/mypy/myunit/__init__.py index 79fda5d3b777..8361f44b35b1 100644 --- a/mypy/myunit/__init__.py +++ b/mypy/myunit/__init__.py @@ -107,7 +107,8 @@ def fail() -> None: class BaseTestCase: """Common base class for _MyUnitTestCase and DataDrivenTestCase. - Handles temporary folder creation and deletion""" + Handles temporary folder creation and deletion. + """ def __init__(self, name: str) -> None: self.name = name self.old_cwd = None # type: Optional[str] @@ -141,10 +142,10 @@ def __init__(self, name: str, suite: 'Suite', run: Callable[[], None]) -> None: def setup(self) -> None: super().setup() - self.suite.set_up() + self.suite.setup() def teardown(self) -> None: - self.suite.tear_down() # No-op + self.suite.teardown() # No-op super().teardown() @@ -152,7 +153,8 @@ class Suite: """Abstract class for myunit test suites - node in the tree whose leaves are _MyUnitTestCases. The children `cases` are looked up during __init__, looking for attributes named test_* - they are either no-arg methods or of a pair (name, Suite).""" + they are either no-arg methods or of a pair (name, Suite). + """ cases = None # type: Iterable[Union[_MyUnitTestCase, Tuple[str, Suite]]] @@ -168,11 +170,11 @@ def __init__(self) -> None: assert isinstance(t, MethodType) self.cases.append(_MyUnitTestCase(m, self, t)) - def set_up(self) -> None: + def setup(self) -> None: """Set up fixtures""" pass - def tear_down(self) -> None: + def teardown(self) -> None: # This method is not overridden in practice pass diff --git a/mypy/test/data.py b/mypy/test/data.py index dc4ad74d7312..509de8170947 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -555,13 +555,14 @@ def pytest_pycollect_makeitem(collector: Any, name: str, obj: object) -> 'Optional[Any]': """Called by pytest on each object in modules configured in conftest.py files. - collector is pytest.Collector, returns Optional[pytest.Class]""" - + collector is pytest.Collector, returns Optional[pytest.Class] + """ if isinstance(obj, type): # Only classes derived from DataSuite contain test cases, not the DataSuite class itself if issubclass(obj, DataSuite) and obj is not DataSuite: - # Non-None result means this obj is a test case - # result.collect will be called, with self.obj being obj + # Non-None result means this obj is a test case. + # The collect method of the returned MypyDataSuite instance will be called later, + # with self.obj being obj. return MypyDataSuite(name, parent=collector) return None diff --git a/mypy/test/testsubtypes.py b/mypy/test/testsubtypes.py index 7a19c6c1325b..2e5d960809d7 100644 --- a/mypy/test/testsubtypes.py +++ b/mypy/test/testsubtypes.py @@ -6,7 +6,7 @@ class SubtypingSuite(Suite): - def set_up(self) -> None: + def setup(self) -> None: self.fx = TypeFixture(INVARIANT) self.fx_contra = TypeFixture(CONTRAVARIANT) self.fx_co = TypeFixture(COVARIANT) diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 8b025d66b40c..c8d258fc1168 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -93,7 +93,7 @@ def test_generic_function_type(self) -> None: class TypeOpsSuite(Suite): - def set_up(self) -> None: + def setup(self) -> None: self.fx = TypeFixture(INVARIANT) self.fx_co = TypeFixture(COVARIANT) self.fx_contra = TypeFixture(CONTRAVARIANT) @@ -358,7 +358,7 @@ def callable(self, vars: List[str], *a: Type) -> CallableType: class JoinSuite(Suite): - def set_up(self) -> None: + def setup(self) -> None: self.fx = TypeFixture() def test_trivial_cases(self) -> None: @@ -628,7 +628,7 @@ def type_callable(self, *a: Type) -> CallableType: class MeetSuite(Suite): - def set_up(self) -> None: + def setup(self) -> None: self.fx = TypeFixture() def test_trivial_cases(self) -> None: From c26671bbcfd683632f73d94a5f07bb2fc681af11 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Tue, 12 Dec 2017 17:01:35 +0200 Subject: [PATCH 12/14] fix switched messages --- mypy/test/testdmypy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/test/testdmypy.py b/mypy/test/testdmypy.py index 6cd1650ae7c9..52e06850deed 100644 --- a/mypy/test/testdmypy.py +++ b/mypy/test/testdmypy.py @@ -42,8 +42,8 @@ def filter(cls, testcase: DataDrivenTestCase) -> bool: return has_stable_flags(testcase) and is_incremental(testcase) def run_case(self, testcase: DataDrivenTestCase) -> None: - assert has_stable_flags(testcase), "Testcase is not incremental" - assert is_incremental(testcase), "Testcase has varying flags" + assert has_stable_flags(testcase), "Testcase has varying flags" + assert is_incremental(testcase), "Testcase is not incremental" # All tests run once with a cold cache, then at least once # with a warm cache and maybe changed files. Expected output From 5600859d2c2736364c9ba69062df59b1512a47e7 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Tue, 12 Dec 2017 17:07:45 +0200 Subject: [PATCH 13/14] remove annotations from DataSuite subclasses --- mypy/test/testcheck.py | 6 +++--- mypy/test/testcmdline.py | 8 ++++---- mypy/test/testdeps.py | 6 +++--- mypy/test/testdiff.py | 6 +++--- mypy/test/testdmypy.py | 6 +++--- mypy/test/testfinegrained.py | 6 +++--- mypy/test/testmerge.py | 6 +++--- mypy/test/testparse.py | 4 ++-- mypy/test/testpythoneval.py | 6 +++--- mypy/test/testsemanal.py | 22 +++++++++++----------- mypy/test/testtransform.py | 6 +++--- mypy/test/testtypegen.py | 4 ++-- 12 files changed, 43 insertions(+), 43 deletions(-) diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index d5f99f2dba77..b33dfba6405f 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -80,9 +80,9 @@ class TypeCheckSuite(DataSuite): - files = typecheck_files # type: List[str] - base_path = test_temp_dir # type: str - optional_out = True # type: bool + files = typecheck_files + base_path = test_temp_dir + optional_out = True def run_case(self, testcase: DataDrivenTestCase) -> None: incremental = ('incremental' in testcase.name.lower() diff --git a/mypy/test/testcmdline.py b/mypy/test/testcmdline.py index 8d43ce54916b..afdee5583968 100644 --- a/mypy/test/testcmdline.py +++ b/mypy/test/testcmdline.py @@ -29,10 +29,10 @@ class PythonCmdlineSuite(DataSuite): - files = cmdline_files # type: List[str] - base_path = test_temp_dir # type: str - optional_out = True # type: bool - native_sep = True # type: bool + files = cmdline_files + base_path = test_temp_dir + optional_out = True + native_sep = True def run_case(self, testcase: DataDrivenTestCase) -> None: test_python_cmdline(testcase) diff --git a/mypy/test/testdeps.py b/mypy/test/testdeps.py index 5894f263e1a8..98ff104962eb 100644 --- a/mypy/test/testdeps.py +++ b/mypy/test/testdeps.py @@ -23,9 +23,9 @@ class GetDependenciesSuite(DataSuite): 'deps-expressions.test', 'deps-statements.test', 'deps-classes.test', - ] # type: List[str] - base_path = test_temp_dir # type: str - optional_out = True # type: bool + ] + base_path = test_temp_dir + optional_out = True def run_case(self, testcase: DataDrivenTestCase) -> None: src = '\n'.join(testcase.input) diff --git a/mypy/test/testdiff.py b/mypy/test/testdiff.py index 779807c182e0..b1cfc65a4a29 100644 --- a/mypy/test/testdiff.py +++ b/mypy/test/testdiff.py @@ -15,9 +15,9 @@ class ASTDiffSuite(DataSuite): - files = ['diff.test'] # type: List[str] - base_path = test_temp_dir # type: str - optional_out = True # type: bool + files = ['diff.test'] + base_path = test_temp_dir + optional_out = True def run_case(self, testcase: DataDrivenTestCase) -> None: first_src = '\n'.join(testcase.input) diff --git a/mypy/test/testdmypy.py b/mypy/test/testdmypy.py index 52e06850deed..82de0940f242 100644 --- a/mypy/test/testdmypy.py +++ b/mypy/test/testdmypy.py @@ -33,9 +33,9 @@ class TypeCheckSuite(DataSuite): - files = dmypy_files # type: List[str] - base_path = test_temp_dir # type: str - optional_out = True # type: bool + files = dmypy_files + base_path = test_temp_dir + optional_out = True @classmethod def filter(cls, testcase: DataDrivenTestCase) -> bool: diff --git a/mypy/test/testfinegrained.py b/mypy/test/testfinegrained.py index ac19de89ad58..b4379b25f63f 100644 --- a/mypy/test/testfinegrained.py +++ b/mypy/test/testfinegrained.py @@ -36,9 +36,9 @@ class FineGrainedSuite(DataSuite): 'fine-grained-cycles.test', 'fine-grained-blockers.test', 'fine-grained-modules.test', - ] # type: List[str] - base_path = test_temp_dir # type: str - optional_out = True # type: bool + ] + base_path = test_temp_dir + optional_out = True def run_case(self, testcase: DataDrivenTestCase) -> None: main_src = '\n'.join(testcase.input) diff --git a/mypy/test/testmerge.py b/mypy/test/testmerge.py index 6970409dfd1e..07a26d9b9c99 100644 --- a/mypy/test/testmerge.py +++ b/mypy/test/testmerge.py @@ -34,9 +34,9 @@ class ASTMergeSuite(DataSuite): - files = ['merge.test'] # type: List[str] - base_path = test_temp_dir # type: str - optional_out = True # type: bool + files = ['merge.test'] + base_path = test_temp_dir + optional_out = True def __init__(self, *, update_data: bool) -> None: super().__init__(update_data=update_data) diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py index 38ad25f53ff3..5646ed57c6c9 100644 --- a/mypy/test/testparse.py +++ b/mypy/test/testparse.py @@ -12,7 +12,7 @@ class ParserSuite(DataSuite): files = ['parse.test', - 'parse-python2.test'] # type: List[str] + 'parse-python2.test'] def run_case(self, testcase: DataDrivenTestCase) -> None: test_parser(testcase) @@ -49,7 +49,7 @@ def test_parser(testcase: DataDrivenTestCase) -> None: class ParseErrorSuite(DataSuite): - files = ['parse-errors.test'] # type: List[str] + files = ['parse-errors.test'] def run_case(self, testcase: DataDrivenTestCase) -> None: test_parse_error(testcase) diff --git a/mypy/test/testpythoneval.py b/mypy/test/testpythoneval.py index 1e318c0710fd..222fa6ff32c2 100644 --- a/mypy/test/testpythoneval.py +++ b/mypy/test/testpythoneval.py @@ -33,9 +33,9 @@ class PythonEvaluationSuite(DataSuite): files = ['pythoneval.test', 'python2eval.test', - 'pythoneval-asyncio.test'] # type: List[str] - base_path = test_temp_dir # type: str - optional_out = True # type: bool + 'pythoneval-asyncio.test'] + base_path = test_temp_dir + optional_out = True def run_case(self, testcase: DataDrivenTestCase) -> None: test_python_evaluation(testcase) diff --git a/mypy/test/testsemanal.py b/mypy/test/testsemanal.py index 0a3786752230..98f3ef64b26c 100644 --- a/mypy/test/testsemanal.py +++ b/mypy/test/testsemanal.py @@ -42,10 +42,10 @@ def get_semanal_options() -> Options: class SemAnalSuite(DataSuite): - files = semanal_files # type: List[str] - base_path = test_temp_dir # type: str - optional_out = True # type: bool - native_sep = True # type: bool + files = semanal_files + base_path = test_temp_dir + optional_out = True + native_sep = True def run_case(self, testcase: DataDrivenTestCase) -> None: test_semanal(testcase) @@ -96,9 +96,9 @@ def test_semanal(testcase: DataDrivenTestCase) -> None: # Semantic analyzer error test cases class SemAnalErrorSuite(DataSuite): - files = ['semanal-errors.test'] # type: List[str] - base_path = test_temp_dir # type: str - optional_out = True # type: bool + files = ['semanal-errors.test'] + base_path = test_temp_dir + optional_out = True def run_case(self, testcase: DataDrivenTestCase) -> None: test_semanal_error(testcase) @@ -126,8 +126,8 @@ def test_semanal_error(testcase: DataDrivenTestCase) -> None: # SymbolNode table export test cases class SemAnalSymtableSuite(DataSuite): - files = ['semanal-symtable.test'] # type: List[str] - base_path = test_temp_dir # type: str + files = ['semanal-symtable.test'] + base_path = test_temp_dir def run_case(self, testcase: DataDrivenTestCase) -> None: """Perform a test case.""" @@ -156,8 +156,8 @@ def run_case(self, testcase: DataDrivenTestCase) -> None: # Type info export test cases class SemAnalTypeInfoSuite(DataSuite): - files = ['semanal-typeinfo.test'] # type: List[str] - base_path = test_temp_dir # type: str + files = ['semanal-typeinfo.test'] + base_path = test_temp_dir def run_case(self, testcase: DataDrivenTestCase) -> None: """Perform a test case.""" diff --git a/mypy/test/testtransform.py b/mypy/test/testtransform.py index 0d366fd04e7d..5f693b5d239a 100644 --- a/mypy/test/testtransform.py +++ b/mypy/test/testtransform.py @@ -25,9 +25,9 @@ class TransformSuite(DataSuite): 'semanal-modules.test', 'semanal-statements.test', 'semanal-abstractclasses.test', - 'semanal-python2.test'] # type: List[str] - base_path = test_temp_dir # type: str - native_sep = True # type: bool + 'semanal-python2.test'] + base_path = test_temp_dir + native_sep = True def run_case(self, testcase: DataDrivenTestCase) -> None: test_transform(testcase) diff --git a/mypy/test/testtypegen.py b/mypy/test/testtypegen.py index 6d5a154b67be..f953b6eaf13b 100644 --- a/mypy/test/testtypegen.py +++ b/mypy/test/testtypegen.py @@ -19,8 +19,8 @@ class TypeExportSuite(DataSuite): - files = ['typexport-basic.test'] # type: List[str] - base_path = test_temp_dir # type: str + files = ['typexport-basic.test'] + base_path = test_temp_dir def run_case(self, testcase: DataDrivenTestCase) -> None: try: From a1d0c183e338253e6c752f0720d268368d214a2f Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Tue, 12 Dec 2017 17:10:52 +0200 Subject: [PATCH 14/14] remove missed annotation --- mypy/test/teststubgen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index e3c52a029535..e292fdd1690e 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -92,7 +92,7 @@ def test_infer_sig_from_docstring(self) -> None: class StubgenPythonSuite(DataSuite): - files = ['stubgen.test'] # type: List[str] + files = ['stubgen.test'] def run_case(self, testcase: DataDrivenTestCase) -> None: test_stubgen(testcase)