134 lines
4.6 KiB
Python
134 lines
4.6 KiB
Python
|
"""End-to-end test cases for the daemon (dmypy).
|
||
|
|
||
|
These are special because they run multiple shell commands.
|
||
|
|
||
|
This also includes some unit tests.
|
||
|
"""
|
||
|
|
||
|
import os
|
||
|
import subprocess
|
||
|
import sys
|
||
|
import tempfile
|
||
|
import unittest
|
||
|
from typing import List, Tuple
|
||
|
|
||
|
from mypy.modulefinder import SearchPaths
|
||
|
from mypy.fscache import FileSystemCache
|
||
|
from mypy.dmypy_server import filter_out_missing_top_level_packages
|
||
|
|
||
|
from mypy.test.config import test_temp_dir, PREFIX
|
||
|
from mypy.test.data import DataDrivenTestCase, DataSuite
|
||
|
from mypy.test.helpers import assert_string_arrays_equal, normalize_error_messages
|
||
|
|
||
|
# Files containing test cases descriptions.
|
||
|
daemon_files = [
|
||
|
'daemon.test',
|
||
|
]
|
||
|
|
||
|
|
||
|
class DaemonSuite(DataSuite):
|
||
|
files = daemon_files
|
||
|
|
||
|
def run_case(self, testcase: DataDrivenTestCase) -> None:
|
||
|
try:
|
||
|
test_daemon(testcase)
|
||
|
finally:
|
||
|
# Kill the daemon if it's still running.
|
||
|
run_cmd('dmypy kill')
|
||
|
|
||
|
|
||
|
def test_daemon(testcase: DataDrivenTestCase) -> None:
|
||
|
assert testcase.old_cwd is not None, "test was not properly set up"
|
||
|
for i, step in enumerate(parse_script(testcase.input)):
|
||
|
cmd = step[0]
|
||
|
expected_lines = step[1:]
|
||
|
assert cmd.startswith('$')
|
||
|
cmd = cmd[1:].strip()
|
||
|
cmd = cmd.replace('{python}', sys.executable)
|
||
|
sts, output = run_cmd(cmd)
|
||
|
output_lines = output.splitlines()
|
||
|
output_lines = normalize_error_messages(output_lines)
|
||
|
if sts:
|
||
|
output_lines.append('== Return code: %d' % sts)
|
||
|
assert_string_arrays_equal(expected_lines,
|
||
|
output_lines,
|
||
|
"Command %d (%s) did not give expected output" %
|
||
|
(i + 1, cmd))
|
||
|
|
||
|
|
||
|
def parse_script(input: List[str]) -> List[List[str]]:
|
||
|
"""Parse testcase.input into steps.
|
||
|
|
||
|
Each command starts with a line starting with '$'.
|
||
|
The first line (less '$') is sent to the shell.
|
||
|
The remaining lines are expected output.
|
||
|
"""
|
||
|
steps = []
|
||
|
step: List[str] = []
|
||
|
for line in input:
|
||
|
if line.startswith('$'):
|
||
|
if step:
|
||
|
assert step[0].startswith('$')
|
||
|
steps.append(step)
|
||
|
step = []
|
||
|
step.append(line)
|
||
|
if step:
|
||
|
steps.append(step)
|
||
|
return steps
|
||
|
|
||
|
|
||
|
def run_cmd(input: str) -> Tuple[int, str]:
|
||
|
if input.startswith('dmypy '):
|
||
|
input = sys.executable + ' -m mypy.' + input
|
||
|
if input.startswith('mypy '):
|
||
|
input = sys.executable + ' -m' + input
|
||
|
env = os.environ.copy()
|
||
|
env['PYTHONPATH'] = PREFIX
|
||
|
try:
|
||
|
output = subprocess.check_output(input,
|
||
|
shell=True,
|
||
|
stderr=subprocess.STDOUT,
|
||
|
universal_newlines=True,
|
||
|
cwd=test_temp_dir,
|
||
|
env=env)
|
||
|
return 0, output
|
||
|
except subprocess.CalledProcessError as err:
|
||
|
return err.returncode, err.output
|
||
|
|
||
|
|
||
|
class DaemonUtilitySuite(unittest.TestCase):
|
||
|
"""Unit tests for helpers"""
|
||
|
|
||
|
def test_filter_out_missing_top_level_packages(self) -> None:
|
||
|
with tempfile.TemporaryDirectory() as td:
|
||
|
self.make_file(td, 'base/a/')
|
||
|
self.make_file(td, 'base/b.py')
|
||
|
self.make_file(td, 'base/c.pyi')
|
||
|
self.make_file(td, 'base/missing.txt')
|
||
|
self.make_file(td, 'typeshed/d.pyi')
|
||
|
self.make_file(td, 'typeshed/@python2/e')
|
||
|
self.make_file(td, 'pkg1/f-stubs')
|
||
|
self.make_file(td, 'pkg2/g-python2-stubs')
|
||
|
self.make_file(td, 'mpath/sub/long_name/')
|
||
|
|
||
|
def makepath(p: str) -> str:
|
||
|
return os.path.join(td, p)
|
||
|
|
||
|
search = SearchPaths(python_path=(makepath('base'),),
|
||
|
mypy_path=(makepath('mpath/sub'),),
|
||
|
package_path=(makepath('pkg1'), makepath('pkg2')),
|
||
|
typeshed_path=(makepath('typeshed'),))
|
||
|
fscache = FileSystemCache()
|
||
|
res = filter_out_missing_top_level_packages(
|
||
|
{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'long_name', 'ff', 'missing'},
|
||
|
search,
|
||
|
fscache)
|
||
|
assert res == {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'long_name'}
|
||
|
|
||
|
def make_file(self, base: str, path: str) -> None:
|
||
|
fullpath = os.path.join(base, path)
|
||
|
os.makedirs(os.path.dirname(fullpath), exist_ok=True)
|
||
|
if not path.endswith('/'):
|
||
|
with open(fullpath, 'w') as f:
|
||
|
f.write('# test file')
|