blob: c96bdc2397e167942d6407f8e2a694bde42399fb [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.refactoring.classes.extractSuperclass;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.util.ArrayUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyElement;
import com.jetbrains.python.refactoring.classes.PyClassRefactoringTest;
import com.jetbrains.python.refactoring.classes.membersManager.MembersManager;
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Dennis.Ushakov
*/
public class PyExtractSuperclassTest extends PyClassRefactoringTest {
public PyExtractSuperclassTest() {
super("extractsuperclass");
}
// Checks that moving methods between files moves imports as well
public void testImportMultiFile() throws Throwable {
multiFileTestHelper(".do_useful_stuff", false);
}
// Checks that moving methods between files moves superclass expressions as well
public void testMoveExtends() throws Throwable {
multiFileTestHelper("TheParentOfItAll", false);
}
// Checks that moving methods between files moves superclass expressions regardless import style (q.name or name)
public void testMoveExtendsCheckReference() throws Throwable {
multiFileTestHelper("TheParentOfItAll", false);
}
// Extracts method as abstract
public void testMoveAndMakeAbstract() throws Throwable {
multiFileTestHelper(".foo_method", true);
}
// Extracts method as abstract and ensures that newly created class imports ABC in Py3
public void testMoveAndMakeAbstractImportExistsPy3() throws Throwable {
setLanguageLevel(LanguageLevel.PYTHON30);
configureMultiFile("abc");
multiFileTestHelper(".foo_method", true);
}
/**
* Moves member from class <pre>MyClass</pre> in module <pre>source_module</pre> to class <pre>NewParent</pre> in module <pre>dest_module</pre>.
* Ensures it is moved correctly.
*
* @param memberToMove name of the member to move
*/
private void multiFileTestHelper(@NotNull final String memberToMove, final boolean toAbstract) {
final String[] modules = {"dest_module", "source_module"};
configureMultiFile(ArrayUtil.mergeArrays(modules, "shared_module"));
myFixture.configureByFile("source_module.py");
final String sourceClass = "MyClass";
final PyMemberInfo<PyElement> member = findMemberInfo(sourceClass, memberToMove);
member.setToAbstract(toAbstract);
final String destUrl = myFixture.getFile().getVirtualFile().getParent().findChild("dest_module.py").getUrl();
new WriteCommandAction.Simple(myFixture.getProject()) {
@Override
protected void run() throws Throwable {
PyExtractSuperclassHelper.extractSuperclass(findClass(sourceClass), Collections.singleton(member), "NewParent", destUrl);
}
}.execute();
checkMultiFile(modules);
}
public void testSimple() throws Exception {
doSimpleTest("Foo", "Suppa", null, true, ".foo");
}
public void testInstanceNotDeclaredInInit() throws Exception {
doSimpleTest("Child", "Parent", null, true, "#eggs");
}
public void testWithSuper() throws Exception {
doSimpleTest("Foo", "Suppa", null, true, ".foo");
}
public void testWithImport() throws Exception {
doSimpleTest("A", "Suppa", null, false, ".foo");
}
// PY-12175
public void testImportNotBroken() throws Exception {
myFixture.copyFileToProject("/refactoring/extractsuperclass/shared.py", "shared.py");
doSimpleTest("Source", "DestClass", null, true, "SharedClass");
}
// PY-12175 but between several files
public void testImportNotBrokenManyFiles() throws Exception {
multiFileTestHelper("SharedClass", false);
}
public void testMoveFields() throws Exception {
doSimpleTest("FromClass", "ToClass", null, true, "#instance_field", "#CLASS_FIELD");
}
public void testProperties() throws Exception {
doSimpleTest("FromClass", "ToClass", null, true, "#C", "#a", "._get", ".foo");
}
private void doSimpleTest(final String className,
final String superclassName,
final String expectedError,
final boolean sameFile,
final String... membersName) throws Exception {
try {
String baseName = "/refactoring/extractsuperclass/" + getTestName(true);
myFixture.configureByFile(baseName + ".before.py");
final PyClass clazz = findClass(className);
final List<PyMemberInfo<PyElement>> members = new ArrayList<PyMemberInfo<PyElement>>();
for (String memberName : membersName) {
final PyElement member = findMember(className, memberName);
members.add(MembersManager.findMember(clazz, member));
}
new WriteCommandAction.Simple(myFixture.getProject()) {
@Override
protected void run() throws Throwable {
//noinspection ConstantConditions
final String url = sameFile ? myFixture.getFile().getVirtualFile().getUrl() :
myFixture.getFile().getVirtualFile().getParent().getUrl();
PyExtractSuperclassHelper.extractSuperclass(clazz, members, superclassName, url);
}
}.execute();
myFixture.checkResultByFile(baseName + ".after.py");
}
catch (Exception e) {
if (expectedError == null) throw e;
assertEquals(expectedError, e.getMessage());
}
}
public void testMultifileNew() {
String baseName = "/refactoring/extractsuperclass/multifile/";
myFixture.configureByFile(baseName + "source.py");
final String className = "Foo";
final String superclassName = "Suppa";
final PyClass clazz = findClass(className);
final List<PyMemberInfo<PyElement>> members = new ArrayList<PyMemberInfo<PyElement>>();
final PyElement member = findMember(className, ".foo");
members.add(MembersManager.findMember(clazz, member));
final VirtualFile base_dir = myFixture.getFile().getVirtualFile().getParent();
new WriteCommandAction.Simple(myFixture.getProject()) {
@Override
protected void run() throws Throwable {
//noinspection ConstantConditions
final String path = base_dir.getPath() + "/a/b";
PyExtractSuperclassHelper.extractSuperclass(clazz, members, superclassName, path);
}
}.execute();
final PsiManager psi_mgr = PsiManager.getInstance(myFixture.getProject());
VirtualFile vfile = base_dir.findChild("a");
assertTrue(vfile.isDirectory());
vfile = vfile.findChild(PyNames.INIT_DOT_PY);
assertNotNull(vfile);
vfile = base_dir.findChild("a").findChild("b");
assertTrue(vfile.isDirectory());
vfile = vfile.findChild(PyNames.INIT_DOT_PY);
assertNotNull(vfile);
PsiFile psi_file = psi_mgr.findFile(vfile);
String result = psi_file.getText().trim();
File expected_file = new File(getTestDataPath() + baseName, "target.new.py");
String expected = psi_mgr.findFile(LocalFileSystem.getInstance().findFileByIoFile(expected_file)).getText().trim();
assertEquals(expected, result);
}
public void testMultifileAppend() {
// this is half-copy-paste of testMultifileNew. generalization won't make either easier to follow.
String baseName = "/refactoring/extractsuperclass/multifile/";
myFixture.configureByFiles(
baseName + "source.py",
baseName + "a/__init__.py",
baseName + "a/b/__init__.py",
baseName + "a/b/foo.py"
);
final String className = "Foo";
final String superclassName = "Suppa";
final PyClass clazz = findClass(className);
final List<PyMemberInfo<PyElement>> members = new ArrayList<PyMemberInfo<PyElement>>();
final PyElement member = findMember(className, ".foo");
members.add(MembersManager.findMember(clazz, member));
final VirtualFile base_dir = myFixture.getFile().getVirtualFile().getParent();
new WriteCommandAction.Simple(myFixture.getProject()) {
@Override
protected void run() throws Throwable {
//TODO: Test via presenter
//noinspection ConstantConditions
final String path = base_dir.getPath() + "/a/b";
PyExtractSuperclassHelper.extractSuperclass(clazz, members, superclassName, path + "/foo.py");
}
}.execute();
final PsiManager psi_mgr = PsiManager.getInstance(myFixture.getProject());
VirtualFile vfile = base_dir.findChild("a");
assertTrue(vfile.isDirectory());
vfile = vfile.findChild(PyNames.INIT_DOT_PY);
assertNotNull(vfile);
vfile = base_dir.findChild("a").findChild("b");
assertTrue(vfile.isDirectory());
assertNotNull(vfile.findChild(PyNames.INIT_DOT_PY));
vfile = vfile.findChild("foo.py");
assertNotNull(vfile);
PsiFile psi_file = psi_mgr.findFile(vfile);
String result = psi_file.getText().trim();
File expected_file = new File(getTestDataPath() + baseName, "target.append.py");
String expected = psi_mgr.findFile(LocalFileSystem.getInstance().findFileByIoFile(expected_file)).getText().trim();
assertEquals(expected, result);
}
}