0a66b1e6bf71fc39c9b6c9d51d01ae50e68027dd
[idea/community.git] / python / testSrc / com / jetbrains / python / refactoring / changeSignature / PyChangeSignatureTest.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.changeSignature;
17
18 import com.intellij.openapi.project.Project;
19 import com.intellij.openapi.util.Disposer;
20 import com.intellij.refactoring.BaseRefactoringProcessor;
21 import com.intellij.testFramework.TestDataPath;
22 import com.intellij.util.Function;
23 import com.intellij.util.containers.ContainerUtil;
24 import com.jetbrains.python.PyBundle;
25 import com.jetbrains.python.PythonFileType;
26 import com.jetbrains.python.fixtures.PyTestCase;
27 import com.jetbrains.python.psi.LanguageLevel;
28 import com.jetbrains.python.psi.PyFunction;
29 import org.jetbrains.annotations.Nullable;
30
31 import java.util.Arrays;
32 import java.util.List;
33
34 /**
35  * User : ktisha
36  */
37 @TestDataPath("$CONTENT_ROOT/../testData/")
38 public class PyChangeSignatureTest extends PyTestCase {
39
40   public void testChooseSuperMethod() {
41     doChangeSignatureTest("baz", null);
42   }
43
44   public void testChangeFunctionName() {
45     doChangeSignatureTest("bar", null);
46   }
47
48   public void testRemovePositionalParam() {
49     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "a", null, false)));
50   }
51
52   public void testRemoveFirstPositionalParam() {
53     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(1, "b", null, false)));
54   }
55
56   public void testRemoveKeyedParam() { //PY-9753
57     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "tries", null, false),
58                                               new PyParameterInfo(2, "delay", "3", true),
59                                               new PyParameterInfo(3, "backoff", "1", true),
60                                               new PyParameterInfo(4, "exceptions_to_check", "Exception", true),
61                                               new PyParameterInfo(5, "retry_for_lambda", "None", true)));
62   }
63
64   public void testSwitchPositionalParam() {
65     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(1, "b", null, false), new PyParameterInfo(0, "a", null, false)));
66   }
67
68   public void testAddPositionalParam() {
69     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "a", null, false), new PyParameterInfo(1, "b", null, false),
70                                               new PyParameterInfo(-1, "c", "3", false)));
71   }
72
73   public void testAddDefaultParam() {
74     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "a", null, false), new PyParameterInfo(1, "b", null, false),
75                                               new PyParameterInfo(-1, "c", "3", true)));
76   }
77
78   public void testRemoveDefaultFromParam() {
79     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "a", null, false), new PyParameterInfo(1, "b", "2", false)));
80   }
81
82   public void testAddDefaultParam1() {
83     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "a", null, false), new PyParameterInfo(-1, "d", "1", true),
84                                               new PyParameterInfo(1, "b", "None", true)));
85   }
86
87   public void testUpdateDocstring() {
88     final PyParameterInfo a = new PyParameterInfo(0, "a", null, false);
89     final PyParameterInfo d1 = new PyParameterInfo(1, "d", "1", true);
90     d1.setName("d1");
91     doChangeSignatureTest(null, Arrays.asList(a, d1));
92   }
93
94   public void testFixDocstringRemove() {
95     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "a", null, false)));
96   }
97
98   public void testClassMethod() {
99     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "self", null, false), new PyParameterInfo(1, "a", null, true),
100                                               new PyParameterInfo(-1, "b", "2", false)));
101   }
102
103   public void testKeywordParam() {
104     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "a", null, false),
105                                               new PyParameterInfo(-1, "b", "2", false)));
106   }
107
108   public void testParamAnnotation() {
109     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "b", null, false)), LanguageLevel.PYTHON32);
110   }
111
112   public void testKwArgs() {
113     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "param", null, false),
114                                               new PyParameterInfo(1, "**kwargs", null, false)), LanguageLevel.PYTHON32);
115   }
116
117   public void testKeywordOnlyParams() {
118     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "param", null, false),
119                                               new PyParameterInfo(-1, "*", null, false),
120                                               new PyParameterInfo(-1, "a", "2", false)), LanguageLevel.PYTHON32);
121   }
122
123   public void testKeywordOnlyParamRemoveDefaultValue() {
124     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "my", "None", false),
125                                               new PyParameterInfo(1, "*", null, false),
126                                               new PyParameterInfo(2, "param", null, false)), LanguageLevel.PYTHON32);
127   }
128
129   public void testKeywordOnlyParamRemoveDefaultValue1() {
130     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "my", "None", false),
131                                               new PyParameterInfo(1, "*", null, false),
132                                               new PyParameterInfo(2, "param", "1", true)), LanguageLevel.PYTHON32);
133   }
134
135   public void testKeywordOnlyParamRemoveDefaultValue2() {
136     doChangeSignatureTest(null, Arrays.asList(new PyParameterInfo(0, "my", "None", true),
137                                               new PyParameterInfo(1, "*", null, false),
138                                               new PyParameterInfo(2, "param", "1", false)), LanguageLevel.PYTHON32);
139   }
140
141   public void testRenameOverriding() {
142     doChangeSignatureTest("m1", Arrays.asList(new PyParameterInfo(0, "self", null, false)));
143   }
144
145   public void testDuplicateParam() {
146     doChangeSignatureTest("some_function_name", Arrays.asList(new PyParameterInfo(0, "argument_1", null, false),
147                                                               new PyParameterInfo(2, "opt2", "None", true),
148                                                               new PyParameterInfo(3, "**extra_info", null, false)));
149   }
150   public void testMoveParam() {
151     doChangeSignatureTest("f1", Arrays.asList(new PyParameterInfo(1, "b", "2", true),
152                                                 new PyParameterInfo(0, "a", "1", true)));
153   }
154
155   public void testMoveRenameParam() {
156     final PyParameterInfo b = new PyParameterInfo(-1, "b", "1", false);
157     final PyParameterInfo a = new PyParameterInfo(0, "a", "2", true);
158     a.setName("a2");
159     doChangeSignatureTest("foo", Arrays.asList(b,
160                                                a));
161   }
162
163   public void testKeywordOnlyMove() {
164     doChangeSignatureTest("f", Arrays.asList(new PyParameterInfo(2, "param2", null, false),
165                                              new PyParameterInfo(0, "*", null, false),
166                                              new PyParameterInfo(1, "param1", null, false)), LanguageLevel.PYTHON32);
167   }
168
169   // PY-8599
170   public void testRenameStarredParameters() {
171     final PyParameterInfo argsParam = new PyParameterInfo(1, "*args", null, false);
172     argsParam.setName("*foo");
173     final PyParameterInfo kwargsParam = new PyParameterInfo(2, "**kwargs", null, false);
174     kwargsParam.setName("**bar");
175     doChangeSignatureTest("func", Arrays.asList(new PyParameterInfo(0, "arg1", null, false), argsParam, kwargsParam), LanguageLevel.PYTHON32);
176   }
177
178   public void testRenameAndMoveParam() {
179     final PyParameterInfo p2 = new PyParameterInfo(1, "p2", null, false);
180     final PyParameterInfo p1 = new PyParameterInfo(0, "p1", null, false);
181     p1.setName("p");
182     doChangeSignatureTest("f", Arrays.asList(p2,
183                                              p1));
184   }
185
186   public void testEmptyParameterName() {
187     doValidationTest(null, Arrays.asList(new PyParameterInfo(-1, "", "2", true)),
188                      PyBundle.message("refactoring.change.signature.dialog.validation.parameter.name"));
189   }
190
191   public void testDecorator() {
192     doChangeSignatureTest("decorator", Arrays.asList(new PyParameterInfo(0, "arg1", null, false)));
193   }
194
195   public void testNonDefaultAfterDefault() {
196     doValidationTest(null, Arrays.asList(new PyParameterInfo(-1, "a", "2", false), new PyParameterInfo(1, "b", "2", false)), null);
197   }
198
199   public void testNonDefaultAfterDefault1() {
200     doValidationTest(null, Arrays.asList(new PyParameterInfo(1, "b", "1", true), new PyParameterInfo(-1, "a", "2", false)),
201                      PyBundle.message("ANN.non.default.param.after.default"));
202   }
203
204   // PY-14774
205   public void testAnnotationsForStarredParametersAreNotShownInDialog() throws Exception {
206     runWithLanguageLevel(LanguageLevel.PYTHON30, new Runnable() {
207       public void run() {
208         myFixture.configureByText(PythonFileType.INSTANCE, "def func(a, b:int, *args: tuple, c:list, d:str='foo', ** kwargs:dict):\n" +
209                                                            "    pass");
210         final PyFunction function = (PyFunction)new PyChangeSignatureHandler().findTargetMember(myFixture.getFile(), myFixture.getEditor());
211         assertNotNull(function);
212         final List<String> expected = Arrays.asList("a", "b", "*args", "c", "d", "**kwargs");
213         final List<PyParameterInfo> parameters = new PyMethodDescriptor(function).getParameters();
214         assertEquals(expected, ContainerUtil.map(parameters, new Function<PyParameterInfo, String>() {
215           @Override
216           public String fun(PyParameterInfo info) {
217             return info.getOldName();
218           }
219         }));
220       }
221     });
222   }
223
224   public void testDuplicateNamesOfStarredParameters() throws Exception {
225     final PyParameterInfo firstParam = new PyParameterInfo(0, "*foo", null, false);
226     firstParam.setName("*bar");
227     doValidationTest(null, Arrays.asList(firstParam, new PyParameterInfo(1, "**bar", null, false)),
228                      PyBundle.message("ANN.duplicate.param.name"));
229   }
230
231   public void testMultipleSingleStarredParameters() throws Exception {
232     final PyParameterInfo firstParam = new PyParameterInfo(0, "foo", null, false);
233     firstParam.setName("*foo");
234     doValidationTest(null, Arrays.asList(firstParam, new PyParameterInfo(1, "*bar", null, false)),
235                      PyBundle.message("refactoring.change.signature.dialog.validation.multiple.star"));
236   }
237
238   public void testMultipleDoubleStarredParameters() throws Exception {
239     final PyParameterInfo firstParam = new PyParameterInfo(0, "foo", null, false);
240     firstParam.setName("**foo");
241     doValidationTest(null, Arrays.asList(firstParam, new PyParameterInfo(1, "**bar", null, false)),
242                      PyBundle.message("refactoring.change.signature.dialog.validation.multiple.double.star"));
243   }
244
245   public void testParameterNameWithMoreThanTwoStars() throws Exception {
246     final PyParameterInfo firstParam = new PyParameterInfo(0, "**kwargs", null, false);
247     firstParam.setName("***kwargs");
248     doValidationTest(null, Arrays.asList(firstParam), PyBundle.message("refactoring.change.signature.dialog.validation.parameter.name"));
249   }
250
251   public void doChangeSignatureTest(@Nullable String newName, @Nullable List<PyParameterInfo> parameters) {
252     myFixture.configureByFile("refactoring/changeSignature/" + getTestName(true) + ".before.py");
253     changeSignature(newName, parameters);
254     myFixture.checkResultByFile("refactoring/changeSignature/" + getTestName(true) + ".after.py");
255   }
256
257   private void doChangeSignatureTest(@Nullable String newName, @Nullable List<PyParameterInfo> parameters, LanguageLevel level) {
258     setLanguageLevel(level);
259     try {
260       doChangeSignatureTest(newName, parameters);
261     }
262     finally {
263       setLanguageLevel(null);
264     }
265   }
266
267   public void doValidationTest(@Nullable String newName, @Nullable List<PyParameterInfo> parameters, @Nullable String expected) {
268     myFixture.configureByFile("refactoring/changeSignature/" + getTestName(true) + ".py");
269     final PyChangeSignatureHandler changeSignatureHandler = new PyChangeSignatureHandler();
270     final PyFunction function = (PyFunction)changeSignatureHandler.findTargetMember(myFixture.getFile(), myFixture.getEditor());
271     assertNotNull(function);
272
273     final PyMethodDescriptor method = new PyMethodDescriptor(function);
274     final TestPyChangeSignatureDialog dialog = new TestPyChangeSignatureDialog(function.getProject(), method);
275     try {
276       if (newName != null) {
277         dialog.setNewName(newName);
278       }
279       if (parameters != null) {
280         dialog.setParameterInfos(parameters);
281       }
282
283       final String validationError = dialog.validateAndCommitData();
284       assertEquals(expected, validationError);
285     }
286     finally {
287       Disposer.dispose(dialog.getDisposable());
288     }
289   }
290
291   private void changeSignature(@Nullable String newName, @Nullable List<PyParameterInfo> parameters) {
292     final PyChangeSignatureHandler changeSignatureHandler = new PyChangeSignatureHandler();
293     final PyFunction function = (PyFunction)changeSignatureHandler.findTargetMember(myFixture.getFile(), myFixture.getEditor());
294     assertNotNull(function);
295     final PyFunction newFunction = PyChangeSignatureHandler.getSuperMethod(function);
296     assertNotNull(newFunction);
297     final PyMethodDescriptor method = new PyMethodDescriptor(newFunction);
298     final TestPyChangeSignatureDialog dialog = new TestPyChangeSignatureDialog(newFunction.getProject(), method);
299     try {
300       if (newName != null) {
301         dialog.setNewName(newName);
302       }
303       if (parameters != null) {
304         dialog.setParameterInfos(parameters);
305       }
306
307       final String validationError = dialog.validateAndCommitData();
308       assertTrue(validationError, validationError == null);
309
310       final BaseRefactoringProcessor baseRefactoringProcessor = dialog.createRefactoringProcessor();
311       assert baseRefactoringProcessor instanceof PyChangeSignatureProcessor;
312
313       final PyChangeSignatureProcessor processor = (PyChangeSignatureProcessor)baseRefactoringProcessor;
314       processor.run();
315     }
316     finally {
317       Disposer.dispose(dialog.getDisposable());
318     }
319   }
320
321
322   public static class TestPyChangeSignatureDialog extends PyChangeSignatureDialog {
323
324     public TestPyChangeSignatureDialog(Project project, PyMethodDescriptor method) {
325       super(project, method);
326     }
327
328     public void setNewName(String newName) {
329       myNameField.setText(newName);
330     }
331   }
332 }