2 * Copyright 2000-2015 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.jetbrains.python.refactoring;
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;
34 import java.io.IOException;
37 * @author Mikhail Golubev
39 public class PyMakeFunctionTopLevelTest extends PyTestCase {
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");
49 private void doTestSuccess() {
53 private void doTestFailure(@NotNull String message) {
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);
63 final VirtualFile srcRoot = ModuleRootManager.getInstance(myFixture.getModule()).getSourceRoots()[0];
64 destination = FileUtil.join(srcRoot.getPath(), destination);
66 assertNotNull(destination);
67 final String finalDestination = destination;
69 WriteCommandAction.runWriteCommandAction(myFixture.getProject(), () -> {
70 if (function.getContainingClass() != null) {
71 new PyMakeMethodTopLevelProcessor(function, finalDestination).run();
74 new PyMakeLocalFunctionTopLevelProcessor(function, finalDestination).run();
78 catch (IncorrectOperationException e) {
79 if (errorMessage == null) {
80 fail("Refactoring failed unexpectedly with message: " + e.getMessage());
82 assertEquals(errorMessage, e.getMessage());
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);
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();
105 public void testLocalFunctionSimple() {
110 //public void testRefactoringAvailability() {
111 // myFixture.configureByFile(getTestName(true) + ".py");
113 // final PsiFile file = myFixture.getFile();
114 // moveByText("func");
115 // assertFalse(isActionEnabled());
116 // moveByText("local");
117 // assertTrue(isActionEnabled());
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());
126 // moveByText("method");
127 // assertTrue(isActionEnabled());
129 // moveByText("static_method");
130 // assertFalse(isActionEnabled());
131 // moveByText("class_method");
132 // assertFalse(isActionEnabled());
134 // // Overridden method
135 // moveByText("overridden_method");
136 // assertFalse(isActionEnabled());
138 // // Overriding method
139 // moveByText("upper");
140 // assertFalse(isActionEnabled());
142 // moveByText("property");
143 // assertFalse(isActionEnabled());
144 // moveByText("__magic__");
145 // assertFalse(isActionEnabled());
149 public void testLocalFunctionNonlocalReferenceToOuterScope() {
150 runWithLanguageLevel(LanguageLevel.PYTHON30,
151 () -> doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.nonlocal.writes")));
155 public void testLocalFunctionNonlocalReferencesInInnerFunction() {
156 runWithLanguageLevel(LanguageLevel.PYTHON30, this::doTestSuccess);
160 public void testLocalFunctionReferenceToSelf() {
161 doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.self.reads"));
164 public void testMethodNonlocalReferenceToOuterScope() {
165 runWithLanguageLevel(LanguageLevel.PYTHON30,
166 () -> doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.nonlocal.writes")));
169 public void testMethodOuterScopeReads() {
170 doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.outer.scope.reads"));
173 public void testMethodOtherMethodCalls() {
174 doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.method.calls"));
177 public void testMethodAttributeWrites() {
178 doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.attribute.writes"));
181 public void testMethodReadPrivateAttributes() {
182 doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.private.attributes"));
185 public void testMethodSelfUsedAsOperand() {
186 doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.special.usage.of.self"));
189 public void testMethodOverriddenSelf() {
190 doTestFailure(PyBundle.message("refactoring.make.function.top.level.error.special.usage.of.self"));
193 public void testMethodSingleAttributeRead() {
197 public void testMethodMultipleAttributesReadReferenceQualifier() {
201 public void testMethodMultipleAttributesConstructorQualifier() {
205 public void testMethodImportUpdates() throws IOException {
206 doMultiFileTest(null, null);
209 public void testMethodMoveToOtherFile() throws IOException {
210 doMultiFileTest("util.py", null);
213 public void testMethodCalledViaClass() {
217 public void testMethodUniqueNameOfExtractedQualifier() {
221 public void testMethodUniqueParamNames() {
225 public void testRecursiveMethod() {
229 public void testRecursiveLocalFunction() {
233 public void testMethodNoNewParams() {
237 public void testMethodNotImportableDestinationFile() throws IOException {
238 doMultiFileTest("not-importable.py", PyBundle.message("refactoring.move.error.cannot.use.module.name.$0", "not-importable.py"));
241 public void testLocalFunctionNameCollision() {
242 doTestFailure(PyBundle.message("refactoring.move.error.destination.file.contains.function.$0", "nested"));
246 protected String getTestDataPath() {
247 return super.getTestDataPath() + "/refactoring/makeFunctionTopLevel/";