/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.junit.util;
import java.util.Collection;
import java.util.Set;
import org.eclipse.jdt.junit.JUnitCore;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IRegion;
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.Signature;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.Modifier;
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.SearchMatch;
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.JUnitCorePlugin;
import org.eclipse.jdt.internal.junit.launcher.ITestKind;
import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
/**
* Custom Search engine for suite() methods
*/
public class CoreTestSearchEngine {
public static boolean isTestOrTestSuite(IType declaringType) throws CoreException {
ITestKind testKind= TestKindRegistry.getContainerTestKind(declaringType);
return testKind.getFinder().isTest(declaringType);
}
public static boolean isAccessibleClass(IType type) throws JavaModelException {
int flags= type.getFlags();
if (Flags.isInterface(flags)) {
return false;
}
IJavaElement parent= type.getParent();
while (true) {
if (parent instanceof ICompilationUnit || parent instanceof IClassFile) {
return true;
}
if (!(parent instanceof IType) || !Flags.isStatic(flags) || !Flags.isPublic(flags)) {
return false;
}
flags= ((IType) parent).getFlags();
parent= parent.getParent();
}
}
public static boolean isAccessibleClass(ITypeBinding type) {
if (type.isInterface()) {
return false;
}
int modifiers= type.getModifiers();
while (true) {
if (type.getDeclaringMethod() != null) {
return false;
}
ITypeBinding declaringClass= type.getDeclaringClass();
if (declaringClass == null) {
return true;
}
if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers)) {
return false;
}
modifiers= declaringClass.getModifiers();
type= declaringClass;
}
}
public static boolean hasTestCaseType(IJavaProject javaProject) {
try {
return javaProject != null && javaProject.findType(JUnitCorePlugin.TEST_SUPERCLASS_NAME) != null;
} catch (JavaModelException e) {
// not available
}
return false;
}
public static boolean hasTestAnnotation(IJavaProject project) {
try {
if (project != null) {
IType type= project.findType(JUnitCorePlugin.JUNIT4_ANNOTATION_NAME);
if (type != null) {
// @Test annotation is not accessible if the JUnit classpath container is set to JUnit 3
// (although it may resolve to a JUnit 4 JAR)
IPackageFragmentRoot root= (IPackageFragmentRoot) type.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
IClasspathEntry cpEntry= root.getRawClasspathEntry();
return ! JUnitCore.JUNIT3_CONTAINER_PATH.equals(cpEntry.getPath());
}
}
} catch (JavaModelException e) {
// not available
}
return false;
}
public static boolean isTestImplementor(IType type) throws JavaModelException {
ITypeHierarchy typeHier= type.newSupertypeHierarchy(null);
IType[] superInterfaces= typeHier.getAllInterfaces();
for (int i= 0; i < superInterfaces.length; i++) {
if (JUnitCorePlugin.TEST_INTERFACE_NAME.equals(superInterfaces[i].getFullyQualifiedName('.'))) {
return true;
}
}
return false;
}
public static boolean isTestImplementor(ITypeBinding type) {
ITypeBinding superType= type.getSuperclass();
if (superType != null && isTestImplementor(superType)) {
return true;
}
ITypeBinding[] interfaces= type.getInterfaces();
for (int i= 0; i < interfaces.length; i++) {
ITypeBinding curr= interfaces[i];
if (JUnitCorePlugin.TEST_INTERFACE_NAME.equals(curr.getQualifiedName()) || isTestImplementor(curr)) {
return true;
}
}
return false;
}
public static boolean hasSuiteMethod(IType type) throws JavaModelException {
IMethod method= type.getMethod("suite", new String[0]); //$NON-NLS-1$
if (!method.exists())
return false;
if (!Flags.isStatic(method.getFlags()) || !Flags.isPublic(method.getFlags())) {
return false;
}
if (!Signature.getSimpleName(Signature.toString(method.getReturnType())).equals(JUnitCorePlugin.SIMPLE_TEST_INTERFACE_NAME)) {
return false;
}
return true;
}
public static IRegion getRegion(IJavaElement element) throws JavaModelException {
IRegion result= JavaCore.newRegion();
if (element.getElementType() == IJavaElement.JAVA_PROJECT) {
// for projects only add the contained source folders
IPackageFragmentRoot[] roots= ((IJavaProject) element).getPackageFragmentRoots();
for (int i= 0; i < roots.length; i++) {
if (!roots[i].isArchive()) {
result.add(roots[i]);
}
}
} else {
result.add(element);
}
return result;
}
public static void findTestImplementorClasses(ITypeHierarchy typeHierarchy, IType testInterface, IRegion region, Set<IType> result)
throws JavaModelException {
IType[] subtypes= typeHierarchy.getAllSubtypes(testInterface);
for (int i= 0; i < subtypes.length; i++) {
IType type= subtypes[i];
int cachedFlags= typeHierarchy.getCachedFlags(type);
if (!Flags.isInterface(cachedFlags) && !Flags.isAbstract(cachedFlags) // do the cheaper tests first
&& region.contains(type) && CoreTestSearchEngine.isAccessibleClass(type)) {
result.add(type);
}
}
}
private static class SuiteMethodTypesCollector extends SearchRequestor {
private Collection<IType> fResult;
public SuiteMethodTypesCollector(Collection<IType> result) {
fResult= result;
}
@Override
public void acceptSearchMatch(SearchMatch match) throws CoreException {
Object enclosingElement= match.getElement();
if (!(enclosingElement instanceof IMethod))
return;
IMethod method= (IMethod) enclosingElement;
if (!Flags.isStatic(method.getFlags()) || !Flags.isPublic(method.getFlags())) {
return;
}
IType declaringType= ((IMethod) enclosingElement).getDeclaringType();
if (!CoreTestSearchEngine.isAccessibleClass(declaringType)) {
return;
}
fResult.add(declaringType);
}
}
public static void findSuiteMethods(IJavaElement element, Set<IType> result, IProgressMonitor pm) throws CoreException {
// fix for bug 36449 JUnit should constrain tests to selected project
// [JUnit]
IJavaSearchScope scope= SearchEngine.createJavaSearchScope(new IJavaElement[] { element }, IJavaSearchScope.SOURCES);
SearchRequestor requestor= new SuiteMethodTypesCollector(result);
int matchRule= SearchPattern.R_EXACT_MATCH | SearchPattern.R_CASE_SENSITIVE | SearchPattern.R_ERASURE_MATCH;
SearchPattern suitePattern= SearchPattern.createPattern("suite() Test", IJavaSearchConstants.METHOD, IJavaSearchConstants.DECLARATIONS, matchRule); //$NON-NLS-1$
SearchParticipant[] participants= new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() };
new SearchEngine().search(suitePattern, participants, scope, requestor, pm);
}
// --- copied from org.eclipse.jdt.internal.corext.util.JavaModelUtil: ---
/**
* @param version1 the first version
* @param version2 the second version
* @return <code>true</code> iff version1 is less than version2
*/
public static boolean isVersionLessThan(String version1, String version2) {
if (JavaCore.VERSION_CLDC_1_1.equals(version1)) {
version1= JavaCore.VERSION_1_1 + 'a';
}
if (JavaCore.VERSION_CLDC_1_1.equals(version2)) {
version2= JavaCore.VERSION_1_1 + 'a';
}
return version1.compareTo(version2) < 0;
}
public static boolean is50OrHigher(String compliance) {
return !isVersionLessThan(compliance, JavaCore.VERSION_1_5);
}
/**
* Checks if the given project or workspace has source compliance 5.0 or greater.
*
* @param project the project to test or <code>null</code> to test the workspace settings
* @return <code>true</code> if the given project or workspace has source compliance 5.0 or greater.
*/
public static boolean is50OrHigher(IJavaProject project) {
String source= project != null ? project.getOption(JavaCore.COMPILER_SOURCE, true) : JavaCore.getOption(JavaCore.COMPILER_SOURCE);
return is50OrHigher(source);
}
// ---
}