]> crepu.dev Git - config.git/blob - djavu-asus/emacs/elpa/elpy-20230803.1455/elpy/tests/support.py
e2cf687a8d6781a9673bda52e88dfc8e734bddf0
[config.git] / djavu-asus / emacs / elpa / elpy-20230803.1455 / elpy / tests / support.py
1 # coding: utf-8
2
3 """Support classes and functions for the elpy test code.
4
5 Elpy uses a bit of a peculiar test setup to avoid redundancy. For the
6 tests of the two backends, we provide generic test cases for generic
7 tests and for specific callback tests.
8
9 These mixins can be included in the actual test classes. We can't add
10 these tests to a BackendTestCase subclass directly because the test
11 discovery would find them there and try to run them, which would fail.
12
13 """
14
15 import os
16 import re
17 import shutil
18 import sys
19 import tempfile
20 import unittest
21
22 from elpy import jedibackend
23 from elpy.rpc import Fault
24 from elpy.tests import compat
25
26 if jedibackend.JEDISUP18:
27 import pathlib
28
29
30 class BackendTestCase(unittest.TestCase):
31 """Base class for backend tests.
32
33 This class sets up a project root directory and provides an easy
34 way to create files within the project root.
35
36 """
37
38 def setUp(self):
39 """Create the project root and make sure it gets cleaned up."""
40 super(BackendTestCase, self).setUp()
41 self.project_root = tempfile.mkdtemp(prefix="elpy-test")
42 self.addCleanup(shutil.rmtree, self.project_root, True)
43
44 def project_file(self, relname, contents):
45 """Create a file named relname within the project root.
46
47 Write contents into that file.
48
49 """
50 full_name = os.path.join(self.project_root, relname)
51 try:
52 os.makedirs(os.path.dirname(full_name))
53 except OSError:
54 pass
55 if compat.PYTHON3:
56 fobj = open(full_name, "w", encoding="utf-8")
57 else:
58 fobj = open(full_name, "w")
59 with fobj as f:
60 f.write(contents)
61 # return full_name
62 if jedibackend.JEDISUP18:
63 return pathlib.Path(full_name)
64 else:
65 return full_name
66
67
68 class GenericRPCTests(object):
69 """Generic RPC test methods.
70
71 This is a mixin to add tests that should be run for all RPC
72 methods that follow the generic (filename, source, offset) calling
73 conventions.
74
75 """
76 METHOD = None
77
78 def rpc(self, filename, source, offset):
79 method = getattr(self.backend, self.METHOD)
80 return method(filename, source, offset)
81
82 def test_should_not_fail_on_inexisting_file(self):
83 filename = self.project_root + "/doesnotexist.py"
84 self.rpc(filename, "", 0)
85
86 def test_should_not_fail_on_empty_file(self):
87 filename = self.project_file("test.py", "")
88 self.rpc(filename, "", 0)
89
90 def test_should_not_fail_if_file_is_none(self):
91 self.rpc(None, "", 0)
92
93 def test_should_not_fail_for_module_syntax_errors(self):
94 source, offset = source_and_offset(
95 "class Foo(object):\n"
96 " def bar(self):\n"
97 " foo(_|_"
98 " bar("
99 "\n"
100 " def a(self):\n"
101 " pass\n"
102 "\n"
103 " def b(self):\n"
104 " pass\n"
105 "\n"
106 " def b(self):\n"
107 " pass\n"
108 "\n"
109 " def b(self):\n"
110 " pass\n"
111 "\n"
112 " def b(self):\n"
113 " pass\n"
114 "\n"
115 " def b(self):\n"
116 " pass\n"
117 )
118 filename = self.project_file("test.py", source)
119
120 self.rpc(filename, source, offset)
121
122 def test_should_not_fail_for_bad_indentation(self):
123 source, offset = source_and_offset(
124 "def foo():\n"
125 " print(23)_|_\n"
126 " print(17)\n")
127 filename = self.project_file("test.py", source)
128
129 self.rpc(filename, source, offset)
130
131 # @unittest.skipIf((3, 3) <= sys.version_info < (3, 4),
132 # "Bug in jedi for Python 3.3")
133 def test_should_not_fail_for_relative_import(self):
134 source, offset = source_and_offset(
135 "from .. import foo_|_"
136 )
137 filename = self.project_file("test.py", source)
138
139 self.rpc(filename, source, offset)
140
141 def test_should_not_fail_on_keyword(self):
142 source, offset = source_and_offset(
143 "_|_try:\n"
144 " pass\n"
145 "except:\n"
146 " pass\n")
147 filename = self.project_file("test.py", source)
148
149 self.rpc(filename, source, offset)
150
151 def test_should_not_fail_with_bad_encoding(self):
152 source, offset = source_and_offset(
153 u'# coding: utf-8X_|_\n'
154 )
155 filename = self.project_file("test.py", source)
156
157 self.rpc(filename, source, offset)
158
159 def test_should_not_fail_with_form_feed_characters(self):
160 # Bug in Jedi: jedi#424
161 source, offset = source_and_offset(
162 "\f\n"
163 "class Test(object):_|_\n"
164 " pass"
165 )
166 filename = self.project_file("test.py", source)
167
168 self.rpc(filename, source, offset)
169
170 def test_should_not_fail_for_dictionaries_in_weird_places(self):
171 # Bug in Jedi: jedi#417
172 source, offset = source_and_offset(
173 "import json\n"
174 "\n"
175 "def foo():\n"
176 " json.loads(_|_\n"
177 "\n"
178 " json.load.return_value = {'foo': [],\n"
179 " 'bar': True}\n"
180 "\n"
181 " c = Foo()\n"
182 )
183 filename = self.project_file("test.py", source)
184
185 self.rpc(filename, source, offset)
186
187 def test_should_not_break_with_binary_characters_in_docstring(self):
188 # Bug in Jedi: jedi#427
189 template = '''\
190 class Foo(object):
191 def __init__(self):
192 """
193 COMMUNITY instance that this conversion belongs to.
194 DISPERSY_VERSION is the dispersy conversion identifier (on the wire version; must be one byte).
195 COMMUNIY_VERSION is the community conversion identifier (on the wire version; must be one byte).
196
197 COMMUNIY_VERSION may not be '\\x00' or '\\xff'. '\\x00' is used by the DefaultConversion until
198 a proper conversion instance can be made for the Community. '\\xff' is reserved for when
199 more than one byte is needed as a version indicator.
200 """
201 pass
202
203
204 x = Foo()
205 x._|_
206 '''
207 source, offset = source_and_offset(template)
208 filename = self.project_file("test.py", source)
209
210 self.rpc(filename, source, offset)
211
212 def test_should_not_fail_for_def_without_name(self):
213 # Bug jedi#429
214 source, offset = source_and_offset(
215 "def_|_():\n"
216 " if True:\n"
217 " return True\n"
218 " else:\n"
219 " return False\n"
220 )
221 filename = self.project_file("project.py", source)
222
223 self.rpc(filename, source, offset)
224
225 def test_should_not_fail_on_lambda(self):
226 # Bug #272 / jedi#431, jedi#572
227 source, offset = source_and_offset(
228 "map(lambda_|_"
229 )
230 filename = self.project_file("project.py", source)
231
232 self.rpc(filename, source, offset)
233
234 def test_should_not_fail_on_literals(self):
235 # Bug #314, #344 / jedi#466
236 source = u'lit = u"""\\\n# -*- coding: utf-8 -*-\n"""\n'
237 offset = 0
238 filename = self.project_file("project.py", source)
239
240 self.rpc(filename, source, offset)
241
242 def test_should_not_fail_with_args_as_args(self):
243 # Bug #347 in rope_py3k
244 source, offset = source_and_offset(
245 "def my_function(*args):\n"
246 " ret_|_"
247 )
248 filename = self.project_file("project.py", source)
249
250 self.rpc(filename, source, offset)
251
252 def test_should_not_fail_for_unicode_chars_in_string(self):
253 # Bug #358 / jedi#482
254 source = '''\
255 # coding: utf-8
256
257 logging.info(u"Saving «{}»...".format(title))
258 requests.get(u"https://web.archive.org/save/{}".format(url))
259 '''
260 offset = 57
261 filename = self.project_file("project.py", source)
262
263 self.rpc(filename, source, offset)
264
265 def test_should_not_fail_for_bad_escape_sequence(self):
266 # Bug #360 / jedi#485
267 source = r"v = '\x'"
268 offset = 8
269 filename = self.project_file("project.py", source)
270
271 self.rpc(filename, source, offset)
272
273 def test_should_not_fail_for_coding_declarations_in_strings(self):
274 # Bug #314 / jedi#465 / python#22221
275 source = u'lit = """\\\n# -*- coding: utf-8 -*-\n"""'
276 offset = 8
277 filename = self.project_file("project.py", source)
278
279 self.rpc(filename, source, offset)
280
281 def test_should_not_fail_if_root_vanishes(self):
282 # Bug #353
283 source, offset = source_and_offset(
284 "import foo\n"
285 "foo._|_"
286 )
287 filename = self.project_file("project.py", source)
288 shutil.rmtree(self.project_root)
289
290 self.rpc(filename, source, offset)
291
292 # For some reason, this breaks a lot of other tests. Couldn't
293 # figure out why.
294 #
295 # def test_should_not_fail_for_sys_path(self):
296 # # Bug #365 / jedi#486
297 # source, offset = source_and_offset(
298 # "import sys\n"
299 # "\n"
300 # "sys.path.index(_|_\n"
301 # )
302 # filename = self.project_file("project.py", source)
303 #
304 # self.rpc(filename, source, offset)
305
306 def test_should_not_fail_for_key_error(self):
307 # Bug #561, #564, #570, #588, #593, #599 / jedi#572, jedi#579,
308 # jedi#590
309 source, offset = source_and_offset(
310 "map(lambda_|_"
311 )
312 filename = self.project_file("project.py", source)
313
314 self.rpc(filename, source, offset)
315
316 def test_should_not_fail_for_badly_defined_global_variable(self):
317 # Bug #519 / jedi#610
318 source, offset = source_and_offset(
319 """\
320 def funct1():
321 global global_dict_var
322 global_dict_var = dict()
323
324 def funct2():
325 global global_dict_var
326 q = global_dict_var.copy_|_()
327 print(q)""")
328 filename = self.project_file("project.py", source)
329
330 self.rpc(filename, source, offset)
331
332 def test_should_not_fail_with_mergednamesdict(self):
333 # Bug #563 / jedi#589
334 source, offset = source_and_offset(
335 u'from email import message_|_'
336 )
337 filename = self.project_file("project.py", source)
338
339 self.rpc(filename, source, offset)
340
341
342 class RPCGetCompletionsTests(GenericRPCTests):
343 METHOD = "rpc_get_completions"
344
345 def test_should_complete_builtin(self):
346 source, offset = source_and_offset("o_|_")
347
348 expected = self.BUILTINS
349 actual = [cand['name'] for cand in
350 self.backend.rpc_get_completions("test.py",
351 source, offset)]
352
353 for candidate in expected:
354 self.assertIn(candidate, actual)
355
356 if sys.version_info >= (3, 5) or sys.version_info < (3, 0):
357 JSON_COMPLETIONS = ["SONDecoder", "SONEncoder", "SONDecodeError"]
358 else:
359 JSON_COMPLETIONS = ["SONDecoder", "SONEncoder"]
360
361 def test_should_complete_imports(self):
362 source, offset = source_and_offset("import json\n"
363 "json.J_|_")
364 filename = self.project_file("test.py", source)
365 completions = self.backend.rpc_get_completions(filename,
366 source,
367 offset)
368 self.assertEqual(
369 sorted([cand['suffix'] for cand in completions]),
370 sorted(self.JSON_COMPLETIONS))
371
372 def test_should_complete_top_level_modules_for_import(self):
373 source, offset = source_and_offset("import multi_|_")
374 filename = self.project_file("test.py", source)
375 completions = self.backend.rpc_get_completions(filename,
376 source,
377 offset)
378 if compat.PYTHON3:
379 expected = ["processing"]
380 else:
381 expected = ["file", "processing"]
382 self.assertEqual(sorted([cand['suffix'] for cand in completions]),
383 sorted(expected))
384
385 def test_should_complete_packages_for_import(self):
386 source, offset = source_and_offset("import email.mi_|_")
387 filename = self.project_file("test.py", source)
388 completions = self.backend.rpc_get_completions(filename,
389 source,
390 offset)
391 if sys.version_info < (3, 0):
392 compl = [u'me', u'METext']
393 else:
394 compl = ['me']
395 self.assertEqual([cand['suffix'] for cand in completions],
396 compl)
397
398 def test_should_not_complete_for_import(self):
399 source, offset = source_and_offset("import foo.Conf_|_")
400 filename = self.project_file("test.py", source)
401 completions = self.backend.rpc_get_completions(filename,
402 source,
403 offset)
404 self.assertEqual([cand['suffix'] for cand in completions],
405 [])
406
407 # @unittest.skipIf((3, 3) <= sys.version_info < (3, 4),
408 # "Bug in jedi for Python 3.3")
409 def test_should_not_fail_for_short_module(self):
410 source, offset = source_and_offset("from .. import foo_|_")
411 filename = self.project_file("test.py", source)
412 completions = self.backend.rpc_get_completions(filename,
413 source,
414 offset)
415 self.assertIsNotNone(completions)
416
417 def test_should_complete_sys(self):
418 source, offset = source_and_offset("import sys\nsys._|_")
419 filename = self.project_file("test.py", source)
420 completions = self.backend.rpc_get_completions(filename,
421 source,
422 offset)
423 self.assertIn('path', [cand['suffix'] for cand in completions])
424
425 def test_should_find_with_trailing_text(self):
426 source, offset = source_and_offset(
427 "import threading\nthreading.T_|_mumble mumble")
428
429 expected = ["Thread", "ThreadError", "Timer"]
430 actual = [cand['name'] for cand in
431 self.backend.rpc_get_completions("test.py", source, offset)]
432
433 for candidate in expected:
434 self.assertIn(candidate, actual)
435
436 def test_should_find_completion_different_package(self):
437 # See issue #74
438 self.project_file("project/__init__.py", "")
439 source1 = ("class Add:\n"
440 " def add(self, a, b):\n"
441 " return a + b\n")
442 self.project_file("project/add.py", source1)
443 source2, offset = source_and_offset(
444 "from project.add import Add\n"
445 "class Calculator:\n"
446 " def add(self, a, b):\n"
447 " c = Add()\n"
448 " c.ad_|_\n")
449 file2 = self.project_file("project/calculator.py", source2)
450 proposals = self.backend.rpc_get_completions(file2,
451 source2,
452 offset)
453 self.assertEqual(["add"],
454 [proposal["name"] for proposal in proposals])
455
456 def test_should_return_nothing_when_no_completion(self):
457 source, offset = source_and_offset("nothingcancompletethis_|_")
458 self.assertEqual([], self.backend.rpc_get_completions("test.py",
459 source, offset))
460
461
462 class RPCGetCompletionDocstringTests(object):
463 def test_should_return_docstring(self):
464 source, offset = source_and_offset("import json\n"
465 "json.JSONEnc_|_")
466 filename = self.project_file("test.py", source)
467 completions = self.backend.rpc_get_completions(filename,
468 source,
469 offset)
470 completions.sort(key=lambda p: p["name"])
471 prop = completions[0]
472 self.assertEqual(prop["name"], "JSONEncoder")
473
474 docs = self.backend.rpc_get_completion_docstring("JSONEncoder")
475
476 self.assertIn("Extensible JSON", docs)
477
478 def test_should_return_none_if_unknown(self):
479 docs = self.backend.rpc_get_completion_docstring("Foo")
480
481 self.assertIsNone(docs)
482
483 def test_should_return_none_if_on_a_builtin(self):
484 source, offset = source_and_offset("a = 12\n"
485 "print(12_|_)")
486 filename = self.project_file("test.py", source)
487 completions = self.backend.rpc_get_docstring(filename,
488 source,
489 offset)
490 self.assertIsNone(completions)
491
492
493 class RPCGetCompletionLocationTests(object):
494 def test_should_return_location(self):
495 source, offset = source_and_offset("donaudampfschiff = 1\n"
496 "donau_|_")
497 filename = self.project_file("test.py", source)
498 completions = self.backend.rpc_get_completions(filename,
499 source,
500 offset)
501 prop = completions[0]
502 self.assertEqual(prop["name"], "donaudampfschiff")
503
504 loc = self.backend.rpc_get_completion_location("donaudampfschiff")
505
506 self.assertEqual((filename, 1), loc)
507
508 def test_should_return_none_if_unknown(self):
509 docs = self.backend.rpc_get_completion_location("Foo")
510
511 self.assertIsNone(docs)
512
513
514 class RPCGetDefinitionTests(GenericRPCTests):
515 METHOD = "rpc_get_definition"
516
517 def test_should_return_definition_location_same_file(self):
518 source, offset = source_and_offset("import threading\n"
519 "def test_function(a, b):\n"
520 " return a + b\n"
521 "\n"
522 "test_func_|_tion(\n")
523 filename = self.project_file("test.py", source)
524
525 location = self.backend.rpc_get_definition(filename,
526 source,
527 offset)
528
529 self.assertEqual(location[0], filename)
530 # On def or on the function name
531 self.assertIn(location[1], (17, 21))
532
533 def test_should_return_location_in_same_file_if_not_saved(self):
534 source, offset = source_and_offset(
535 "import threading\n"
536 "\n"
537 "\n"
538 "def other_function():\n"
539 " test_f_|_unction(1, 2)\n"
540 "\n"
541 "\n"
542 "def test_function(a, b):\n"
543 " return a + b\n")
544 filename = self.project_file("test.py", "")
545
546 location = self.backend.rpc_get_definition(filename,
547 source,
548 offset)
549
550 self.assertEqual(location[0], filename)
551 # def or function name
552 self.assertIn(location[1], (67, 71))
553
554 def test_should_return_location_in_different_file(self):
555 source1 = ("def test_function(a, b):\n"
556 " return a + b\n")
557 file1 = self.project_file("test1.py", source1)
558 source2, offset = source_and_offset("from test1 import test_function\n"
559 "test_funct_|_ion(1, 2)\n")
560 file2 = self.project_file("test2.py", source2)
561
562 definition = self.backend.rpc_get_definition(file2,
563 source2,
564 offset)
565
566 self.assertEqual(definition[0], file1)
567 # Either on the def or on the function name
568 self.assertIn(definition[1], (0, 4))
569
570 def test_should_return_none_if_location_not_found(self):
571 source, offset = source_and_offset("test_f_|_unction()\n")
572 filename = self.project_file("test.py", source)
573
574 definition = self.backend.rpc_get_definition(filename,
575 source,
576 offset)
577
578 self.assertIsNone(definition)
579
580 def test_should_return_none_if_outside_of_symbol(self):
581 source, offset = source_and_offset("test_function(_|_)\n")
582 filename = self.project_file("test.py", source)
583
584 definition = self.backend.rpc_get_definition(filename,
585 source,
586 offset)
587
588 self.assertIsNone(definition)
589
590 def test_should_return_definition_location_different_package(self):
591 # See issue #74
592 self.project_file("project/__init__.py", "")
593 source1 = ("class Add:\n"
594 " def add(self, a, b):\n"
595 " return a + b\n")
596 file1 = self.project_file("project/add.py", source1)
597 source2, offset = source_and_offset(
598 "from project.add import Add\n"
599 "class Calculator:\n"
600 " def add(self, a, b):\n"
601 " return Add_|_().add(a, b)\n")
602 file2 = self.project_file("project/calculator.py", source2)
603
604 location = self.backend.rpc_get_definition(file2,
605 source2,
606 offset)
607
608 self.assertEqual(location[0], file1)
609 # class or class name
610 self.assertIn(location[1], (0, 6))
611
612 def test_should_find_variable_definition(self):
613 source, offset = source_and_offset("SOME_VALUE = 1\n"
614 "\n"
615 "variable = _|_SOME_VALUE\n")
616 filename = self.project_file("test.py", source)
617 self.assertEqual(self.backend.rpc_get_definition(filename,
618 source,
619 offset),
620 (filename, 0))
621
622
623 class RPCGetAssignmentTests():
624 METHOD = "rpc_get_assignment"
625
626 def test_should_raise_fault(self):
627 if jedibackend.JEDISUP17:
628 with self.assertRaises(Fault):
629 self.backend.rpc_get_assignment("test.py", "dummy code", 1)
630
631
632 class RPCGetCalltipTests(GenericRPCTests):
633 METHOD = "rpc_get_calltip"
634
635 def test_should_get_calltip(self):
636 expected = self.THREAD_CALLTIP
637 source, offset = source_and_offset(
638 "import threading\nthreading.Thread(_|_")
639 filename = self.project_file("test.py", source)
640 calltip = self.backend.rpc_get_calltip(filename,
641 source,
642 offset)
643 self.assertEqual(calltip, expected)
644 calltip = self.backend.rpc_get_calltip_or_oneline_docstring(filename,
645 source,
646 offset)
647 calltip.pop('kind')
648 self.assertEqual(calltip, expected)
649
650 def test_should_get_calltip_even_after_parens(self):
651 source, offset = source_and_offset(
652 "import threading\nthreading.Thread(foo()_|_")
653 filename = self.project_file("test.py", source)
654 actual = self.backend.rpc_get_calltip(filename,
655 source,
656 offset)
657 self.assertEqual(self.THREAD_CALLTIP, actual)
658 actual = self.backend.rpc_get_calltip_or_oneline_docstring(filename,
659 source,
660 offset)
661 actual.pop('kind')
662 self.assertEqual(self.THREAD_CALLTIP, actual)
663
664 def test_should_get_calltip_at_closing_paren(self):
665 source, offset = source_and_offset(
666 "import threading\nthreading.Thread(_|_)")
667 filename = self.project_file("test.py", source)
668 actual = self.backend.rpc_get_calltip(filename,
669 source,
670 offset)
671 self.assertEqual(self.THREAD_CALLTIP, actual)
672 actual = self.backend.rpc_get_calltip_or_oneline_docstring(filename,
673 source,
674 offset)
675 self.assertEqual(actual['kind'], "oneline_doc")
676
677 def test_should_not_missing_attribute_get_definition(self):
678 # Bug #627 / jedi#573
679 source, offset = source_and_offset(
680 "import threading\nthreading.Thread(_|_)")
681 filename = self.project_file("test.py", source)
682
683 self.backend.rpc_get_calltip(filename, source, offset)
684
685 def test_should_return_none_for_bad_identifier(self):
686 source, offset = source_and_offset(
687 "froblgoo(_|_")
688 filename = self.project_file("test.py", source)
689 calltip = self.backend.rpc_get_calltip(filename,
690 source,
691 offset)
692 self.assertIsNone(calltip)
693 calltip = self.backend.rpc_get_calltip_or_oneline_docstring(filename,
694 source,
695 offset)
696 self.assertIsNone(calltip)
697
698 def test_should_remove_self_argument(self):
699 source, offset = source_and_offset(
700 "d = dict()\n"
701 "d.keys(_|_")
702 filename = self.project_file("test.py", source)
703
704 actual = self.backend.rpc_get_calltip(filename,
705 source,
706 offset)
707 self.assertEqual(self.KEYS_CALLTIP, actual)
708 actual = self.backend.rpc_get_calltip_or_oneline_docstring(filename,
709 source,
710 offset)
711 actual.pop('kind')
712 self.assertEqual(self.KEYS_CALLTIP, actual)
713
714 def test_should_remove_package_prefix(self):
715 source, offset = source_and_offset(
716 "import decimal\n"
717 "d = decimal.Decimal('1.5')\n"
718 "d.radix(_|_")
719 filename = self.project_file("test.py", source)
720
721 actual = self.backend.rpc_get_calltip(filename,
722 source,
723 offset)
724 self.assertEqual(self.RADIX_CALLTIP, actual)
725 actual = self.backend.rpc_get_calltip_or_oneline_docstring(filename,
726 source,
727 offset)
728 actual.pop('kind')
729 self.assertEqual(self.RADIX_CALLTIP, actual)
730
731 def test_should_return_none_outside_of_all(self):
732 filename = self.project_file("test.py", "")
733 source, offset = source_and_offset("import thr_|_eading\n")
734 calltip = self.backend.rpc_get_calltip(filename,
735 source, offset)
736 self.assertIsNone(calltip)
737 calltip = self.backend.rpc_get_calltip_or_oneline_docstring(filename,
738 source,
739 offset)
740 self.assertIsNotNone(calltip)
741
742 def test_should_find_calltip_different_package(self):
743 # See issue #74
744 self.project_file("project/__init__.py", "")
745 source1 = ("class Add:\n"
746 " def add(self, a, b):\n"
747 " return a + b\n")
748 self.project_file("project/add.py", source1)
749 source2, offset = source_and_offset(
750 "from project.add import Add\n"
751 "class Calculator:\n"
752 " def add(self, a, b):\n"
753 " c = Add()\n"
754 " c.add(_|_\n")
755 file2 = self.project_file("project/calculator.py", source2)
756
757 actual = self.backend.rpc_get_calltip(file2,
758 source2,
759 offset)
760 self.assertEqual(self.ADD_CALLTIP, actual)
761 actual = self.backend.rpc_get_calltip_or_oneline_docstring(file2,
762 source2,
763 offset)
764 actual.pop('kind')
765 self.assertEqual(self.ADD_CALLTIP, actual)
766
767 def test_should_return_oneline_docstring_if_no_calltip(self):
768 source, offset = source_and_offset(
769 "def foo(a, b):\n fo_|_o(1 + 2)")
770 filename = self.project_file("test.py", source)
771 calltip = self.backend.rpc_get_calltip_or_oneline_docstring(filename,
772 source,
773 offset)
774 self.assertEqual(calltip['kind'], 'oneline_doc')
775 self.assertEqual(calltip['doc'], 'No documentation')
776
777
778 class RPCGetDocstringTests(GenericRPCTests):
779 METHOD = "rpc_get_docstring"
780
781 def check_docstring(self, docstring):
782
783 def first_line(s):
784 return s[:s.index("\n")]
785 match = re.match(self.JSON_LOADS_REGEX,
786 first_line(docstring))
787 self.assertIsNotNone(match)
788
789 def test_should_get_docstring(self):
790 source, offset = source_and_offset(
791 "import json\njson.loads_|_(")
792 filename = self.project_file("test.py", source)
793 docstring = self.backend.rpc_get_docstring(filename,
794 source,
795 offset)
796 self.check_docstring(docstring)
797
798 def test_should_return_none_for_bad_identifier(self):
799 source, offset = source_and_offset(
800 "froblgoo_|_(\n")
801 filename = self.project_file("test.py", source)
802 docstring = self.backend.rpc_get_docstring(filename,
803 source,
804 offset)
805 self.assertIsNone(docstring)
806
807
808 class RPCGetOnelineDocstringTests(GenericRPCTests):
809 METHOD = "rpc_get_oneline_docstring"
810
811 def check_docstring(self, docstring):
812
813 self.assertEqual(docstring['doc'],
814 self.JSON_LOADS_DOCSTRING)
815
816 def check_module_docstring(self, docstring):
817
818 self.assertEqual(docstring['doc'],
819 self.JSON_DOCSTRING)
820
821 def test_should_get_oneline_docstring(self):
822 source, offset = source_and_offset(
823 "import json\njson.loads_|_(")
824 filename = self.project_file("test.py", source)
825 docstring = self.backend.rpc_get_oneline_docstring(filename,
826 source,
827 offset)
828 self.check_docstring(docstring)
829 docstring = self.backend.rpc_get_calltip_or_oneline_docstring(filename,
830 source,
831 offset)
832 docstring.pop('kind')
833 self.check_docstring(docstring)
834
835 def test_should_get_oneline_docstring_for_modules(self):
836 source, offset = source_and_offset(
837 "import json_|_\njson.loads(")
838 filename = self.project_file("test.py", source)
839 docstring = self.backend.rpc_get_oneline_docstring(filename,
840 source,
841 offset)
842 self.check_module_docstring(docstring)
843 docstring = self.backend.rpc_get_calltip_or_oneline_docstring(filename,
844 source,
845 offset)
846 docstring.pop('kind')
847 self.check_module_docstring(docstring)
848
849 def test_should_return_none_for_bad_identifier(self):
850 source, offset = source_and_offset(
851 "froblgoo_|_(\n")
852 filename = self.project_file("test.py", source)
853 docstring = self.backend.rpc_get_oneline_docstring(filename,
854 source,
855 offset)
856 self.assertIsNone(docstring)
857 docstring = self.backend.rpc_get_calltip_or_oneline_docstring(filename,
858 source,
859 offset)
860 self.assertIsNone(docstring)
861
862
863 @unittest.skipIf(not jedibackend.JEDISUP17,
864 "Refactoring not available with jedi<17")
865 @unittest.skipIf(sys.version_info < (3, 6),
866 "Jedi refactoring not available for python < 3.6")
867 class RPCGetRenameDiffTests(object):
868 METHOD = "rpc_get_rename_diff"
869
870 def test_should_return_rename_diff(self):
871 source, offset = source_and_offset("def foo(a, b):\n"
872 " print(a_|_)\n"
873 " return b")
874 new_name = "c"
875 diff = self.backend.rpc_get_rename_diff("test.py", source, offset,
876 new_name)
877 assert diff['success']
878 self.assertIn("-def foo(a, b):\n"
879 "- print(a)\n"
880 "+def foo(c, b):\n"
881 "+ print(c)",
882 diff['diff'])
883
884 def test_should_fail_for_invalid_symbol_at_point(self):
885 source, offset = source_and_offset("def foo(a, b):\n"
886 " print(12_|_)\n"
887 " return b")
888 new_name = "c"
889 diff = self.backend.rpc_get_rename_diff("test.py", source, offset,
890 new_name)
891 self.assertFalse(diff['success'])
892
893
894 @unittest.skipIf(not jedibackend.JEDISUP17,
895 "Refactoring not available with jedi<17")
896 @unittest.skipIf(sys.version_info < (3, 6),
897 "Jedi refactoring not available for python < 3.6")
898 class RPCGetExtractFunctionDiffTests(object):
899 METHOD = "rpc_get_extract_function_diff"
900
901 def test_should_return_function_extraction_diff(self):
902 source, offset = source_and_offset("print(a)\n"
903 "return b_|_\n")
904 new_name = "foo"
905 diff = self.backend.rpc_get_extract_function_diff(
906 "test.py", source, offset,
907 new_name,
908 line_beg=1, line_end=2,
909 col_beg=0, col_end=8)
910 assert diff['success']
911 self.assertIn('-print(a)\n'
912 '-return b\n'
913 '+def foo(a, b):\n'
914 '+ print(a)\n'
915 '+ return b\n',
916 diff['diff'])
917
918
919 @unittest.skipIf(not jedibackend.JEDISUP17,
920 "Refactoring not available with jedi<17")
921 @unittest.skipIf(sys.version_info < (3, 6),
922 "Jedi refactoring not available for python < 3.6")
923 class RPCGetExtractVariableDiffTests(object):
924 METHOD = "rpc_get_extract_variable_diff"
925
926 def test_should_return_variable_extraction_diff(self):
927 source, offset = source_and_offset("b = 12\n"
928 "a = 2\n"
929 "print_|_(a + 1 + b/2)\n")
930 new_name = "c"
931 diff = self.backend.rpc_get_extract_variable_diff(
932 "test.py", source, offset,
933 new_name,
934 line_beg=3, line_end=3,
935 col_beg=7, col_end=16)
936 assert diff['success']
937 self.assertIn("-print(a + 1 + b/2)\n+c = a + 1 + b/2\n+print(c)\n",
938 diff['diff'])
939
940
941 @unittest.skipIf(not jedibackend.JEDISUP17,
942 "Refactoring not available with jedi<17")
943 @unittest.skipIf(sys.version_info < (3, 6),
944 "Jedi refactoring not available for python < 3.6")
945 class RPCGetInlineDiffTests(object):
946 METHOD = "rpc_get_inline_diff"
947
948 def test_should_return_inline_diff(self):
949 source, offset = source_and_offset("foo = 3.1\n"
950 "bar = foo + 1\n"
951 "x = int(ba_|_r)\n")
952 diff = self.backend.rpc_get_inline_diff("test.py", source,
953 offset)
954 assert diff['success']
955 self.assertIn("-bar = foo + 1\n-x = int(bar)\n+x = int(foo + 1)",
956 diff['diff'])
957
958 def test_should_error_on_refactoring_failure(self):
959 source, offset = source_and_offset("foo = 3.1\n"
960 "bar = foo + 1\n"
961 "x = in_|_t(bar)\n")
962 diff = self.backend.rpc_get_inline_diff("test.py", source,
963 offset)
964 self.assertFalse(diff['success'])
965
966
967 class RPCGetNamesTests(GenericRPCTests):
968 METHOD = "rpc_get_names"
969
970 def test_shouldreturn_names_in_same_file(self):
971 filename = self.project_file("test.py", "")
972 source, offset = source_and_offset(
973 "def foo(x, y):\n"
974 " return x + y\n"
975 "c = _|_foo(5, 2)\n")
976
977 names = self.backend.rpc_get_names(filename,
978 source,
979 offset)
980
981 self.assertEqual(names,
982 [{'name': 'foo',
983 'filename': filename,
984 'offset': 4},
985 {'name': 'x',
986 'filename': filename,
987 'offset': 8},
988 {'name': 'y',
989 'filename': filename,
990 'offset': 11},
991 {'name': 'x',
992 'filename': filename,
993 'offset': 26},
994 {'name': 'y',
995 'filename': filename,
996 'offset': 30},
997 {'name': 'c',
998 'filename': filename,
999 'offset': 32},
1000 {'name': 'foo',
1001 'filename': filename,
1002 'offset': 36}])
1003
1004 def test_should_not_fail_without_symbol(self):
1005 filename = self.project_file("test.py", "")
1006
1007 names = self.backend.rpc_get_names(filename,
1008 "",
1009 0)
1010
1011 self.assertEqual(names, [])
1012
1013
1014 class RPCGetUsagesTests(GenericRPCTests):
1015 METHOD = "rpc_get_usages"
1016
1017 def test_should_return_uses_in_same_file(self):
1018 filename = self.project_file("test.py", "")
1019 source, offset = source_and_offset(
1020 "def foo(x):\n"
1021 " return _|_x + x\n")
1022
1023 usages = self.backend.rpc_get_usages(filename,
1024 source,
1025 offset)
1026
1027 self.assertEqual(usages,
1028 [{'name': 'x',
1029 'offset': 8,
1030 'filename': filename},
1031 {'name': 'x',
1032 'filename': filename,
1033 'offset': 23},
1034 {'name': u'x',
1035 'filename': filename,
1036 'offset': 27}])
1037
1038 def test_should_return_uses_in_other_file(self):
1039 file1 = self.project_file("file1.py", "")
1040 file2 = self.project_file("file2.py", "\n\n\n\n\nx = 5")
1041 source, offset = source_and_offset(
1042 "import file2\n"
1043 "file2._|_x\n")
1044
1045 usages = self.backend.rpc_get_usages(file1,
1046 source,
1047 offset)
1048
1049 self.assertEqual(usages,
1050 [{'name': 'x',
1051 'filename': file1,
1052 'offset': 19},
1053 {'name': 'x',
1054 'filename': file2,
1055 'offset': 5}])
1056
1057 def test_should_not_fail_without_symbol(self):
1058 filename = self.project_file("file.py", "")
1059
1060 usages = self.backend.rpc_get_usages(filename,
1061 "",
1062 0)
1063
1064 self.assertEqual(usages, [])
1065
1066
1067 def source_and_offset(source):
1068 """Return a source and offset from a source description.
1069
1070 >>> source_and_offset("hello, _|_world")
1071 ("hello, world", 7)
1072 >>> source_and_offset("_|_hello, world")
1073 ("hello, world", 0)
1074 >>> source_and_offset("hello, world_|_")
1075 ("hello, world", 12)
1076 """
1077 offset = source.index("_|_")
1078 return source[:offset] + source[offset + 3:], offset