2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * Created by IntelliJ IDEA.
23 package com.theoryinpractice.testng.configuration;
25 import com.intellij.diagnostic.logging.LogConfigurationPanel;
26 import com.intellij.execution.*;
27 import com.intellij.execution.configuration.EnvironmentVariablesComponent;
28 import com.intellij.execution.configurations.*;
29 import com.intellij.execution.junit.RefactoringListeners;
30 import com.intellij.execution.runners.ExecutionEnvironment;
31 import com.intellij.execution.testframework.SourceScope;
32 import com.intellij.execution.util.JavaParametersUtil;
33 import com.intellij.execution.util.ProgramParametersUtil;
34 import com.intellij.openapi.components.PathMacroManager;
35 import com.intellij.openapi.module.Module;
36 import com.intellij.openapi.options.SettingsEditor;
37 import com.intellij.openapi.options.SettingsEditorGroup;
38 import com.intellij.openapi.project.Project;
39 import com.intellij.openapi.util.DefaultJDOMExternalizer;
40 import com.intellij.openapi.util.InvalidDataException;
41 import com.intellij.openapi.util.WriteExternalException;
42 import com.intellij.psi.*;
43 import com.intellij.psi.search.GlobalSearchScope;
44 import com.intellij.refactoring.listeners.RefactoringElementAdapter;
45 import com.intellij.refactoring.listeners.RefactoringElementListener;
46 import com.intellij.refactoring.listeners.UndoRefactoringElementListener;
47 import com.theoryinpractice.testng.model.TestData;
48 import com.theoryinpractice.testng.model.TestType;
49 import com.theoryinpractice.testng.util.TestNGUtil;
50 import org.jdom.Element;
51 import org.jetbrains.annotations.NonNls;
52 import org.jetbrains.annotations.NotNull;
53 import org.jetbrains.annotations.Nullable;
54 import org.testng.xml.Parser;
58 public class TestNGConfiguration extends ModuleBasedConfiguration<JavaRunConfigurationModule>
59 implements CommonJavaRunConfigurationParameters, RefactoringListenerProvider {
60 @NonNls private static final String PATTERNS_EL_NAME = "patterns";
61 @NonNls private static final String PATTERN_EL_NAME = "pattern";
62 @NonNls private static final String TEST_CLASS_ATT_NAME = "testClass";
64 //private TestNGResultsContainer resultsContainer;
65 protected TestData data;
66 protected transient Project project;
67 public boolean ALTERNATIVE_JRE_PATH_ENABLED;
68 public String ALTERNATIVE_JRE_PATH;
70 private static final Object PARSE_LOCK = new Object();
72 public static final String DEFAULT_PACKAGE_NAME = ExecutionBundle.message("default.package.presentable.name");
73 public static final String DEFAULT_PACKAGE_CONFIGURATION_NAME = ExecutionBundle.message("default.package.configuration.name");
74 private final RefactoringListeners.Accessor<PsiPackage> myPackage = new RefactoringListeners.Accessor<PsiPackage>() {
75 public void setName(final String qualifiedName) {
76 final boolean generatedName = isGeneratedName();
77 data.PACKAGE_NAME = qualifiedName;
78 if (generatedName) setGeneratedName();
82 public PsiPackage getPsiElement() {
83 final String qualifiedName = data.getPackageName();
84 return qualifiedName != null ? JavaPsiFacade.getInstance(getProject()).findPackage(qualifiedName) : null;
87 public void setPsiElement(final PsiPackage psiPackage) {
88 setName(psiPackage.getQualifiedName());
92 private final RefactoringListeners.Accessor<PsiClass> myClass = new RefactoringListeners.Accessor<PsiClass>() {
93 public void setName(final String qualifiedName) {
94 final boolean generatedName = isGeneratedName();
95 data.MAIN_CLASS_NAME = qualifiedName;
96 if (generatedName) setGeneratedName();
100 public PsiClass getPsiElement() {
101 final String qualifiedName = data.getMainClassName();
102 return qualifiedName != null
103 ? JavaPsiFacade.getInstance(getProject()).findClass(qualifiedName, GlobalSearchScope.allScope(project))
107 public void setPsiElement(final PsiClass psiClass) {
108 setName(psiClass.getQualifiedName());
112 public TestNGConfiguration(String s, Project project, ConfigurationFactory factory) {
113 this(s, project, new TestData(), factory);
116 private TestNGConfiguration(String s, Project project, TestData data, ConfigurationFactory factory) {
117 super(s, new JavaRunConfigurationModule(project, false), factory);
119 this.project = project;
122 public RunProfileState getState(@NotNull final Executor executor, @NotNull final ExecutionEnvironment env) throws ExecutionException {
123 return new TestNGRunnableState(env, this);
126 public TestData getPersistantData() {
131 protected ModuleBasedConfiguration createInstance() {
133 return new TestNGConfiguration(getName(), getProject(), (TestData)data.clone(),
134 TestNGConfigurationType.getInstance().getConfigurationFactories()[0]);
136 catch (CloneNotSupportedException e) {
137 //can't happen right?
144 public Collection<Module> getValidModules() {
145 //TODO add handling for package
146 return JavaRunConfigurationModule.getModulesForClass(getProject(), data.getMainClassName());
150 public boolean isGeneratedName() {
151 return data.isGeneratedName(getName(), getConfigurationModule());
155 public String suggestedName() {
156 if (TestType.CLASS.getType().equals(data.TEST_OBJECT)) {
157 String shortName = JavaExecutionUtil.getShortClassName(data.MAIN_CLASS_NAME);
158 return ProgramRunnerUtil.shortenName(shortName, 0);
160 if (TestType.PACKAGE.getType().equals(data.TEST_OBJECT)) {
161 String s = getName();
162 if (!isGeneratedName()) return '\"' + s + '\"';
163 if (data.getPackageName().trim().length() > 0) {
164 return "Tests in \"" + data.getPackageName() + '\"';
170 if (TestType.METHOD.getType().equals(data.TEST_OBJECT)) {
171 return data.getMethodName() + "()";
173 if (TestType.SUITE.getType().equals(data.TEST_OBJECT)) {
174 return data.getSuiteName();
176 return data.getGroupName();
179 public void setVMParameters(String value) {
180 data.setVMParameters(value);
183 public String getVMParameters() {
184 return data.getVMParameters();
187 public void setProgramParameters(String value) {
188 data.setProgramParameters(value);
191 public String getProgramParameters() {
192 return data.getProgramParameters();
195 public void setWorkingDirectory(String value) {
196 data.setWorkingDirectory(value);
199 public String getWorkingDirectory() {
200 return data.getWorkingDirectory(project);
203 public void setEnvs(@NotNull Map<String, String> envs) {
208 public Map<String, String> getEnvs() {
209 return data.getEnvs();
212 public void setPassParentEnvs(boolean passParentEnvs) {
213 data.PASS_PARENT_ENVS = passParentEnvs;
216 public boolean isPassParentEnvs() {
217 return data.PASS_PARENT_ENVS;
220 public boolean isAlternativeJrePathEnabled() {
221 return ALTERNATIVE_JRE_PATH_ENABLED;
224 public void setAlternativeJrePathEnabled(boolean enabled) {
225 this.ALTERNATIVE_JRE_PATH_ENABLED = enabled;
228 public String getAlternativeJrePath() {
229 return ALTERNATIVE_JRE_PATH;
232 public void setAlternativeJrePath(String path) {
233 this.ALTERNATIVE_JRE_PATH = path;
236 public String getRunClass() {
237 return !data.TEST_OBJECT.equals(TestType.CLASS.getType()) && !data.TEST_OBJECT.equals(TestType.METHOD.getType()) ? null : data.getMainClassName();
240 public String getPackage() {
241 return !data.TEST_OBJECT.equals(TestType.PACKAGE.getType()) ? null : data.getPackageName();
244 public void setClassConfiguration(PsiClass psiclass) {
245 setModule(data.setMainClass(psiclass));
246 data.TEST_OBJECT = TestType.CLASS.getType();
250 public void setPackageConfiguration(Module module, PsiPackage pkg) {
251 data.setPackage(pkg);
253 data.TEST_OBJECT = TestType.PACKAGE.getType();
257 public void setMethodConfiguration(Location<PsiMethod> location) {
258 setModule(data.setTestMethod(location));
263 public void bePatternConfiguration(List<PsiClass> classes, PsiMethod method) {
264 Set<String> patterns = new HashSet<String>();
265 for (PsiClass pattern : classes) {
266 patterns.add(JavaExecutionUtil.getRuntimeQualifiedName(pattern));
268 data.setPatterns(patterns);
269 data.METHOD_NAME = method.getName();
270 data.TEST_OBJECT = TestType.PATTERN.getType();
274 public void setGeneratedName() {
275 setName(getGeneratedName());
278 public String getGeneratedName() {
279 return data.getGeneratedName(getConfigurationModule());
282 public SettingsEditor<? extends RunConfiguration> getConfigurationEditor() {
283 SettingsEditorGroup<TestNGConfiguration> group = new SettingsEditorGroup<TestNGConfiguration>();
284 group.addEditor(ExecutionBundle.message("run.configuration.configuration.tab.title"), new TestNGConfigurationEditor(getProject()));
285 JavaRunConfigurationExtensionManager.getInstance().appendEditors(this, group);
286 group.addEditor(ExecutionBundle.message("logs.tab.title"), new LogConfigurationPanel<TestNGConfiguration>());
291 public void checkConfiguration() throws RuntimeConfigurationException {
292 if (data.TEST_OBJECT.equals(TestType.CLASS.getType()) || data.TEST_OBJECT.equals(TestType.METHOD.getType())) {
293 final SourceScope scope = data.getScope().getSourceScope(this);
295 throw new RuntimeConfigurationException("Invalid scope specified");
297 PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(data.getMainClassName(), scope.getGlobalSearchScope());
298 if (psiClass == null) throw new RuntimeConfigurationException("Class '" + data.getMainClassName() + "' not found");
299 if (data.TEST_OBJECT.equals(TestType.METHOD.getType())) {
300 PsiMethod[] methods = psiClass.findMethodsByName(data.getMethodName(), true);
301 if (methods.length == 0) {
302 throw new RuntimeConfigurationException("Method '" + data.getMethodName() + "' not found");
304 for (PsiMethod method : methods) {
305 if (!method.hasModifierProperty(PsiModifier.PUBLIC)) {
306 throw new RuntimeConfigurationException("Non public method '" + data.getMethodName() + "'specified");
311 else if (data.TEST_OBJECT.equals(TestType.PACKAGE.getType())) {
312 PsiPackage psiPackage = JavaPsiFacade.getInstance(project).findPackage(data.getPackageName());
313 if (psiPackage == null) throw new RuntimeConfigurationException("Package '" + data.getMainClassName() + "' not found");
315 else if (data.TEST_OBJECT.equals(TestType.SUITE.getType())) {
317 final Parser parser = new Parser(data.getSuiteName());
318 parser.setLoadClasses(false);
319 synchronized (PARSE_LOCK) {
320 parser.parse();//try to parse suite.xml
323 catch (Exception e) {
324 throw new RuntimeConfigurationException("Unable to parse '" + data.getSuiteName() + "' specified");
326 } else if (data.TEST_OBJECT.equals(TestType.PATTERN.getType())) {
327 final Set<String> patterns = data.getPatterns();
328 if (patterns.isEmpty()) {
329 throw new RuntimeConfigurationWarning("No pattern selected");
331 final GlobalSearchScope searchScope = GlobalSearchScope.allScope(getProject());
332 for (String pattern : patterns) {
333 final PsiClass psiClass = JavaExecutionUtil.findMainClass(getProject(), pattern, searchScope);
334 if (psiClass == null) {
335 throw new RuntimeConfigurationWarning("Class " + pattern + " not found");
337 if (!TestNGUtil.hasTest(psiClass)) {
338 throw new RuntimeConfigurationWarning("Class " + pattern + " not a test");
342 JavaRunConfigurationExtensionManager.checkConfigurationIsValid(this);
343 ProgramParametersUtil.checkWorkingDirectoryExist(this, getProject(), getConfigurationModule().getModule());
344 JavaParametersUtil.checkAlternativeJRE(this);
345 //TODO add various checks here
349 public void readExternal(Element element) throws InvalidDataException {
350 PathMacroManager.getInstance(getProject()).expandPaths(element);
351 super.readExternal(element);
352 JavaRunConfigurationExtensionManager.getInstance().readExternal(this, element);
354 DefaultJDOMExternalizer.readExternal(this, element);
355 DefaultJDOMExternalizer.readExternal(getPersistantData(), element);
356 EnvironmentVariablesComponent.readExternal(element, getPersistantData().getEnvs());
358 Map<String, String> properties = getPersistantData().TEST_PROPERTIES;
360 Element propertiesElement = element.getChild("properties");
361 if (propertiesElement != null) {
362 List<Element> children = propertiesElement.getChildren("property");
363 for (Element property : children) {
364 properties.put(property.getAttributeValue("name"), property.getAttributeValue("value"));
368 List<String> listeners = getPersistantData().TEST_LISTENERS;
370 Element listenersElement = element.getChild("listeners");
371 if (listenersElement != null) {
372 List<Element> children = listenersElement.getChildren("listener");
373 for (Element listenerClassName : children) {
374 listeners.add(listenerClassName.getAttributeValue("class"));
377 final Element patternsElement = element.getChild(PATTERNS_EL_NAME);
378 if (patternsElement != null) {
379 final Set<String> tests = new LinkedHashSet<String>();
380 for (Object o : patternsElement.getChildren(PATTERN_EL_NAME)) {
381 Element patternElement = (Element)o;
382 tests.add(patternElement.getAttributeValue(TEST_CLASS_ATT_NAME));
384 getPersistantData().setPatterns(tests);
389 public void writeExternal(Element element) throws WriteExternalException {
390 super.writeExternal(element);
391 JavaRunConfigurationExtensionManager.getInstance().writeExternal(this, element);
392 writeModule(element);
393 DefaultJDOMExternalizer.writeExternal(this, element);
394 DefaultJDOMExternalizer.writeExternal(getPersistantData(), element);
395 EnvironmentVariablesComponent.writeExternal(element, getPersistantData().getEnvs());
397 Element propertiesElement = element.getChild("properties");
399 if (propertiesElement == null) {
400 propertiesElement = new Element("properties");
401 element.addContent(propertiesElement);
404 Map<String, String> properties = getPersistantData().TEST_PROPERTIES;
405 for (Map.Entry<String, String> entry : properties.entrySet()) {
406 Element property = new Element("property");
407 property.setAttribute("name", entry.getKey());
408 property.setAttribute("value", entry.getValue());
409 propertiesElement.addContent(property);
412 Element listenersElement = element.getChild("listeners");
413 if (listenersElement == null) {
414 listenersElement = new Element("listeners");
415 element.addContent(listenersElement);
418 List<String> listeners = getPersistantData().TEST_LISTENERS;
419 for (String listener : listeners) {
420 Element listenerElement = new Element("listener");
421 listenerElement.setAttribute("class", listener);
422 listenersElement.addContent(listenerElement);
424 final Set<String> patterns = getPersistantData().getPatterns();
425 if (!patterns.isEmpty()) {
426 final Element patternsElement = new Element(PATTERNS_EL_NAME);
427 for (String o : patterns) {
428 final Element patternElement = new Element(PATTERN_EL_NAME);
429 patternElement.setAttribute(TEST_CLASS_ATT_NAME, o);
430 patternsElement.addContent(patternElement);
432 element.addContent(patternsElement);
435 PathMacroManager.getInstance(getProject()).collapsePathsRecursively(element);
439 public RefactoringElementListener getRefactoringElementListener(final PsiElement element) {
440 if (data.TEST_OBJECT.equals(TestType.PACKAGE.getType())) {
441 if (!(element instanceof PsiPackage)) return null;
442 final RefactoringElementListener listener = RefactoringListeners.getListener((PsiPackage)element, myPackage);
443 return RunConfigurationExtension.wrapRefactoringElementListener(element, this, listener);
445 else if (data.TEST_OBJECT.equals(TestType.CLASS.getType())) {
446 if (!(element instanceof PsiClass)) return null;
447 final RefactoringElementListener listener = RefactoringListeners.getClassOrPackageListener(element, myClass);
448 return RunConfigurationExtension.wrapRefactoringElementListener(element, this, listener);
450 else if (data.TEST_OBJECT.equals(TestType.METHOD.getType())) {
451 if (!(element instanceof PsiMethod)) {
452 final RefactoringElementListener listener = RefactoringListeners.getClassOrPackageListener(element, myClass);
453 return RunConfigurationExtension.wrapRefactoringElementListener(element, this, listener);
455 final PsiMethod method = (PsiMethod)element;
456 if (!method.getName().equals(data.getMethodName())) return null;
457 if (!method.getContainingClass().equals(myClass.getPsiElement())) return null;
458 class Listener extends RefactoringElementAdapter implements UndoRefactoringElementListener {
459 public void elementRenamedOrMoved(@NotNull final PsiElement newElement) {
460 final boolean generatedName = isGeneratedName();
461 data.setTestMethod(PsiLocation.fromPsiElement((PsiMethod)newElement));
462 if (generatedName) setGeneratedName();
466 public void undoElementMovedOrRenamed(@NotNull PsiElement newElement, @NotNull String oldQualifiedName) {
467 final int methodIdx = oldQualifiedName.indexOf("#") + 1;
468 if (methodIdx <= 0 || methodIdx >= oldQualifiedName.length()) return;
469 final boolean generatedName = isGeneratedName();
470 data.METHOD_NAME = oldQualifiedName.substring(methodIdx);
471 if (generatedName) setGeneratedName();
474 return RunConfigurationExtension.wrapRefactoringElementListener(element, this, new Listener());
480 public boolean collectOutputFromProcessHandler() {