Klimi's new dotfiles with stow.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

545 lines
20 KiB

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