junit: run test method of an abstract class in inheritors (IDEA-53111)
authoranna <Anna.Kozlova@jetbrains.com>
Tue, 17 Jan 2012 13:12:41 +0000 (14:12 +0100)
committeranna <Anna.Kozlova@jetbrains.com>
Tue, 17 Jan 2012 15:41:51 +0000 (16:41 +0100)
platform/lang-api/src/com/intellij/execution/junit/RuntimeConfigurationProducer.java
platform/lang-impl/src/com/intellij/execution/actions/BaseRunConfigurationAction.java
plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java
plugins/junit/src/com/intellij/execution/junit/JUnitConfigurationProducer.java
plugins/junit/src/com/intellij/execution/junit/TestMethod.java
plugins/junit/src/com/intellij/execution/junit/TestMethodConfigurationProducer.java
plugins/junit/src/com/intellij/execution/junit/TestsPattern.java
plugins/junit/src/com/intellij/execution/junit2/configuration/JUnitConfigurable.java
plugins/junit_rt/src/com/intellij/junit4/JUnit4TestRunnerUtil.java

index b38472a37a5bf30fa1bffde848b92a3c0db511f4..40cf1f7910190877bb7e87222f2a9e48d4ef6610 100644 (file)
@@ -128,6 +128,10 @@ public abstract class RuntimeConfigurationProducer implements Comparable, Clonea
   public ConfigurationType getConfigurationType() {
     return myConfigurationFactory.getType();
   }
