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.
21 package com
.theoryinpractice
.testng
.inspection
;
23 import com
.intellij
.codeInspection
.*;
24 import com
.intellij
.openapi
.application
.Result
;
25 import com
.intellij
.openapi
.command
.WriteCommandAction
;
26 import com
.intellij
.openapi
.diagnostic
.Logger
;
27 import com
.intellij
.openapi
.fileChooser
.FileChooser
;
28 import com
.intellij
.openapi
.fileChooser
.FileChooserDescriptor
;
29 import com
.intellij
.openapi
.module
.Module
;
30 import com
.intellij
.openapi
.project
.Project
;
31 import com
.intellij
.openapi
.util
.text
.StringUtil
;
32 import com
.intellij
.openapi
.vfs
.LocalFileSystem
;
33 import com
.intellij
.openapi
.vfs
.VirtualFile
;
34 import com
.intellij
.psi
.*;
35 import com
.intellij
.psi
.search
.GlobalSearchScope
;
36 import com
.intellij
.psi
.search
.PsiNonJavaFileReferenceProcessor
;
37 import com
.intellij
.psi
.util
.PsiClassUtil
;
38 import com
.intellij
.psi
.util
.PsiTreeUtil
;
39 import com
.intellij
.psi
.xml
.XmlAttribute
;
40 import com
.intellij
.psi
.xml
.XmlFile
;
41 import com
.intellij
.psi
.xml
.XmlTag
;
42 import com
.intellij
.util
.IncorrectOperationException
;
43 import com
.theoryinpractice
.testng
.configuration
.browser
.SuiteBrowser
;
44 import com
.theoryinpractice
.testng
.util
.TestNGUtil
;
45 import org
.jetbrains
.annotations
.Nls
;
46 import org
.jetbrains
.annotations
.NonNls
;
47 import org
.jetbrains
.annotations
.NotNull
;
48 import org
.jetbrains
.annotations
.Nullable
;
52 public class UndeclaredTestInspection
extends BaseJavaLocalInspectionTool
{
53 private static final Logger LOG
= Logger
.getInstance("#" + UndeclaredTestInspection
.class.getName());
57 public String
getGroupDisplayName() {
58 return TestNGUtil
.TESTNG_GROUP_NAME
;
63 public String
getDisplayName() {
64 return "Undeclared test";
69 public String
getShortName() {
70 return "UndeclaredTests";
74 public ProblemDescriptor
[] checkClass(@NotNull final PsiClass aClass
,
75 @NotNull final InspectionManager manager
,
76 final boolean isOnTheFly
) {
77 if (TestNGUtil
.hasTest(aClass
) && PsiClassUtil
.isRunnableClass(aClass
, true)) {
78 final Project project
= aClass
.getProject();
79 final String qName
= aClass
.getQualifiedName();
80 if (qName
== null) return null;
81 final String packageQName
= ((PsiJavaFile
)aClass
.getContainingFile()).getPackageName();
82 final String packageName
= StringUtil
.getShortName(packageQName
);
84 if (packageQName
.length() > 0) {
85 final String pName
= packageName
.length() > 0 ? packageName
: packageQName
;
86 names
= new String
[]{qName
, pName
};
89 names
= new String
[]{qName
};
91 for (final String name
: names
) {
92 final boolean[] found
= new boolean[]{false};
93 PsiManager
.getInstance(project
).getSearchHelper()
94 .processUsagesInNonJavaFiles(name
, new PsiNonJavaFileReferenceProcessor() {
95 public boolean process(final PsiFile file
, final int startOffset
, final int endOffset
) {
96 if (file
.findReferenceAt(startOffset
) != null) {
97 if (packageQName
.endsWith(name
)) { //special package tag required
98 final XmlTag tag
= PsiTreeUtil
.getParentOfType(file
.findElementAt(startOffset
), XmlTag
.class);
99 if (tag
== null || !tag
.getName().equals("package")){
102 final XmlAttribute attribute
= tag
.getAttribute("name");
103 if (attribute
== null) return true;
104 final String value
= attribute
.getValue();
105 if (!value
.equals(StringUtil
.getQualifiedName(packageQName
, "*"))) return true;
112 }, new TestNGSearchScope());
113 if (found
[0]) return null;
115 final PsiIdentifier nameIdentifier
= aClass
.getNameIdentifier();
116 LOG
.assertTrue(nameIdentifier
!= null);
117 return new ProblemDescriptor
[]{manager
.createProblemDescriptor(nameIdentifier
, "Undeclared test \'" + aClass
.getName() + "\'",
118 isOnTheFly
, new LocalQuickFix
[]{new RegisterClassFix(aClass
),
119 new CreateTestngFix()},
120 ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
)};
125 private static class RegisterClassFix
implements LocalQuickFix
{
126 private final PsiClass myClass
;
128 public RegisterClassFix(final PsiClass aClass
) {
133 public String
getName() {
134 return "Register \'" + myClass
.getName() + "\'";
138 public String
getFamilyName() {
142 public void applyFix(@NotNull final Project project
, @NotNull ProblemDescriptor descriptor
) {
143 final PsiClass psiClass
= PsiTreeUtil
.getParentOfType(descriptor
.getPsiElement(), PsiClass
.class);
144 LOG
.assertTrue(psiClass
!= null);
145 SwingUtilities
.invokeLater(new Runnable() { //need to show dialog
148 final String testngXmlPath
= new SuiteBrowser(project
).showDialog();
149 if (testngXmlPath
== null) return;
150 final VirtualFile virtualFile
= LocalFileSystem
.getInstance().findFileByPath(testngXmlPath
);
151 LOG
.assertTrue(virtualFile
!= null);
152 final PsiFile psiFile
= PsiManager
.getInstance(project
).findFile(virtualFile
);
153 LOG
.assertTrue(psiFile
instanceof XmlFile
);
154 final XmlFile testngXML
= (XmlFile
)psiFile
;
155 new WriteCommandAction(project
, getName(), testngXML
) {
156 protected void run(final Result result
) throws Throwable
{
157 patchTestngXml(testngXML
, psiClass
);
165 //make public for tests only
166 public static void patchTestngXml(final XmlFile testngXML
, final PsiClass psiClass
) {
167 final XmlTag rootTag
= testngXML
.getDocument().getRootTag();
168 if (rootTag
!= null && rootTag
.getName().equals("suite")) {
170 XmlTag testTag
= rootTag
.findFirstSubTag("test");
171 if (testTag
== null) {
172 testTag
= (XmlTag
)rootTag
.add(rootTag
.createChildTag("test", rootTag
.getNamespace(), null, false));
173 testTag
.setAttribute("name", psiClass
.getName());
175 XmlTag classesTag
= testTag
.findFirstSubTag("classes");
176 if (classesTag
== null) {
177 classesTag
= (XmlTag
)testTag
.add(testTag
.createChildTag("classes", testTag
.getNamespace(), null, false));
179 final XmlTag classTag
= (XmlTag
)classesTag
.add(classesTag
.createChildTag("class", classesTag
.getNamespace(), null, false));
180 final String qualifiedName
= psiClass
.getQualifiedName();
181 LOG
.assertTrue(qualifiedName
!= null);
182 classTag
.setAttribute("name", qualifiedName
);
184 catch (IncorrectOperationException e
) {
190 private static class CreateTestngFix
implements LocalQuickFix
{
192 public String
getName() {
193 return "Create suite";
197 public String
getFamilyName() {
201 public void applyFix(@NotNull final Project project
, @NotNull final ProblemDescriptor descriptor
) {
202 final PsiClass psiClass
= PsiTreeUtil
.getParentOfType(descriptor
.getPsiElement(), PsiClass
.class);
203 SwingUtilities
.invokeLater(new Runnable() {
205 final VirtualFile
[] files
= FileChooser
.chooseFiles(project
, new FileChooserDescriptor(false, true, false, false, false, false));
206 if (files
.length
== 1) {
207 final PsiManager psiManager
= PsiManager
.getInstance(project
);
208 final PsiDirectory directory
= psiManager
.findDirectory(files
[0]);
209 LOG
.assertTrue(directory
!= null);
210 new WriteCommandAction(project
, getName(), null) {
211 protected void run(final Result result
) throws Throwable
{
212 XmlFile testngXml
= (XmlFile
)PsiFileFactory
.getInstance(psiManager
.getProject())
213 .createFileFromText("testng.xml", "<!DOCTYPE suite SYSTEM \"http://testng.org/testng-1.0.dtd\">\n<suite></suite>");
215 testngXml
= (XmlFile
)directory
.add(testngXml
);
217 catch (IncorrectOperationException e
) {
218 //todo suggest new name
221 patchTestngXml(testngXml
, psiClass
);
230 private static class TestNGSearchScope
extends GlobalSearchScope
{
231 public boolean contains(VirtualFile file
) {
232 return TestNGUtil
.isTestngXML(file
);
235 public int compare(VirtualFile file1
, VirtualFile file2
) {
239 public boolean isSearchInModuleContent(@NotNull Module aModule
) {
243 public boolean isSearchInLibraries() {