Skip to content
Open
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
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/java"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="test" value="true"/>
Expand All @@ -8,5 +9,5 @@
<classpathentry kind="con"
path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
<classpathentry kind="output" path="target/test-classes"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>preview-junit</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.14.0</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package example;

public class AdditionalMainSource {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package example;

import org.junit.jupiter.api.Test;

public class AdditionalMainSourceTest {

@Test
public void mainSourceTest() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.Collection;
import java.util.List;

import org.eclipse.core.resources.IProject;
Expand Down Expand Up @@ -81,7 +84,27 @@ public void testDiscoverJUnit5TestsWithOlderPreviewSource() throws Exception {
assertTrue(packages.get(0).getChildren().stream().anyMatch(item -> "MiniTest".equals(item.getLabel())));
assertTrue(packages.get(0).getChildren().stream().anyMatch(item -> "SiblingTest".equals(item.getLabel())));
assertTrue(packages.get(0).getChildren().stream().anyMatch(item -> "NestedOnlyTest".equals(item.getLabel())));
assertTrue(packages.get(0).getChildren().stream()
.noneMatch(item -> "AdditionalMainSourceTest".equals(item.getLabel())));

final String mainSourcePath = javaProject.getPackageFragmentRoot(project.getFolder("src/main/java"))
.getResource().getLocation().toOSString();
final List<JavaTestItem> packagesWithAdditionalSource = TestSearchUtils.findTestPackagesAndTypes(
Arrays.<Object>asList(javaProject.getHandleIdentifier(), Collections.singletonList(mainSourcePath)),
new NullProgressMonitor());
assertTrue(packagesWithAdditionalSource.get(0).getChildren().stream()
.anyMatch(item -> "AdditionalMainSourceTest".equals(item.getLabel())));
assertTrue(packagesWithAdditionalSource.get(0).getChildren().stream()
.anyMatch(item -> "MiniTest".equals(item.getLabel())));

final String additionalTestUri = javaProject.findType("example.AdditionalMainSource")
.getResource().getLocationURI().toString();
final Object defaultNavigation = TestNavigationUtils.findTestOrTarget(
Arrays.asList(additionalTestUri, true), new NullProgressMonitor());
assertTrue(getNavigationItems(defaultNavigation).isEmpty());
final Object navigationWithAdditionalSource = TestNavigationUtils.findTestOrTarget(Arrays.<Object>asList(
additionalTestUri, true, Collections.singletonList(mainSourcePath)), new NullProgressMonitor());
assertEquals(1, getNavigationItems(navigationWithAdditionalSource).size());
final List<JavaTestItem> methods = TestSearchUtils.findDirectTestChildrenForClass(
Arrays.asList(type.getHandleIdentifier()), new NullProgressMonitor());
assertEquals(2, methods.size());
Expand All @@ -94,4 +117,10 @@ public void testDiscoverJUnit5TestsWithOlderPreviewSource() throws Exception {
assertEquals("Child", nestedChildren.get(0).getLabel());
assertEquals(JavaCore.ENABLED, javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true));
}

private static Collection<?> getNavigationItems(Object navigationResult) throws Exception {
final Field itemsField = navigationResult.getClass().getField("items");
itemsField.setAccessible(true);
return (Collection<?>) itemsField.get(navigationResult);
}
}
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 @@ -4,6 +4,7 @@
<extension point="org.eclipse.jdt.ls.core.delegateCommandHandler">
<delegateCommandHandler class="com.microsoft.java.test.plugin.handler.TestDelegateCommandHandler">
<command id="vscode.java.test.get.testpath" />
<command id="vscode.java.test.get.sourcepath" />
<command id="vscode.java.test.junit.argument" />
<command id="vscode.java.test.generateTests" />
<command id="vscode.java.test.findJavaProjects" />
Expand Down
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 GET_TEST_SOURCE_PATH = "vscode.java.test.get.testpath";
private static final String GET_SOURCE_PATH = "vscode.java.test.get.sourcepath";
private static final String RESOLVE_JUNIT_ARGUMENT = "vscode.java.test.junit.argument";
private static final String GENERATE_TESTS = "vscode.java.test.generateTests";
private static final String FIND_JAVA_PROJECT = "vscode.java.test.findJavaProjects";
Expand All @@ -51,6 +52,8 @@ public Object executeCommand(String commandId, List<Object> arguments, IProgress
switch (commandId) {
case GET_TEST_SOURCE_PATH:
return ProjectTestUtils.listTestSourcePaths(arguments, monitor);
case GET_SOURCE_PATH:
return ProjectTestUtils.listSourcePaths(arguments, monitor);
case RESOLVE_JUNIT_ARGUMENT:
return JUnitLaunchUtils.resolveLaunchArgument(arguments, monitor);
case GENERATE_TESTS:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.ClasspathEntry;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
Expand All @@ -25,11 +26,17 @@
import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;

import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -66,6 +73,78 @@ public static List<TestSourcePath> listTestSourcePaths(List<Object> arguments, I
return resultList;
}

@SuppressWarnings("unchecked")
public static List<TestSourcePath> listSourcePaths(List<Object> arguments, IProgressMonitor monitor)
throws JavaModelException {
if (arguments == null || arguments.isEmpty()) {
return Collections.emptyList();
}

final ArrayList<String> uriArray = (ArrayList<String>) arguments.get(0);
final Map<String, TestSourcePath> sourcePaths = new LinkedHashMap<>();
for (final String uri : uriArray) {
if (monitor != null && monitor.isCanceled()) {
return Collections.emptyList();
}
for (final IJavaProject project : parseProjects(uri)) {
for (final IPackageFragmentRoot root : getSourceRoots(project)) {
if (monitor != null && monitor.isCanceled()) {
return Collections.emptyList();
}
if (root.getResource() != null && root.getResource().getLocation() != null) {
final String path = root.getResource().getLocation().toOSString();
sourcePaths.putIfAbsent(path, new TestSourcePath(path, false));
}
}
}
}
return new ArrayList<>(sourcePaths.values());
}

public static List<IPackageFragmentRoot> getSourceRoots(IJavaProject project) throws JavaModelException {
if (ProjectsManager.DEFAULT_PROJECT_NAME.equals(project.getProject().getName())) {
return Collections.emptyList();
}

final List<IPackageFragmentRoot> roots = new LinkedList<>();
for (final IClasspathEntry entry : project.getRawClasspath()) {
if (entry.getEntryKind() == ClasspathEntry.CPE_SOURCE) {
roots.addAll(Arrays.asList(project.findPackageFragmentRoots(entry)));
}
}
return roots;
}

public static Set<IPackageFragmentRoot> getSourceRoots(IJavaProject project, Collection<String> selectedPaths,
IProgressMonitor monitor) throws JavaModelException {
final Set<String> requestedPaths = new HashSet<>();
for (final String selectedPath : selectedPaths) {
if (monitor != null && monitor.isCanceled()) {
return Collections.emptySet();
}
requestedPaths.add(normalizeSourcePath(selectedPath));
}
if (requestedPaths.isEmpty()) {
return Collections.emptySet();
}

final Set<IPackageFragmentRoot> matchingRoots = new LinkedHashSet<>();
for (final IPackageFragmentRoot root : getSourceRoots(project)) {
if (monitor != null && monitor.isCanceled()) {
return Collections.emptySet();
}
if (root.getResource() != null && root.getResource().getLocation() != null &&
requestedPaths.contains(normalizeSourcePath(root.getResource().getLocation().toOSString()))) {
matchingRoots.add(root);
}
}
return matchingRoots;
}

private static String normalizeSourcePath(String sourcePath) {
return Paths.get(sourcePath).toAbsolutePath().normalize().toString();
}

public static List<IClasspathEntry> getTestEntries(IJavaProject project) throws JavaModelException {
return getClasspathEntries(project, true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,16 @@ public static TestNavigationResult findTestOrTarget(List<Object> arguments, IPro
}
final SearchEngine searchEngine = new SearchEngine();
final boolean goToTest = (boolean) arguments.get(1);
final List<String> additionalSourcePaths = new LinkedList<>();
if (arguments.size() > 2 && arguments.get(2) instanceof List<?>) {
for (final Object value : (List<?>) arguments.get(2)) {
if (value instanceof String) {
additionalSourcePaths.add((String) value);
}
}
}
final String nameToSearch = goToTest ? typeName : guessTestSubjectName(typeName);
final IJavaSearchScope scope = getSearchScope(goToTest);
final IJavaSearchScope scope = getSearchScope(goToTest, additionalSourcePaths, monitor);
final IJavaProject javaProject = unit.getJavaProject();
final Set<TestNavigationItem> items = new HashSet<>();
searchEngine.searchAllTypeNames(
Expand All @@ -96,7 +104,8 @@ public static TestNavigationResult findTestOrTarget(List<Object> arguments, IPro
* @param isTest Whether containing test source or not
* @throws JavaModelException
*/
private static IJavaSearchScope getSearchScope(boolean isTest) throws JavaModelException {
private static IJavaSearchScope getSearchScope(boolean isTest, List<String> additionalSourcePaths,
IProgressMonitor monitor) throws JavaModelException {
final List<IJavaElement> javaElements = new LinkedList<>();
final IJavaProject[] javaProjects = ProjectUtils.getJavaProjects();
for (final IJavaProject project : javaProjects) {
Expand All @@ -105,6 +114,9 @@ private static IJavaSearchScope getSearchScope(boolean isTest) throws JavaModelE
for (final IClasspathEntry entry : testEntries) {
javaElements.addAll(Arrays.asList(project.findPackageFragmentRoots(entry)));
}
if (isTest && !additionalSourcePaths.isEmpty()) {
javaElements.addAll(ProjectTestUtils.getSourceRoots(project, additionalSourcePaths, monitor));
}
}

return SearchEngine.createJavaSearchScope(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@
import org.eclipse.jdt.ls.core.internal.handlers.DocumentLifeCycleHandler;
import org.eclipse.lsp4j.Location;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -144,7 +146,7 @@ public static List<JavaTestItem> findJavaProjects(List<Object> arguments, IProgr
* Package B
* ├── ...
*
* @param arguments argument list which contains the JDT handler ID of a Java project
* @param arguments argument list which contains the JDT handler ID and optional selected source-root paths
* @param monitor monitor
* @throws CoreException
*/
Expand All @@ -159,36 +161,40 @@ public static List<JavaTestItem> findTestPackagesAndTypes(List<Object> arguments
final IJavaProject javaProject = (IJavaProject) element;
final Map<String, JavaTestItem> testItemMapping = new HashMap<>();
final List<TestKind> testKinds = TestKindProvider.getTestKindsFromCache(javaProject);
final Set<IPackageFragmentRoot> rootsToSearch = new LinkedHashSet<>();
for (final IClasspathEntry entry : ProjectTestUtils.getTestEntries(javaProject)) {
rootsToSearch.addAll(Arrays.asList(javaProject.findPackageFragmentRoots(entry)));
}
rootsToSearch.addAll(getAdditionalSourceRoots(javaProject, arguments, monitor));
for (final TestKind kind : testKinds) {
if (monitor != null && monitor.isCanceled()) {
return Collections.emptyList();
}
final TestFrameworkSearcher searcher = TestFrameworkUtils.getSearcherByTestKind(kind);
final Set<IType> testTypes = new HashSet<>();
final List<IClasspathEntry> testEntries = ProjectTestUtils.getTestEntries(javaProject);
for (final IClasspathEntry entry : testEntries) {
final IPackageFragmentRoot[] packageRoots = javaProject.findPackageFragmentRoots(entry);
for (final IPackageFragmentRoot root : packageRoots) {
try {
testTypes.addAll(searcher.findTestItemsInContainer(root, monitor));
} catch (CoreException e) {
JUnitPlugin.logException("failed to search tests in: " + root.getElementName(), e);
}
if ((kind == TestKind.JUnit5 || kind == TestKind.JUnit6) &&
requiresPreviewFallback(javaProject) && root.getKind() == IPackageFragmentRoot.K_SOURCE) {
for (final IJavaElement child : root.getChildren()) {
if (!(child instanceof IPackageFragment)) {
continue;
for (final IPackageFragmentRoot root : rootsToSearch) {
if (monitor != null && monitor.isCanceled()) {
return Collections.emptyList();
}
try {
testTypes.addAll(searcher.findTestItemsInContainer(root, monitor));
} catch (CoreException e) {
JUnitPlugin.logException("failed to search tests in: " + root.getElementName(), e);
}
if ((kind == TestKind.JUnit5 || kind == TestKind.JUnit6) &&
requiresPreviewFallback(javaProject) && root.getKind() == IPackageFragmentRoot.K_SOURCE) {
for (final IJavaElement child : root.getChildren()) {
if (!(child instanceof IPackageFragment)) {
continue;
}
for (final ICompilationUnit unit : ((IPackageFragment) child).getCompilationUnits()) {
if (monitor != null && monitor.isCanceled()) {
return Collections.emptyList();
}
for (final ICompilationUnit unit : ((IPackageFragment) child).getCompilationUnits()) {
if (monitor != null && monitor.isCanceled()) {
return Collections.emptyList();
}
for (final JavaTestItem item : findTestTypesAndMethods(unit, monitor)) {
final IJavaElement testType = JavaCore.create(item.getJdtHandler());
if (testType instanceof IType && item.getTestKind() == kind) {
testTypes.add((IType) testType);
}
for (final JavaTestItem item : findTestTypesAndMethods(unit, monitor)) {
final IJavaElement testType = JavaCore.create(item.getJdtHandler());
if (testType instanceof IType && item.getTestKind() == kind) {
testTypes.add((IType) testType);
}
}
}
Expand Down Expand Up @@ -269,6 +275,24 @@ public static List<JavaTestItem> findTestPackagesAndTypes(List<Object> arguments
return new LinkedList<>(mergedPackages.values());
}

private static Set<IPackageFragmentRoot> getAdditionalSourceRoots(IJavaProject javaProject,
List<Object> arguments, IProgressMonitor monitor) throws JavaModelException {
if (arguments == null || arguments.size() < 2 || !(arguments.get(1) instanceof List<?>)) {
return Collections.emptySet();
}

final Set<String> requestedPaths = new HashSet<>();
for (final Object value : (List<?>) arguments.get(1)) {
if (monitor != null && monitor.isCanceled()) {
return Collections.emptySet();
}
if (value instanceof String) {
requestedPaths.add((String) value);
}
}
return ProjectTestUtils.getSourceRoots(javaProject, requestedPaths, monitor);
}

/**
* Find the direct declared testable class and method for a given class
* @param arguments
Expand Down
Loading