PY-17265 Add missing test on handling imports for moved local function
[idea/community.git] / python / testSrc / com / jetbrains / python / refactoring / PyMakeFunctionTopLevelTest.java
1 /*
2  * Copyright 2000-2015 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;
17
18 import com.intellij.openapi.command.WriteCommandAction;
19 import com.intellij.openapi.roots.ModuleRootManager;
20 import com.intellij.openapi.util.io.FileUtil;
21 import com.intellij.openapi.vfs.VirtualFile;
22 import com.intellij.testFramework.PlatformTestUtil;
23 import com.intellij.util.IncorrectOperationException;
24 import com.jetbrains.python.PyBundle;
25 import com.jetbrains.python.fixtures.PyTestCase;
26 import com.jetbrains.python.psi.LanguageLevel;
27 import com.jetbrains.python.psi.PyFunction;
28 import com.jetbrains.python.psi.impl.PyPsiUtils;
29 import com.jetbrains.python.refactoring.move.makeFunctionTopLevel.PyMakeLocalFunctionTopLevelProcessor;
30 import com.jetbrains.python.refactoring.move.makeFunctionTopLevel.PyMakeMethodTopLevelProcessor;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33
34 import java.io.IOException;
35
36 /**
37  * @author Mikhail Golubev
38  */
39 public class PyMakeFunctionTopLevelTest extends PyTestCase {
40
41   public void doTest(@Nullable String errorMessage) {
42     myFixture.configureByFile(getTestName(true) + ".py");
43     runRefactoring(null, errorMessage);
44     if (errorMessage == null) {
45       myFixture.checkResultByFile(getTestName(true) + ".after.py");
46     }
47   }
48
49   private void doTestSuccess() {
50     doTest(null);
51   }
52
53   private void doTestFailure(@NotNull String message) {
54     doTest(message);
55   }
56
57   private void runRefactoring(@Nullable String destination, @Nullable String errorMessage) {
58     final PyFunction function = assertInstanceOf(myFixture.getElementAtCaret(), PyFunction.class);
59     if (destination == null) {
60       destination = PyPsiUtils.getContainingFilePath(function);
61     }
62     else {
63       final VirtualFile srcRoot = ModuleRootManager.getInstance(myFixture.getModule()).getSourceRoots()[0];
64       destination = FileUtil.join(srcRoot.getPath(), destination);
65     }
66     assertNotNull(destination);
67     final String finalDestination = destination;
68     try {
69       WriteCommandAction.runWriteCommandAction(myFixture.getProject(), () -> {
70         if (function.getContainingClass() != null) {
71           new PyMakeMethodTopLevelProcessor(function, finalDestination).run();
72         }
73         else {
74           new PyMakeLocalFunctionTopLevelProcessor(function, finalDestination).run();
75         }
76       });
77     }
78     catch (IncorrectOperationException e) {
79       if (errorMessage == null) {
80         fail("Refactoring failed unexpectedly with message: " + e.getMessage());
81       }
82       assertEquals(errorMessage, e.getMessage());
83     }
84   }
85
86   private void doMultiFileTest(@Nullable String destination, @Nullable String errorMessage) throws IOException {
87     final String rootBeforePath = getTestName(true) + "/before";
88     final String rootAfterPath = getTestName(true) + "/after";
89     final VirtualFile copiedDirectory = myFixture.copyDirectoryToProject(rootBeforePath, "");
90     myFixture.configureByFile("main.py");
91     runRefactoring(destination, errorMessage);
92     if (errorMessage == null) {
93       PlatformTestUtil.assertDirectoriesEqual(getVirtualFileByName(getTestDataPath() + rootAfterPath), copiedDirectory);
94     }
95   }
96
97   //private static boolean isActionEnabled() {
98   //  final PyMakeFunctionTopLevelRefactoring action = new PyMakeFunctionTopLevelRefactoring();
99   //  final TestActionEvent event = new TestActionEvent(action);
100   //  action.beforeActionPerformedUpdate(event);
101   //  return event.getPresentation().isEnabled();
102   //}
103
104   // PY-6637
105   public void testLocalFunctionSimple() {
106     doTestSuccess();
107   }
108
109   // PY-6637
110   //public void testRefactoringAvailability() {
111   //  myFixture.configureByFile(getTestName(true) + ".py");
112   //
113   //  final PsiFile file = myFixture.getFile();
114   //  moveByText("func");
115   //  assertFalse(isActionEnabled());
116   //  moveByText("local");
117   //  assertTrue(isActionEnabled());
118   //
119   //  // move to "def" keyword
120   //  myFixture.getEditor().getCaretModel().moveCaretRelatively(-3, 0, false, false, false);
121   //  final PsiElement tokenAtCaret = file.findElementAt(myFixture.getCaretOffset());
122   //  assertNotNull(tokenAtCaret);
123   //  assertEquals(tokenAtCaret.getNode().getElementType(), PyTokenTypes.DEF_KEYWORD);
124   //  assertTrue(isActionEnabled());
125   //
126   //  moveByText("method");
127   //  assertTrue(isActionEnabled());
128   //
129   //  moveByText("static_method");
130   //  assertFalse(isActionEnabled());
131   //  moveByText("class_method");
132   //  assertFalse(isActionEnabled());
133   //
134   //  // Overridden method
135   //  moveByText("overridden_method");
136   //  assertFalse(isActionEnabled());
137   //
138   //  // Overriding method
139   //  moveByText("upper");
140   //  assertFalse(isActionEnabled());
141   //
142   //  moveByText("property");
143   //  assertFalse(isActionEnabled());
144   //  moveByText("__magic__");
145   //  assertFalse(isActionEnabled());
146   //}
147
148   // PY-6637
149   public void testLocalFunctionNonlocalReferenceToOuterScope() {
150     runWithLanguageLevel(LanguageLevel.PYTHON30,
151                          () -> doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.nonlocal.writes")));
152   }
153
154   // PY-6637
155   public void testLocalFunctionNonlocalReferencesInInnerFunction() {
156     runWithLanguageLevel(LanguageLevel.PYTHON30, this::doTestSuccess);
157   }
158
159   // PY-6637
160   public void testLocalFunctionReferenceToSelf() {
161     doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.self.reads"));
162   }
163
164   public void testMethodNonlocalReferenceToOuterScope() {
165     runWithLanguageLevel(LanguageLevel.PYTHON30,
166                          () -> doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.nonlocal.writes")));
167   }
168
169   public void testMethodOuterScopeReads() {
170     doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.outer.scope.reads"));
171   }
172
173   public void testMethodOtherMethodCalls() {
174     doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.method.calls"));
175   }
176
177   public void testMethodAttributeWrites() {
178     doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.attribute.writes"));
179   }
180
181   public void testMethodReadPrivateAttributes() {
182     doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.private.attributes"));
183   }
184
185   public void testMethodSelfUsedAsOperand() {
186     doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.special.usage.of.self"));
187   }
188
189   public void testMethodOverriddenSelf() {
190     doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.special.usage.of.self"));
191   }
192
193   public void testMethodSingleAttributeRead() {
194     doTestSuccess();
195   }
196
197   public void testMethodMultipleAttributesReadReferenceQualifier() {
198     doTestSuccess();
199   }
200
201   public void testMethodMultipleAttributesConstructorQualifier() {
202     doTestSuccess();
203   }
204
205   public void testMethodImportUpdates() throws IOException {
206     doMultiFileTest(null, null);
207   }
208
209   public void testMethodMoveToOtherFile() throws IOException {
210     doMultiFileTest("util.py", null);
211   }
212
213   public void testLocalFunctionMoveToOtherFile() throws IOException {
214     doMultiFileTest("util.py", null);
215   }
216
217   public void testMethodCalledViaClass() {
218     doTestSuccess();
219   }
220
221   public void testMethodUniqueNameOfExtractedQualifier() {
222     doTestSuccess();
223   }
224
225   public void testMethodUniqueParamNames() {
226     doTestSuccess();
227   }
228
229   public void testRecursiveMethod() {
230     doTestSuccess();
231   }
232
233   public void testRecursiveLocalFunction() {
234     doTestSuccess();
235   }
236
237   public void testMethodNoNewParams() {
238     doTestSuccess();
239   }
240
241   public void testMethodNotImportableDestinationFile() throws IOException {
242     doMultiFileTest("not-importable.py", PyBundle.message("refactoring.move.error.cannot.use.module.name.$0", "not-importable.py"));
243   }
244
245   public void testLocalFunctionNameCollision() {
246     doTestFailure(PyBundle.message("refactoring.move.error.destination.file.contains.function.$0", "nested"));
247   }
248
249   @Override
250   protected String getTestDataPath() {
251     return super.getTestDataPath() + "/refactoring/makeFunctionTopLevel/";
252   }
253 }