11import signal
22import textwrap
33
4+ import pexpect
45import pytest
56
67
8+ PYTEST_VERSION = tuple (int (v ) for v in pytest .__version__ .split ("." )[:2 ])
9+
10+
711@pytest .fixture (
812 params = [
913 True , # xdist enabled, active
@@ -309,7 +313,8 @@ def pyfunc(x):
309313 assert result .ret != 0
310314
311315
312- def test_looponfail (testdir ):
316+ @pytest .mark .parametrize ("module_name" , ["__init__" , "test_demo" ])
317+ def test_looponfail (testdir , module_name ):
313318 """Ensure that the plugin works with --looponfail."""
314319
315320 pass_source = textwrap .dedent (
@@ -324,7 +329,7 @@ def pyfunc(x: int) -> str:
324329 return x * 2
325330 """ ,
326331 )
327- pyfile = testdir .makepyfile (fail_source )
332+ pyfile = testdir .makepyfile (** { module_name : fail_source } )
328333 looponfailroot = testdir .mkdir ("looponfailroot" )
329334 looponfailroot_pyfile = looponfailroot .join (pyfile .basename )
330335 pyfile .move (looponfailroot_pyfile )
@@ -345,6 +350,14 @@ def pyfunc(x: int) -> str:
345350 expect_timeout = 30.0 ,
346351 )
347352
353+ num_tests = 2
354+ if module_name == "__init__" and (3 , 10 ) <= PYTEST_VERSION < (6 , 2 ):
355+ # https://github.com/pytest-dev/pytest/issues/8016
356+ # Pytest had a bug where it assumed only a Package would have a basename of
357+ # __init__.py. In this test, Pytest mistakes MypyFile for a Package and
358+ # returns after collecting only one object (the MypyFileItem).
359+ num_tests = 1
360+
348361 def _expect_session ():
349362 child .expect ("==== test session starts ====" )
350363
@@ -353,10 +366,11 @@ def _expect_failure():
353366 child .expect ("==== FAILURES ====" )
354367 child .expect (pyfile .basename + " ____" )
355368 child .expect ("2: error: Incompatible return value" )
356- # These only show with mypy>=0.730:
357- # child.expect("==== mypy ====")
358- # child.expect("Found 1 error in 1 file (checked 1 source file)")
359- child .expect ("2 failed" )
369+ # if num_tests == 2:
370+ # # These only show with mypy>=0.730:
371+ # child.expect("==== mypy ====")
372+ # child.expect("Found 1 error in 1 file (checked 1 source file)")
373+ child .expect (str (num_tests ) + " failed" )
360374 child .expect ("#### LOOPONFAILING ####" )
361375 _expect_waiting ()
362376
@@ -375,10 +389,27 @@ def _expect_changed():
375389 def _expect_success ():
376390 for _ in range (2 ):
377391 _expect_session ()
378- # These only show with mypy>=0.730:
379- # child.expect("==== mypy ====")
380- # child.expect("Success: no issues found in 1 source file")
381- child .expect ("2 passed" )
392+ # if num_tests == 2:
393+ # # These only show with mypy>=0.730:
394+ # child.expect("==== mypy ====")
395+ # child.expect("Success: no issues found in 1 source file")
396+ try :
397+ child .expect (str (num_tests ) + " passed" )
398+ except pexpect .exceptions .TIMEOUT :
399+ if module_name == "__init__" and (6 , 0 ) <= PYTEST_VERSION < (6 , 2 ):
400+ # MypyItems hit the __init__.py bug too when --looponfail
401+ # re-collects them after the failing file is modified.
402+ # Unlike MypyFile, MypyItem is not a Collector, so this used
403+ # to cause an AttributeError until a workaround was added
404+ # (MypyItem.collect was defined to yield itself).
405+ # Mypy probably noticed the __init__.py problem during the
406+ # development of Pytest 6.0, but the error was addressed
407+ # with an isinstance assertion, which broke the workaround.
408+ # Here, we hit that assertion:
409+ child .expect ("AssertionError" )
410+ child .expect ("1 error" )
411+ pytest .xfail ("https://github.com/pytest-dev/pytest/issues/8016" )
412+ raise
382413 _expect_waiting ()
383414
384415 def _break ():
@@ -391,3 +422,27 @@ def _break():
391422 _break ()
392423 _fix ()
393424 child .kill (signal .SIGTERM )
425+
426+
427+ def test_mypy_item_collect (testdir , xdist_args ):
428+ """Ensure coverage for a 3.10<=pytest<6.0 workaround."""
429+ testdir .makepyfile (
430+ """
431+ def test_mypy_item_collect(request):
432+ plugin = request.config.pluginmanager.getplugin("mypy")
433+ mypy_items = [
434+ item
435+ for item in request.session.items
436+ if isinstance(item, plugin.MypyItem)
437+ ]
438+ assert mypy_items
439+ for mypy_item in mypy_items:
440+ assert all(item is mypy_item for item in mypy_item.collect())
441+ """ ,
442+ )
443+ result = testdir .runpytest_subprocess ("--mypy" , * xdist_args )
444+ test_count = 1
445+ mypy_file_checks = 1
446+ mypy_status_check = 1
447+ result .assert_outcomes (passed = test_count + mypy_file_checks + mypy_status_check )
448+ assert result .ret == 0
0 commit comments