blob: a5b3a578c424a250a9f6cdfa87e7c496b66fc6d8 [file] [log] [blame]
/*
* 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: 18-Jun-2007
*/
package com.theoryinpractice.testng.inspection;
import com.intellij.codeInspection.*;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.search.PsiNonJavaFileReferenceProcessor;
import com.intellij.psi.search.PsiSearchHelper;
import com.intellij.psi.util.PsiClassUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlFile;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.IncorrectOperationException;
import com.theoryinpractice.testng.configuration.browser.SuiteBrowser;
import com.theoryinpractice.testng.util.TestNGUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class UndeclaredTestInspection extends BaseJavaLocalInspectionTool {
private static final Logger LOG = Logger.getInstance("#" + UndeclaredTestInspection.class.getName());
@Nls
@NotNull
public String getGroupDisplayName() {
return TestNGUtil.TESTNG_GROUP_NAME;
}
@Nls
@NotNull
public String getDisplayName() {
return "Undeclared test";
}
@NonNls
@NotNull
public String getShortName() {
return "UndeclaredTests";
}
@Nullable
public ProblemDescriptor[] checkClass(@NotNull final PsiClass aClass,
@NotNull final InspectionManager manager,
final boolean isOnTheFly) {
if (TestNGUtil.hasTest(aClass) && PsiClassUtil.isRunnableClass(aClass, true)) {
final Project project = aClass.getProject();
final String qName = aClass.getQualifiedName();
if (qName == null) return null;
final String packageQName = StringUtil.getPackageName(qName);
final List<String> names = new ArrayList<String>();
for(int i = 0; i < qName.length(); i++) {
if (qName.charAt(i) == '.') {
names.add(qName.substring(0, i));
}
}
names.add(qName);
Collections.reverse(names);
for (final String name : names) {
final boolean isFullName = qName.equals(name);
final boolean[] found = new boolean[]{false};
PsiSearchHelper.SERVICE.getInstance(project)
.processUsagesInNonJavaFiles(name, new PsiNonJavaFileReferenceProcessor() {
public boolean process(final PsiFile file, final int startOffset, final int endOffset) {
if (file.findReferenceAt(startOffset) != null) {
if (!isFullName) { //special package tag required
final XmlTag tag = PsiTreeUtil.getParentOfType(file.findElementAt(startOffset), XmlTag.class);
if (tag == null || !tag.getName().equals("package")) {
return true;
}
final XmlAttribute attribute = tag.getAttribute("name");
if (attribute == null) return true;
final String value = attribute.getValue();
if (value == null) return true;
if (!value.endsWith(".*") && !value.equals(packageQName)) return true;
}
found[0] = true;
return false;
}
return true;
}
}, new TestNGSearchScope(project));
if (found[0]) return null;
}
final PsiIdentifier nameIdentifier = aClass.getNameIdentifier();
LOG.assertTrue(nameIdentifier != null);
return new ProblemDescriptor[]{manager.createProblemDescriptor(nameIdentifier, "Undeclared test \'" + aClass.getName() + "\'",
isOnTheFly, new LocalQuickFix[]{new RegisterClassFix(aClass),
new CreateTestngFix()},
ProblemHighlightType.GENERIC_ERROR_OR_WARNING)};
}
return null;
}
private static class RegisterClassFix implements LocalQuickFix {
private final PsiClass myClass;
public RegisterClassFix(final PsiClass aClass) {
myClass = aClass;
}
@NotNull
public String getName() {
return "Register \'" + myClass.getName() + "\'";
}
@NotNull
public String getFamilyName() {
return getName();
}
public void applyFix(@NotNull final Project project, @NotNull ProblemDescriptor descriptor) {
final PsiClass psiClass = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiClass.class);
LOG.assertTrue(psiClass != null);
SwingUtilities.invokeLater(new Runnable() { //need to show dialog
public void run() {
final String testngXmlPath = new SuiteBrowser(project).showDialog();
if (testngXmlPath == null) return;
final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(testngXmlPath);
LOG.assertTrue(virtualFile != null);
final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
LOG.assertTrue(psiFile instanceof XmlFile);
final XmlFile testngXML = (XmlFile)psiFile;
new WriteCommandAction(project, getName(), testngXML) {
protected void run(final Result result) throws Throwable {
patchTestngXml(testngXML, psiClass);
}
}.execute();
}
});
}
}
//make public for tests only
public static void patchTestngXml(final XmlFile testngXML, final PsiClass psiClass) {
final XmlTag rootTag = testngXML.getDocument().getRootTag();
if (rootTag != null && rootTag.getName().equals("suite")) {
try {
XmlTag testTag = rootTag.findFirstSubTag("test");
if (testTag == null) {
testTag = (XmlTag)rootTag.add(rootTag.createChildTag("test", rootTag.getNamespace(), null, false));
testTag.setAttribute("name", psiClass.getName());
}
XmlTag classesTag = testTag.findFirstSubTag("classes");
if (classesTag == null) {
classesTag = (XmlTag)testTag.add(testTag.createChildTag("classes", testTag.getNamespace(), null, false));
}
final XmlTag classTag = (XmlTag)classesTag.add(classesTag.createChildTag("class", classesTag.getNamespace(), null, false));
final String qualifiedName = psiClass.getQualifiedName();
LOG.assertTrue(qualifiedName != null);
classTag.setAttribute("name", qualifiedName);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
}
private static class CreateTestngFix implements LocalQuickFix {
@NotNull
public String getName() {
return "Create suite";
}
@NotNull
public String getFamilyName() {
return getName();
}
public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
final PsiClass psiClass = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiClass.class);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
final VirtualFile file = FileChooser.chooseFile(FileChooserDescriptorFactory.createSingleFolderDescriptor(), project, null);
if (file != null) {
final PsiManager psiManager = PsiManager.getInstance(project);
final PsiDirectory directory = psiManager.findDirectory(file);
LOG.assertTrue(directory != null);
new WriteCommandAction(project, getName(), null) {
protected void run(final Result result) throws Throwable {
XmlFile testngXml = (XmlFile)PsiFileFactory.getInstance(psiManager.getProject())
.createFileFromText("testng.xml", "<!DOCTYPE suite SYSTEM \"http://testng.org/testng-1.0.dtd\">\n<suite></suite>");
try {
testngXml = (XmlFile)directory.add(testngXml);
}
catch (IncorrectOperationException e) {
//todo suggest new name
return;
}
patchTestngXml(testngXml, psiClass);
}
}.execute();
}
}
});
}
}
}