test_update_data.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. """
  2. A "meta test" which tests the `--update-data` feature for updating .test files.
  3. Updating the expected output, especially when it's in the form of inline (comment) assertions,
  4. can be brittle, which is why we're "meta-testing" here.
  5. """
  6. import shlex
  7. import subprocess
  8. import sys
  9. import textwrap
  10. import uuid
  11. from pathlib import Path
  12. from mypy.test.config import test_data_prefix
  13. from mypy.test.helpers import Suite
  14. class UpdateDataSuite(Suite):
  15. def _run_pytest_update_data(self, data_suite: str, *, max_attempts: int) -> str:
  16. """
  17. Runs a suite of data test cases through 'pytest --update-data' until either tests pass
  18. or until a maximum number of attempts (needed for incremental tests).
  19. """
  20. p_test_data = Path(test_data_prefix)
  21. p_root = p_test_data.parent.parent
  22. p = p_test_data / f"check-meta-{uuid.uuid4()}.test"
  23. assert not p.exists()
  24. try:
  25. p.write_text(textwrap.dedent(data_suite).lstrip())
  26. test_nodeid = f"mypy/test/testcheck.py::TypeCheckSuite::{p.name}"
  27. args = [sys.executable, "-m", "pytest", "-n", "0", "-s", "--update-data", test_nodeid]
  28. cmd = shlex.join(args)
  29. for i in range(max_attempts - 1, -1, -1):
  30. res = subprocess.run(args, cwd=p_root)
  31. if res.returncode == 0:
  32. break
  33. print(f"`{cmd}` returned {res.returncode}: {i} attempts remaining")
  34. return p.read_text()
  35. finally:
  36. p.unlink()
  37. def test_update_data(self) -> None:
  38. # Note: We test multiple testcases rather than 'test case per test case'
  39. # so we could also exercise rewriting multiple testcases at once.
  40. actual = self._run_pytest_update_data(
  41. """
  42. [case testCorrect]
  43. s: str = 42 # E: Incompatible types in assignment (expression has type "int", variable has type "str")
  44. [case testWrong]
  45. s: str = 42 # E: wrong error
  46. [case testXfail-xfail]
  47. s: str = 42 # E: wrong error
  48. [case testWrongMultiline]
  49. s: str = 42 # E: foo \
  50. # N: bar
  51. [case testMissingMultiline]
  52. s: str = 42; i: int = 'foo'
  53. [case testExtraneous]
  54. s: str = 'foo' # E: wrong error
  55. [case testExtraneousMultiline]
  56. s: str = 'foo' # E: foo \
  57. # E: bar
  58. [case testExtraneousMultilineNonError]
  59. s: str = 'foo' # W: foo \
  60. # N: bar
  61. [case testOutCorrect]
  62. s: str = 42
  63. [out]
  64. main:1: error: Incompatible types in assignment (expression has type "int", variable has type "str")
  65. [case testOutWrong]
  66. s: str = 42
  67. [out]
  68. main:1: error: foobar
  69. [case testOutWrongIncremental]
  70. s: str = 42
  71. [out]
  72. main:1: error: foobar
  73. [out2]
  74. main:1: error: foobar
  75. [case testWrongMultipleFiles]
  76. import a, b
  77. s: str = 42 # E: foo
  78. [file a.py]
  79. s1: str = 42 # E: bar
  80. [file b.py]
  81. s2: str = 43 # E: baz
  82. [builtins fixtures/list.pyi]
  83. """,
  84. max_attempts=3,
  85. )
  86. # Assert
  87. expected = """
  88. [case testCorrect]
  89. s: str = 42 # E: Incompatible types in assignment (expression has type "int", variable has type "str")
  90. [case testWrong]
  91. s: str = 42 # E: Incompatible types in assignment (expression has type "int", variable has type "str")
  92. [case testXfail-xfail]
  93. s: str = 42 # E: wrong error
  94. [case testWrongMultiline]
  95. s: str = 42 # E: Incompatible types in assignment (expression has type "int", variable has type "str")
  96. [case testMissingMultiline]
  97. s: str = 42; i: int = 'foo' # E: Incompatible types in assignment (expression has type "int", variable has type "str") \\
  98. # E: Incompatible types in assignment (expression has type "str", variable has type "int")
  99. [case testExtraneous]
  100. s: str = 'foo'
  101. [case testExtraneousMultiline]
  102. s: str = 'foo'
  103. [case testExtraneousMultilineNonError]
  104. s: str = 'foo'
  105. [case testOutCorrect]
  106. s: str = 42
  107. [out]
  108. main:1: error: Incompatible types in assignment (expression has type "int", variable has type "str")
  109. [case testOutWrong]
  110. s: str = 42
  111. [out]
  112. main:1: error: Incompatible types in assignment (expression has type "int", variable has type "str")
  113. [case testOutWrongIncremental]
  114. s: str = 42
  115. [out]
  116. main:1: error: Incompatible types in assignment (expression has type "int", variable has type "str")
  117. [out2]
  118. main:1: error: Incompatible types in assignment (expression has type "int", variable has type "str")
  119. [case testWrongMultipleFiles]
  120. import a, b
  121. s: str = 42 # E: Incompatible types in assignment (expression has type "int", variable has type "str")
  122. [file a.py]
  123. s1: str = 42 # E: Incompatible types in assignment (expression has type "int", variable has type "str")
  124. [file b.py]
  125. s2: str = 43 # E: Incompatible types in assignment (expression has type "int", variable has type "str")
  126. [builtins fixtures/list.pyi]
  127. """
  128. assert actual == textwrap.dedent(expected).lstrip()