blob: c73be05e1be3faa2c6b1e8481273af209d75adec [file] [log] [blame] [edit]
-- End-to-end test cases for the daemon (dmypy).
-- These are special because they run multiple shell commands.
[case testDaemonStartStop]
$ dmypy start -- --follow-imports=error
Daemon started
$ dmypy stop
Daemon stopped
[case testDaemonBasic]
$ dmypy start -- --follow-imports=error
Daemon started
$ dmypy check -- foo.py
Success: no issues found in 1 source file
$ dmypy recheck
Success: no issues found in 1 source file
$ dmypy stop
Daemon stopped
[file foo.py]
def f(): pass
[case testDaemonRun]
$ dmypy run -- foo.py --follow-imports=error
Daemon started
Success: no issues found in 1 source file
$ dmypy stop
Daemon stopped
[file foo.py]
def f(): pass
[case testDaemonIgnoreConfigFiles]
$ dmypy start -- --follow-imports=error
Daemon started
[file mypy.ini]
\[mypy]
files = ./foo.py
[case testDaemonRunRestart]
$ dmypy run -- foo.py --follow-imports=error
Daemon started
Success: no issues found in 1 source file
$ dmypy run -- foo.py --follow-imports=error
Success: no issues found in 1 source file
$ {python} -c "print('[mypy]')" >mypy.ini
$ {python} -c "print('disallow_untyped_defs = True')" >>mypy.ini
$ dmypy run -- foo.py --follow-imports=error
Restarting: configuration changed
Daemon stopped
Daemon started
foo.py:1: error: Function is missing a return type annotation
foo.py:1: note: Use "-> None" if function does not return a value
Found 1 error in 1 file (checked 1 source file)
== Return code: 1
$ {python} -c "print('def f() -> None: pass')" >foo.py
$ dmypy run -- foo.py --follow-imports=error
Success: no issues found in 1 source file
$ dmypy stop
Daemon stopped
[file foo.py]
def f(): pass
[case testDaemonRunRestartPretty]
$ dmypy run -- foo.py --follow-imports=error --pretty
Daemon started
Success: no issues found in 1 source file
$ dmypy run -- foo.py --follow-imports=error --pretty
Success: no issues found in 1 source file
$ {python} -c "print('[mypy]')" >mypy.ini
$ {python} -c "print('disallow_untyped_defs = True')" >>mypy.ini
$ dmypy run -- foo.py --follow-imports=error --pretty
Restarting: configuration changed
Daemon stopped
Daemon started
foo.py:1: error: Function is missing a return type annotation
def f(): pass
^
foo.py:1: note: Use "-> None" if function does not return a value
Found 1 error in 1 file (checked 1 source file)
== Return code: 1
$ {python} -c "print('def f() -> None: pass')" >foo.py
$ dmypy run -- foo.py --follow-imports=error --pretty
Success: no issues found in 1 source file
$ dmypy stop
Daemon stopped
[file foo.py]
def f(): pass
[case testDaemonRunRestartPluginVersion]
$ dmypy run -- foo.py --no-error-summary
Daemon started
$ {python} -c "print(' ')" >> plug.py
$ dmypy run -- foo.py --no-error-summary
Restarting: plugins changed
Daemon stopped
Daemon started
$ dmypy stop
Daemon stopped
[file mypy.ini]
\[mypy]
follow_imports = error
plugins = plug.py
[file foo.py]
pass
[file plug.py]
from mypy.plugin import Plugin
class Dummy(Plugin): pass
def plugin(version): return Dummy
[case testDaemonRunRestartGlobs]
-- Ensure dmypy is not restarted if the configuration doesn't change and it contains globs
-- Note: Backslash path separator in output is replaced with forward slash so the same test succeeds on Windows as well
$ dmypy run -- foo --follow-imports=error --python-version=3.6
Daemon started
foo/lol.py:1: error: Name "fail" is not defined
Found 1 error in 1 file (checked 3 source files)
== Return code: 1
$ dmypy run -- foo --follow-imports=error --python-version=3.6
foo/lol.py:1: error: Name "fail" is not defined
Found 1 error in 1 file (checked 3 source files)
== Return code: 1
$ {python} -c "print('[mypy]')" >mypy.ini
$ {python} -c "print('ignore_errors=True')" >>mypy.ini
$ dmypy run -- foo --follow-imports=error --python-version=3.6
Restarting: configuration changed
Daemon stopped
Daemon started
Success: no issues found in 3 source files
$ dmypy stop
Daemon stopped
[file mypy.ini]
\[mypy]
ignore_errors = True
\[mypy-*.lol]
ignore_errors = False
[file foo/__init__.py]
[file foo/lol.py]
fail
[file foo/ok.py]
a: int = 1
[case testDaemonStatusKillRestartRecheck]
$ dmypy status
No status file found
== Return code: 2
$ dmypy stop
No status file found
== Return code: 2
$ dmypy kill
No status file found
== Return code: 2
$ dmypy recheck
No status file found
== Return code: 2
$ dmypy start -- --follow-imports=error --no-error-summary
Daemon started
$ dmypy status
Daemon is up and running
$ dmypy start
Daemon is still alive
== Return code: 2
$ dmypy restart -- --follow-imports=error --no-error-summary
Daemon stopped
Daemon started
$ dmypy stop
Daemon stopped
$ dmypy status
No status file found
== Return code: 2
$ dmypy restart -- --follow-imports=error --no-error-summary
Daemon started
$ dmypy recheck
Command 'recheck' is only valid after a 'check' command
== Return code: 2
$ dmypy kill
Daemon killed
$ dmypy status
Daemon has died
== Return code: 2
[case testDaemonRecheck]
$ dmypy start -- --follow-imports=error --no-error-summary
Daemon started
$ dmypy check foo.py bar.py
$ dmypy recheck
$ dmypy recheck --update foo.py --remove bar.py sir_not_appearing_in_this_film.py
foo.py:1: error: Import of "bar" ignored
foo.py:1: note: (Using --follow-imports=error, module not passed on command line)
== Return code: 1
$ dmypy recheck --update bar.py
$ dmypy recheck --update sir_not_appearing_in_this_film.py
$ dmypy recheck --update --remove
$ dmypy stop
Daemon stopped
[file foo.py]
import bar
[file bar.py]
pass
[case testDaemonTimeout]
$ dmypy start --timeout 1 -- --follow-imports=error
Daemon started
$ {python} -c "import time;time.sleep(1)"
$ dmypy status
No status file found
== Return code: 2
[case testDaemonRunNoTarget]
$ dmypy run -- --follow-imports=error
Daemon started
mypy-daemon: error: Missing target module, package, files, or command.
== Return code: 2
$ dmypy stop
Daemon stopped
-- this is carefully constructed to be able to break if the quickstart system lets
-- something through incorrectly. in particular, the files need to have the same size
[case testDaemonQuickstart]
$ {python} -c "print('x=1')" >foo.py
$ {python} -c "print('x=1')" >bar.py
$ mypy --local-partial-types --cache-fine-grained --follow-imports=error --no-sqlite-cache --python-version=3.6 -- foo.py bar.py
Success: no issues found in 2 source files
$ {python} -c "import shutil; shutil.copy('.mypy_cache/3.6/bar.meta.json', 'asdf.json')"
-- update bar's timestamp but don't change the file
$ {python} -c "import time;time.sleep(1)"
$ {python} -c "print('x=1')" >bar.py
$ dmypy run -- foo.py bar.py --follow-imports=error --use-fine-grained-cache --no-sqlite-cache --python-version=3.6
Daemon started
Success: no issues found in 2 source files
$ dmypy status --fswatcher-dump-file test.json
Daemon is up and running
$ dmypy stop
Daemon stopped
-- copy the original bar cache file back so that the mtime mismatches
$ {python} -c "import shutil; shutil.copy('asdf.json', '.mypy_cache/3.6/bar.meta.json')"
-- sleep guarantees timestamp changes
$ {python} -c "import time;time.sleep(1)"
$ {python} -c "print('lol')" >foo.py
$ dmypy run --log-file=log -- foo.py bar.py --follow-imports=error --use-fine-grained-cache --no-sqlite-cache --python-version=3.6 --quickstart-file test.json
Daemon started
foo.py:1: error: Name "lol" is not defined
Found 1 error in 1 file (checked 2 source files)
== Return code: 1
-- make sure no errors made it to the log file
$ {python} -c "import sys; sys.stdout.write(open('log').read())"
-- make sure the meta file didn't get updated. we use this as an imperfect proxy for
-- whether the source file got rehashed, which we don't want it to have been.
$ {python} -c "x = open('.mypy_cache/3.6/bar.meta.json').read(); y = open('asdf.json').read(); assert x == y"
[case testDaemonSuggest]
$ dmypy start --log-file log.txt -- --follow-imports=error --no-error-summary
Daemon started
$ dmypy suggest foo:foo
Command 'suggest' is only valid after a 'check' command (that produces no parse errors)
== Return code: 2
$ dmypy check foo.py bar.py
$ dmypy suggest foo.bar
Unknown function foo.bar
== Return code: 2
$ dmypy suggest foo.var
Object foo.var is not a function
== Return code: 2
$ dmypy suggest foo.Foo.var
Unknown class foo.Foo
== Return code: 2
$ dmypy suggest foo.Bar.baz
Unknown method foo.Bar.baz
== Return code: 2
$ dmypy suggest foo.foo.baz
Object foo.foo is not a class
== Return code: 2
$ dmypy suggest --callsites foo.foo
bar.py:3: (str)
bar.py:4: (arg=str)
$ dmypy suggest foo.foo
(str) -> int
$ {python} -c "import shutil; shutil.copy('foo2.py', 'foo.py')"
$ dmypy check foo.py bar.py
bar.py:3: error: Incompatible types in assignment (expression has type "int", variable has type "str")
== Return code: 1
[file foo.py]
def foo(arg):
return 12
class Bar:
def bar(self): pass
var = 0
[file foo2.py]
def foo(arg: str) -> int:
return 12
class Bar:
def bar(self) -> None: pass
var = 0
[file bar.py]
from foo import foo
def bar() -> None:
x = foo('abc') # type: str
foo(arg='xyz')