Skip to content

Commit

Permalink
List test classes & methods in a file (#1243)
Browse files Browse the repository at this point in the history
  • Loading branch information
jdneo authored Jul 29, 2021
1 parent 155611a commit 21cf7f3
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 0 deletions.
1 change: 1 addition & 0 deletions java-extension/com.microsoft.java.test.plugin/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<command id="vscode.java.test.findJavaProjects" />
<command id="vscode.java.test.findTestPackagesAndTypes" />
<command id="vscode.java.test.findDirectTestChildrenForClass" />
<command id="vscode.java.test.findTestTypesAndMethods" />
</delegateCommandHandler>
</extension>
</plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class TestDelegateCommandHandler implements IDelegateCommandHandler {
private static final String FIND_JAVA_PROJECT = "vscode.java.test.findJavaProjects";
private static final String FIND_PACKAGES_AND_TYPES = "vscode.java.test.findTestPackagesAndTypes";
private static final String FIND_DIRECT_CHILDREN_FOR_CLASS = "vscode.java.test.findDirectTestChildrenForClass";
private static final String FIND_TYPES_AND_METHODS = "vscode.java.test.findTestTypesAndMethods";

@Override
public Object executeCommand(String commandId, List<Object> arguments, IProgressMonitor monitor) throws Exception {
Expand All @@ -51,6 +52,8 @@ public Object executeCommand(String commandId, List<Object> arguments, IProgress
return TestSearchUtils.findTestPackagesAndTypes(arguments, monitor);
case FIND_DIRECT_CHILDREN_FOR_CLASS:
return TestSearchUtils.findDirectTestChildrenForClass(arguments, monitor);
case FIND_TYPES_AND_METHODS:
return TestSearchUtils.findTestTypesAndMethods(arguments, monitor);
default:
throw new UnsupportedOperationException(
String.format("Java test plugin doesn't support the command '%s'.", commandId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.manipulation.CoreASTProvider;
import org.eclipse.jdt.internal.junit.util.CoreTestSearchEngine;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
import org.eclipse.jdt.ls.core.internal.ResourceUtils;
Expand All @@ -52,6 +54,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@SuppressWarnings("restriction")
public class TestSearchUtils {
Expand Down Expand Up @@ -268,6 +271,120 @@ public static List<JavaTestItem> findDirectTestChildrenForClass(List<Object> arg
return result;
}

/**
* Get all the test types and methods is the given file
* @param arguments Contains the target file's uri
* @param monitor Progress monitor
* @throws CoreException
* @throws OperationCanceledException
* @throws InterruptedException
*/
public static List<JavaTestItem> findTestTypesAndMethods(List<Object> arguments, IProgressMonitor monitor)
throws CoreException, OperationCanceledException, InterruptedException {
// todo: This method is somehow duplicated with findDirectTestChildrenForClass, considering merge them in the future.
final String uriString = (String) arguments.get(0);

// wait for the LS finishing updating
Job.getJobManager().join(DocumentLifeCycleHandler.DOCUMENT_LIFE_CYCLE_JOBS, monitor);

final ICompilationUnit unit = JDTUtils.resolveCompilationUnit(uriString);
if (unit == null) {
return Collections.emptyList();
}

final IType primaryType = unit.findPrimaryType();
if (primaryType == null) {
return Collections.emptyList();
}

final CompilationUnit root = (CompilationUnit) parseToAst(unit, true /* fromCache */, monitor);
if (root == null) {
return Collections.emptyList();
}

final List<TestKind> testKinds = TestKindProvider.getTestKindsFromCache(unit.getJavaProject());
final List<TestFrameworkSearcher> searchers = new LinkedList<>();
for (final TestKind kind : testKinds) {
final TestFrameworkSearcher searcher = TestFrameworkUtils.getSearcherByTestKind(kind);
if (searcher != null) {
searchers.add(searcher);
}
}

if (searchers.size() == 0) {
Collections.emptyList();
}

final ASTNode node = root.findDeclaringNode(primaryType.getKey());
if (!(node instanceof TypeDeclaration)) {
return Collections.emptyList();
}

final ITypeBinding binding = ((TypeDeclaration) node).resolveBinding();
if (binding == null) {
return Collections.emptyList();
}

final JavaTestItem fakeRoot = new JavaTestItem();
findTestItemsInTypeBinding(binding, fakeRoot, searchers, monitor);
return fakeRoot.getChildren();
}

private static void findTestItemsInTypeBinding(ITypeBinding typeBinding, JavaTestItem parentItem,
List<TestFrameworkSearcher> searchers, IProgressMonitor monitor) throws JavaModelException {
if (monitor.isCanceled()) {
return;
}

final IType type = (IType) typeBinding.getJavaElement();
final List<JavaTestItem> testMethods = new LinkedList<>();
searchers = searchers.stream().filter(s -> {
try {
return CoreTestSearchEngine.isAccessibleClass(type, s.getJdtTestKind());
} catch (JavaModelException e) {
return false;
}
}).collect(Collectors.toList());

for (final IMethodBinding methodBinding : typeBinding.getDeclaredMethods()) {
for (final TestFrameworkSearcher searcher : searchers) {
if (searcher.isTestMethod(methodBinding)) {
final JavaTestItem methodItem = TestItemUtils.constructJavaTestItem(
(IMethod) methodBinding.getJavaElement(),
TestLevel.METHOD,
searcher.getTestKind()
);
testMethods.add(methodItem);
break;
}
}
}

JavaTestItem classItem = null;
if (testMethods.size() > 0) {
classItem = TestItemUtils.constructJavaTestItem(type, TestLevel.CLASS, testMethods.get(0).getTestKind());
classItem.setChildren(testMethods);
} else {
if (TestFrameworkUtils.JUNIT4_TEST_SEARCHER.isTestClass(type)) {
// to handle @RunWith classes
classItem = TestItemUtils.constructJavaTestItem(type, TestLevel.CLASS, TestKind.JUnit);
} else if (TestFrameworkUtils.JUNIT5_TEST_SEARCHER.isTestClass(type)) {
// to handle @Nested and @Testable classes
classItem = TestItemUtils.constructJavaTestItem(type, TestLevel.CLASS, TestKind.JUnit5);
}
}

// set the class item as the child of its declaring type
if (classItem != null && parentItem != null) {
parentItem.addChild(classItem);
}

for (final ITypeBinding childTypeBinding : typeBinding.getDeclaredTypes()) {
findTestItemsInTypeBinding(childTypeBinding, classItem, searchers, monitor);
}

}

public static ASTNode parseToAst(final ICompilationUnit unit, final boolean fromCache,
final IProgressMonitor monitor) {
if (fromCache) {
Expand Down

0 comments on commit 21cf7f3

Please sign in to comment.