xonsh/tests/xontribs/test_jedi.py

198 lines
7.1 KiB
Python
Raw Normal View History

"""Tests for the Jedi completer xontrib"""
import sys
import pytest
import builtins
import importlib
from unittest.mock import MagicMock, call
from xonsh.xontribs import find_xontrib
from xonsh.completers.tools import RichCompletion
@pytest.fixture
def jedi_mock(monkeypatch):
jedi_mock = MagicMock()
jedi_mock.__version__ = '0.16.0'
jedi_mock.Interpreter().complete.return_value = []
jedi_mock.reset_mock()
monkeypatch.setitem(sys.modules, 'jedi', jedi_mock)
yield jedi_mock
@pytest.fixture
def completer_mock(monkeypatch):
completer_mock = MagicMock()
# so that args will be passed
def comp(args):
completer_mock(args)
monkeypatch.setitem(builtins.aliases, 'completer', comp)
yield completer_mock
@pytest.fixture
def jedi_xontrib(monkeypatch, source_path, jedi_mock, completer_mock):
monkeypatch.syspath_prepend(source_path)
spec = find_xontrib('jedi')
yield importlib.import_module(spec.name)
del sys.modules[spec.name]
def test_completer_added(jedi_xontrib, completer_mock):
assert completer_mock.call_args_list == [call(['remove', 'python_mode']),
call(['add', 'jedi_python',
'complete_jedi', '<python']),
call(['remove', 'python'])]
@pytest.mark.parametrize('prefix, line, start, end, ctx', [
('x', '10 + x', 5, 6, {}),
], ids='x')
@pytest.mark.parametrize('version', ['new', 'old'])
def test_jedi_api(jedi_xontrib, jedi_mock, version, prefix, line, start, end,
ctx):
if version == 'old':
jedi_mock.__version__ = '0.15.0'
jedi_mock.Interpreter().completions.return_value = []
jedi_mock.reset_mock()
jedi_xontrib.complete_jedi(prefix, line, start, end, ctx)
extra_namespace = {'__xonsh__': builtins.__xonsh__}
try:
extra_namespace['_'] = _
except NameError:
pass
namespaces = [{}, extra_namespace]
if version == 'new':
assert jedi_mock.Interpreter.call_args_list == [call(line, namespaces)]
assert jedi_mock.Interpreter().complete.call_args_list == [call(1, end)]
else:
assert jedi_mock.Interpreter.call_args_list == [
call(line, namespaces, line=1, column=end)]
assert jedi_mock.Interpreter().completions.call_args_list == [call()]
def test_multiline(jedi_xontrib, jedi_mock, monkeypatch):
shell_mock = MagicMock()
complete_document = 'xx = 1\n1 + x'
shell_mock.shell_type = 'prompt_toolkit'
shell_mock.shell.pt_completer.current_document.text = complete_document
shell_mock.shell.pt_completer.current_document.cursor_position_row = 1
shell_mock.shell.pt_completer.current_document.cursor_position_col = 5
monkeypatch.setattr(builtins.__xonsh__, 'shell', shell_mock)
jedi_xontrib.complete_jedi('x', 'x', 0, 1, {})
assert jedi_mock.Interpreter.call_args_list[0][0][0] == complete_document
assert jedi_mock.Interpreter().complete.call_args_list == [
call(2, 5) # line (one-indexed), column (zero-indexed)
]
@pytest.mark.parametrize('completion, rich_completion', [
(
# from jedi when code is 'x' and xx=3
('instance', 'xx', 'x', 'int(x=None, /) -> int',
('instance', 'instance int')),
RichCompletion('x', display='xx', description='instance int')
),
(
# from jedi when code is 'xx=3\nx'
('statement', 'xx', 'x', None, ('instance', 'instance int')),
RichCompletion('x', display='xx', description='instance int')
),
(
# from jedi when code is 'x.' and x=3
('function', 'from_bytes', 'from_bytes',
'from_bytes(bytes, byteorder, *, signed=False)',
('function', 'def __get__')),
RichCompletion('from_bytes', display='from_bytes()',
description=
'from_bytes(bytes, byteorder, *, signed=False)')
),
(
# from jedi when code is 'x=3\nx.'
('function', 'imag', 'imag', None, ('instance', 'instance int')),
RichCompletion('imag', display='imag', description='instance int')
),
(
# from '(3).from_bytes(byt'
('param', 'bytes=', 'es=', None, ('instance', 'instance Sequence')),
RichCompletion('es=', display='bytes=',
description='instance Sequence')
),
(
# from 'x.from_bytes(byt' when x=3
('param', 'bytes=', 'es=', None, None),
RichCompletion('es=', display='bytes=', description='param')
),
(
# from 'import colle'
('module', 'collections', 'ctions', None,
('module', 'module collections')),
RichCompletion('ctions', display='collections',
description='module collections')
),
(
# from 'NameErr'
('class', 'NameError', 'or', 'NameError(*args: object)',
('class', 'class NameError')),
RichCompletion('or', display='NameError',
description='NameError(*args: object)')
),
(
# from 'a["' when a={'name':None}
('string', '"name"', 'name"', None, None),
RichCompletion('name"', display='"name"', description='string')
),
(
# from 'open("/etc/pass'
('path', 'passwd"', 'wd"', None, None),
RichCompletion('wd"', display='passwd"', description='path')
),
(
# from 'cla'
('keyword', 'class', 'ss', None, None),
RichCompletion('ss', display='class', description='keyword')
),
])
def test_rich_completions(jedi_xontrib, jedi_mock, completion, rich_completion):
comp_type, comp_name, comp_complete, sig, inf = completion
comp_mock = MagicMock()
comp_mock.type = comp_type
comp_mock.name = comp_name
comp_mock.complete = comp_complete
if sig:
sig_mock = MagicMock()
sig_mock.to_string.return_value = sig
comp_mock.get_signatures.return_value = [sig_mock]
else:
comp_mock.get_signatures.return_value = []
if inf:
inf_type, inf_desc = inf
inf_mock = MagicMock()
inf_mock.type = inf_type
inf_mock.description = inf_desc
comp_mock.infer.return_value = [inf_mock]
else:
comp_mock.infer.return_value = []
jedi_xontrib.XONSH_SPECIAL_TOKENS = []
jedi_mock.Interpreter().complete.return_value = [comp_mock]
completions = jedi_xontrib.complete_jedi('', '', 0, 0, {})
assert len(completions) == 1
ret_completion, = completions
assert isinstance(ret_completion, RichCompletion)
assert ret_completion == rich_completion
assert ret_completion.display == rich_completion.display
assert ret_completion.description == rich_completion.description
def test_special_tokens(jedi_xontrib):
assert jedi_xontrib.complete_jedi('', '', 0, 0,
{}) == jedi_xontrib.XONSH_SPECIAL_TOKENS
assert jedi_xontrib.complete_jedi('@', '@', 0, 1, {}) == {'@', '@(', '@$('}
assert jedi_xontrib.complete_jedi('$', '$', 0, 1, {}) == {'$[', '${', '$('}