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
.vfs
.LocalFileSystem
;
32 import com
.intellij
.openapi
.vfs
.VirtualFile
;
33 import com
.intellij
.psi
.*;
34 import com
.intellij
.psi
.search
.GlobalSearchScope
;
35 import com
.intellij
.psi
.search
.PsiNonJavaFileReferenceProcessor
;
36 import com
.intellij
.psi
.util
.PsiClassUtil
;
37 import com
.intellij
.psi
.util
.PsiTreeUtil
;
38 import com
.intellij
.psi
.xml
.XmlFile
;
39 import com
.intellij
.psi
.xml
.XmlTag
;
40 import com
.intellij
.util
.IncorrectOperationException
;
41 import com
.theoryinpractice
.testng
.configuration
.browser
.SuiteBrowser
;
42 import com
.theoryinpractice
.testng
.util
.TestNGUtil
;
43 import org
.jetbrains
.annotations
.Nls
;
44 import org
.jetbrains
.annotations
.NonNls
;
45 import org
.jetbrains
.annotations
.NotNull
;
46 import org
.jetbrains
.annotations
.Nullable
;
50 public class UndeclaredTestInspection
extends BaseJavaLocalInspectionTool
{
51 private static final Logger LOG
= Logger
.getInstance("#" + UndeclaredTestInspection
.class.getName());
55 public String
getGroupDisplayName() {
56 return TestNGUtil
.TESTNG_GROUP_NAME
;
61 public String
getDisplayName() {
62 return "Undeclared test";
67 public String
getShortName() {
68 return "UndeclaredTests";
72 public ProblemDescriptor
[] checkClass(@NotNull final PsiClass aClass
,
73 @NotNull final InspectionManager manager
,
74 final boolean isOnTheFly
) {
75 if (TestNGUtil
.hasTest(aClass
) && PsiClassUtil
.isRunnableClass(aClass
, true)) {
76 final Project project
= aClass
.getProject();
77 final String qName
= aClass
.getQualifiedName();
78 if (qName
== null) return null;
79 final String packageName
= ((PsiJavaFile
)aClass
.getContainingFile()).getPackageName();
80 final String
[] names
= new String
[]{qName
, packageName
};
81 for (final String name
: names
) {
82 final boolean[] found
= new boolean[]{false};
83 PsiManager
.getInstance(project
).getSearchHelper()
84 .processUsagesInNonJavaFiles(name
, new PsiNonJavaFileReferenceProcessor() {
85 public boolean process(final PsiFile file
, final int startOffset
, final int endOffset
) {
86 if (file
.findReferenceAt(startOffset
) != null) {
87 if (name
.equals(packageName
)) { //special package tag required
88 final XmlTag tag
= PsiTreeUtil
.getParentOfType(file
.findElementAt(startOffset
), XmlTag
.class);
89 if (tag
== null || !tag
.getName().equals("package")){
98 }, new TestNGSearchScope());
99 if (found
[0]) return null;
101 final PsiIdentifier nameIdentifier
= aClass
.getNameIdentifier();
102 LOG
.assertTrue(nameIdentifier
!= null);
103 return new ProblemDescriptor
[]{manager
.createProblemDescriptor(nameIdentifier
, "Undeclared test \'" + aClass
.getName() + "\'",
104 new LocalQuickFix
[]{new RegisterClassFix(aClass
),
105 new CreateTestngFix()},
106 ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
, isOnTheFly
)};
111 private static class RegisterClassFix
implements LocalQuickFix
{
112 private final PsiClass myClass
;
114 public RegisterClassFix(final PsiClass aClass
) {
119 public String
getName() {
120 return "Register \'" + myClass
.getName() + "\'";
124 public String
getFamilyName() {
128 public void applyFix(@NotNull final Project project
, @NotNull ProblemDescriptor descriptor
) {
129 final PsiClass psiClass
= PsiTreeUtil
.getParentOfType(descriptor
.getPsiElement(), PsiClass
.class);
130 LOG
.assertTrue(psiClass
!= null);
131 SwingUtilities
.invokeLater(new Runnable() { //need to show dialog
134 final String testngXmlPath
= new SuiteBrowser(project
).showDialog();
135 if (testngXmlPath
== null) return;
136 final VirtualFile virtualFile
= LocalFileSystem
.getInstance().findFileByPath(testngXmlPath
);
137 LOG
.assertTrue(virtualFile
!= null);
138 final PsiFile psiFile
= PsiManager
.getInstance(project
).findFile(virtualFile
);
139 LOG
.assertTrue(psiFile
instanceof XmlFile
);
140 final XmlFile testngXML
= (XmlFile
)psiFile
;
141 new WriteCommandAction(project
, getName(), testngXML
) {
142 protected void run(final Result result
) throws Throwable
{
143 patchTestngXml(testngXML
, psiClass
);
151 //make public for tests only
152 public static void patchTestngXml(final XmlFile testngXML
, final PsiClass psiClass
) {
153 final XmlTag rootTag
= testngXML
.getDocument().getRootTag();
154 if (rootTag
!= null && rootTag
.getName().equals("suite")) {
156 XmlTag testTag
= rootTag
.findFirstSubTag("test");
157 if (testTag
== null) {
158 testTag
= (XmlTag
)rootTag
.add(rootTag
.createChildTag("test", rootTag
.getNamespace(), null, false));
159 testTag
.setAttribute("name", psiClass
.getName());
161 XmlTag classesTag
= testTag
.findFirstSubTag("classes");
162 if (classesTag
== null) {
163 classesTag
= (XmlTag
)testTag
.add(testTag
.createChildTag("classes", testTag
.getNamespace(), null, false));
165 final XmlTag classTag
= (XmlTag
)classesTag
.add(classesTag
.createChildTag("class", classesTag
.getNamespace(), null, false));
166 final String qualifiedName
= psiClass
.getQualifiedName();
167 LOG
.assertTrue(qualifiedName
!= null);
168 classTag
.setAttribute("name", qualifiedName
);
170 catch (IncorrectOperationException e
) {
176 private static class CreateTestngFix
implements LocalQuickFix
{
178 public String
getName() {
179 return "Create suite";
183 public String
getFamilyName() {
187 public void applyFix(@NotNull final Project project
, @NotNull final ProblemDescriptor descriptor
) {
188 final PsiClass psiClass
= PsiTreeUtil
.getParentOfType(descriptor
.getPsiElement(), PsiClass
.class);
189 SwingUtilities
.invokeLater(new Runnable() {
191 final VirtualFile
[] files
= FileChooser
.chooseFiles(project
, new FileChooserDescriptor(false, true, false, false, false, false));
192 if (files
.length
== 1) {
193 final PsiManager psiManager
= PsiManager
.getInstance(project
);
194 final PsiDirectory directory
= psiManager
.findDirectory(files
[0]);
195 LOG
.assertTrue(directory
!= null);
196 new WriteCommandAction(project
, getName(), null) {
197 protected void run(final Result result
) throws Throwable
{
198 XmlFile testngXml
= (XmlFile
)PsiFileFactory
.getInstance(psiManager
.getProject())
199 .createFileFromText("testng.xml", "<!DOCTYPE suite SYSTEM \"http://testng.org/testng-1.0.dtd\">\n<suite></suite>");
201 testngXml
= (XmlFile
)directory
.add(testngXml
);
203 catch (IncorrectOperationException e
) {
204 //todo suggest new name
207 patchTestngXml(testngXml
, psiClass
);
216 private static class TestNGSearchScope
extends GlobalSearchScope
{
217 public boolean contains(VirtualFile file
) {
218 return TestNGUtil
.isTestngXML(file
);
221 public int compare(VirtualFile file1
, VirtualFile file2
) {
225 public boolean isSearchInModuleContent(@NotNull Module aModule
) {
229 public boolean isSearchInLibraries() {