+  
+  public void perform(ConfigurationContext context, Runnable performRunnable){
+    performRunnable.run();
+  }
 
   public static <T extends RuntimeConfigurationProducer> T getInstance(final Class<T> aClass) {
     final RuntimeConfigurationProducer[] configurationProducers = Extensions.getExtensions(RUNTIME_CONFIGURATION_PRODUCER);
index 4081008b010d54b119cefd897e20292c275a72f2..798b31326271db53fc7b746552eba2fe60359ae6 100644 (file)
@@ -168,7 +168,12 @@ public abstract class BaseRunConfigurationAction extends ActionGroup {
   private void perform(final RuntimeConfigurationProducer producer, final ConfigurationContext context) {
     final RunnerAndConfigurationSettings configuration = context.updateConfiguration(producer);
     if (configuration != null) {
-      perform(context);
+      producer.perform(context, new Runnable() {
+        @Override
+        public void run() {
+          perform(context);
+        }
+      });
     }
   }
 
index f83067678aa6ad948d60750e9c125f7a87c39649..8fbc94bbf542fb3591d67af1aeee153bb2dd941c 100644 (file)
@@ -362,6 +362,16 @@ public class JUnitConfiguration extends ModuleBasedConfiguration<JavaRunConfigur
     return false;
   }
 
+  public void bePatternConfiguration(List<PsiClass> classes, PsiMethod method) {
+    Set<String> patterns = new HashSet<String>();
+    for (PsiClass pattern : classes) {
+      patterns.add(pattern.getQualifiedName());
+    }
+    myData.setPatterns(patterns);
+    myData.METHOD_NAME = method.getName();
+    myData.TEST_OBJECT = TEST_PATTERN;
+  }
+
   public static class Data implements Cloneable {
     public String PACKAGE_NAME;
     private String DIR_NAME;
index a78e9ce33e3e445a5f9e9c0171f4faf3f50d37bb..9330be77d79d8a804d4f3f696dd9d33ecff4fbac 100644 (file)
@@ -59,7 +59,7 @@ public abstract class JUnitConfigurationProducer extends JavaRuntimeConfiguratio
     location = JavaExecutionUtil.stepIntoSingleClass(location);
     final PsiElement element = location.getPsiElement();
     final PsiClass testClass = JUnitUtil.getTestClass(element);
-    final PsiMethod testMethod = JUnitUtil.getTestMethod(element);
+    final PsiMethod testMethod = JUnitUtil.getTestMethod(element, false);
     final PsiPackage testPackage;
     if (element instanceof PsiPackage) {
       testPackage = (PsiPackage)element;
index c3e384b9418c845bf6ce800364201c4a51229e2d..ab93fa1232b9e6fc94122fad3900422cbd1c2489 100644 (file)
@@ -21,10 +21,7 @@ import com.intellij.execution.*;
 import com.intellij.execution.configurations.*;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Comparing;
-import com.intellij.psi.PsiClass;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiMethod;
-import com.intellij.psi.PsiPackage;
+import com.intellij.psi.*;
 import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.refactoring.listeners.RefactoringElementAdapter;
 import com.intellij.refactoring.listeners.RefactoringElementListener;
@@ -108,9 +105,13 @@ class TestMethod extends TestObject {
                                        PsiClass testClass,
                                        PsiMethod testMethod,
                                        PsiPackage testPackage) {
-    if (testClass == null) return false;
     if (testMethod == null) return false;
+    final PsiClass containingClass = testMethod.getContainingClass();
+    if (testClass == null && (containingClass == null || !containingClass.hasModifierProperty(PsiModifier.ABSTRACT))) return false;
     final JUnitConfiguration.Data data = configuration.getPersistentData();
+    if (containingClass != null && containingClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
+      return Comparing.equal(testMethod.getName(), data.getMethodName());
+    }
     return
       Comparing.equal(JavaExecutionUtil.getRuntimeQualifiedName(testClass), data.getMainClassName()) &&
       Comparing.equal(testMethod.getName(), data.getMethodName());
index dabf13b9c65a5118e91325ca81eb53ab50bd87bf..30b2837ac00719b536f12961770fc9ec9edd1ae0 100644 (file)
@@ -18,14 +18,29 @@ package com.intellij.execution.junit;
 
 import com.intellij.execution.JavaRunConfigurationExtensionManager;
 import com.intellij.execution.Location;
+import com.intellij.execution.PsiLocation;
 import com.intellij.execution.RunnerAndConfigurationSettings;
 import com.intellij.execution.actions.ConfigurationContext;
+import com.intellij.execution.junit2.info.MethodLocation;
+import com.intellij.ide.util.PsiClassListCellRenderer;
 import com.intellij.openapi.module.Module;
+import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.popup.JBPopupFactory;
+import com.intellij.psi.PsiClass;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiModifier;
+import com.intellij.psi.search.searches.ClassInheritorsSearch;
+import com.intellij.ui.ColoredListCellRenderer;
+import com.intellij.ui.components.JBList;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.Processor;
 
+import javax.swing.*;
+import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.List;
 
 public class TestMethodConfigurationProducer extends JUnitConfigurationProducer {
   private Location<PsiMethod> myMethodLocation;
@@ -52,9 +67,117 @@ public class TestMethodConfigurationProducer extends JUnitConfigurationProducer
   private static Location<PsiMethod> getTestMethod(final Location<?> location) {
     for (Iterator<Location<PsiMethod>> iterator = location.getAncestors(PsiMethod.class, false); iterator.hasNext();) {
       final Location<PsiMethod> methodLocation = iterator.next();
-      if (JUnitUtil.isTestMethod(methodLocation)) return methodLocation;
+      if (JUnitUtil.isTestMethod(methodLocation, false)) return methodLocation;
     }
     return null;
   }
+
+  @Override
+  public void perform(final ConfigurationContext context, final Runnable performRunnable) {
+    final PsiMethod psiMethod = myMethodLocation.getPsiElement();
+    final PsiClass containingClass = psiMethod.getContainingClass();
+    if (containingClass != null && containingClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
+      final List<PsiClass> classes = new ArrayList<PsiClass>();
+      if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
+        @Override
+        public void run() {
+          ClassInheritorsSearch.search(containingClass).forEach(new Processor<PsiClass>() {
+            @Override
+            public boolean process(PsiClass aClass) {
+              if (!aClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
+                classes.add(aClass);
+              }
+              return true;
+            }
+          });
+        }
+      }, "Search for " + containingClass.getQualifiedName() + " inheritors", true, containingClass.getProject())) {
+        return;
+      }
+
+      if (classes.size() == 1) {
+        runForClass(classes.get(0), psiMethod, context, performRunnable);
+        return;
+      }
+      //suggest to run all inherited tests 
+      classes.add(0, null);
+      final JBList list = new JBList(classes);
+      list.setCellRenderer(new PsiClassListCellRenderer() {
+        @Override
+        protected boolean customizeNonPsiElementLeftRenderer(ColoredListCellRenderer renderer,
+                                                             JList list,
+                                                             Object value,
+                                                             int index,
+                                                             boolean selected,
+                                                             boolean hasFocus) {
+          if (value == null) {
+            renderer.append("All");
+            return true;
+          }
+          return super.customizeNonPsiElementLeftRenderer(renderer, list, value, index, selected, hasFocus);
+        }
+      });
+      JBPopupFactory.getInstance().createListPopupBuilder(list)
+        .setTitle("Choose executable classes to run " + psiMethod.getName())
+        .setMovable(false)
+        .setResizable(false)
+        .setRequestFocus(true)
+        .setItemChoosenCallback(new Runnable() {
+          public void run() {
+            final Object[] values = list.getSelectedValues();
+            if (values == null) return;
+            runMethod(values, psiMethod, context, performRunnable, classes);
+          }
+        }).createPopup().showInBestPositionFor(context.getDataContext());
+      return;
+    }
+    super.perform(context, performRunnable);
+  }
+
+  private static void runMethod(Object[] values,
+                                PsiMethod psiMethod,
+                                ConfigurationContext context,
+                                Runnable performRunnable,
+                                List<PsiClass> classes) {
+    if (values.length == 1) {
+      final Object value = values[0];
+      if (value instanceof PsiClass) {
+        runForClass((PsiClass)value, psiMethod, context, performRunnable);
+      }
+      else {
+        runForClasses(classes, psiMethod, context, performRunnable);
+      }
+      return;
+    }
+    if (ArrayUtil.contains(null, values)) {
+      runForClasses(classes, psiMethod, context, performRunnable);
+    }
+    else {
+      final List<PsiClass> selectedClasses = new ArrayList<PsiClass>();
+      for (Object value : values) {
+        if (value instanceof PsiClass) {
+          selectedClasses.add((PsiClass)value);
+        }
+      }
+      runForClasses(selectedClasses, psiMethod, context, performRunnable);
+    }
+  }
+
+  private static void runForClasses(List<PsiClass> classes, PsiMethod method, ConfigurationContext context, Runnable performRunnable) {
+    classes.remove(null);
+    ((JUnitConfiguration)context.getConfiguration().getConfiguration()).bePatternConfiguration(classes, method);
+    performRunnable.run();
+  }
+
+  private static void runForClass(final PsiClass aClass,
+                                  final PsiMethod psiMethod, 
+                                  final ConfigurationContext context,
+                                  final Runnable performRunnable) {
+    final Project project = psiMethod.getProject();
+    ((JUnitConfiguration)context.getConfiguration().getConfiguration()).beMethodConfiguration(
+      new MethodLocation(project, psiMethod,
+                         new PsiLocation<PsiClass>(project, aClass)));
+    performRunnable.run();
+  }
 }
 
index 64e2317e51932b5b50a98bad6f4635bcdc08d797..6263fcea49267f3092edc04eb48efbbffae62714 100644 (file)
@@ -27,12 +27,15 @@ import com.intellij.execution.util.JavaParametersUtil;
 import com.intellij.openapi.module.Module;
 import com.intellij.openapi.module.ModuleUtil;
 import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.psi.PsiClass;
 import com.intellij.psi.PsiElement;
 import com.intellij.psi.PsiMethod;
 import com.intellij.psi.PsiPackage;
 import com.intellij.psi.search.GlobalSearchScope;
 import com.intellij.refactoring.listeners.RefactoringElementListener;
+import com.intellij.util.Function;
 import com.intellij.util.FunctionUtil;
 import org.jetbrains.annotations.Nullable;
 
@@ -78,7 +81,12 @@ public class TestsPattern extends TestObject {
     } else {
       JavaParametersUtil.configureProject(project, myJavaParameters, JavaParameters.JDK_AND_CLASSES_AND_TESTS, jreHome);
     }
-    addClassesListToJavaParameters(classNames, FunctionUtil.<String>id(), "", true, isJUnit4);
+    addClassesListToJavaParameters(classNames, StringUtil.isEmpty(data.METHOD_NAME) ? FunctionUtil.<String>id() : new Function<String, String>() {
+      @Override
+      public String fun(String className) {
+        return className + "," + data.METHOD_NAME;
+      }
+    }, "", true, isJUnit4);
   }
 
   @Override
