fix many leaking threads in tests

This commit is contained in:
Alexey Kudravtsev
2018-11-20 15:36:41 +03:00
parent 44ffc93d00
commit df49bfbecd
8 changed files with 76 additions and 21 deletions
@@ -164,18 +164,18 @@ public class RepositoryLibrarySynchronizer implements StartupActivity, DumbAware
}, project.getDisposed());
};
project.getMessageBus().connect().subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() {
private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, project);
@Override
public void rootsChanged(@NotNull final ModuleRootEvent event) {
if (!myAlarm.isDisposed() && event.getSource() instanceof Project) {
myAlarm.cancelAllRequests();
myAlarm.addRequest(syncTask, 300L);
}
}
});
if (!ApplicationManager.getApplication().isUnitTestMode()) {
project.getMessageBus().connect().subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootListener() {
private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.POOLED_THREAD, project);
@Override
public void rootsChanged(@NotNull final ModuleRootEvent event) {
if (!myAlarm.isDisposed() && event.getSource() instanceof Project) {
myAlarm.cancelAllRequests();
myAlarm.addRequest(syncTask, 300L);
}
}
});
ApplicationManager.getApplication().executeOnPooledThread(() -> {
removeDuplicatedUrlsFromRepositoryLibraries(project);
syncTask.run();
@@ -323,15 +323,14 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements Pers
FileStatusMap fileStatusMap = getFileStatusMap();
Map<FileEditor, HighlightingPass[]> map = new HashMap<>();
try {
waitForAllEditorsFinallyLoaded(project, 10, TimeUnit.SECONDS);
}
catch (TimeoutException e) {
throw new RuntimeException("editors have not completed loading in 10 seconds");
}
for (TextEditor textEditor : textEditors) {
if (textEditor instanceof TextEditorImpl) {
try {
((TextEditorImpl)textEditor).waitForLoaded(10, TimeUnit.SECONDS);
}
catch (TimeoutException e) {
throw new RuntimeException(textEditor + " has not completed loading in 10 seconds");
}
}
TextEditorBackgroundHighlighter highlighter = (TextEditorBackgroundHighlighter)textEditor.getBackgroundHighlighter();
if (highlighter == null) {
Editor editor = textEditor.getEditor();
@@ -392,6 +391,28 @@ public class DaemonCodeAnalyzerImpl extends DaemonCodeAnalyzerEx implements Pers
}
}
@TestOnly
public static void waitForAllEditorsFinallyLoaded(@NotNull Project project, long timeout, @NotNull TimeUnit unit) throws TimeoutException {
ApplicationManager.getApplication().assertIsDispatchThread();
long deadline = unit.toMillis(timeout) + System.currentTimeMillis();
W:
while (true) {
UIUtil.dispatchAllInvocationEvents();
if (System.currentTimeMillis() > deadline) throw new TimeoutException();
for (FileEditor editor : FileEditorManager.getInstance(project).getAllEditors()) {
if (editor instanceof TextEditorImpl) {
try {
((TextEditorImpl)editor).waitForLoaded(1, TimeUnit.MILLISECONDS);
}
catch (TimeoutException ignored) {
continue W;
}
}
}
break;
}
}
@TestOnly
private boolean waitInOtherThread(int millis, boolean canChangeDocument) throws Throwable {
Disposable disposable = Disposer.newDisposable();
@@ -3,6 +3,8 @@ package com.intellij.ide;
import com.intellij.AppTopics;
import com.intellij.ProjectTopics;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.components.ProjectComponent;
import com.intellij.openapi.editor.Document;
@@ -32,7 +34,7 @@ import java.util.concurrent.TimeUnit;
/**
* @author nik
*/
public class GeneratedSourceFileChangeTrackerImpl extends GeneratedSourceFileChangeTracker implements ProjectComponent {
public class GeneratedSourceFileChangeTrackerImpl extends GeneratedSourceFileChangeTracker implements ProjectComponent, Disposable {
private final Project myProject;
private final FileDocumentManager myDocumentManager;
private final EditorNotifications myEditorNotifications;
@@ -44,12 +46,19 @@ public class GeneratedSourceFileChangeTrackerImpl extends GeneratedSourceFileCha
myProject = project;
myDocumentManager = documentManager;
myEditorNotifications = editorNotifications;
myCheckingQueue = new SingleAlarm(this::checkFiles, 500, Alarm.ThreadToUse.POOLED_THREAD, project);
myCheckingQueue = new SingleAlarm(this::checkFiles, 500, Alarm.ThreadToUse.POOLED_THREAD, this);
}
@Override
public void dispose() {
}
@TestOnly
public void waitForAlarm() throws Exception {
myCheckingQueue.waitForAllExecuted(1, TimeUnit.SECONDS);
if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
throw new IllegalStateException("Must not wait for the alarm under write action");
}
myCheckingQueue.waitForAllExecuted(10, TimeUnit.SECONDS);
}
@Override
@@ -5,6 +5,7 @@ import com.intellij.ProjectTopics;
import com.intellij.ReviseWhenPortedToJDK;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.completion.CompletionProgressIndicator;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.hint.HintManagerImpl;
import com.intellij.codeInsight.lookup.LookupManager;
@@ -386,6 +387,7 @@ public abstract class LightPlatformTestCase extends UsefulTestCase implements Da
// don't use method references here to make stack trace reading easier
//noinspection Convert2MethodRef
new RunAll(
() -> DaemonCodeAnalyzerImpl.waitForAllEditorsFinallyLoaded(project, 10, TimeUnit.SECONDS),
() -> CodeStyle.dropTemporarySettings(project),
() -> myCodeStyleSettingsTracker.checkForSettingsDamage(),
() -> doTearDown(project, ourApplication),
@@ -3,6 +3,7 @@ package com.intellij.testFramework;
import com.intellij.application.options.CodeStyle;
import com.intellij.codeInsight.AutoPopupController;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
import com.intellij.ide.highlighter.ModuleFileType;
import com.intellij.ide.highlighter.ProjectFileType;
import com.intellij.ide.startup.impl.StartupManagerImpl;
@@ -500,6 +501,7 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro
protected void tearDown() throws Exception {
Project project = myProject;
if (project != null && !project.isDisposed()) {
DaemonCodeAnalyzerImpl.waitForAllEditorsFinallyLoaded(project, 10, TimeUnit.SECONDS);
AutoPopupController.getInstance(project).cancelAllRequests(); // clear "show param info" delayed requests leaking project
}
// don't use method references here to make stack trace reading easier
@@ -5,6 +5,7 @@ import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.concurrency.IdeaForkJoinWorkerThreadFactory;
import com.intellij.diagnostic.PerformanceWatcher;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.application.impl.ApplicationInfoImpl;
import com.intellij.openapi.command.impl.StartMarkAction;
@@ -30,6 +31,8 @@ import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.PeekableIterator;
import com.intellij.util.containers.PeekableIteratorWrapper;
import com.intellij.util.containers.hash.HashMap;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.indexing.FileBasedIndexImpl;
import com.intellij.util.lang.CompoundRuntimeException;
import com.intellij.util.ui.UIUtil;
import gnu.trove.Equality;
@@ -52,6 +55,7 @@ import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* @author peter
@@ -141,6 +145,10 @@ public abstract class UsefulTestCase extends TestCase {
// don't use method references here to make stack trace reading easier
//noinspection Convert2MethodRef
new RunAll(
() -> EdtTestUtil.runInEdtAndWait(() -> {
FileBasedIndexImpl index = ApplicationManager.getApplication() == null ? null : (FileBasedIndexImpl)FileBasedIndex.getInstance();
if (index != null) index.waitForVfsEventsExecuted(1, TimeUnit.MINUTES);
}),
() -> disposeRootDisposable(),
() -> cleanupSwingDataStructures(),
() -> cleanupDeleteOnExitHookList(),
@@ -1,6 +1,7 @@
// 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.
package com.intellij.testFramework.fixtures;
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
@@ -12,6 +13,7 @@ import com.intellij.testFramework.builders.ModuleFixtureBuilder;
import org.jetbrains.annotations.NonNls;
import java.io.File;
import java.util.concurrent.TimeUnit;
/**
* @author yole
@@ -42,6 +44,8 @@ public abstract class CodeInsightFixtureTestCase<T extends ModuleFixtureBuilder>
@Override
protected void tearDown() throws Exception {
DaemonCodeAnalyzerImpl.waitForAllEditorsFinallyLoaded(getProject(), 10, TimeUnit.SECONDS);
myModule = null;
try {
myFixture.tearDown();
@@ -17,13 +17,18 @@ package com.intellij.testFramework.fixtures.impl;
import com.intellij.concurrency.IdeaForkJoinWorkerThreadFactory;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Disposer;
import com.intellij.testFramework.EdtTestUtil;
import com.intellij.testFramework.UsefulTestCase;
import com.intellij.testFramework.fixtures.IdeaTestFixture;
import com.intellij.util.indexing.FileBasedIndex;
import com.intellij.util.indexing.FileBasedIndexImpl;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import java.util.concurrent.TimeUnit;
/**
* @author max
*/
@@ -47,6 +52,10 @@ public class BaseFixture implements IdeaTestFixture {
public void tearDown() throws Exception {
Assert.assertTrue("setUp() has not been called", myInitialized);
Assert.assertFalse("tearDown() already has been called", myDisposed);
EdtTestUtil.runInEdtAndWait(() -> {
FileBasedIndexImpl index = ApplicationManager.getApplication() == null ? null : (FileBasedIndexImpl)FileBasedIndex.getInstance();
if (index != null) index.waitForVfsEventsExecuted(1, TimeUnit.MINUTES);
});
disposeRootDisposable();
myDisposed = true;
resetClassFields(getClass());