c1f8cb9b004bfa0cd26bd9b7ba2da19a7ed03fec
[idea/community.git] / platform / testFramework / src / com / intellij / testFramework / LightPlatformTestCase.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.testFramework;
17
18 import com.intellij.ProjectTopics;
19 import com.intellij.codeInsight.completion.CompletionProgressIndicator;
20 import com.intellij.codeInsight.daemon.HighlightDisplayKey;
21 import com.intellij.codeInsight.hint.HintManager;
22 import com.intellij.codeInsight.hint.HintManagerImpl;
23 import com.intellij.codeInsight.lookup.LookupManager;
24 import com.intellij.codeInspection.*;
25 import com.intellij.codeInspection.ex.InspectionProfileImpl;
26 import com.intellij.codeInspection.ex.InspectionToolRegistrar;
27 import com.intellij.codeInspection.ex.InspectionToolWrapper;
28 import com.intellij.ide.highlighter.ProjectFileType;
29 import com.intellij.ide.startup.StartupManagerEx;
30 import com.intellij.ide.startup.impl.StartupManagerImpl;
31 import com.intellij.idea.IdeaLogger;
32 import com.intellij.idea.IdeaTestApplication;
33 import com.intellij.mock.MockApplication;
34 import com.intellij.openapi.Disposable;
35 import com.intellij.openapi.actionSystem.DataProvider;
36 import com.intellij.openapi.application.Application;
37 import com.intellij.openapi.application.ApplicationManager;
38 import com.intellij.openapi.application.ex.ApplicationEx;
39 import com.intellij.openapi.application.impl.ApplicationInfoImpl;
40 import com.intellij.openapi.command.WriteCommandAction;
41 import com.intellij.openapi.command.impl.UndoManagerImpl;
42 import com.intellij.openapi.command.undo.UndoManager;
43 import com.intellij.openapi.editor.Document;
44 import com.intellij.openapi.editor.Editor;
45 import com.intellij.openapi.editor.EditorFactory;
46 import com.intellij.openapi.editor.impl.EditorFactoryImpl;
47 import com.intellij.openapi.editor.impl.EditorImpl;
48 import com.intellij.openapi.extensions.Extensions;
49 import com.intellij.openapi.fileEditor.FileDocumentManager;
50 import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl;
51 import com.intellij.openapi.fileTypes.FileType;
52 import com.intellij.openapi.fileTypes.FileTypeManager;
53 import com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl;
54 import com.intellij.openapi.module.EmptyModuleType;
55 import com.intellij.openapi.module.Module;
56 import com.intellij.openapi.module.ModuleType;
57 import com.intellij.openapi.project.DumbService;
58 import com.intellij.openapi.project.ModuleAdapter;
59 import com.intellij.openapi.project.Project;
60 import com.intellij.openapi.project.ProjectManager;
61 import com.intellij.openapi.project.ex.ProjectManagerEx;
62 import com.intellij.openapi.project.impl.ProjectImpl;
63 import com.intellij.openapi.project.impl.ProjectManagerImpl;
64 import com.intellij.openapi.projectRoots.Sdk;
65 import com.intellij.openapi.roots.ModuleRootManager;
66 import com.intellij.openapi.roots.OrderRootType;
67 import com.intellij.openapi.startup.StartupManager;
68 import com.intellij.openapi.util.Computable;
69 import com.intellij.openapi.util.Disposer;
70 import com.intellij.openapi.util.EmptyRunnable;
71 import com.intellij.openapi.util.io.FileUtil;
72 import com.intellij.openapi.vfs.LocalFileSystem;
73 import com.intellij.openapi.vfs.VirtualFile;
74 import com.intellij.openapi.vfs.encoding.EncodingManager;
75 import com.intellij.openapi.vfs.encoding.EncodingManagerImpl;
76 import com.intellij.openapi.vfs.impl.VirtualFilePointerManagerImpl;
77 import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
78 import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl;
79 import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
80 import com.intellij.profile.codeInspection.InspectionProfileManager;
81 import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
82 import com.intellij.psi.PsiDocumentManager;
83 import com.intellij.psi.PsiFile;
84 import com.intellij.psi.PsiFileFactory;
85 import com.intellij.psi.PsiManager;
86 import com.intellij.psi.codeStyle.CodeStyleSchemes;
87 import com.intellij.psi.codeStyle.CodeStyleSettings;
88 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
89 import com.intellij.psi.impl.DocumentCommitThread;
90 import com.intellij.psi.impl.PsiDocumentManagerImpl;
91 import com.intellij.psi.impl.PsiManagerImpl;
92 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageManagerImpl;
93 import com.intellij.psi.templateLanguages.TemplateDataLanguageMappings;
94 import com.intellij.testFramework.fixtures.impl.CodeInsightTestFixtureImpl;
95 import com.intellij.util.*;
96 import com.intellij.util.containers.ContainerUtil;
97 import com.intellij.util.indexing.UnindexedFilesUpdater;
98 import com.intellij.util.lang.CompoundRuntimeException;
99 import com.intellij.util.messages.MessageBusConnection;
100 import com.intellij.util.ui.UIUtil;
101 import gnu.trove.THashMap;
102 import junit.framework.TestCase;
103 import org.jetbrains.annotations.NonNls;
104 import org.jetbrains.annotations.NotNull;
105 import org.jetbrains.annotations.Nullable;
106 import org.jetbrains.annotations.TestOnly;
107
108 import javax.swing.*;
109 import java.io.ByteArrayOutputStream;
110 import java.io.File;
111 import java.io.IOException;
112 import java.io.PrintStream;
113 import java.lang.management.GarbageCollectorMXBean;
114 import java.lang.management.ManagementFactory;
115 import java.util.Arrays;
116 import java.util.Collections;
117 import java.util.List;
118
119 /**
120  * @author yole
121  */
122 public abstract class LightPlatformTestCase extends UsefulTestCase implements DataProvider {
123   @NonNls public static final String PROFILE = "Configurable";
124
125   @NonNls private static final String LIGHT_PROJECT_MARK = "Light project: ";
126
127   private static IdeaTestApplication ourApplication;
128   protected static Project ourProject;
129   private static Module ourModule;
130   private static PsiManager ourPsiManager;
131   private static boolean ourAssertionsInTestDetected;
132   private static VirtualFile ourSourceRoot;
133   private static TestCase ourTestCase;
134   public static Thread ourTestThread;
135   private static LightProjectDescriptor ourProjectDescriptor;
136
137   private ThreadTracker myThreadTracker;
138
139   /**
140    * @return Project to be used in tests for example for project components retrieval.
141    */
142   public static Project getProject() {
143     return ourProject;
144   }
145
146   /**
147    * @return Module to be used in tests for example for module components retrieval.
148    */
149   public static Module getModule() {
150     return ourModule;
151   }
152
153   /**
154    * Shortcut to PsiManager.getInstance(getProject())
155    */
156   @NotNull
157   public static PsiManager getPsiManager() {
158     if (ourPsiManager == null) {
159       ourPsiManager = PsiManager.getInstance(ourProject);
160     }
161     return ourPsiManager;
162   }
163
164   @NotNull
165   public static IdeaTestApplication initApplication() {
166     ourApplication = IdeaTestApplication.getInstance();
167     return ourApplication;
168   }
169
170   @TestOnly
171   public static void disposeApplication() {
172     if (ourApplication != null) {
173       ApplicationManager.getApplication().runWriteAction(() -> Disposer.dispose(ourApplication));
174
175       ourApplication = null;
176     }
177   }
178
179   public static IdeaTestApplication getApplication() {
180     return ourApplication;
181   }
182
183   @SuppressWarnings("UseOfSystemOutOrSystemErr")
184   public static void reportTestExecutionStatistics() {
185     System.out.println("----- TEST STATISTICS -----");
186     UsefulTestCase.logSetupTeardownCosts();
187     System.out.println(String.format("##teamcity[buildStatisticValue key='ideaTests.appInstancesCreated' value='%d']",
188                                      MockApplication.INSTANCES_CREATED));
189     System.out.println(String.format("##teamcity[buildStatisticValue key='ideaTests.projectInstancesCreated' value='%d']",
190                                      ProjectManagerImpl.TEST_PROJECTS_CREATED));
191     long totalGcTime = 0;
192     for (GarbageCollectorMXBean mxBean : ManagementFactory.getGarbageCollectorMXBeans()) {
193       totalGcTime += mxBean.getCollectionTime();
194     }
195     System.out.println(String.format("##teamcity[buildStatisticValue key='ideaTests.gcTimeMs' value='%d']", totalGcTime));
196     System.out.println(String.format("##teamcity[buildStatisticValue key='ideaTests.classesLoaded' value='%d']",
197                                      ManagementFactory.getClassLoadingMXBean().getTotalLoadedClassCount()));
198   }
199
200   protected void resetAllFields() {
201     resetClassFields(getClass());
202   }
203
204   private void resetClassFields(@NotNull Class<?> aClass) {
205     try {
206       UsefulTestCase.clearDeclaredFields(this, aClass);
207     }
208     catch (IllegalAccessException e) {
209       throw new RuntimeException(e);
210     }
211
212     if (aClass == LightPlatformTestCase.class) return;
213     resetClassFields(aClass.getSuperclass());
214   }
215
216   private static void cleanPersistedVFSContent() {
217     ((PersistentFSImpl)PersistentFS.getInstance()).cleanPersistedContents();
218   }
219
220   private static void initProject(@NotNull final LightProjectDescriptor descriptor) throws Exception {
221     ourProjectDescriptor = descriptor;
222
223     ApplicationManager.getApplication().runWriteAction(() -> {
224       if (ourProject != null) {
225         closeAndDeleteProject();
226       }
227       else {
228         cleanPersistedVFSContent();
229       }
230     });
231
232     final File projectFile = FileUtil.createTempFile(ProjectImpl.LIGHT_PROJECT_NAME, ProjectFileType.DOT_DEFAULT_EXTENSION);
233     LocalFileSystem.getInstance().refreshAndFindFileByIoFile(projectFile);
234
235     ByteArrayOutputStream buffer = new ByteArrayOutputStream();
236     new Throwable(projectFile.getPath()).printStackTrace(new PrintStream(buffer));
237
238     ourProject = PlatformTestCase.createProject(projectFile, LIGHT_PROJECT_MARK + buffer);
239     ourPathToKeep = projectFile.getPath();
240     ourPsiManager = null;
241
242     ourProjectDescriptor.setUpProject(ourProject, new LightProjectDescriptor.SetupHandler() {
243       @Override
244       public void moduleCreated(@NotNull Module module) {
245         //noinspection AssignmentToStaticFieldFromInstanceMethod
246         ourModule = module;
247       }
248
249       @Override
250       public void sourceRootCreated(@NotNull VirtualFile sourceRoot) {
251         //noinspection AssignmentToStaticFieldFromInstanceMethod
252         ourSourceRoot = sourceRoot;
253       }
254     });
255
256     // project creation may make a lot of pointers, do not regard them as leak
257     ((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).storePointers();
258   }
259
260   /**
261    * @return The only source root
262    */
263   public static VirtualFile getSourceRoot() {
264     return ourSourceRoot;
265   }
266
267   @Override
268   protected void setUp() throws Exception {
269     EdtTestUtil.runInEdtAndWait((ThrowableRunnable<Throwable>)() -> {
270       super.setUp();
271       initApplication();
272       ApplicationInfoImpl.setInPerformanceTest(isPerformanceTest());
273
274       ourApplication.setDataProvider(this);
275       LightProjectDescriptor descriptor = new SimpleLightProjectDescriptor(getModuleType(), getProjectJDK());
276       doSetup(descriptor, configureLocalInspectionTools(), getTestRootDisposable());
277       InjectedLanguageManagerImpl.pushInjectors(getProject());
278
279       storeSettings();
280
281       myThreadTracker = new ThreadTracker();
282       ModuleRootManager.getInstance(ourModule).orderEntries().getAllLibrariesAndSdkClassesRoots();
283       VirtualFilePointerManagerImpl filePointerManager = (VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance();
284       filePointerManager.storePointers();
285     });
286   }
287
288   public static void doSetup(@NotNull LightProjectDescriptor descriptor,
289                              @NotNull LocalInspectionTool[] localInspectionTools,
290                              @NotNull Disposable parentDisposable) throws Exception {
291     assertNull("Previous test " + ourTestCase + " hasn't called tearDown(). Probably overridden without super call.", ourTestCase);
292     IdeaLogger.ourErrorsOccurred = null;
293     ApplicationManager.getApplication().assertIsDispatchThread();
294     boolean reusedProject = true;
295     if (ourProject == null || ourProjectDescriptor == null || !ourProjectDescriptor.equals(descriptor)) {
296       initProject(descriptor);
297       reusedProject = false;
298     }
299
300     ProjectManagerEx projectManagerEx = ProjectManagerEx.getInstanceEx();
301     projectManagerEx.openTestProject(ourProject);
302     if (reusedProject) {
303       DumbService.getInstance(ourProject).queueTask(new UnindexedFilesUpdater(ourProject, false));
304     }
305
306     MessageBusConnection connection = ourProject.getMessageBus().connect(parentDisposable);
307     connection.subscribe(ProjectTopics.MODULES, new ModuleAdapter() {
308       @Override
309       public void moduleAdded(@NotNull Project project, @NotNull Module module) {
310         fail("Adding modules is not permitted in LightIdeaTestCase.");
311       }
312     });
313
314     clearUncommittedDocuments(getProject());
315
316     CodeInsightTestFixtureImpl.configureInspections(localInspectionTools, getProject(),
317                                                     Collections.emptyList(), parentDisposable);
318
319     assertFalse(getPsiManager().isDisposed());
320     Boolean passed = null;
321     try {
322       passed = StartupManagerEx.getInstanceEx(getProject()).startupActivityPassed();
323     }
324     catch (Exception ignored) {
325
326     }
327     assertTrue("open: " + getProject().isOpen() +
328                "; disposed:" + getProject().isDisposed() +
329                "; startup passed:" + passed +
330                "; all open projects: " + Arrays.asList(ProjectManager.getInstance().getOpenProjects()), getProject().isInitialized());
331
332     CodeStyleSettingsManager.getInstance(getProject()).setTemporarySettings(new CodeStyleSettings());
333
334     final FileDocumentManager manager = FileDocumentManager.getInstance();
335     if (manager instanceof FileDocumentManagerImpl) {
336       Document[] unsavedDocuments = manager.getUnsavedDocuments();
337       manager.saveAllDocuments();
338       ApplicationManager.getApplication().runWriteAction(((FileDocumentManagerImpl)manager)::dropAllUnsavedDocuments);
339
340       assertEmpty("There are unsaved documents", Arrays.asList(unsavedDocuments));
341     }
342     UIUtil.dispatchAllInvocationEvents(); // startup activities
343
344     ((FileTypeManagerImpl)FileTypeManager.getInstance()).drainReDetectQueue();
345   }
346
347   // todo: use Class<? extends InspectionProfileEntry> once on Java 7
348   protected void enableInspectionTools(@NotNull Class<?>... classes) {
349     final InspectionProfileEntry[] tools = new InspectionProfileEntry[classes.length];
350
351     final List<InspectionEP> eps = ContainerUtil.newArrayList();
352     ContainerUtil.addAll(eps, Extensions.getExtensions(LocalInspectionEP.LOCAL_INSPECTION));
353     ContainerUtil.addAll(eps, Extensions.getExtensions(InspectionEP.GLOBAL_INSPECTION));
354
355     next:
356     for (int i = 0; i < classes.length; i++) {
357       for (InspectionEP ep : eps) {
358         if (classes[i].getName().equals(ep.implementationClass)) {
359           tools[i] = ep.instantiateTool();
360           continue next;
361         }
362       }
363       throw new IllegalArgumentException("Unable to find extension point for " + classes[i].getName());
364     }
365
366     enableInspectionTools(tools);
367   }
368
369   protected void enableInspectionTools(@NotNull InspectionProfileEntry... tools) {
370     for (InspectionProfileEntry tool : tools) {
371       enableInspectionTool(tool);
372     }
373   }
374
375   protected void enableInspectionTool(@NotNull InspectionToolWrapper toolWrapper) {
376     enableInspectionTool(getProject(), toolWrapper);
377   }
378   protected void enableInspectionTool(@NotNull InspectionProfileEntry tool) {
379     InspectionToolWrapper toolWrapper = InspectionToolRegistrar.wrapTool(tool);
380     enableInspectionTool(getProject(), toolWrapper);
381   }
382
383   public static void enableInspectionTool(@NotNull final Project project, @NotNull final InspectionToolWrapper toolWrapper) {
384     final InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getCurrentProfile();
385     final String shortName = toolWrapper.getShortName();
386     final HighlightDisplayKey key = HighlightDisplayKey.find(shortName);
387     if (key == null) {
388       HighlightDisplayKey.register(shortName, toolWrapper.getDisplayName(), toolWrapper.getID());
389     }
390     InspectionProfileImpl.initAndDo((Computable)() -> {
391       InspectionProfileImpl impl = (InspectionProfileImpl)profile;
392       InspectionToolWrapper existingWrapper = impl.getInspectionTool(shortName, project);
393       if (existingWrapper == null || existingWrapper.isInitialized() != toolWrapper.isInitialized() || toolWrapper.isInitialized() && toolWrapper.getTool() != existingWrapper.getTool()) {
394         impl.addTool(project, toolWrapper, new THashMap<>());
395       }
396       impl.enableTool(shortName, project);
397       return null;
398     });
399   }
400
401   @NotNull
402   protected LocalInspectionTool[] configureLocalInspectionTools() {
403     return LocalInspectionTool.EMPTY_ARRAY;
404   }
405
406   @Override
407   protected void tearDown() throws Exception {
408     Project project = getProject();
409     CodeStyleSettingsManager.getInstance(project).dropTemporarySettings();
410     List<Throwable> errors = ContainerUtil.newSmartList();
411     Function<ThrowableRunnable<?>, ?> runSafe = c -> {
412       try {
413         c.run();
414       }
415       catch (Throwable e) {
416         errors.add(e);
417       }
418       return true;
419     };
420     try {
421       runSafe.fun(() -> checkForSettingsDamage(errors));
422       runSafe.fun(() -> doTearDown(project, ourApplication, true, errors));
423       runSafe.fun(super::tearDown);
424       runSafe.fun(() -> myThreadTracker.checkLeak());
425       runSafe.fun(() -> InjectedLanguageManagerImpl.checkInjectorsAreDisposed(project));
426       runSafe.fun(() -> ((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).assertPointersAreDisposed());
427     }
428     catch (Throwable e) {
429       errors.add(e);
430     }
431     finally {
432       CompoundRuntimeException.throwIfNotEmpty(errors);
433     }
434   }
435
436   public static void doTearDown(@NotNull final Project project, @NotNull IdeaTestApplication application, boolean checkForEditors, @NotNull List<Throwable> exceptions) throws Exception {
437     PsiDocumentManagerImpl documentManager;
438     try {
439       ((FileTypeManagerImpl)FileTypeManager.getInstance()).drainReDetectQueue();
440       DocumentCommitThread.getInstance().clearQueue();
441       CodeStyleSettingsManager.getInstance(project).dropTemporarySettings();
442
443       checkJavaSwingTimersAreDisposed(exceptions);
444
445       UsefulTestCase.doPostponedFormatting(project);
446
447       LookupManager lookupManager = LookupManager.getInstance(project);
448       if (lookupManager != null) {
449         lookupManager.hideActiveLookup();
450       }
451       ((StartupManagerImpl)StartupManager.getInstance(project)).prepareForNextTest();
452       InspectionProfileManager.getInstance().deleteProfile(PROFILE);
453       if (ProjectManager.getInstance() == null) {
454         exceptions.add(new AssertionError("Application components damaged"));
455       }
456
457       ContainerUtil.addIfNotNull(exceptions, new WriteCommandAction.Simple(project) {
458         @Override
459         protected void run() throws Throwable {
460           if (ourSourceRoot != null) {
461             try {
462               final VirtualFile[] children = ourSourceRoot.getChildren();
463               for (VirtualFile child : children) {
464                 child.delete(this);
465               }
466             }
467             catch (IOException e) {
468               //noinspection CallToPrintStackTrace
469               e.printStackTrace();
470             }
471           }
472           EncodingManager encodingManager = EncodingManager.getInstance();
473           if (encodingManager instanceof EncodingManagerImpl) ((EncodingManagerImpl)encodingManager).clearDocumentQueue();
474
475           FileDocumentManager manager = FileDocumentManager.getInstance();
476
477           ApplicationManager.getApplication().runWriteAction(EmptyRunnable.getInstance()); // Flush postponed formatting if any.
478           manager.saveAllDocuments();
479           if (manager instanceof FileDocumentManagerImpl) {
480             ((FileDocumentManagerImpl)manager).dropAllUnsavedDocuments();
481           }
482         }
483       }.execute().getThrowable());
484
485       assertFalse(PsiManager.getInstance(project).isDisposed());
486       if (!ourAssertionsInTestDetected) {
487         if (IdeaLogger.ourErrorsOccurred != null) {
488           throw IdeaLogger.ourErrorsOccurred;
489         }
490       }
491       documentManager = clearUncommittedDocuments(project);
492       ((HintManagerImpl)HintManager.getInstance()).cleanup();
493       DocumentCommitThread.getInstance().clearQueue();
494
495       EdtTestUtil.runInEdtAndWait((Runnable)() -> {
496         ((UndoManagerImpl)UndoManager.getGlobalInstance()).dropHistoryInTests();
497         ((UndoManagerImpl)UndoManager.getInstance(project)).dropHistoryInTests();
498
499         UIUtil.dispatchAllInvocationEvents();
500       });
501
502       TemplateDataLanguageMappings.getInstance(project).cleanupForNextTest();
503     }
504     finally {
505       ProjectManagerEx.getInstanceEx().closeTestProject(project);
506       application.setDataProvider(null);
507       ourTestCase = null;
508       ((PsiManagerImpl)PsiManager.getInstance(project)).cleanupForNextTest();
509       CompletionProgressIndicator.cleanupForNextTest();
510     }
511
512     if (checkForEditors) {
513       checkEditorsReleased(exceptions);
514     }
515     documentManager.clearUncommittedDocuments();
516     
517     if (ourTestCount++ % 100 == 0) {
518       // some tests are written in Groovy, and running all of them may result in some 40M of memory wasted on bean infos
519       // so let's clear the cache every now and then to ensure it doesn't grow too large
520       GCUtil.clearBeanInfoCache();
521     }
522   }
523   
524   private static int ourTestCount;
525
526   public static PsiDocumentManagerImpl clearUncommittedDocuments(@NotNull Project project) {
527     PsiDocumentManagerImpl documentManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(project);
528     documentManager.clearUncommittedDocuments();
529
530     ProjectManagerImpl projectManager = (ProjectManagerImpl)ProjectManager.getInstance();
531     if (projectManager.isDefaultProjectInitialized()) {
532       Project defaultProject = projectManager.getDefaultProject();
533       ((PsiDocumentManagerImpl)PsiDocumentManager.getInstance(defaultProject)).clearUncommittedDocuments();
534     }
535     return documentManager;
536   }
537
538   public static void checkEditorsReleased(@NotNull List<Throwable> exceptions) {
539     Editor[] allEditors = EditorFactory.getInstance().getAllEditors();
540     if (allEditors.length == 0) {
541       return;
542     }
543
544     for (Editor editor : allEditors) {
545       try {
546         EditorFactoryImpl.throwNotReleasedError(editor);
547       }
548       catch (Throwable e) {
549         exceptions.add(e);
550       }
551       finally {
552         EditorFactory.getInstance().releaseEditor(editor);
553       }
554     }
555
556     try {
557       ((EditorImpl)allEditors[0]).throwDisposalError("Unreleased editors: " + allEditors.length);
558     }
559     catch (Throwable e) {
560       exceptions.add(e);
561     }
562   }
563
564   @Override
565   public final void runBare() throws Throwable {
566     if (!shouldRunTest()) {
567       return;
568     }
569
570     TestRunnerUtil.replaceIdeEventQueueSafely();
571     EdtTestUtil.runInEdtAndWait((ThrowableRunnable<Throwable>)() -> {
572       try {
573         ourTestThread = Thread.currentThread();
574         startRunAndTear();
575       }
576       finally {
577         ourTestThread = null;
578         try {
579           Application application = ApplicationManager.getApplication();
580           if (application instanceof ApplicationEx) {
581             PlatformTestCase.cleanupApplicationCaches(ourProject);
582           }
583           resetAllFields();
584         }
585         catch (Throwable e) {
586           e.printStackTrace();
587         }
588       }
589     });
590
591     // just to make sure all deferred Runnables to finish
592     SwingUtilities.invokeAndWait(EmptyRunnable.getInstance());
593
594     if (IdeaLogger.ourErrorsOccurred != null) {
595       throw IdeaLogger.ourErrorsOccurred;
596     }
597   }
598
599   private void startRunAndTear() throws Throwable {
600     setUp();
601     try {
602       ourAssertionsInTestDetected = true;
603       runTest();
604       ourAssertionsInTestDetected = false;
605     }
606     finally {
607       //try{
608       tearDown();
609       //}
610       //catch(Throwable th){
611       //  noinspection CallToPrintStackTrace
612       //th.printStackTrace();
613       //}
614     }
615   }
616
617   @Override
618   public Object getData(String dataId) {
619     return ourProject == null || ourProject.isDisposed() ? null : new TestDataProvider(ourProject).getData(dataId);
620   }
621
622   protected Sdk getProjectJDK() {
623     return null;
624   }
625
626   @NotNull
627   protected ModuleType getModuleType() {
628     return EmptyModuleType.getInstance();
629   }
630
631   /**
632    * Creates dummy source file. One is not placed under source root so some PSI functions like resolve to external classes
633    * may not work. Though it works significantly faster and yet can be used if you need to create some PSI structures for
634    * test purposes
635    *
636    * @param fileName - name of the file to create. Extension is used to choose what PSI should be created like java, jsp, aj, xml etc.
637    * @param text     - file text.
638    * @return dummy psi file.
639    *
640    */
641   @NotNull
642   protected static PsiFile createFile(@NonNls @NotNull String fileName, @NonNls @NotNull String text) throws IncorrectOperationException {
643     FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(fileName);
644     return PsiFileFactory.getInstance(getProject())
645       .createFileFromText(fileName, fileType, text, LocalTimeCounter.currentTime(), true, false);
646   }
647
648   @NotNull
649   protected static PsiFile createLightFile(@NonNls @NotNull String fileName, @NotNull String text) throws IncorrectOperationException {
650     FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(fileName);
651     return PsiFileFactory.getInstance(getProject())
652       .createFileFromText(fileName, fileType, text, LocalTimeCounter.currentTime(), false, false);
653   }
654
655   /**
656    * Convenient conversion of testSomeTest -> someTest | SomeTest where testSomeTest is the name of current test.
657    *
658    * @param lowercaseFirstLetter - whether first letter after test should be lowercased.
659    */
660   @NotNull
661   @Override
662   protected String getTestName(boolean lowercaseFirstLetter) {
663     String name = getName();
664     assertTrue("Test name should start with 'test': " + name, name.startsWith("test"));
665     name = name.substring("test".length());
666     if (!name.isEmpty() && lowercaseFirstLetter && !PlatformTestUtil.isAllUppercaseName(name)) {
667       name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
668     }
669     return name;
670   }
671
672   protected static void commitDocument(@NotNull Document document) {
673     PsiDocumentManager.getInstance(getProject()).commitDocument(document);
674   }
675
676   protected static void commitAllDocuments() {
677     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
678   }
679
680   @NotNull
681   @Override
682   protected CodeStyleSettings getCurrentCodeStyleSettings() {
683     if (CodeStyleSchemes.getInstance().getCurrentScheme() == null) return new CodeStyleSettings();
684     return CodeStyleSettingsManager.getSettings(getProject());
685   }
686
687   protected static Document getDocument(@NotNull PsiFile file) {
688     return PsiDocumentManager.getInstance(getProject()).getDocument(file);
689   }
690
691   @SuppressWarnings("NonPrivateFieldAccessedInSynchronizedContext")
692   public static synchronized void closeAndDeleteProject() {
693     if (ourProject != null) {
694       ApplicationManager.getApplication().assertWriteAccessAllowed();
695
696       if (!ourProject.isDisposed()) {
697         File ioFile = new File(ourProject.getProjectFilePath());
698         Disposer.dispose(ourProject);
699         if (ioFile.exists()) {
700           File dir = ioFile.getParentFile();
701           if (dir.getName().startsWith(UsefulTestCase.TEMP_DIR_MARKER)) {
702             FileUtil.delete(dir);
703           }
704           else {
705             FileUtil.delete(ioFile);
706           }
707         }
708       }
709
710       ProjectManagerEx.getInstanceEx().closeAndDispose(ourProject);
711
712       // project may be disposed but empty folder may still be there
713       if (ourPathToKeep != null) {
714         File parent = new File(ourPathToKeep).getParentFile();
715         if (parent.getName().startsWith(UsefulTestCase.TEMP_DIR_MARKER)) {
716           parent.delete(); // delete only empty folders
717         }
718       }
719
720       ourProject = null;
721       ourPathToKeep = null;
722     }
723   }
724
725   private static class SimpleLightProjectDescriptor extends LightProjectDescriptor {
726     @NotNull private final ModuleType myModuleType;
727     @Nullable private final Sdk mySdk;
728
729     SimpleLightProjectDescriptor(@NotNull ModuleType moduleType, @Nullable Sdk sdk) {
730       myModuleType = moduleType;
731       mySdk = sdk;
732     }
733
734     @NotNull
735     @Override
736     public ModuleType getModuleType() {
737       return myModuleType;
738     }
739
740     @Nullable 
741     @Override
742     public Sdk getSdk() {
743       return mySdk;
744     }
745
746     @Override
747     public boolean equals(Object o) {
748       if (this == o) return true;
749       if (o == null || getClass() != o.getClass()) return false;
750
751       SimpleLightProjectDescriptor that = (SimpleLightProjectDescriptor)o;
752
753       if (!myModuleType.equals(that.myModuleType)) return false;
754       return areJdksEqual(that.getSdk());
755     }
756
757     @Override
758     public int hashCode() {
759       return myModuleType.hashCode();
760     }
761
762     private boolean areJdksEqual(final Sdk newSdk) {
763       if (mySdk == null || newSdk == null) return mySdk == newSdk;
764
765       final String[] myUrls = mySdk.getRootProvider().getUrls(OrderRootType.CLASSES);
766       final String[] newUrls = newSdk.getRootProvider().getUrls(OrderRootType.CLASSES);
767       return ContainerUtil.newHashSet(myUrls).equals(ContainerUtil.newHashSet(newUrls));
768     }
769   }
770 }