Personal emacs config
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

381 lines
13 KiB

  1. # coding: utf-8
  2. """Tests for the elpy.server module"""
  3. import os
  4. import tempfile
  5. import unittest
  6. import mock
  7. from elpy import rpc
  8. from elpy import server
  9. from elpy.tests import compat
  10. from elpy.tests.support import BackendTestCase
  11. class ServerTestCase(unittest.TestCase):
  12. def setUp(self):
  13. self.srv = server.ElpyRPCServer()
  14. class BackendCallTestCase(ServerTestCase):
  15. def assert_calls_backend(self, method, add_args=[], add_kwargs={}):
  16. with mock.patch("elpy.server.get_source") as get_source:
  17. with mock.patch.object(self.srv, "backend") as backend:
  18. get_source.return_value = "transformed source"
  19. getattr(self.srv, method)("filename", "source", "offset",
  20. *add_args,
  21. **add_kwargs)
  22. get_source.assert_called_with("source")
  23. getattr(backend, method).assert_called_with(
  24. "filename", "transformed source", "offset",
  25. *add_args,
  26. **add_kwargs
  27. )
  28. class TestInit(ServerTestCase):
  29. def test_should_not_select_a_backend_by_default(self):
  30. self.assertIsNone(self.srv.backend)
  31. class TestRPCEcho(ServerTestCase):
  32. def test_should_return_arguments(self):
  33. self.assertEqual(("hello", "world"),
  34. self.srv.rpc_echo("hello", "world"))
  35. class TestRPCInit(ServerTestCase):
  36. @mock.patch("elpy.jedibackend.JediBackend")
  37. def test_should_set_project_root(self, JediBackend):
  38. self.srv.rpc_init({"project_root": "/project/root",
  39. "environment": "/project/env"})
  40. self.assertEqual("/project/root", self.srv.project_root)
  41. @mock.patch("jedi.create_environment")
  42. def test_should_set_project_env(self, create_environment):
  43. self.srv.rpc_init({"project_root": "/project/root",
  44. "environment": "/project/env"})
  45. create_environment.assert_called_with("/project/env", safe=False)
  46. @mock.patch("elpy.jedibackend.JediBackend")
  47. def test_should_initialize_jedi(self, JediBackend):
  48. self.srv.rpc_init({"project_root": "/project/root",
  49. "environment": "/project/env"})
  50. JediBackend.assert_called_with("/project/root", "/project/env")
  51. @mock.patch("elpy.jedibackend.JediBackend")
  52. def test_should_use_jedi_if_available(self, JediBackend):
  53. JediBackend.return_value.name = "jedi"
  54. self.srv.rpc_init({"project_root": "/project/root",
  55. "environment": "/project/env"})
  56. self.assertEqual("jedi", self.srv.backend.name)
  57. @mock.patch("elpy.jedibackend.JediBackend")
  58. def test_should_use_none_if_nothing_available(
  59. self, JediBackend):
  60. JediBackend.return_value.name = "jedi"
  61. old_jedi = server.jedibackend
  62. server.jedibackend = None
  63. try:
  64. self.srv.rpc_init({"project_root": "/project/root",
  65. "environment": "/project/env"})
  66. finally:
  67. server.jedibackend = old_jedi
  68. self.assertIsNone(self.srv.backend)
  69. class TestRPCGetCalltip(BackendCallTestCase):
  70. def test_should_call_backend(self):
  71. self.assert_calls_backend("rpc_get_calltip")
  72. def test_should_handle_no_backend(self):
  73. self.srv.backend = None
  74. self.assertIsNone(self.srv.rpc_get_calltip("filname", "source",
  75. "offset"))
  76. class TestRPCGetCompletions(BackendCallTestCase):
  77. def test_should_call_backend(self):
  78. self.assert_calls_backend("rpc_get_completions")
  79. def test_should_handle_no_backend(self):
  80. self.srv.backend = None
  81. self.assertEqual([],
  82. self.srv.rpc_get_completions("filname", "source",
  83. "offset"))
  84. def test_should_sort_results(self):
  85. with mock.patch.object(self.srv, 'backend') as backend:
  86. backend.rpc_get_completions.return_value = [
  87. {'name': '_e'},
  88. {'name': '__d'},
  89. {'name': 'c'},
  90. {'name': 'B'},
  91. {'name': 'a'},
  92. ]
  93. expected = list(reversed(backend.rpc_get_completions.return_value))
  94. actual = self.srv.rpc_get_completions("filename", "source",
  95. "offset")
  96. self.assertEqual(expected, actual)
  97. def test_should_uniquify_results(self):
  98. with mock.patch.object(self.srv, 'backend') as backend:
  99. backend.rpc_get_completions.return_value = [
  100. {'name': 'a'},
  101. {'name': 'a'},
  102. ]
  103. expected = [{'name': 'a'}]
  104. actual = self.srv.rpc_get_completions("filename", "source",
  105. "offset")
  106. self.assertEqual(expected, actual)
  107. class TestRPCGetCompletionDocs(ServerTestCase):
  108. def test_should_call_backend(self):
  109. with mock.patch.object(self.srv, "backend") as backend:
  110. self.srv.rpc_get_completion_docstring("completion")
  111. (backend.rpc_get_completion_docstring
  112. .assert_called_with("completion"))
  113. def test_should_handle_no_backend(self):
  114. self.srv.backend = None
  115. self.assertIsNone(self.srv.rpc_get_completion_docstring("foo"))
  116. class TestRPCGetCompletionLocation(ServerTestCase):
  117. def test_should_call_backend(self):
  118. with mock.patch.object(self.srv, "backend") as backend:
  119. self.srv.rpc_get_completion_location("completion")
  120. (backend.rpc_get_completion_location
  121. .assert_called_with("completion"))
  122. def test_should_handle_no_backend(self):
  123. self.srv.backend = None
  124. self.assertIsNone(self.srv.rpc_get_completion_location("foo"))
  125. class TestRPCGetDefinition(BackendCallTestCase):
  126. def test_should_call_backend(self):
  127. self.assert_calls_backend("rpc_get_definition")
  128. def test_should_handle_no_backend(self):
  129. self.srv.backend = None
  130. self.assertIsNone(self.srv.rpc_get_definition("filname", "source",
  131. "offset"))
  132. class TestRPCGetDocstring(BackendCallTestCase):
  133. def test_should_call_backend(self):
  134. self.assert_calls_backend("rpc_get_docstring")
  135. def test_should_handle_no_backend(self):
  136. self.srv.backend = None
  137. self.assertIsNone(self.srv.rpc_get_docstring("filname", "source",
  138. "offset"))
  139. class TestRPCGetOnelineDocstring(BackendCallTestCase):
  140. def test_should_call_backend(self):
  141. self.assert_calls_backend("rpc_get_oneline_docstring")
  142. def test_should_handle_no_backend(self):
  143. self.srv.backend = None
  144. self.assertIsNone(self.srv.rpc_get_oneline_docstring("filname",
  145. "source",
  146. "offset"))
  147. class TestRPCGetCalltipOrOnelineDocstring(BackendCallTestCase):
  148. def test_should_call_backend(self):
  149. self.assert_calls_backend("rpc_get_calltip_or_oneline_docstring")
  150. def test_should_handle_no_backend(self):
  151. self.srv.backend = None
  152. self.assertIsNone(
  153. self.srv.rpc_get_calltip_or_oneline_docstring("filname",
  154. "source",
  155. "offset"))
  156. class TestRPCGetRenameDiff(BackendCallTestCase):
  157. def test_should_call_backend(self):
  158. self.assert_calls_backend("rpc_get_rename_diff",
  159. add_args=['new_name'])
  160. def test_should_handle_no_backend(self):
  161. self.srv.backend = None
  162. self.assertIsNone(self.srv.rpc_get_rename_diff("filname", "source",
  163. "offset", "new_name"))
  164. class TestRPCGetExtract_VariableDiff(BackendCallTestCase):
  165. def test_should_call_backend(self):
  166. self.assert_calls_backend("rpc_get_extract_variable_diff",
  167. add_args=['name', 12, 13, 3, 5])
  168. def test_should_handle_no_backend(self):
  169. self.srv.backend = None
  170. self.assertIsNone(self.srv.rpc_get_extract_variable_diff(
  171. "filname", "source", "offset", "new_name", 1, 1, 0, 3))
  172. class TestRPCGetExtract_FunctionDiff(BackendCallTestCase):
  173. def test_should_call_backend(self):
  174. self.assert_calls_backend("rpc_get_extract_function_diff",
  175. add_args=['name', 12, 13, 3, 5])
  176. def test_should_handle_no_backend(self):
  177. self.srv.backend = None
  178. self.assertIsNone(self.srv.rpc_get_extract_function_diff(
  179. "filname", "source", "offset", "new_name", 1, 1, 0, 4))
  180. class TestRPCGetInlineDiff(BackendCallTestCase):
  181. def test_should_call_backend(self):
  182. self.assert_calls_backend("rpc_get_inline_diff")
  183. def test_should_handle_no_backend(self):
  184. self.srv.backend = None
  185. self.assertIsNone(self.srv.rpc_get_inline_diff("filname", "source",
  186. "offset"))
  187. class TestRPCGetPydocCompletions(ServerTestCase):
  188. @mock.patch.object(server, 'get_pydoc_completions')
  189. def test_should_call_pydoc_completions(self, get_pydoc_completions):
  190. srv = server.ElpyRPCServer()
  191. srv.rpc_get_pydoc_completions()
  192. get_pydoc_completions.assert_called_with(None)
  193. srv.rpc_get_pydoc_completions("foo")
  194. get_pydoc_completions.assert_called_with("foo")
  195. class TestGetPydocDocumentation(ServerTestCase):
  196. @mock.patch("pydoc.render_doc")
  197. def test_should_find_documentation(self, render_doc):
  198. render_doc.return_value = "expected"
  199. actual = self.srv.rpc_get_pydoc_documentation("open")
  200. render_doc.assert_called_with("open",
  201. "Elpy Pydoc Documentation for %s",
  202. False)
  203. self.assertEqual("expected", actual)
  204. def test_should_return_none_for_unknown_module(self):
  205. actual = self.srv.rpc_get_pydoc_documentation("frob.open")
  206. self.assertIsNone(actual)
  207. def test_should_return_valid_unicode(self):
  208. import json
  209. docstring = self.srv.rpc_get_pydoc_documentation("tarfile")
  210. json.dumps(docstring)
  211. class TestRPCGetUsages(BackendCallTestCase):
  212. def test_should_call_backend(self):
  213. self.assert_calls_backend("rpc_get_usages")
  214. def test_should_handle_no_backend(self):
  215. self.srv.backend = None
  216. self.assertIsNone(self.srv.rpc_get_usages("filname", "source",
  217. "offset"))
  218. class TestRPCGetNames(BackendCallTestCase):
  219. def test_should_call_backend(self):
  220. self.assert_calls_backend("rpc_get_names")
  221. def test_should_handle_no_backend(self):
  222. self.srv.backend = None
  223. self.assertIsNone(self.srv.rpc_get_names("filname", "source", 0))
  224. class TestGetSource(unittest.TestCase):
  225. def test_should_return_string_by_default(self):
  226. self.assertEqual(server.get_source("foo"),
  227. "foo")
  228. def test_should_return_file_contents(self):
  229. fd, filename = tempfile.mkstemp(prefix="elpy-test-")
  230. self.addCleanup(os.remove, filename)
  231. with open(filename, "w") as f:
  232. f.write("file contents")
  233. fileobj = {'filename': filename}
  234. self.assertEqual(server.get_source(fileobj),
  235. "file contents")
  236. def test_should_clean_up_tempfile(self):
  237. fd, filename = tempfile.mkstemp(prefix="elpy-test-")
  238. with open(filename, "w") as f:
  239. f.write("file contents")
  240. fileobj = {'filename': filename,
  241. 'delete_after_use': True}
  242. self.assertEqual(server.get_source(fileobj),
  243. "file contents")
  244. self.assertFalse(os.path.exists(filename))
  245. def test_should_support_utf8(self):
  246. fd, filename = tempfile.mkstemp(prefix="elpy-test-")
  247. self.addCleanup(os.remove, filename)
  248. with open(filename, "wb") as f:
  249. f.write(u"möp".encode("utf-8"))
  250. source = server.get_source({'filename': filename})
  251. self.assertEqual(source, u"möp")
  252. class TestPysymbolKey(BackendTestCase):
  253. def keyLess(self, a, b):
  254. self.assertLess(b, a)
  255. self.assertLess(server._pysymbol_key(a),
  256. server._pysymbol_key(b))
  257. def test_should_be_case_insensitive(self):
  258. self.keyLess("bar", "Foo")
  259. def test_should_sort_private_symbols_after_public_symbols(self):
  260. self.keyLess("foo", "_bar")
  261. def test_should_sort_private_symbols_after_dunder_symbols(self):
  262. self.assertLess(server._pysymbol_key("__foo__"),
  263. server._pysymbol_key("_bar"))
  264. def test_should_sort_dunder_symbols_after_public_symbols(self):
  265. self.keyLess("bar", "__foo")
  266. class Autopep8TestCase(ServerTestCase):
  267. def test_rpc_fix_code_should_return_formatted_string(self):
  268. code_block = 'x= 123\n'
  269. new_block = self.srv.rpc_fix_code(code_block, os.getcwd())
  270. self.assertEqual(new_block, 'x = 123\n')