Skip to content

Commit 515e84f

Browse files
committed
Add an xfailed test for issue #9852
As part of doing this I upgraded the pep561 tests to support incremental tests, and removed at least a little of the duplication between different incremental test suites.
1 parent 188b7d4 commit 515e84f

File tree

8 files changed

+88
-80
lines changed

8 files changed

+88
-80
lines changed

mypy/test/data.py

-1
Original file line numberDiff line numberDiff line change
@@ -335,7 +335,6 @@ def module_from_path(path: str) -> str:
335335
path = re.sub(r'\.pyi?$', '', path)
336336
# We can have a mix of Unix-style and Windows-style separators.
337337
parts = re.split(r'[/\\]', path)
338-
assert parts[0] == test_temp_dir
339338
del parts[0]
340339
module = '.'.join(parts)
341340
module = re.sub(r'\.__init__$', '', module)

mypy/test/helpers.py

+22-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import shutil
66
import contextlib
77

8-
from typing import List, Iterable, Dict, Tuple, Callable, Any, Iterator
8+
from typing import List, Iterable, Dict, Tuple, Callable, Any, Iterator, Union
99

1010
from mypy import defaults
1111
import mypy.api as api
@@ -18,7 +18,9 @@
1818

1919
from mypy.main import process_options
2020
from mypy.options import Options
21-
from mypy.test.data import DataDrivenTestCase, fix_cobertura_filename
21+
from mypy.test.data import (
22+
DataDrivenTestCase, fix_cobertura_filename, UpdateFile, DeleteFile
23+
)
2224
from mypy.test.config import test_temp_dir
2325
import mypy.version
2426

@@ -423,6 +425,24 @@ def copy_and_fudge_mtime(source_path: str, target_path: str) -> None:
423425
os.utime(target_path, times=(new_time, new_time))
424426

425427

428+
def perform_file_operations(
429+
operations: List[Union[UpdateFile, DeleteFile]]) -> None:
430+
for op in operations:
431+
if isinstance(op, UpdateFile):
432+
# Modify/create file
433+
copy_and_fudge_mtime(op.source_path, op.target_path)
434+
else:
435+
# Delete file/directory
436+
if os.path.isdir(op.path):
437+
# Sanity check to avoid unexpected deletions
438+
assert op.path.startswith('tmp')
439+
shutil.rmtree(op.path)
440+
else:
441+
# Use retries to work around potential flakiness on Windows (AppVeyor).
442+
path = op.path
443+
retry_on_error(lambda: os.remove(path))
444+
445+
426446
def check_test_output_files(testcase: DataDrivenTestCase,
427447
step: int,
428448
strip_prefix: str = '') -> None:

mypy/test/testcheck.py

+4-17
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import os
44
import re
5-
import shutil
65
import sys
76

