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 @@
- + @@ -32,10 +32,10 @@ - + - + @@ -71,7 +71,7 @@ - + @@ -126,7 +126,7 @@ - + @@ -142,11 +142,22 @@ + + + + + + + + + + + - + @@ -390,15 +401,24 @@ + + + + + + + + - - + + + 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