Skip to content

Commit f2d3ee5

Browse files
committed
Running Kotlin and Spock tests
1 parent e5e4aff commit f2d3ee5

11 files changed

Lines changed: 337 additions & 81 deletions

File tree

java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/model/builder/JavaTestItemBuilder.java

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
import com.microsoft.java.test.plugin.model.TestKind;
1616
import com.microsoft.java.test.plugin.model.TestLevel;
1717
import com.microsoft.java.test.plugin.util.TestItemUtils;
18-
1918
import org.eclipse.core.resources.IProject;
19+
import org.eclipse.core.resources.IResource;
2020
import org.eclipse.core.runtime.IPath;
2121
import org.eclipse.jdt.core.IJavaElement;
2222
import org.eclipse.jdt.core.IJavaProject;
2323
import org.eclipse.jdt.core.IPackageFragment;
2424
import org.eclipse.jdt.core.JavaModelException;
25+
import org.eclipse.jdt.internal.core.PackageFragmentRoot;
2526
import org.eclipse.jdt.internal.core.manipulation.JavaElementLabelsCore;
2627
import org.eclipse.jdt.ls.core.internal.JDTUtils;
2728
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
@@ -36,6 +37,7 @@ public class JavaTestItemBuilder {
3637
private IJavaElement element;
3738
private TestLevel level;
3839
private TestKind kind;
40+
private String displayName;
3941

4042
public JavaTestItemBuilder setJavaElement(IJavaElement element) {
4143
this.element = element;
@@ -52,31 +54,50 @@ public JavaTestItemBuilder setKind(TestKind kind) {
5254
return this;
5355
}
5456

57+
public JavaTestItemBuilder setDisplayName(String displayName) {
58+
this.displayName = displayName;
59+
return this;
60+
}
61+
5562
public JavaTestItem build() throws JavaModelException {
5663
if (this.element == null || this.level == null || this.kind == null) {
5764
throw new IllegalArgumentException("Failed to build Java test item due to missing arguments");
5865
}
5966

60-
final String displayName;
6167
String uri = null;
62-
if (this.element instanceof IJavaProject) {
63-
final IJavaProject javaProject = (IJavaProject) this.element;
64-
final IProject project = javaProject.getProject();
65-
if (ProjectUtils.isVisibleProject(project)) {
66-
displayName = project.getName();
68+
if (this.displayName == null) {
69+
if (this.element instanceof IJavaProject) {
70+
final IJavaProject javaProject = (IJavaProject) this.element;
71+
final IProject project = javaProject.getProject();
72+
if (ProjectUtils.isVisibleProject(project)) {
73+
displayName = project.getName();
74+
} else {
75+
final IPath realPath = ProjectUtils.getProjectRealFolder(project);
76+
displayName = realPath.lastSegment();
77+
uri = realPath.toFile().toURI().toString();
78+
}
79+
} else if (this.element instanceof IPackageFragment &&
80+
((IPackageFragment) this.element).isDefaultPackage()) {
81+
displayName = DEFAULT_PACKAGE_NAME;
82+
final IResource resource = getResource((IPackageFragment) this.element);
83+
if (resource == null || !resource.exists()) {
84+
return null;
85+
}
86+
uri = JDTUtils.getFileURI(resource);
6787
} else {
68-
final IPath realPath = ProjectUtils.getProjectRealFolder(project);
69-
displayName = realPath.lastSegment();
70-
uri = realPath.toFile().toURI().toString();
88+
displayName = JavaElementLabelsCore.getElementLabel(this.element, JavaElementLabelsCore.ALL_DEFAULT);
7189
}
72-
} else if (this.element instanceof IPackageFragment && ((IPackageFragment) this.element).isDefaultPackage()) {
73-
displayName = DEFAULT_PACKAGE_NAME;
74-
} else {
75-
displayName = JavaElementLabelsCore.getElementLabel(this.element, JavaElementLabelsCore.ALL_DEFAULT);
7690
}
7791
final String fullName = TestItemUtils.parseFullName(this.element, this.level, this.kind);
7892
if (uri == null) {
79-
uri = JDTUtils.getFileURI(this.element.getResource());
93+
IResource resource = this.element.getResource();
94+
if (resource == null && this.element instanceof IPackageFragment) {
95+
resource = getResource((IPackageFragment) this.element);
96+
}
97+
if (resource == null || !resource.exists()) {
98+
return null;
99+
}
100+
uri = JDTUtils.getFileURI(resource);
80101
}
81102
Range range = null;
82103
if (this.level == TestLevel.CLASS || this.level == TestLevel.METHOD) {
@@ -89,4 +110,22 @@ public JavaTestItem build() throws JavaModelException {
89110

90111
return result;
91112
}
113+
114+
private IResource getResource(IPackageFragment packageFragment) {
115+
if (packageFragment == null) {
116+
return null;
117+
}
118+
IResource resource = packageFragment.getResource();
119+
if (resource == null) {
120+
final IJavaElement e = packageFragment.getParent();
121+
if (e instanceof PackageFragmentRoot) {
122+
final PackageFragmentRoot root = (PackageFragmentRoot) e;
123+
resource = root.getResource();
124+
if (resource == null) {
125+
resource = root.resource(root);
126+
}
127+
}
128+
}
129+
return resource;
130+
}
92131
}

java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit4TestSearcher.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,9 @@ public Set<IType> findTestItemsInContainer(IJavaElement element, IProgressMonito
7777

7878
return types;
7979
}
80+
81+
@Override
82+
public String getDisplayName(IMethodBinding methodBinding) {
83+
return null;
84+
}
8085
}

java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit5TestSearcher.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.eclipse.jdt.core.IType;
2222
import org.eclipse.jdt.core.JavaModelException;
2323
import org.eclipse.jdt.core.dom.IAnnotationBinding;
24+
import org.eclipse.jdt.core.dom.IMemberValuePairBinding;
2425
import org.eclipse.jdt.core.dom.IMethodBinding;
2526
import org.eclipse.jdt.core.dom.ITypeBinding;
2627
import org.eclipse.jdt.core.dom.Modifier;
@@ -40,9 +41,11 @@ public class JUnit5TestSearcher extends BaseFrameworkSearcher {
4041

4142
protected static final String DISPLAY_NAME_ANNOTATION_JUNIT5 = "org.junit.jupiter.api.DisplayName";
4243

44+
protected static final String SPOCK_FEATURE_METADATA = "org.spockframework.runtime.model.FeatureMetadata";
45+
4346
public JUnit5TestSearcher() {
4447
super();
45-
this.testMethodAnnotations = new String[] { JUNIT_PLATFORM_TESTABLE };
48+
this.testMethodAnnotations = new String[] { JUNIT_PLATFORM_TESTABLE, SPOCK_FEATURE_METADATA };
4649
}
4750

4851
@Override
@@ -133,4 +136,19 @@ public Set<IType> findTestItemsInContainer(IJavaElement element, IProgressMonito
133136
}
134137
return types;
135138
}
139+
140+
@Override
141+
public String getDisplayName(IMethodBinding methodBinding) {
142+
for (final IAnnotationBinding annotation : methodBinding.getAnnotations()) {
143+
if (matchesName(annotation.getAnnotationType(), SPOCK_FEATURE_METADATA)) {
144+
final IMemberValuePairBinding[] pairs = annotation.getDeclaredMemberValuePairs();
145+
for (final IMemberValuePairBinding pair : pairs) {
146+
if ("name".equals(pair.getName()) && (pair.getValue() instanceof String)) {
147+
return (String) pair.getValue();
148+
}
149+
}
150+
}
151+
}
152+
return null;
153+
}
136154
}

java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/JUnit6TestFinder.java

Lines changed: 161 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.eclipse.jdt.core.dom.Modifier;
3535
import org.eclipse.jdt.core.dom.RecordDeclaration;
3636
import org.eclipse.jdt.core.dom.TypeDeclaration;
37+
import org.eclipse.jdt.internal.junit.JUnitCorePlugin;
3738
import org.eclipse.jdt.internal.junit.launcher.ITestFinder;
3839
import org.eclipse.jdt.internal.junit.util.CoreTestSearchEngine;
3940

@@ -67,6 +68,157 @@ public class JUnit6TestFinder implements ITestFinder {
6768
*/
6869
private static final String JUNIT6_LOADER = "org.eclipse.jdt.junit.loader.junit6";
6970

71+
private static class Annotation {
72+
73+
private static final Annotation RUN_WITH = new Annotation("org.junit.runner.RunWith"); //$NON-NLS-1$
74+
75+
private static final Annotation TEST_4 = new Annotation("org.junit.Test"); //$NON-NLS-1$
76+
77+
private static final Annotation SUITE = new Annotation("org.junit.platform.suite.api.Suite"); //$NON-NLS-1$
78+
79+
private static final Annotation TESTABLE = new Annotation(JUnitCorePlugin.JUNIT5_TESTABLE_ANNOTATION_NAME);
80+
81+
private static final Annotation NESTED = new Annotation(JUnitCorePlugin.JUNIT5_JUPITER_NESTED_ANNOTATION_NAME);
82+
83+
private final String fName;
84+
85+
private Annotation(String name) {
86+
fName = name;
87+
}
88+
89+
String getName() {
90+
return fName;
91+
}
92+
93+
boolean annotatesAtLeastOneInnerClass(ITypeBinding type) {
94+
if (type == null) {
95+
return false;
96+
}
97+
if (annotatesDeclaredTypes(type)) {
98+
return true;
99+
}
100+
final ITypeBinding superClass = type.getSuperclass();
101+
if (annotatesAtLeastOneInnerClass(superClass)) {
102+
return true;
103+
}
104+
final ITypeBinding[] interfaces = type.getInterfaces();
105+
for (final ITypeBinding intf : interfaces) {
106+
if (annotatesAtLeastOneInnerClass(intf)) {
107+
return true;
108+
}
109+
}
110+
return false;
111+
}
112+
113+
private boolean annotatesDeclaredTypes(ITypeBinding type) {
114+
final ITypeBinding[] declaredTypes = type.getDeclaredTypes();
115+
for (final ITypeBinding declaredType : declaredTypes) {
116+
if (isNestedClass(declaredType)) {
117+
return true;
118+
}
119+
}
120+
return false;
121+
}
122+
123+
private boolean isNestedClass(ITypeBinding type) {
124+
final int modifiers = type.getModifiers();
125+
if (type.isInterface() || Modifier.isPrivate(modifiers) || Modifier.isStatic(modifiers)) {
126+
return false;
127+
}
128+
if (annotates(type.getAnnotations())) {
129+
return true;
130+
}
131+
return false;
132+
}
133+
134+
boolean annotatesTypeOrSuperTypes(ITypeBinding type) {
135+
while (type != null) {
136+
if (annotates(type.getAnnotations())) {
137+
return true;
138+
}
139+
type = type.getSuperclass();
140+
}
141+
return false;
142+
}
143+
144+
boolean annotatesAtLeastOneMethod(ITypeBinding type) {
145+
if (type == null) {
146+
return false;
147+
}
148+
if (annotatesDeclaredMethods(type)) {
149+
return true;
150+
}
151+
final ITypeBinding superClass = type.getSuperclass();
152+
if (annotatesAtLeastOneMethod(superClass)) {
153+
return true;
154+
}
155+
final ITypeBinding[] interfaces = type.getInterfaces();
156+
for (final ITypeBinding intf : interfaces) {
157+
if (annotatesAtLeastOneMethod(intf)) {
158+
return true;
159+
}
160+
}
161+
return false;
162+
}
163+
164+
private boolean annotatesDeclaredMethods(ITypeBinding type) {
165+
final IMethodBinding[] declaredMethods = type.getDeclaredMethods();
166+
for (final IMethodBinding curr : declaredMethods) {
167+
if (annotates(curr.getAnnotations())) {
168+
return true;
169+
}
170+
}
171+
return false;
172+
}
173+
174+
// See JUnitLaunchConfigurationTab#isAnnotatedWithTestable also.
175+
private boolean annotates(IAnnotationBinding[] annotations) {
176+
for (final IAnnotationBinding annotation : annotations) {
177+
if (annotation == null) {
178+
continue;
179+
}
180+
if (matchesName(annotation.getAnnotationType())) {
181+
return true;
182+
}
183+
if (TESTABLE.getName().equals(fName) || NESTED.getName().equals(fName)) {
184+
final Set<ITypeBinding> hierarchy = new HashSet<>();
185+
if (matchesNameInAnnotationHierarchy(annotation, hierarchy)) {
186+
return true;
187+
}
188+
}
189+
}
190+
return false;
191+
}
192+
193+
private boolean matchesName(ITypeBinding annotationType) {
194+
if (annotationType != null) {
195+
final String qualifiedName = annotationType.getQualifiedName();
196+
if (qualifiedName.equals(fName)) {
197+
return true;
198+
}
199+
}
200+
return false;
201+
}
202+
203+
private boolean matchesNameInAnnotationHierarchy(IAnnotationBinding annotation, Set<ITypeBinding> hierarchy) {
204+
final ITypeBinding type = annotation.getAnnotationType();
205+
if (type != null) {
206+
for (final IAnnotationBinding annotationBinding : type.getAnnotations()) {
207+
if (annotationBinding != null) {
208+
final ITypeBinding annotationType = annotationBinding.getAnnotationType();
209+
if (annotationType != null && hierarchy.add(annotationType)) {
210+
if (matchesName(annotationType) ||
211+
matchesNameInAnnotationHierarchy(annotationBinding, hierarchy)) {
212+
return true;
213+
}
214+
}
215+
}
216+
}
217+
}
218+
return false;
219+
}
220+
}
221+
70222
public JUnit6TestFinder() {
71223
}
72224

@@ -172,32 +324,20 @@ private static boolean isAvailable(ISourceRange range) {
172324
return range != null && range.getOffset() != -1;
173325
}
174326

175-
private boolean isTest(ITypeBinding typeBinding) {
176-
if (typeBinding == null || Modifier.isAbstract(typeBinding.getModifiers())) {
327+
private boolean isTest(ITypeBinding binding) {
328+
if (binding == null || Modifier.isAbstract(binding.getModifiers())) {
177329
return false;
178330
}
179331

180-
// Check if the type itself has test methods
181-
if (hasTestMethods(typeBinding)) {
332+
if (Annotation.RUN_WITH.annotatesTypeOrSuperTypes(binding) ||
333+
Annotation.SUITE.annotatesTypeOrSuperTypes(binding) ||
334+
Annotation.TEST_4.annotatesAtLeastOneMethod(binding) ||
335+
Annotation.TESTABLE.annotatesAtLeastOneMethod(binding) ||
336+
Annotation.TESTABLE.annotatesTypeOrSuperTypes(binding) ||
337+
Annotation.NESTED.annotatesAtLeastOneInnerClass(binding)) {
182338
return true;
183339
}
184-
185-
// Check nested classes with @Nested annotation
186-
for (final ITypeBinding nestedType : typeBinding.getDeclaredTypes()) {
187-
if (isNestedTestClass(nestedType)) {
188-
return true;
189-
}
190-
}
191-
192-
// Check superclass
193-
final ITypeBinding superclass = typeBinding.getSuperclass();
194-
if (superclass != null && !superclass.getQualifiedName().equals("java.lang.Object")) {
195-
if (isTest(superclass)) {
196-
return true;
197-
}
198-
}
199-
200-
return false;
340+
return CoreTestSearchEngine.isTestImplementor(binding);
201341
}
202342

203343
private boolean hasTestMethods(ITypeBinding typeBinding) {

java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/TestFrameworkSearcher.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ public interface TestFrameworkSearcher {
3131

3232
boolean isTestMethod(IMethodBinding methodBinding);
3333

34+
String getDisplayName(IMethodBinding methodBinding);
35+
3436
boolean isTestClass(IType type) throws JavaModelException;
3537

3638
String[] getTestMethodAnnotations();

java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/searcher/TestNGTestSearcher.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,11 @@ public Set<IType> findTestItemsInContainer(IJavaElement element, IProgressMonito
227227
}
228228
return types;
229229
}
230+
231+
@Override
232+
public String getDisplayName(IMethodBinding methodBinding) {
233+
return null;
234+
}
230235
}
231236

232237
/*

0 commit comments

Comments
 (0)