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 """)