LightQuickFixTestCase: run intentions in a transaction
[idea/community.git] / java / testFramework / src / com / intellij / codeInsight / daemon / quickFix / LightQuickFixTestCase.java
1 /*
2  * Copyright 2000-2016 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.intellij.codeInsight.daemon.quickFix;
17
18 import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
19 import com.intellij.codeInsight.daemon.impl.HighlightInfo;
20 import com.intellij.codeInsight.intention.IntentionAction;
21 import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler;
22 import com.intellij.openapi.application.ApplicationManager;
23 import com.intellij.openapi.application.WriteAction;
24 import com.intellij.openapi.command.CommandProcessor;
25 import com.intellij.openapi.editor.Editor;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.util.io.FileUtil;
28 import com.intellij.openapi.util.text.StringUtil;
29 import com.intellij.openapi.vcs.readOnlyHandler.ReadonlyStatusHandlerImpl;
30 import com.intellij.openapi.vfs.CharsetToolkit;
31 import com.intellij.openapi.vfs.ReadonlyStatusHandler;
32 import com.intellij.psi.PsiFile;
33 import com.intellij.rt.execution.junit.FileComparisonFailure;
34 import com.intellij.testFramework.LightPlatformCodeInsightTestCase;
35 import com.intellij.testFramework.LightPlatformTestCase;
36 import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl;
37 import com.intellij.util.IncorrectOperationException;
38 import com.intellij.util.ObjectUtils;
39 import com.intellij.util.io.ReadOnlyAttributeUtil;
40 import com.intellij.util.ui.UIUtil;
41 import org.jetbrains.annotations.NonNls;
42 import org.jetbrains.annotations.NotNull;
43
44 import java.io.File;
45 import java.io.IOException;
46 import java.io.UncheckedIOException;
47 import java.util.List;
48
49 public abstract class LightQuickFixTestCase extends LightDaemonAnalyzerTestCase {
50   @NonNls protected static final String BEFORE_PREFIX = "before";
51   @NonNls protected static final String AFTER_PREFIX = "after";
52
53   private static QuickFixTestCase myWrapper;
54
55   protected boolean shouldBeAvailableAfterExecution() {
56     return false;
57   }
58
59   private static void doTestFor(@NotNull String testName, @NotNull QuickFixTestCase quickFixTestCase) {
60     final String relativePath = ObjectUtils.notNull(quickFixTestCase.getBasePath(), "") + "/" + BEFORE_PREFIX + testName;
61     final String testFullPath = quickFixTestCase.getTestDataPath().replace(File.separatorChar, '/') + relativePath;
62     final File testFile = new File(testFullPath);
63     CommandProcessor.getInstance().executeCommand(quickFixTestCase.getProject(), () -> {
64       try {
65         String contents = StringUtil.convertLineSeparators(FileUtil.loadFile(testFile, CharsetToolkit.UTF8_CHARSET));
66         quickFixTestCase.configureFromFileText(testFile.getName(), contents);
67         quickFixTestCase.bringRealEditorBack();
68         final ActionHint actionHint = quickFixTestCase.parseActionHintImpl(quickFixTestCase.getFile(), contents);
69
70         quickFixTestCase.beforeActionStarted(testName, contents);
71
72         try {
73           myWrapper = quickFixTestCase;
74           quickFixTestCase.doAction(actionHint, testFullPath, testName);
75         }
76         finally {
77           myWrapper = null;
78           quickFixTestCase.afterActionCompleted(testName, contents);
79         }
80       }
81       catch (FileComparisonFailure e){
82         throw e;
83       }
84       catch (Throwable e) {
85         e.printStackTrace();
86         fail(testName);
87       }
88     }, "", "");
89   }
90
91   protected void afterActionCompleted(final String testName, final String contents) {
92   }
93
94   protected void beforeActionStarted(final String testName, final String contents) {
95   }
96
97   public static void doAction(@NotNull ActionHint actionHint,
98                               String testFullPath,
99                               String testName,
100                               QuickFixTestCase quickFix) throws Exception {
101     IntentionAction action = actionHint.findAndCheck(quickFix.getAvailableActions(),
102                                                      () -> "Test: "+testFullPath+"\nInfos: "+quickFix.doHighlighting());
103     if (action != null) {
104       String text = action.getText();
105       quickFix.invoke(action);
106       UIUtil.dispatchAllInvocationEvents();
107       UIUtil.dispatchAllInvocationEvents();
108       if (!quickFix.shouldBeAvailableAfterExecution()) {
109         final IntentionAction afterAction = quickFix.findActionWithText(text);
110         if (afterAction != null) {
111           fail("Action '" + text + "' is still available after its invocation in test " + testFullPath);
112         }
113       }
114       String expectedFilePath = ObjectUtils.notNull(quickFix.getBasePath(), "") + "/" + AFTER_PREFIX + testName;
115       quickFix.checkResultByFile("In file :" + expectedFilePath, expectedFilePath, false);
116     }
117   }
118
119   protected void doAction(@NotNull ActionHint actionHint, final String testFullPath, final String testName)
120     throws Exception {
121     doAction(actionHint, testFullPath, testName, myWrapper);
122   }
123
124   protected void doAction(@NotNull String actionName) {
125     final List<IntentionAction> available = getAvailableActions();
126     final IntentionAction action = findActionWithText(available, actionName);
127     assertNotNull("Action '" + actionName + "' not found among " + available, action);
128     invoke(action);
129   }
130
131   protected static void invoke(@NotNull IntentionAction action) throws IncorrectOperationException {
132     PsiFile file = getFile();
133     WriteAction.run(() -> {
134       try {
135         // Test that action will automatically clear the read-only attribute if modification is necessary.
136         // If your test fails due to this, make sure that your quick-fix/intention has the following line:
137         // if (!FileModificationService.getInstance().prepareFileForWrite(file)) return;
138         ReadOnlyAttributeUtil.setReadOnlyAttribute(file.getVirtualFile(), true);
139       }
140       catch (IOException e) {
141         throw new UncheckedIOException(e);
142       }
143     });
144     ReadonlyStatusHandlerImpl handler = (ReadonlyStatusHandlerImpl)ReadonlyStatusHandler.getInstance(file.getProject());
145     handler.setClearReadOnlyInTests(true);
146     try {
147       ApplicationManager.getApplication().invokeLater(() ->
148         ShowIntentionActionsHandler.chooseActionAndInvoke(file, getEditor(), action, action.getText()));
149       UIUtil.dispatchAllInvocationEvents();
150     }
151     finally {
152       handler.setClearReadOnlyInTests(false);
153     }
154   }
155
156   protected IntentionAction findActionAndCheck(@NotNull ActionHint hint, String testFullPath) {
157     return hint.findAndCheck(getAvailableActions(), () -> "Test: "+testFullPath);
158   }
159
160   protected IntentionAction findActionWithText(@NotNull String text) {
161     return findActionWithText(getAvailableActions(), text);
162   }
163
164   public static IntentionAction findActionWithText(@NotNull List<IntentionAction> actions, @NotNull String text) {
165     for (IntentionAction action : actions) {
166       if (text.equals(action.getText())) {
167         return action;
168       }
169     }
170     return null;
171   }
172
173   /**
174    * @deprecated use {@link LightQuickFixParameterizedTestCase}
175    * to get separate tests for all data files in testData directory.
176    */
177   protected void doAllTests() {
178     doAllTests(createWrapper());
179   }
180
181   public static void doAllTests(QuickFixTestCase testCase) {
182     final File[] files = getBeforeTestFiles(testCase);
183
184     for (File file : files) {
185       final String testName = file.getName().substring(BEFORE_PREFIX.length());
186       doTestFor(testName, testCase);
187     }
188   }
189
190   @NotNull
191   public static File[] getBeforeTestFiles(@NotNull QuickFixTestCase testCase) {
192     assertNotNull("getBasePath() should not return null!", testCase.getBasePath());
193
194     final String testDirPath = testCase.getTestDataPath().replace(File.separatorChar, '/') + testCase.getBasePath();
195     File testDir = new File(testDirPath);
196     final File[] files = testDir.listFiles((dir, name) -> name.startsWith(BEFORE_PREFIX));
197
198     if (files == null || files.length == 0) {
199       fail("Test files not found in " + testDirPath);
200     }
201     return files;
202   }
203
204   protected void doSingleTest(@NotNull String fileSuffix) {
205     doTestFor(fileSuffix, createWrapper());
206   }
207
208   protected void doSingleTest(String fileSuffix, String testDataPath) {
209     doTestFor(fileSuffix, createWrapper(testDataPath));
210   }
211
212   @NotNull
213   protected QuickFixTestCase createWrapper() {
214     return createWrapper(null);
215   }
216
217   @NotNull
218   protected QuickFixTestCase createWrapper(final String testDataPath) {
219     return new QuickFixTestCase() {
220       public String myTestDataPath = testDataPath;
221
222       @Override
223       public String getBasePath() {
224         return LightQuickFixTestCase.this.getBasePath();
225       }
226
227       @NotNull
228       @Override
229       public String getTestDataPath() {
230         if (myTestDataPath == null) {
231           myTestDataPath = LightQuickFixTestCase.this.getTestDataPath();
232         }
233         return myTestDataPath;
234       }
235
236       @NotNull
237       @Override
238       public ActionHint parseActionHintImpl(@NotNull PsiFile file, @NotNull String contents) {
239         return ActionHint.parse(file, contents);
240       }
241
242       @Override
243       public void beforeActionStarted(@NotNull String testName, @NotNull String contents) {
244         LightQuickFixTestCase.this.beforeActionStarted(testName, contents);
245       }
246
247       @Override
248       public void afterActionCompleted(@NotNull String testName, @NotNull String contents) {
249         LightQuickFixTestCase.this.afterActionCompleted(testName, contents);
250       }
251
252       @Override
253       public void doAction(@NotNull ActionHint actionHint, @NotNull String testFullPath, @NotNull String testName) throws Exception {
254         LightQuickFixTestCase.this.doAction(actionHint, testFullPath, testName);
255       }
256
257       @Override
258       public void checkResultByFile(@NotNull String message, @NotNull String expectedFilePath, boolean ignoreTrailingSpaces) throws Exception {
259         LightQuickFixTestCase.this.checkResultByFile(message, expectedFilePath, ignoreTrailingSpaces);
260       }
261
262       @Override
263       public IntentionAction findActionWithText(@NotNull String text) {
264         return LightQuickFixTestCase.this.findActionWithText(text);
265       }
266
267       @Override
268       public boolean shouldBeAvailableAfterExecution() {
269         return LightQuickFixTestCase.this.shouldBeAvailableAfterExecution();
270       }
271
272       @Override
273       public void invoke(@NotNull IntentionAction action) {
274         LightQuickFixTestCase.invoke(action);
275       }
276
277       @NotNull
278       @Override
279       public List<HighlightInfo> doHighlighting() {
280         return LightQuickFixTestCase.this.doHighlighting();
281       }
282
283       @NotNull
284       @Override
285       public List<IntentionAction> getAvailableActions() {
286         return LightQuickFixTestCase.this.getAvailableActions();
287       }
288
289       @Override
290       public void configureFromFileText(@NotNull String name, @NotNull String contents) throws IOException {
291         LightPlatformCodeInsightTestCase.configureFromFileText(name, contents, true);
292       }
293
294       @Override
295       public PsiFile getFile() {
296         return LightPlatformCodeInsightTestCase.getFile();
297       }
298
299       @Override
300       public Project getProject() {
301         return LightPlatformTestCase.getProject();
302       }
303
304       @Override
305       public void bringRealEditorBack() {
306         LightPlatformCodeInsightTestCase.bringRealEditorBack();
307       }
308     };
309   }
310
311   protected List<IntentionAction> getAvailableActions() {
312     doHighlighting();
313     return getAvailableActions(getEditor(), getFile());
314   }
315
316   @NotNull
317   public static List<IntentionAction> getAvailableActions(@NotNull Editor editor, @NotNull PsiFile file) {
318     return CodeInsightTestFixtureImpl.getAvailableIntentions(editor, file);
319   }
320
321   @NonNls protected String getBasePath() {return null;}
322 }