replaced <code></code> with more concise {@code}
[idea/community.git] / python / testSrc / com / jetbrains / python / refactoring / classes / PyClassRefactoringTest.java
1 /*
2  * Copyright 2000-2013 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.jetbrains.python.refactoring.classes;
17
18 import com.intellij.openapi.application.ApplicationManager;
19 import com.intellij.openapi.command.CommandProcessor;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.refactoring.BaseRefactoringProcessor;
22 import com.jetbrains.python.fixtures.PyTestCase;
23 import com.jetbrains.python.psi.PyClass;
24 import com.jetbrains.python.psi.PyElement;
25 import com.jetbrains.python.psi.PyFunction;
26 import com.jetbrains.python.psi.PyTargetExpression;
27 import com.jetbrains.python.psi.stubs.PyClassNameIndex;
28 import com.jetbrains.python.refactoring.classes.membersManager.MembersManager;
29 import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
30 import org.hamcrest.Matchers;
31 import org.jetbrains.annotations.NotNull;
32 import org.junit.Assert;
33
34 import java.util.Collection;
35
36 /**
37  * @author Dennis.Ushakov
38  */
39 public abstract class PyClassRefactoringTest extends PyTestCase {
40   @NotNull
41   private final String myRefactoringName;
42
43   /**
44    * @param refactoringName name of the refactoring. It will be used as folder name for tests
45    */
46   protected PyClassRefactoringTest(@NotNull final String refactoringName) {
47     myRefactoringName = refactoringName;
48   }
49
50   /**
51    * Finds memberInfo by class name and member name.
52    *
53    * @param clazzName  name of class
54    * @param memberName name of member (See {@link #findMember(String, String)} for naming protocol)
55    * @return member info
56    * @see #findMember(String, String)
57    */
58   @NotNull
59   protected PyMemberInfo<PyElement> findMemberInfo(@NotNull final String clazzName, @NotNull final String memberName) {
60     final PyClass clazz = findClass(clazzName);
61     return MembersManager.findMember(clazz, findMember(clazzName, memberName));
62   }
63
64   /**
65    * @param className  class where member should be found
66    * @param memberName member that starts with dot ({@code .}) is treated as method.
67    *                   member that starts with dash ({@code #}) is treated as attribute.
68    *                   It is treated parent class otherwise
69    * @return member or null if not found
70    */
71   @NotNull
72   protected PyElement findMember(@NotNull final String className, @NotNull String memberName) {
73     final PyElement result;
74     //TODO: Get rid of this chain of copy pastes
75     if (memberName.contains(".")) {
76       result = findMethod(className, memberName.substring(1));
77     }
78     else if (memberName.contains("#")) {
79       result = findField(className, memberName.substring(1));
80     }
81     else {
82       result = findClass(memberName);
83     }
84     Assert.assertNotNull(String.format("No member %s found in class %s", memberName, className), result);
85     return result;
86   }
87
88   private PyElement findField(final String className, final String memberName) {
89     final PyClass aClass = findClass(className);
90     final PyTargetExpression attribute = aClass.findClassAttribute(memberName, false, null);
91     if (attribute != null) {
92       return attribute;
93     }
94     return aClass.findInstanceAttribute(memberName, false);
95   }
96
97   private PyFunction findMethod(final String className, final String name) {
98     final PyClass clazz = findClass(className);
99     return clazz.findMethodByName(name, false, null);
100   }
101
102   protected PyClass findClass(final String name) {
103     final Project project = myFixture.getProject();
104     final Collection<PyClass> classes = PyClassNameIndex.find(name, project, false);
105     Assert.assertThat(String.format("Expected one class named %s", name), classes, Matchers.hasSize(1));
106     return classes.iterator().next();
107   }
108
109
110   protected void moveViaProcessor(@NotNull Project project, @NotNull final BaseRefactoringProcessor processor) {
111     CommandProcessor.getInstance().executeCommand(project, () -> ApplicationManager.getApplication().runWriteAction(() -> processor.run()), null, null);
112   }
113
114   /**
115    * Adds several files to project from folder {@link #myRefactoringName} with extension <pre>py</pre>.
116    * Call it <strong>before</strong> refactoring.
117    * After refactoring use {@link #checkMultiFile(String...)} to make sure refactoring is ok.
118    *
119    * @param fileNamesNoExtensions file (module) names to add with out of extensions
120    * @see #checkMultiFile(String...)
121    */
122   protected void configureMultiFile(@NotNull final String... fileNamesNoExtensions) {
123     final String baseName = getMultiFileBaseName() + "/";
124
125     for (final String fileNameNoExtension : fileNamesNoExtensions) {
126       final String fileNameBefore = String.format("%s.py", fileNameNoExtension);
127       myFixture.copyFileToProject(baseName + fileNameBefore, fileNameBefore);
128     }
129   }
130
131   /**
132    * Checks files <strong>after</strong> refactoring. See {@link #configureMultiFile(String...)} for more info.
133    *
134    * @param fileNamesNoExtensions file names to check with out of extension
135    * @see #configureMultiFile(String...)
136    */
137   protected void checkMultiFile(@NotNull final String... fileNamesNoExtensions) {
138     for (final String fileNameNoExtension : fileNamesNoExtensions) {
139       final String fileNameAfter = String.format("%s.after.py", fileNameNoExtension);
140       final String fileNameBefore = String.format("%s.py", fileNameNoExtension);
141       myFixture.checkResultByFile(fileNameBefore, "/" + getMultiFileBaseName() + "/" + fileNameAfter, true);
142     }
143   }
144
145   /**
146    * @return folder name with {@link #myRefactoringName} and test name added
147    */
148   @NotNull
149   protected String getMultiFileBaseName() {
150     return "refactoring/" + myRefactoringName + "/" + getTestName(true);
151   }
152 }