@@ -100,6 +108,9 @@ public class TestsPattern extends TestObject {
                                        PsiClass testClass,
                                        PsiMethod testMethod,
                                        PsiPackage testPackage) {
+    if (testMethod != null && Comparing.strEqual(testMethod.getName(), configuration.getPersistentData().METHOD_NAME)) {
+      return true;
+    }
     return false;
   }
 
index 6aee88ac6ffd51d49c19f8322d413c5f3baf0bb3..5f615b0d8e5ba65f77b7492cc2283404b0fb50ae 100644 (file)
@@ -307,7 +307,7 @@ public class JUnitConfigurable extends SettingsEditor<JUnitConfiguration> implem
       myPattern.setVisible(true);
       myDir.setVisible(false);
       myClass.setVisible(false);
-      myMethod.setVisible(false);
+      myMethod.setVisible(true);
       myForkCb.setEnabled(true);
       myForkCb.setModel(new DefaultComboBoxModel(FORK_MODE_ALL));
       myForkCb.setSelectedItem(selectedItem);
index 1788a1afde4fd9af7c706b9a360a07abb1b7bb6d..95cf1181ab5d2f9d135136ef24bd3fac263209f7 100644 (file)
@@ -103,7 +103,7 @@ public class JUnit4TestRunnerUtil {
               }
 
               public String describe() {
-                return "Failed tests";
+                return "Tests";
               }
             });
           }