diff --git a/java/testFramework/src/com/intellij/ide/projectWizard/ProjectWizardTestCase.java b/java/testFramework/src/com/intellij/ide/projectWizard/ProjectWizardTestCase.java index e0c45d2e2db9..4a9a1524a085 100644 --- a/java/testFramework/src/com/intellij/ide/projectWizard/ProjectWizardTestCase.java +++ b/java/testFramework/src/com/intellij/ide/projectWizard/ProjectWizardTestCase.java @@ -22,6 +22,7 @@ import com.intellij.ide.util.newProjectWizard.SelectTemplateSettings; import com.intellij.ide.util.projectWizard.ModuleWizardStep; import com.intellij.ide.wizard.Step; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.impl.LaterInvocator; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.ProjectJdkTable; @@ -59,6 +60,42 @@ public abstract class ProjectWizardTestCase ext private Project myCreatedProject; private Sdk myOldDefaultProjectSdk; + @Override + protected void setUp() throws Exception { + super.setUp(); + myOldDefaultProjectSdk = ProjectRootManager.getInstance(myProjectManager.getDefaultProject()).getProjectSdk(); + Sdk projectSdk = ProjectRootManager.getInstance(getProject()).getProjectSdk(); + for (final Sdk jdk : ProjectJdkTable.getInstance().getAllJdks()) { + if (projectSdk != jdk) { + ApplicationManager.getApplication().runWriteAction(() -> ProjectJdkTable.getInstance().removeJdk(jdk)); + } + } + } + + @Override + public void tearDown() throws Exception { + try { + if (myWizard != null) { + Disposer.dispose(myWizard.getDisposable()); + myWizard = null; + } + if (myCreatedProject != null) { + myProjectManager.closeProject(myCreatedProject); + ApplicationManager.getApplication().runWriteAction(() -> Disposer.dispose(myCreatedProject)); + myCreatedProject = null; + } + ApplicationManager.getApplication().runWriteAction(() -> { + ProjectRootManager.getInstance(myProjectManager.getDefaultProject()).setProjectSdk(myOldDefaultProjectSdk); + }); + SelectTemplateSettings.getInstance().setLastTemplate(null, null); + UIUtil.dispatchAllInvocationEvents(); // let vfs update pass + LaterInvocator.dispatchPendingFlushes(); + } + finally { + super.tearDown(); + } + } + protected Project createProjectFromTemplate(@NotNull String group, @Nullable String name, @Nullable Consumer adjuster) throws IOException { runWizard(group, name, null, adjuster); try { @@ -139,18 +176,6 @@ public abstract class ProjectWizardTestCase ext throw new RuntimeException(); } - @Override - protected void setUp() throws Exception { - super.setUp(); - myOldDefaultProjectSdk = ProjectRootManager.getInstance(myProjectManager.getDefaultProject()).getProjectSdk(); - Sdk projectSdk = ProjectRootManager.getInstance(getProject()).getProjectSdk(); - for (final Sdk jdk : ProjectJdkTable.getInstance().getAllJdks()) { - if (projectSdk != jdk) { - ApplicationManager.getApplication().runWriteAction(() -> ProjectJdkTable.getInstance().removeJdk(jdk)); - } - } - } - protected void configureJdk() { ApplicationManager.getApplication().runWriteAction(() -> { addSdk(new SimpleJavaSdkType().createJdk(DEFAULT_SDK, SystemProperties.getJavaHome())); @@ -165,28 +190,6 @@ public abstract class ProjectWizardTestCase ext ApplicationManager.getApplication().runWriteAction(() -> ProjectJdkTable.getInstance().addJdk(sdk, getTestRootDisposable())); } - @Override - public void tearDown() throws Exception { - try { - if (myWizard != null) { - Disposer.dispose(myWizard.getDisposable()); - myWizard = null; - } - if (myCreatedProject != null) { - myProjectManager.closeProject(myCreatedProject); - ApplicationManager.getApplication().runWriteAction(() -> Disposer.dispose(myCreatedProject)); - myCreatedProject = null; - } - ApplicationManager.getApplication().runWriteAction(() -> { - ProjectRootManager.getInstance(myProjectManager.getDefaultProject()).setProjectSdk(myOldDefaultProjectSdk); - }); - SelectTemplateSettings.getInstance().setLastTemplate(null, null); - } - finally { - super.tearDown(); - } - } - protected Module importModuleFrom(ProjectImportProvider provider, String path) { return importFrom(path, getProject(), null, provider); } diff --git a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java index b1915480af02..9bf0b44a4a6e 100644 --- a/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/util/indexing/FileBasedIndexImpl.java @@ -75,6 +75,7 @@ import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.stubs.SerializationManagerEx; import com.intellij.util.*; import com.intellij.util.concurrency.AppExecutorUtil; +import com.intellij.util.concurrency.BoundedTaskExecutor; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.IntObjectMap; import com.intellij.util.gist.GistManager; @@ -86,6 +87,7 @@ import com.intellij.util.io.IOUtil; import com.intellij.util.io.storage.HeavyProcessLatch; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.MessageBusConnection; +import com.intellij.util.ui.UIUtil; import gnu.trove.THashMap; import gnu.trove.THashSet; import gnu.trove.TIntArrayList; @@ -2455,4 +2457,24 @@ public class FileBasedIndexImpl extends FileBasedIndex implements BaseComponent, new FileOutputStream(corruptionMarker).close(); } catch (Throwable ignore) {} } + + @TestOnly + public void waitForVfsEventsExecuted(long timeout, @NotNull TimeUnit unit) throws Exception { + ApplicationManager.getApplication().assertIsDispatchThread(); + // wait in the other thread to be able to handle invokeLater() which myChangedFilesCollector.myVfsEventsExecutor issues + Future future = ApplicationManager.getApplication().executeOnPooledThread(() -> + { + try { + ((BoundedTaskExecutor)myChangedFilesCollector.myVfsEventsExecutor) + .waitAllTasksExecuted(timeout, unit); + } + catch (Exception e) { + throw new RuntimeException(e); + } + }); + while (!future.isDone()) { + UIUtil.dispatchAllInvocationEvents(); + } + future.get(); + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/LaterInvocator.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/LaterInvocator.java index 4109371c8023..5bd996bc3f89 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/LaterInvocator.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/LaterInvocator.java @@ -481,4 +481,16 @@ public class LaterInvocator { } } } + + @TestOnly + public static void dispatchPendingFlushes() { + if (!isDispatchThread()) throw new IllegalStateException("Must call from EDT"); + + Semaphore semaphore = new Semaphore(); + semaphore.down(); + invokeLaterWithCallback(semaphore::up, ModalityState.any(), Conditions.FALSE, null); + while (!semaphore.isUp()) { + UIUtil.dispatchAllInvocationEvents(); + } + } } diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java index cac928a6d98d..68cc57deb24b 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java @@ -13,6 +13,7 @@ import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.PathManager; import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.application.impl.LaterInvocator; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.command.impl.DocumentReferenceManagerImpl; @@ -88,6 +89,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.Objects; import java.util.Set; +import java.util.concurrent.TimeUnit; import static com.intellij.testFramework.TemporaryDirectoryKt.generateTemporaryPath; @@ -520,6 +522,8 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro getTempDir().deleteAll(); LocalFileSystem.getInstance().refreshIoFiles(myFilesToDelete); + LaterInvocator.dispatchPendingFlushes(); + ((FileBasedIndexImpl)FileBasedIndex.getInstance()).waitForVfsEventsExecuted(1, TimeUnit.MINUTES); }) .append(() -> { if (!myAssertionsInTestDetected) { diff --git a/platform/testFramework/src/com/intellij/testFramework/ThreadTracker.java b/platform/testFramework/src/com/intellij/testFramework/ThreadTracker.java index 430b88aff3b7..189a0e7cb502 100644 --- a/platform/testFramework/src/com/intellij/testFramework/ThreadTracker.java +++ b/platform/testFramework/src/com/intellij/testFramework/ThreadTracker.java @@ -143,7 +143,9 @@ public class ThreadTracker { if (isWellKnownOffender(thread)) continue; if (!thread.isAlive()) continue; - if (thread.getStackTrace().length == 0) { + if (thread.getStackTrace().length == 0 + // give thread a chance to run up to the completion + || thread.getState() == Thread.State.RUNNABLE) { thread.interrupt(); if (new WaitFor(10000){ @Override diff --git a/platform/util/src/com/intellij/util/concurrency/BoundedTaskExecutor.java b/platform/util/src/com/intellij/util/concurrency/BoundedTaskExecutor.java index 75513282300e..bf7acaed7c4a 100644 --- a/platform/util/src/com/intellij/util/concurrency/BoundedTaskExecutor.java +++ b/platform/util/src/com/intellij/util/concurrency/BoundedTaskExecutor.java @@ -91,7 +91,7 @@ public class BoundedTaskExecutor extends AbstractExecutorService { if (task instanceof Callable && task.getClass().getName().equals("java.util.concurrent.Executors$RunnableAdapter")) { task = ObjectUtils.chooseNotNull(ReflectionUtil.getField(task.getClass(), task, Runnable.class, "task"), task); } - return extra == null ? task : task == null ? extra : task.getClass() + extra; + return extra == null ? task : task.getClass() + extra; } @Override @@ -204,24 +204,35 @@ public class BoundedTaskExecutor extends AbstractExecutorService { myBackendExecutor.execute(new Runnable() { @Override public void run() { - // we are back inside backend executor, no need to call .execute() - just run synchronously - Runnable task = currentTask.get(); - do { - currentTask.set(task); - try { - task.run(); - } - catch (Throwable e) { - // do not lose queued tasks because of this exception + String oldName = Thread.currentThread().getName(); + boolean sameName = myName.equals(oldName); + if (!sameName) { + Thread.currentThread().setName(myName); + } + try { + Runnable task = currentTask.get(); + do { + currentTask.set(task); try { - LOG.error(e); + task.run(); } - catch (Throwable ignored) { + catch (Throwable e) { + // do not lose queued tasks because of this exception + try { + LOG.error(e); + } + catch (Throwable ignored) { + } } + task = pollOrGiveUp(status); + } + while (task != null); + } + finally { + if (!sameName) { + Thread.currentThread().setName(oldName); } - task = pollOrGiveUp(status); } - while (task != null); } @Override