Mercurial > hg > dotemacs
comparison elpa/elpy-1.12.0/elpy/tests/test_refactor.py @ 152:55ceabc58fcc
elpy: upgrade to 1.12
author | Jordi Gutiérrez Hermoso <jordigh@octave.org> |
---|---|
date | Fri, 14 Oct 2016 10:57:18 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
151:34d8d00044fc | 152:55ceabc58fcc |
---|---|
1 import unittest | |
2 import tempfile | |
3 import shutil | |
4 import os | |
5 import mock | |
6 import sys | |
7 | |
8 from elpy import refactor | |
9 from textwrap import dedent | |
10 | |
11 | |
12 class RefactorTestCase(unittest.TestCase): | |
13 def setUp(self): | |
14 self.project_root = tempfile.mkdtemp(prefix="test-refactor-root") | |
15 self.addCleanup(shutil.rmtree, self.project_root, | |
16 ignore_errors=True) | |
17 | |
18 def create_file(self, name, contents=""): | |
19 filename = os.path.join(self.project_root, name) | |
20 contents = dedent(contents) | |
21 offset = contents.find("_|_") | |
22 if offset > -1: | |
23 contents = contents[:offset] + contents[offset + 3:] | |
24 with open(filename, "w") as f: | |
25 f.write(contents) | |
26 return filename, offset | |
27 | |
28 def assertSourceEqual(self, first, second, msg=None): | |
29 """Fail if the two objects are unequal, ignoring indentation.""" | |
30 self.assertEqual(dedent(first), dedent(second), msg=msg) | |
31 | |
32 | |
33 class TestGetRefactorOptions(RefactorTestCase): | |
34 def test_should_only_return_importsmodule_if_not_on_symbol(self): | |
35 filename, offset = self.create_file("foo.py", | |
36 """\ | |
37 import foo | |
38 _|_""") | |
39 ref = refactor.Refactor(self.project_root, filename) | |
40 options = ref.get_refactor_options(offset) | |
41 self.assertTrue(all(opt['category'] in ('Imports', | |
42 'Module') | |
43 for opt in options)) | |
44 filename, offset = self.create_file("foo.py", | |
45 """\ | |
46 _|_ | |
47 import foo""") | |
48 ref = refactor.Refactor(self.project_root, filename) | |
49 options = ref.get_refactor_options(offset) | |
50 self.assertTrue(all(opt['category'] in ('Imports', | |
51 'Module') | |
52 for opt in options)) | |
53 | |
54 def test_should_return_all_if_on_symbol(self): | |
55 filename, offset = self.create_file("foo.py", | |
56 "import _|_foo") | |
57 ref = refactor.Refactor(self.project_root, filename) | |
58 options = ref.get_refactor_options(offset) | |
59 self.assertTrue(all(opt['category'] in ('Imports', | |
60 'Method', | |
61 'Module', | |
62 'Symbol') | |
63 for opt in options)) | |
64 | |
65 def test_should_return_only_region_if_endoffset(self): | |
66 filename, offset = self.create_file("foo.py", | |
67 "import foo") | |
68 ref = refactor.Refactor(self.project_root, filename) | |
69 options = ref.get_refactor_options(offset, 5) | |
70 self.assertTrue(all(opt['category'] == 'Region' | |
71 for opt in options)) | |
72 | |
73 @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") | |
74 def test_should_treat_from_import_special(self): | |
75 filename, offset = self.create_file("foo.py", | |
76 """\ | |
77 import foo | |
78 _|_""") | |
79 ref = refactor.Refactor(self.project_root, filename) | |
80 options = ref.get_refactor_options(offset) | |
81 self.assertFalse(any(opt['name'] == "refactor_froms_to_imports" | |
82 for opt in options)) | |
83 filename, offset = self.create_file("foo.py", | |
84 "imp_|_ort foo") | |
85 ref = refactor.Refactor(self.project_root, filename) | |
86 options = ref.get_refactor_options(offset) | |
87 self.assertTrue(any(opt['name'] == "refactor_froms_to_imports" | |
88 for opt in options)) | |
89 | |
90 | |
91 class TestGetChanges(RefactorTestCase): | |
92 def test_should_fail_if_method_is_not_refactoring(self): | |
93 filename, offset = self.create_file("foo.py") | |
94 ref = refactor.Refactor(self.project_root, filename) | |
95 self.assertRaises(ValueError, ref.get_changes, "bad_name") | |
96 | |
97 def test_should_return_method_results(self): | |
98 filename, offset = self.create_file("foo.py") | |
99 ref = refactor.Refactor(self.project_root, filename) | |
100 with mock.patch.object(ref, 'refactor_extract_method') as test: | |
101 test.return_value = "Meep!" | |
102 self.assertEqual(ref.get_changes("refactor_extract_method", | |
103 1, 2), | |
104 "Meep!") | |
105 test.assert_called_with(1, 2) | |
106 | |
107 | |
108 @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") | |
109 class TestIsOnSymbol(RefactorTestCase): | |
110 def test_should_find_symbol(self): | |
111 filename, offset = self.create_file("test.py", "__B_|_AR = 100") | |
112 r = refactor.Refactor(self.project_root, filename) | |
113 self.assertTrue(r._is_on_symbol(offset)) | |
114 | |
115 # Issue #111 | |
116 def test_should_find_symbol_with_underscores(self): | |
117 filename, offset = self.create_file("test.py", "_|___BAR = 100") | |
118 r = refactor.Refactor(self.project_root, filename) | |
119 self.assertTrue(r._is_on_symbol(offset)) | |
120 | |
121 def test_should_not_find_weird_places(self): | |
122 filename, offset = self.create_file("test.py", "hello = _|_ 1 + 1") | |
123 r = refactor.Refactor(self.project_root, filename) | |
124 self.assertFalse(r._is_on_symbol(offset)) | |
125 | |
126 | |
127 @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") | |
128 class TestFromsToImports(RefactorTestCase): | |
129 def test_should_refactor(self): | |
130 filename, offset = self.create_file( | |
131 "foo.py", | |
132 """\ | |
133 _|_from datetime import datetime | |
134 | |
135 d = datetime(2013, 4, 7) | |
136 """) | |
137 ref = refactor.Refactor(self.project_root, filename) | |
138 (change,) = ref.get_changes("refactor_froms_to_imports", offset) | |
139 self.assertEqual(change['action'], 'change') | |
140 self.assertEqual(change['file'], filename) | |
141 self.assertSourceEqual(change['contents'], | |
142 """\ | |
143 import datetime | |
144 | |
145 d = datetime.datetime(2013, 4, 7) | |
146 """) | |
147 | |
148 | |
149 @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") | |
150 class TestOrganizeImports(RefactorTestCase): | |
151 def test_should_refactor(self): | |
152 filename, offset = self.create_file( | |
153 "foo.py", | |
154 """\ | |
155 import unittest, base64 | |
156 import datetime, json | |
157 | |
158 obj = json.dumps(23) | |
159 unittest.TestCase() | |
160 """) | |
161 ref = refactor.Refactor(self.project_root, filename) | |
162 (change,) = ref.get_changes("refactor_organize_imports") | |
163 self.assertEqual(change['action'], 'change') | |
164 self.assertEqual(change['file'], filename) | |
165 self.assertSourceEqual(change['contents'], | |
166 """\ | |
167 import json | |
168 import unittest | |
169 | |
170 | |
171 obj = json.dumps(23) | |
172 unittest.TestCase() | |
173 """) | |
174 | |
175 | |
176 @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") | |
177 class TestModuleToPackage(RefactorTestCase): | |
178 def test_should_refactor(self): | |
179 filename, offset = self.create_file( | |
180 "foo.py", | |
181 "_|_import os\n") | |
182 ref = refactor.Refactor(self.project_root, filename) | |
183 changes = ref.refactor_module_to_package() | |
184 a, b, c = changes | |
185 # Not sure why the a change is there. It's a CHANGE that | |
186 # changes nothing... | |
187 self.assertEqual(a['diff'], '') | |
188 | |
189 self.assertEqual(b['action'], 'create') | |
190 self.assertEqual(b['type'], 'directory') | |
191 self.assertEqual(b['path'], os.path.join(self.project_root, "foo")) | |
192 | |
193 self.assertEqual(c['action'], 'move') | |
194 self.assertEqual(c['type'], 'file') | |
195 self.assertEqual(c['source'], os.path.join(self.project_root, | |
196 "foo.py")) | |
197 self.assertEqual(c['destination'], os.path.join(self.project_root, | |
198 "foo/__init__.py")) | |
199 | |
200 | |
201 @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") | |
202 class TestRenameAtPoint(RefactorTestCase): | |
203 def test_should_refactor(self): | |
204 filename, offset = self.create_file( | |
205 "foo.py", | |
206 """\ | |
207 class Foo(object): | |
208 def _|_foo(self): | |
209 return 5 | |
210 | |
211 def bar(self): | |
212 return self.foo() | |
213 """) | |
214 file2, offset2 = self.create_file( | |
215 "bar.py", | |
216 """\ | |
217 import foo | |
218 | |
219 | |
220 x = foo.Foo() | |
221 x.foo()""") | |
222 ref = refactor.Refactor(self.project_root, filename) | |
223 first, second = ref.refactor_rename_at_point(offset, "frob", | |
224 in_hierarchy=False, | |
225 docs=False) | |
226 if first['file'] == filename: | |
227 a, b = first, second | |
228 else: | |
229 a, b = second, first | |
230 self.assertEqual(a['action'], 'change') | |
231 self.assertEqual(a['file'], filename) | |
232 self.assertSourceEqual(a['contents'], | |
233 """\ | |
234 class Foo(object): | |
235 def frob(self): | |
236 return 5 | |
237 | |
238 def bar(self): | |
239 return self.frob() | |
240 """) | |
241 self.assertEqual(b['action'], 'change') | |
242 self.assertEqual(b['file'], file2) | |
243 self.assertSourceEqual(b['contents'], | |
244 """\ | |
245 import foo | |
246 | |
247 | |
248 x = foo.Foo() | |
249 x.frob()""") | |
250 | |
251 def test_should_refactor_in_hierarchy(self): | |
252 filename, offset = self.create_file( | |
253 "foo.py", | |
254 """\ | |
255 class Foo(object): | |
256 def _|_foo(self): | |
257 return 5 | |
258 | |
259 def bar(self): | |
260 return self.foo() | |
261 | |
262 class Bar(Foo): | |
263 def foo(self): | |
264 return 42 | |
265 | |
266 class Baz(object): | |
267 def foo(self): | |
268 return 42 | |
269 """) | |
270 file2, offset2 = self.create_file( | |
271 "bar.py", | |
272 """\ | |
273 import foo | |
274 | |
275 | |
276 x, y, z = foo.Foo(), foo.Bar(), foo.Baz() | |
277 x.foo() | |
278 y.foo() | |
279 z.foo()""") | |
280 ref = refactor.Refactor(self.project_root, filename) | |
281 first, second = ref.refactor_rename_at_point(offset, "frob", | |
282 in_hierarchy=True, | |
283 docs=False) | |
284 if first['file'] == filename: | |
285 a, b = first, second | |
286 else: | |
287 a, b = second, first | |
288 self.assertEqual(a['action'], 'change') | |
289 self.assertEqual(a['file'], filename) | |
290 self.assertSourceEqual(a['contents'], | |
291 """\ | |
292 class Foo(object): | |
293 def frob(self): | |
294 return 5 | |
295 | |
296 def bar(self): | |
297 return self.frob() | |
298 | |
299 class Bar(Foo): | |
300 def frob(self): | |
301 return 42 | |
302 | |
303 class Baz(object): | |
304 def foo(self): | |
305 return 42 | |
306 """) | |
307 self.assertEqual(b['action'], 'change') | |
308 self.assertEqual(b['file'], file2) | |
309 self.assertSourceEqual(b['contents'], | |
310 """\ | |
311 import foo | |
312 | |
313 | |
314 x, y, z = foo.Foo(), foo.Bar(), foo.Baz() | |
315 x.frob() | |
316 y.frob() | |
317 z.foo()""") | |
318 | |
319 def test_should_refactor_in_docstrings(self): | |
320 filename, offset = self.create_file( | |
321 "foo.py", | |
322 """\ | |
323 class Foo(object): | |
324 "Frobnicate the foo" | |
325 def _|_foo(self): | |
326 return 5 | |
327 | |
328 print("I'm an unrelated foo") | |
329 """) | |
330 ref = refactor.Refactor(self.project_root, filename) | |
331 (change,) = ref.refactor_rename_at_point(offset, "frob", | |
332 in_hierarchy=False, | |
333 docs=True) | |
334 self.assertEqual(change['action'], 'change') | |
335 self.assertEqual(change['file'], filename) | |
336 self.assertSourceEqual(change['contents'], | |
337 """\ | |
338 class Foo(object): | |
339 "Frobnicate the frob" | |
340 def frob(self): | |
341 return 5 | |
342 | |
343 print("I'm an unrelated foo") | |
344 """) | |
345 | |
346 | |
347 @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") | |
348 class TestRenameCurrentModule(RefactorTestCase): | |
349 def test_should_refactor(self): | |
350 filename, offset = self.create_file( | |
351 "foo.py", | |
352 "_|_import os\n") | |
353 file2, offset = self.create_file( | |
354 "bar.py", | |
355 """\ | |
356 _|_import foo | |
357 foo.os | |
358 """) | |
359 dest = os.path.join(self.project_root, "frob.py") | |
360 ref = refactor.Refactor(self.project_root, filename) | |
361 a, b = ref.refactor_rename_current_module("frob") | |
362 | |
363 self.assertEqual(a['action'], 'change') | |
364 self.assertEqual(a['file'], file2) | |
365 self.assertEqual(a['contents'], | |
366 "import frob\n" | |
367 "frob.os\n") | |
368 | |
369 self.assertEqual(b['action'], 'move') | |
370 self.assertEqual(b['type'], 'file') | |
371 self.assertEqual(b['source'], filename) | |
372 self.assertEqual(b['destination'], dest) | |
373 | |
374 | |
375 @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") | |
376 class TestMoveModule(RefactorTestCase): | |
377 def test_should_refactor(self): | |
378 filename, offset = self.create_file( | |
379 "foo.py", | |
380 "_|_import os\n") | |
381 file2, offset = self.create_file( | |
382 "bar.py", | |
383 """\ | |
384 _|_import foo | |
385 foo.os | |
386 """) | |
387 dest = os.path.join(self.project_root, "frob") | |
388 os.mkdir(dest) | |
389 with open(os.path.join(dest, "__init__.py"), "w") as f: | |
390 f.write("") | |
391 ref = refactor.Refactor(self.project_root, filename) | |
392 a, b = ref.refactor_move_module(dest) | |
393 | |
394 self.assertEqual(a['action'], 'change') | |
395 self.assertEqual(a['file'], file2) | |
396 self.assertSourceEqual(a['contents'], | |
397 """\ | |
398 import frob.foo | |
399 frob.foo.os | |
400 """) | |
401 | |
402 self.assertEqual(b['action'], 'move') | |
403 self.assertEqual(b['type'], 'file') | |
404 self.assertEqual(b['source'], filename) | |
405 self.assertEqual(b['destination'], | |
406 os.path.join(dest, "foo.py")) | |
407 | |
408 | |
409 @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") | |
410 class TestCreateInline(RefactorTestCase): | |
411 def setUp(self): | |
412 super(TestCreateInline, self).setUp() | |
413 self.filename, self.offset = self.create_file( | |
414 "foo.py", | |
415 """\ | |
416 def add(a, b): | |
417 return a + b | |
418 | |
419 x = _|_add(2, 3) | |
420 y = add(17, 4) | |
421 """) | |
422 | |
423 def test_should_refactor_single_occurrenc(self): | |
424 ref = refactor.Refactor(self.project_root, self.filename) | |
425 (change,) = ref.refactor_create_inline(self.offset, True) | |
426 | |
427 self.assertEqual(change['action'], 'change') | |
428 self.assertEqual(change['file'], self.filename) | |
429 self.assertSourceEqual(change['contents'], | |
430 """\ | |
431 def add(a, b): | |
432 return a + b | |
433 | |
434 x = 2 + 3 | |
435 y = add(17, 4) | |
436 """) | |
437 | |
438 def test_should_refactor_all_occurrencs(self): | |
439 ref = refactor.Refactor(self.project_root, self.filename) | |
440 (change,) = ref.refactor_create_inline(self.offset, False) | |
441 | |
442 self.assertEqual(change['action'], 'change') | |
443 self.assertEqual(change['file'], self.filename) | |
444 self.assertSourceEqual(change['contents'], | |
445 """\ | |
446 x = 2 + 3 | |
447 y = 17 + 4 | |
448 """) | |
449 | |
450 | |
451 @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") | |
452 class TestExtractMethod(RefactorTestCase): | |
453 def setUp(self): | |
454 super(TestExtractMethod, self).setUp() | |
455 self.filename, self.offset = self.create_file( | |
456 "foo.py", | |
457 """\ | |
458 class Foo(object): | |
459 def spaghetti(self, a, b): | |
460 _|_x = a + 5 | |
461 y = b + 23 | |
462 return y | |
463 """) | |
464 | |
465 @unittest.skipIf(sys.version_info >= (3, 5), "Python 3.5 not supported") | |
466 def test_should_refactor_local(self): | |
467 ref = refactor.Refactor(self.project_root, self.filename) | |
468 (change,) = ref.refactor_extract_method(self.offset, 104, | |
469 "calc", False) | |
470 self.assertEqual(change['action'], 'change') | |
471 self.assertEqual(change['file'], self.filename) | |
472 expected = """\ | |
473 class Foo(object): | |
474 def spaghetti(self, a, b): | |
475 return self.calc(a, b) | |
476 | |
477 def calc(self, a, b): | |
478 x = a + 5 | |
479 y = b + 23 | |
480 return y | |
481 """ | |
482 expected2 = expected.replace("return self.calc(a, b)", | |
483 "return self.calc(b, a)") | |
484 expected2 = expected2.replace("def calc(self, a, b)", | |
485 "def calc(self, b, a)") | |
486 # This is silly, but it's what we got. | |
487 if change['contents'] == dedent(expected2): | |
488 self.assertSourceEqual(change['contents'], expected2) | |
489 else: | |
490 self.assertSourceEqual(change['contents'], expected) | |
491 | |
492 @unittest.skipIf(sys.version_info >= (3, 5), "Python 3.5 not supported") | |
493 def test_should_refactor_global(self): | |
494 ref = refactor.Refactor(self.project_root, self.filename) | |
495 (change,) = ref.refactor_extract_method(self.offset, 104, | |
496 "calc", True) | |
497 self.assertEqual(change['action'], 'change') | |
498 self.assertEqual(change['file'], self.filename) | |
499 expected = """\ | |
500 class Foo(object): | |
501 def spaghetti(self, a, b): | |
502 return calc(a, b) | |
503 | |
504 def calc(a, b): | |
505 x = a + 5 | |
506 y = b + 23 | |
507 return y | |
508 """ | |
509 expected2 = expected.replace("return calc(a, b)", | |
510 "return calc(b, a)") | |
511 expected2 = expected2.replace("def calc(a, b)", | |
512 "def calc(b, a)") | |
513 if change['contents'] == dedent(expected2): | |
514 self.assertSourceEqual(change['contents'], expected2) | |
515 else: | |
516 self.assertSourceEqual(change['contents'], expected) | |
517 | |
518 | |
519 @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope") | |
520 class TestUseFunction(RefactorTestCase): | |
521 def test_should_refactor(self): | |
522 filename, offset = self.create_file( | |
523 "foo.py", | |
524 """\ | |
525 def _|_add_and_multiply(a, b, c): | |
526 temp = a + b | |
527 return temp * c | |
528 | |
529 f = 1 + 2 | |
530 g = f * 3 | |
531 """) | |
532 | |
533 ref = refactor.Refactor(self.project_root, filename) | |
534 (change,) = ref.refactor_use_function(offset) | |
535 | |
536 self.assertEqual(change['action'], 'change') | |
537 self.assertEqual(change['file'], filename) | |
538 self.assertSourceEqual(change['contents'], | |
539 """\ | |
540 def add_and_multiply(a, b, c): | |
541 temp = a + b | |
542 return temp * c | |
543 | |
544 g = add_and_multiply(1, 2, 3) | |
545 """) |