blob: e1b74d5452ef7d2143264a766c1d16c362814c55 [file] [log] [blame]
/*
* Copyright 2000-2011 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.junit;
import com.intellij.ExtensionPoints;
import com.intellij.execution.*;
import com.intellij.execution.configurations.*;
import com.intellij.execution.junit2.TestProxy;
import com.intellij.execution.junit2.segments.DeferredActionsQueue;
import com.intellij.execution.junit2.segments.DeferredActionsQueueImpl;
import com.intellij.execution.junit2.segments.DispatchListener;
import com.intellij.execution.junit2.segments.Extractor;
import com.intellij.execution.junit2.ui.JUnitTreeConsoleView;
import com.intellij.execution.junit2.ui.TestsPacketsReceiver;
import com.intellij.execution.junit2.ui.actions.RerunFailedTestsAction;
import com.intellij.execution.junit2.ui.model.CompletionEvent;
import com.intellij.execution.junit2.ui.model.JUnitRunningModel;
import com.intellij.execution.junit2.ui.model.RootTestInfo;
import com.intellij.execution.junit2.ui.properties.JUnitConsoleProperties;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.testframework.*;
import com.intellij.execution.testframework.sm.SMTestRunnerConnectionUtil;
import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties;
import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.execution.util.JavaParametersUtil;
import com.intellij.execution.util.ProgramParametersUtil;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.projectRoots.JavaSdkType;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.projectRoots.ex.JavaSdkUtil;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Getter;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.psi.*;
import com.intellij.refactoring.listeners.RefactoringElementListener;
import com.intellij.rt.execution.junit.IDEAJUnitListener;
import com.intellij.rt.execution.junit.JUnitStarter;
import com.intellij.util.Function;
import com.intellij.util.PathUtil;
import com.intellij.util.ui.UIUtil;
import jetbrains.buildServer.messages.serviceMessages.ServiceMessageTypes;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.model.serialization.PathMacroUtil;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
public abstract class TestObject implements JavaCommandLine {
protected static final Logger LOG = Logger.getInstance(TestObject.class);
private static final String MESSAGE = ExecutionBundle.message("configuration.not.speficied.message");
@NonNls private static final String JUNIT_TEST_FRAMEWORK_NAME = "JUnit";
protected JavaParameters myJavaParameters;
protected final JUnitConfiguration myConfiguration;
protected final ExecutionEnvironment myEnvironment;
protected File myTempFile = null;
protected File myWorkingDirsFile = null;
public File myListenersFile;
public static TestObject fromString(final String id,
final JUnitConfiguration configuration,
@NotNull ExecutionEnvironment environment) {
if (JUnitConfiguration.TEST_METHOD.equals(id)) {
return new TestMethod(configuration, environment);
}
if (JUnitConfiguration.TEST_CLASS.equals(id)) {
return new TestClass(configuration, environment);
}
if (JUnitConfiguration.TEST_PACKAGE.equals(id)){
return new TestPackage(configuration, environment);
}
if (JUnitConfiguration.TEST_DIRECTORY.equals(id)) {
return new TestDirectory(configuration, environment);
}
if (JUnitConfiguration.TEST_CATEGORY.equals(id)) {
return new TestCategory(configuration, environment);
}
if (JUnitConfiguration.TEST_PATTERN.equals(id)) {
return new TestsPattern(configuration, environment);
}
return NOT_CONFIGURED;
}
public Module[] getModulesToCompile() {
final SourceScope sourceScope = getSourceScope();
return sourceScope != null ? sourceScope.getModulesToCompile() : Module.EMPTY_ARRAY;
}
protected TestObject(JUnitConfiguration configuration, ExecutionEnvironment environment) {
myConfiguration = configuration;
myEnvironment = environment;
}
public abstract String suggestActionName();
public RunnerSettings getRunnerSettings() {
return myEnvironment.getRunnerSettings();
}
public abstract RefactoringElementListener getListener(PsiElement element, JUnitConfiguration configuration);
public abstract boolean isConfiguredByElement(JUnitConfiguration configuration,
PsiClass testClass,
PsiMethod testMethod,
PsiPackage testPackage,
PsiDirectory testDir);
protected void configureModule(final JavaParameters parameters, final RunConfigurationModule configurationModule, final String mainClassName)
throws CantRunException {
int classPathType = JavaParametersUtil.getClasspathType(configurationModule, mainClassName, true);
JavaParametersUtil.configureModule(configurationModule, parameters, classPathType,
myConfiguration.isAlternativeJrePathEnabled() ? myConfiguration.getAlternativeJrePath() : null);
}
private static final TestObject NOT_CONFIGURED = new TestObject(null, null) {
@Override
public RefactoringElementListener getListener(final PsiElement element, final JUnitConfiguration configuration) {
return null;
}
@Override
public String suggestActionName() {
throw new RuntimeException(String.valueOf(myConfiguration));
}
@Override
public boolean isConfiguredByElement(final JUnitConfiguration configuration,
PsiClass testClass,
PsiMethod testMethod,
PsiPackage testPackage,
PsiDirectory testDir) {
return false;
}
@Override
public void checkConfiguration() throws RuntimeConfigurationException {
throw new RuntimeConfigurationError(MESSAGE);
}
@Override
public JavaParameters getJavaParameters() throws ExecutionException {
throw new ExecutionException(MESSAGE);
}
@Override
protected void initialize() throws ExecutionException {
throw new ExecutionException(MESSAGE);
}
};
public void checkConfiguration() throws RuntimeConfigurationException{
JavaParametersUtil.checkAlternativeJRE(myConfiguration);
ProgramParametersUtil.checkWorkingDirectoryExist(myConfiguration, myConfiguration.getProject(), myConfiguration.getConfigurationModule().getModule());
}
public SourceScope getSourceScope() {
return SourceScope.modulesWithDependencies(myConfiguration.getModules());
}
protected void initialize() throws ExecutionException {
String parameters = myConfiguration.getProgramParameters();
myConfiguration.getPersistentData().setProgramParameters(null);
try {
JavaParametersUtil.configureConfiguration(myJavaParameters, myConfiguration);
}
finally {
myConfiguration.getPersistentData().setProgramParameters(parameters);
}
myJavaParameters.setMainClass(JUnitConfiguration.JUNIT_START_CLASS);
final Module module = myConfiguration.getConfigurationModule().getModule();
if (myJavaParameters.getJdk() == null){
myJavaParameters.setJdk(module != null
? ModuleRootManager.getInstance(module).getSdk()
: ProjectRootManager.getInstance(myEnvironment.getProject()).getProjectSdk());
}
configureAdditionalClasspath(myJavaParameters);
myJavaParameters.getProgramParametersList().add(JUnitStarter.IDE_VERSION + JUnitStarter.VERSION);
if (!StringUtil.isEmptyOrSpaces(parameters)) {
myJavaParameters.getProgramParametersList().add("@name" + parameters);
}
for (RunConfigurationExtension ext : Extensions.getExtensions(RunConfigurationExtension.EP_NAME)) {
ext.updateJavaParameters(myConfiguration, myJavaParameters, getRunnerSettings());
}
final Object[] listeners = Extensions.getExtensions(IDEAJUnitListener.EP_NAME);
final StringBuilder buf = new StringBuilder();
for (final Object listener : listeners) {
boolean enabled = true;
for (RunConfigurationExtension ext : Extensions.getExtensions(RunConfigurationExtension.EP_NAME)) {
if (ext.isListenerDisabled(myConfiguration, listener, getRunnerSettings())) {
enabled = false;
break;
}
}
if (enabled) {
final Class classListener = listener.getClass();
buf.append(classListener.getName()).append("\n");
myJavaParameters.getClassPath().add(PathUtil.getJarPathForClass(classListener));
}
}
if (buf.length() > 0) {
try {
myListenersFile = FileUtil.createTempFile("junit_listeners_", "");
myListenersFile.deleteOnExit();
myJavaParameters.getProgramParametersList().add("@@" + myListenersFile.getPath());
FileUtil.writeToFile(myListenersFile, buf.toString().getBytes(CharsetToolkit.UTF8_CHARSET));
}
catch (IOException e) {
LOG.error(e);
}
}
}
private void configureAdditionalClasspath(JavaParameters javaParameters) {
javaParameters.getClassPath().add(JavaSdkUtil.getIdeaRtJarPath());
javaParameters.getClassPath().add(PathUtil.getJarPathForClass(JUnitStarter.class));
if (Registry.is("junit_sm_runner")) {
javaParameters.getClassPath().add(PathUtil.getJarPathForClass(ServiceMessageTypes.class));
}
}
@Override
public JavaParameters getJavaParameters() throws ExecutionException {
if (myJavaParameters == null) {
myJavaParameters = new JavaParameters();
initialize();
final Module module = myConfiguration.getConfigurationModule().getModule();
final Object[] patchers = Extensions.getExtensions(ExtensionPoints.JUNIT_PATCHER);
for (Object patcher : patchers) {
((JUnitPatcher)patcher).patchJavaParameters(module, myJavaParameters);
}
}
return myJavaParameters;
}
@Override
public ExecutionResult execute(final Executor executor, @NotNull final ProgramRunner runner) throws ExecutionException {
final boolean smRunner = Registry.is("junit_sm_runner");
if (smRunner) {
myJavaParameters.getVMParametersList().add("-Didea.junit.sm_runner");
}
final JUnitProcessHandler handler = createHandler(executor);
final RunnerSettings runnerSettings = getRunnerSettings();
JavaRunConfigurationExtensionManager.getInstance().attachExtensionsToProcess(myConfiguration, handler, runnerSettings);
if (smRunner) {
return useSmRunner(executor, handler);
}
final TestProxy unboundOutputRoot = new TestProxy(new RootTestInfo());
final JUnitConsoleProperties consoleProperties = new JUnitConsoleProperties(myConfiguration, executor);
final JUnitTreeConsoleView consoleView = new JUnitTreeConsoleView(consoleProperties, myEnvironment, unboundOutputRoot);
consoleView.initUI();
consoleView.attachToProcess(handler);
unboundOutputRoot.setPrinter(consoleView.getPrinter());
Disposer.register(consoleView, unboundOutputRoot);
final TestsPacketsReceiver packetsReceiver = new TestsPacketsReceiver(consoleView, unboundOutputRoot) {
@Override
public synchronized void notifyStart(TestProxy root) {
if (!isRunning()) return;
super.notifyStart(root);
unboundOutputRoot.addChild(root);
if (myConfiguration.isSaveOutputToFile()) {
unboundOutputRoot.setOutputFilePath(myConfiguration.getOutputFilePath());
}
final JUnitRunningModel model = getModel();
if (model != null) {
handler.getOut().setDispatchListener(model.getNotifier());
Disposer.register(model, new Disposable() {
@Override
public void dispose() {
handler.getOut().setDispatchListener(DispatchListener.DEAF);
}
});
consoleView.attachToModel(model);
}
}
};
Disposer.register(consoleView, packetsReceiver);
final DeferredActionsQueue queue = new DeferredActionsQueueImpl();
handler.getOut().setPacketDispatcher(packetsReceiver, queue);
handler.getErr().setPacketDispatcher(packetsReceiver, queue);
handler.addProcessListener(new ProcessAdapter() {
private boolean myStarted = false;
@Override
public void startNotified(ProcessEvent event) {
myStarted = true;
}
@Override
public void processTerminated(ProcessEvent event) {
handler.removeProcessListener(this);
if (myTempFile != null) {
FileUtil.delete(myTempFile);
}
if (myListenersFile != null) {
FileUtil.delete(myListenersFile);
}
final Runnable runnable = new Runnable() {
@Override
public void run() {
unboundOutputRoot.flush();
packetsReceiver.checkTerminated();
final JUnitRunningModel model = packetsReceiver.getModel();
notifyByBalloon(model, myStarted, consoleProperties);
}
};
handler.getOut().addRequest(runnable, queue);
}
@Override
public void onTextAvailable(final ProcessEvent event, final Key outputType) {
final String text = event.getText();
final ConsoleViewContentType consoleViewType = ConsoleViewContentType.getConsoleViewType(outputType);
final Printable printable = new Printable() {
@Override
public void printOn(final Printer printer) {
printer.print(text, consoleViewType);
}
};
final Extractor extractor;
if (consoleViewType == ConsoleViewContentType.ERROR_OUTPUT ||
consoleViewType == ConsoleViewContentType.SYSTEM_OUTPUT) {
extractor = handler.getErr();
}
else {
extractor = handler.getOut();
}
extractor.getEventsDispatcher().processOutput(printable);
}
});
final RerunFailedTestsAction rerunFailedTestsAction = new RerunFailedTestsAction(consoleView, consoleProperties);
rerunFailedTestsAction.setModelProvider(new Getter<TestFrameworkRunningModel>() {
@Override
public TestFrameworkRunningModel get() {
return packetsReceiver.getModel();
}
});
final DefaultExecutionResult result = new DefaultExecutionResult(consoleView, handler);
result.setRestartActions(rerunFailedTestsAction);
return result;
}
private ExecutionResult useSmRunner(Executor executor, JUnitProcessHandler handler) {
TestConsoleProperties testConsoleProperties = new SMTRunnerConsoleProperties(myConfiguration, JUNIT_TEST_FRAMEWORK_NAME, executor);
testConsoleProperties.setIfUndefined(TestConsoleProperties.HIDE_PASSED_TESTS, false);
final ConsoleView consoleView = SMTestRunnerConnectionUtil.createConsoleWithCustomLocator(
JUNIT_TEST_FRAMEWORK_NAME,
testConsoleProperties,
myEnvironment, null);
consoleView.attachToProcess(handler);
RerunFailedTestsAction rerunFailedTestsAction = new RerunFailedTestsAction(consoleView, testConsoleProperties);
rerunFailedTestsAction.setModelProvider(new Getter<TestFrameworkRunningModel>() {
@Override
public TestFrameworkRunningModel get() {
return ((SMTRunnerConsoleView)consoleView).getResultsViewer();
}
});
final DefaultExecutionResult result = new DefaultExecutionResult(consoleView, handler);
result.setRestartActions(rerunFailedTestsAction);
return result;
}
protected void notifyByBalloon(JUnitRunningModel model, boolean started, JUnitConsoleProperties consoleProperties) {
String comment;
if (model != null) {
final CompletionEvent done = model.getProgress().getDone();
comment = done != null ? done.getComment() : null;
}
else {
comment = null;
}
TestsUIUtil.notifyByBalloon(myEnvironment.getProject(), started, model != null ? model.getRoot() : null, consoleProperties, comment);
}
protected JUnitProcessHandler createHandler(Executor executor) throws ExecutionException {
appendForkInfo(executor);
return JUnitProcessHandler.runCommandLine(CommandLineBuilder.createFromJavaParameters(myJavaParameters, myEnvironment.getProject(), true));
}
private boolean forkPerModule() {
final String workingDirectory = myConfiguration.getWorkingDirectory();
return JUnitConfiguration.TEST_PACKAGE.equals(myConfiguration.getPersistentData().TEST_OBJECT) &&
myConfiguration.getPersistentData().getScope() != TestSearchScope.SINGLE_MODULE &&
("$" + PathMacroUtil.MODULE_DIR_MACRO_NAME + "$").equals(workingDirectory);
}
private void appendForkInfo(Executor executor) throws ExecutionException {
final String forkMode = myConfiguration.getForkMode();
if (Comparing.strEqual(forkMode, "none")) {
if (forkPerModule()) {
if (getRunnerSettings() != null) {
final String actionName = UIUtil.removeMnemonic(executor.getStartActionText());
throw new CantRunException("'" + actionName + "' is disabled when per-module working directory is configured.<br/>" +
"Please specify single working directory, or change test scope to single module.");
}
} else {
return;
}
} else if (getRunnerSettings() != null) {
final String actionName = executor.getActionName();
throw new CantRunException(actionName + " is disabled in fork mode.<br/>Please change fork mode to &lt;none&gt; to " + actionName.toLowerCase(Locale.ENGLISH) + ".");
}
final JavaParameters javaParameters = getJavaParameters();
final Sdk jdk = javaParameters.getJdk();
if (jdk == null) {
throw new ExecutionException(ExecutionBundle.message("run.configuration.error.no.jdk.specified"));
}
try {
final File tempFile = FileUtil.createTempFile("command.line", "", true);
final PrintWriter writer = new PrintWriter(tempFile, CharsetToolkit.UTF8);
try {
writer.println(((JavaSdkType)jdk.getSdkType()).getVMExecutablePath(jdk));
for (String vmParameter : javaParameters.getVMParametersList().getList()) {
writer.println(vmParameter);
}
writer.println("-classpath");
writer.println(javaParameters.getClassPath().getPathsString());
}
finally {
writer.close();
}
myJavaParameters.getProgramParametersList().add("@@@" + forkMode + ',' + tempFile.getAbsolutePath());
}
catch (Exception e) {
LOG.error(e);
}
}
protected <T> void addClassesListToJavaParameters(Collection<? extends T> elements, Function<T, String> nameFunction, String packageName,
boolean createTempFile,
boolean junit4) throws CantRunException {
try {
if (createTempFile) {
createTempFiles();
}
final Map<Module, List<String>> perModule = forkPerModule() ? new TreeMap<Module, List<String>>(new Comparator<Module>() {
@Override
public int compare(Module o1, Module o2) {
return StringUtil.compare(o1.getName(), o2.getName(), true);
}
}) : null;
final List<String> testNames = new ArrayList<String>();
for (final T element : elements) {
final String name = nameFunction.fun(element);
if (name == null) {
LOG.error("invalid element " + element);
return;
}
if (perModule != null && element instanceof PsiElement) {
final Module module = ModuleUtilCore.findModuleForPsiElement((PsiElement)element);
if (module != null) {
List<String> list = perModule.get(module);
if (list == null) {
list = new ArrayList<String>();
perModule.put(module, list);
}
list.add(name);
}
}
else {
testNames.add(name);
}
}
if (perModule != null) {
for (List<String> perModuleClasses : perModule.values()) {
Collections.sort(perModuleClasses);
testNames.addAll(perModuleClasses);
}
}
else {
Collections.sort(testNames); //sort tests in FQN order
}
final JUnitConfiguration.Data data = myConfiguration.getPersistentData();
final String category = data.TEST_OBJECT == JUnitConfiguration.TEST_CATEGORY ? data.getCategory() : "";
JUnitStarter.printClassesList(testNames, packageName, category, myTempFile);
if (perModule != null && perModule.size() > 1) {
final PrintWriter wWriter = new PrintWriter(myWorkingDirsFile, CharsetToolkit.UTF8);
try {
wWriter.println(packageName);
for (Module module : perModule.keySet()) {
final String moduleDir = PathMacroUtil.getModuleDir(module.getModuleFilePath());
wWriter.println(moduleDir);
final JavaParameters parameters = new JavaParameters();
JavaParametersUtil.configureModule(module, parameters, JavaParameters.JDK_AND_CLASSES_AND_TESTS,
myConfiguration.isAlternativeJrePathEnabled() ? myConfiguration.getAlternativeJrePath() : null);
configureAdditionalClasspath(parameters);
wWriter.println(parameters.getClassPath().getPathsString());
final List<String> classNames = perModule.get(module);
wWriter.println(classNames.size());
for (String className : classNames) {
wWriter.println(className);
}
}
} finally {
wWriter.close();
}
}
}
catch (IOException e) {
LOG.error(e);
}
}
protected void createTempFiles() throws IOException {
myTempFile = FileUtil.createTempFile("idea_junit", ".tmp");
myTempFile.deleteOnExit();
myJavaParameters.getProgramParametersList().add("@" + myTempFile.getAbsolutePath());
myWorkingDirsFile = FileUtil.createTempFile("idea_working_dirs_junit", ".tmp");
myWorkingDirsFile.deleteOnExit();
myJavaParameters.getProgramParametersList().add("@w@" + myWorkingDirsFile.getAbsolutePath());
}
public void clear() {
myJavaParameters = null;
}
}