import unittest
|
|
import tempfile
|
|
import shutil
|
|
import os
|
|
import mock
|
|
|
|
from elpy import refactor
|
|
|
|
|
|
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)
|
|
offset = contents.find("_|_")
|
|
if offset > -1:
|
|
contents = contents[:offset] + contents[offset + 3:]
|
|
with open(filename, "w") as f:
|
|
f.write(contents)
|
|
return filename, offset
|
|
|
|
|
|
class TestGetRefactorOptions(RefactorTestCase):
|
|
def test_should_only_return_importsmodule_if_not_on_symbol(self):
|
|
filename, offset = self.create_file("foo.py",
|
|
"import foo\n"
|
|
"_|_")
|
|
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",
|
|
"_|_\n"
|
|
"import foo\n")
|
|
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\n"
|
|
"_|_")
|
|
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\n"
|
|
"\n"
|
|
"d = datetime(2013, 4, 7)\n")
|
|
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.assertEqual(change['contents'],
|
|
"import datetime\n"
|
|
"\n"
|
|
"d = datetime.datetime(2013, 4, 7)\n")
|
|
|
|
|
|
@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\n"
|
|
"import datetime, json\n"
|
|
"\n"
|
|
"obj = json.dumps(23)\n"
|
|
"unittest.TestCase()\n")
|
|
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.assertEqual(change['contents'],
|
|
"import json\n"
|
|
"import unittest\n"
|
|
"\n"
|
|
"\n"
|
|
"obj = json.dumps(23)\n"
|
|
"unittest.TestCase()\n")
|
|
|
|
|
|
@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):\n"
|
|
" def _|_foo(self):\n"
|
|
" return 5\n"
|
|
"\n"
|
|
" def bar(self):\n"
|
|
" return self.foo()\n")
|
|
file2, offset2 = self.create_file(
|
|
"bar.py",
|
|
"import foo\n"
|
|
"\n"
|
|
"\n"
|
|
"x = foo.Foo()\n"
|
|
"x.foo()")
|
|
ref = refactor.Refactor(self.project_root, filename)
|
|
first, second = ref.refactor_rename_at_point(offset, "frob")
|
|
if first['file'] == filename:
|
|
a, b = first, second
|
|
else:
|
|
a, b = second, first
|
|
self.assertEqual(a['action'], 'change')
|
|
self.assertEqual(a['file'], filename)
|
|
self.assertEqual(a['contents'],
|
|
"class Foo(object):\n"
|
|
" def frob(self):\n"
|
|
" return 5\n"
|
|
"\n"
|
|
" def bar(self):\n"
|
|
" return self.frob()\n")
|
|
self.assertEqual(b['action'], 'change')
|
|
self.assertEqual(b['file'], file2)
|
|
self.assertEqual(b['contents'],
|
|
"import foo\n"
|
|
"\n"
|
|
"\n"
|
|
"x = foo.Foo()\n"
|
|
"x.frob()")
|
|
|
|
|
|
@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\n"
|
|
"foo.os\n")
|
|
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\n"
|
|
"foo.os\n")
|
|
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.assertEqual(a['contents'],
|
|
"import frob.foo\n"
|
|
"frob.foo.os\n")
|
|
|
|
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):\n"
|
|
" return a + b\n"
|
|
"\n"
|
|
"x = _|_add(2, 3)\n"
|
|
"y = add(17, 4)\n")
|
|
|
|
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.assertEqual(change['contents'],
|
|
"def add(a, b):\n"
|
|
" return a + b\n"
|
|
"\n"
|
|
"x = 2 + 3\n"
|
|
"y = add(17, 4)\n")
|
|
|
|
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.assertEqual(change['contents'],
|
|
"x = 2 + 3\n"
|
|
"y = 17 + 4\n")
|
|
|
|
|
|
@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):\n"
|
|
" def spaghetti(self, a, b):\n"
|
|
" _|_x = a + 5\n"
|
|
" y = b + 23\n"
|
|
" return y\n")
|
|
|
|
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):\n"
|
|
" def spaghetti(self, a, b):\n"
|
|
" return self.calc(a, b)\n"
|
|
"\n"
|
|
" def calc(self, a, b):\n"
|
|
" x = a + 5\n"
|
|
" y = b + 23\n"
|
|
" return y\n")
|
|
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)")
|
|
if change['contents'] == expected2:
|
|
self.assertEqual(change['contents'], expected2)
|
|
else:
|
|
self.assertEqual(change['contents'], expected)
|
|
|
|
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):\n"
|
|
" def spaghetti(self, a, b):\n"
|
|
" return calc(a, b)\n"
|
|
"\n"
|
|
"def calc(a, b):\n"
|
|
" x = a + 5\n"
|
|
" y = b + 23\n"
|
|
" return y\n")
|
|
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'] == expected2:
|
|
self.assertEqual(change['contents'], expected2)
|
|
else:
|
|
self.assertEqual(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):\n"
|
|
" temp = a + b\n"
|
|
" return temp * c\n"
|
|
"\n"
|
|
"f = 1 + 2\n"
|
|
"g = f * 3\n")
|
|
|
|
ref = refactor.Refactor(self.project_root, filename)
|
|
(change,) = ref.refactor_use_function(offset)
|
|
|
|
self.assertEqual(change['action'], 'change')
|
|
self.assertEqual(change['file'], filename)
|
|
self.assertEqual(change['contents'],
|
|
"def add_and_multiply(a, b, c):\n"
|
|
" temp = a + b\n"
|
|
" return temp * c\n"
|
|
"\n"
|
|
"g = add_and_multiply(1, 2, 3)\n")
|