From 2ece9a04f1684e4073d02650fa6ff9fd81529b89 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Fri, 15 Dec 2017 09:49:44 +0200 Subject: [PATCH 1/8] remove myunit --- ROADMAP.md | 3 - mypy/myunit/__init__.py | 394 ------------------------------------ mypy/myunit/__main__.py | 9 - mypy/options.py | 2 +- mypy/test/data.py | 37 +++- mypy/test/helpers.py | 65 +++++- mypy/test/testargs.py | 8 +- mypy/test/testcheck.py | 3 +- mypy/test/testcmdline.py | 2 +- mypy/test/testdmypy.py | 5 +- mypy/test/testgraph.py | 3 +- mypy/test/testinfer.py | 2 +- mypy/test/testmerge.py | 4 +- mypy/test/testmoduleinfo.py | 4 +- mypy/test/testparse.py | 4 +- mypy/test/testreports.py | 2 +- mypy/test/testsolve.py | 5 +- mypy/test/teststubgen.py | 3 +- mypy/test/testsubtypes.py | 8 +- mypy/test/testtypes.py | 27 +-- pytest.ini | 2 +- runtests.py | 34 +--- setup.py | 2 +- 23 files changed, 137 insertions(+), 491 deletions(-) delete mode 100644 mypy/myunit/__init__.py delete mode 100644 mypy/myunit/__main__.py diff --git a/ROADMAP.md b/ROADMAP.md index c68ca2e553e8..1d5c30257c5c 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -19,9 +19,6 @@ more accurate. - Continue making error messages more useful and informative. ([issue](https://github.com/python/mypy/labels/topic-usability)) -- Switch completely to pytest and remove the custom testing framework. - ([issue](https://github.com/python/mypy/issues/1673)) - - Make it possible to run mypy as a daemon to avoid reprocessing the entire program on each run. This will improve performance significantly. Even when using the incremental mode, processing a diff --git a/mypy/myunit/__init__.py b/mypy/myunit/__init__.py deleted file mode 100644 index 8361f44b35b1..000000000000 --- a/mypy/myunit/__init__.py +++ /dev/null @@ -1,394 +0,0 @@ -import importlib -import os -import sys -import tempfile -import time -import traceback - -from typing import List, Tuple, Any, Callable, Union, cast, Optional, Iterable -from types import TracebackType, MethodType - - -# TODO remove global state -is_verbose = False -is_quiet = False -patterns = [] # type: List[str] -times = [] # type: List[Tuple[float, str]] - - -class AssertionFailure(Exception): - """Exception used to signal failed test cases.""" - def __init__(self, s: Optional[str] = None) -> None: - if s: - super().__init__(s) - else: - super().__init__() - - -class SkipTestCaseException(Exception): - """Exception used to signal skipped test cases.""" - pass - - -def assert_true(b: bool, msg: Optional[str] = None) -> None: - if not b: - raise AssertionFailure(msg) - - -def assert_false(b: bool, msg: Optional[str] = None) -> None: - if b: - raise AssertionFailure(msg) - - -def good_repr(obj: object) -> str: - if isinstance(obj, str): - if obj.count('\n') > 1: - bits = ["'''\\"] - for line in obj.split('\n'): - # force repr to use ' not ", then cut it off - bits.append(repr('"' + line)[2:-1]) - bits[-1] += "'''" - return '\n'.join(bits) - return repr(obj) - - -def assert_equal(a: object, b: object, fmt: str = '{} != {}') -> None: - if a != b: - raise AssertionFailure(fmt.format(good_repr(a), good_repr(b))) - - -def assert_not_equal(a: object, b: object, fmt: str = '{} == {}') -> None: - if a == b: - raise AssertionFailure(fmt.format(good_repr(a), good_repr(b))) - - -def assert_raises(typ: type, *rest: Any) -> None: - """Usage: assert_raises(exception class[, message], function[, args]) - - Call function with the given arguments and expect an exception of the given - type. - - TODO use overloads for better type checking - """ - # Parse arguments. - msg = None # type: Optional[str] - if isinstance(rest[0], str) or rest[0] is None: - msg = rest[0] - rest = rest[1:] - f = rest[0] - args = [] # type: List[Any] - if len(rest) > 1: - args = rest[1] - assert len(rest) <= 2 - - # Perform call and verify the exception. - try: - f(*args) - except BaseException as e: - if isinstance(e, KeyboardInterrupt): - raise - assert_type(typ, e) - if msg: - assert_equal(e.args[0], msg, 'Invalid message {}, expected {}') - else: - raise AssertionFailure('No exception raised') - - -def assert_type(typ: type, value: object) -> None: - if type(value) != typ: - raise AssertionFailure('Invalid type {}, expected {}'.format( - typename(type(value)), typename(typ))) - - -def fail() -> None: - raise AssertionFailure() - - -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] - self.tmpdir = None # type: Optional[tempfile.TemporaryDirectory[str]] - - 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 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) - try: - self.tmpdir.cleanup() - except OSError: - pass - self.old_cwd = None - self.tmpdir = 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.run = run - self.suite = suite - - def setup(self) -> None: - super().setup() - self.suite.setup() - - def teardown(self) -> None: - self.suite.teardown() # No-op - super().teardown() - - -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). - """ - - cases = None # type: Iterable[Union[_MyUnitTestCase, Tuple[str, Suite]]] - - def __init__(self) -> None: - self.prefix = typename(type(self)) + '.' - self.cases = [] - for m in dir(self): - if m.startswith('test_'): - t = getattr(self, m) - if isinstance(t, Suite): - self.cases.append((m + '.', t)) - else: - assert isinstance(t, MethodType) - self.cases.append(_MyUnitTestCase(m, self, t)) - - def setup(self) -> None: - """Set up fixtures""" - pass - - def teardown(self) -> None: - # This method is not overridden in practice - pass - - def skip(self) -> None: - raise SkipTestCaseException() - - -def add_suites_from_module(suites: List[Suite], mod_name: str) -> None: - mod = importlib.import_module(mod_name) - got_suite = False - for suite in mod.__dict__.values(): - if isinstance(suite, type) and issubclass(suite, Suite) and suite is not Suite: - got_suite = True - suites.append(cast(Callable[[], Suite], suite)()) - if not got_suite: - # Sanity check in case e.g. it uses unittest instead of a myunit. - # The codecs tests do since they need to be python2-compatible. - sys.exit('Test module %s had no test!' % mod_name) - - -class ListSuite(Suite): - def __init__(self, suites: List[Suite]) -> None: - for suite in suites: - mod_name = type(suite).__module__.replace('.', '_') - mod_name = mod_name.replace('mypy_', '') - mod_name = mod_name.replace('test_', '') - mod_name = mod_name.strip('_').replace('__', '_') - type_name = type(suite).__name__ - name = 'test_%s_%s' % (mod_name, type_name) - setattr(self, name, suite) - super().__init__() - - -def main(args: Optional[List[str]] = None) -> None: - global patterns, is_verbose, is_quiet - if not args: - args = sys.argv[1:] - is_verbose = False - is_quiet = False - suites = [] # type: List[Suite] - patterns = [] - i = 0 - while i < len(args): - a = args[i] - if a == '-v': - is_verbose = True - elif a == '-q': - is_quiet = True - elif a == '-m': - i += 1 - if i == len(args): - sys.exit('-m requires an argument') - add_suites_from_module(suites, args[i]) - elif not a.startswith('-'): - patterns.append(a) - else: - sys.exit('Usage: python -m mypy.myunit [-v] [-q]' - + ' -m mypy.test.module [-m mypy.test.module ...] [filter ...]') - i += 1 - if len(patterns) == 0: - patterns.append('*') - if not suites: - sys.exit('At least one -m argument is required') - - t = ListSuite(suites) - num_total, num_fail, num_skip = run_test_recursive(t, 0, 0, 0, '', 0) - - skip_msg = '' - if num_skip > 0: - skip_msg = ', {} skipped'.format(num_skip) - - if num_fail == 0: - if not is_quiet: - print('%d test cases run%s, all passed.' % (num_total, skip_msg)) - print('*** OK ***') - else: - sys.stderr.write('%d/%d test cases failed%s.\n' % (num_fail, - num_total, - skip_msg)) - sys.stderr.write('*** FAILURE ***\n') - sys.exit(1) - - -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 _MyUnitTestCase, Suite or (str, Suite).""" - if isinstance(test, _MyUnitTestCase): - name = prefix + test.name - for pattern in patterns: - if match_pattern(name, pattern): - match = True - break - else: - match = False - if match: - is_fail, is_skip = run_single_test(name, test) - if is_fail: num_fail += 1 - if is_skip: num_skip += 1 - num_total += 1 - else: - suite_prefix = '' - if isinstance(test, list) or isinstance(test, tuple): - suite = test[1] # type: Suite - suite_prefix = test[0] - else: - suite = test - suite_prefix = test.prefix - - for stest in suite.cases: - new_prefix = prefix - if depth > 0: - new_prefix = prefix + suite_prefix - num_total, num_fail, num_skip = run_test_recursive( - stest, num_total, num_fail, num_skip, new_prefix, depth + 1) - return num_total, num_fail, num_skip - - -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.setup() # FIX: check exceptions - exc_traceback = None # type: Optional[TracebackType] - try: - test.run() - except BaseException as e: - if isinstance(e, KeyboardInterrupt): - raise - exc_type, exc_value, exc_traceback = sys.exc_info() - finally: - test.teardown() - times.append((time.time() - time0, name)) - - if exc_traceback: - if isinstance(exc_value, SkipTestCaseException): - if is_verbose: - sys.stderr.write(' (skipped)\n') - return False, True - else: - assert exc_type is not None and exc_value is not None - handle_failure(name, exc_type, exc_value, exc_traceback) - return True, False - elif is_verbose: - sys.stderr.write('\n') - - return False, False - - -def handle_failure(name: str, - exc_type: type, - exc_value: BaseException, - exc_traceback: TracebackType, - ) -> None: - # Report failed test case. - if is_verbose: - sys.stderr.write('\n\n') - msg = '' - if exc_value.args and exc_value.args[0]: - msg = ': ' + str(exc_value) - else: - msg = '' - if not isinstance(exc_value, SystemExit): - # We assume that before doing exit() (which raises SystemExit) we've printed - # enough context about what happened so that a stack trace is not useful. - # In particular, uncaught exceptions during semantic analysis or type checking - # call exit() and they already print out a stack trace. - sys.stderr.write('Traceback (most recent call last):\n') - tb = traceback.format_tb(exc_traceback) - tb = clean_traceback(tb) - for s in tb: - sys.stderr.write(s) - else: - sys.stderr.write('\n') - exception = typename(exc_type) - sys.stderr.write('{}{}\n\n'.format(exception, msg)) - sys.stderr.write('{} failed\n\n'.format(name)) - - -def typename(t: type) -> str: - if '.' in str(t): - return str(t).split('.')[-1].rstrip("'>") - else: - return str(t)[8:-2] - - -def match_pattern(s: str, p: str) -> bool: - if len(p) == 0: - return len(s) == 0 - elif p[0] == '*': - if len(p) == 1: - return True - else: - for i in range(len(s) + 1): - if match_pattern(s[i:], p[1:]): - return True - return False - elif len(s) == 0: - return False - else: - return s[0] == p[0] and match_pattern(s[1:], p[1:]) - - -def clean_traceback(tb: List[str]) -> List[str]: - # Remove clutter from the traceback. - start = 0 - for i, s in enumerate(tb): - if '\n test.run()\n' in s or '\n self.func()\n' in s: - start = i + 1 - tb = tb[start:] - for f in ['assert_equal', 'assert_not_equal', 'assert_type', - 'assert_raises', 'assert_true']: - if tb != [] and ', in {}\n'.format(f) in tb[-1]: - tb = tb[:-1] - return tb diff --git a/mypy/myunit/__main__.py b/mypy/myunit/__main__.py deleted file mode 100644 index 34098d40648d..000000000000 --- a/mypy/myunit/__main__.py +++ /dev/null @@ -1,9 +0,0 @@ -# This is a separate module from mypy.myunit so it doesn't exist twice. -"""Myunit test runner command line tool. - -Usually used as a slave by runtests.py, but can be used directly. -""" - -from mypy.myunit import main - -main() diff --git a/mypy/options.py b/mypy/options.py index dd9bfe08c095..0487279811f7 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -129,7 +129,7 @@ def __init__(self) -> None: self.scripts_are_modules = False # Config file name - self.config_file = None # type: Optional[str] + self.config_file = 'setup.cfg' # type: Optional[str] # Write junit.xml to given file self.junit_xml = None # type: Optional[str] diff --git a/mypy/test/data.py b/mypy/test/data.py index 0be3be2e3fc9..26a22a1fdfeb 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -2,6 +2,7 @@ import os.path import os +import tempfile import posixpath import re from os import remove, rmdir @@ -11,7 +12,6 @@ import pytest # type: ignore # no pytest in typeshed 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 root_dir = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..')) @@ -192,7 +192,7 @@ def parse_test_cases( return out -class DataDrivenTestCase(BaseTestCase): +class DataDrivenTestCase: """Holds parsed data and handles directory setup and teardown for MypyDataCase.""" # TODO: rename to ParsedTestCase or merge with MypyDataCase (yet avoid multiple inheritance) @@ -229,7 +229,9 @@ def __init__(self, native_sep: bool = False, triggered: Optional[List[str]] = None, ) -> None: - super().__init__(name) + self.name = name + self.old_cwd = None # type: Optional[str] + self.tmpdir = None # type: Optional[tempfile.TemporaryDirectory[str]] self.input = input self.output = output self.output2 = output2 @@ -245,7 +247,10 @@ def __init__(self, self.triggered = triggered or [] def setup(self) -> None: - super().setup() + self.old_cwd = os.getcwd() + self.tmpdir = tempfile.TemporaryDirectory(prefix='mypy-test-') + os.chdir(self.tmpdir.name) + os.mkdir('tmp') encountered_files = set() self.clean_up = [] for paths in self.deleted_paths.values(): @@ -321,7 +326,15 @@ def teardown(self) -> None: if path.startswith(test_temp_dir + '/') and os.path.isdir(path): shutil.rmtree(path) raise - super().teardown() + assert self.old_cwd is not None and self.tmpdir is not None, \ + "test was not properly set up" + os.chdir(self.old_cwd) + try: + self.tmpdir.cleanup() + except OSError: + pass + self.old_cwd = None + self.tmpdir = None def find_steps(self) -> List[List[FileOperation]]: """Return a list of descriptions of file operations for each incremental step. @@ -614,8 +627,10 @@ def __init__(self, name: str, parent: MypyDataSuite, case: DataDrivenTestCase) - 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.case) + suite = self.parent.obj() + suite.update_data = self.config.getoption('--update-data', False) + suite.setup() + suite.run_case(self.case) def setup(self) -> None: self.case.setup() @@ -647,8 +662,12 @@ class DataSuite: optional_out = False # type: bool native_sep = False # type: bool - def __init__(self, *, update_data: bool) -> None: - self.update_data = update_data + # Assigned from MypyDataCase.runtest + update_data = False # type: bool + + def setup(self) -> None: + """Setup fixtures (ad-hoc)""" + pass @abstractmethod def run_case(self, testcase: DataDrivenTestCase) -> None: diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 8bd3a615694d..656555bc3ac5 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -3,12 +3,14 @@ import sys import time -from typing import List, Dict, Tuple, Callable, Any +from typing import List, Dict, Tuple, Callable, Any, Optional from mypy import defaults -from mypy.myunit import AssertionFailure +import pytest # type: ignore # no pytest in typeshed +from unittest import TestCase as Suite from mypy.test.data import DataDrivenTestCase +skip = pytest.mark.skip # AssertStringArraysEqual displays special line alignment helper messages if # the first different line has at least this many characters, @@ -308,3 +310,62 @@ def retry_on_error(func: Callable[[], Any], max_wait: float = 1.0) -> None: # Done enough waiting, the error seems persistent. raise time.sleep(wait_time) + + +class AssertionFailure(Exception): + """Exception used to signal failed test cases.""" + def __init__(self, s: Optional[str] = None) -> None: + if s: + super().__init__(s) + else: + super().__init__() + + +class SkipTestCaseException(Exception): + """Exception used to signal skipped test cases.""" + pass + + +def assert_true(b: bool, msg: Optional[str] = None) -> None: + if not b: + raise AssertionFailure(msg) + + +def assert_false(b: bool, msg: Optional[str] = None) -> None: + if b: + raise AssertionFailure(msg) + + +def good_repr(obj: object) -> str: + if isinstance(obj, str): + if obj.count('\n') > 1: + bits = ["'''\\"] + for line in obj.split('\n'): + # force repr to use ' not ", then cut it off + bits.append(repr('"' + line)[2:-1]) + bits[-1] += "'''" + return '\n'.join(bits) + return repr(obj) + + +def assert_equal(a: object, b: object, fmt: str = '{} != {}') -> None: + if a != b: + raise AssertionFailure(fmt.format(good_repr(a), good_repr(b))) + + +def assert_not_equal(a: object, b: object, fmt: str = '{} == {}') -> None: + if a == b: + raise AssertionFailure(fmt.format(good_repr(a), good_repr(b))) + + +def typename(t: type) -> str: + if '.' in str(t): + return str(t).split('.')[-1].rstrip("'>") + else: + return str(t)[8:-2] + + +def assert_type(typ: type, value: object) -> None: + if type(value) != typ: + raise AssertionFailure('Invalid type {}, expected {}'.format( + typename(type(value)), typename(typ))) diff --git a/mypy/test/testargs.py b/mypy/test/testargs.py index edee17b90ffb..ea6ca7904046 100644 --- a/mypy/test/testargs.py +++ b/mypy/test/testargs.py @@ -5,8 +5,8 @@ object it creates. """ -from mypy.myunit import Suite, assert_equal -from mypy.options import Options, BuildType +from mypy.test.helpers import Suite +from mypy.options import Options from mypy.main import process_options @@ -14,4 +14,6 @@ class ArgSuite(Suite): def test_coherence(self) -> None: options = Options() _, parsed_options = process_options([], require_targets=False) - assert_equal(options, parsed_options) + assert options.__dict__.keys() == parsed_options.__dict__.keys() + for k in options.__dict__: + assert getattr(options, k) == getattr(parsed_options, k), k diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index b33dfba6405f..c1e7c5759ea5 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -9,11 +9,10 @@ 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 from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.test.helpers import ( - assert_string_arrays_equal, normalize_error_messages, + assert_string_arrays_equal, normalize_error_messages, AssertionFailure, retry_on_error, testcase_pyversion, update_testcase_output, ) from mypy.errors import CompileError diff --git a/mypy/test/testcmdline.py b/mypy/test/testcmdline.py index afdee5583968..177c9cd35641 100644 --- a/mypy/test/testcmdline.py +++ b/mypy/test/testcmdline.py @@ -11,7 +11,7 @@ from typing import List -from mypy.myunit import AssertionFailure +from mypy.test.helpers import AssertionFailure from mypy.test.config import test_temp_dir from mypy.test.data import fix_cobertura_filename from mypy.test.data import DataDrivenTestCase, DataSuite diff --git a/mypy/test/testdmypy.py b/mypy/test/testdmypy.py index 4be4ed8259d5..bdae12156a66 100644 --- a/mypy/test/testdmypy.py +++ b/mypy/test/testdmypy.py @@ -10,11 +10,10 @@ 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 from mypy.test.data import DataDrivenTestCase, DataSuite, has_stable_flags, is_incremental from mypy.test.helpers import ( - assert_string_arrays_equal, normalize_error_messages, + assert_string_arrays_equal, normalize_error_messages, AssertionFailure, retry_on_error, testcase_pyversion, update_testcase_output, ) from mypy.options import Options @@ -33,7 +32,7 @@ dmypy_files = [] -class TypeCheckSuite(DataSuite): +class DmypySuite(DataSuite): files = dmypy_files base_path = test_temp_dir optional_out = True diff --git a/mypy/test/testgraph.py b/mypy/test/testgraph.py index dbbe4872aa75..dc7828932235 100644 --- a/mypy/test/testgraph.py +++ b/mypy/test/testgraph.py @@ -2,14 +2,13 @@ from typing import AbstractSet, Dict, Set, List -from mypy.myunit import Suite, assert_equal +from mypy.test.helpers import assert_equal, Suite from mypy.build import BuildManager, State, BuildSourceSet from mypy.build import topsort, strongly_connected_components, sorted_components, order_ascc from mypy.version import __version__ from mypy.options import Options from mypy.report import Reports from mypy.plugin import Plugin -from mypy import defaults from mypy.errors import Errors diff --git a/mypy/test/testinfer.py b/mypy/test/testinfer.py index 39ab474667f0..8fc6bf2cfe60 100644 --- a/mypy/test/testinfer.py +++ b/mypy/test/testinfer.py @@ -2,7 +2,7 @@ from typing import List, Optional, Tuple, Union -from mypy.myunit import Suite, assert_equal, assert_true +from mypy.test.helpers import Suite, assert_equal from mypy.checkexpr import map_actuals_to_formals from mypy.nodes import ARG_POS, ARG_OPT, ARG_STAR, ARG_STAR2, ARG_NAMED from mypy.types import AnyType, TupleType, Type, TypeOfAny diff --git a/mypy/test/testmerge.py b/mypy/test/testmerge.py index e0e9850e29a9..8719d5f84033 100644 --- a/mypy/test/testmerge.py +++ b/mypy/test/testmerge.py @@ -38,8 +38,8 @@ class ASTMergeSuite(DataSuite): base_path = test_temp_dir optional_out = True - def __init__(self, *, update_data: bool) -> None: - super().__init__(update_data=update_data) + def setup(self) -> None: + super().setup() self.str_conv = StrConv(show_ids=True) assert self.str_conv.id_mapper is not None self.id_mapper = self.str_conv.id_mapper # type: IdMapper diff --git a/mypy/test/testmoduleinfo.py b/mypy/test/testmoduleinfo.py index 581847914c3c..329eccc285ed 100644 --- a/mypy/test/testmoduleinfo.py +++ b/mypy/test/testmoduleinfo.py @@ -1,7 +1,5 @@ from mypy import moduleinfo -from mypy.myunit import ( - Suite, assert_equal, assert_true, assert_false -) +from mypy.test.helpers import assert_true, assert_false, Suite class ModuleInfoSuite(Suite): diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py index 17546d87e682..1f204f6cf220 100644 --- a/mypy/test/testparse.py +++ b/mypy/test/testparse.py @@ -1,9 +1,7 @@ """Tests for the mypy parser.""" -from typing import List from mypy import defaults -from mypy.myunit import AssertionFailure -from mypy.test.helpers import assert_string_arrays_equal +from mypy.test.helpers import assert_string_arrays_equal, AssertionFailure from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.parse import parse from mypy.errors import CompileError diff --git a/mypy/test/testreports.py b/mypy/test/testreports.py index 285e8330655d..84ac3e005bec 100644 --- a/mypy/test/testreports.py +++ b/mypy/test/testreports.py @@ -1,7 +1,7 @@ """Test cases for reports generated by mypy.""" import textwrap -from mypy.myunit import Suite, assert_equal +from mypy.test.helpers import Suite, assert_equal from mypy.report import CoberturaPackage, get_line_rate import lxml.etree as etree # type: ignore diff --git a/mypy/test/testsolve.py b/mypy/test/testsolve.py index 5d68f6cb61e5..172e4e4743c4 100644 --- a/mypy/test/testsolve.py +++ b/mypy/test/testsolve.py @@ -2,7 +2,7 @@ from typing import List, Union, Tuple, Optional -from mypy.myunit import Suite, assert_equal +from mypy.test.helpers import Suite, assert_equal from mypy.constraints import SUPERTYPE_OF, SUBTYPE_OF, Constraint from mypy.solve import solve_constraints from mypy.test.typefixture import TypeFixture @@ -10,8 +10,7 @@ class SolveSuite(Suite): - def __init__(self) -> None: - super().__init__() + def setUp(self) -> None: self.fx = TypeFixture() def test_empty_input(self) -> None: diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index e292fdd1690e..7150e44078d1 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -9,8 +9,7 @@ from typing import List, Tuple -from mypy.myunit import Suite, assert_equal -from mypy.test.helpers import assert_string_arrays_equal +from mypy.test.helpers import Suite, assert_equal, assert_string_arrays_equal 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 diff --git a/mypy/test/testsubtypes.py b/mypy/test/testsubtypes.py index 2e5d960809d7..fe3f3a973856 100644 --- a/mypy/test/testsubtypes.py +++ b/mypy/test/testsubtypes.py @@ -1,4 +1,4 @@ -from mypy.myunit import Suite, assert_true +from mypy.test.helpers import Suite, assert_true, skip from mypy.nodes import CONTRAVARIANT, INVARIANT, COVARIANT from mypy.subtypes import is_subtype from mypy.test.typefixture import TypeFixture, InterfaceTypeFixture @@ -6,7 +6,7 @@ class SubtypingSuite(Suite): - def setup(self) -> None: + def setUp(self) -> None: self.fx = TypeFixture(INVARIANT) self.fx_contra = TypeFixture(CONTRAVARIANT) self.fx_co = TypeFixture(COVARIANT) @@ -67,10 +67,8 @@ def test_interface_subtyping(self) -> None: self.assert_equivalent(self.fx.f, self.fx.f) self.assert_not_subtype(self.fx.a, self.fx.f) + @skip def test_generic_interface_subtyping(self) -> None: - # TODO make this work - self.skip() - fx2 = InterfaceTypeFixture() self.assert_subtype(fx2.m1, fx2.gfa) diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index c8d258fc1168..2e8d2c153d7a 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -2,9 +2,7 @@ from typing import List, Tuple -from mypy.myunit import ( - Suite, assert_equal, assert_true, assert_false, assert_type -) +from mypy.test.helpers import Suite, assert_equal, assert_true, assert_false, assert_type, skip from mypy.erasetype import erase_type from mypy.expandtype import expand_type from mypy.join import join_types, join_simple @@ -19,8 +17,7 @@ class TypesSuite(Suite): - def __init__(self) -> None: - super().__init__() + def setUp(self) -> None: self.x = UnboundType('X') # Helpers self.y = UnboundType('Y') self.fx = TypeFixture() @@ -93,7 +90,7 @@ def test_generic_function_type(self) -> None: class TypeOpsSuite(Suite): - def setup(self) -> None: + def setUp(self) -> None: self.fx = TypeFixture(INVARIANT) self.fx_co = TypeFixture(COVARIANT) self.fx_contra = TypeFixture(CONTRAVARIANT) @@ -358,7 +355,7 @@ def callable(self, vars: List[str], *a: Type) -> CallableType: class JoinSuite(Suite): - def setup(self) -> None: + def setUp(self) -> None: self.fx = TypeFixture() def test_trivial_cases(self) -> None: @@ -529,31 +526,29 @@ def ov(*items: CallableType) -> Overloaded: self.assert_join(ov(c(fx.a, fx.a), c(fx.b, fx.b)), c(any, fx.b), c(any, fx.b)) self.assert_join(ov(c(fx.a, fx.a), c(any, fx.b)), c(fx.b, fx.b), c(any, fx.b)) + @skip def test_join_interface_types(self) -> None: - self.skip() # FIX self.assert_join(self.fx.f, self.fx.f, self.fx.f) self.assert_join(self.fx.f, self.fx.f2, self.fx.o) self.assert_join(self.fx.f, self.fx.f3, self.fx.f) + @skip def test_join_interface_and_class_types(self) -> None: - self.skip() # FIX - self.assert_join(self.fx.o, self.fx.f, self.fx.o) self.assert_join(self.fx.a, self.fx.f, self.fx.o) self.assert_join(self.fx.e, self.fx.f, self.fx.f) + @skip def test_join_class_types_with_interface_result(self) -> None: - self.skip() # FIX # Unique result self.assert_join(self.fx.e, self.fx.e2, self.fx.f) # Ambiguous result self.assert_join(self.fx.e2, self.fx.e3, self.fx.anyt) + @skip def test_generic_interfaces(self) -> None: - self.skip() # FIX - fx = InterfaceTypeFixture() self.assert_join(fx.gfa, fx.gfa, fx.gfa) @@ -628,7 +623,7 @@ def type_callable(self, *a: Type) -> CallableType: class MeetSuite(Suite): - def setup(self) -> None: + def setUp(self) -> None: self.fx = TypeFixture() def test_trivial_cases(self) -> None: @@ -761,10 +756,8 @@ def test_meet_class_types_with_shared_interfaces(self) -> None: self.assert_meet(self.fx.e, self.fx.e2, self.fx.nonet) self.assert_meet(self.fx.e2, self.fx.e3, self.fx.nonet) + @skip def test_meet_with_generic_interfaces(self) -> None: - # TODO fix - self.skip() - fx = InterfaceTypeFixture() self.assert_meet(fx.gfa, fx.m1, fx.m1) self.assert_meet(fx.gfa, fx.gfa, fx.gfa) diff --git a/pytest.ini b/pytest.ini index cfbac2538003..9c54c4269bc2 100644 --- a/pytest.ini +++ b/pytest.ini @@ -14,7 +14,7 @@ python_files = test*.py # Because we provide our own collection logic, disable the default # python collector by giving it empty patterns to search for. -python_classes = +python_classes = *Suite python_functions = # always run in parallel (requires pytest-xdist, see test-requirements.txt) diff --git a/runtests.py b/runtests.py index d4712bbfbabb..be7dd72afbe7 100755 --- a/runtests.py +++ b/runtests.py @@ -164,7 +164,6 @@ def add_basic(driver: Driver) -> None: driver.add_mypy('file setup.py', 'setup.py') driver.add_mypy('file runtests.py', 'runtests.py') driver.add_mypy('legacy entry script', 'scripts/mypy') - driver.add_mypy('legacy myunit script', 'scripts/myunit') # needs typed_ast installed: driver.add_mypy('fast-parse', '--fast-parse', 'test-data/samples/hello.py') @@ -213,29 +212,27 @@ def test_path(*names: str): 'testtransform', 'testtypegen', 'testparse', - 'testsemanal' -) - -SLOW_FILES = test_path( - 'testpythoneval', - 'testcmdline', - 'teststubgen', -) - -MYUNIT_FILES = test_path( - 'teststubgen', - 'testargs', + 'testsemanal', + # non-data-driven: 'testgraph', 'testinfer', 'testmoduleinfo', + 'teststubgen', + 'testargs', 'testreports', 'testsolve', 'testsubtypes', 'testtypes', ) +SLOW_FILES = test_path( + 'testpythoneval', + 'testcmdline', + 'teststubgen', +) + for f in find_files('mypy', prefix='test', suffix='.py'): - assert f in PYTEST_FILES + SLOW_FILES + MYUNIT_FILES, f + assert f in PYTEST_FILES + SLOW_FILES, f def add_pytest(driver: Driver) -> None: @@ -243,13 +240,6 @@ def add_pytest(driver: Driver) -> None: [('integration', name) for name in SLOW_FILES]) -def add_myunit(driver: Driver) -> None: - for f in MYUNIT_FILES: - mod = file_to_module(f) - driver.add_python_mod('myunit unit-test %s' % mod, 'mypy.myunit', '-m', mod, - *driver.arglist, coverage=True) - - def add_stubs(driver: Driver) -> None: # We only test each module in the one version mypy prefers to find. # TODO: test stubs for other versions, especially Python 2 stubs. @@ -310,7 +300,6 @@ def usage(status: int) -> None: print(' --ff run all tests but run the last failures first') print(' -q, --quiet decrease driver verbosity') print(' -jN run N tasks at once (default: one per CPU)') - print(' -a, --argument ARG pass an argument to myunit tasks') print(' -p, --pytest_arg ARG pass an argument to pytest tasks') print(' (-v: verbose; glob pattern: filter by test name)') print(' -l, --list list included tasks (after filtering) and exit') @@ -427,7 +416,6 @@ def main() -> None: add_pytest(driver) add_basic(driver) add_selftypecheck(driver) - add_myunit(driver) add_imports(driver) add_stubs(driver) add_stdlibsamples(driver) diff --git a/setup.py b/setup.py index 746fb84e6a38..0fff83c85b68 100644 --- a/setup.py +++ b/setup.py @@ -100,7 +100,7 @@ def run(self): license='MIT License', platforms=['POSIX'], py_modules=[], - packages=['mypy', 'mypy.test', 'mypy.myunit', 'mypy.server'], + packages=['mypy', 'mypy.test', 'mypy.server'], entry_points={'console_scripts': ['mypy=mypy.__main__:console_entry', 'stubgen=mypy.stubgen:main', 'dmypy=mypy.dmypy:main', From ca12acf2454e48513d3189d92ea3fbc3c68bcb0c Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Fri, 15 Dec 2017 13:28:53 +0200 Subject: [PATCH 2/8] remove unused helpers --- mypy/test/helpers.py | 71 -------------------------------------------- 1 file changed, 71 deletions(-) diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 656555bc3ac5..6d773e5e1d69 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -171,21 +171,6 @@ def show_align_message(s1: str, s2: str) -> None: sys.stderr.write('\n') -def assert_string_arrays_equal_wildcards(expected: List[str], - actual: List[str], - msg: str) -> None: - # Like above, but let a line with only '...' in expected match any number - # of lines in actual. - actual = clean_up(actual) - - while actual != [] and actual[-1] == '': - actual = actual[:-1] - - # Expand "..." wildcards away. - expected = match_array(expected, actual) - assert_string_arrays_equal(expected, actual, msg) - - def clean_up(a: List[str]) -> List[str]: """Remove common directory prefix from all strings in a. @@ -205,52 +190,6 @@ def clean_up(a: List[str]) -> List[str]: return res -def match_array(pattern: List[str], target: List[str]) -> List[str]: - """Expand '...' wildcards in pattern by matching against target.""" - - res = [] # type: List[str] - i = 0 - j = 0 - - while i < len(pattern): - if pattern[i] == '...': - # Wildcard in pattern. - if i + 1 == len(pattern): - # Wildcard at end of pattern; match the rest of target. - res.extend(target[j:]) - # Finished. - break - else: - # Must find the instance of the next pattern line in target. - jj = j - while jj < len(target): - if target[jj] == pattern[i + 1]: - break - jj += 1 - if jj == len(target): - # No match. Get out. - res.extend(pattern[i:]) - break - res.extend(target[j:jj]) - i += 1 - j = jj - elif (j < len(target) and (pattern[i] == target[j] - or (i + 1 < len(pattern) - and j + 1 < len(target) - and pattern[i + 1] == target[j + 1]))): - # In sync; advance one line. The above condition keeps sync also if - # only a single line is different, but loses it if two consecutive - # lines fail to match. - res.append(pattern[i]) - i += 1 - j += 1 - else: - # Out of sync. Get out. - res.extend(pattern[i:]) - break - return res - - def num_skipped_prefix_lines(a1: List[str], a2: List[str]) -> int: num_eq = 0 while num_eq < min(len(a1), len(a2)) and a1[num_eq] == a2[num_eq]: @@ -321,11 +260,6 @@ def __init__(self, s: Optional[str] = None) -> None: super().__init__() -class SkipTestCaseException(Exception): - """Exception used to signal skipped test cases.""" - pass - - def assert_true(b: bool, msg: Optional[str] = None) -> None: if not b: raise AssertionFailure(msg) @@ -353,11 +287,6 @@ def assert_equal(a: object, b: object, fmt: str = '{} != {}') -> None: raise AssertionFailure(fmt.format(good_repr(a), good_repr(b))) -def assert_not_equal(a: object, b: object, fmt: str = '{} == {}') -> None: - if a == b: - raise AssertionFailure(fmt.format(good_repr(a), good_repr(b))) - - def typename(t: type) -> str: if '.' in str(t): return str(t).split('.')[-1].rstrip("'>") From 57a2599cab950d0965d0e1a77587259ebbec9f8b Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Sat, 16 Dec 2017 20:48:50 +0200 Subject: [PATCH 3/8] revert ROADMAP, return TODO and assert_equal --- ROADMAP.md | 3 +++ mypy/test/testargs.py | 6 ++---- mypy/test/testsubtypes.py | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 1d5c30257c5c..c68ca2e553e8 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -19,6 +19,9 @@ more accurate. - Continue making error messages more useful and informative. ([issue](https://github.com/python/mypy/labels/topic-usability)) +- Switch completely to pytest and remove the custom testing framework. + ([issue](https://github.com/python/mypy/issues/1673)) + - Make it possible to run mypy as a daemon to avoid reprocessing the entire program on each run. This will improve performance significantly. Even when using the incremental mode, processing a diff --git a/mypy/test/testargs.py b/mypy/test/testargs.py index ea6ca7904046..c77b0c3f74c5 100644 --- a/mypy/test/testargs.py +++ b/mypy/test/testargs.py @@ -5,7 +5,7 @@ object it creates. """ -from mypy.test.helpers import Suite +from mypy.test.helpers import Suite, assert_equal from mypy.options import Options from mypy.main import process_options @@ -14,6 +14,4 @@ class ArgSuite(Suite): def test_coherence(self) -> None: options = Options() _, parsed_options = process_options([], require_targets=False) - assert options.__dict__.keys() == parsed_options.__dict__.keys() - for k in options.__dict__: - assert getattr(options, k) == getattr(parsed_options, k), k + assert_equal(options, parsed_options) diff --git a/mypy/test/testsubtypes.py b/mypy/test/testsubtypes.py index fe3f3a973856..876f3eaf3c74 100644 --- a/mypy/test/testsubtypes.py +++ b/mypy/test/testsubtypes.py @@ -69,6 +69,7 @@ def test_interface_subtyping(self) -> None: @skip def test_generic_interface_subtyping(self) -> None: + # TODO make this work fx2 = InterfaceTypeFixture() self.assert_subtype(fx2.m1, fx2.gfa) From ff8ef0a81d37a5092c9e426795694466b921920f Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Sun, 7 Jan 2018 01:09:49 +0200 Subject: [PATCH 4/8] Add blank line --- mypy/test/helpers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index f627cb242eda..8a0838732075 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -303,6 +303,7 @@ def assert_type(typ: type, value: object) -> None: raise AssertionFailure('Invalid type {}, expected {}'.format( typename(type(value)), typename(typ))) + def parse_options(program_text: str, testcase: DataDrivenTestCase, incremental_step: int) -> Options: """Parse comments like '# flags: --foo' in a test case.""" From b9c47a7cf732390bad573d21fdefd555c469f609 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Mon, 22 Jan 2018 23:21:50 +0200 Subject: [PATCH 5/8] undo removal of empty line --- mypy/test/testdmypy.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mypy/test/testdmypy.py b/mypy/test/testdmypy.py index 46053a764211..9539fa90c3d1 100644 --- a/mypy/test/testdmypy.py +++ b/mypy/test/testdmypy.py @@ -32,6 +32,7 @@ else: dmypy_files = [] # type: List[str] + # By default we complain about missing files. This is a special module prefix # for which we allow non-existence. This is used for testing missing files. NON_EXISTENT_PREFIX = 'nonexistent' From 0cb579892fe32fd4af00ab870216eccd9bb3e488 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Mon, 22 Jan 2018 23:41:05 +0200 Subject: [PATCH 6/8] fix testerrorstream --- mypy/test/testerrorstream.py | 3 +-- typeshed | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mypy/test/testerrorstream.py b/mypy/test/testerrorstream.py index 55e4e3d9228e..58a99d533594 100644 --- a/mypy/test/testerrorstream.py +++ b/mypy/test/testerrorstream.py @@ -5,8 +5,7 @@ from mypy import defaults, build from mypy.test.config import test_temp_dir -from mypy.myunit import AssertionFailure -from mypy.test.helpers import assert_string_arrays_equal +from mypy.test.helpers import assert_string_arrays_equal, AssertionFailure from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.build import BuildSource from mypy.errors import CompileError diff --git a/typeshed b/typeshed index c2fa0a153a6b..7073bc0a49f8 160000 --- a/typeshed +++ b/typeshed @@ -1 +1 @@ -Subproject commit c2fa0a153a6bb83881c7abca6d57af43df605d3d +Subproject commit 7073bc0a49f87ed86444b69cc7332d74b3a4234d From 491bac94d9bf3c06d1084f9ba4e55b33862dcd01 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Fri, 2 Feb 2018 02:32:36 +0200 Subject: [PATCH 7/8] Follow review * remove redundant type declarartions * comment about aliasing * s/AssertionFailure/AssertionError/g * Fix comment in pytest.ini --- mypy/test/data.py | 8 ++++---- mypy/test/helpers.py | 22 +++++++++------------- mypy/test/testcheck.py | 6 +++--- mypy/test/testcmdline.py | 3 +-- mypy/test/testdmypy.py | 6 +++--- mypy/test/testerrorstream.py | 2 +- mypy/test/testparse.py | 4 ++-- mypy/test/testtransform.py | 4 ++-- pytest.ini | 8 +++++--- typeshed | 2 +- 10 files changed, 31 insertions(+), 34 deletions(-) diff --git a/mypy/test/data.py b/mypy/test/data.py index 26a22a1fdfeb..e948aaaecc90 100644 --- a/mypy/test/data.py +++ b/mypy/test/data.py @@ -658,12 +658,12 @@ def repr_failure(self, excinfo: Any) -> str: class DataSuite: # option fields - class variables files = None # type: List[str] - base_path = '.' # type: str - optional_out = False # type: bool - native_sep = False # type: bool + base_path = '.' + optional_out = False + native_sep = False # Assigned from MypyDataCase.runtest - update_data = False # type: bool + update_data = False def setup(self) -> None: """Setup fixtures (ad-hoc)""" diff --git a/mypy/test/helpers.py b/mypy/test/helpers.py index 8a0838732075..daffa8344dc0 100644 --- a/mypy/test/helpers.py +++ b/mypy/test/helpers.py @@ -8,6 +8,9 @@ from mypy import defaults import pytest # type: ignore # no pytest in typeshed + +# Exporting Suite as alias to TestCase for backwards compatibility +# TODO: avoid aliasing - import and subclass TestCase directly from unittest import TestCase as Suite from mypy.main import process_options @@ -89,7 +92,7 @@ def assert_string_arrays_equal(expected: List[str], actual: List[str], # long lines. show_align_message(expected[first_diff], actual[first_diff]) - raise AssertionFailure(msg) + raise AssertionError(msg) def update_testcase_output(testcase: DataDrivenTestCase, output: List[str]) -> None: @@ -254,24 +257,17 @@ def retry_on_error(func: Callable[[], Any], max_wait: float = 1.0) -> None: raise time.sleep(wait_time) - -class AssertionFailure(Exception): - """Exception used to signal failed test cases.""" - def __init__(self, s: Optional[str] = None) -> None: - if s: - super().__init__(s) - else: - super().__init__() +# TODO: assert_true and assert_false are redundant - use plain assert def assert_true(b: bool, msg: Optional[str] = None) -> None: if not b: - raise AssertionFailure(msg) + raise AssertionError(msg) def assert_false(b: bool, msg: Optional[str] = None) -> None: if b: - raise AssertionFailure(msg) + raise AssertionError(msg) def good_repr(obj: object) -> str: @@ -288,7 +284,7 @@ def good_repr(obj: object) -> str: def assert_equal(a: object, b: object, fmt: str = '{} != {}') -> None: if a != b: - raise AssertionFailure(fmt.format(good_repr(a), good_repr(b))) + raise AssertionError(fmt.format(good_repr(a), good_repr(b))) def typename(t: type) -> str: @@ -300,7 +296,7 @@ def typename(t: type) -> str: def assert_type(typ: type, value: object) -> None: if type(value) != typ: - raise AssertionFailure('Invalid type {}, expected {}'.format( + raise AssertionError('Invalid type {}, expected {}'.format( typename(type(value)), typename(typ))) diff --git a/mypy/test/testcheck.py b/mypy/test/testcheck.py index de56941e71a2..ddcf9de53320 100644 --- a/mypy/test/testcheck.py +++ b/mypy/test/testcheck.py @@ -11,7 +11,7 @@ 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, AssertionFailure, + assert_string_arrays_equal, normalize_error_messages, retry_on_error, update_testcase_output, parse_options ) from mypy.errors import CompileError @@ -245,8 +245,8 @@ def verify_cache(self, module_data: List[Tuple[str, str, str]], a: List[str], modules.update({module_name: path for module_name, path, text in module_data}) missing_paths = self.find_missing_cache_files(modules, manager) if not missing_paths.issubset(error_paths): - raise AssertionFailure("cache data discrepancy %s != %s" % - (missing_paths, error_paths)) + raise AssertionError("cache data discrepancy %s != %s" % + (missing_paths, error_paths)) def find_error_paths(self, a: List[str]) -> Set[str]: hits = set() diff --git a/mypy/test/testcmdline.py b/mypy/test/testcmdline.py index 08f8aa4a1960..57910c1a1dc0 100644 --- a/mypy/test/testcmdline.py +++ b/mypy/test/testcmdline.py @@ -11,7 +11,6 @@ from typing import List -from mypy.test.helpers import AssertionFailure from mypy.test.config import test_temp_dir from mypy.test.data import fix_cobertura_filename from mypy.test.data import DataDrivenTestCase, DataSuite @@ -65,7 +64,7 @@ def test_python_cmdline(testcase: DataDrivenTestCase) -> None: if testcase.output_files: for path, expected_content in testcase.output_files: if not os.path.exists(path): - raise AssertionFailure( + raise AssertionError( 'Expected file {} was not produced by test case'.format(path)) with open(path, 'r') as output_file: actual_output_content = output_file.read().splitlines() diff --git a/mypy/test/testdmypy.py b/mypy/test/testdmypy.py index 9539fa90c3d1..08f843642896 100644 --- a/mypy/test/testdmypy.py +++ b/mypy/test/testdmypy.py @@ -13,7 +13,7 @@ from mypy.test.config import test_temp_dir from mypy.test.data import DataDrivenTestCase, DataSuite, has_stable_flags, is_incremental from mypy.test.helpers import ( - assert_string_arrays_equal, normalize_error_messages, AssertionFailure, + assert_string_arrays_equal, normalize_error_messages, retry_on_error, testcase_pyversion, update_testcase_output, ) from mypy.options import Options @@ -194,8 +194,8 @@ def verify_cache(self, module_data: List[Tuple[str, str, Optional[str]]], a: Lis modules.update({module_name: path for module_name, path, text in module_data}) missing_paths = self.find_missing_cache_files(modules, manager) if not missing_paths.issubset(error_paths): - raise AssertionFailure("cache data discrepancy %s != %s" % - (missing_paths, error_paths)) + raise AssertionError("cache data discrepancy %s != %s" % + (missing_paths, error_paths)) def find_error_paths(self, a: List[str]) -> Set[str]: hits = set() diff --git a/mypy/test/testerrorstream.py b/mypy/test/testerrorstream.py index 58a99d533594..b67451db787f 100644 --- a/mypy/test/testerrorstream.py +++ b/mypy/test/testerrorstream.py @@ -5,7 +5,7 @@ from mypy import defaults, build from mypy.test.config import test_temp_dir -from mypy.test.helpers import assert_string_arrays_equal, AssertionFailure +from mypy.test.helpers import assert_string_arrays_equal from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.build import BuildSource from mypy.errors import CompileError diff --git a/mypy/test/testparse.py b/mypy/test/testparse.py index 1f204f6cf220..2c0350f92cd5 100644 --- a/mypy/test/testparse.py +++ b/mypy/test/testparse.py @@ -1,7 +1,7 @@ """Tests for the mypy parser.""" from mypy import defaults -from mypy.test.helpers import assert_string_arrays_equal, AssertionFailure +from mypy.test.helpers import assert_string_arrays_equal from mypy.test.data import DataDrivenTestCase, DataSuite from mypy.parse import parse from mypy.errors import CompileError @@ -59,7 +59,7 @@ def test_parse_error(testcase: DataDrivenTestCase) -> None: # Compile temporary file. The test file contains non-ASCII characters. parse(bytes('\n'.join(testcase.input), 'utf-8'), INPUT_FILE_NAME, '__main__', None, Options()) - raise AssertionFailure('No errors reported') + raise AssertionError('No errors reported') except CompileError as e: assert e.module_with_blocker == '__main__' # Verify that there was a compile error and that the error messages diff --git a/mypy/test/testtransform.py b/mypy/test/testtransform.py index 5f693b5d239a..e5f0c2ceb02e 100644 --- a/mypy/test/testtransform.py +++ b/mypy/test/testtransform.py @@ -63,7 +63,7 @@ def test_transform(testcase: DataDrivenTestCase) -> None: and not os.path.basename(f.path).startswith('_') and not os.path.splitext( os.path.basename(f.path))[0].endswith('_')): - t = TestTransformVisitor() + t = TypeAssertTransformVisitor() f = t.mypyfile(f) a += str(f).split('\n') except CompileError as e: @@ -75,7 +75,7 @@ def test_transform(testcase: DataDrivenTestCase) -> None: testcase.line)) -class TestTransformVisitor(TransformVisitor): +class TypeAssertTransformVisitor(TransformVisitor): def type(self, type: Type) -> Type: assert type is not None return type diff --git a/pytest.ini b/pytest.ini index 9c54c4269bc2..808f581b639b 100644 --- a/pytest.ini +++ b/pytest.ini @@ -12,9 +12,11 @@ python_files = test*.py # and invokes it at the relevant moment. See # http://doc.pytest.org/en/latest/writing_plugins.html#collection-hooks -# Because we provide our own collection logic, disable the default -# python collector by giving it empty patterns to search for. -python_classes = *Suite +# Both our plugin and unittest provide their own collection logic, +# So we can disable the default python collector by giving it empty +# patterns to search for. +# Note that unittest requires that no "Test*" classes exist. +python_classes = python_functions = # always run in parallel (requires pytest-xdist, see test-requirements.txt) diff --git a/typeshed b/typeshed index 7073bc0a49f8..c2fa0a153a6b 160000 --- a/typeshed +++ b/typeshed @@ -1 +1 @@ -Subproject commit 7073bc0a49f87ed86444b69cc7332d74b3a4234d +Subproject commit c2fa0a153a6bb83881c7abca6d57af43df605d3d From a2ebf8097ab2cfedd97d8a5f16e865008506bc46 Mon Sep 17 00:00:00 2001 From: Elazar Gershuni Date: Fri, 2 Feb 2018 04:07:31 +0200 Subject: [PATCH 8/8] ArgSuite: do not test options.config_file --- mypy/options.py | 2 +- mypy/test/testargs.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mypy/options.py b/mypy/options.py index e8392ce64807..c62520075d34 100644 --- a/mypy/options.py +++ b/mypy/options.py @@ -131,7 +131,7 @@ def __init__(self) -> None: self.scripts_are_modules = False # Config file name - self.config_file = 'setup.cfg' # type: Optional[str] + self.config_file = None # type: Optional[str] # Write junit.xml to given file self.junit_xml = None # type: Optional[str] diff --git a/mypy/test/testargs.py b/mypy/test/testargs.py index c77b0c3f74c5..20db610cda18 100644 --- a/mypy/test/testargs.py +++ b/mypy/test/testargs.py @@ -14,4 +14,6 @@ class ArgSuite(Suite): def test_coherence(self) -> None: options = Options() _, parsed_options = process_options([], require_targets=False) + # FIX: test this too. Requires changing working dir to avoid finding 'setup.cfg' + options.config_file = parsed_options.config_file assert_equal(options, parsed_options)