|
|
- import unittest
- import tempfile
- import shutil
- import os
- import mock
- import sys
-
- from elpy import refactor
- from textwrap import dedent
-
-
- class RefactorTestCase(unittest.TestCase):
- def setUp(self):
- self.project_root = tempfile.mkdtemp(prefix="test-refactor-root")
- self.addCleanup(shutil.rmtree, self.project_root,
- ignore_errors=True)
-
- def create_file(self, name, contents=""):
- filename = os.path.join(self.project_root, name)
- contents = dedent(contents)
- offset = contents.find("_|_")
- if offset > -1:
- contents = contents[:offset] + contents[offset + 3:]
- with open(filename, "w") as f:
- f.write(contents)
- return filename, offset
-
- def assertSourceEqual(self, first, second, msg=None):
- """Fail if the two objects are unequal, ignoring indentation."""
- self.assertEqual(dedent(first), dedent(second), msg=msg)
-
-
- class TestGetRefactorOptions(RefactorTestCase):
- def test_should_only_return_importsmodule_if_not_on_symbol(self):
- filename, offset = self.create_file("foo.py",
- """\
- import foo
- _|_""")
- ref = refactor.Refactor(self.project_root, filename)
- options = ref.get_refactor_options(offset)
- self.assertTrue(all(opt['category'] in ('Imports',
- 'Module')
- for opt in options))
- filename, offset = self.create_file("foo.py",
- """\
- _|_
- import foo""")
- ref = refactor.Refactor(self.project_root, filename)
- options = ref.get_refactor_options(offset)
- self.assertTrue(all(opt['category'] in ('Imports',
- 'Module')
- for opt in options))
-
- def test_should_return_all_if_on_symbol(self):
- filename, offset = self.create_file("foo.py",
- "import _|_foo")
- ref = refactor.Refactor(self.project_root, filename)
- options = ref.get_refactor_options(offset)
- self.assertTrue(all(opt['category'] in ('Imports',
- 'Method',
- 'Module',
- 'Symbol')
- for opt in options))
-
- def test_should_return_only_region_if_endoffset(self):
- filename, offset = self.create_file("foo.py",
- "import foo")
- ref = refactor.Refactor(self.project_root, filename)
- options = ref.get_refactor_options(offset, 5)
- self.assertTrue(all(opt['category'] == 'Region'
- for opt in options))
-
- @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope")
- def test_should_treat_from_import_special(self):
- filename, offset = self.create_file("foo.py",
- """\
- import foo
- _|_""")
- ref = refactor.Refactor(self.project_root, filename)
- options = ref.get_refactor_options(offset)
- self.assertFalse(any(opt['name'] == "refactor_froms_to_imports"
- for opt in options))
- filename, offset = self.create_file("foo.py",
- "imp_|_ort foo")
- ref = refactor.Refactor(self.project_root, filename)
- options = ref.get_refactor_options(offset)
- self.assertTrue(any(opt['name'] == "refactor_froms_to_imports"
- for opt in options))
-
-
- class TestGetChanges(RefactorTestCase):
- def test_should_fail_if_method_is_not_refactoring(self):
- filename, offset = self.create_file("foo.py")
- ref = refactor.Refactor(self.project_root, filename)
- self.assertRaises(ValueError, ref.get_changes, "bad_name")
-
- def test_should_return_method_results(self):
- filename, offset = self.create_file("foo.py")
- ref = refactor.Refactor(self.project_root, filename)
- with mock.patch.object(ref, 'refactor_extract_method') as test:
- test.return_value = "Meep!"
- self.assertEqual(ref.get_changes("refactor_extract_method",
- 1, 2),
- "Meep!")
- test.assert_called_with(1, 2)
-
-
- @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope")
- class TestIsOnSymbol(RefactorTestCase):
- def test_should_find_symbol(self):
- filename, offset = self.create_file("test.py", "__B_|_AR = 100")
- r = refactor.Refactor(self.project_root, filename)
- self.assertTrue(r._is_on_symbol(offset))
-
- # Issue #111
- def test_should_find_symbol_with_underscores(self):
- filename, offset = self.create_file("test.py", "_|___BAR = 100")
- r = refactor.Refactor(self.project_root, filename)
- self.assertTrue(r._is_on_symbol(offset))
-
- def test_should_not_find_weird_places(self):
- filename, offset = self.create_file("test.py", "hello = _|_ 1 + 1")
- r = refactor.Refactor(self.project_root, filename)
- self.assertFalse(r._is_on_symbol(offset))
-
-
- @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope")
- class TestFromsToImports(RefactorTestCase):
- def test_should_refactor(self):
- filename, offset = self.create_file(
- "foo.py",
- """\
- _|_from datetime import datetime
-
- d = datetime(2013, 4, 7)
- """)
- ref = refactor.Refactor(self.project_root, filename)
- (change,) = ref.get_changes("refactor_froms_to_imports", offset)
- self.assertEqual(change['action'], 'change')
- self.assertEqual(change['file'], filename)
- self.assertSourceEqual(change['contents'],
- """\
- import datetime
-
- d = datetime.datetime(2013, 4, 7)
- """)
-
-
- @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope")
- class TestOrganizeImports(RefactorTestCase):
- def test_should_refactor(self):
- filename, offset = self.create_file(
- "foo.py",
- """\
- import unittest, base64
- import datetime, json
-
- obj = json.dumps(23)
- unittest.TestCase()
- """)
- ref = refactor.Refactor(self.project_root, filename)
- (change,) = ref.get_changes("refactor_organize_imports")
- self.assertEqual(change['action'], 'change')
- self.assertEqual(change['file'], filename)
- self.assertSourceEqual(change['contents'],
- """\
- import json
- import unittest
-
-
- obj = json.dumps(23)
- unittest.TestCase()
- """)
-
-
- @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope")
- class TestModuleToPackage(RefactorTestCase):
- def test_should_refactor(self):
- filename, offset = self.create_file(
- "foo.py",
- "_|_import os\n")
- ref = refactor.Refactor(self.project_root, filename)
- changes = ref.refactor_module_to_package()
- a, b, c = changes
- # Not sure why the a change is there. It's a CHANGE that
- # changes nothing...
- self.assertEqual(a['diff'], '')
-
- self.assertEqual(b['action'], 'create')
- self.assertEqual(b['type'], 'directory')
- self.assertEqual(b['path'], os.path.join(self.project_root, "foo"))
-
- self.assertEqual(c['action'], 'move')
- self.assertEqual(c['type'], 'file')
- self.assertEqual(c['source'], os.path.join(self.project_root,
- "foo.py"))
- self.assertEqual(c['destination'], os.path.join(self.project_root,
- "foo/__init__.py"))
-
-
- @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope")
- class TestRenameAtPoint(RefactorTestCase):
- def test_should_refactor(self):
- filename, offset = self.create_file(
- "foo.py",
- """\
- class Foo(object):
- def _|_foo(self):
- return 5
-
- def bar(self):
- return self.foo()
- """)
- file2, offset2 = self.create_file(
- "bar.py",
- """\
- import foo
-
-
- x = foo.Foo()
- x.foo()""")
- ref = refactor.Refactor(self.project_root, filename)
- first, second = ref.refactor_rename_at_point(offset, "frob",
- in_hierarchy=False,
- docs=False)
- if first['file'] == filename:
- a, b = first, second
- else:
- a, b = second, first
- self.assertEqual(a['action'], 'change')
- self.assertEqual(a['file'], filename)
- self.assertSourceEqual(a['contents'],
- """\
- class Foo(object):
- def frob(self):
- return 5
-
- def bar(self):
- return self.frob()
- """)
- self.assertEqual(b['action'], 'change')
- self.assertEqual(b['file'], file2)
- self.assertSourceEqual(b['contents'],
- """\
- import foo
-
-
- x = foo.Foo()
- x.frob()""")
-
- def test_should_refactor_in_hierarchy(self):
- filename, offset = self.create_file(
- "foo.py",
- """\
- class Foo(object):
- def _|_foo(self):
- return 5
-
- def bar(self):
- return self.foo()
-
- class Bar(Foo):
- def foo(self):
- return 42
-
- class Baz(object):
- def foo(self):
- return 42
- """)
- file2, offset2 = self.create_file(
- "bar.py",
- """\
- import foo
-
-
- x, y, z = foo.Foo(), foo.Bar(), foo.Baz()
- x.foo()
- y.foo()
- z.foo()""")
- ref = refactor.Refactor(self.project_root, filename)
- first, second = ref.refactor_rename_at_point(offset, "frob",
- in_hierarchy=True,
- docs=False)
- if first['file'] == filename:
- a, b = first, second
- else:
- a, b = second, first
- self.assertEqual(a['action'], 'change')
- self.assertEqual(a['file'], filename)
- self.assertSourceEqual(a['contents'],
- """\
- class Foo(object):
- def frob(self):
- return 5
-
- def bar(self):
- return self.frob()
-
- class Bar(Foo):
- def frob(self):
- return 42
-
- class Baz(object):
- def foo(self):
- return 42
- """)
- self.assertEqual(b['action'], 'change')
- self.assertEqual(b['file'], file2)
- self.assertSourceEqual(b['contents'],
- """\
- import foo
-
-
- x, y, z = foo.Foo(), foo.Bar(), foo.Baz()
- x.frob()
- y.frob()
- z.foo()""")
-
- def test_should_refactor_in_docstrings(self):
- filename, offset = self.create_file(
- "foo.py",
- """\
- class Foo(object):
- "Frobnicate the foo"
- def _|_foo(self):
- return 5
-
- print("I'm an unrelated foo")
- """)
- ref = refactor.Refactor(self.project_root, filename)
- (change,) = ref.refactor_rename_at_point(offset, "frob",
- in_hierarchy=False,
- docs=True)
- self.assertEqual(change['action'], 'change')
- self.assertEqual(change['file'], filename)
- self.assertSourceEqual(change['contents'],
- """\
- class Foo(object):
- "Frobnicate the frob"
- def frob(self):
- return 5
-
- print("I'm an unrelated foo")
- """)
-
-
- @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope")
- class TestRenameCurrentModule(RefactorTestCase):
- def test_should_refactor(self):
- filename, offset = self.create_file(
- "foo.py",
- "_|_import os\n")
- file2, offset = self.create_file(
- "bar.py",
- """\
- _|_import foo
- foo.os
- """)
- dest = os.path.join(self.project_root, "frob.py")
- ref = refactor.Refactor(self.project_root, filename)
- a, b = ref.refactor_rename_current_module("frob")
-
- self.assertEqual(a['action'], 'change')
- self.assertEqual(a['file'], file2)
- self.assertEqual(a['contents'],
- "import frob\n"
- "frob.os\n")
-
- self.assertEqual(b['action'], 'move')
- self.assertEqual(b['type'], 'file')
- self.assertEqual(b['source'], filename)
- self.assertEqual(b['destination'], dest)
-
-
- @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope")
- class TestMoveModule(RefactorTestCase):
- def test_should_refactor(self):
- filename, offset = self.create_file(
- "foo.py",
- "_|_import os\n")
- file2, offset = self.create_file(
- "bar.py",
- """\
- _|_import foo
- foo.os
- """)
- dest = os.path.join(self.project_root, "frob")
- os.mkdir(dest)
- with open(os.path.join(dest, "__init__.py"), "w") as f:
- f.write("")
- ref = refactor.Refactor(self.project_root, filename)
- a, b = ref.refactor_move_module(dest)
-
- self.assertEqual(a['action'], 'change')
- self.assertEqual(a['file'], file2)
- self.assertSourceEqual(a['contents'],
- """\
- import frob.foo
- frob.foo.os
- """)
-
- self.assertEqual(b['action'], 'move')
- self.assertEqual(b['type'], 'file')
- self.assertEqual(b['source'], filename)
- self.assertEqual(b['destination'],
- os.path.join(dest, "foo.py"))
-
-
- @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope")
- class TestCreateInline(RefactorTestCase):
- def setUp(self):
- super(TestCreateInline, self).setUp()
- self.filename, self.offset = self.create_file(
- "foo.py",
- """\
- def add(a, b):
- return a + b
-
- x = _|_add(2, 3)
- y = add(17, 4)
- """)
-
- def test_should_refactor_single_occurrenc(self):
- ref = refactor.Refactor(self.project_root, self.filename)
- (change,) = ref.refactor_create_inline(self.offset, True)
-
- self.assertEqual(change['action'], 'change')
- self.assertEqual(change['file'], self.filename)
- self.assertSourceEqual(change['contents'],
- """\
- def add(a, b):
- return a + b
-
- x = 2 + 3
- y = add(17, 4)
- """)
-
- def test_should_refactor_all_occurrencs(self):
- ref = refactor.Refactor(self.project_root, self.filename)
- (change,) = ref.refactor_create_inline(self.offset, False)
-
- self.assertEqual(change['action'], 'change')
- self.assertEqual(change['file'], self.filename)
- self.assertSourceEqual(change['contents'],
- """\
- x = 2 + 3
- y = 17 + 4
- """)
-
-
- @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope")
- class TestExtractMethod(RefactorTestCase):
- def setUp(self):
- super(TestExtractMethod, self).setUp()
- self.filename, self.offset = self.create_file(
- "foo.py",
- """\
- class Foo(object):
- def spaghetti(self, a, b):
- _|_x = a + 5
- y = b + 23
- return y
- """)
-
- @unittest.skipIf(sys.version_info >= (3, 5), "Python 3.5 not supported")
- def test_should_refactor_local(self):
- ref = refactor.Refactor(self.project_root, self.filename)
- (change,) = ref.refactor_extract_method(self.offset, 104,
- "calc", False)
- self.assertEqual(change['action'], 'change')
- self.assertEqual(change['file'], self.filename)
- expected = """\
- class Foo(object):
- def spaghetti(self, a, b):
- return self.calc(a, b)
-
- def calc(self, a, b):
- x = a + 5
- y = b + 23
- return y
- """
- expected2 = expected.replace("return self.calc(a, b)",
- "return self.calc(b, a)")
- expected2 = expected2.replace("def calc(self, a, b)",
- "def calc(self, b, a)")
- # This is silly, but it's what we got.
- if change['contents'] == dedent(expected2):
- self.assertSourceEqual(change['contents'], expected2)
- else:
- self.assertSourceEqual(change['contents'], expected)
-
- @unittest.skipIf(sys.version_info >= (3, 5), "Python 3.5 not supported")
- def test_should_refactor_global(self):
- ref = refactor.Refactor(self.project_root, self.filename)
- (change,) = ref.refactor_extract_method(self.offset, 104,
- "calc", True)
- self.assertEqual(change['action'], 'change')
- self.assertEqual(change['file'], self.filename)
- expected = """\
- class Foo(object):
- def spaghetti(self, a, b):
- return calc(a, b)
-
- def calc(a, b):
- x = a + 5
- y = b + 23
- return y
- """
- expected2 = expected.replace("return calc(a, b)",
- "return calc(b, a)")
- expected2 = expected2.replace("def calc(a, b)",
- "def calc(b, a)")
- if change['contents'] == dedent(expected2):
- self.assertSourceEqual(change['contents'], expected2)
- else:
- self.assertSourceEqual(change['contents'], expected)
-
-
- @unittest.skipIf(not refactor.ROPE_AVAILABLE, "Requires Rope")
- class TestUseFunction(RefactorTestCase):
- def test_should_refactor(self):
- filename, offset = self.create_file(
- "foo.py",
- """\
- def _|_add_and_multiply(a, b, c):
- temp = a + b
- return temp * c
-
- f = 1 + 2
- g = f * 3
- """)
-
- ref = refactor.Refactor(self.project_root, filename)
- (change,) = ref.refactor_use_function(offset)
-
- self.assertEqual(change['action'], 'change')
- self.assertEqual(change['file'], filename)
- self.assertSourceEqual(change['contents'],
- """\
- def add_and_multiply(a, b, c):
- temp = a + b
- return temp * c
-
- g = add_and_multiply(1, 2, 3)
- """)
|