diff --git a/plugins/testng/src/META-INF/plugin.xml b/plugins/testng/src/META-INF/plugin.xml
index 8e67df9c8566..d946f530892a 100644
--- a/plugins/testng/src/META-INF/plugin.xml
+++ b/plugins/testng/src/META-INF/plugin.xml
@@ -8,6 +8,7 @@
+
diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java
index 7b3b1e9a78a7..0be68346398a 100644
--- a/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java
+++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/SearchingForTestsTask.java
@@ -371,6 +371,30 @@ public class SearchingForTestsTask extends Task.Backgroundable {
}
}
}
+ else if (data.TEST_OBJECT.equals(TestType.PATTERN.getType())) {
+ for (final String className : data.getPatterns()) {
+ final PsiClass psiClass = ApplicationManager.getApplication().runReadAction(new Computable() {
+ @Nullable
+ @Override
+ public PsiClass compute() {
+ return ClassUtil.findPsiClass(psiManager, className.replace('/', '.'), null, true, getSearchScope());
+ }
+ });
+ if (psiClass == null) {
+ throw new CantRunException("Class " + className + " not found");
+ }
+ if (ApplicationManager.getApplication().runReadAction(new Computable() {
+ @Override
+ public Boolean compute() {
+ return TestNGUtil.hasTest(psiClass);
+ }
+ })) {
+ calculateDependencies(null, classes, psiClass);
+ } else {
+ throw new CantRunException("No tests found in class " + className);
+ }
+ }
+ }
}
private Map buildTestParameters() {
diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java
index 726629aad1df..324b23f9d776 100644
--- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java
+++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfiguration.java
@@ -44,17 +44,21 @@ import com.intellij.refactoring.listeners.RefactoringElementListener;
import com.intellij.refactoring.listeners.UndoRefactoringElementListener;
import com.theoryinpractice.testng.model.TestData;
import com.theoryinpractice.testng.model.TestType;
+import com.theoryinpractice.testng.util.TestNGUtil;
import org.jdom.Element;
+import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.testng.xml.Parser;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
public class TestNGConfiguration extends ModuleBasedConfiguration
implements CommonJavaRunConfigurationParameters, RefactoringListenerProvider {
+ @NonNls private static final String PATTERNS_EL_NAME = "patterns";
+ @NonNls private static final String PATTERN_EL_NAME = "pattern";
+ @NonNls private static final String TEST_CLASS_ATT_NAME = "testClass";
+
//private TestNGResultsContainer resultsContainer;
protected TestData data;
protected transient Project project;
@@ -305,6 +309,21 @@ public class TestNGConfiguration extends ModuleBasedConfiguration patterns = data.getPatterns();
+ if (patterns.isEmpty()) {
+ throw new RuntimeConfigurationWarning("No pattern selected");
+ }
+ final GlobalSearchScope searchScope = GlobalSearchScope.allScope(getProject());
+ for (String pattern : patterns) {
+ final PsiClass psiClass = JavaExecutionUtil.findMainClass(getProject(), pattern, searchScope);
+ if (psiClass == null) {
+ throw new RuntimeConfigurationWarning("Class " + pattern + " not found");
+ }
+ if (!TestNGUtil.hasTest(psiClass)) {
+ throw new RuntimeConfigurationWarning("Class " + pattern + " not a test");
+ }
+ }
}
JavaRunConfigurationExtensionManager.checkConfigurationIsValid(this);
//TODO add various checks here
@@ -339,6 +358,15 @@ public class TestNGConfiguration extends ModuleBasedConfiguration tests = new LinkedHashSet();
+ for (Object o : patternsElement.getChildren(PATTERN_EL_NAME)) {
+ Element patternElement = (Element)o;
+ tests.add(patternElement.getAttributeValue(TEST_CLASS_ATT_NAME));
+ }
+ getPersistantData().setPatterns(tests);
+ }
}
@Override
@@ -377,6 +405,16 @@ public class TestNGConfiguration extends ModuleBasedConfiguration patterns = getPersistantData().getPatterns();
+ if (!patterns.isEmpty()) {
+ final Element patternsElement = new Element(PATTERNS_EL_NAME);
+ for (String o : patterns) {
+ final Element patternElement = new Element(PATTERN_EL_NAME);
+ patternElement.setAttribute(TEST_CLASS_ATT_NAME, o);
+ patternsElement.addContent(patternElement);
+ }
+ element.addContent(patternsElement);
+ }
PathMacroManager.getInstance(getProject()).collapsePathsRecursively(element);
}
diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.form b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.form
index 8418d00197fe..25853fe593e8 100644
--- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.form
+++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.form
@@ -1,6 +1,6 @@
diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.java
index 0296bfddb5f0..fb8c5ef9b5e2 100644
--- a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.java
+++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGConfigurationEditor.java
@@ -39,18 +39,18 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.ui.ComponentWithBrowseButton;
-import com.intellij.openapi.ui.LabeledComponent;
-import com.intellij.openapi.ui.TextFieldWithBrowseButton;
+import com.intellij.openapi.ui.*;
+import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaCodeFragment;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.search.GlobalSearchScope;
-import com.intellij.ui.PanelWithAnchor;
import com.intellij.ui.EditorTextFieldWithBrowseButton;
+import com.intellij.ui.PanelWithAnchor;
import com.intellij.ui.table.TableView;
+import com.intellij.util.PlatformIcons;
import com.intellij.util.TextFieldCompletionProvider;
import com.theoryinpractice.testng.MessageInfoException;
import com.theoryinpractice.testng.configuration.browser.*;
@@ -62,6 +62,7 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
+import javax.swing.text.Document;
import javax.swing.text.PlainDocument;
import java.awt.*;
import java.awt.event.ActionEvent;
@@ -84,6 +85,7 @@ public class TestNGConfigurationEditor extends SettingsEditor methodField;
private LabeledComponent packageField;
@@ -105,6 +107,8 @@ public class TestNGConfigurationEditor extends SettingsEditor myPattern;
+ TextFieldWithBrowseButton myPatternTextField;
private final CommonJavaParametersPanel commonJavaParameters = new CommonJavaParametersPanel();
private ArrayList propertiesList;
private TestNGListenersTableModel listenerModel;
@@ -114,7 +118,20 @@ public class TestNGConfigurationEditor extends SettingsEditor 0 ? "||" : "") + psiClass.getQualifiedName());
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ showDialog();
+ }
+ }};
model = new TestNGConfigurationModel(project);
model.setListener(this);
createView();
@@ -125,7 +142,22 @@ public class TestNGConfigurationEditor extends SettingsEditor 1) {
+ return null;
+ }
+
PsiClass psiClass = null;
PsiElement element = location.getPsiElement();
while (element != null) {
diff --git a/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGPatternConfigurationProducer.java b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGPatternConfigurationProducer.java
new file mode 100644
index 000000000000..9294974ccd79
--- /dev/null
+++ b/plugins/testng/src/com/theoryinpractice/testng/configuration/TestNGPatternConfigurationProducer.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2000-2012 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.
+ */
+
+/*
+ * User: anna
+ * Date: 23-May-2007
+ */
+package com.theoryinpractice.testng.configuration;
+
+import com.intellij.execution.JavaRunConfigurationExtensionManager;
+import com.intellij.execution.Location;
+import com.intellij.execution.RunnerAndConfigurationSettings;
+import com.intellij.execution.actions.ConfigurationContext;
+import com.intellij.execution.junit.JUnitUtil;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.actionSystem.LangDataKeys;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Comparing;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassOwner;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.theoryinpractice.testng.model.TestData;
+import com.theoryinpractice.testng.model.TestType;
+import com.theoryinpractice.testng.util.TestNGUtil;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+public class TestNGPatternConfigurationProducer extends TestNGConfigurationProducer{
+
+ private PsiElement[] myElements;
+
+
+ public int compareTo(Object o) {
+ return PREFERED;
+ }
+
+ protected RunnerAndConfigurationSettings createConfigurationByElement(final Location location, final ConfigurationContext context) {
+ final Project project = location.getProject();
+ final LinkedHashSet classes = new LinkedHashSet();
+ myElements = collectPatternElements(context, classes);
+ if (classes.size() <= 1) return null;
+ RunnerAndConfigurationSettings settings = cloneTemplateConfiguration(project, context);
+ final TestNGConfiguration configuration = (TestNGConfiguration)settings.getConfiguration();
+ final TestData data = configuration.getPersistantData();
+ data.getPatterns().addAll(classes);
+ data.TEST_OBJECT = TestType.PATTERN.getType();
+ data.setScope(setupPackageConfiguration(context, project, configuration, data.getScope()));
+ configuration.setGeneratedName();
+ JavaRunConfigurationExtensionManager.getInstance().extendCreatedConfiguration(configuration, location);
+ return settings;
+ }
+
+ static Set collectTestClasses(PsiElement[] psiElements) {
+ final Set foundClasses = new LinkedHashSet();
+ for (PsiElement psiElement : psiElements) {
+ if (psiElement instanceof PsiClassOwner) {
+ final PsiClass[] classes = ((PsiClassOwner)psiElement).getClasses();
+ for (PsiClass aClass : classes) {
+ if (JUnitUtil.isTestClass(aClass)) {
+ foundClasses.add(aClass);
+ }
+ }
+ } else if (psiElement instanceof PsiClass) {
+ if (TestNGUtil.hasTest((PsiClass)psiElement)) {
+ foundClasses.add((PsiClass)psiElement);
+ }
+ }
+ }
+ return foundClasses;
+ }
+
+ private static PsiElement[] collectPatternElements(ConfigurationContext context, LinkedHashSet classes) {
+ final DataContext dataContext = context.getDataContext();
+ PsiElement[] elements = LangDataKeys.PSI_ELEMENT_ARRAY.getData(dataContext);
+ if (elements != null) {
+ for (PsiClass psiClass : collectTestClasses(elements)) {
+ classes.add(psiClass.getQualifiedName());
+ }
+ return elements;
+ } else {
+ final PsiFile file = LangDataKeys.PSI_FILE.getData(dataContext);
+ if (file instanceof PsiClassOwner) {
+ for (PsiClass psiClass : collectTestClasses(((PsiClassOwner)file).getClasses())) {
+ classes.add(psiClass.getQualifiedName());
+ }
+ return new PsiElement[]{file};
+ }
+ }
+ return null;
+ }
+
+ public PsiElement getSourceElement() {
+ return myElements[0];
+ }
+
+ @Override
+ protected RunnerAndConfigurationSettings findExistingByElement(@NotNull Location location,
+ @NotNull RunnerAndConfigurationSettings[] existingConfigurations,
+ ConfigurationContext context) {
+ final LinkedHashSet classes = new LinkedHashSet();
+ collectPatternElements(context, classes);
+ for (RunnerAndConfigurationSettings existingConfiguration : existingConfigurations) {
+ final TestNGConfiguration unitConfiguration = (TestNGConfiguration)existingConfiguration.getConfiguration();
+ final String type = unitConfiguration.getPersistantData().TEST_OBJECT;
+ if (Comparing.equal(type, TestType.PATTERN.getType())) {
+ if (Comparing.equal(classes, unitConfiguration.getPersistantData().getPatterns())) {
+ return existingConfiguration;
+ }
+ }
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java
index 113e32c2e253..00394391257e 100644
--- a/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java
+++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestData.java
@@ -24,6 +24,7 @@ import com.intellij.execution.testframework.TestSearchScope;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
@@ -55,6 +56,7 @@ public class TestData implements Cloneable
public List TEST_LISTENERS = new ArrayList();
public boolean USE_DEFAULT_REPORTERS = false;
public String PROPERTIES_FILE;
+ private Set myPatterns = new HashSet();
public TestData() {
TEST_OBJECT = TestType.CLASS.getType();
@@ -132,6 +134,7 @@ public class TestData implements Cloneable
&& Comparing.equal(OUTPUT_DIRECTORY, data.OUTPUT_DIRECTORY)
&& Comparing.equal(VM_PARAMETERS, data.VM_PARAMETERS)
&& Comparing.equal(PARAMETERS, data.PARAMETERS)
+ && Comparing.equal(myPatterns, data.myPatterns)
&& USE_DEFAULT_REPORTERS == data.USE_DEFAULT_REPORTERS;
}
}
@@ -147,7 +150,8 @@ public class TestData implements Cloneable
Comparing.hashcode(OUTPUT_DIRECTORY) ^
Comparing.hashcode(VM_PARAMETERS) ^
Comparing.hashcode(PARAMETERS) ^
- Comparing.hashcode(USE_DEFAULT_REPORTERS);
+ Comparing.hashcode(USE_DEFAULT_REPORTERS) ^
+ Comparing.hashcode(myPatterns);
}
@Override
@@ -163,6 +167,8 @@ public class TestData implements Cloneable
data.USE_DEFAULT_REPORTERS = USE_DEFAULT_REPORTERS;
data.ENVS = new LinkedHashMap(ENVS);
+ data.myPatterns = new HashSet();
+ data.myPatterns.addAll(myPatterns);
data.setScope(getScope());
return data;
}
@@ -187,6 +193,11 @@ public class TestData implements Cloneable
return getSuiteName();
}
else {
+ if (TestType.PATTERN.getType().equals(TEST_OBJECT)) {
+ final int size = myPatterns.size();
+ if (size == 0) return "Temp suite";
+ return StringUtil.getShortName(myPatterns.iterator().next()) + (size > 1 ? " and " + (size - 1) + " more" : "");
+ }
return name;
}
}
@@ -242,4 +253,12 @@ public class TestData implements Cloneable
public void setEnvs(final Map envs) {
ENVS = envs;
}
+
+ public Set getPatterns() {
+ return myPatterns;
+ }
+
+ public void setPatterns(Set set) {
+ myPatterns = set;
+ }
}
diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGConfigurationModel.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGConfigurationModel.java
index 0aa261a23bbf..6291fbfd6e46 100644
--- a/plugins/testng/src/com/theoryinpractice/testng/model/TestNGConfigurationModel.java
+++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestNGConfigurationModel.java
@@ -17,26 +17,19 @@ package com.theoryinpractice.testng.model;
import com.intellij.execution.JavaExecutionUtil;
import com.intellij.execution.junit.JUnitUtil;
-import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
-import com.intellij.openapi.editor.RangeMarker;
-import com.intellij.openapi.editor.event.DocumentListener;
-import com.intellij.openapi.editor.markup.MarkupModel;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Key;
-import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
import com.theoryinpractice.testng.configuration.TestNGConfiguration;
import com.theoryinpractice.testng.configuration.TestNGConfigurationEditor;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;
-import java.beans.PropertyChangeListener;
+import java.util.LinkedHashSet;
/**
* @author Hani Suleiman Date: Jul 21, 2005 Time: 1:20:14 PM
@@ -47,7 +40,7 @@ public class TestNGConfigurationModel
private TestNGConfigurationEditor editor;
private TestType type;
- private final Object[] typeDocuments = new Object[5];
+ private final Object[] typeDocuments = new Object[6];
private final Document propertiesFileDocument = new PlainDocument();
private final Document outputDirectoryDocument = new PlainDocument();
private final Project project;
@@ -60,7 +53,7 @@ public class TestNGConfigurationModel
this.project = project;
}
- public void setDocument(int type, com.intellij.openapi.editor.Document doc) {
+ public void setDocument(int type, Object doc) {
typeDocuments[type] = doc;
}
@@ -138,6 +131,16 @@ public class TestNGConfigurationModel
data.MAIN_CLASS_NAME = "";
data.METHOD_NAME = "";
}
+ else if (TestType.PATTERN == type) {
+ final LinkedHashSet set = new LinkedHashSet();
+ final String[] patterns = getText(TestType.PATTERN).split("\\|\\|");
+ for (String pattern : patterns) {
+ if (pattern.length() > 0) {
+ set.add(pattern);
+ }
+ }
+ data.setPatterns(set);
+ }
try {
data.PROPERTIES_FILE = propertiesFileDocument.getText(0, propertiesFileDocument.getLength());
@@ -177,6 +180,7 @@ public class TestNGConfigurationModel
setTypeValue(TestType.METHOD, data.getMethodName());
setTypeValue(TestType.GROUP, data.getGroupName());
setTypeValue(TestType.SUITE, data.getSuiteName());
+ setTypeValue(TestType.PATTERN, StringUtil.join(data.getPatterns(), "||"));
setDocumentText(propertiesFileDocument, data.getPropertiesFile());
setDocumentText(outputDirectoryDocument, data.getOutputDirectory());
diff --git a/plugins/testng/src/com/theoryinpractice/testng/model/TestType.java b/plugins/testng/src/com/theoryinpractice/testng/model/TestType.java
index 05c57398acf4..788c6cd58641 100644
--- a/plugins/testng/src/com/theoryinpractice/testng/model/TestType.java
+++ b/plugins/testng/src/com/theoryinpractice/testng/model/TestType.java
@@ -30,6 +30,7 @@ public class TestType
public static final TestType METHOD = new TestType("METHOD", 2);
public static final TestType GROUP = new TestType("GROUP", 3);
public static final TestType SUITE = new TestType("SUITE", 4);
+ public static final TestType PATTERN = new TestType("PATTERN", 5);
public final String type;
public final int value;
@@ -73,6 +74,9 @@ public class TestType
{
return SUITE;
}
+ if (PATTERN.type.equals(type)) {
+ return PATTERN;
+ }
throw new IllegalArgumentException("Invalid type requested " + type);
}
}
\ No newline at end of file