diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/JavaCodeInsightSanityTest.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/JavaCodeInsightSanityTest.java index 4c5423c291ed..bbd13f42259c 100644 --- a/java/java-tests/testSrc/com/intellij/java/propertyBased/JavaCodeInsightSanityTest.java +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/JavaCodeInsightSanityTest.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.java.propertyBased; import com.intellij.application.options.CodeStyle; @@ -59,7 +59,7 @@ public class JavaCodeInsightSanityTest extends LightJavaCodeInsightFixtureTestCa } private void enableInspections() { - MadTestingUtil.enableAllInspections(getProject(), getTestRootDisposable()); + MadTestingUtil.enableAllInspections(getProject()); } public void testPreserveComments() { diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/JavaSwitchExpressionSanityTest.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/JavaSwitchExpressionSanityTest.java index f0244176cc3a..9b1dba909712 100644 --- a/java/java-tests/testSrc/com/intellij/java/propertyBased/JavaSwitchExpressionSanityTest.java +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/JavaSwitchExpressionSanityTest.java @@ -47,7 +47,7 @@ public class JavaSwitchExpressionSanityTest extends LightJavaCodeInsightFixtureT } public void testIntentionsAroundSwitch() { - MadTestingUtil.enableAllInspections(getProject(), getTestRootDisposable(), "BoundedWildcard"); // IDEA-194460 + MadTestingUtil.enableAllInspections(getProject(), "BoundedWildcard"); // IDEA-194460 Function> fileActions = file -> { diff --git a/java/java-tests/testSrc/com/intellij/java/propertyBased/UnivocityTest.java b/java/java-tests/testSrc/com/intellij/java/propertyBased/UnivocityTest.java index e6d93027d89c..cbf3174573e5 100644 --- a/java/java-tests/testSrc/com/intellij/java/propertyBased/UnivocityTest.java +++ b/java/java-tests/testSrc/com/intellij/java/propertyBased/UnivocityTest.java @@ -1,25 +1,13 @@ -/* - * Copyright 2000-2017 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. - */ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.java.propertyBased; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.ex.ProjectEx; import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.psi.*; @@ -49,7 +37,20 @@ public class UnivocityTest extends AbstractApplyAndRevertTestCase { public void setUp() throws Exception { super.setUp(); ((PsiDocumentManagerImpl)PsiDocumentManager.getInstance(myProject)).disableBackgroundCommit(getTestRootDisposable()); - MadTestingUtil.enableAllInspections(myProject, myProject); + MadTestingUtil.enableAllInspections(myProject); + } + + @Override + public void tearDown() throws Exception { + try { + Disposer.dispose(((ProjectEx)myProject).getEarlyDisposable()); + } + catch (Throwable e) { + addSuppressedException(e); + } + finally { + super.tearDown(); + } } public void testCompilabilityAfterIntentions() { diff --git a/platform/platform-impl/src/com/intellij/openapi/project/ex/ProjectEx.java b/platform/platform-impl/src/com/intellij/openapi/project/ex/ProjectEx.java index 7e8ff33862ef..29ac541796dc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/ex/ProjectEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/ex/ProjectEx.java @@ -1,8 +1,10 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.openapi.project.ex; +import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; import com.intellij.util.messages.Topic; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.TestOnly; @@ -28,4 +30,20 @@ public interface ProjectEx extends Project { default boolean isLight() { return false; } + + /** + * {@link Disposable} that will be disposed right after container started to be disposed. + * Use it to dispose something that need to be disposed very early, e.g. {@link com.intellij.util.Alarm}. + * Or, only and only in unit test mode, if you need to publish something to message bus during dispose.

+ * + * In unit test mode light project is not disposed, but this disposable is disposed for each test. + * So, you don't need to have another disposable and can use this one instead.

+ * + * Dependent {@link Disposable#dispose} may be called in any thread. + * Implementation of {@link Disposable#dispose} must be self-contained and isolated (getting services is forbidden, publishing to message bus is allowed only in tests). + */ + @NotNull + @ApiStatus.Experimental + @ApiStatus.Internal + Disposable getEarlyDisposable(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/DefaultProject.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/DefaultProject.java index fc26f022555d..66e8dd8f9a6b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/DefaultProject.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/DefaultProject.java @@ -29,7 +29,7 @@ import java.util.List; /** * @author peter */ -final class DefaultProject extends UserDataHolderBase implements ProjectEx, ProjectStoreOwner { +final class DefaultProject extends UserDataHolderBase implements Project, ProjectStoreOwner { private static final Logger LOG = Logger.getInstance(DefaultProject.class); private final DefaultProjectTimed myDelegate = new DefaultProjectTimed(this) { @@ -129,11 +129,6 @@ final class DefaultProject extends UserDataHolderBase implements ProjectEx, Proj return myDelegate.isCached(); } - @Override - public void setProjectName(@NotNull String name) { - throw new IllegalStateException(); - } - // delegates @Override @NotNull diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java index d594fe460f73..2b59ceb2d3e0 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectImpl.java @@ -9,6 +9,7 @@ import com.intellij.ide.plugins.IdeaPluginDescriptorImpl; import com.intellij.ide.plugins.PluginManagerCore; import com.intellij.ide.startup.StartupManagerEx; import com.intellij.idea.ApplicationLoader; +import com.intellij.openapi.Disposable; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ex.ApplicationManagerEx; @@ -30,6 +31,7 @@ import com.intellij.openapi.project.ex.ProjectEx; import com.intellij.openapi.project.ex.ProjectManagerEx; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.AtomicNotNullLazyValue; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -47,6 +49,7 @@ import java.nio.file.Path; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicReference; public class ProjectImpl extends PlatformComponentManagerImpl implements ProjectEx, ProjectStoreOwner { private static final Logger LOG = Logger.getInstance(ProjectImpl.class); @@ -64,6 +67,9 @@ public class ProjectImpl extends PlatformComponentManagerImpl implements Project private final String creationTrace; private ProjectStoreFactory myProjectStoreFactory; + private final AtomicReference earlyDisposable = new AtomicReference<>(Disposer.newDisposable()); + private volatile boolean temporarilyDisposed; + private final AtomicNotNullLazyValue myComponentStore = AtomicNotNullLazyValue.createValue(() -> { ProjectStoreFactory factory = myProjectStoreFactory != null ? myProjectStoreFactory : ServiceManager.getService(ProjectStoreFactory.class); return factory.createStore(this); @@ -111,13 +117,23 @@ public class ProjectImpl extends PlatformComponentManagerImpl implements Project return myLight; } - private volatile boolean temporarilyDisposed; - @TestOnly void setTemporarilyDisposed(boolean value) { - if (!value && super.isDisposed()) { - LOG.error("Project was already disposed, flag temporarilyDisposed cannot be set to `true`"); + if (temporarilyDisposed == value) { + return; } + + if (value && super.isDisposed()) { + throw new IllegalStateException("Project was already disposed, flag temporarilyDisposed cannot be set to `true`"); + } + + if (!value) { + Disposable newDisposable = Disposer.newDisposable(); + if (!earlyDisposable.compareAndSet(null, newDisposable)) { + throw new IllegalStateException("earlyDisposable must be null on second opening of light project"); + } + } + temporarilyDisposed = value; } @@ -337,8 +353,6 @@ public class ProjectImpl extends PlatformComponentManagerImpl implements Project @Override public synchronized void dispose() { - setDisposeInProgress(); - Application application = ApplicationManager.getApplication(); application.assertWriteAccessAllowed(); // dispose must be under write action @@ -386,6 +400,25 @@ public class ProjectImpl extends PlatformComponentManagerImpl implements Project return "project "; } + @Override + @NotNull + @ApiStatus.Experimental + @ApiStatus.Internal + public final Disposable getEarlyDisposable() { + if (isDisposed()) { + throw new IllegalStateException(this + " is disposed already"); + } + + // maybe null only if disposed, but this condition is checked above + return earlyDisposable.get(); + } + + @ApiStatus.Internal + public final void disposeEarlyDisposable() { + Disposable earlyDisposable = this.earlyDisposable.getAndSet(null); + Disposer.dispose(earlyDisposable); + } + @ApiStatus.Internal public final void setDisposeInProgress() { myContainerState.set(ContainerState.DISPOSE_IN_PROGRESS); diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java index 30402e2fe3d2..3531c310130b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java @@ -689,9 +689,10 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable { // if we are shutting down the entire test framework, proceed to full dispose ProjectImpl projectImpl = (ProjectImpl)project; if (!projectImpl.isTemporarilyDisposed()) { + projectImpl.disposeEarlyDisposable(); projectImpl.setTemporarilyDisposed(true); removeFromOpened(project); - ((ProjectManagerImpl)ProjectManager.getInstance()).updateTheOnlyProjectField(); + updateTheOnlyProjectField(); return true; } projectImpl.setTemporarilyDisposed(false); @@ -725,6 +726,11 @@ public class ProjectManagerImpl extends ProjectManagerEx implements Disposable { // somebody can start progress here, do not wrap in write action fireProjectClosing(project); + // ignore dispose flag + if (project instanceof ProjectImpl) { + ((ProjectImpl)project).disposeEarlyDisposable(); + } + app.runWriteAction(() -> { if (dispose && project instanceof ProjectImpl) { ((ProjectImpl)project).setDisposeInProgress(); diff --git a/platform/testFramework/src/com/intellij/mock/MockProjectEx.java b/platform/testFramework/src/com/intellij/mock/MockProjectEx.java index 43fdf89039e4..bf1af8a3ae5b 100644 --- a/platform/testFramework/src/com/intellij/mock/MockProjectEx.java +++ b/platform/testFramework/src/com/intellij/mock/MockProjectEx.java @@ -14,4 +14,10 @@ public class MockProjectEx extends MockProject implements ProjectEx { @Override public void setProjectName(@NotNull String name) { } + + @NotNull + @Override + public final Disposable getEarlyDisposable() { + return this; + } } \ No newline at end of file diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java index 986a85a21e22..c48bb46ff5f7 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/CodeInsightTestFixture.java @@ -22,6 +22,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.Inlay; import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.project.ex.ProjectEx; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; @@ -656,6 +657,6 @@ public interface CodeInsightTestFixture extends IdeaProjectTestFixture { */ @NotNull default Disposable getProjectDisposable() { - return getProject(); + return ((ProjectEx)getProject()).getEarlyDisposable(); } } \ No newline at end of file diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/IdeaProjectTestFixture.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/IdeaProjectTestFixture.java index 12a624a21132..5c3f7b2a54e0 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/IdeaProjectTestFixture.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/IdeaProjectTestFixture.java @@ -4,6 +4,8 @@ package com.intellij.testFramework.fixtures; import com.intellij.openapi.Disposable; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ex.ProjectEx; +import com.intellij.openapi.project.impl.ProjectImpl; import org.jetbrains.annotations.NotNull; /** @@ -19,6 +21,6 @@ public interface IdeaProjectTestFixture extends IdeaTestFixture { @NotNull default Disposable getTestRootDisposable() { - return getProject(); + return ((ProjectEx)getProject()).getEarlyDisposable(); } } diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java index c1c87d34c48e..bd8d74155f98 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java @@ -2057,5 +2057,4 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig public Disposable getProjectDisposable() { return myProjectFixture.getTestRootDisposable(); } - } diff --git a/platform/testFramework/src/com/intellij/testFramework/propertyBased/MadTestingUtil.java b/platform/testFramework/src/com/intellij/testFramework/propertyBased/MadTestingUtil.java index feee795d8502..914db2c2eb1e 100644 --- a/platform/testFramework/src/com/intellij/testFramework/propertyBased/MadTestingUtil.java +++ b/platform/testFramework/src/com/intellij/testFramework/propertyBased/MadTestingUtil.java @@ -21,6 +21,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.impl.ProjectImpl; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.TextRange; @@ -63,7 +64,7 @@ import java.util.stream.Stream; /** * @author peter */ -public class MadTestingUtil { +public final class MadTestingUtil { private static final Logger LOG = Logger.getInstance(MadTestingUtil.class); private static final boolean USE_ROULETTE_WHEEL = true; @@ -169,7 +170,7 @@ public class MadTestingUtil { * @param disposable when this is disposed, reverts to the previous project inspection profile * @param except short names of inspections to disable */ - public static void enableAllInspections(Project project, Disposable disposable, String... except) { + public static void enableAllInspections(@NotNull Project project, String... except) { InspectionProfileImpl.INIT_INSPECTIONS = true; InspectionProfileImpl profile = new InspectionProfileImpl("allEnabled"); profile.enableAllTools(project); @@ -186,7 +187,7 @@ public class MadTestingUtil { manager.addProfile(profile); InspectionProfileImpl prev = manager.getCurrentProfile(); manager.setCurrentProfile(profile); - Disposer.register(disposable, () -> { + Disposer.register(((ProjectImpl)project).getEarlyDisposable(), () -> { InspectionProfileImpl.INIT_INSPECTIONS = false; manager.setCurrentProfile(prev); manager.deleteProfile(profile); @@ -549,7 +550,7 @@ public class MadTestingUtil { Arrays.sort(children, Comparator.comparing(File::getName)); while (true) { int[] weights = Arrays.stream(children).mapToInt(child -> estimateWeight(child, exhausted)).toArray(); - int index = 0; + int index; try { index = spin(data, weights); } diff --git a/plugins/properties/testSrc/com/intellij/lang/properties/propertyBased/PropertiesCodeInsightSanityTest.java b/plugins/properties/testSrc/com/intellij/lang/properties/propertyBased/PropertiesCodeInsightSanityTest.java index 35537deb5494..2afecbadaca5 100644 --- a/plugins/properties/testSrc/com/intellij/lang/properties/propertyBased/PropertiesCodeInsightSanityTest.java +++ b/plugins/properties/testSrc/com/intellij/lang/properties/propertyBased/PropertiesCodeInsightSanityTest.java @@ -1,4 +1,4 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.lang.properties.propertyBased; import com.intellij.lang.properties.PropertiesFileType; @@ -16,7 +16,6 @@ import java.util.function.Supplier; @SkipSlowTestLocally public class PropertiesCodeInsightSanityTest extends LightJavaCodeInsightFixtureTestCase { - public void testIncrementalHighlighterUpdate() { PropertyChecker.checkScenarios(actionsOnPropertiesFiles(CheckHighlighterConsistency.randomEditsWithHighlighterChecks)); } @@ -26,7 +25,7 @@ public class PropertiesCodeInsightSanityTest extends LightJavaCodeInsightFixture } public void testRandomActivity() { - MadTestingUtil.enableAllInspections(getProject(), getTestRootDisposable()); + MadTestingUtil.enableAllInspections(getProject()); Function> fileActions = file -> Generator.sampledFrom(new InvokeIntention(file, new IntentionPolicy() { @Override diff --git a/python/testSrc/com/intellij/python/propertyBased/PythonCodeInsightSanityTest.java b/python/testSrc/com/intellij/python/propertyBased/PythonCodeInsightSanityTest.java index 91f11d004e40..29d35953279f 100644 --- a/python/testSrc/com/intellij/python/propertyBased/PythonCodeInsightSanityTest.java +++ b/python/testSrc/com/intellij/python/propertyBased/PythonCodeInsightSanityTest.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2017 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. - */ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.python.propertyBased; import com.intellij.openapi.application.ApplicationManager; @@ -40,7 +26,6 @@ import java.util.function.Supplier; */ @SkipSlowTestLocally public class PythonCodeInsightSanityTest extends PyEnvTestCase { - /** * When this test fail please record its rechecking seed and create stable test providing it to {@link #runActivity(Pair)} */ @@ -84,7 +69,7 @@ public class PythonCodeInsightSanityTest extends PyEnvTestCase { private void runActivity(@Nullable final Pair seedToRepeat) { runSanityTest(pathAndFixture -> { final CodeInsightTestFixture fixture = pathAndFixture.second; - MadTestingUtil.enableAllInspections(fixture.getProject(), fixture.getProject()); + MadTestingUtil.enableAllInspections(fixture.getProject()); Function> fileActions = file -> Generator.sampledFrom(new InvokeIntention(file, new IntentionPolicy()), new InvokeCompletion(file, new CompletionPolicy()),