fix "IDEA-221944 Deadlock on opening second project" and support preloading for proje...
[idea/community.git] / platform / testFramework / src / com / intellij / idea / IdeaTestApplication.java
1 // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.idea;
3
4 import com.intellij.concurrency.IdeaForkJoinWorkerThreadFactory;
5 import com.intellij.diagnostic.ThreadDumper;
6 import com.intellij.ide.DataManager;
7 import com.intellij.ide.impl.HeadlessDataManager;
8 import com.intellij.ide.plugins.IdeaPluginDescriptor;
9 import com.intellij.ide.plugins.PluginManagerCore;
10 import com.intellij.openapi.Disposable;
11 import com.intellij.openapi.actionSystem.DataProvider;
12 import com.intellij.openapi.application.Application;
13 import com.intellij.openapi.application.ApplicationManager;
14 import com.intellij.openapi.application.impl.ApplicationImpl;
15 import com.intellij.openapi.util.Disposer;
16 import com.intellij.testFramework.HeavyPlatformTestCase;
17 import com.intellij.ui.IconManager;
18 import com.intellij.util.ExceptionUtil;
19 import com.intellij.util.concurrency.AppExecutorUtil;
20 import org.jetbrains.annotations.NotNull;
21 import org.jetbrains.annotations.Nullable;
22
23 import java.util.List;
24 import java.util.concurrent.CompletableFuture;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.TimeUnit;
27 import java.util.concurrent.TimeoutException;
28
29 public final class IdeaTestApplication implements Disposable {
30   private static volatile IdeaTestApplication ourInstance;
31   private static volatile RuntimeException bootstrapError;
32   private static volatile boolean isBootstrappingAppNow;
33
34   private IdeaTestApplication() { }
35
36   public void setDataProvider(@Nullable DataProvider provider) {
37     getDataManager().setTestDataProvider(provider);
38   }
39
40   public void setDataProvider(@Nullable DataProvider provider, Disposable parentDisposable) {
41     getDataManager().setTestDataProvider(provider, parentDisposable);
42   }
43
44   public @Nullable Object getData(@NotNull String dataId) {
45     return getDataManager().getDataContext().getData(dataId);
46   }
47
48   private static HeadlessDataManager getDataManager() {
49     return (HeadlessDataManager)ApplicationManager.getApplication().getComponent(DataManager.class);
50   }
51
52   public static IdeaTestApplication getInstance() {
53     IdeaTestApplication instance = ourInstance;
54     if (instance == null) {
55       try {
56         instance = createInstance();
57       }
58       catch (RuntimeException e) {
59         bootstrapError = e;
60         isBootstrappingAppNow = false;
61         throw e;
62       }
63     }
64     return instance;
65   }
66
67   @NotNull
68   private static synchronized IdeaTestApplication createInstance() {
69     if (ourInstance != null) {
70       return ourInstance;
71     }
72
73     if (bootstrapError != null) {
74       throw bootstrapError;
75     }
76
77     if (isBootstrappingAppNow) {
78       throw new IllegalStateException("App bootstrap is already in process");
79     }
80     isBootstrappingAppNow = true;
81
82     HeavyPlatformTestCase.doAutodetectPlatformPrefix();
83
84     String[] args = {"inspect", "", "", ""};
85     Main.setFlags(args);
86     assert Main.isHeadless();
87     assert Main.isCommandLine();
88     PluginManagerCore.isUnitTestMode = true;
89     IdeaForkJoinWorkerThreadFactory.setupForkJoinCommonPool(true);
90
91     CompletableFuture<List<IdeaPluginDescriptor>> loadedPluginFuture = CompletableFuture.supplyAsync(() -> {
92       return PluginManagerCore.getLoadedPlugins(IdeaTestApplication.class.getClassLoader());
93     }, AppExecutorUtil.getAppExecutorService());
94
95     ApplicationImpl.patchSystem();
96     ApplicationImpl app = new ApplicationImpl(true, true, true, true);
97     IconManager.activate();
98     try {
99       ApplicationLoader.registerRegistryAndInitStore(loadedPluginFuture.thenApply(it -> {
100         app.registerComponents(it);
101         return it;
102       }), app)
103         .thenCompose(it -> app.preloadServices(it))
104         .get(20, TimeUnit.SECONDS);
105     }
106     catch (TimeoutException e) {
107       throw new RuntimeException("Cannot load plugin descriptors in 20 seconds: " + ThreadDumper.dumpThreadsToString(), e);
108     }
109     catch (ExecutionException | InterruptedException e) {
110       ExceptionUtil.rethrow(e.getCause() == null ? e : e.getCause());
111     }
112
113     app.loadComponents(null);
114
115     isBootstrappingAppNow = false;
116     ourInstance = new IdeaTestApplication();
117     return ourInstance;
118   }
119
120   @Override
121   public void dispose() {
122     disposeInstance();
123   }
124
125   private static synchronized void disposeInstance() {
126     if (ourInstance != null) {
127       Application application = ApplicationManager.getApplication();
128       if (application != null) {
129         // `ApplicationManager#ourApplication` will be automatically set to `null`
130         Disposer.dispose(application);
131       }
132       ourInstance = null;
133     }
134   }
135 }