/* * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.execution.testframework.autotest; import com.intellij.execution.ExecutionManager; import com.intellij.execution.RunnerAndConfigurationSettings; import com.intellij.execution.configurations.RunConfiguration; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.impl.RunManagerImpl; import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.process.ProcessListener; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ExecutionUtil; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.execution.ui.RunContentManager; import com.intellij.ide.DataManager; import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.ui.content.Content; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xmlb.annotations.AbstractCollection; import com.intellij.util.xmlb.annotations.Attribute; import com.intellij.util.xmlb.annotations.Tag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.List; import java.util.Set; public abstract class AbstractAutoTestManager implements PersistentStateComponent<AbstractAutoTestManager.State> { protected static final String AUTO_TEST_MANAGER_DELAY = "auto.test.manager.delay"; protected static final int AUTO_TEST_MANAGER_DELAY_DEFAULT = 3000; private static final Key<ProcessListener> ON_TERMINATION_RESTARTER_KEY = Key.create("auto.test.manager.on.termination.restarter"); private final Project myProject; private final Set<RunProfile> myEnabledRunProfiles = ContainerUtil.newHashSet(); protected int myDelayMillis; private AutoTestWatcher myWatcher; public AbstractAutoTestManager(@NotNull Project project) { myProject = project; myDelayMillis = PropertiesComponent.getInstance(project).getInt(AUTO_TEST_MANAGER_DELAY, AUTO_TEST_MANAGER_DELAY_DEFAULT); myWatcher = createWatcher(project); } @Nullable private static ExecutionEnvironment getCurrentEnvironment(@NotNull Content content) { JComponent component = content.getComponent(); if (component == null) { return null; } return LangDataKeys.EXECUTION_ENVIRONMENT.getData(DataManager.getInstance().getDataContext(component)); } private static void clearRestarterListener(@NotNull ProcessHandler processHandler) { ProcessListener restarterListener = ON_TERMINATION_RESTARTER_KEY.get(processHandler, null); if (restarterListener != null) { processHandler.removeProcessListener(restarterListener); ON_TERMINATION_RESTARTER_KEY.set(processHandler, null); } } private static void restart(@NotNull RunContentDescriptor descriptor) { descriptor.setActivateToolWindowWhenAdded(false); descriptor.setReuseToolWindowActivation(true); ExecutionUtil.restart(descriptor); } public static void saveConfigurationState(State state, RunProfile profile) { RunConfiguration runConfiguration = ObjectUtils.tryCast(profile, RunConfiguration.class); if (runConfiguration != null) { RunConfigurationDescriptor descriptor = new RunConfigurationDescriptor(); descriptor.myType = runConfiguration.getType().getId(); descriptor.myName = runConfiguration.getName(); state.myEnabledRunConfigurations.add(descriptor); } } public static List<RunConfiguration> loadConfigurations(State state, Project project) { List<RunConfiguration> configurations = ContainerUtil.newArrayList(); RunManagerImpl runManager = RunManagerImpl.getInstanceImpl(project); List<RunConfigurationDescriptor> descriptors = ContainerUtil.notNullize(state.myEnabledRunConfigurations); for (RunConfigurationDescriptor descriptor : descriptors) { if (descriptor.myType != null && descriptor.myName != null) { RunnerAndConfigurationSettings settings = runManager.findConfigurationByTypeAndName(descriptor.myType, descriptor.myName); RunConfiguration configuration = settings != null ? settings.getConfiguration() : null; if (configuration != null) { configurations.add(configuration); } } } return configurations; } @NotNull protected abstract AutoTestWatcher createWatcher(Project project); public void setAutoTestEnabled(@NotNull RunContentDescriptor descriptor, @NotNull ExecutionEnvironment environment, boolean enabled) { Content content = descriptor.getAttachedContent(); if (content != null) { if (enabled) { myEnabledRunProfiles.add(environment.getRunProfile()); myWatcher.activate(); } else { myEnabledRunProfiles.remove(environment.getRunProfile()); if (!hasEnabledAutoTests()) { myWatcher.deactivate(); } ProcessHandler processHandler = descriptor.getProcessHandler(); if (processHandler != null) { clearRestarterListener(processHandler); } } } } private boolean hasEnabledAutoTests() { RunContentManager contentManager = ExecutionManager.getInstance(myProject).getContentManager(); for (RunContentDescriptor descriptor : contentManager.getAllDescriptors()) { if (isAutoTestEnabledForDescriptor(descriptor)) { return true; } } return false; } public boolean isAutoTestEnabled(@NotNull RunContentDescriptor descriptor) { return isAutoTestEnabledForDescriptor(descriptor); } private boolean isAutoTestEnabledForDescriptor(@NotNull RunContentDescriptor descriptor) { Content content = descriptor.getAttachedContent(); if (content != null) { ExecutionEnvironment environment = getCurrentEnvironment(content); return environment != null && myEnabledRunProfiles.contains(environment.getRunProfile()); } return false; } protected void restartAllAutoTests(int modificationStamp) { RunContentManager contentManager = ExecutionManager.getInstance(myProject).getContentManager(); boolean active = false; for (RunContentDescriptor descriptor : contentManager.getAllDescriptors()) { if (isAutoTestEnabledForDescriptor(descriptor)) { restartAutoTest(descriptor, modificationStamp, myWatcher); active = true; } } if (!active) { myWatcher.deactivate(); } } private void restartAutoTest(@NotNull RunContentDescriptor descriptor, int modificationStamp, @NotNull AutoTestWatcher documentWatcher) { ProcessHandler processHandler = descriptor.getProcessHandler(); if (processHandler != null && !processHandler.isProcessTerminated()) { scheduleRestartOnTermination(descriptor, processHandler, modificationStamp, documentWatcher); } else { restart(descriptor); } } private void scheduleRestartOnTermination(@NotNull final RunContentDescriptor descriptor, @NotNull final ProcessHandler processHandler, final int modificationStamp, @NotNull final AutoTestWatcher watcher) { ProcessListener restarterListener = ON_TERMINATION_RESTARTER_KEY.get(processHandler); if (restarterListener != null) { clearRestarterListener(processHandler); } restarterListener = new ProcessAdapter() { @Override public void processTerminated(ProcessEvent event) { clearRestarterListener(processHandler); ApplicationManager.getApplication().invokeLater(() -> { if (isAutoTestEnabledForDescriptor(descriptor) && watcher.isUpToDate(modificationStamp)) { restart(descriptor); } }, ModalityState.any()); } }; ON_TERMINATION_RESTARTER_KEY.set(processHandler, restarterListener); processHandler.addProcessListener(restarterListener); } int getDelay() { return myDelayMillis; } void setDelay(int delay) { myDelayMillis = delay; myWatcher.deactivate(); myWatcher = createWatcher(myProject); if (hasEnabledAutoTests()) { myWatcher.activate(); } PropertiesComponent.getInstance(myProject).setValue(AUTO_TEST_MANAGER_DELAY, myDelayMillis, AUTO_TEST_MANAGER_DELAY_DEFAULT); } @Nullable @Override public State getState() { State state = new State(); for (RunProfile profile : myEnabledRunProfiles) { saveConfigurationState(state, profile); } return state; } @Override public void loadState(State state) { List<RunConfiguration> configurations = loadConfigurations(state, myProject); myEnabledRunProfiles.clear(); myEnabledRunProfiles.addAll(configurations); if (!configurations.isEmpty()) { myWatcher.activate(); } } public static class State { @Tag("enabled-run-configurations") @AbstractCollection(surroundWithTag = false) List<AutoTestManager.RunConfigurationDescriptor> myEnabledRunConfigurations = ContainerUtil.newArrayList(); } @Tag("run-configuration") static class RunConfigurationDescriptor { @Attribute("type") String myType; @Attribute("name") String myName; } }