]>
Commit | Line | Data |
---|---|---|
1 | # coding: utf-8 | |
2 | ||
3 | """Tests for the elpy.server module""" | |
4 | ||
5 | import os | |
6 | import tempfile | |
7 | import unittest | |
8 | ||
9 | try: | |
10 | from unittest import mock | |
11 | except ImportError: | |
12 | import mock | |
13 | ||
14 | from elpy import rpc | |
15 | from elpy import server | |
16 | from elpy.tests import compat | |
17 | from elpy.tests.support import BackendTestCase | |
18 | ||
19 | ||
20 | class ServerTestCase(unittest.TestCase): | |
21 | def setUp(self): | |
22 | self.srv = server.ElpyRPCServer() | |
23 | ||
24 | ||
25 | class BackendCallTestCase(ServerTestCase): | |
26 | def assert_calls_backend(self, method, add_args=[], add_kwargs={}): | |
27 | with mock.patch("elpy.server.get_source") as get_source: | |
28 | with mock.patch.object(self.srv, "backend") as backend: | |
29 | get_source.return_value = "transformed source" | |
30 | ||
31 | getattr(self.srv, method)("filename", "source", "offset", | |
32 | *add_args, | |
33 | **add_kwargs) | |
34 | ||
35 | get_source.assert_called_with("source") | |
36 | getattr(backend, method).assert_called_with( | |
37 | "filename", "transformed source", "offset", | |
38 | *add_args, | |
39 | **add_kwargs | |
40 | ) | |
41 | ||
42 | ||
43 | class TestInit(ServerTestCase): | |
44 | def test_should_not_select_a_backend_by_default(self): | |
45 | self.assertIsNone(self.srv.backend) | |
46 | ||
47 | ||
48 | class TestRPCEcho(ServerTestCase): | |
49 | def test_should_return_arguments(self): | |
50 | self.assertEqual(("hello", "world"), | |
51 | self.srv.rpc_echo("hello", "world")) | |
52 | ||
53 | ||
54 | class TestRPCInit(ServerTestCase): | |
55 | @mock.patch("elpy.jedibackend.JediBackend") | |
56 | def test_should_set_project_root(self, JediBackend): | |
57 | self.srv.rpc_init({"project_root": "/project/root", | |
58 | "environment": "/project/env"}) | |
59 | ||
60 | self.assertEqual("/project/root", self.srv.project_root) | |
61 | ||
62 | @mock.patch("jedi.create_environment") | |
63 | def test_should_set_project_env(self, create_environment): | |
64 | self.srv.rpc_init({"project_root": "/project/root", | |
65 | "environment": "/project/env"}) | |
66 | ||
67 | create_environment.assert_called_with("/project/env", safe=False) | |
68 | ||
69 | @mock.patch("elpy.jedibackend.JediBackend") | |
70 | def test_should_initialize_jedi(self, JediBackend): | |
71 | self.srv.rpc_init({"project_root": "/project/root", | |
72 | "environment": "/project/env"}) | |
73 | ||
74 | JediBackend.assert_called_with("/project/root", "/project/env") | |
75 | ||
76 | ||
77 | @mock.patch("elpy.jedibackend.JediBackend") | |
78 | def test_should_use_jedi_if_available(self, JediBackend): | |
79 | JediBackend.return_value.name = "jedi" | |
80 | ||
81 | self.srv.rpc_init({"project_root": "/project/root", | |
82 | "environment": "/project/env"}) | |
83 | ||
84 | self.assertEqual("jedi", self.srv.backend.name) | |
85 | ||
86 | ||
87 | @mock.patch("elpy.jedibackend.JediBackend") | |
88 | def test_should_use_none_if_nothing_available( | |
89 | self, JediBackend): | |
90 | JediBackend.return_value.name = "jedi" | |
91 | old_jedi = server.jedibackend | |
92 | server.jedibackend = None | |
93 | ||
94 | try: | |
95 | self.srv.rpc_init({"project_root": "/project/root", | |
96 | "environment": "/project/env"}) | |
97 | finally: | |
98 | server.jedibackend = old_jedi | |
99 | ||
100 | self.assertIsNone(self.srv.backend) | |
101 | ||
102 | ||
103 | class TestRPCGetCalltip(BackendCallTestCase): | |
104 | def test_should_call_backend(self): | |
105 | self.assert_calls_backend("rpc_get_calltip") | |
106 | ||
107 | def test_should_handle_no_backend(self): | |
108 | self.srv.backend = None | |
109 | self.assertIsNone(self.srv.rpc_get_calltip("filname", "source", | |
110 | "offset")) | |
111 | ||
112 | ||
113 | class TestRPCGetCompletions(BackendCallTestCase): | |
114 | def test_should_call_backend(self): | |
115 | self.assert_calls_backend("rpc_get_completions") | |
116 | ||
117 | def test_should_handle_no_backend(self): | |
118 | self.srv.backend = None | |
119 | self.assertEqual([], | |
120 | self.srv.rpc_get_completions("filname", "source", | |
121 | "offset")) | |
122 | ||
123 | def test_should_sort_results(self): | |
124 | with mock.patch.object(self.srv, 'backend') as backend: | |
125 | backend.rpc_get_completions.return_value = [ | |
126 | {'name': '_e'}, | |
127 | {'name': '__d'}, | |
128 | {'name': 'c'}, | |
129 | {'name': 'B'}, | |
130 | {'name': 'a'}, | |
131 | ] | |
132 | expected = list(reversed(backend.rpc_get_completions.return_value)) | |
133 | ||
134 | actual = self.srv.rpc_get_completions("filename", "source", | |
135 | "offset") | |
136 | ||
137 | self.assertEqual(expected, actual) | |
138 | ||
139 | def test_should_uniquify_results(self): | |
140 | with mock.patch.object(self.srv, 'backend') as backend: | |
141 | backend.rpc_get_completions.return_value = [ | |
142 | {'name': 'a'}, | |
143 | {'name': 'a'}, | |
144 | ] | |
145 | expected = [{'name': 'a'}] | |
146 | ||
147 | actual = self.srv.rpc_get_completions("filename", "source", | |
148 | "offset") | |
149 | ||
150 | self.assertEqual(expected, actual) | |
151 | ||
152 | ||
153 | class TestRPCGetCompletionDocs(ServerTestCase): | |
154 | def test_should_call_backend(self): | |
155 | with mock.patch.object(self.srv, "backend") as backend: | |
156 | self.srv.rpc_get_completion_docstring("completion") | |
157 | ||
158 | (backend.rpc_get_completion_docstring | |
159 | .assert_called_with("completion")) | |
160 | ||
161 | def test_should_handle_no_backend(self): | |
162 | self.srv.backend = None | |
163 | self.assertIsNone(self.srv.rpc_get_completion_docstring("foo")) | |
164 | ||
165 | ||
166 | class TestRPCGetCompletionLocation(ServerTestCase): | |
167 | def test_should_call_backend(self): | |
168 | with mock.patch.object(self.srv, "backend") as backend: | |
169 | self.srv.rpc_get_completion_location("completion") | |
170 | ||
171 | (backend.rpc_get_completion_location | |
172 | .assert_called_with("completion")) | |
173 | ||
174 | def test_should_handle_no_backend(self): | |
175 | self.srv.backend = None | |
176 | self.assertIsNone(self.srv.rpc_get_completion_location("foo")) | |
177 | ||
178 | ||
179 | class TestRPCGetDefinition(BackendCallTestCase): | |
180 | def test_should_call_backend(self): | |
181 | self.assert_calls_backend("rpc_get_definition") | |
182 | ||
183 | def test_should_handle_no_backend(self): | |
184 | self.srv.backend = None | |
185 | self.assertIsNone(self.srv.rpc_get_definition("filname", "source", | |
186 | "offset")) | |
187 | ||
188 | ||
189 | class TestRPCGetDocstring(BackendCallTestCase): | |
190 | def test_should_call_backend(self): | |
191 | self.assert_calls_backend("rpc_get_docstring") | |
192 | ||
193 | def test_should_handle_no_backend(self): | |
194 | self.srv.backend = None | |
195 | self.assertIsNone(self.srv.rpc_get_docstring("filname", "source", | |
196 | "offset")) | |
197 | ||
198 | ||
199 | class TestRPCGetOnelineDocstring(BackendCallTestCase): | |
200 | def test_should_call_backend(self): | |
201 | self.assert_calls_backend("rpc_get_oneline_docstring") | |
202 | ||
203 | def test_should_handle_no_backend(self): | |
204 | self.srv.backend = None | |
205 | self.assertIsNone(self.srv.rpc_get_oneline_docstring("filname", | |
206 | "source", | |
207 | "offset")) | |
208 | ||
209 | ||
210 | class TestRPCGetCalltipOrOnelineDocstring(BackendCallTestCase): | |
211 | def test_should_call_backend(self): | |
212 | self.assert_calls_backend("rpc_get_calltip_or_oneline_docstring") | |
213 | ||
214 | def test_should_handle_no_backend(self): | |
215 | self.srv.backend = None | |
216 | self.assertIsNone( | |
217 | self.srv.rpc_get_calltip_or_oneline_docstring("filname", | |
218 | "source", | |
219 | "offset")) | |
220 | ||
221 | ||
222 | class TestRPCGetRenameDiff(BackendCallTestCase): | |
223 | def test_should_call_backend(self): | |
224 | self.assert_calls_backend("rpc_get_rename_diff", | |
225 | add_args=['new_name']) | |
226 | ||
227 | def test_should_handle_no_backend(self): | |
228 | self.srv.backend = None | |
229 | self.assertIsNone(self.srv.rpc_get_rename_diff("filname", "source", | |
230 | "offset", "new_name")) | |
231 | ||
232 | ||
233 | class TestRPCGetExtract_VariableDiff(BackendCallTestCase): | |
234 | def test_should_call_backend(self): | |
235 | self.assert_calls_backend("rpc_get_extract_variable_diff", | |
236 | add_args=['name', 12, 13, 3, 5]) | |
237 | ||
238 | def test_should_handle_no_backend(self): | |
239 | self.srv.backend = None | |
240 | self.assertIsNone(self.srv.rpc_get_extract_variable_diff( | |
241 | "filname", "source", "offset", "new_name", 1, 1, 0, 3)) | |
242 | ||
243 | ||
244 | class TestRPCGetExtract_FunctionDiff(BackendCallTestCase): | |
245 | def test_should_call_backend(self): | |
246 | self.assert_calls_backend("rpc_get_extract_function_diff", | |
247 | add_args=['name', 12, 13, 3, 5]) | |
248 | ||
249 | ||
250 | def test_should_handle_no_backend(self): | |
251 | self.srv.backend = None | |
252 | self.assertIsNone(self.srv.rpc_get_extract_function_diff( | |
253 | "filname", "source", "offset", "new_name", 1, 1, 0, 4)) | |
254 | ||
255 | ||
256 | class TestRPCGetInlineDiff(BackendCallTestCase): | |
257 | def test_should_call_backend(self): | |
258 | self.assert_calls_backend("rpc_get_inline_diff") | |
259 | ||
260 | def test_should_handle_no_backend(self): | |
261 | self.srv.backend = None | |
262 | self.assertIsNone(self.srv.rpc_get_inline_diff("filname", "source", | |
263 | "offset")) | |
264 | ||
265 | ||
266 | class TestRPCGetPydocCompletions(ServerTestCase): | |
267 | @mock.patch.object(server, 'get_pydoc_completions') | |
268 | def test_should_call_pydoc_completions(self, get_pydoc_completions): | |
269 | srv = server.ElpyRPCServer() | |
270 | srv.rpc_get_pydoc_completions() | |
271 | get_pydoc_completions.assert_called_with(None) | |
272 | srv.rpc_get_pydoc_completions("foo") | |
273 | get_pydoc_completions.assert_called_with("foo") | |
274 | ||
275 | ||
276 | class TestGetPydocDocumentation(ServerTestCase): | |
277 | @mock.patch("pydoc.render_doc") | |
278 | def test_should_find_documentation(self, render_doc): | |
279 | render_doc.return_value = "expected" | |
280 | ||
281 | actual = self.srv.rpc_get_pydoc_documentation("open") | |
282 | ||
283 | render_doc.assert_called_with("open", | |
284 | "Elpy Pydoc Documentation for %s", | |
285 | False) | |
286 | self.assertEqual("expected", actual) | |
287 | ||
288 | def test_should_return_none_for_unknown_module(self): | |
289 | actual = self.srv.rpc_get_pydoc_documentation("frob.open") | |
290 | ||
291 | self.assertIsNone(actual) | |
292 | ||
293 | def test_should_return_valid_unicode(self): | |
294 | import json | |
295 | ||
296 | docstring = self.srv.rpc_get_pydoc_documentation("tarfile") | |
297 | ||
298 | json.dumps(docstring) | |
299 | ||
300 | ||
301 | class TestRPCGetUsages(BackendCallTestCase): | |
302 | def test_should_call_backend(self): | |
303 | self.assert_calls_backend("rpc_get_usages") | |
304 | ||
305 | def test_should_handle_no_backend(self): | |
306 | self.srv.backend = None | |
307 | self.assertIsNone(self.srv.rpc_get_usages("filname", "source", | |
308 | "offset")) | |
309 | ||
310 | ||
311 | class TestRPCGetNames(BackendCallTestCase): | |
312 | def test_should_call_backend(self): | |
313 | self.assert_calls_backend("rpc_get_names") | |
314 | ||
315 | def test_should_handle_no_backend(self): | |
316 | self.srv.backend = None | |
317 | self.assertIsNone(self.srv.rpc_get_names("filname", "source", 0)) | |
318 | ||
319 | ||
320 | class TestGetSource(unittest.TestCase): | |
321 | def test_should_return_string_by_default(self): | |
322 | self.assertEqual(server.get_source("foo"), | |
323 | "foo") | |
324 | ||
325 | def test_should_return_file_contents(self): | |
326 | fd, filename = tempfile.mkstemp(prefix="elpy-test-") | |
327 | self.addCleanup(os.remove, filename) | |
328 | with open(filename, "w") as f: | |
329 | f.write("file contents") | |
330 | ||
331 | fileobj = {'filename': filename} | |
332 | ||
333 | self.assertEqual(server.get_source(fileobj), | |
334 | "file contents") | |
335 | ||
336 | def test_should_clean_up_tempfile(self): | |
337 | fd, filename = tempfile.mkstemp(prefix="elpy-test-") | |
338 | with open(filename, "w") as f: | |
339 | f.write("file contents") | |
340 | ||
341 | fileobj = {'filename': filename, | |
342 | 'delete_after_use': True} | |
343 | ||
344 | self.assertEqual(server.get_source(fileobj), | |
345 | "file contents") | |
346 | self.assertFalse(os.path.exists(filename)) | |
347 | ||
348 | def test_should_support_utf8(self): | |
349 | fd, filename = tempfile.mkstemp(prefix="elpy-test-") | |
350 | self.addCleanup(os.remove, filename) | |
351 | with open(filename, "wb") as f: | |
352 | f.write(u"möp".encode("utf-8")) | |
353 | ||
354 | source = server.get_source({'filename': filename}) | |
355 | ||
356 | self.assertEqual(source, u"möp") | |
357 | ||
358 | ||
359 | class TestPysymbolKey(BackendTestCase): | |
360 | def keyLess(self, a, b): | |
361 | self.assertLess(b, a) | |
362 | self.assertLess(server._pysymbol_key(a), | |
363 | server._pysymbol_key(b)) | |
364 | ||
365 | def test_should_be_case_insensitive(self): | |
366 | self.keyLess("bar", "Foo") | |
367 | ||
368 | def test_should_sort_private_symbols_after_public_symbols(self): | |
369 | self.keyLess("foo", "_bar") | |
370 | ||
371 | def test_should_sort_private_symbols_after_dunder_symbols(self): | |
372 | self.assertLess(server._pysymbol_key("__foo__"), | |
373 | server._pysymbol_key("_bar")) | |
374 | ||
375 | def test_should_sort_dunder_symbols_after_public_symbols(self): | |
376 | self.keyLess("bar", "__foo") | |
377 | ||
378 | ||
379 | class Autopep8TestCase(ServerTestCase): | |
380 | ||
381 | def test_rpc_fix_code_should_return_formatted_string(self): | |
382 | code_block = 'x= 123\n' | |
383 | new_block = self.srv.rpc_fix_code(code_block, os.getcwd()) | |
384 | self.assertEqual(new_block, 'x = 123\n') |