package org.testng.eclipse.launch;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IRegion;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jdt.internal.junit.util.CoreTestSearchEngine;
import org.testng.eclipse.util.ResourceUtil;
public class TestFinder {
public void findTestsInContainer(IJavaElement element, Set result, IProgressMonitor pm) throws CoreException {
if (element == null || result == null) {
throw new IllegalArgumentException();
}
if (element instanceof IType) {
if (internalIsTest((IType) element, pm)) {
result.add(element);
return;
}
}
if (pm == null)
pm= new NullProgressMonitor();
try {
pm.beginTask(ResourceUtil.getString("TestSearchEngine.message.searching"), 4);
IRegion region= CoreTestSearchEngine.getRegion(element);
ITypeHierarchy hierarchy= JavaCore.newTypeHierarchy(region, null, new SubProgressMonitor(pm, 1));
IType[] allClasses= hierarchy.getAllClasses();
// search for all types with references to RunWith and Test and all subclasses
Set<IType> candidates= new HashSet<>(allClasses.length);
SearchRequestor requestor= new AnnotationSearchRequestor(hierarchy, candidates);
IJavaSearchScope scope= SearchEngine.createJavaSearchScope(allClasses, IJavaSearchScope.SOURCES);
int matchRule= SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE;
SearchPattern annotationsPattern= SearchPattern.createPattern("org.testng.annotations.Test", IJavaSearchConstants.ANNOTATION_TYPE, IJavaSearchConstants.ANNOTATION_TYPE_REFERENCE, matchRule);
SearchParticipant[] searchParticipants= new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() };
new SearchEngine().search(annotationsPattern, searchParticipants, scope, requestor, new SubProgressMonitor(pm, 2));
// find all classes in the region
for (IType curr : candidates) {
if (CoreTestSearchEngine.isAccessibleClass(curr) && !Flags.isAbstract(curr.getFlags()) && region.contains(curr)) {
result.add(curr);
}
}
} finally {
pm.done();
}
}
private boolean internalIsTest(IType type, IProgressMonitor monitor) throws JavaModelException {
if (CoreTestSearchEngine.isAccessibleClass(type)) {
ASTParser parser= ASTParser.newParser(AST.JLS4);
/* TODO: When bug 156352 is fixed:
parser.setProject(type.getJavaProject());
IBinding[] bindings= parser.createBindings(new IJavaElement[] { type }, monitor);
if (bindings.length == 1 && bindings[0] instanceof ITypeBinding) {
ITypeBinding binding= (ITypeBinding) bindings[0];
return isTest(binding);
}*/
if (type.getCompilationUnit() != null) {
parser.setSource(type.getCompilationUnit());
} else if (!isAvailable(type.getSourceRange())) { // class file with no source
parser.setProject(type.getJavaProject());
IBinding[] bindings= parser.createBindings(new IJavaElement[] { type }, monitor);
if (bindings.length == 1 && bindings[0] instanceof ITypeBinding) {
ITypeBinding binding= (ITypeBinding) bindings[0];
return isTest(binding);
}
return false;
} else {
parser.setSource(type.getClassFile());
}
parser.setFocalPosition(0);
parser.setResolveBindings(true);
CompilationUnit root= (CompilationUnit) parser.createAST(monitor);
ASTNode node= root.findDeclaringNode(type.getKey());
if (node instanceof TypeDeclaration) {
ITypeBinding binding= ((TypeDeclaration) node).resolveBinding();
if (binding != null) {
return isTest(binding);
}
}
}
return false;
}
private static boolean isAvailable(ISourceRange range) {
return range != null && range.getOffset() != -1;
}
private boolean isTest(ITypeBinding binding) {
if (Modifier.isAbstract(binding.getModifiers()))
return false;
return annotatesAtLeastOneMethod(binding, "org.testng.annotations.Test");
}
public boolean annotatesAtLeastOneMethod(ITypeBinding type, String qualifiedName) {
while (type != null) {
IMethodBinding[] declaredMethods= type.getDeclaredMethods();
for (int i= 0; i < declaredMethods.length; i++) {
IMethodBinding curr= declaredMethods[i];
if (annotates(curr.getAnnotations(), qualifiedName)) {
return true;
}
}
type= type.getSuperclass();
}
return false;
}
private boolean annotates(IAnnotationBinding[] annotations, String qualifiedName) {
for (int i= 0; i < annotations.length; i++) {
ITypeBinding annotationType= annotations[i].getAnnotationType();
if (annotationType != null && (annotationType.getQualifiedName().equals(qualifiedName))) {
return true;
}
}
return false;
}
}