1 """Tests for the elpy.jedibackend module."""
8 from unittest
import mock
13 from elpy
import jedibackend
15 from elpy
.tests
import compat
16 from elpy
.tests
.support
import BackendTestCase
17 from elpy
.tests
.support
import RPCGetCompletionsTests
18 from elpy
.tests
.support
import RPCGetCompletionDocstringTests
19 from elpy
.tests
.support
import RPCGetCompletionLocationTests
20 from elpy
.tests
.support
import RPCGetDocstringTests
21 from elpy
.tests
.support
import RPCGetOnelineDocstringTests
22 from elpy
.tests
.support
import RPCGetDefinitionTests
23 from elpy
.tests
.support
import RPCGetCalltipTests
24 from elpy
.tests
.support
import RPCGetUsagesTests
25 from elpy
.tests
.support
import RPCGetNamesTests
26 from elpy
.tests
.support
import RPCGetRenameDiffTests
27 from elpy
.tests
.support
import RPCGetExtractFunctionDiffTests
28 from elpy
.tests
.support
import RPCGetExtractVariableDiffTests
29 from elpy
.tests
.support
import RPCGetInlineDiffTests
30 from elpy
.tests
.support
import RPCGetAssignmentTests
33 class JediBackendTestCase(BackendTestCase
):
35 super(JediBackendTestCase
, self
).setUp()
36 env
= jedi
.get_default_environment().path
37 self
.backend
= jedibackend
.JediBackend(self
.project_root
, env
)
40 class TestInit(JediBackendTestCase
):
41 def test_should_have_jedi_as_name(self
):
42 self
.assertEqual(self
.backend
.name
, "jedi")
45 class TestRPCGetCompletions(RPCGetCompletionsTests
,
47 BUILTINS
= ['object', 'oct', 'open', 'ord', 'OSError', 'OverflowError']
50 class TestRPCGetAssignment(RPCGetAssignmentTests
,
55 class TestRPCGetCompletionDocstring(RPCGetCompletionDocstringTests
,
60 class TestRPCGetCompletionLocation(RPCGetCompletionLocationTests
,
65 class TestRPCGetDocstring(RPCGetDocstringTests
,
68 def __init__(self
, *args
, **kwargs
):
69 super(TestRPCGetDocstring
, self
).__init
__(*args
, **kwargs
)
70 self
.JSON_LOADS_REGEX
= (
71 r
'loads\(s.*, cls.*, object_hook.*, parse_float.*, '
75 def check_docstring(self
, docstring
):
76 lines
= docstring
.splitlines()
77 self
.assertEqual(lines
[0], 'Documentation for json.loads:')
78 match
= re
.match(self
.JSON_LOADS_REGEX
, lines
[2])
79 self
.assertIsNotNone(match
)
81 @mock.patch("elpy.jedibackend.run_with_debug")
82 def test_should_not_return_empty_docstring(self
, run_with_debug
):
83 location
= mock
.MagicMock()
84 location
.full_name
= "testthing"
85 location
.docstring
.return_value
= ""
86 run_with_debug
.return_value
= [location
]
87 filename
= self
.project_file("test.py", "print")
88 docstring
= self
.backend
.rpc_get_docstring(filename
, "print", 0)
89 self
.assertIsNone(docstring
)
92 class TestRPCGetOnelineDocstring(RPCGetOnelineDocstringTests
,
95 def __init__(self
, *args
, **kwargs
):
96 super(TestRPCGetOnelineDocstring
, self
).__init
__(*args
, **kwargs
)
97 if sys
.version_info
>= (3, 6):
98 self
.JSON_LOADS_DOCSTRING
= (
99 'Deserialize ``s`` (a ``str``, ``bytes`` or'
100 ' ``bytearray`` instance containing a JSON'
101 ' document) to a Python object.'
103 self
.JSON_DOCSTRING
= (
104 "JSON (JavaScript Object Notation) <http://json.org>"
105 " is a subset of JavaScript syntax (ECMA-262"
106 " 3rd edition) used as a lightweight data interchange format.")
107 elif sys
.version_info
>= (3, 0):
108 self
.JSON_LOADS_DOCSTRING
= (
109 'Deserialize ``s`` (a ``str`` instance '
110 'containing a JSON document) to a Python object.'
112 self
.JSON_DOCSTRING
= (
113 "JSON (JavaScript Object Notation) <http://json.org>"
114 " is a subset of JavaScript syntax (ECMA-262"
115 " 3rd edition) used as a lightweight data interchange format.")
117 self
.JSON_LOADS_DOCSTRING
= (
118 'Deserialize ``s`` (a ``str`` or ``unicode`` '
119 'instance containing a JSON document) to a Python object.'
121 self
.JSON_DOCSTRING
= (
122 "JSON (JavaScript Object Notation) <http://json.org>"
123 " is a subset of JavaScript syntax (ECMA-262"
124 " 3rd edition) used as a lightweight data interchange format.")
126 @mock.patch("elpy.jedibackend.run_with_debug")
127 def test_should_not_return_empty_docstring(self
, run_with_debug
):
128 location
= mock
.MagicMock()
129 location
.full_name
= "testthing"
130 location
.docstring
.return_value
= ""
131 run_with_debug
.return_value
= [location
]
132 filename
= self
.project_file("test.py", "print")
133 docstring
= self
.backend
.rpc_get_oneline_docstring(filename
, "print", 0)
134 self
.assertIsNone(docstring
)
137 class TestRPCGetDefinition(RPCGetDefinitionTests
,
138 JediBackendTestCase
):
139 @mock.patch("jedi.Script")
140 def test_should_not_fail_if_module_path_is_none(self
, Script
):
141 """Do not fail if loc.module_path is None.
143 This can happen under some circumstances I am unsure about.
144 See #537 for the issue that reported this.
148 mock
.Mock(module_path
=None)
150 script
= Script
.return_value
151 script
.goto_definitions
.return_value
= locations
152 script
.goto_assignments
.return_value
= locations
154 location
= self
.rpc("", "", 0)
156 self
.assertIsNone(location
)
159 class TestRPCGetRenameDiff(RPCGetRenameDiffTests
,
160 JediBackendTestCase
):
164 class TestRPCGetExtractFunctionDiff(RPCGetExtractFunctionDiffTests
,
165 JediBackendTestCase
):
169 class TestRPCGetExtractVariableDiff(RPCGetExtractVariableDiffTests
,
170 JediBackendTestCase
):
174 class TestRPCGetInlineDiff(RPCGetInlineDiffTests
,
175 JediBackendTestCase
):
179 class TestRPCGetCalltip(RPCGetCalltipTests
,
180 JediBackendTestCase
):
181 KEYS_CALLTIP
= {'index': None,
184 RADIX_CALLTIP
= {'index': None,
187 ADD_CALLTIP
= {'index': 0,
188 'params': [u
'a', u
'b'],
191 THREAD_CALLTIP
= {'name': 'Thread',
193 'params': ['group: None=...',
194 'target: Optional[Callable[..., Any]]=...',
195 'name: Optional[str]=...',
196 'args: Iterable[Any]=...',
197 'kwargs: Mapping[str, Any]=...',
198 'daemon: Optional[bool]=...']}
201 THREAD_CALLTIP
= {'index': 0,
203 'params': [u
'group: None=...',
204 u
'target: Optional[Callable[..., Any]]=...',
205 u
'name: Optional[str]=...',
206 u
'args: Iterable[Any]=...',
207 u
'kwargs: Mapping[str, Any]=...']}
209 def test_should_not_fail_with_get_subscope_by_name(self
):
210 # Bug #677 / jedi#628
212 u
"my_lambda = lambda x: x+1\n"
215 filename
= self
.project_file("project.py", source
)
218 sigs
= self
.backend
.rpc_get_calltip(filename
, source
, offset
)
222 class TestRPCGetUsages(RPCGetUsagesTests
,
223 JediBackendTestCase
):
224 def test_should_not_fail_for_missing_module(self
):
225 # This causes use.module_path to be None
226 source
= "import sys\n\nsys.path.\n" # insert()"
228 filename
= self
.project_file("project.py", source
)
230 self
.rpc(filename
, source
, offset
)
233 class TestRPCGetNames(RPCGetNamesTests
,
234 JediBackendTestCase
):
238 class TestPosToLinecol(unittest
.TestCase
):
239 def test_should_handle_beginning_of_string(self
):
240 self
.assertEqual(jedibackend
.pos_to_linecol("foo", 0),
243 def test_should_handle_end_of_line(self
):
244 self
.assertEqual(jedibackend
.pos_to_linecol("foo\nbar\nbaz\nqux", 9),
247 def test_should_handle_end_of_string(self
):
248 self
.assertEqual(jedibackend
.pos_to_linecol("foo\nbar\nbaz\nqux", 14),
252 class TestLinecolToPos(unittest
.TestCase
):
253 def test_should_handle_beginning_of_string(self
):
254 self
.assertEqual(jedibackend
.linecol_to_pos("foo", 1, 0),
257 def test_should_handle_end_of_string(self
):
258 self
.assertEqual(jedibackend
.linecol_to_pos("foo\nbar\nbaz\nqux",
262 def test_should_return_offset(self
):
263 self
.assertEqual(jedibackend
.linecol_to_pos("foo\nbar\nbaz\nqux",
267 def test_should_fail_for_line_past_text(self
):
268 self
.assertRaises(ValueError,
269 jedibackend
.linecol_to_pos
, "foo\n", 3, 1)
271 def test_should_fail_for_column_past_text(self
):
272 self
.assertRaises(ValueError,
273 jedibackend
.linecol_to_pos
, "foo\n", 1, 10)
276 class TestRunWithDebug(unittest
.TestCase
):
277 @mock.patch('jedi.Script')
278 def test_should_call_method(self
, Script
):
279 Script
.return_value
.test_method
.return_value
= "test-result"
281 result
= jedibackend
.run_with_debug(jedi
, 'test_method', {}, 1, 2,
284 Script
.assert_called_with(1, 2, arg
=3)
285 self
.assertEqual(result
, 'test-result')
287 @mock.patch('jedi.Script')
288 def test_should_re_raise(self
, Script
):
289 Script
.side_effect
= RuntimeError
291 with self
.assertRaises(RuntimeError):
292 jedibackend
.run_with_debug(jedi
, 'test_method', 1, 2, arg
=3,
293 re_raise
=(RuntimeError,))
295 @mock.patch('jedi.Script')
296 @mock.patch('jedi.set_debug_function')
297 def test_should_keep_debug_info(self
, set_debug_function
, Script
):
298 Script
.side_effect
= RuntimeError
301 jedibackend
.run_with_debug(jedi
, 'test_method', {}, 1, 2, arg
=3)
302 except rpc
.Fault
as e
:
303 self
.assertGreaterEqual(e
.code
, 400)
304 self
.assertIsNotNone(e
.data
)
305 self
.assertIn("traceback", e
.data
)
306 jedi_debug_info
= e
.data
["jedi_debug_info"]
307 self
.assertIsNotNone(jedi_debug_info
)
308 self
.assertEqual(jedi_debug_info
["script_args"],
310 self
.assertEqual(jedi_debug_info
["source"], None)
311 self
.assertEqual(jedi_debug_info
["method"], "test_method")
312 self
.assertEqual(jedi_debug_info
["debug_info"], [])
314 self
.fail("Fault not thrown")
316 @mock.patch('jedi.Script')
317 @mock.patch('jedi.set_debug_function')
318 def test_should_keep_error_text(self
, set_debug_function
, Script
):
319 Script
.side_effect
= RuntimeError
322 jedibackend
.run_with_debug(jedi
, 'test_method', {}, 1, 2, arg
=3)
323 except rpc
.Fault
as e
:
324 self
.assertEqual(str(e
), str(RuntimeError()))
325 self
.assertEqual(e
.message
, str(RuntimeError()))
327 self
.fail("Fault not thrown")
329 @mock.patch('jedi.Script')
330 @mock.patch('jedi.set_debug_function')
331 def test_should_handle_source_special(self
, set_debug_function
, Script
):
332 Script
.side_effect
= RuntimeError
335 jedibackend
.run_with_debug(jedi
, 'test_method', source
="foo")
336 except rpc
.Fault
as e
:
337 self
.assertEqual(e
.data
["jedi_debug_info"]["script_args"],
339 self
.assertEqual(e
.data
["jedi_debug_info"]["source"], "foo")
341 self
.fail("Fault not thrown")
343 @mock.patch('jedi.Script')
344 @mock.patch('jedi.set_debug_function')
345 def test_should_set_debug_info(self
, set_debug_function
, Script
):
346 the_debug_function
= [None]
348 def my_set_debug_function(debug_function
, **kwargs
):
349 the_debug_function
[0] = debug_function
351 def my_script(*args
, **kwargs
):
352 the_debug_function
[0](jedi
.debug
.NOTICE
, "Notice")
353 the_debug_function
[0](jedi
.debug
.WARNING
, "Warning")
354 the_debug_function
[0]("other", "Other")
357 set_debug_function
.side_effect
= my_set_debug_function
358 Script
.return_value
.test_method
= my_script
361 jedibackend
.run_with_debug(jedi
, 'test_method', source
="foo")
362 except rpc
.Fault
as e
:
363 self
.assertEqual(e
.data
["jedi_debug_info"]["debug_info"],
368 self
.fail("Fault not thrown")
370 @mock.patch('jedi.set_debug_function')
371 @mock.patch('jedi.Script')
372 def test_should_not_fail_with_bad_data(self
, Script
, set_debug_function
):
375 def set_debug(function
, speed
=True):
376 if function
is not None:
377 function(jedi
.debug
.NOTICE
, u
"\xab")
379 set_debug_function
.side_effect
= set_debug
380 Script
.return_value
.test_method
.side_effect
= Exception
382 with self
.assertRaises(rpc
.Fault
):
383 jedibackend
.run_with_debug(jedi
, 'test_method', {}, 1, 2, arg
=3)