"""Tests for the elpy.jedibackend module."""
|
|
|
|
import sys
|
|
import unittest
|
|
|
|
import jedi
|
|
import mock
|
|
|
|
from elpy import jedibackend
|
|
from elpy import rpc
|
|
from elpy.tests import compat
|
|
from elpy.tests.support import BackendTestCase
|
|
from elpy.tests.support import RPCGetCompletionsTests
|
|
from elpy.tests.support import RPCGetCompletionDocstringTests
|
|
from elpy.tests.support import RPCGetCompletionLocationTests
|
|
from elpy.tests.support import RPCGetDocstringTests
|
|
from elpy.tests.support import RPCGetOnelineDocstringTests
|
|
from elpy.tests.support import RPCGetDefinitionTests
|
|
from elpy.tests.support import RPCGetAssignmentTests
|
|
from elpy.tests.support import RPCGetCalltipTests
|
|
from elpy.tests.support import RPCGetUsagesTests
|
|
from elpy.tests.support import RPCGetNamesTests
|
|
|
|
|
|
class JediBackendTestCase(BackendTestCase):
|
|
def setUp(self):
|
|
super(JediBackendTestCase, self).setUp()
|
|
self.backend = jedibackend.JediBackend(self.project_root)
|
|
|
|
|
|
class TestInit(JediBackendTestCase):
|
|
def test_should_have_jedi_as_name(self):
|
|
self.assertEqual(self.backend.name, "jedi")
|
|
|
|
|
|
class TestRPCGetCompletions(RPCGetCompletionsTests,
|
|
JediBackendTestCase):
|
|
BUILTINS = ['object', 'oct', 'open', 'ord', 'OSError', 'OverflowError']
|
|
|
|
|
|
class TestRPCGetCompletionDocstring(RPCGetCompletionDocstringTests,
|
|
JediBackendTestCase):
|
|
pass
|
|
|
|
|
|
class TestRPCGetCompletionLocation(RPCGetCompletionLocationTests,
|
|
JediBackendTestCase):
|
|
pass
|
|
|
|
|
|
class TestRPCGetDocstring(RPCGetDocstringTests,
|
|
JediBackendTestCase):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(TestRPCGetDocstring, self).__init__(*args, **kwargs)
|
|
if sys.version_info >= (3, 6):
|
|
self.JSON_LOADS_DOCSTRING = (
|
|
'loads(s, *, encoding=None, cls=None, object_hook=None, '
|
|
'parse_float=None, parse_int=None, parse_constant=None, '
|
|
'object_pairs_hook=None, **kw)'
|
|
)
|
|
else:
|
|
self.JSON_LOADS_DOCSTRING = (
|
|
'loads(s, encoding=None, cls=None, object_hook=None, '
|
|
'parse_float=None, parse_int=None, parse_constant=None, '
|
|
'object_pairs_hook=None, **kw)'
|
|
)
|
|
|
|
def check_docstring(self, docstring):
|
|
lines = docstring.splitlines()
|
|
self.assertEqual(lines[0], 'Documentation for json.loads:')
|
|
self.assertEqual(lines[2], self.JSON_LOADS_DOCSTRING)
|
|
|
|
@mock.patch("elpy.jedibackend.run_with_debug")
|
|
def test_should_not_return_empty_docstring(self, run_with_debug):
|
|
location = mock.MagicMock()
|
|
location.full_name = "testthing"
|
|
location.docstring.return_value = ""
|
|
run_with_debug.return_value = [location]
|
|
filename = self.project_file("test.py", "print")
|
|
docstring = self.backend.rpc_get_docstring(filename, "print", 0)
|
|
self.assertIsNone(docstring)
|
|
|
|
|
|
class TestRPCGetOnelineDocstring(RPCGetOnelineDocstringTests,
|
|
JediBackendTestCase):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(TestRPCGetOnelineDocstring, self).__init__(*args, **kwargs)
|
|
if sys.version_info >= (3, 6):
|
|
self.JSON_LOADS_DOCSTRING = (
|
|
'Deserialize ``s`` (a ``str``, ``bytes`` or'
|
|
' ``bytearray`` instance containing a JSON'
|
|
' document) to a Python object.'
|
|
)
|
|
self.JSON_DOCSTRING = (
|
|
"JSON (JavaScript Object Notation) <http://json.org>"
|
|
" is a subset of JavaScript syntax (ECMA-262"
|
|
" 3rd edition) used as a lightweight data interchange format.")
|
|
elif sys.version_info >= (3, 0):
|
|
self.JSON_LOADS_DOCSTRING = (
|
|
'Deserialize ``s`` (a ``str`` instance '
|
|
'containing a JSON document) to a Python object.'
|
|
)
|
|
self.JSON_DOCSTRING = (
|
|
"JSON (JavaScript Object Notation) <http://json.org>"
|
|
" is a subset of JavaScript syntax (ECMA-262"
|
|
" 3rd edition) used as a lightweight data interchange format.")
|
|
else:
|
|
self.JSON_LOADS_DOCSTRING = (
|
|
'Deserialize ``s`` (a ``str`` or ``unicode`` '
|
|
'instance containing a JSON document) to a Python object.'
|
|
)
|
|
self.JSON_DOCSTRING = (
|
|
"JSON (JavaScript Object Notation) <http://json.org>"
|
|
" is a subset of JavaScript syntax (ECMA-262"
|
|
" 3rd edition) used as a lightweight data interchange format.")
|
|
|
|
@mock.patch("elpy.jedibackend.run_with_debug")
|
|
def test_should_not_return_empty_docstring(self, run_with_debug):
|
|
location = mock.MagicMock()
|
|
location.full_name = "testthing"
|
|
location.docstring.return_value = ""
|
|
run_with_debug.return_value = [location]
|
|
filename = self.project_file("test.py", "print")
|
|
docstring = self.backend.rpc_get_oneline_docstring(filename, "print", 0)
|
|
self.assertIsNone(docstring)
|
|
|
|
|
|
class TestRPCGetDefinition(RPCGetDefinitionTests,
|
|
JediBackendTestCase):
|
|
@mock.patch("jedi.Script")
|
|
def test_should_not_fail_if_module_path_is_none(self, Script):
|
|
"""Do not fail if loc.module_path is None.
|
|
|
|
This can happen under some circumstances I am unsure about.
|
|
See #537 for the issue that reported this.
|
|
|
|
"""
|
|
locations = [
|
|
mock.Mock(module_path=None)
|
|
]
|
|
script = Script.return_value
|
|
script.goto_definitions.return_value = locations
|
|
script.goto_assignments.return_value = locations
|
|
|
|
location = self.rpc("", "", 0)
|
|
|
|
self.assertIsNone(location)
|
|
|
|
|
|
class TestRPCGetAssignment(RPCGetAssignmentTests,
|
|
JediBackendTestCase):
|
|
@mock.patch("jedi.Script")
|
|
def test_should_not_fail_if_module_path_is_none(self, Script):
|
|
"""Do not fail if loc.module_path is None.
|
|
|
|
"""
|
|
locations = [
|
|
mock.Mock(module_path=None)
|
|
]
|
|
script = Script.return_value
|
|
script.goto_assignments.return_value = locations
|
|
script.goto_assignments.return_value = locations
|
|
|
|
location = self.rpc("", "", 0)
|
|
|
|
self.assertIsNone(location)
|
|
|
|
|
|
class TestRPCGetCalltip(RPCGetCalltipTests,
|
|
JediBackendTestCase):
|
|
KEYS_CALLTIP = {'index': None,
|
|
'params': [],
|
|
'name': u'keys'}
|
|
RADIX_CALLTIP = {'index': None,
|
|
'params': [],
|
|
'name': u'radix'}
|
|
ADD_CALLTIP = {'index': 0,
|
|
'params': [u'a', u'b'],
|
|
'name': u'add'}
|
|
if compat.PYTHON3:
|
|
THREAD_CALLTIP = {'name': 'Thread',
|
|
'index': 0,
|
|
'params': ['group: None=...',
|
|
'target: Optional[Callable[..., Any]]=...',
|
|
'name: Optional[str]=...',
|
|
'args: Iterable=...',
|
|
'kwargs: Mapping[str, Any]=...',
|
|
'daemon: Optional[bool]=...']}
|
|
|
|
else:
|
|
THREAD_CALLTIP = {'index': 0,
|
|
'name': u'Thread',
|
|
'params': [u'group: None=...',
|
|
u'target: Optional[Callable[..., Any]]=...',
|
|
u'name: Optional[str]=...',
|
|
u'args: Iterable=...',
|
|
u'kwargs: Mapping[str, Any]=...']}
|
|
|
|
def test_should_not_fail_with_get_subscope_by_name(self):
|
|
# Bug #677 / jedi#628
|
|
source = (
|
|
u"my_lambda = lambda x: x+1\n"
|
|
u"my_lambda(1)"
|
|
)
|
|
filename = self.project_file("project.py", source)
|
|
offset = 37
|
|
|
|
sigs = self.backend.rpc_get_calltip(filename, source, offset)
|
|
sigs["index"]
|
|
|
|
|
|
class TestRPCGetUsages(RPCGetUsagesTests,
|
|
JediBackendTestCase):
|
|
def test_should_not_fail_for_missing_module(self):
|
|
# This causes use.module_path to be None
|
|
source = "import sys\n\nsys.path.\n" # insert()"
|
|
offset = 21
|
|
filename = self.project_file("project.py", source)
|
|
|
|
self.rpc(filename, source, offset)
|
|
|
|
|
|
class TestRPCGetNames(RPCGetNamesTests,
|
|
JediBackendTestCase):
|
|
pass
|
|
|
|
|
|
class TestPosToLinecol(unittest.TestCase):
|
|
def test_should_handle_beginning_of_string(self):
|
|
self.assertEqual(jedibackend.pos_to_linecol("foo", 0),
|
|
(1, 0))
|
|
|
|
def test_should_handle_end_of_line(self):
|
|
self.assertEqual(jedibackend.pos_to_linecol("foo\nbar\nbaz\nqux", 9),
|
|
(3, 1))
|
|
|
|
def test_should_handle_end_of_string(self):
|
|
self.assertEqual(jedibackend.pos_to_linecol("foo\nbar\nbaz\nqux", 14),
|
|
(4, 2))
|
|
|
|
|
|
class TestLinecolToPos(unittest.TestCase):
|
|
def test_should_handle_beginning_of_string(self):
|
|
self.assertEqual(jedibackend.linecol_to_pos("foo", 1, 0),
|
|
0)
|
|
|
|
def test_should_handle_end_of_string(self):
|
|
self.assertEqual(jedibackend.linecol_to_pos("foo\nbar\nbaz\nqux",
|
|
3, 1),
|
|
9)
|
|
|
|
def test_should_return_offset(self):
|
|
self.assertEqual(jedibackend.linecol_to_pos("foo\nbar\nbaz\nqux",
|
|
4, 2),
|
|
14)
|
|
|
|
def test_should_fail_for_line_past_text(self):
|
|
self.assertRaises(ValueError,
|
|
jedibackend.linecol_to_pos, "foo\n", 3, 1)
|
|
|
|
def test_should_fail_for_column_past_text(self):
|
|
self.assertRaises(ValueError,
|
|
jedibackend.linecol_to_pos, "foo\n", 1, 10)
|
|
|
|
|
|
class TestRunWithDebug(unittest.TestCase):
|
|
@mock.patch('jedi.Script')
|
|
def test_should_call_method(self, Script):
|
|
Script.return_value.test_method.return_value = "test-result"
|
|
|
|
result = jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3)
|
|
|
|
Script.assert_called_with(1, 2, arg=3)
|
|
self.assertEqual(result, 'test-result')
|
|
|
|
@mock.patch('jedi.Script')
|
|
def test_should_re_raise(self, Script):
|
|
Script.side_effect = RuntimeError
|
|
|
|
with self.assertRaises(RuntimeError):
|
|
jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3,
|
|
re_raise=(RuntimeError,))
|
|
|
|
@mock.patch('jedi.Script')
|
|
@mock.patch('jedi.set_debug_function')
|
|
def test_should_keep_debug_info(self, set_debug_function, Script):
|
|
Script.side_effect = RuntimeError
|
|
|
|
try:
|
|
jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3)
|
|
except rpc.Fault as e:
|
|
self.assertGreaterEqual(e.code, 400)
|
|
self.assertIsNotNone(e.data)
|
|
self.assertIn("traceback", e.data)
|
|
jedi_debug_info = e.data["jedi_debug_info"]
|
|
self.assertIsNotNone(jedi_debug_info)
|
|
self.assertEqual(jedi_debug_info["script_args"],
|
|
"1, 2, arg=3")
|
|
self.assertEqual(jedi_debug_info["source"], None)
|
|
self.assertEqual(jedi_debug_info["method"], "test_method")
|
|
self.assertEqual(jedi_debug_info["debug_info"], [])
|
|
else:
|
|
self.fail("Fault not thrown")
|
|
|
|
@mock.patch('jedi.Script')
|
|
@mock.patch('jedi.set_debug_function')
|
|
def test_should_keep_error_text(self, set_debug_function, Script):
|
|
Script.side_effect = RuntimeError
|
|
|
|
try:
|
|
jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3)
|
|
except rpc.Fault as e:
|
|
self.assertEqual(str(e), str(RuntimeError()))
|
|
self.assertEqual(e.message, str(RuntimeError()))
|
|
else:
|
|
self.fail("Fault not thrown")
|
|
|
|
@mock.patch('jedi.Script')
|
|
@mock.patch('jedi.set_debug_function')
|
|
def test_should_handle_source_special(self, set_debug_function, Script):
|
|
Script.side_effect = RuntimeError
|
|
|
|
try:
|
|
jedibackend.run_with_debug(jedi, 'test_method', source="foo")
|
|
except rpc.Fault as e:
|
|
self.assertEqual(e.data["jedi_debug_info"]["script_args"],
|
|
"source=source")
|
|
self.assertEqual(e.data["jedi_debug_info"]["source"], "foo")
|
|
else:
|
|
self.fail("Fault not thrown")
|
|
|
|
@mock.patch('jedi.Script')
|
|
@mock.patch('jedi.set_debug_function')
|
|
def test_should_set_debug_info(self, set_debug_function, Script):
|
|
the_debug_function = [None]
|
|
|
|
def my_set_debug_function(debug_function, **kwargs):
|
|
the_debug_function[0] = debug_function
|
|
|
|
def my_script(*args, **kwargs):
|
|
the_debug_function[0](jedi.debug.NOTICE, "Notice")
|
|
the_debug_function[0](jedi.debug.WARNING, "Warning")
|
|
the_debug_function[0]("other", "Other")
|
|
raise RuntimeError
|
|
|
|
set_debug_function.side_effect = my_set_debug_function
|
|
Script.return_value.test_method = my_script
|
|
|
|
try:
|
|
jedibackend.run_with_debug(jedi, 'test_method', source="foo")
|
|
except rpc.Fault as e:
|
|
self.assertEqual(e.data["jedi_debug_info"]["debug_info"],
|
|
["[N] Notice",
|
|
"[W] Warning",
|
|
"[?] Other"])
|
|
else:
|
|
self.fail("Fault not thrown")
|
|
|
|
@mock.patch('jedi.set_debug_function')
|
|
@mock.patch('jedi.Script')
|
|
def test_should_not_fail_with_bad_data(self, Script, set_debug_function):
|
|
import jedi.debug
|
|
|
|
def set_debug(function, speed=True):
|
|
if function is not None:
|
|
function(jedi.debug.NOTICE, u"\xab")
|
|
|
|
set_debug_function.side_effect = set_debug
|
|
Script.return_value.test_method.side_effect = Exception
|
|
|
|
with self.assertRaises(rpc.Fault):
|
|
jedibackend.run_with_debug(jedi, 'test_method', 1, 2, arg=3)
|