do not highlight method unused if referenced via overridden
[idea/community.git] / java / testFramework / src / com / intellij / codeInsight / daemon / DaemonAnalyzerTestCase.java
1 /*
2  * Copyright 2000-2014 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;
17
18 import com.intellij.codeHighlighting.HighlightDisplayLevel;
19 import com.intellij.codeHighlighting.Pass;
20 import com.intellij.codeInsight.CodeInsightTestCase;
21 import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx;
22 import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
23 import com.intellij.codeInsight.daemon.impl.HighlightInfo;
24 import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase;
25 import com.intellij.codeInsight.intention.IntentionAction;
26 import com.intellij.codeInsight.intention.IntentionManager;
27 import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler;
28 import com.intellij.codeInspection.InspectionProfileEntry;
29 import com.intellij.codeInspection.InspectionToolProvider;
30 import com.intellij.codeInspection.LocalInspectionTool;
31 import com.intellij.codeInspection.ModifiableModel;
32 import com.intellij.codeInspection.ex.*;
33 import com.intellij.ide.highlighter.JavaFileType;
34 import com.intellij.ide.startup.StartupManagerEx;
35 import com.intellij.ide.startup.impl.StartupManagerImpl;
36 import com.intellij.lang.ExternalAnnotatorsFilter;
37 import com.intellij.lang.LanguageAnnotators;
38 import com.intellij.lang.StdLanguages;
39 import com.intellij.lang.annotation.HighlightSeverity;
40 import com.intellij.lang.injection.InjectedLanguageManager;
41 import com.intellij.lang.java.JavaLanguage;
42 import com.intellij.openapi.Disposable;
43 import com.intellij.openapi.application.ApplicationManager;
44 import com.intellij.openapi.application.Result;
45 import com.intellij.openapi.application.ex.PathManagerEx;
46 import com.intellij.openapi.command.WriteCommandAction;
47 import com.intellij.openapi.editor.Document;
48 import com.intellij.openapi.editor.Editor;
49 import com.intellij.openapi.extensions.Extensions;
50 import com.intellij.openapi.module.Module;
51 import com.intellij.openapi.project.DumbService;
52 import com.intellij.openapi.project.Project;
53 import com.intellij.openapi.roots.ModuleRootManager;
54 import com.intellij.openapi.startup.StartupManager;
55 import com.intellij.openapi.util.Condition;
56 import com.intellij.openapi.util.Disposer;
57 import com.intellij.openapi.util.Pair;
58 import com.intellij.openapi.util.TextRange;
59 import com.intellij.openapi.util.io.FileUtil;
60 import com.intellij.openapi.vfs.*;
61 import com.intellij.profile.codeInspection.InspectionProfileManager;
62 import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
63 import com.intellij.psi.*;
64 import com.intellij.psi.impl.JavaPsiFacadeEx;
65 import com.intellij.psi.impl.cache.CacheManager;
66 import com.intellij.psi.impl.search.IndexPatternBuilder;
67 import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
68 import com.intellij.psi.impl.source.tree.TreeElement;
69 import com.intellij.psi.impl.source.tree.TreeUtil;
70 import com.intellij.psi.search.GlobalSearchScope;
71 import com.intellij.psi.search.UsageSearchContext;
72 import com.intellij.psi.xml.XmlFileNSInfoProvider;
73 import com.intellij.testFramework.ExpectedHighlightingData;
74 import com.intellij.testFramework.FileTreeAccessFilter;
75 import com.intellij.testFramework.HighlightTestInfo;
76 import com.intellij.testFramework.LightPlatformTestCase;
77 import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl;
78 import com.intellij.util.IncorrectOperationException;
79 import com.intellij.util.containers.ContainerUtil;
80 import com.intellij.util.ui.UIUtil;
81 import com.intellij.xml.XmlSchemaProvider;
82 import gnu.trove.THashMap;
83 import gnu.trove.TIntArrayList;
84 import org.jetbrains.annotations.NonNls;
85 import org.jetbrains.annotations.NotNull;
86
87 import java.io.File;
88 import java.io.IOException;
89 import java.lang.annotation.ElementType;
90 import java.lang.annotation.Retention;
91 import java.lang.annotation.RetentionPolicy;
92 import java.lang.annotation.Target;
93 import java.util.ArrayList;
94 import java.util.Collection;
95 import java.util.List;
96 import java.util.Map;
97
98 public abstract class DaemonAnalyzerTestCase extends CodeInsightTestCase {
99   private final Map<String, InspectionToolWrapper> myAvailableTools = new THashMap<String, InspectionToolWrapper>();
100   private final FileTreeAccessFilter myFileTreeAccessFilter = new FileTreeAccessFilter();
101
102   @Override
103   protected boolean isRunInWriteAction() {
104     return false;
105   }
106
107   @Override
108   protected void setUp() throws Exception {
109     super.setUp();
110
111     final LocalInspectionTool[] tools = configureLocalInspectionTools();
112     for (LocalInspectionTool tool : tools) {
113       enableInspectionTool(tool);
114     }
115
116     final InspectionProfileImpl profile = new InspectionProfileImpl(LightPlatformTestCase.PROFILE) {
117       @Override
118       @NotNull
119       public ModifiableModel getModifiableModel() {
120         mySource = this;
121         return this;
122       }
123
124       @Override
125       @NotNull
126       public InspectionToolWrapper[] getInspectionTools(PsiElement element) {
127         Collection<InspectionToolWrapper> values = myAvailableTools.values();
128         return values.toArray(new InspectionToolWrapper[values.size()]);
129       }
130
131       @NotNull
132       @Override
133       public List<Tools> getAllEnabledInspectionTools(Project project) {
134         List<Tools> result = new ArrayList<Tools>();
135         for (InspectionToolWrapper toolWrapper : getInspectionTools(null)) {
136           result.add(new ToolsImpl(toolWrapper, toolWrapper.getDefaultLevel(), true));
137         }
138         return result;
139       }
140
141       @Override
142       public boolean isToolEnabled(HighlightDisplayKey key, PsiElement element) {
143         return key != null && myAvailableTools.containsKey(key.toString());
144       }
145
146       @Override
147       public HighlightDisplayLevel getErrorLevel(@NotNull HighlightDisplayKey key, PsiElement element) {
148         final InspectionToolWrapper localInspectionTool = myAvailableTools.get(key.toString());
149         return localInspectionTool != null ? localInspectionTool.getDefaultLevel() : HighlightDisplayLevel.WARNING;
150       }
151
152       @Override
153       public InspectionToolWrapper getInspectionTool(@NotNull String shortName, @NotNull PsiElement element) {
154         return myAvailableTools.get(shortName);
155       }
156     };
157     final InspectionProfileManager inspectionProfileManager = InspectionProfileManager.getInstance();
158     inspectionProfileManager.addProfile(profile);
159     inspectionProfileManager.setRootProfile(LightPlatformTestCase.PROFILE);
160     Disposer.register(getProject(), new Disposable() {
161       @Override
162       public void dispose() {
163         inspectionProfileManager.deleteProfile(LightPlatformTestCase.PROFILE);
164       }
165     });
166     InspectionProjectProfileManager.getInstance(getProject()).updateProfile(profile);
167     InspectionProjectProfileManager.getInstance(getProject()).setProjectProfile(profile.getName());
168     DaemonCodeAnalyzerImpl daemonCodeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject());
169     daemonCodeAnalyzer.prepareForTest();
170     final StartupManagerImpl startupManager = (StartupManagerImpl)StartupManagerEx.getInstanceEx(getProject());
171     startupManager.runStartupActivities();
172     startupManager.startCacheUpdate();
173     startupManager.runPostStartupActivities();
174     DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(false);
175
176     if (isPerformanceTest()) {
177       IntentionManager.getInstance().getAvailableIntentionActions();  // hack to avoid slowdowns in PyExtensionFactory
178       PathManagerEx.getTestDataPath(); // to cache stuff
179       ReferenceProvidersRegistry.getInstance(); // pre-load tons of classes
180       InjectedLanguageManager.getInstance(getProject()); // zillion of Dom Sem classes
181       LanguageAnnotators.INSTANCE.allForLanguage(JavaLanguage.INSTANCE); // pile of annotator classes loads
182       LanguageAnnotators.INSTANCE.allForLanguage(StdLanguages.XML);
183       ProblemHighlightFilter.EP_NAME.getExtensions();
184       Extensions.getExtensions(ImplicitUsageProvider.EP_NAME);
185       Extensions.getExtensions(XmlSchemaProvider.EP_NAME);
186       Extensions.getExtensions(XmlFileNSInfoProvider.EP_NAME);
187       Extensions.getExtensions(ExternalAnnotatorsFilter.EXTENSION_POINT_NAME);
188       Extensions.getExtensions(IndexPatternBuilder.EP_NAME);
189     }
190   }
191
192   @Override
193   protected void tearDown() throws Exception {
194     ((StartupManagerImpl)StartupManager.getInstance(getProject())).checkCleared();
195     ((DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject())).cleanupAfterTest();
196     super.tearDown();
197     //((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).assertPointersDisposed();
198   }
199
200   protected void enableInspectionTool(@NotNull InspectionProfileEntry tool) {
201     InspectionToolWrapper toolWrapper = InspectionToolRegistrar.wrapTool(tool);
202     LightPlatformTestCase.enableInspectionTool(myAvailableTools, toolWrapper);
203   }
204
205   protected void enableInspectionToolsFromProvider(InspectionToolProvider toolProvider){
206     try {
207       for(Class c:toolProvider.getInspectionClasses()) {
208         enableInspectionTool((LocalInspectionTool)c.newInstance());
209       }
210     }
211     catch (Exception e) {
212       throw new RuntimeException(e);
213     }
214   }
215
216   protected void disableInspectionTool(String shortName){
217     myAvailableTools.remove(shortName);
218   }
219
220   protected LocalInspectionTool[] configureLocalInspectionTools() {
221     return LocalInspectionTool.EMPTY_ARRAY;
222   }
223
224   protected static LocalInspectionTool[] createLocalInspectionTools(final InspectionToolProvider... provider) {
225     final ArrayList<LocalInspectionTool> result = new ArrayList<LocalInspectionTool>();
226     for (InspectionToolProvider toolProvider : provider) {
227       for (Class aClass : toolProvider.getInspectionClasses()) {
228         try {
229           final Object tool = aClass.newInstance();
230           assertTrue(tool instanceof LocalInspectionTool);
231           result.add((LocalInspectionTool)tool);
232         }
233         catch (Exception e) {
234           LOG.error(e);
235         }
236       }
237     }
238     return result.toArray(new LocalInspectionTool[result.size()]);
239   }
240
241   protected void doTest(@NonNls @NotNull String filePath, boolean checkWarnings, boolean checkInfos, boolean checkWeakWarnings) throws Exception {
242     configureByFile(filePath);
243     doDoTest(checkWarnings, checkInfos, checkWeakWarnings);
244   }
245
246   protected void doTest(@NonNls @NotNull String filePath, boolean checkWarnings, boolean checkInfos) throws Exception {
247     doTest(filePath, checkWarnings, checkInfos, false);
248   }
249
250   protected void doTest(@NonNls @NotNull String filePath, @NonNls String projectRoot, boolean checkWarnings, boolean checkInfos) throws Exception {
251     configureByFile(filePath, projectRoot);
252     doDoTest(checkWarnings, checkInfos);
253   }
254
255   @NotNull
256   @SuppressWarnings("TestMethodWithIncorrectSignature")
257   protected HighlightTestInfo testFile(@NonNls @NotNull String... filePath) {
258     return new HighlightTestInfo(getTestRootDisposable(), filePath) {
259       @Override
260       public HighlightTestInfo doTest() {
261         try { configureByFiles(projectRoot, filePaths); }
262         catch (Exception e) { throw new RuntimeException(e); }
263         ExpectedHighlightingData data = new ExpectedHighlightingData(myEditor.getDocument(), checkWarnings, checkWeakWarnings, checkInfos, myFile);
264         if (checkSymbolNames) data.checkSymbolNames();
265         checkHighlighting(data);
266         return this;
267       }
268     };
269   }
270
271   protected void doTest(@NotNull VirtualFile vFile, boolean checkWarnings, boolean checkInfos) throws Exception {
272     doTest(new VirtualFile[] { vFile }, checkWarnings, checkInfos );
273   }
274
275   protected void doTest(@NotNull VirtualFile[] vFile, boolean checkWarnings, boolean checkInfos) throws Exception {
276     configureByFiles(null, vFile);
277     doDoTest(checkWarnings, checkInfos);
278   }
279
280   protected void doTest(boolean checkWarnings, boolean checkInfos, String ... files) throws Exception {
281     configureByFiles(null, files);
282     doDoTest(checkWarnings, checkInfos);
283   }
284
285   @NotNull
286   protected Collection<HighlightInfo> doDoTest(boolean checkWarnings, boolean checkInfos) {
287     return doDoTest(checkWarnings, checkInfos, false);
288   }
289
290   protected Collection<HighlightInfo> doDoTest(final boolean checkWarnings, final boolean checkInfos, final boolean checkWeakWarnings) {
291     return ContainerUtil.filter(
292       checkHighlighting(new ExpectedHighlightingData(myEditor.getDocument(), checkWarnings, checkWeakWarnings, checkInfos, myFile)),
293       new Condition<HighlightInfo>() {
294         @Override
295         public boolean value(HighlightInfo info) {
296           return (info.getSeverity() == HighlightSeverity.INFORMATION) && checkInfos ||
297                  (info.getSeverity() == HighlightSeverity.WARNING) && checkWarnings ||
298                  (info.getSeverity() == HighlightSeverity.WEAK_WARNING) && checkWeakWarnings ||
299                   info.getSeverity().compareTo(HighlightSeverity.WARNING) > 0;
300         }
301       });
302   }
303
304   @NotNull
305   protected Collection<HighlightInfo> checkHighlighting(@NotNull final ExpectedHighlightingData data) {
306     data.init();
307     PsiDocumentManager.getInstance(myProject).commitAllDocuments();
308
309     //to load text
310     ApplicationManager.getApplication().runWriteAction(new Runnable() {
311       @Override
312       public void run() {
313         TreeUtil.clearCaches((TreeElement)myFile.getNode());
314       }
315     });
316
317
318     //to initialize caches
319     if (!DumbService.isDumb(getProject())) {
320       CacheManager.SERVICE.getInstance(myProject).getFilesWithWord("XXX", UsageSearchContext.IN_COMMENTS, GlobalSearchScope.allScope(myProject), true);
321     }
322     final JavaPsiFacadeEx facade = getJavaFacade();
323     if (facade != null) {
324       facade.setAssertOnFileLoadingFilter(myFileTreeAccessFilter, myTestRootDisposable); // check repository work
325     }
326
327     try {
328       Collection<HighlightInfo> infos = doHighlighting();
329
330       String text = myEditor.getDocument().getText();
331       data.checkLineMarkers(DaemonCodeAnalyzerImpl.getLineMarkers(getDocument(getFile()), getProject()), text);
332       data.checkResult(infos, text);
333       return infos;
334     }
335     finally {
336       if (facade != null) {
337         facade.setAssertOnFileLoadingFilter(VirtualFileFilter.NONE, myTestRootDisposable);
338       }
339     }
340   }
341
342   public void allowTreeAccessForFile(@NotNull VirtualFile file) {
343     myFileTreeAccessFilter.allowTreeAccessForFile(file);
344   }
345   public void allowTreeAccessForAllFiles() {
346     myFileTreeAccessFilter.allowTreeAccessForAllFiles();
347   }
348
349   @NotNull
350   protected List<HighlightInfo> highlightErrors() {
351     return doHighlighting(HighlightSeverity.ERROR);
352   }
353
354   @NotNull
355   protected List<HighlightInfo> doHighlighting(@NotNull HighlightSeverity minSeverity) {
356     return filter(doHighlighting(), minSeverity);
357   }
358
359   @NotNull
360   protected List<HighlightInfo> doHighlighting() {
361     PsiDocumentManager.getInstance(myProject).commitAllDocuments();
362
363     TIntArrayList toIgnore = new TIntArrayList();
364     if (!doTestLineMarkers()) {
365       toIgnore.add(Pass.UPDATE_OVERRIDEN_MARKERS);
366       toIgnore.add(Pass.VISIBLE_LINE_MARKERS);
367       toIgnore.add(Pass.LINE_MARKERS);
368     }
369
370     if (!doExternalValidation()) {
371       toIgnore.add(Pass.EXTERNAL_TOOLS);
372     }
373     if (forceExternalValidation()) {
374       toIgnore.add(Pass.LINE_MARKERS);
375       toIgnore.add(Pass.LOCAL_INSPECTIONS);
376       toIgnore.add(Pass.WHOLE_FILE_LOCAL_INSPECTIONS);
377       toIgnore.add(Pass.POPUP_HINTS);
378       toIgnore.add(Pass.POST_UPDATE_ALL);
379       toIgnore.add(Pass.UPDATE_ALL);
380       toIgnore.add(Pass.UPDATE_OVERRIDEN_MARKERS);
381       toIgnore.add(Pass.VISIBLE_LINE_MARKERS);
382     }
383
384     boolean canChange = canChangeDocumentDuringHighlighting();
385     List<HighlightInfo> infos = CodeInsightTestFixtureImpl.instantiateAndRun(getFile(), getEditor(), toIgnore.toNativeArray(), canChange);
386
387     if (!canChange) {
388       Document document = getDocument(getFile());
389       DaemonCodeAnalyzerEx daemonCodeAnalyzer = DaemonCodeAnalyzerEx.getInstanceEx(myProject);
390       daemonCodeAnalyzer.getFileStatusMap().assertAllDirtyScopesAreNull(document);
391     }
392
393     return infos;
394   }
395
396   @Retention(RetentionPolicy.RUNTIME)
397   @Target({ElementType.METHOD, ElementType.TYPE})
398   public @interface CanChangeDocumentDuringHighlighting {}
399
400   private boolean canChangeDocumentDuringHighlighting() {
401     return annotatedWith(CanChangeDocumentDuringHighlighting.class);
402   }
403
404   @NotNull
405   public static List<HighlightInfo> filter(@NotNull List<HighlightInfo> infos, @NotNull HighlightSeverity minSeverity) {
406     ArrayList<HighlightInfo> result = new ArrayList<HighlightInfo>();
407     for (final HighlightInfo info : infos) {
408       if (info.getSeverity().compareTo(minSeverity) >= 0) result.add(info);
409     }
410     return result;
411   }
412
413   protected boolean doTestLineMarkers() {
414     return false;
415   }
416
417   protected boolean doExternalValidation() {
418     return true;
419   }
420
421   protected boolean forceExternalValidation() {
422     return false;
423   }
424
425   protected static void findAndInvokeIntentionAction(@NotNull Collection<HighlightInfo> infos, @NotNull String intentionActionName, @NotNull Editor editor,
426                                                      @NotNull PsiFile file) throws IncorrectOperationException {
427     IntentionAction intentionAction = findIntentionAction(infos, intentionActionName, editor, file);
428
429     assertNotNull(intentionActionName, intentionAction);
430     assertTrue(ShowIntentionActionsHandler.chooseActionAndInvoke(file, editor, intentionAction, intentionActionName));
431     UIUtil.dispatchAllInvocationEvents();
432   }
433
434   protected static IntentionAction findIntentionAction(@NotNull Collection<HighlightInfo> infos, @NotNull String intentionActionName, @NotNull Editor editor,
435                                                        @NotNull PsiFile file) {
436     List<IntentionAction> actions = LightQuickFixTestCase.getAvailableActions(editor, file);
437     IntentionAction intentionAction = LightQuickFixTestCase.findActionWithText(actions, intentionActionName);
438
439     if (intentionAction == null) {
440       final List<IntentionAction> availableActions = new ArrayList<IntentionAction>();
441
442       for (HighlightInfo info :infos) {
443         if (info.quickFixActionRanges != null) {
444           for (Pair<HighlightInfo.IntentionActionDescriptor, TextRange> pair : info.quickFixActionRanges) {
445             IntentionAction action = pair.first.getAction();
446             if (action.isAvailable(file.getProject(), editor, file)) availableActions.add(action);
447           }
448         }
449       }
450
451       intentionAction = LightQuickFixTestCase.findActionWithText(
452         availableActions,
453         intentionActionName
454       );
455     }
456     return intentionAction;
457   }
458
459   public void checkHighlighting(Editor editor, boolean checkWarnings, boolean checkInfos) {
460     setActiveEditor(editor);
461     doDoTest(checkWarnings, checkInfos);
462   }
463
464   public PsiClass createClass(String text) throws IOException {
465     return createClass(myModule, text);
466   }
467
468   protected PsiClass createClass(final Module module, final String text) throws IOException {
469     return new WriteCommandAction<PsiClass>(getProject()) {
470       @Override
471       protected void run(Result<PsiClass> result) throws Throwable {
472         final PsiFileFactory factory = PsiFileFactory.getInstance(getProject());
473         final PsiJavaFile javaFile = (PsiJavaFile)factory.createFileFromText("a.java", JavaFileType.INSTANCE, text);
474         final String qname = javaFile.getClasses()[0].getQualifiedName();
475         assertNotNull(qname);
476         final VirtualFile[] files = ModuleRootManager.getInstance(module).getSourceRoots();
477         File dir;
478         if (files.length > 0) {
479           dir = VfsUtilCore.virtualToIoFile(files[0]);
480         }
481         else {
482           dir = createTempDirectory();
483           VirtualFile vDir =
484             LocalFileSystem.getInstance().refreshAndFindFileByPath(dir.getCanonicalPath().replace(File.separatorChar, '/'));
485           addSourceContentToRoots(module, vDir);
486         }
487
488         File file = new File(dir, qname.replace('.', '/') + ".java");
489         FileUtil.createIfDoesntExist(file);
490         VirtualFile vFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(file.getCanonicalPath().replace(File.separatorChar, '/'));
491         assertNotNull(vFile);
492         VfsUtil.saveText(vFile, text);
493         PsiJavaFile psiFile = (PsiJavaFile)myPsiManager.findFile(vFile);
494         assertNotNull(psiFile);
495         PsiClass psiClass = psiFile.getClasses()[0];
496         result.setResult(psiClass);
497
498       }
499     }.execute().throwException().getResultObject();
500   }
501 }