blob: 6bde0b4419dddb8666704dc28ef596dab857a989 [file] [log] [blame]
/*
* Copyright 2000-2009 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.execution.*;
import com.intellij.execution.configurations.RuntimeConfigurationException;
import com.intellij.execution.configurations.RuntimeConfigurationWarning;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.execution.junit2.ui.model.JUnitRunningModel;
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.ExecutionEnvironmentBuilder;
import com.intellij.execution.testframework.SourceScope;
import com.intellij.execution.testframework.TestSearchScope;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.impl.BackgroundableProcessIndicator;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PackageScope;
import com.intellij.refactoring.listeners.RefactoringElementListener;
import com.intellij.ui.HyperlinkAdapter;
import com.intellij.util.Function;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.event.HyperlinkEvent;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class TestPackage extends TestObject {
protected BackgroundableProcessIndicator mySearchForTestsIndicator;
protected ServerSocket myServerSocket;
private boolean myFoundTests = true;
public TestPackage(JUnitConfiguration configuration, ExecutionEnvironment environment) {
super(configuration, environment);
}
@Override
public SourceScope getSourceScope() {
final JUnitConfiguration.Data data = myConfiguration.getPersistentData();
return data.getScope().getSourceScope(myConfiguration);
}
@Override
protected JUnitProcessHandler createHandler(Executor executor) throws ExecutionException {
final JUnitProcessHandler handler = super.createHandler(executor);
final SearchForTestsTask[] tasks = new SearchForTestsTask[1];
handler.addProcessListener(new ProcessAdapter() {
@Override
public void startNotified(ProcessEvent event) {
super.startNotified(event);
tasks[0] = (SearchForTestsTask)findTests();
}
@Override
public void processTerminated(ProcessEvent event) {
handler.removeProcessListener(this);
if (mySearchForTestsIndicator != null && !mySearchForTestsIndicator.isCanceled()) {
if (tasks[0] != null) {
tasks[0].finish();
}
}
}
});
return handler;
}
public Task findTests() {
final JUnitConfiguration.Data data = myConfiguration.getPersistentData();
final TestClassFilter filter;
try {
filter = getClassFilter(data);
}
catch (CantRunException ignored) {
//should not happen
return null;
}
return findTestsWithProgress(new FindCallback() {
@Override
public void found(@NotNull final Collection<PsiClass> classes, final boolean isJunit4) {
try {
addClassesListToJavaParameters(classes, new Function<PsiElement, String>() {
@Override
@Nullable
public String fun(PsiElement element) {
if (element instanceof PsiClass) {
return JavaExecutionUtil.getRuntimeQualifiedName((PsiClass)element);
}
else if (element instanceof PsiMethod) {
PsiMethod method = (PsiMethod)element;
return JavaExecutionUtil.getRuntimeQualifiedName(method.getContainingClass()) + "," + method.getName();
}
else {
return null;
}
}
}, getPackageName(data), false, isJunit4);
}
catch (CantRunException ignored) {
//can't be here
}
}
}, filter);
}
protected String getPackageName(JUnitConfiguration.Data data) throws CantRunException {
return getPackage(data).getQualifiedName();
}
@Override
protected void initialize() throws ExecutionException {
super.initialize();
final JUnitConfiguration.Data data = myConfiguration.getPersistentData();
getClassFilter(data);//check if junit found
configureClasspath();
try {
createTempFiles();
}
catch (IOException e) {
LOG.error(e);
}
try {
myServerSocket = new ServerSocket(0, 0, InetAddress.getByName("127.0.0.1"));
myJavaParameters.getProgramParametersList().add("-socket" + myServerSocket.getLocalPort());
}
catch (IOException e) {
LOG.error(e);
}
}
protected void configureClasspath() throws ExecutionException {
final ExecutionException[] exception = new ExecutionException[1];
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
try {
myConfiguration.configureClasspath(myJavaParameters);
}
catch (CantRunException e) {
exception[0] = e;
}
}
});
if (exception[0] != null) {
throw exception[0];
}
}
protected TestClassFilter getClassFilter(final JUnitConfiguration.Data data) throws CantRunException {
Module module = myConfiguration.getConfigurationModule().getModule();
if (myConfiguration.getPersistentData().getScope() == TestSearchScope.WHOLE_PROJECT){
module = null;
}
final TestClassFilter classFilter = TestClassFilter.create(getSourceScope(), module);
return classFilter.intersectionWith(filterScope(data));
}
protected GlobalSearchScope filterScope(final JUnitConfiguration.Data data) throws CantRunException {
final PsiPackage aPackage = getPackage(data);
return PackageScope.packageScope(aPackage, true);
}
protected PsiPackage getPackage(JUnitConfiguration.Data data) throws CantRunException {
final Project project = myConfiguration.getProject();
final String packageName = data.getPackageName();
final PsiManager psiManager = PsiManager.getInstance(project);
final PsiPackage aPackage = JavaPsiFacade.getInstance(psiManager.getProject()).findPackage(packageName);
if (aPackage == null) throw CantRunException.packageNotFound(packageName);
return aPackage;
}
@Override
public String suggestActionName() {
final JUnitConfiguration.Data data = myConfiguration.getPersistentData();
if (data.getPackageName().trim().length() > 0) {
return ExecutionBundle.message("test.in.scope.presentable.text", data.getPackageName());
}
return ExecutionBundle.message("all.tests.scope.presentable.text");
}
@Override
public RefactoringElementListener getListener(final PsiElement element, final JUnitConfiguration configuration) {
if (!(element instanceof PsiPackage)) return null;
return RefactoringListeners.getListener((PsiPackage)element, configuration.myPackage);
}
@Override
public boolean isConfiguredByElement(final JUnitConfiguration configuration,
PsiClass testClass,
PsiMethod testMethod,
PsiPackage testPackage,
PsiDirectory testDir) {
return testPackage != null
&& Comparing.equal(testPackage.getQualifiedName(), configuration.getPersistentData().getPackageName());
}
@Override
public void checkConfiguration() throws RuntimeConfigurationException {
super.checkConfiguration();
final String packageName = myConfiguration.getPersistentData().getPackageName();
final PsiPackage aPackage =
JavaPsiFacade.getInstance(myConfiguration.getProject()).findPackage(packageName);
if (aPackage == null) {
throw new RuntimeConfigurationWarning(ExecutionBundle.message("package.does.not.exist.error.message", packageName));
}
if (getSourceScope() == null) {
myConfiguration.getConfigurationModule().checkForWarning();
}
}
private MySearchForTestsTask findTestsWithProgress(final FindCallback callback, final TestClassFilter classFilter) {
if (isSyncSearch()) {
THashSet<PsiClass> classes = new THashSet<PsiClass>();
boolean isJUnit4 = ConfigurationUtil.findAllTestClasses(classFilter, classes);
callback.found(classes, isJUnit4);
return null;
}
final THashSet<PsiClass> classes = new THashSet<PsiClass>();
final boolean[] isJunit4 = new boolean[1];
final MySearchForTestsTask task =
new MySearchForTestsTask(classFilter, isJunit4, classes, callback);
mySearchForTestsIndicator = new BackgroundableProcessIndicator(task);
ProgressManager.getInstance().runProcessWithProgressAsynchronously(task, mySearchForTestsIndicator);
return task;
}
private static boolean isSyncSearch() {
return ApplicationManager.getApplication().isUnitTestMode();
}
@Override
protected void notifyByBalloon(JUnitRunningModel model, boolean started, final JUnitConsoleProperties consoleProperties) {
if (myFoundTests) {
super.notifyByBalloon(model, started, consoleProperties);
}
else {
final String packageName = myConfiguration.getPackage();
if (packageName == null) return;
final Project project = myConfiguration.getProject();
final PsiPackage aPackage = JavaPsiFacade.getInstance(project).findPackage(packageName);
if (aPackage == null) return;
final Module module = myConfiguration.getConfigurationModule().getModule();
if (module == null) return;
final Set<Module> modulesWithPackage = new HashSet<Module>();
final PsiDirectory[] directories = aPackage.getDirectories();
for (PsiDirectory directory : directories) {
final Module currentModule = ModuleUtilCore.findModuleForFile(directory.getVirtualFile(), project);
if (module != currentModule && currentModule != null) {
modulesWithPackage.add(currentModule);
}
}
if (!modulesWithPackage.isEmpty()) {
final String testRunDebugId = consoleProperties.isDebug() ? ToolWindowId.DEBUG : ToolWindowId.RUN;
final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(project);
final Function<Module, String> moduleNameRef = new Function<Module, String>() {
@Override
public String fun(Module module) {
final String moduleName = module.getName();
return "<a href=\"" + moduleName + "\">" + moduleName + "</a>";
}
};
String message = "Tests were not found in module \"" + module.getName() + "\".\n" +
"Use ";
if (modulesWithPackage.size() == 1) {
message += "module \"" + moduleNameRef.fun(modulesWithPackage.iterator().next()) + "\" ";
}
else {
message += "one of\n" + StringUtil.join(modulesWithPackage, moduleNameRef, "\n") + "\n";
}
message += "instead";
toolWindowManager.notifyByBalloon(testRunDebugId, MessageType.WARNING, message, null,
new ResetConfigurationModuleAdapter(project, consoleProperties, toolWindowManager,
testRunDebugId));
}
}
}
public interface FindCallback {
/**
* Invoked in dispatch thread
*/
void found(@NotNull Collection<PsiClass> classes, final boolean isJunit4);
}
protected abstract class SearchForTestsTask extends Task.Backgroundable {
protected Socket mySocket;
public SearchForTestsTask(@Nullable final Project project, @NotNull final String title, final boolean canBeCancelled) {
super(project, title, canBeCancelled);
}
protected void finish() {
DataOutputStream os = null;
try {
if (mySocket == null || mySocket.isClosed()) return;
os = new DataOutputStream(mySocket.getOutputStream());
os.writeBoolean(true);
}
catch (Throwable e) {
LOG.info(e);
}
finally {
try {
if (os != null) os.close();
}
catch (Throwable e) {
LOG.info(e);
}
try {
if (!myServerSocket.isClosed()) {
myServerSocket.close();
}
}
catch (Throwable e) {
LOG.info(e);
}
}
}
@Override
public void onCancel() {
finish();
}
}
private class MySearchForTestsTask extends SearchForTestsTask {
private final TestClassFilter myClassFilter;
private final boolean[] myJunit4;
private final THashSet<PsiClass> myClasses;
private final FindCallback myCallback;
public MySearchForTestsTask(TestClassFilter classFilter, boolean[] junit4, THashSet<PsiClass> classes, FindCallback callback) {
super(classFilter.getProject(), ExecutionBundle.message("seaching.test.progress.title"), true);
myClassFilter = classFilter;
myJunit4 = junit4;
myClasses = classes;
myCallback = callback;
}
@Override
public void run(@NotNull ProgressIndicator indicator) {
try {
mySocket = myServerSocket.accept();
DumbService.getInstance(myProject).repeatUntilPassesInSmartMode(new Runnable() {
@Override
public void run() {
myClasses.clear();
myJunit4[0] = ConfigurationUtil.findAllTestClasses(myClassFilter, myClasses);
}
});
myFoundTests = !myClasses.isEmpty();
}
catch (IOException e) {
LOG.info(e);
}
catch (Throwable e) {
LOG.error(e);
}
}
@Override
public void onSuccess() {
myCallback.found(myClasses, myJunit4[0]);
finish();
}
}
private class ResetConfigurationModuleAdapter extends HyperlinkAdapter {
private final Project myProject;
private final JUnitConsoleProperties myConsoleProperties;
private final ToolWindowManager myToolWindowManager;
private final String myTestRunDebugId;
public ResetConfigurationModuleAdapter(final Project project,
final JUnitConsoleProperties consoleProperties,
final ToolWindowManager toolWindowManager,
final String testRunDebugId) {
myProject = project;
myConsoleProperties = consoleProperties;
myToolWindowManager = toolWindowManager;
myTestRunDebugId = testRunDebugId;
}
@Override
protected void hyperlinkActivated(HyperlinkEvent e) {
final Module moduleByName = ModuleManager.getInstance(myProject).findModuleByName(e.getDescription());
if (moduleByName != null) {
myConfiguration.getConfigurationModule().setModule(moduleByName);
try {
Executor executor = myConsoleProperties.isDebug() ? DefaultDebugExecutor.getDebugExecutorInstance()
: DefaultRunExecutor.getRunExecutorInstance();
ExecutionEnvironmentBuilder.create(myProject, executor, myConfiguration).contentToReuse(null).buildAndExecute();
Balloon balloon = myToolWindowManager.getToolWindowBalloon(myTestRunDebugId);
if (balloon != null) {
balloon.hide();
}
}
catch (ExecutionException e1) {
LOG.error(e1);
}
}
}
}
}