blob: a8219e80f5155edca78cefb0cab4fc975ab0c190 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.resources.IResource;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PlatformUI;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IClassFile;
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.ISourceReference;
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.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.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.internal.junit.launcher.ITestKind;
import org.eclipse.jdt.internal.junit.launcher.TestKind;
import org.eclipse.jdt.internal.junit.launcher.TestKindRegistry;
import org.eclipse.jdt.internal.junit.ui.JUnitMessages;
import org.eclipse.jdt.internal.junit.ui.JUnitPlugin;
/**
* Custom Search engine for suite() methods
*/
public class TestSearchEngine {
private static class JUnitSearchResultCollector extends SearchRequestor {
List fList;
Set fFailed= new HashSet();
Set fMatches= new HashSet();
public JUnitSearchResultCollector(List list) {
fList= list;
}
public void acceptSearchMatch(SearchMatch match) throws CoreException {
Object enclosingElement= match.getElement();
if (! (enclosingElement instanceof IMethod))
return;
IMethod method= (IMethod) enclosingElement;
IType declaringType= method.getDeclaringType();
if (fMatches.contains(declaringType) || fFailed.contains(declaringType))
return;
if (isTestOrTestSuite(declaringType)) {
fMatches.add(declaringType);
} else {
fFailed.add(declaringType);
}
}
public void endReporting() {
fList.addAll(fMatches);
}
}
public static boolean isTestOrTestSuite(IType declaringType) throws JavaModelException {
//TODO: finds JUnit3 and JUnit4 tests, although some of the callers only search for JUnit3
return !TestKindRegistry.getDefault().getKind(declaringType).isNull();
}
private List searchMethod(IProgressMonitor pm, final IJavaSearchScope scope) throws CoreException {
final List typesFound= new ArrayList(200);
searchMethod(typesFound, scope, pm);
return typesFound;
}
private List searchMethod(final List v, IJavaSearchScope scope, final IProgressMonitor progressMonitor) throws CoreException {
SearchRequestor requestor= new JUnitSearchResultCollector(v);
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, progressMonitor);
return v;
}
public static IType[] findTests(IRunnableContext context, final Object[] elements, final ITestKind testKind) throws InvocationTargetException, InterruptedException {
final Set result= new HashSet();
if (elements.length > 0) {
IRunnableWithProgress runnable= new IRunnableWithProgress() {
public void run(IProgressMonitor pm) throws InterruptedException {
testKind.createFinder().findTestsInContainer(elements, result, pm);
// doFindJUnit3Tests(elements, result, pm);
}
};
context.run(true, true, runnable);
}
return (IType[]) result.toArray(new IType[result.size()]);
}
public static IType[] findTests(final Object[] elements) throws InvocationTargetException, InterruptedException {
final Set result= new HashSet();
if (elements.length > 0) {
IRunnableWithProgress runnable= new IRunnableWithProgress() {
public void run(IProgressMonitor pm) throws InterruptedException {
ArrayList/*<TestKind>*/ kinds= TestKindRegistry.getDefault().getAllKinds();
int kindCount= kinds.size();
pm.beginTask("", kindCount); //$NON-NLS-1$
for (int i= 0; i < kindCount; i++) {
TestKind kind= (TestKind) kinds.get(i);
kind.createFinder().findTestsInContainer(elements, result, new SubProgressMonitor(pm, 1));
if (! result.isEmpty()) {
break;
}
}
pm.done();
// doFindJUnit3Tests(elements, result, pm);
}
};
PlatformUI.getWorkbench().getProgressService().busyCursorWhile(runnable);
}
return (IType[]) result.toArray(new IType[result.size()]);
}
public static void doFindJUnit3Tests(Object[] elements, Set result, IProgressMonitor pm) throws InterruptedException {
int nElements= elements.length;
pm.beginTask(JUnitMessages.TestSearchEngine_message_searching, nElements);
try {
for (int i= 0; i < nElements; i++) {
try {
collectTypes(elements[i], new SubProgressMonitor(pm, 1), result);
} catch (CoreException e) {
JUnitPlugin.log(e.getStatus());
}
if (pm.isCanceled()) {
throw new InterruptedException();
}
}
} finally {
pm.done();
}
}
private static void collectTypes(Object element, IProgressMonitor pm, Set result) throws CoreException {
pm.beginTask(JUnitMessages.TestSearchEngine_message_searching, 10);
element= computeScope(element);
try {
while ( (element instanceof ISourceReference) && ! (element instanceof ICompilationUnit)) {
if (element instanceof IType) {
IType type= (IType) element;
if (isTestOrTestSuite(type)) {
result.add(type);
return;
}
}
element= ((IJavaElement) element).getParent();
}
if (element instanceof ICompilationUnit) {
ICompilationUnit cu= (ICompilationUnit) element;
IType[] types= cu.getAllTypes();
for (int i= 0; i < types.length; i++) {
IType type= types[i];
if (isTestOrTestSuite(type))
result.add(type);
}
} else if (element instanceof IJavaElement) {
List testCases= findTestCases((IJavaElement) element, new SubProgressMonitor(pm, 7));
List suiteMethods= searchSuiteMethods(new SubProgressMonitor(pm, 3), (IJavaElement) element);
while (!suiteMethods.isEmpty()) {
if (!testCases.contains(suiteMethods.get(0))) {
testCases.add(suiteMethods.get(0));
}
suiteMethods.remove(0);
}
result.addAll(testCases);
}
} finally {
pm.done();
}
}
private static List findTestCases(IJavaElement element, IProgressMonitor pm) throws JavaModelException {
List found= new ArrayList();
IJavaProject javaProject= element.getJavaProject();
IType testCaseType= testCaseType(javaProject);
if (testCaseType == null)
return found;
IType[] subtypes= javaProject.newTypeHierarchy(testCaseType, getRegion(element), pm).getAllSubtypes(testCaseType);
if (subtypes == null)
throw new JavaModelException(new CoreException(new Status(IStatus.ERROR, JUnitPlugin.PLUGIN_ID,
IJavaLaunchConfigurationConstants.ERR_UNSPECIFIED_MAIN_TYPE, JUnitMessages.JUnitBaseLaunchConfiguration_error_notests, null)));
for (int i= 0; i < subtypes.length; i++) {
try {
if (hasValidModifiers(subtypes[i]))
found.add(subtypes[i]);
} catch (JavaModelException e) {
JUnitPlugin.log(e.getStatus());
}
}
return found;
}
private static IType testCaseType(IJavaProject javaProject) {
try {
return javaProject.findType("junit.framework.TestCase"); //$NON-NLS-1$
} catch (JavaModelException e) {
JUnitPlugin.log(e.getStatus());
return null;
}
}
private 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 Object computeScope(Object element) throws JavaModelException {
if (element instanceof IFileEditorInput)
element= ((IFileEditorInput) element).getFile();
if (element instanceof IResource)
element= JavaCore.create((IResource) element);
if (element instanceof IClassFile) {
IClassFile cf= (IClassFile) element;
element= cf.getType();
}
return element;
}
private static List searchSuiteMethods(IProgressMonitor pm, IJavaElement element) throws CoreException {
// fix for bug 36449 JUnit should constrain tests to selected project
// [JUnit]
IJavaSearchScope scope= SearchEngine.createJavaSearchScope(new IJavaElement[] { element }, IJavaSearchScope.SOURCES);
TestSearchEngine searchEngine= new TestSearchEngine();
return searchEngine.searchMethod(pm, scope);
}
public static boolean hasValidModifiers(IType type) throws JavaModelException {
if (Flags.isAbstract(type.getFlags()))
return false;
if (!Flags.isPublic(type.getFlags()))
return false;
return true;
}
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 (superInterfaces[i].getFullyQualifiedName('.').equals(JUnitPlugin.TEST_INTERFACE_NAME))
return true;
}
return false;
}
public static boolean hasTestCaseType(IJavaProject javaProject) {
return testCaseType(javaProject) != null;
}
public static boolean hasTestAnnotation(IJavaProject project) {
try {
return project != null && project.findType(JUnitPlugin.JUNIT4_ANNOTATION_NAME) != null;
} catch (JavaModelException e) {
// not available
}
return false;
}
}