Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show progress when searching test methods in JUnit run configuration #653 #675

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ public final class JUnitMessages extends NLS {

public static String JUnitLaunchConfigurationTab_error_notJavaProject;

public static String JUnitLaunchConfigurationTab_error_operation_canceled;

public static String JUnitLaunchConfigurationTab_error_projectnotdefined;

public static String JUnitLaunchConfigurationTab_error_projectnotexists;
Expand Down Expand Up @@ -372,4 +374,6 @@ private JUnitMessages() {
public static String TestRunnerViewPart_JUnitPasteAction_label;

public static String TestRunnerViewPart_layout_menu;

public static String TestSearchEngine_search_message_progress_monitor;
}
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ JUnitLaunchConfigurationTab_folderdialog_message=Choose a Project, Source Folder
JUnitLaunchConfigurationTab_error_projectnotdefined=Project not specified
JUnitLaunchConfigurationTab_error_projectnotexists=Project does not exist
JUnitLaunchConfigurationTab_error_notJavaProject=Specified project is not a Java project
JUnitLaunchConfigurationTab_error_operation_canceled=(Operation canceled by the user)
JUnitLaunchConfigurationTab_error_testnotdefined=Test not specified
JUnitLaunchConfigurationTab_error_testcasenotonpath=Cannot find class 'junit.framework.TestCase' on project build path.
JUnitLaunchConfigurationTab_addtag_label=Con&figure...
Expand Down Expand Up @@ -276,3 +277,5 @@ JUnitViewEditorLauncher_dialog_title=Import Test Run
JUnitViewEditorLauncher_error_occurred=An error occurred while opening a test run file.
ClasspathVariableMarkerResolutionGenerator_use_JUnit3=Use the JUnit 3 library
ClasspathVariableMarkerResolutionGenerator_use_JUnit3_desc=Changes the classpath variable entry to use the JUnit 3 library

TestSearchEngine_search_message_progress_monitor=Searching for test methods in ''{0}''
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2010 IBM Corporation and others.
* Copyright (c) 2000, 2023 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -18,14 +18,27 @@
import java.util.Set;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;

import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;

import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.dom.Modifier;

import org.eclipse.jdt.internal.junit.JUnitCorePlugin;
import org.eclipse.jdt.internal.junit.Messages;
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;


/**
Expand All @@ -48,4 +61,124 @@ public static Set<IType> findTests(IRunnableContext context, final IJavaElement
return result;
}

public static Set<String> findTestMethods(IRunnableContext context, final IJavaProject javaProject, IType type, TestKind testKind) throws InvocationTargetException, InterruptedException {
final Set<String> result= new HashSet<>();

IRunnableWithProgress runnable= progressMonitor -> {
try {
String message= Messages.format(JUnitMessages.TestSearchEngine_search_message_progress_monitor, type.getElementName());
SubMonitor subMonitor= SubMonitor.convert(progressMonitor, message, 1);

collectMethodNames(type, javaProject, testKind.getId(), result, subMonitor.split(1));
} catch (CoreException e) {
throw new InvocationTargetException(e);
}
};
context.run(true, true, runnable);
return result;
}

private static void collectMethodNames(IType type, IJavaProject javaProject, String testKindId, Set<String> methodNames, IProgressMonitor monitor) throws JavaModelException {
if (type == null) {
return;
}

SubMonitor subMonitor= SubMonitor.convert(monitor, 3);

collectDeclaredMethodNames(type, javaProject, testKindId, methodNames);
subMonitor.split(1);

String superclassName= type.getSuperclassName();
IType superType= getResolvedType(superclassName, type, javaProject);
collectMethodNames(superType, javaProject, testKindId, methodNames, subMonitor.split(1));

String[] superInterfaceNames= type.getSuperInterfaceNames();
subMonitor.setWorkRemaining(superInterfaceNames.length);
for (String interfaceName : superInterfaceNames) {
superType= getResolvedType(interfaceName, type, javaProject);
collectMethodNames(superType, javaProject, testKindId, methodNames, subMonitor.split(1));
}
}

private static IType getResolvedType(String typeName, IType type, IJavaProject javaProject) throws JavaModelException {
IType resolvedType= null;
if (typeName != null) {
int pos= typeName.indexOf('<');
if (pos != -1) {
typeName= typeName.substring(0, pos);
}
String[][] resolvedTypeNames= type.resolveType(typeName);
if (resolvedTypeNames != null && resolvedTypeNames.length > 0) {
String[] resolvedTypeName= resolvedTypeNames[0];
resolvedType= javaProject.findType(resolvedTypeName[0], resolvedTypeName[1]); // secondary types not found by this API
}
}
return resolvedType;
}

private static void collectDeclaredMethodNames(IType type, IJavaProject javaProject, String testKindId, Set<String> methodNames) throws JavaModelException {
IMethod[] methods= type.getMethods();
for (IMethod method : methods) {
String methodName= method.getElementName();
int flags= method.getFlags();
// Only include public, non-static, no-arg methods that return void and start with "test":
if (Modifier.isPublic(flags) && !Modifier.isStatic(flags) &&
method.getNumberOfParameters() == 0 && Signature.SIG_VOID.equals(method.getReturnType()) &&
methodName.startsWith("test")) { //$NON-NLS-1$
methodNames.add(methodName);
}
boolean isJUnit3= TestKindRegistry.JUNIT3_TEST_KIND_ID.equals(testKindId);
boolean isJUnit5= TestKindRegistry.JUNIT5_TEST_KIND_ID.equals(testKindId);
if (!isJUnit3 && !Modifier.isPrivate(flags) && !Modifier.isStatic(flags)) {
IAnnotation annotation= method.getAnnotation("Test"); //$NON-NLS-1$
if (annotation.exists()) {
methodNames.add(methodName + JUnitStubUtility.getParameterTypes(method, false));
} else if (isJUnit5) {
boolean hasAnyTestAnnotation= method.getAnnotation("TestFactory").exists() //$NON-NLS-1$
|| method.getAnnotation("Testable").exists() //$NON-NLS-1$
|| method.getAnnotation("TestTemplate").exists() //$NON-NLS-1$
|| method.getAnnotation("ParameterizedTest").exists() //$NON-NLS-1$
|| method.getAnnotation("RepeatedTest").exists(); //$NON-NLS-1$
if (hasAnyTestAnnotation || isAnnotatedWithTestable(method, type, javaProject)) {
methodNames.add(methodName + JUnitStubUtility.getParameterTypes(method, false));
}
}
}
}
}

// See JUnit5TestFinder.Annotation#annotates also.
private static boolean isAnnotatedWithTestable(IMethod method, IType declaringType, IJavaProject javaProject) throws JavaModelException {
for (IAnnotation annotation : method.getAnnotations()) {
IType annotationType= getResolvedType(annotation.getElementName(), declaringType, javaProject);
if (annotationType != null) {
if (matchesTestable(annotationType)) {
return true;
}
Set<IType> hierarchy= new HashSet<>();
if (matchesTestableInAnnotationHierarchy(annotationType, javaProject, hierarchy)) {
return true;
}
}
}
return false;
}

private static boolean matchesTestable(IType annotationType) {
return annotationType != null && JUnitCorePlugin.JUNIT5_TESTABLE_ANNOTATION_NAME.equals(annotationType.getFullyQualifiedName());
}

private static boolean matchesTestableInAnnotationHierarchy(IType annotationType, IJavaProject javaProject, Set<IType> hierarchy) throws JavaModelException {
if (annotationType != null) {
for (IAnnotation annotation : annotationType.getAnnotations()) {
IType annType= getResolvedType(annotation.getElementName(), annotationType, javaProject);
if (annType != null && hierarchy.add(annType)) {
if (matchesTestable(annType) || matchesTestableInAnnotationHierarchy(annType, javaProject, hierarchy)) {
return true;
}
}
}
}
return false;
}
}
Loading