87
from typing import Dict, List, Set, Tuple
@@ -12,12 +11,12 @@
1211
from mypy.modulefinder import BuildSource, SearchPaths, FindModuleCache
1312
from mypy.test.config import test_temp_dir, test_data_prefix
1413
from mypy.test.data import (
15-
DataDrivenTestCase, DataSuite, FileOperation, UpdateFile, module_from_path
14+
DataDrivenTestCase, DataSuite, FileOperation, module_from_path
1615
)
1716
from mypy.test.helpers import (
1817
assert_string_arrays_equal, normalize_error_messages, assert_module_equivalence,
19-
retry_on_error, update_testcase_output, parse_options,
20-
copy_and_fudge_mtime, assert_target_equivalence, check_test_output_files
18+
update_testcase_output, parse_options,
19+
assert_target_equivalence, check_test_output_files, perform_file_operations,
2120
)
2221
from mypy.errors import CompileError
2322
from mypy.semanal_main import core_modules
@@ -156,19 +155,7 @@ def run_case_once(self, testcase: DataDrivenTestCase,
156155
break
157156
elif incremental_step > 1:
158157
# In runs 2+, copy *.[num] files to * files.
159-
for op in operations:
160-
if isinstance(op, UpdateFile):
161-
# Modify/create file
162-
copy_and_fudge_mtime(op.source_path, op.target_path)
163-
else:
164-
# Delete file/directory
165-
path = op.path
166-
if os.path.isdir(path):
167-
# Sanity check to avoid unexpected deletions
168-
assert path.startswith('tmp')
169-
shutil.rmtree(path)
170-
# Use retries to work around potential flakiness on Windows (AppVeyor).
171-
retry_on_error(lambda: os.remove(path))
158+
perform_file_operations(operations)
172159

173160
# Parse options after moving files (in case mypy.ini is being moved).
174161
options = parse_options(original_program_text, testcase, incremental_step)

mypy/test/testfinegrained.py

+3-15
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
import os
1616
import re
17-
import shutil
1817

1918
from typing import List, Dict, Any, Tuple, Union, cast
2019

@@ -27,8 +26,8 @@
2726
DataDrivenTestCase, DataSuite, UpdateFile, DeleteFile
2827
)
2928
from mypy.test.helpers import (
30-
assert_string_arrays_equal, parse_options, copy_and_fudge_mtime, assert_module_equivalence,
31-
assert_target_equivalence
29+
assert_string_arrays_equal, parse_options, assert_module_equivalence,
30+
assert_target_equivalence, perform_file_operations,
3231
)
3332
from mypy.server.mergecheck import check_consistency
3433
from mypy.dmypy_util import DEFAULT_STATUS_FILE
@@ -211,18 +210,7 @@ def perform_step(self,
211210
212211
Return (mypy output, triggered targets).
213212
"""
214-
for op in operations:
215-
if isinstance(op, UpdateFile):
216-
# Modify/create file
217-
copy_and_fudge_mtime(op.source_path, op.target_path)
218-
else:
219-
# Delete file/directory
220-
if os.path.isdir(op.path):
221-
# Sanity check to avoid unexpected deletions
222-
assert op.path.startswith('tmp')
223-
shutil.rmtree(op.path)
224-
else:
225-
os.remove(op.path)
213+
perform_file_operations(operations)
226214
sources = self.parse_sources(main_src, step, options)
227215

228216
if step <= num_regular_incremental_steps:

mypy/test/testpep561.py

+33-32
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from mypy.util import try_find_python2_interpreter
1414
from mypy.test.data import DataDrivenTestCase, DataSuite
1515
from mypy.test.config import test_temp_dir
16-
from mypy.test.helpers import assert_string_arrays_equal
16+
from mypy.test.helpers import assert_string_arrays_equal, perform_file_operations
1717

1818

1919
# NOTE: options.use_builtins_fixtures should not be set in these
@@ -38,6 +38,7 @@ class PEP561Suite(DataSuite):
3838
files = [
3939
'pep561.test',
4040
]
41+
base_path = '.'
4142

4243
def run_case(self, test_case: DataDrivenTestCase) -> None:
4344
test_pep561(test_case)
@@ -102,6 +103,7 @@ def test_pep561(testcase: DataDrivenTestCase) -> None:
102103
pytest.skip()
103104
else:
104105
python = sys.executable
106+
105107
assert python is not None, "Should be impossible"
106108
pkgs, pip_args = parse_pkgs(testcase.input[0])
107109
mypy_args = parse_mypy_args(testcase.input[1])
@@ -118,34 +120,30 @@ def test_pep561(testcase: DataDrivenTestCase) -> None:
118120
for pkg in pkgs:
119121
install_package(pkg, python_executable, use_pip, editable)
120122

121-
if venv_dir is not None:
122-
old_dir = os.getcwd()
123-
os.chdir(venv_dir)
124-
try:
125-
cmd_line = list(mypy_args)
126-
has_program = not ('-p' in cmd_line or '--package' in cmd_line)
127-
if has_program:
128-
program = testcase.name + '.py'
129-
with open(program, 'w', encoding='utf-8') as f:
130-
for s in testcase.input:
131-
f.write('{}\n'.format(s))
132-
cmd_line.append(program)
133-
cmd_line.extend(['--no-incremental', '--no-error-summary'])
134-
if python_executable != sys.executable:
135-
cmd_line.append('--python-executable={}'.format(python_executable))
136-
if testcase.files != []:
137-
for name, content in testcase.files:
138-
if 'mypy.ini' in name:
139-
with open('mypy.ini', 'w') as m:
140-
m.write(content)
141-
elif 'pyproject.toml' in name:
142-
with open('pyproject.toml', 'w') as m:
143-
m.write(content)
123+
cmd_line = list(mypy_args)
124+
has_program = not ('-p' in cmd_line or '--package' in cmd_line)
125+
if has_program:
126+
program = testcase.name + '.py'
127+
with open(program, 'w', encoding='utf-8') as f:
128+
for s in testcase.input:
129+
f.write('{}\n'.format(s))
130+
cmd_line.append(program)
131+
132+
cmd_line.extend(['--no-error-summary'])
133+
if python_executable != sys.executable:
134+
cmd_line.append('--python-executable={}'.format(python_executable))
135+
136+
steps = testcase.find_steps()
137+
if steps != [[]]:
138+
steps = [[]] + steps # type: ignore[operator,assignment]
139+
140+
for i, operations in enumerate(steps):
141+
perform_file_operations(operations)
142+
144143
output = []
145144
# Type check the module
146145
out, err, returncode = mypy.api.run(cmd_line)
147-
if has_program:
148-
os.remove(program)
146+
149147
# split lines, remove newlines, and remove directory of test case
150148
for line in (out + err).splitlines():
151149
if line.startswith(test_temp_dir + os.sep):
@@ -154,12 +152,15 @@ def test_pep561(testcase: DataDrivenTestCase) -> None:
154152
# Normalize paths so that the output is the same on Windows and Linux/macOS.
155153
line = line.replace(test_temp_dir + os.sep, test_temp_dir + '/')
156154
output.append(line.rstrip("\r\n"))
157-
assert_string_arrays_equal([line for line in testcase.output], output,
158-
'Invalid output ({}, line {})'.format(
159-
testcase.file, testcase.line))
160-
finally:
161-
if venv_dir is not None:
162-
os.chdir(old_dir)
155+
iter_count = '' if i == 0 else ' on iteration {}'.format(i + 1)
156+
expected = testcase.output if i == 0 else testcase.output2.get(i + 1, [])
157+
158+
assert_string_arrays_equal(expected, output,
159+
'Invalid output ({}, line {}){}'.format(
160+
testcase.file, testcase.line, iter_count))
161+
162+
if has_program:
163+
os.remove(program)
163164

164165

165166
def parse_pkgs(comment: str) -> Tuple[List[str], List[str]]:

mypyc/test-data/run-multimodule.test

+1
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,7 @@ assert other_a.foo() == 10
780780
[file other_a.py]
781781
def foo() -> int: return 10
782782

783+
[file build/__native_other_a.c]
783784

784785
[delete build/__native_other_a.c.2]
785786

mypyc/test/test_run.py

+5-13
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
from typing import Any, Iterator, List, cast
1313

1414
from mypy import build
15-
from mypy.test.data import DataDrivenTestCase, UpdateFile
15+
from mypy.test.data import DataDrivenTestCase
1616
from mypy.test.config import test_temp_dir
1717
from mypy.errors import CompileError
1818
from mypy.options import Options
19-
from mypy.test.helpers import copy_and_fudge_mtime, assert_module_equivalence
19+
from mypy.test.helpers import assert_module_equivalence, perform_file_operations
2020

2121
from mypyc.codegen import emitmodule
2222
from mypyc.options import CompilerOptions
@@ -135,7 +135,8 @@ def run_case(self, testcase: DataDrivenTestCase) -> None:
135135
self.run_case_inner(testcase)
136136

137137
def run_case_inner(self, testcase: DataDrivenTestCase) -> None:
138-
os.mkdir(WORKDIR)
138+
if not os.path.isdir(WORKDIR): # (one test puts something in build...)
139+
os.mkdir(WORKDIR)
139140

140141
text = '\n'.join(testcase.input)
141142

@@ -161,16 +162,7 @@ def run_case_inner(self, testcase: DataDrivenTestCase) -> None:
161162

162163
step += 1
163164
with chdir_manager('..'):
164-
for op in operations:
165-
if isinstance(op, UpdateFile):
166-
# Modify/create file
167-
copy_and_fudge_mtime(op.source_path, op.target_path)
168-
else:
169-
# Delete file
170-
try:
171-
os.remove(op.path)
172-
except FileNotFoundError:
173-
pass
165+
perform_file_operations(operations)
174166
self.run_case_step(testcase, step)
175167

176168
def run_case_step(self, testcase: DataDrivenTestCase, incremental_step: int) -> None:

test-data/unit/pep561.test

+20
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,23 @@ testTypedPkgNamespaceRegImport.py:4: error: Cannot find implementation or librar
175175
testTypedPkgNamespaceRegImport.py:4: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
176176
testTypedPkgNamespaceRegImport.py:10: error: Argument 1 has incompatible type "bool"; expected "str"
177177
testTypedPkgNamespaceRegImport.py:11: error: Argument 1 has incompatible type "int"; expected "bool"
178+
179+
-- This is really testing the test framework to make sure incremental works
180+
[case testPep561TestIncremental]
181+
# pkgs: typedpkg
182+
import a
183+
[file a.py]
184+
[file a.py.2]
185+
1 + 'no'
186+
[out]
187+
[out2]
188+
a.py:1: error: Unsupported operand types for + ("int" and "str")
189+
190+
-- Test for issue #9852, among others
191+
[case testTypedPkgNamespaceRegFromImportTwice-xfail]
192+
# pkgs: typedpkg, typedpkg_ns
193+
from typedpkg_ns import ns
194+
-- dummy should trigger a second iteration
195+
[file dummy.py.2]
196+
[out]
197+
[out2]

0 commit comments

Comments
 (0)