2 * Copyright 2000-2016 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.testFramework;
18 import com.intellij.ProjectTopics;
19 import com.intellij.codeInsight.completion.CompletionProgressIndicator;
20 import com.intellij.codeInsight.hint.HintManager;
21 import com.intellij.codeInsight.hint.HintManagerImpl;
22 import com.intellij.codeInsight.lookup.LookupManager;
23 import com.intellij.codeInspection.InspectionProfileEntry;
24 import com.intellij.codeInspection.LocalInspectionTool;
25 import com.intellij.ide.highlighter.ProjectFileType;
26 import com.intellij.ide.startup.StartupManagerEx;
27 import com.intellij.ide.startup.impl.StartupManagerImpl;
28 import com.intellij.idea.IdeaLogger;
29 import com.intellij.idea.IdeaTestApplication;
30 import com.intellij.mock.MockApplication;
31 import com.intellij.openapi.Disposable;
32 import com.intellij.openapi.actionSystem.DataProvider;
33 import com.intellij.openapi.application.Application;
34 import com.intellij.openapi.application.ApplicationManager;
35 import com.intellij.openapi.application.ex.ApplicationEx;
36 import com.intellij.openapi.application.impl.ApplicationInfoImpl;
37 import com.intellij.openapi.command.WriteCommandAction;
38 import com.intellij.openapi.command.impl.UndoManagerImpl;
39 import com.intellij.openapi.command.undo.UndoManager;
40 import com.intellij.openapi.editor.Document;
41 import com.intellij.openapi.editor.Editor;
42 import com.intellij.openapi.editor.EditorFactory;
43 import com.intellij.openapi.editor.impl.EditorFactoryImpl;
44 import com.intellij.openapi.editor.impl.EditorImpl;
45 import com.intellij.openapi.fileEditor.FileDocumentManager;
46 import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl;
47 import com.intellij.openapi.fileTypes.FileType;
48 import com.intellij.openapi.fileTypes.FileTypeManager;
49 import com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl;
50 import com.intellij.openapi.module.EmptyModuleType;
51 import com.intellij.openapi.module.Module;
52 import com.intellij.openapi.module.ModuleType;
53 import com.intellij.openapi.project.DumbService;
54 import com.intellij.openapi.project.ModuleAdapter;
55 import com.intellij.openapi.project.Project;
56 import com.intellij.openapi.project.ProjectManager;
57 import com.intellij.openapi.project.ex.ProjectManagerEx;
58 import com.intellij.openapi.project.impl.ProjectImpl;
59 import com.intellij.openapi.project.impl.ProjectManagerImpl;
60 import com.intellij.openapi.projectRoots.Sdk;
61 import com.intellij.openapi.roots.ModuleRootManager;
62 import com.intellij.openapi.roots.OrderRootType;
63 import com.intellij.openapi.startup.StartupManager;
64 import com.intellij.openapi.util.Disposer;
65 import com.intellij.openapi.util.EmptyRunnable;
66 import com.intellij.openapi.util.io.FileUtil;
67 import com.intellij.openapi.vfs.LocalFileSystem;
68 import com.intellij.openapi.vfs.VirtualFile;
69 import com.intellij.openapi.vfs.encoding.EncodingManager;
70 import com.intellij.openapi.vfs.encoding.EncodingManagerImpl;
71 import com.intellij.openapi.vfs.impl.VirtualFilePointerManagerImpl;
72 import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
73 import com.intellij.openapi.vfs.newvfs.persistent.PersistentFSImpl;
74 import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager;
75 import com.intellij.psi.PsiDocumentManager;
76 import com.intellij.psi.PsiFile;
77 import com.intellij.psi.PsiFileFactory;
78 import com.intellij.psi.PsiManager;
79 import com.intellij.psi.codeStyle.CodeStyleSchemes;
80 import com.intellij.psi.codeStyle.CodeStyleSettings;
81 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
82 import com.intellij.psi.impl.DocumentCommitThread;
83 import com.intellij.psi.impl.PsiDocumentManagerImpl;
84 import com.intellij.psi.impl.PsiManagerImpl;
85 import com.intellij.psi.impl.source.tree.injected.InjectedLanguageManagerImpl;
86 import com.intellij.psi.templateLanguages.TemplateDataLanguageMappings;
87 import com.intellij.util.*;
88 import com.intellij.util.containers.ContainerUtil;
89 import com.intellij.util.indexing.UnindexedFilesUpdater;
90 import com.intellij.util.lang.CompoundRuntimeException;
91 import com.intellij.util.messages.MessageBusConnection;
92 import com.intellij.util.ui.UIUtil;
93 import junit.framework.TestCase;
94 import org.jetbrains.annotations.NonNls;
95 import org.jetbrains.annotations.NotNull;
96 import org.jetbrains.annotations.Nullable;
97 import org.jetbrains.annotations.TestOnly;
100 import java.io.ByteArrayOutputStream;
102 import java.io.IOException;
103 import java.io.PrintStream;
104 import java.lang.management.GarbageCollectorMXBean;
105 import java.lang.management.ManagementFactory;
106 import java.util.Arrays;
107 import java.util.List;
108 import java.util.function.Consumer;
113 public abstract class LightPlatformTestCase extends UsefulTestCase implements DataProvider {
114 @NonNls private static final String LIGHT_PROJECT_MARK = "Light project: ";
116 private static IdeaTestApplication ourApplication;
117 @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
118 protected static Project ourProject;
119 private static Module ourModule;
120 private static PsiManager ourPsiManager;
121 private static boolean ourAssertionsInTestDetected;
122 private static VirtualFile ourSourceRoot;
123 private static TestCase ourTestCase;
124 public static Thread ourTestThread;
125 private static LightProjectDescriptor ourProjectDescriptor;
127 private ThreadTracker myThreadTracker;
130 PlatformTestUtil.registerProjectCleanup(LightPlatformTestCase::closeAndDeleteProject);
134 * @return Project to be used in tests for example for project components retrieval.
136 public static Project getProject() {
141 * @return Module to be used in tests for example for module components retrieval.
143 public static Module getModule() {
148 * Shortcut to PsiManager.getInstance(getProject())
151 public static PsiManager getPsiManager() {
152 if (ourPsiManager == null) {
153 ourPsiManager = PsiManager.getInstance(ourProject);
155 return ourPsiManager;
159 public static IdeaTestApplication initApplication() {
160 ourApplication = IdeaTestApplication.getInstance();
161 return ourApplication;
165 public static void disposeApplication() {
166 if (ourApplication != null) {
167 ApplicationManager.getApplication().runWriteAction(() -> Disposer.dispose(ourApplication));
169 ourApplication = null;
173 public static IdeaTestApplication getApplication() {
174 return ourApplication;
177 @SuppressWarnings("UseOfSystemOutOrSystemErr")
178 public static void reportTestExecutionStatistics() {
179 System.out.println("----- TEST STATISTICS -----");
180 UsefulTestCase.logSetupTeardownCosts();
181 System.out.println(String.format("##teamcity[buildStatisticValue key='ideaTests.appInstancesCreated' value='%d']",
182 MockApplication.INSTANCES_CREATED));
183 System.out.println(String.format("##teamcity[buildStatisticValue key='ideaTests.projectInstancesCreated' value='%d']",
184 ProjectManagerImpl.TEST_PROJECTS_CREATED));
185 long totalGcTime = 0;
186 for (GarbageCollectorMXBean mxBean : ManagementFactory.getGarbageCollectorMXBeans()) {
187 totalGcTime += mxBean.getCollectionTime();
189 System.out.println(String.format("##teamcity[buildStatisticValue key='ideaTests.gcTimeMs' value='%d']", totalGcTime));
190 System.out.println(String.format("##teamcity[buildStatisticValue key='ideaTests.classesLoaded' value='%d']",
191 ManagementFactory.getClassLoadingMXBean().getTotalLoadedClassCount()));
194 protected void resetAllFields() {
195 resetClassFields(getClass());
198 private void resetClassFields(@NotNull Class<?> aClass) {
200 UsefulTestCase.clearDeclaredFields(this, aClass);
202 catch (IllegalAccessException e) {
203 throw new RuntimeException(e);
206 if (aClass == LightPlatformTestCase.class) return;
207 resetClassFields(aClass.getSuperclass());
210 private static void cleanPersistedVFSContent() {
211 ((PersistentFSImpl)PersistentFS.getInstance()).cleanPersistedContents();
214 private static void initProject(@NotNull final LightProjectDescriptor descriptor) throws Exception {
215 ourProjectDescriptor = descriptor;
217 if (ourProject != null) {
218 closeAndDeleteProject();
220 ApplicationManager.getApplication().runWriteAction(LightPlatformTestCase::cleanPersistedVFSContent);
222 final File projectFile = FileUtil.createTempFile(ProjectImpl.LIGHT_PROJECT_NAME, ProjectFileType.DOT_DEFAULT_EXTENSION);
223 LocalFileSystem.getInstance().refreshAndFindFileByIoFile(projectFile);
225 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
226 new Throwable(projectFile.getPath()).printStackTrace(new PrintStream(buffer));
228 ourProject = PlatformTestCase.createProject(projectFile, LIGHT_PROJECT_MARK + buffer);
229 ourPathToKeep = projectFile.getPath();
230 ourPsiManager = null;
232 ourProjectDescriptor.setUpProject(ourProject, new LightProjectDescriptor.SetupHandler() {
234 public void moduleCreated(@NotNull Module module) {
235 //noinspection AssignmentToStaticFieldFromInstanceMethod
240 public void sourceRootCreated(@NotNull VirtualFile sourceRoot) {
241 //noinspection AssignmentToStaticFieldFromInstanceMethod
242 ourSourceRoot = sourceRoot;
246 // project creation may make a lot of pointers, do not regard them as leak
247 ((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).storePointers();
251 * @return The only source root
253 public static VirtualFile getSourceRoot() {
254 return ourSourceRoot;
258 protected void setUp() throws Exception {
259 EdtTestUtil.runInEdtAndWait((ThrowableRunnable<Throwable>)() -> {
262 ApplicationInfoImpl.setInPerformanceTest(isPerformanceTest());
264 ourApplication.setDataProvider(this);
265 LightProjectDescriptor descriptor = new SimpleLightProjectDescriptor(getModuleType(), getProjectJDK());
266 doSetup(descriptor, configureLocalInspectionTools(), getTestRootDisposable());
267 InjectedLanguageManagerImpl.pushInjectors(getProject());
271 myThreadTracker = new ThreadTracker();
272 ModuleRootManager.getInstance(ourModule).orderEntries().getAllLibrariesAndSdkClassesRoots();
273 VirtualFilePointerManagerImpl filePointerManager = (VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance();
274 filePointerManager.storePointers();
278 public static void doSetup(@NotNull LightProjectDescriptor descriptor,
279 @NotNull LocalInspectionTool[] localInspectionTools,
280 @NotNull Disposable parentDisposable) throws Exception {
281 assertNull("Previous test " + ourTestCase + " hasn't called tearDown(). Probably overridden without super call.", ourTestCase);
282 IdeaLogger.ourErrorsOccurred = null;
283 ApplicationManager.getApplication().assertIsDispatchThread();
284 boolean reusedProject = true;
285 if (ourProject == null || ourProjectDescriptor == null || !ourProjectDescriptor.equals(descriptor)) {
286 initProject(descriptor);
287 reusedProject = false;
290 ProjectManagerEx projectManagerEx = ProjectManagerEx.getInstanceEx();
291 projectManagerEx.openTestProject(ourProject);
293 DumbService.getInstance(ourProject).queueTask(new UnindexedFilesUpdater(ourProject, false));
296 MessageBusConnection connection = ourProject.getMessageBus().connect(parentDisposable);
297 connection.subscribe(ProjectTopics.MODULES, new ModuleAdapter() {
299 public void moduleAdded(@NotNull Project project, @NotNull Module module) {
300 fail("Adding modules is not permitted in LightIdeaTestCase.");
304 clearUncommittedDocuments(getProject());
306 InspectionsKt.configureInspections(localInspectionTools, getProject(), parentDisposable);
308 assertFalse(getPsiManager().isDisposed());
309 Boolean passed = null;
311 passed = StartupManagerEx.getInstanceEx(getProject()).startupActivityPassed();
313 catch (Exception ignored) {
316 assertTrue("open: " + getProject().isOpen() +
317 "; disposed:" + getProject().isDisposed() +
318 "; startup passed:" + passed +
319 "; all open projects: " + Arrays.asList(ProjectManager.getInstance().getOpenProjects()), getProject().isInitialized());
321 CodeStyleSettingsManager.getInstance(getProject()).setTemporarySettings(new CodeStyleSettings());
323 final FileDocumentManager manager = FileDocumentManager.getInstance();
324 if (manager instanceof FileDocumentManagerImpl) {
325 Document[] unsavedDocuments = manager.getUnsavedDocuments();
326 manager.saveAllDocuments();
327 ApplicationManager.getApplication().runWriteAction(((FileDocumentManagerImpl)manager)::dropAllUnsavedDocuments);
329 assertEmpty("There are unsaved documents", Arrays.asList(unsavedDocuments));
331 UIUtil.dispatchAllInvocationEvents(); // startup activities
333 ((FileTypeManagerImpl)FileTypeManager.getInstance()).drainReDetectQueue();
336 //protected void enableInspectionTools(@NotNull Class<? extends InspectionProfileEntry>[] classes) {
337 // for (InspectionProfileEntry tool : InspectionTestUtil.instantiateTools(Arrays.asList(classes))) {
338 // enableInspectionTool(tool);
342 protected void enableInspectionTools(@NotNull InspectionProfileEntry... tools) {
343 for (InspectionProfileEntry tool : tools) {
344 enableInspectionTool(tool);
348 //protected void enableInspectionTool(@NotNull InspectionToolWrapper toolWrapper) {
349 // InspectionsKt.enableInspectionTool(getProject(), toolWrapper, myTestRootDisposable);
352 protected void enableInspectionTool(@NotNull InspectionProfileEntry tool) {
353 InspectionsKt.enableInspectionTool(getProject(), tool, myTestRootDisposable);
357 protected LocalInspectionTool[] configureLocalInspectionTools() {
358 return LocalInspectionTool.EMPTY_ARRAY;
361 @SuppressWarnings("TearDownDoesntCallSuperTearDown")
363 protected void tearDown() throws Exception {
364 Project project = getProject();
365 List<Throwable> errors = new SmartList<>();
366 Consumer<ThrowableRunnable<?>> runSafe = c -> {
370 catch (Throwable e) {
375 runSafe.accept(() -> CodeStyleSettingsManager.getInstance(project).dropTemporarySettings());
376 runSafe.accept(() -> checkForSettingsDamage(errors));
377 runSafe.accept(() -> doTearDown(project, ourApplication, true, errors));
378 runSafe.accept(super::tearDown);
379 runSafe.accept(() -> myThreadTracker.checkLeak());
380 runSafe.accept(() -> InjectedLanguageManagerImpl.checkInjectorsAreDisposed(project));
381 runSafe.accept(() -> ((VirtualFilePointerManagerImpl)VirtualFilePointerManager.getInstance()).assertPointersAreDisposed());
383 catch (Throwable e) {
387 CompoundRuntimeException.throwIfNotEmpty(errors);
391 public static void doTearDown(@NotNull final Project project, @NotNull IdeaTestApplication application, boolean checkForEditors, @NotNull List<Throwable> exceptions) throws Exception {
392 PsiDocumentManagerImpl documentManager;
394 ((FileTypeManagerImpl)FileTypeManager.getInstance()).drainReDetectQueue();
395 DocumentCommitThread.getInstance().clearQueue();
396 CodeStyleSettingsManager.getInstance(project).dropTemporarySettings();
398 checkJavaSwingTimersAreDisposed(exceptions);
400 UsefulTestCase.doPostponedFormatting(project);
402 LookupManager lookupManager = LookupManager.getInstance(project);
403 if (lookupManager != null) {
404 lookupManager.hideActiveLookup();
406 ((StartupManagerImpl)StartupManager.getInstance(project)).prepareForNextTest();
407 if (ProjectManager.getInstance() == null) {
408 exceptions.add(new AssertionError("Application components damaged"));
411 ContainerUtil.addIfNotNull(exceptions, new WriteCommandAction.Simple(project) {
413 protected void run() throws Throwable {
414 if (ourSourceRoot != null) {
416 final VirtualFile[] children = ourSourceRoot.getChildren();
417 for (VirtualFile child : children) {
421 catch (IOException e) {
422 //noinspection CallToPrintStackTrace
426 EncodingManager encodingManager = EncodingManager.getInstance();
427 if (encodingManager instanceof EncodingManagerImpl) ((EncodingManagerImpl)encodingManager).clearDocumentQueue();
429 FileDocumentManager manager = FileDocumentManager.getInstance();
431 ApplicationManager.getApplication().runWriteAction(EmptyRunnable.getInstance()); // Flush postponed formatting if any.
432 manager.saveAllDocuments();
433 if (manager instanceof FileDocumentManagerImpl) {
434 ((FileDocumentManagerImpl)manager).dropAllUnsavedDocuments();
437 }.execute().getThrowable());
439 assertFalse(PsiManager.getInstance(project).isDisposed());
440 if (!ourAssertionsInTestDetected) {
441 if (IdeaLogger.ourErrorsOccurred != null) {
442 throw IdeaLogger.ourErrorsOccurred;
445 documentManager = clearUncommittedDocuments(project);
446 ((HintManagerImpl)HintManager.getInstance()).cleanup();
447 DocumentCommitThread.getInstance().clearQueue();
449 EdtTestUtil.runInEdtAndWait((Runnable)() -> {
450 ((UndoManagerImpl)UndoManager.getGlobalInstance()).dropHistoryInTests();
451 ((UndoManagerImpl)UndoManager.getInstance(project)).dropHistoryInTests();
453 UIUtil.dispatchAllInvocationEvents();
456 TemplateDataLanguageMappings.getInstance(project).cleanupForNextTest();
459 ProjectManagerEx.getInstanceEx().closeTestProject(project);
460 application.setDataProvider(null);
462 ((PsiManagerImpl)PsiManager.getInstance(project)).cleanupForNextTest();
463 CompletionProgressIndicator.cleanupForNextTest();
466 if (checkForEditors) {
467 checkEditorsReleased(exceptions);
469 documentManager.clearUncommittedDocuments();
471 if (ourTestCount++ % 100 == 0) {
472 // some tests are written in Groovy, and running all of them may result in some 40M of memory wasted on bean infos
473 // so let's clear the cache every now and then to ensure it doesn't grow too large
474 GCUtil.clearBeanInfoCache();
478 private static int ourTestCount;
480 public static PsiDocumentManagerImpl clearUncommittedDocuments(@NotNull Project project) {
481 PsiDocumentManagerImpl documentManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(project);
482 documentManager.clearUncommittedDocuments();
484 ProjectManagerImpl projectManager = (ProjectManagerImpl)ProjectManager.getInstance();
485 if (projectManager.isDefaultProjectInitialized()) {
486 Project defaultProject = projectManager.getDefaultProject();
487 ((PsiDocumentManagerImpl)PsiDocumentManager.getInstance(defaultProject)).clearUncommittedDocuments();
489 return documentManager;
492 public static void checkEditorsReleased(@NotNull List<Throwable> exceptions) {
493 Editor[] allEditors = EditorFactory.getInstance().getAllEditors();
494 if (allEditors.length == 0) {
498 for (Editor editor : allEditors) {
500 EditorFactoryImpl.throwNotReleasedError(editor);
502 catch (Throwable e) {
506 EditorFactory.getInstance().releaseEditor(editor);
511 ((EditorImpl)allEditors[0]).throwDisposalError("Unreleased editors: " + allEditors.length);
513 catch (Throwable e) {
518 @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod")
520 public final void runBare() throws Throwable {
521 if (!shouldRunTest()) {
525 TestRunnerUtil.replaceIdeEventQueueSafely();
526 EdtTestUtil.runInEdtAndWait((ThrowableRunnable<Throwable>)() -> {
528 ourTestThread = Thread.currentThread();
532 ourTestThread = null;
534 Application application = ApplicationManager.getApplication();
535 if (application instanceof ApplicationEx) {
536 PlatformTestCase.cleanupApplicationCaches(ourProject);
540 catch (Throwable e) {
541 //noinspection CallToPrintStackTrace
547 // just to make sure all deferred Runnables to finish
548 SwingUtilities.invokeAndWait(EmptyRunnable.getInstance());
550 if (IdeaLogger.ourErrorsOccurred != null) {
551 throw IdeaLogger.ourErrorsOccurred;
555 @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod")
556 private void startRunAndTear() throws Throwable {
559 ourAssertionsInTestDetected = true;
561 ourAssertionsInTestDetected = false;
567 //catch(Throwable th){
568 // noinspection CallToPrintStackTrace
569 //th.printStackTrace();
575 public Object getData(String dataId) {
576 return ourProject == null || ourProject.isDisposed() ? null : new TestDataProvider(ourProject).getData(dataId);
579 protected Sdk getProjectJDK() {
584 protected ModuleType getModuleType() {
585 return EmptyModuleType.getInstance();
589 * Creates dummy source file. One is not placed under source root so some PSI functions like resolve to external classes
590 * may not work. Though it works significantly faster and yet can be used if you need to create some PSI structures for
593 * @param fileName - name of the file to create. Extension is used to choose what PSI should be created like java, jsp, aj, xml etc.
594 * @param text - file text.
595 * @return dummy psi file.
599 protected static PsiFile createFile(@NonNls @NotNull String fileName, @NonNls @NotNull String text) throws IncorrectOperationException {
600 FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(fileName);
601 return PsiFileFactory.getInstance(getProject())
602 .createFileFromText(fileName, fileType, text, LocalTimeCounter.currentTime(), true, false);
606 protected static PsiFile createLightFile(@NonNls @NotNull String fileName, @NotNull String text) throws IncorrectOperationException {
607 FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(fileName);
608 return PsiFileFactory.getInstance(getProject())
609 .createFileFromText(fileName, fileType, text, LocalTimeCounter.currentTime(), false, false);
613 * Convenient conversion of testSomeTest -> someTest | SomeTest where testSomeTest is the name of current test.
615 * @param lowercaseFirstLetter - whether first letter after test should be lowercased.
619 protected String getTestName(boolean lowercaseFirstLetter) {
620 String name = getName();
621 assertTrue("Test name should start with 'test': " + name, name.startsWith("test"));
622 name = name.substring("test".length());
623 if (!name.isEmpty() && lowercaseFirstLetter && !PlatformTestUtil.isAllUppercaseName(name)) {
624 name = Character.toLowerCase(name.charAt(0)) + name.substring(1);
629 protected static void commitDocument(@NotNull Document document) {
630 PsiDocumentManager.getInstance(getProject()).commitDocument(document);
633 protected static void commitAllDocuments() {
634 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
639 protected CodeStyleSettings getCurrentCodeStyleSettings() {
640 if (CodeStyleSchemes.getInstance().getCurrentScheme() == null) return new CodeStyleSettings();
641 return CodeStyleSettingsManager.getSettings(getProject());
644 protected static Document getDocument(@NotNull PsiFile file) {
645 return PsiDocumentManager.getInstance(getProject()).getDocument(file);
648 @SuppressWarnings("NonPrivateFieldAccessedInSynchronizedContext")
649 public static synchronized void closeAndDeleteProject() {
650 if (ourProject == null) {
653 if (ApplicationManager.getApplication().isWriteAccessAllowed()) {
654 throw new IllegalStateException("Must not call closeAndDeleteProject from under write action");
657 if (!ourProject.isDisposed()) {
658 @SuppressWarnings("ConstantConditions")
659 File ioFile = new File(ourProject.getProjectFilePath());
660 Disposer.dispose(ourProject);
661 if (ioFile.exists()) {
662 File dir = ioFile.getParentFile();
663 if (dir.getName().startsWith(UsefulTestCase.TEMP_DIR_MARKER)) {
664 FileUtil.delete(dir);
667 FileUtil.delete(ioFile);
672 ProjectManagerEx.getInstanceEx().closeAndDispose(ourProject);
674 // project may be disposed but empty folder may still be there
675 if (ourPathToKeep != null) {
676 File parent = new File(ourPathToKeep).getParentFile();
677 if (parent.getName().startsWith(UsefulTestCase.TEMP_DIR_MARKER)) {
678 // delete only empty folders
679 //noinspection ResultOfMethodCallIgnored
685 ourPathToKeep = null;
688 private static class SimpleLightProjectDescriptor extends LightProjectDescriptor {
689 @NotNull private final ModuleType myModuleType;
690 @Nullable private final Sdk mySdk;
692 SimpleLightProjectDescriptor(@NotNull ModuleType moduleType, @Nullable Sdk sdk) {
693 myModuleType = moduleType;
699 public ModuleType getModuleType() {
705 public Sdk getSdk() {
710 public boolean equals(Object o) {
711 if (this == o) return true;
712 if (o == null || getClass() != o.getClass()) return false;
714 SimpleLightProjectDescriptor that = (SimpleLightProjectDescriptor)o;
716 if (!myModuleType.equals(that.myModuleType)) return false;
717 return areJdksEqual(that.getSdk());
721 public int hashCode() {
722 return myModuleType.hashCode();
725 private boolean areJdksEqual(final Sdk newSdk) {
726 if (mySdk == null || newSdk == null) return mySdk == newSdk;
728 final String[] myUrls = mySdk.getRootProvider().getUrls(OrderRootType.CLASSES);
729 final String[] newUrls = newSdk.getRootProvider().getUrls(OrderRootType.CLASSES);
730 return ContainerUtil.newHashSet(myUrls).equals(ContainerUtil.newHashSet(newUrls));