42373bd2d8000c24a8e0f58a72b617ee0d0ea3cb
[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       LightPlatformTestCase.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 = new SmartList<>();
411     try {
412       checkForSettingsDamage(errors);
413       doTearDown(project, ourApplication, true, errors);
414     }
415     catch (Throwable e) {
416       errors.add(e);
417     }
418
419     try {
420       //noinspection SuperTearDownInFinally
421       super.tearDown();
422     }
423     catch (Throwable e) {
424       errors.add(e);
425     }
426
427     try {
428       myThreadTracker.checkLeak();
429       InjectedLanguageManagerImpl.checkInjectorsAreDisposed(project);
430       ((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).assertPointersAreDisposed();
431     }
432     catch (Throwable e) {
433       errors.add(e);
434     }
435     finally {
436       CompoundRuntimeException.throwIfNotEmpty(errors);
437     }
438   }
439
440   public static void doTearDown(@NotNull final Project project, @NotNull IdeaTestApplication application, boolean checkForEditors, @NotNull List<Throwable> exceptions) throws Exception {
441     PsiDocumentManagerImpl documentManager;
442     try {
443       ((FileTypeManagerImpl)FileTypeManager.getInstance()).drainReDetectQueue();
444       DocumentCommitThread.getInstance().clearQueue();
445       CodeStyleSettingsManager.getInstance(project).dropTemporarySettings();
446
447       checkJavaSwingTimersAreDisposed(exceptions);
448
449       UsefulTestCase.doPostponedFormatting(project);
450
451       LookupManager lookupManager = LookupManager.getInstance(project);
452       if (lookupManager != null) {
453         lookupManager.hideActiveLookup();
454       }
455       ((StartupManagerImpl)StartupManager.getInstance(project)).prepareForNextTest();
456       InspectionProfileManager.getInstance().deleteProfile(PROFILE);
457       if (ProjectManager.getInstance() == null) {
458         exceptions.add(new AssertionError("Application components damaged"));
459       }
460
461       ContainerUtil.addIfNotNull(exceptions, new WriteCommandAction.Simple(project) {
462         @Override
463         protected void run() throws Throwable {
464           if (ourSourceRoot != null) {
465             try {
466               final VirtualFile[] children = ourSourceRoot.getChildren();
467               for (VirtualFile child : children) {
468                 child.delete(this);
469               }
470             }
471             catch (IOException e) {
472               //noinspection CallToPrintStackTrace
473               e.printStackTrace();
474             }
475           }
476           EncodingManager encodingManager = EncodingManager.getInstance();
477           if (encodingManager instanceof EncodingManagerImpl) ((EncodingManagerImpl)encodingManager).clearDocumentQueue();
478
479           FileDocumentManager manager = FileDocumentManager.getInstance();
480
481           ApplicationManager.getApplication().runWriteAction(EmptyRunnable.getInstance()); // Flush postponed formatting if any.
482           manager.saveAllDocuments();
483           if (manager instanceof FileDocumentManagerImpl) {
484             ((FileDocumentManagerImpl)manager).dropAllUnsavedDocuments();
485           }
486         }
487       }.execute().getThrowable());
488
489       assertFalse(PsiManager.getInstance(project).isDisposed());
490       if (!ourAssertionsInTestDetected) {
491         if (IdeaLogger.ourErrorsOccurred != null) {
492           throw IdeaLogger.ourErrorsOccurred;
493         }
494       }
495       documentManager = clearUncommittedDocuments(project);
496       ((HintManagerImpl)HintManager.getInstance()).cleanup();
497       DocumentCommitThread.getInstance().clearQueue();
498
499       EdtTestUtil.runInEdtAndWait((Runnable)() -> {
500         ((UndoManagerImpl)UndoManager.getGlobalInstance()).dropHistoryInTests();
501         ((UndoManagerImpl)UndoManager.getInstance(project)).dropHistoryInTests();
502
503         UIUtil.dispatchAllInvocationEvents();
504       });
505
506       TemplateDataLanguageMappings.getInstance(project).cleanupForNextTest();
507     }
508     finally {
509       ProjectManagerEx.getInstanceEx().closeTestProject(project);
510       application.setDataProvider(null);
511       ourTestCase = null;
512       ((PsiManagerImpl)PsiManager.getInstance(project)).cleanupForNextTest();
513       CompletionProgressIndicator.cleanupForNextTest();
514     }
515
516     if (checkForEditors) {
517       checkEditorsReleased(exceptions);
518     }
519     documentManager.clearUncommittedDocuments();
520     
521     if (ourTestCount++ % 100 == 0) {
522       // some tests are written in Groovy, and running all of them may result in some 40M of memory wasted on bean infos
523       // so let's clear the cache every now and then to ensure it doesn't grow too large
524       GCUtil.clearBeanInfoCache();
525     }
526   }
527   
528   private static int ourTestCount;
529
530   public static PsiDocumentManagerImpl clearUncommittedDocuments(@NotNull Project project) {
531     PsiDocumentManagerImpl documentManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(project);
532     documentManager.clearUncommittedDocuments();
533
534     ProjectManagerImpl projectManager = (ProjectManagerImpl)ProjectManager.getInstance();
535     if (projectManager.isDefaultProjectInitialized()) {
536       Project defaultProject = projectManager.getDefaultProject();
537       ((PsiDocumentManagerImpl)PsiDocumentManager.getInstance(defaultProject)).clearUncommittedDocuments();
538     }
539     return documentManager;
540   }
541
542   public static void checkEditorsReleased(@NotNull List<Throwable> exceptions) {
543     Editor[] allEditors = EditorFactory.getInstance().getAllEditors();
544     if (allEditors.length == 0) {
545       return;
546     }
547
548     for (Editor editor : allEditors) {
549       try {
550         EditorFactoryImpl.throwNotReleasedError(editor);
551       }
552       catch (Throwable e) {
553         exceptions.add(e);
554       }
555       finally {
556         EditorFactory.getInstance().releaseEditor(editor);
557       }
558     }
559
560     try {
561       ((EditorImpl)allEditors[0]).throwDisposalError("Unreleased editors: " + allEditors.length);
562     }
563     catch (Throwable e) {
564       exceptions.add(e);
565     }
566   }
567
568   @Override
569   public final void runBare() throws Throwable {
570     if (!shouldRunTest()) {
571       return;
572     }
573
574     TestRunnerUtil.replaceIdeEventQueueSafely();
575     EdtTestUtil.runInEdtAndWait((ThrowableRunnable<Throwable>)() -> {
576       try {
577         ourTestThread = Thread.currentThread();
578         startRunAndTear();
579       }
580       finally {
581         ourTestThread = null;
582         try {
583           Application application = ApplicationManager.getApplication();
584           if (application instanceof ApplicationEx) {
585             PlatformTestCase.cleanupApplicationCaches(ourProject);
586           }
587           resetAllFields();
588         }
589         catch (Throwable e) {
590           e.printStackTrace();
591         }
592       }
593     });
594
595     // just to make sure all deferred Runnables to finish
596     SwingUtilities.invokeAndWait(EmptyRunnable.getInstance());
597
598     if (IdeaLogger.ourErrorsOccurred != null) {
599       throw IdeaLogger.ourErrorsOccurred;
600     }
601   }
602
603   private void startRunAndTear() throws Throwable {
604     setUp();
605     try {
606       ourAssertionsInTestDetected = true;
607       runTest();
608       ourAssertionsInTestDetected = false;
609     }
610     finally {
611       //try{
612       tearDown();
613       //}
614       //catch(Throwable th){
615       //  noinspection CallToPrintStackTrace
616       //th.printStackTrace();
617       //}
618     }
619   }
620
621   @Override
622   public Object getData(String dataId) {
623     return ourProject == null || ourProject.isDisposed() ? null : new TestDataProvider(ourProject).getData(dataId);
624   }
625
626   protected Sdk getProjectJDK() {
627     return null;
628   }
629
630   @NotNull
631   protected ModuleType getModuleType() {
632     return EmptyModuleType.getInstance();
633   }
634
635   /**
636    * Creates dummy source file. One is not placed under source root so some PSI functions like resolve to external classes
637    * may not work. Though it works significantly faster and yet can be used if you need to create some PSI structures for
638    * test purposes
639    *
640    * @param fileName - name of the file to create. Extension is used to choose what PSI should be created like java, jsp, aj, xml etc.
641    * @param text     - file text.
642    * @return dummy psi file.
643    *
644    */
645   @NotNull
646   protected static PsiFile createFile(@NonNls @NotNull String fileName, @NonNls @NotNull String text) throws IncorrectOperationException {
647     FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(fileName);
648     return PsiFileFactory.getInstance(getProject())
649       .createFileFromText(fileName, fileType, text, LocalTimeCounter.currentTime(), true, false);
650   }
651
652   @NotNull
653   protected static PsiFile createLightFile(@NonNls @NotNull String fileName, @NotNull String text) throws IncorrectOperationException {
654     FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(fileName);
655     return PsiFileFactory.getInstance(getProject())
656       .createFileFromText(fileName, fileType, text, LocalTimeCounter.currentTime(), false, false);
657   }
658
659   /**
660    * Convenient conversion of testSomeTest -> someTest | SomeTest where testSomeTest is the name of current test.
661    *
662    * @param lowercaseFirstLetter - whether first letter after test should be lowercased.
663    */
664   @NotNull
665   @Override
666   protected String getTestName(boolean lowercaseFirstLetter) {
667     String name = getName();
668     assertTrue("Test name should start with 'test': " + name, name.startsWith("test"));
669     name = name.substring("test".length());
670     if (!name.isEmpty() && lowercaseFirstLetter && !PlatformTestUtil.isAllUppercaseName(name)) {
671       name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
672     }
673     return name;
674   }
675
676   protected static void commitDocument(@NotNull Document document) {
677     PsiDocumentManager.getInstance(getProject()).commitDocument(document);
678   }
679
680   protected static void commitAllDocuments() {
681     PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
682   }
683
684   @NotNull
685   @Override
686   protected CodeStyleSettings getCurrentCodeStyleSettings() {
687     if (CodeStyleSchemes.getInstance().getCurrentScheme() == null) return new CodeStyleSettings();
688     return CodeStyleSettingsManager.getSettings(getProject());
689   }
690
691   protected static Document getDocument(@NotNull PsiFile file) {
692     return PsiDocumentManager.getInstance(getProject()).getDocument(file);
693   }
694
695   @SuppressWarnings("NonPrivateFieldAccessedInSynchronizedContext")
696   public static synchronized void closeAndDeleteProject() {
697     if (ourProject != null) {
698       ApplicationManager.getApplication().assertWriteAccessAllowed();
699
700       if (!ourProject.isDisposed()) {
701         File ioFile = new File(ourProject.getProjectFilePath());
702         Disposer.dispose(ourProject);
703         if (ioFile.exists()) {
704           File dir = ioFile.getParentFile();
705           if (dir.getName().startsWith(UsefulTestCase.TEMP_DIR_MARKER)) {
706             FileUtil.delete(dir);
707           }
708           else {
709             FileUtil.delete(ioFile);
710           }
711         }
712       }
713
714       ProjectManagerEx.getInstanceEx().closeAndDispose(ourProject);
715
716       // project may be disposed but empty folder may still be there
717       if (ourPathToKeep != null) {
718         File parent = new File(ourPathToKeep).getParentFile();
719         if (parent.getName().startsWith(UsefulTestCase.TEMP_DIR_MARKER)) {
720           parent.delete(); // delete only empty folders
721         }
722       }
723
724       ourProject = null;
725       ourPathToKeep = null;
726     }
727   }
728
729   private static class SimpleLightProjectDescriptor extends LightProjectDescriptor {
730     @NotNull private final ModuleType myModuleType;
731     @Nullable private final Sdk mySdk;
732
733     SimpleLightProjectDescriptor(@NotNull ModuleType moduleType, @Nullable Sdk sdk) {
734       myModuleType = moduleType;
735       mySdk = sdk;
736     }
737
738     @NotNull
739     @Override
740     public ModuleType getModuleType() {
741       return myModuleType;
742     }
743
744     @Nullable 
745     @Override
746     public Sdk getSdk() {
747       return mySdk;
748     }
749
750     @Override
751     public boolean equals(Object o) {
752       if (this == o) return true;
753       if (o == null || getClass() != o.getClass()) return false;
754
755       SimpleLightProjectDescriptor that = (SimpleLightProjectDescriptor)o;
756
757       if (!myModuleType.equals(that.myModuleType)) return false;
758       return areJdksEqual(that.getSdk());
759     }
760
761     @Override
762     public int hashCode() {
763       return myModuleType.hashCode();
764     }
765
766     private boolean areJdksEqual(final Sdk newSdk) {
767       if (mySdk == null || newSdk == null) return mySdk == newSdk;
768
769       final String[] myUrls = mySdk.getRootProvider().getUrls(OrderRootType.CLASSES);
770       final String[] newUrls = newSdk.getRootProvider().getUrls(OrderRootType.CLASSES);
771       return ContainerUtil.newHashSet(myUrls).equals(ContainerUtil.newHashSet(newUrls));
772     }
773   }
774 }