92a67174157c5aa66e2e1aa0a4013bb4a325e8ef
[idea/community.git] / plugins / testng / src / com / theoryinpractice / testng / configuration / TestNGConfiguration.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /*
18  * Created by IntelliJ IDEA.
19  * User: amrk
20  * Date: Jul 2, 2005
21  * Time: 12:16:02 AM
22  */
23 package com.theoryinpractice.testng.configuration;
24
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;
55
56 import java.util.*;
57
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";
63   
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;
69   
70   private static final Object PARSE_LOCK = new Object();
71
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();
79     }
80
81     @Nullable
82     public PsiPackage getPsiElement() {
83       final String qualifiedName = data.getPackageName();
84       return qualifiedName != null ? JavaPsiFacade.getInstance(getProject()).findPackage(qualifiedName) : null;
85     }
86
87     public void setPsiElement(final PsiPackage psiPackage) {
88       setName(psiPackage.getQualifiedName());
89     }
90   };
91
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();
97     }
98
99     @Nullable
100     public PsiClass getPsiElement() {
101       final String qualifiedName = data.getMainClassName();
102       return qualifiedName != null
103              ? JavaPsiFacade.getInstance(getProject()).findClass(qualifiedName, GlobalSearchScope.allScope(project))
104              : null;
105     }
106
107     public void setPsiElement(final PsiClass psiClass) {
108       setName(psiClass.getQualifiedName());
109     }
110   };
111
112   public TestNGConfiguration(String s, Project project, ConfigurationFactory factory) {
113     this(s, project, new TestData(), factory);
114   }
115
116   private TestNGConfiguration(String s, Project project, TestData data, ConfigurationFactory factory) {
117     super(s, new JavaRunConfigurationModule(project, false), factory);
118     this.data = data;
119     this.project = project;
120   }
121
122   public RunProfileState getState(@NotNull final Executor executor, @NotNull final ExecutionEnvironment env) throws ExecutionException {
123     return new TestNGRunnableState(env, this);
124   }
125
126   public TestData getPersistantData() {
127     return data;
128   }
129
130   @Override
131   protected ModuleBasedConfiguration createInstance() {
132     try {
133       return new TestNGConfiguration(getName(), getProject(), (TestData)data.clone(),
134                                      TestNGConfigurationType.getInstance().getConfigurationFactories()[0]);
135     }
136     catch (CloneNotSupportedException e) {
137       //can't happen right?
138       e.printStackTrace();
139     }
140     return null;
141   }
142
143   @Override
144   public Collection<Module> getValidModules() {
145     //TODO add handling for package
146     return JavaRunConfigurationModule.getModulesForClass(getProject(), data.getMainClassName());
147   }
148
149   @Override
150   public boolean isGeneratedName() {
151     return data.isGeneratedName(getName(), getConfigurationModule());
152   }
153
154   @Override
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);
159     }
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() + '\"';
165       }
166       else {
167         return "All Tests";
168       }
169     }
170     if (TestType.METHOD.getType().equals(data.TEST_OBJECT)) {
171       return data.getMethodName() + "()";
172     }
173     if (TestType.SUITE.getType().equals(data.TEST_OBJECT)) {
174       return data.getSuiteName();
175     }
176     return data.getGroupName();
177   }
178
179   public void setVMParameters(String value) {
180     data.setVMParameters(value);
181   }
182
183   public String getVMParameters() {
184     return data.getVMParameters();
185   }
186
187   public void setProgramParameters(String value) {
188     data.setProgramParameters(value);
189   }
190
191   public String getProgramParameters() {
192     return data.getProgramParameters();
193   }
194
195   public void setWorkingDirectory(String value) {
196     data.setWorkingDirectory(value);
197   }
198
199   public String getWorkingDirectory() {
200     return data.getWorkingDirectory(project);
201   }
202
203   public void setEnvs(@NotNull Map<String, String> envs) {
204     data.setEnvs(envs);
205   }
206
207   @NotNull
208   public Map<String, String> getEnvs() {
209     return data.getEnvs();
210   }
211
212   public void setPassParentEnvs(boolean passParentEnvs) {
213     data.PASS_PARENT_ENVS = passParentEnvs;
214   }
215
216   public boolean isPassParentEnvs() {
217     return data.PASS_PARENT_ENVS;
218   }
219
220   public boolean isAlternativeJrePathEnabled() {
221      return ALTERNATIVE_JRE_PATH_ENABLED;
222    }
223
224    public void setAlternativeJrePathEnabled(boolean enabled) {
225      this.ALTERNATIVE_JRE_PATH_ENABLED = enabled;
226    }
227
228    public String getAlternativeJrePath() {
229      return ALTERNATIVE_JRE_PATH;
230    }
231
232    public void setAlternativeJrePath(String path) {
233      this.ALTERNATIVE_JRE_PATH = path;
234    }
235
236   public String getRunClass() {
237     return !data.TEST_OBJECT.equals(TestType.CLASS.getType()) && !data.TEST_OBJECT.equals(TestType.METHOD.getType()) ? null : data.getMainClassName();
238   }
239
240   public String getPackage() {
241     return !data.TEST_OBJECT.equals(TestType.PACKAGE.getType()) ? null : data.getPackageName();
242   }
243
244   public void setClassConfiguration(PsiClass psiclass) {
245     setModule(data.setMainClass(psiclass));
246     data.TEST_OBJECT = TestType.CLASS.getType();
247     setGeneratedName();
248   }
249
250   public void setPackageConfiguration(Module module, PsiPackage pkg) {
251     data.setPackage(pkg);
252     setModule(module);
253     data.TEST_OBJECT = TestType.PACKAGE.getType();
254     setGeneratedName();
255   }
256
257   public void setMethodConfiguration(Location<PsiMethod> location) {
258     setModule(data.setTestMethod(location));
259     setGeneratedName();
260   }
261   
262   
263   public void bePatternConfiguration(List<PsiClass> classes, PsiMethod method) {
264     data.TEST_OBJECT = TestType.PATTERN.getType();
265     data.METHOD_NAME = method.getName();
266     Set<String> patterns = new HashSet<String>();
267     for (PsiClass pattern : classes) {
268       patterns.add(JavaExecutionUtil.getRuntimeQualifiedName(pattern) + "," + data.METHOD_NAME);
269     }
270     data.setPatterns(patterns);
271     setGeneratedName();
272   }
273
274   public void setGeneratedName() {
275     setName(getGeneratedName());
276   }
277
278   public String getGeneratedName() {
279     return data.getGeneratedName(getConfigurationModule());
280   }
281
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>());
287     return group;
288   }
289
290   @Override
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);
294       if (scope == null) {
295         throw new RuntimeConfigurationException("Invalid scope specified");
296       }
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");
303         }
304         for (PsiMethod method : methods) {
305           if (!method.hasModifierProperty(PsiModifier.PUBLIC)) {
306             throw new RuntimeConfigurationException("Non public method '" + data.getMethodName() + "'specified");
307           }
308         }
309       }
310     }
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");
314     }
315     else if (data.TEST_OBJECT.equals(TestType.SUITE.getType())) {
316       try {
317         final Parser parser = new Parser(data.getSuiteName());
318         parser.setLoadClasses(false);
319         synchronized (PARSE_LOCK) {
320           parser.parse();//try to parse suite.xml
321         }
322       }
323       catch (Exception e) {
324         throw new RuntimeConfigurationException("Unable to parse '" + data.getSuiteName() + "' specified");
325       }
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");
330       }
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");
336         }
337         if (!TestNGUtil.hasTest(psiClass)) {
338           throw new RuntimeConfigurationWarning("Class " + pattern + " not a test");
339         }
340       }
341     }
342     JavaRunConfigurationExtensionManager.checkConfigurationIsValid(this);
343     ProgramParametersUtil.checkWorkingDirectoryExist(this, getProject(), getConfigurationModule().getModule());
344     JavaParametersUtil.checkAlternativeJRE(this);
345     //TODO add various checks here
346   }
347
348   @Override
349   public void readExternal(Element element) throws InvalidDataException {
350     PathMacroManager.getInstance(getProject()).expandPaths(element);
351     super.readExternal(element);
352     JavaRunConfigurationExtensionManager.getInstance().readExternal(this, element);
353     readModule(element);
354     DefaultJDOMExternalizer.readExternal(this, element);
355     DefaultJDOMExternalizer.readExternal(getPersistantData(), element);
356     EnvironmentVariablesComponent.readExternal(element, getPersistantData().getEnvs());
357
358     Map<String, String> properties = getPersistantData().TEST_PROPERTIES;
359     properties.clear();
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"));
365       }
366     }
367
368     List<String> listeners = getPersistantData().TEST_LISTENERS;
369     listeners.clear();
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"));
375       }
376     }
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));
383       }
384       getPersistantData().setPatterns(tests);
385     }
386   }
387
388   @Override
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());
396
397     Element propertiesElement = element.getChild("properties");
398
399     if (propertiesElement == null) {
400       propertiesElement = new Element("properties");
401       element.addContent(propertiesElement);
402     }
403
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);
410     }
411
412     Element listenersElement = element.getChild("listeners");
413     if (listenersElement == null) {
414       listenersElement = new Element("listeners");
415       element.addContent(listenersElement);
416     }
417
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);
423     }
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);
431       }
432       element.addContent(patternsElement);
433     }
434
435     PathMacroManager.getInstance(getProject()).collapsePathsRecursively(element);
436   }
437
438   @Nullable
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);
444     }
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);
449     }
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);
454       }
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();
463         }
464
465         @Override
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();
472         }
473       }
474       return RunConfigurationExtension.wrapRefactoringElementListener(element, this, new Listener());
475     }
476     return null;
477   }
478
479   @Override
480   public boolean collectOutputFromProcessHandler() {
481     return false;
482   }
483 }