@@ -14,35 +14,41 @@ def xdist_args(request):
1414 return ['-n' , 'auto' ] if request .param else []
1515
1616
17- @pytest .mark .parametrize ('test_count ' , [1 , 2 ])
18- def test_mypy_success (testdir , test_count , xdist_args ):
17+ @pytest .mark .parametrize ('pyfile_count ' , [1 , 2 ])
18+ def test_mypy_success (testdir , pyfile_count , xdist_args ):
1919 """Verify that running on a module with no type errors passes."""
2020 testdir .makepyfile (
2121 ** {
22- 'test_ ' + str (test_i ): '''
23- def myfunc (x: int) -> int:
22+ 'pyfile_ ' + str (pyfile_i ): '''
23+ def pyfunc (x: int) -> int:
2424 return x * 2
2525 '''
26- for test_i in range (test_count )
26+ for pyfile_i in range (pyfile_count )
2727 }
2828 )
2929 result = testdir .runpytest_subprocess (* xdist_args )
3030 result .assert_outcomes ()
3131 result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
32- result .assert_outcomes (passed = test_count )
32+ mypy_file_checks = pyfile_count
33+ mypy_status_check = 1
34+ mypy_checks = mypy_file_checks + mypy_status_check
35+ result .assert_outcomes (passed = mypy_checks )
3336 assert result .ret == 0
3437
3538
3639def test_mypy_error (testdir , xdist_args ):
3740 """Verify that running on a module with type errors fails."""
3841 testdir .makepyfile ('''
39- def myfunc (x: int) -> str:
42+ def pyfunc (x: int) -> str:
4043 return x * 2
4144 ''' )
4245 result = testdir .runpytest_subprocess (* xdist_args )
4346 result .assert_outcomes ()
4447 result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
45- result .assert_outcomes (failed = 1 )
48+ mypy_file_checks = 1
49+ mypy_status_check = 1
50+ mypy_checks = mypy_file_checks + mypy_status_check
51+ result .assert_outcomes (failed = mypy_checks )
4652 result .stdout .fnmatch_lines ([
4753 '2: error: Incompatible return value*' ,
4854 ])
@@ -54,20 +60,29 @@ def test_mypy_ignore_missings_imports(testdir, xdist_args):
5460 Verify that --mypy-ignore-missing-imports
5561 causes mypy to ignore missing imports.
5662 """
63+ module_name = 'is_always_missing'
5764 testdir .makepyfile ('''
58- import pytest_mypy
59- ''' )
65+ try:
66+ import {module_name}
67+ except ImportError:
68+ pass
69+ ''' .format (module_name = module_name ))
6070 result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
61- result .assert_outcomes (failed = 1 )
71+ mypy_file_checks = 1
72+ mypy_status_check = 1
73+ mypy_checks = mypy_file_checks + mypy_status_check
74+ result .assert_outcomes (failed = mypy_checks )
6275 result .stdout .fnmatch_lines ([
63- "1: error: Cannot find *module named 'pytest_mypy'" ,
76+ "2: error: Cannot find *module named '{module_name}'" .format (
77+ module_name = module_name ,
78+ ),
6479 ])
6580 assert result .ret != 0
6681 result = testdir .runpytest_subprocess (
6782 '--mypy-ignore-missing-imports' ,
6883 * xdist_args
6984 )
70- result .assert_outcomes (passed = 1 )
85+ result .assert_outcomes (passed = mypy_checks )
7186 assert result .ret == 0
7287
7388
@@ -78,28 +93,39 @@ def test_fails():
7893 assert False
7994 ''' )
8095 result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
81- result .assert_outcomes (failed = 1 , passed = 1 )
96+ test_count = 1
97+ mypy_file_checks = 1
98+ mypy_status_check = 1
99+ mypy_checks = mypy_file_checks + mypy_status_check
100+ result .assert_outcomes (failed = test_count , passed = mypy_checks )
82101 assert result .ret != 0
83102 result = testdir .runpytest_subprocess ('--mypy' , '-m' , 'mypy' , * xdist_args )
84- result .assert_outcomes (passed = 1 )
103+ result .assert_outcomes (passed = mypy_checks )
85104 assert result .ret == 0
86105
87106
88107def test_non_mypy_error (testdir , xdist_args ):
89108 """Verify that non-MypyError exceptions are passed through the plugin."""
90109 message = 'This is not a MypyError.'
91- testdir .makepyfile ('''
92- import pytest_mypy
110+ testdir .makepyfile (conftest = '''
111+ def pytest_configure(config):
112+ plugin = config.pluginmanager.getplugin('mypy')
93113
94- def _patched_runtest(*args, **kwargs):
95- raise Exception('{message}')
114+ class PatchedMypyFileItem(plugin.MypyFileItem):
115+ def runtest(self):
116+ raise Exception('{message}')
96117
97- pytest_mypy.MypyItem.runtest = _patched_runtest
118+ plugin.MypyFileItem = PatchedMypyFileItem
98119 ''' .format (message = message ))
99120 result = testdir .runpytest_subprocess (* xdist_args )
100121 result .assert_outcomes ()
101122 result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
102- result .assert_outcomes (failed = 1 )
123+ mypy_file_checks = 1 # conftest.py
124+ mypy_status_check = 1
125+ result .assert_outcomes (
126+ failed = mypy_file_checks , # patched to raise an Exception
127+ passed = mypy_status_check , # conftest.py has no type errors.
128+ )
103129 result .stdout .fnmatch_lines (['*' + message ])
104130 assert result .ret != 0
105131
@@ -159,23 +185,75 @@ def pytest_configure(config):
159185
160186
161187def test_pytest_collection_modifyitems (testdir , xdist_args ):
188+ """
189+ Verify that collected files which are removed in a
190+ pytest_collection_modifyitems implementation are not
191+ checked by mypy.
192+
193+ This would also fail if a MypyStatusItem were injected
194+ despite there being no MypyFileItems.
195+ """
162196 testdir .makepyfile (conftest = '''
163197 def pytest_collection_modifyitems(session, config, items):
164198 plugin = config.pluginmanager.getplugin('mypy')
165199 for mypy_item_i in reversed([
166200 i
167201 for i, item in enumerate(items)
168- if isinstance(item, plugin.MypyItem )
202+ if isinstance(item, plugin.MypyFileItem )
169203 ]):
170204 items.pop(mypy_item_i)
171205 ''' )
172206 testdir .makepyfile ('''
173- def myfunc (x: int) -> str:
207+ def pyfunc (x: int) -> str:
174208 return x * 2
175209
176210 def test_pass():
177211 pass
178212 ''' )
179213 result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
180- result .assert_outcomes (passed = 1 )
214+ test_count = 1
215+ result .assert_outcomes (passed = test_count )
181216 assert result .ret == 0
217+
218+
219+ def test_mypy_indirect (testdir , xdist_args ):
220+ """Verify that uncollected files checked by mypy cause a failure."""
221+ testdir .makepyfile (bad = '''
222+ def pyfunc(x: int) -> str:
223+ return x * 2
224+ ''' )
225+ testdir .makepyfile (good = '''
226+ import bad
227+ ''' )
228+ xdist_args .append ('good.py' ) # Nothing may come after xdist_args in py34.
229+ result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
230+ assert result .ret != 0
231+
232+
233+ def test_mypy_indirect_inject (testdir , xdist_args ):
234+ """
235+ Verify that uncollected files checked by mypy because of a MypyFileItem
236+ injected in pytest_collection_modifyitems cause a failure.
237+ """
238+ testdir .makepyfile (bad = '''
239+ def pyfunc(x: int) -> str:
240+ return x * 2
241+ ''' )
242+ testdir .makepyfile (good = '''
243+ import bad
244+ ''' )
245+ testdir .makepyfile (conftest = '''
246+ import py
247+ import pytest
248+
249+ @pytest.hookimpl(trylast=True) # Inject as late as possible.
250+ def pytest_collection_modifyitems(session, config, items):
251+ plugin = config.pluginmanager.getplugin('mypy')
252+ items.append(
253+ plugin.MypyFileItem(py.path.local('good.py'), session),
254+ )
255+ ''' )
256+ testdir .mkdir ('empty' )
257+ xdist_args .append ('empty' ) # Nothing may come after xdist_args in py34.
258+ result = testdir .runpytest_subprocess ('--mypy' , * xdist_args )
259+ assert result .ret != 0
0 commit comments