|
|
import unittestimport tempfileimport shutilimport osimport mockimport sys
from elpy import refactorfrom 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) """)
|