| /* |
| * Copyright 2000-2014 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.intellij.codeInsight.daemon; |
| |
| import com.intellij.codeHighlighting.HighlightDisplayLevel; |
| import com.intellij.codeHighlighting.Pass; |
| import com.intellij.codeInsight.CodeInsightTestCase; |
| import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx; |
| import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl; |
| import com.intellij.codeInsight.daemon.impl.HighlightInfo; |
| import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase; |
| import com.intellij.codeInsight.intention.IntentionAction; |
| import com.intellij.codeInsight.intention.IntentionManager; |
| import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler; |
| import com.intellij.codeInspection.InspectionProfileEntry; |
| import com.intellij.codeInspection.InspectionToolProvider; |
| import com.intellij.codeInspection.LocalInspectionTool; |
| import com.intellij.codeInspection.ModifiableModel; |
| import com.intellij.codeInspection.ex.*; |
| import com.intellij.ide.highlighter.JavaFileType; |
| import com.intellij.ide.startup.StartupManagerEx; |
| import com.intellij.ide.startup.impl.StartupManagerImpl; |
| import com.intellij.lang.ExternalAnnotatorsFilter; |
| import com.intellij.lang.LanguageAnnotators; |
| import com.intellij.lang.StdLanguages; |
| import com.intellij.lang.annotation.HighlightSeverity; |
| import com.intellij.lang.injection.InjectedLanguageManager; |
| import com.intellij.lang.java.JavaLanguage; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.Result; |
| import com.intellij.openapi.application.ex.PathManagerEx; |
| import com.intellij.openapi.command.WriteCommandAction; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.extensions.Extensions; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.project.DumbService; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.roots.ModuleRootManager; |
| import com.intellij.openapi.startup.StartupManager; |
| import com.intellij.openapi.util.Condition; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.util.Pair; |
| import com.intellij.openapi.util.TextRange; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.vfs.*; |
| import com.intellij.profile.codeInspection.InspectionProfileManager; |
| import com.intellij.profile.codeInspection.InspectionProjectProfileManager; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.JavaPsiFacadeEx; |
| import com.intellij.psi.impl.cache.CacheManager; |
| import com.intellij.psi.impl.search.IndexPatternBuilder; |
| import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; |
| import com.intellij.psi.impl.source.tree.TreeElement; |
| import com.intellij.psi.impl.source.tree.TreeUtil; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.search.UsageSearchContext; |
| import com.intellij.psi.xml.XmlFileNSInfoProvider; |
| import com.intellij.testFramework.ExpectedHighlightingData; |
| import com.intellij.testFramework.FileTreeAccessFilter; |
| import com.intellij.testFramework.HighlightTestInfo; |
| import com.intellij.testFramework.LightPlatformTestCase; |
| import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.ui.UIUtil; |
| import com.intellij.xml.XmlSchemaProvider; |
| import gnu.trove.THashMap; |
| import gnu.trove.TIntArrayList; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.annotation.ElementType; |
| import java.lang.annotation.Retention; |
| import java.lang.annotation.RetentionPolicy; |
| import java.lang.annotation.Target; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| |
| public abstract class DaemonAnalyzerTestCase extends CodeInsightTestCase { |
| private final Map<String, InspectionToolWrapper> myAvailableTools = new THashMap<String, InspectionToolWrapper>(); |
| private final FileTreeAccessFilter myFileTreeAccessFilter = new FileTreeAccessFilter(); |
| |
| @Override |
| protected boolean isRunInWriteAction() { |
| return false; |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| |
| final LocalInspectionTool[] tools = configureLocalInspectionTools(); |
| for (LocalInspectionTool tool : tools) { |
| enableInspectionTool(tool); |
| } |
| |
| final InspectionProfileImpl profile = new InspectionProfileImpl(LightPlatformTestCase.PROFILE) { |
| @Override |
| @NotNull |
| public ModifiableModel getModifiableModel() { |
| mySource = this; |
| return this; |
| } |
| |
| @Override |
| @NotNull |
| public InspectionToolWrapper[] getInspectionTools(PsiElement element) { |
| Collection<InspectionToolWrapper> values = myAvailableTools.values(); |
| return values.toArray(new InspectionToolWrapper[values.size()]); |
| } |
| |
| @NotNull |
| @Override |
| public List<Tools> getAllEnabledInspectionTools(Project project) { |
| List<Tools> result = new ArrayList<Tools>(); |
| for (InspectionToolWrapper toolWrapper : getInspectionTools(null)) { |
| result.add(new ToolsImpl(toolWrapper, toolWrapper.getDefaultLevel(), true)); |
| } |
| return result; |
| } |
| |
| @Override |
| public boolean isToolEnabled(HighlightDisplayKey key, PsiElement element) { |
| return key != null && myAvailableTools.containsKey(key.toString()); |
| } |
| |
| @Override |
| public HighlightDisplayLevel getErrorLevel(@NotNull HighlightDisplayKey key, PsiElement element) { |
| final InspectionToolWrapper localInspectionTool = myAvailableTools.get(key.toString()); |
| return localInspectionTool != null ? localInspectionTool.getDefaultLevel() : HighlightDisplayLevel.WARNING; |
| } |
| |
| @Override |
| public InspectionToolWrapper getInspectionTool(@NotNull String shortName, @NotNull PsiElement element) { |
| return myAvailableTools.get(shortName); |
| } |
| }; |
| final InspectionProfileManager inspectionProfileManager = InspectionProfileManager.getInstance(); |
| inspectionProfileManager.addProfile(profile); |
| inspectionProfileManager.setRootProfile(LightPlatformTestCase.PROFILE); |
| Disposer.register(getProject(), new Disposable() { |
| @Override |
| public void dispose() { |
| inspectionProfileManager.deleteProfile(LightPlatformTestCase.PROFILE); |
| } |
| }); |
| InspectionProjectProfileManager.getInstance(getProject()).updateProfile(profile); |
| InspectionProjectProfileManager.getInstance(getProject()).setProjectProfile(profile.getName()); |
| DaemonCodeAnalyzerImpl daemonCodeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject()); |
| daemonCodeAnalyzer.prepareForTest(); |
| final StartupManagerImpl startupManager = (StartupManagerImpl)StartupManagerEx.getInstanceEx(getProject()); |
| startupManager.runStartupActivities(); |
| startupManager.startCacheUpdate(); |
| startupManager.runPostStartupActivities(); |
| DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(false); |
| |
| if (isPerformanceTest()) { |
| IntentionManager.getInstance().getAvailableIntentionActions(); // hack to avoid slowdowns in PyExtensionFactory |
| PathManagerEx.getTestDataPath(); // to cache stuff |
| ReferenceProvidersRegistry.getInstance(); // pre-load tons of classes |
| InjectedLanguageManager.getInstance(getProject()); // zillion of Dom Sem classes |
| LanguageAnnotators.INSTANCE.allForLanguage(JavaLanguage.INSTANCE); // pile of annotator classes loads |
| LanguageAnnotators.INSTANCE.allForLanguage(StdLanguages.XML); |
| ProblemHighlightFilter.EP_NAME.getExtensions(); |
| Extensions.getExtensions(ImplicitUsageProvider.EP_NAME); |
| Extensions.getExtensions(XmlSchemaProvider.EP_NAME); |
| Extensions.getExtensions(XmlFileNSInfoProvider.EP_NAME); |
| Extensions.getExtensions(ExternalAnnotatorsFilter.EXTENSION_POINT_NAME); |
| Extensions.getExtensions(IndexPatternBuilder.EP_NAME); |
| } |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| ((StartupManagerImpl)StartupManager.getInstance(getProject())).checkCleared(); |
| ((DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(getProject())).cleanupAfterTest(); |
| super.tearDown(); |
| //((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).assertPointersDisposed(); |
| } |
| |
| protected void enableInspectionTool(@NotNull InspectionProfileEntry tool) { |
| InspectionToolWrapper toolWrapper = InspectionToolRegistrar.wrapTool(tool); |
| LightPlatformTestCase.enableInspectionTool(myAvailableTools, toolWrapper); |
| } |
| |
| protected void enableInspectionToolsFromProvider(InspectionToolProvider toolProvider){ |
| try { |
| for(Class c:toolProvider.getInspectionClasses()) { |
| enableInspectionTool((LocalInspectionTool)c.newInstance()); |
| } |
| } |
| catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| protected void disableInspectionTool(String shortName){ |
| myAvailableTools.remove(shortName); |
| } |
| |
| protected LocalInspectionTool[] configureLocalInspectionTools() { |
| return LocalInspectionTool.EMPTY_ARRAY; |
| } |
| |
| protected static LocalInspectionTool[] createLocalInspectionTools(final InspectionToolProvider... provider) { |
| final ArrayList<LocalInspectionTool> result = new ArrayList<LocalInspectionTool>(); |
| for (InspectionToolProvider toolProvider : provider) { |
| for (Class aClass : toolProvider.getInspectionClasses()) { |
| try { |
| final Object tool = aClass.newInstance(); |
| assertTrue(tool instanceof LocalInspectionTool); |
| result.add((LocalInspectionTool)tool); |
| } |
| catch (Exception e) { |
| LOG.error(e); |
| } |
| } |
| } |
| return result.toArray(new LocalInspectionTool[result.size()]); |
| } |
| |
| protected void doTest(@NonNls @NotNull String filePath, boolean checkWarnings, boolean checkInfos, boolean checkWeakWarnings) throws Exception { |
| configureByFile(filePath); |
| doDoTest(checkWarnings, checkInfos, checkWeakWarnings); |
| } |
| |
| protected void doTest(@NonNls @NotNull String filePath, boolean checkWarnings, boolean checkInfos) throws Exception { |
| doTest(filePath, checkWarnings, checkInfos, false); |
| } |
| |
| protected void doTest(@NonNls @NotNull String filePath, @NonNls String projectRoot, boolean checkWarnings, boolean checkInfos) throws Exception { |
| configureByFile(filePath, projectRoot); |
| doDoTest(checkWarnings, checkInfos); |
| } |
| |
| @NotNull |
| @SuppressWarnings("TestMethodWithIncorrectSignature") |
| protected HighlightTestInfo testFile(@NonNls @NotNull String... filePath) { |
| return new HighlightTestInfo(getTestRootDisposable(), filePath) { |
| @Override |
| public HighlightTestInfo doTest() { |
| try { configureByFiles(projectRoot, filePaths); } |
| catch (Exception e) { throw new RuntimeException(e); } |
| ExpectedHighlightingData data = new ExpectedHighlightingData(myEditor.getDocument(), checkWarnings, checkWeakWarnings, checkInfos, myFile); |
| if (checkSymbolNames) data.checkSymbolNames(); |
| checkHighlighting(data); |
| return this; |
| } |
| }; |
| } |
| |
| protected void doTest(@NotNull VirtualFile vFile, boolean checkWarnings, boolean checkInfos) throws Exception { |
| doTest(new VirtualFile[] { vFile }, checkWarnings, checkInfos ); |
| } |
| |
| protected void doTest(@NotNull VirtualFile[] vFile, boolean checkWarnings, boolean checkInfos) throws Exception { |
| configureByFiles(null, vFile); |
| doDoTest(checkWarnings, checkInfos); |
| } |
| |
| protected void doTest(boolean checkWarnings, boolean checkInfos, String ... files) throws Exception { |
| configureByFiles(null, files); |
| doDoTest(checkWarnings, checkInfos); |
| } |
| |
| @NotNull |
| protected Collection<HighlightInfo> doDoTest(boolean checkWarnings, boolean checkInfos) { |
| return doDoTest(checkWarnings, checkInfos, false); |
| } |
| |
| protected Collection<HighlightInfo> doDoTest(final boolean checkWarnings, final boolean checkInfos, final boolean checkWeakWarnings) { |
| return ContainerUtil.filter( |
| checkHighlighting(new ExpectedHighlightingData(myEditor.getDocument(), checkWarnings, checkWeakWarnings, checkInfos, myFile)), |
| new Condition<HighlightInfo>() { |
| @Override |
| public boolean value(HighlightInfo info) { |
| return (info.getSeverity() == HighlightSeverity.INFORMATION) && checkInfos || |
| (info.getSeverity() == HighlightSeverity.WARNING) && checkWarnings || |
| (info.getSeverity() == HighlightSeverity.WEAK_WARNING) && checkWeakWarnings || |
| info.getSeverity().compareTo(HighlightSeverity.WARNING) > 0; |
| } |
| }); |
| } |
| |
| @NotNull |
| protected Collection<HighlightInfo> checkHighlighting(@NotNull final ExpectedHighlightingData data) { |
| data.init(); |
| PsiDocumentManager.getInstance(myProject).commitAllDocuments(); |
| |
| //to load text |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| @Override |
| public void run() { |
| TreeUtil.clearCaches((TreeElement)myFile.getNode()); |
| } |
| }); |
| |
| |
| //to initialize caches |
| if (!DumbService.isDumb(getProject())) { |
| CacheManager.SERVICE.getInstance(myProject).getFilesWithWord("XXX", UsageSearchContext.IN_COMMENTS, GlobalSearchScope.allScope(myProject), true); |
| } |
| final JavaPsiFacadeEx facade = getJavaFacade(); |
| if (facade != null) { |
| facade.setAssertOnFileLoadingFilter(myFileTreeAccessFilter, myTestRootDisposable); // check repository work |
| } |
| |
| try { |
| Collection<HighlightInfo> infos = doHighlighting(); |
| |
| String text = myEditor.getDocument().getText(); |
| data.checkLineMarkers(DaemonCodeAnalyzerImpl.getLineMarkers(getDocument(getFile()), getProject()), text); |
| data.checkResult(infos, text); |
| return infos; |
| } |
| finally { |
| if (facade != null) { |
| facade.setAssertOnFileLoadingFilter(VirtualFileFilter.NONE, myTestRootDisposable); |
| } |
| } |
| } |
| |
| public void allowTreeAccessForFile(@NotNull VirtualFile file) { |
| myFileTreeAccessFilter.allowTreeAccessForFile(file); |
| } |
| |
| @NotNull |
| protected List<HighlightInfo> highlightErrors() { |
| return doHighlighting(HighlightSeverity.ERROR); |
| } |
| |
| @NotNull |
| protected List<HighlightInfo> doHighlighting(@NotNull HighlightSeverity minSeverity) { |
| return filter(doHighlighting(), minSeverity); |
| } |
| |
| @NotNull |
| protected List<HighlightInfo> doHighlighting() { |
| PsiDocumentManager.getInstance(myProject).commitAllDocuments(); |
| |
| TIntArrayList toIgnore = new TIntArrayList(); |
| if (!doTestLineMarkers()) { |
| toIgnore.add(Pass.UPDATE_OVERRIDEN_MARKERS); |
| toIgnore.add(Pass.VISIBLE_LINE_MARKERS); |
| toIgnore.add(Pass.LINE_MARKERS); |
| } |
| |
| if (!doExternalValidation()) { |
| toIgnore.add(Pass.EXTERNAL_TOOLS); |
| } |
| if (forceExternalValidation()) { |
| toIgnore.add(Pass.LINE_MARKERS); |
| toIgnore.add(Pass.LOCAL_INSPECTIONS); |
| toIgnore.add(Pass.WHOLE_FILE_LOCAL_INSPECTIONS); |
| toIgnore.add(Pass.POPUP_HINTS); |
| toIgnore.add(Pass.POST_UPDATE_ALL); |
| toIgnore.add(Pass.UPDATE_ALL); |
| toIgnore.add(Pass.UPDATE_OVERRIDEN_MARKERS); |
| toIgnore.add(Pass.VISIBLE_LINE_MARKERS); |
| } |
| |
| boolean canChange = canChangeDocumentDuringHighlighting(); |
| List<HighlightInfo> infos = CodeInsightTestFixtureImpl.instantiateAndRun(getFile(), getEditor(), toIgnore.toNativeArray(), canChange); |
| |
| if (!canChange) { |
| Document document = getDocument(getFile()); |
| DaemonCodeAnalyzerEx daemonCodeAnalyzer = DaemonCodeAnalyzerEx.getInstanceEx(myProject); |
| daemonCodeAnalyzer.getFileStatusMap().assertAllDirtyScopesAreNull(document); |
| } |
| |
| return infos; |
| } |
| |
| @Retention(RetentionPolicy.RUNTIME) |
| @Target({ElementType.METHOD, ElementType.TYPE}) |
| public @interface CanChangeDocumentDuringHighlighting {} |
| |
| private boolean canChangeDocumentDuringHighlighting() { |
| return annotatedWith(CanChangeDocumentDuringHighlighting.class); |
| } |
| |
| @NotNull |
| public static List<HighlightInfo> filter(@NotNull List<HighlightInfo> infos, @NotNull HighlightSeverity minSeverity) { |
| ArrayList<HighlightInfo> result = new ArrayList<HighlightInfo>(); |
| for (final HighlightInfo info : infos) { |
| if (info.getSeverity().compareTo(minSeverity) >= 0) result.add(info); |
| } |
| return result; |
| } |
| |
| protected boolean doTestLineMarkers() { |
| return false; |
| } |
| |
| protected boolean doExternalValidation() { |
| return true; |
| } |
| |
| protected boolean forceExternalValidation() { |
| return false; |
| } |
| |
| protected static void findAndInvokeIntentionAction(@NotNull Collection<HighlightInfo> infos, @NotNull String intentionActionName, @NotNull Editor editor, |
| @NotNull PsiFile file) throws IncorrectOperationException { |
| IntentionAction intentionAction = findIntentionAction(infos, intentionActionName, editor, file); |
| |
| assertNotNull(intentionActionName, intentionAction); |
| assertTrue(ShowIntentionActionsHandler.chooseActionAndInvoke(file, editor, intentionAction, intentionActionName)); |
| UIUtil.dispatchAllInvocationEvents(); |
| } |
| |
| protected static IntentionAction findIntentionAction(@NotNull Collection<HighlightInfo> infos, @NotNull String intentionActionName, @NotNull Editor editor, |
| @NotNull PsiFile file) { |
| List<IntentionAction> actions = LightQuickFixTestCase.getAvailableActions(editor, file); |
| IntentionAction intentionAction = LightQuickFixTestCase.findActionWithText(actions, intentionActionName); |
| |
| if (intentionAction == null) { |
| final List<IntentionAction> availableActions = new ArrayList<IntentionAction>(); |
| |
| for (HighlightInfo info :infos) { |
| if (info.quickFixActionRanges != null) { |
| for (Pair<HighlightInfo.IntentionActionDescriptor, TextRange> pair : info.quickFixActionRanges) { |
| IntentionAction action = pair.first.getAction(); |
| if (action.isAvailable(file.getProject(), editor, file)) availableActions.add(action); |
| } |
| } |
| } |
| |
| intentionAction = LightQuickFixTestCase.findActionWithText( |
| availableActions, |
| intentionActionName |
| ); |
| } |
| return intentionAction; |
| } |
| |
| public void checkHighlighting(Editor editor, boolean checkWarnings, boolean checkInfos) { |
| setActiveEditor(editor); |
| doDoTest(checkWarnings, checkInfos); |
| } |
| |
| public PsiClass createClass(String text) throws IOException { |
| return createClass(myModule, text); |
| } |
| |
| protected PsiClass createClass(final Module module, final String text) throws IOException { |
| return new WriteCommandAction<PsiClass>(getProject()) { |
| @Override |
| protected void run(Result<PsiClass> result) throws Throwable { |
| final PsiFileFactory factory = PsiFileFactory.getInstance(getProject()); |
| final PsiJavaFile javaFile = (PsiJavaFile)factory.createFileFromText("a.java", JavaFileType.INSTANCE, text); |
| final String qname = javaFile.getClasses()[0].getQualifiedName(); |
| assertNotNull(qname); |
| final VirtualFile[] files = ModuleRootManager.getInstance(module).getSourceRoots(); |
| File dir; |
| if (files.length > 0) { |
| dir = VfsUtilCore.virtualToIoFile(files[0]); |
| } |
| else { |
| dir = createTempDirectory(); |
| VirtualFile vDir = |
| LocalFileSystem.getInstance().refreshAndFindFileByPath(dir.getCanonicalPath().replace(File.separatorChar, '/')); |
| addSourceContentToRoots(module, vDir); |
| } |
| |
| File file = new File(dir, qname.replace('.', '/') + ".java"); |
| FileUtil.createIfDoesntExist(file); |
| VirtualFile vFile = LocalFileSystem.getInstance().refreshAndFindFileByPath(file.getCanonicalPath().replace(File.separatorChar, '/')); |
| assertNotNull(vFile); |
| VfsUtil.saveText(vFile, text); |
| PsiJavaFile psiFile = (PsiJavaFile)myPsiManager.findFile(vFile); |
| assertNotNull(psiFile); |
| PsiClass psiClass = psiFile.getClasses()[0]; |
| result.setResult(psiClass); |
| |
| } |
| }.execute().throwException().getResultObject(); |
| } |
| } |