From 0705d2736a08c88cb6bfd81f3970e845c4c0c3dd Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Tue, 3 Nov 2009 11:25:21 +0300 Subject: [PATCH] tests hang fixed check for leaked injectors added --- .../source/tree/injected/MyTestInjector.java | 64 +++++++++---------- .../LightCodeInsightTestCase.java | 2 +- .../daemon/impl/DaemonCodeAnalyzerImpl.java | 8 +-- .../injected/InjectedLanguageManagerImpl.java | 28 +++++++- .../LightPlatformCodeInsightTestCase.java | 2 +- .../testFramework/LightPlatformTestCase.java | 9 ++- .../testFramework/UsefulTestCase.java | 16 +++-- 7 files changed, 85 insertions(+), 44 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/MyTestInjector.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/MyTestInjector.java index 19e86f0195d8..55eee847fef3 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/MyTestInjector.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/MyTestInjector.java @@ -29,7 +29,9 @@ import com.intellij.lang.injection.ConcatenationAwareInjector; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.lang.injection.MultiHostInjector; import com.intellij.lang.injection.MultiHostRegistrar; +import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.ProperTextRange; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; @@ -44,35 +46,34 @@ import java.util.Arrays; import java.util.*; public class MyTestInjector { - private final List myInjectors = new ArrayList(); private final PsiManager myPsiManager; - private MultiHostInjector myMultiHostInjector; @TestOnly public MyTestInjector(PsiManager psiManager) { myPsiManager = psiManager; } - public void injectAll() { - injectVariousStuffEverywhere(myPsiManager); + public void injectAll(Disposable parent) { + injectVariousStuffEverywhere(parent, myPsiManager); Project project = myPsiManager.getProject(); Language ql = findLanguageByID("JPAQL"); Language js = findLanguageByID("JavaScript"); - registerForStringVarInitializer(project, ql, "ql", null, null); - registerForStringVarInitializer(project, ql, "qlPrefixed", "xxx", null); - registerForStringVarInitializer(project, js, "js", null, null); - registerForStringVarInitializer(project, js, "jsSeparated", " + ", " + 'separator'"); - registerForStringVarInitializer(project, js, "jsBrokenPrefix", "xx ", ""); + registerForStringVarInitializer(parent, project, ql, "ql", null, null); + registerForStringVarInitializer(parent, project, ql, "qlPrefixed", "xxx", null); + registerForStringVarInitializer(parent, project, js, "js", null, null); + registerForStringVarInitializer(parent, project, js, "jsSeparated", " + ", " + 'separator'"); + registerForStringVarInitializer(parent, project, js, "jsBrokenPrefix", "xx ", ""); } - private ConcatenationAwareInjector registerForStringVarInitializer(@NotNull Project project, - final Language language, - @NotNull @NonNls final String varName, - @NonNls final String prefix, - @NonNls final String suffix) { - if (language == null) return null; - ConcatenationAwareInjector injector = new ConcatenationAwareInjector() { + private static void registerForStringVarInitializer(@NotNull Disposable parent, + @NotNull final Project project, + final Language language, + @NotNull @NonNls final String varName, + @NonNls final String prefix, + @NonNls final String suffix) { + if (language == null) return; + final ConcatenationAwareInjector injector = new ConcatenationAwareInjector() { public void getLanguagesToInject(@NotNull MultiHostRegistrar injectionPlacesRegistrar, @NotNull PsiElement... operands) { PsiVariable variable = PsiTreeUtil.getParentOfType(operands[0], PsiVariable.class); if (variable == null) return; @@ -104,19 +105,12 @@ public class MyTestInjector { } }; JavaConcatenationInjectorManager.getInstance(project).registerConcatenationInjector(injector); - myInjectors.add(injector); - return injector; - } - - - public void uninjectAll() { - Project project = myPsiManager.getProject(); - for (ConcatenationAwareInjector i : myInjectors) { - boolean b = JavaConcatenationInjectorManager.getInstance(project).unregisterConcatenationInjector(i); - assert b; - } - boolean b = InjectedLanguageManager.getInstance(project).unregisterMultiHostInjector(myMultiHostInjector); - assert b; + Disposer.register(parent, new Disposable() { + public void dispose() { + boolean b = JavaConcatenationInjectorManager.getInstance(project).unregisterConcatenationInjector(injector); + assert b; + } + }); } private static Language findLanguageByID(@NonNls String id) { @@ -127,11 +121,12 @@ public class MyTestInjector { return null; } - private void injectVariousStuffEverywhere(PsiManager psiManager) { + private static void injectVariousStuffEverywhere(Disposable parent, final PsiManager psiManager) { final Language ql = findLanguageByID("JPAQL"); final Language js = findLanguageByID("JavaScript"); if (ql == null || js == null) return; - myMultiHostInjector = new MultiHostInjector() { + + final MultiHostInjector myMultiHostInjector = new MultiHostInjector() { public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) { XmlAttributeValue value = (XmlAttributeValue)context; PsiElement parent = value.getParent(); @@ -158,6 +153,11 @@ public class MyTestInjector { } }; InjectedLanguageManager.getInstance(psiManager.getProject()).registerMultiHostInjector(myMultiHostInjector); + Disposer.register(parent, new Disposable() { + public void dispose() { + InjectedLanguageManager.getInstance(psiManager.getProject()).unregisterMultiHostInjector(myMultiHostInjector); + } + }); LanguageInjector myInjector = new LanguageInjector() { public void getLanguagesToInject(@NotNull PsiLanguageInjectionHost host, @NotNull InjectedLanguagePlaces placesToInject) { @@ -264,7 +264,7 @@ public class MyTestInjector { } }; - psiManager.registerLanguageInjector(myInjector, psiManager.getProject()); + psiManager.registerLanguageInjector(myInjector, parent); } private static void inject(final PsiLanguageInjectionHost host, final InjectedLanguagePlaces placesToInject, final Language language) { diff --git a/java/testFramework/src/com/intellij/testFramework/LightCodeInsightTestCase.java b/java/testFramework/src/com/intellij/testFramework/LightCodeInsightTestCase.java index 25255146fdcf..a19e47513be4 100644 --- a/java/testFramework/src/com/intellij/testFramework/LightCodeInsightTestCase.java +++ b/java/testFramework/src/com/intellij/testFramework/LightCodeInsightTestCase.java @@ -24,7 +24,7 @@ import com.intellij.openapi.module.StdModuleTypes; /** * A TestCase for single PsiFile being opened in Editor conversion. See configureXXX and checkResultXXX method docs. */ -public class LightCodeInsightTestCase extends LightPlatformCodeInsightTestCase { +public abstract class LightCodeInsightTestCase extends LightPlatformCodeInsightTestCase { public static JavaPsiFacadeEx getJavaFacade() { return JavaPsiFacadeEx.getInstanceEx(ourProject); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java index ae9df3036253..1ecdbfa408fa 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/DaemonCodeAnalyzerImpl.java @@ -105,7 +105,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx private StatusBarUpdater myStatusBarUpdater; private final PassExecutorService myPassExecutorService; private static final Key> HIGHLIGHTS_TO_REMOVE_KEY = Key.create("HIGHLIGHTS_TO_REMOVE"); - private final AtomicInteger myModificationCount = new AtomicInteger(); + private int myModificationCount = 0; public DaemonCodeAnalyzerImpl(Project project, DaemonCodeAnalyzerSettings daemonCodeAnalyzerSettings, EditorTracker editorTracker) { myProject = project; @@ -339,8 +339,8 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx return myFileStatusMap; } - public int getModificationCount() { - return myModificationCount.get(); + public synchronized int getModificationCount() { + return myModificationCount; } public synchronized boolean isRunning() { @@ -358,7 +358,7 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzer implements JDOMEx private synchronized void cancelUpdateProgress(final boolean start, @NonNls String reason) { PassExecutorService.log(myUpdateProgress, null, reason, start); - myModificationCount.incrementAndGet(); + myModificationCount++; if (myUpdateProgress != null) { myUpdateProgress.cancel(); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageManagerImpl.java index ee6be184e273..92c4a339fe49 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/InjectedLanguageManagerImpl.java @@ -22,6 +22,7 @@ import com.intellij.lang.Language; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.lang.injection.MultiHostInjector; import com.intellij.lang.injection.MultiHostRegistrar; +import com.intellij.openapi.Disposable; import com.intellij.openapi.editor.Document; import com.intellij.openapi.extensions.ExtensionPoint; import com.intellij.openapi.extensions.ExtensionPointListener; @@ -35,14 +36,15 @@ import com.intellij.openapi.util.Key; import com.intellij.openapi.util.ProperTextRange; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.Disposable; import com.intellij.psi.*; import com.intellij.psi.impl.PsiManagerEx; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ConcurrentHashMap; +import gnu.trove.THashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import java.util.*; import java.util.concurrent.ConcurrentMap; @@ -272,6 +274,30 @@ public class InjectedLanguageManagerImpl extends InjectedLanguageManager { return InjectedLanguageUtil.findInjectedElementNoCommitWithOffset(hostFile, hostDocumentOffset); } + private final Map myInjectorsClone = new THashMap(); + @TestOnly + public void pushInjectors() { + assert myInjectorsClone.isEmpty() : myInjectorsClone; + myInjectorsClone.putAll(injectors); + } + @TestOnly + public void checkInjectorsAreDisposed() { + try { + for (Map.Entry entry : injectors.entrySet()) { + Class key = entry.getKey(); + MultiHostInjector[] oldInjectors = myInjectorsClone.get(key); + for (MultiHostInjector injector : entry.getValue()) { + if (!ArrayUtil.contains(injector, oldInjectors)) { + throw new AssertionError("Injector was not disposed: " + key + " -> " + injector); + } + } + } + } + finally { + myInjectorsClone.clear(); + } + } + public interface InjProcessor { boolean process(PsiElement element, MultiHostInjector injector); } diff --git a/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java b/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java index dfa93363ae5c..ecc558628445 100644 --- a/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/LightPlatformCodeInsightTestCase.java @@ -54,7 +54,7 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.io.IOException; -public class LightPlatformCodeInsightTestCase extends LightPlatformTestCase { +public abstract class LightPlatformCodeInsightTestCase extends LightPlatformTestCase { private static final Logger LOG = Logger.getInstance("#com.intellij.testFramework.LightCodeInsightTestCase"); protected static Editor myEditor; diff --git a/platform/testFramework/src/com/intellij/testFramework/LightPlatformTestCase.java b/platform/testFramework/src/com/intellij/testFramework/LightPlatformTestCase.java index 2dfabea586c9..a648b75bd7f7 100644 --- a/platform/testFramework/src/com/intellij/testFramework/LightPlatformTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/LightPlatformTestCase.java @@ -29,6 +29,7 @@ import com.intellij.ide.highlighter.ProjectFileType; import com.intellij.ide.startup.impl.StartupManagerImpl; import com.intellij.idea.IdeaLogger; import com.intellij.idea.IdeaTestApplication; +import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.actionSystem.DataConstants; import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.application.ApplicationManager; @@ -71,6 +72,7 @@ import com.intellij.psi.codeStyle.CodeStyleSettingsManager; import com.intellij.psi.impl.PsiDocumentManagerImpl; import com.intellij.psi.impl.PsiManagerEx; import com.intellij.psi.impl.PsiManagerImpl; +import com.intellij.psi.impl.source.tree.injected.InjectedLanguageManagerImpl; import com.intellij.util.IncorrectOperationException; import com.intellij.util.LocalTimeCounter; import com.intellij.util.indexing.FileBasedIndex; @@ -355,6 +357,7 @@ public abstract class LightPlatformTestCase extends UsefulTestCase implements Da assertFalse(getPsiManager().isDisposed()); CodeStyleSettingsManager.getInstance(getProject()).setTemporarySettings(new CodeStyleSettings()); + ((InjectedLanguageManagerImpl)InjectedLanguageManager.getInstance(getProject())).pushInjectors(); } protected void enableInspectionTool(LocalInspectionTool tool){ @@ -389,13 +392,13 @@ public abstract class LightPlatformTestCase extends UsefulTestCase implements Da super.tearDown(); myThreadTracker.checkLeak(); + checkInjectorsAreDisposed(); } public static void doTearDown() throws Exception { UsefulTestCase.doPostponedFormatting(ourProject); LookupManager.getInstance(ourProject).hideActiveLookup(); - InspectionProfileManager.getInstance().deleteProfile(PROFILE); assertNotNull("Application components damaged", ProjectManager.getInstance()); @@ -445,6 +448,10 @@ public abstract class LightPlatformTestCase extends UsefulTestCase implements Da } } + private static void checkInjectorsAreDisposed() { + ((InjectedLanguageManagerImpl)InjectedLanguageManager.getInstance(getProject())).checkInjectorsAreDisposed(); + } + public final void runBare() throws Throwable { final Throwable[] throwables = new Throwable[1]; diff --git a/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java b/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java index 39a7eb0757fb..50722c7febaf 100644 --- a/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java @@ -114,10 +114,18 @@ public abstract class UsefulTestCase extends TestCase { return; } final CodeInsightSettings settings = CodeInsightSettings.getInstance(); - Element newS = new Element("temp"); - settings.writeExternal(newS); - Assert.assertEquals("Code insight settings damaged", DEFAULT_SETTINGS_EXTERNALIZED, JDOMUtil.writeElement(newS, "\n")); - + try { + Element newS = new Element("temp"); + settings.writeExternal(newS); + Assert.assertEquals("Code insight settings damaged", DEFAULT_SETTINGS_EXTERNALIZED, JDOMUtil.writeElement(newS, "\n")); + } + catch (AssertionError error) { + CodeInsightSettings clean = new CodeInsightSettings(); + Element temp = new Element("temp"); + clean.writeExternal(temp); + settings.loadState(temp); + throw error; + } CodeStyleSettings codeStyleSettings = getCurrentCodeStyleSettings(); codeStyleSettings.getIndentOptions(StdFileTypes.JAVA);