simplify code
[idea/community.git] / platform / testRunner / src / com / intellij / execution / testframework / autotest / AutoTestManager.java
1 /*
2  * Copyright 2000-2015 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.execution.testframework.autotest;
17
18 import com.intellij.execution.DelayedDocumentWatcher;
19 import com.intellij.execution.ExecutionManager;
20 import com.intellij.execution.process.ProcessAdapter;
21 import com.intellij.execution.process.ProcessEvent;
22 import com.intellij.execution.process.ProcessHandler;
23 import com.intellij.execution.process.ProcessListener;
24 import com.intellij.execution.runners.ExecutionEnvironment;
25 import com.intellij.execution.runners.ExecutionUtil;
26 import com.intellij.execution.ui.RunContentDescriptor;
27 import com.intellij.execution.ui.RunContentManager;
28 import com.intellij.ide.DataManager;
29 import com.intellij.ide.scratch.ScratchFileService;
30 import com.intellij.ide.util.PropertiesComponent;
31 import com.intellij.openapi.actionSystem.LangDataKeys;
32 import com.intellij.openapi.application.ApplicationManager;
33 import com.intellij.openapi.application.ModalityState;
34 import com.intellij.openapi.components.ServiceManager;
35 import com.intellij.openapi.fileEditor.FileEditorManager;
36 import com.intellij.openapi.project.Project;
37 import com.intellij.openapi.util.Condition;
38 import com.intellij.openapi.util.Key;
39 import com.intellij.openapi.vfs.VirtualFile;
40 import com.intellij.ui.content.Content;
41 import com.intellij.util.Consumer;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
44
45 import javax.swing.*;
46
47 /**
48  * @author yole
49  */
50 public class AutoTestManager {
51   private static final String AUTO_TEST_MANAGER_DELAY = "auto.test.manager.delay";
52   private static final int AUTO_TEST_MANAGER_DELAY_DEFAULT = 3000;
53
54   private static final Key<ProcessListener> ON_TERMINATION_RESTARTER_KEY = Key.create("auto.test.manager.on.termination.restarter");
55   private static final Key<ExecutionEnvironment> EXECUTION_ENVIRONMENT_KEY = Key.create("auto.test.manager.execution.environment");
56
57   private final Project myProject;
58   private int myDelayMillis;
59   private DelayedDocumentWatcher myDocumentWatcher;
60
61   @NotNull
62   public static AutoTestManager getInstance(Project project) {
63     return ServiceManager.getService(project, AutoTestManager.class);
64   }
65
66   public AutoTestManager(@NotNull Project project) {
67     myProject = project;
68     myDelayMillis = PropertiesComponent.getInstance(project).getInt(AUTO_TEST_MANAGER_DELAY, AUTO_TEST_MANAGER_DELAY_DEFAULT);
69     myDocumentWatcher = createWatcher();
70   }
71
72   @NotNull
73   private DelayedDocumentWatcher createWatcher() {
74     return new DelayedDocumentWatcher(myProject, myDelayMillis, new Consumer<Integer>() {
75       @Override
76       public void consume(Integer modificationStamp) {
77         restartAllAutoTests(modificationStamp);
78       }
79     }, new Condition<VirtualFile>() {
80       @Override
81       public boolean value(VirtualFile file) {
82         if (ScratchFileService.getInstance().getRootType(file) != null) {
83           return false;
84         }
85         // Vladimir.Krivosheev - I don't know, why AutoTestManager checks it, but old behavior is preserved
86         return FileEditorManager.getInstance(myProject).isFileOpen(file);
87       }
88     });
89   }
90
91   public void setAutoTestEnabled(@NotNull RunContentDescriptor descriptor, @NotNull ExecutionEnvironment environment, boolean enabled) {
92     Content content = descriptor.getAttachedContent();
93     if (content != null) {
94       if (enabled) {
95         EXECUTION_ENVIRONMENT_KEY.set(content, environment);
96         myDocumentWatcher.activate();
97       }
98       else {
99         EXECUTION_ENVIRONMENT_KEY.set(content, null);
100         if (!hasEnabledAutoTests()) {
101           myDocumentWatcher.deactivate();
102         }
103         ProcessHandler processHandler = descriptor.getProcessHandler();
104         if (processHandler != null) {
105           clearRestarterListener(processHandler);
106         }
107       }
108     }
109   }
110
111   private boolean hasEnabledAutoTests() {
112     RunContentManager contentManager = ExecutionManager.getInstance(myProject).getContentManager();
113     for (RunContentDescriptor descriptor : contentManager.getAllDescriptors()) {
114       if (isAutoTestEnabledForDescriptor(descriptor)) {
115         return true;
116       }
117     }
118     return false;
119   }
120
121   public boolean isAutoTestEnabled(@NotNull RunContentDescriptor descriptor) {
122     return isAutoTestEnabledForDescriptor(descriptor);
123   }
124
125   private static boolean isAutoTestEnabledForDescriptor(@NotNull RunContentDescriptor descriptor) {
126     Content content = descriptor.getAttachedContent();
127     if (content != null) {
128       ExecutionEnvironment watched = EXECUTION_ENVIRONMENT_KEY.get(content);
129       if (watched != null) {
130         ExecutionEnvironment current = getCurrentEnvironment(content);
131         boolean result = current != null && equals(current, watched);
132         if (!result) {
133           // let GC do its work
134           EXECUTION_ENVIRONMENT_KEY.set(content, null);
135         }
136         return result;
137       }
138     }
139     return false;
140   }
141
142   @Nullable
143   private static ExecutionEnvironment getCurrentEnvironment(@NotNull Content content) {
144     JComponent component = content.getComponent();
145     if (component == null) {
146       return null;
147     }
148     return LangDataKeys.EXECUTION_ENVIRONMENT.getData(DataManager.getInstance().getDataContext(component));
149   }
150
151   private static boolean equals(@NotNull ExecutionEnvironment env1, @NotNull ExecutionEnvironment env2) {
152     return env1.getRunProfile() == env2.getRunProfile() &&
153            env1.getRunner() == env2.getRunner() &&
154            env1.getExecutor() == env2.getExecutor() &&
155            env1.getExecutionTarget() == env2.getExecutionTarget();
156   }
157
158   private static void clearRestarterListener(@NotNull ProcessHandler processHandler) {
159     ProcessListener restarterListener = ON_TERMINATION_RESTARTER_KEY.get(processHandler, null);
160     if (restarterListener != null) {
161       processHandler.removeProcessListener(restarterListener);
162       ON_TERMINATION_RESTARTER_KEY.set(processHandler, null);
163     }
164   }
165
166   private void restartAllAutoTests(int modificationStamp) {
167     RunContentManager contentManager = ExecutionManager.getInstance(myProject).getContentManager();
168     boolean active = false;
169     for (RunContentDescriptor descriptor : contentManager.getAllDescriptors()) {
170       if (isAutoTestEnabledForDescriptor(descriptor)) {
171         restartAutoTest(descriptor, modificationStamp, myDocumentWatcher);
172         active = true;
173       }
174     }
175     if (!active) {
176       myDocumentWatcher.deactivate();
177     }
178   }
179
180   private static void restartAutoTest(@NotNull RunContentDescriptor descriptor,
181                                       int modificationStamp,
182                                       @NotNull DelayedDocumentWatcher documentWatcher) {
183     ProcessHandler processHandler = descriptor.getProcessHandler();
184     if (processHandler != null && !processHandler.isProcessTerminated()) {
185       scheduleRestartOnTermination(descriptor, processHandler, modificationStamp, documentWatcher);
186     }
187     else {
188       restart(descriptor);
189     }
190   }
191
192   private static void scheduleRestartOnTermination(@NotNull final RunContentDescriptor descriptor,
193                                                    @NotNull final ProcessHandler processHandler,
194                                                    final int modificationStamp,
195                                                    @NotNull final DelayedDocumentWatcher documentWatcher) {
196     ProcessListener restarterListener = ON_TERMINATION_RESTARTER_KEY.get(processHandler);
197     if (restarterListener != null) {
198       clearRestarterListener(processHandler);
199     }
200     restarterListener = new ProcessAdapter() {
201       @Override
202       public void processTerminated(ProcessEvent event) {
203         clearRestarterListener(processHandler);
204         ApplicationManager.getApplication().invokeLater(new Runnable() {
205           @Override
206           public void run() {
207             if (isAutoTestEnabledForDescriptor(descriptor) && documentWatcher.isUpToDate(modificationStamp)) {
208               restart(descriptor);
209             }
210           }
211         }, ModalityState.any());
212       }
213     };
214     ON_TERMINATION_RESTARTER_KEY.set(processHandler, restarterListener);
215     processHandler.addProcessListener(restarterListener);
216   }
217
218   private static void restart(@NotNull RunContentDescriptor descriptor) {
219     descriptor.setActivateToolWindowWhenAdded(false);
220     descriptor.setReuseToolWindowActivation(true);
221     ExecutionUtil.restart(descriptor);
222   }
223
224   int getDelay() {
225     return myDelayMillis;
226   }
227
228   void setDelay(int delay) {
229     myDelayMillis = delay;
230     myDocumentWatcher.deactivate();
231     myDocumentWatcher = createWatcher();
232     if (hasEnabledAutoTests()) {
233       myDocumentWatcher.activate();
234     }
235     PropertiesComponent.getInstance(myProject).setValue(AUTO_TEST_MANAGER_DELAY, myDelayMillis, AUTO_TEST_MANAGER_DELAY_DEFAULT);
236   }
237 }