Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="/inheritance-parent"/>
<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"/>
</classpath>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>inheritance-child</name>
<comment></comment>
<projects>
<project>inheritance-parent</project>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments></arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package example;

import org.junit.jupiter.api.Test;

public class InheritanceChildTest extends InheritanceBaseTest {
@Test
void child() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<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/4"/>
<classpathentry kind="output" path="target/test-classes"/>
</classpath>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>inheritance-junit4</name>
<comment></comment>
<projects></projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments></arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package example;

import org.junit.Test;

public class JUnit4BaseTest {
@Test
public void inherited() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package example;

public class JUnit4ChildTest extends JUnit4BaseTest {
@Override
public void inherited() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<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"/>
</classpath>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>inheritance-parent</name>
<comment></comment>
<projects></projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments></arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package example;

import org.junit.jupiter.api.Test;

public class InheritanceBaseTest {
@Test
void base() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package example;

import org.junit.jupiter.api.Test;

public class InheritanceBaseTest {
@Test
void base() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package example;

import org.junit.jupiter.api.Test;

public class InheritanceChildTest extends InheritanceBaseTest {
@Test
void child() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package example;

import org.junit.jupiter.api.Test;

public class InheritanceOverrideTest extends InheritanceBaseTest {
@Test
@Override
void base() {
}

@Test
void extra() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package example;

public class InheritanceUnannotatedOverrideTest extends InheritanceBaseTest {
@Override
void base() {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.junit.Test;

import com.microsoft.java.test.plugin.AbstractProjectsManagerBasedTest;
import com.microsoft.java.test.plugin.WorkspaceHelper;
import com.microsoft.java.test.plugin.model.Response;
import com.google.gson.Gson;

Expand Down Expand Up @@ -82,4 +83,28 @@ public void testAddOpensForAllSelectedPackagesInModularProject() throws Exceptio
assertEquals("--add-opens", vmArguments.get(vmArguments.indexOf(secondPackageOpen) - 1));
}

@Test
public void testInheritedMethodLaunchesAgainstChildClass() throws Exception {
importProjects(Arrays.asList("inheritance-parent", "inheritance-child"));
final IProject childProject = WorkspaceHelper.getProject("inheritance-child");
final IJavaProject childJavaProject = JavaCore.create(childProject);
final IType parentType = JavaCore.create(WorkspaceHelper.getProject("inheritance-parent"))
.findType("example.InheritanceBaseTest");

final Map<String, Object> request = new LinkedHashMap<>();
request.put("projectName", childProject.getName());
request.put("testLevel", 6);
request.put("testKind", 0);
request.put("testNames", Arrays.asList(parentType.getMethod("base", new String[0]).getHandleIdentifier()));
request.put("executionClassNames", Arrays.asList("example.InheritanceChildTest"));

final Response<JUnitLaunchArguments> response = JUnitLaunchUtils.resolveLaunchArgument(
Arrays.asList(new Gson().toJson(request)), new NullProgressMonitor());

assertEquals(0, response.getStatus());
assertEquals(childJavaProject.getElementName(), response.getBody().projectName);
assertTrue(Arrays.asList(response.getBody().programArguments)
.contains("example.InheritanceChildTest:base"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

import com.microsoft.java.test.plugin.AbstractProjectsManagerBasedTest;
import com.microsoft.java.test.plugin.model.JavaTestItem;
import com.microsoft.java.test.plugin.WorkspaceHelper;

public class TestSearchUtilsTest extends AbstractProjectsManagerBasedTest {

Expand Down Expand Up @@ -77,10 +78,12 @@ public void testDiscoverJUnit5TestsWithOlderPreviewSource() throws Exception {
final List<JavaTestItem> packages = TestSearchUtils.findTestPackagesAndTypes(
Arrays.asList(javaProject.getHandleIdentifier()), new NullProgressMonitor());
assertEquals(1, packages.size());
assertEquals(3, packages.get(0).getChildren().size());
assertEquals(7, packages.get(0).getChildren().size());
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()
.anyMatch(item -> "InheritanceChildTest".equals(item.getLabel())));

final List<JavaTestItem> methods = TestSearchUtils.findDirectTestChildrenForClass(
Arrays.asList(type.getHandleIdentifier()), new NullProgressMonitor());
Expand All @@ -94,4 +97,86 @@ public void testDiscoverJUnit5TestsWithOlderPreviewSource() throws Exception {
assertEquals("Child", nestedChildren.get(0).getLabel());
assertEquals(JavaCore.ENABLED, javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true));
}

@Test
public void testDiscoverInheritedTestMethods() throws Exception {
final IProject project = importProjects("preview-junit").get(0);
final IJavaProject javaProject = JavaCore.create(project);
final IType childType = javaProject.findType("example.InheritanceChildTest");
assertNotNull(childType);

final List<JavaTestItem> childMethods = TestSearchUtils.findDirectTestChildrenForClass(
Arrays.asList(childType.getHandleIdentifier()), new NullProgressMonitor());
assertEquals(2, childMethods.size());
assertTrue(childMethods.stream().anyMatch(
item -> "example.InheritanceChildTest#child()".equals(item.getFullName())));
assertTrue(childMethods.stream().anyMatch(
item -> "example.InheritanceChildTest#base()".equals(item.getFullName())));
for (final JavaTestItem item : childMethods) {
assertTrue(item.getId().startsWith(item.getProjectName() + "@example.InheritanceChildTest#"));
}

final IType baseType = javaProject.findType("example.InheritanceBaseTest");
final List<JavaTestItem> baseMethods = TestSearchUtils.findDirectTestChildrenForClass(
Arrays.asList(baseType.getHandleIdentifier()), new NullProgressMonitor());
assertEquals(1, baseMethods.size());
assertEquals("example.InheritanceBaseTest#base()", baseMethods.get(0).getFullName());

final IType overrideType = javaProject.findType("example.InheritanceOverrideTest");
final List<JavaTestItem> overrideMethods = TestSearchUtils.findDirectTestChildrenForClass(
Arrays.asList(overrideType.getHandleIdentifier()), new NullProgressMonitor());
assertEquals(2, overrideMethods.size());
assertTrue(overrideMethods.stream().anyMatch(
item -> "example.InheritanceOverrideTest#base()".equals(item.getFullName())));
assertTrue(overrideMethods.stream().anyMatch(
item -> "example.InheritanceOverrideTest#extra()".equals(item.getFullName())));
}

@Test
public void testInheritedMethodUsesChildProjectAndExecutionClass() throws Exception {
importProjects(Arrays.asList("inheritance-parent", "inheritance-child"));
final IProject childProject = WorkspaceHelper.getProject("inheritance-child");
final IJavaProject javaProject = JavaCore.create(childProject);
final IType childType = javaProject.findType("example.InheritanceChildTest");
final IType parentType = JavaCore.create(WorkspaceHelper.getProject("inheritance-parent"))
.findType("example.InheritanceBaseTest");
assertNotNull(childType);
assertNotNull(parentType);

final List<JavaTestItem> methods = TestSearchUtils.findDirectTestChildrenForClass(
Arrays.asList(childType.getHandleIdentifier()), new NullProgressMonitor());
final JavaTestItem inherited = methods.stream()
.filter(item -> item.getFullName().endsWith("#base()"))
.findFirst().orElse(null);
assertNotNull(inherited);
assertEquals(childProject.getName(), inherited.getProjectName());
assertEquals(childProject.getName() + "@example.InheritanceChildTest#base()", inherited.getId());
assertEquals("example.InheritanceChildTest", inherited.getExecutionClassName());
assertEquals(parentType.getMethod("base", new String[0]).getHandleIdentifier(), inherited.getJdtHandler());
}

@Test
public void testJUnit4TestSurvivesUnannotatedOverride() throws Exception {
importProjects("inheritance-junit4");
final IJavaProject javaProject = JavaCore.create(WorkspaceHelper.getProject("inheritance-junit4"));
final IType childType = javaProject.findType("example.JUnit4ChildTest");
assertNotNull(childType);

final List<JavaTestItem> methods = TestSearchUtils.findDirectTestChildrenForClass(
Arrays.asList(childType.getHandleIdentifier()), new NullProgressMonitor());
assertEquals(1, methods.size());
assertEquals("example.JUnit4ChildTest#inherited", methods.get(0).getFullName());
assertEquals("example.JUnit4ChildTest", methods.get(0).getExecutionClassName());
}

@Test
public void testJUnit5UnannotatedOverrideSuppressesInheritedTest() throws Exception {
final IProject project = importProjects("preview-junit").get(0);
final IType childType = JavaCore.create(project).findType("example.InheritanceUnannotatedOverrideTest");
assertNotNull(childType);

final List<JavaTestItem> methods = TestSearchUtils.findDirectTestChildrenForClass(
Arrays.asList(childType.getHandleIdentifier()), new NullProgressMonitor());
assertTrue(methods.isEmpty());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ private void addTestItemArgs(List<String> arguments) throws CoreException {
arguments.add(fileName);
} else {
arguments.add("-test");
arguments.add(resolveMethodTestName(this.args.testNames[0]));
arguments.add(resolveMethodTestName(this.args.testNames[0], getExecutionClassName(0)));

if (StringUtils.isNotBlank(this.args.uniqueId)) {
arguments.add("-uniqueId");
Expand All @@ -202,7 +202,7 @@ private void addTestItemArgs(List<String> arguments) throws CoreException {
}
}

private String resolveMethodTestName(String handleId) throws CoreException {
private String resolveMethodTestName(String handleId, String executionClassName) throws CoreException {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the base class comes from a dependency JAR, running an inherited test like base(TestInfo info) on its own fails here. getCompilationUnit() is null for a binary method, so we get Cannot get compilation unit of methodbase. Running the whole child class avoids this, but running or debugging just that method doesn't.

Could we get the parameter types from an IMethodBinding for binary methods instead of looking for source? We can leave the current source-method path as-is and keep using the child class for execution. A test with a compiled base class and a method parameter would catch this.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 57344f1. For parameterized JUnit 5/6 methods without a compilation unit, the launch resolver now uses ASTParser.createBindings() to obtain an IMethodBinding and formats its parameter types. The source-backed AST path remains unchanged, and the child class is still used as the execution target. Added a regression with a parent compiled to target/test-classes and a child project referencing it as a binary library; it verifies the selector example.InheritanceBinaryChildTest:base(org.junit.jupiter.api.TestInfo). The full Java reactor passes all 12 plugin tests.

final IMethod method = (IMethod) JavaCore.create(handleId);
String testName = method.getElementName();
if ((this.args.testKind == TestKind.JUnit5 || this.args.testKind == TestKind.JUnit6) &&
Expand Down Expand Up @@ -239,7 +239,14 @@ private String resolveMethodTestName(String handleId) throws CoreException {
testName += "(" + String.join(",", parameters) + ")";
}
}
return method.getDeclaringType().getFullyQualifiedName() + ':' + testName;
final String className = StringUtils.isNotBlank(executionClassName) ? executionClassName :
method.getDeclaringType().getFullyQualifiedName();
return className + ':' + testName;
}

private String getExecutionClassName(int index) {
return this.args.executionClassNames != null && index < this.args.executionClassNames.length ?
this.args.executionClassNames[index] : null;
}

private String createTestNamesFile(String[] testNames) throws CoreException {
Expand All @@ -266,8 +273,8 @@ private String createMethodTestNamesFile(String[] testNames) throws CoreExceptio
file.deleteOnExit();
try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(file), StandardCharsets.UTF_8));) {
for (final String handleId : testNames) {
bw.write(resolveMethodTestName(handleId));
for (int index = 0; index < testNames.length; index++) {
bw.write(resolveMethodTestName(testNames[index], getExecutionClassName(index)));
bw.newLine();
}
}
Expand Down
Loading