Skip to content

Commit fec8bbe

Browse files
committed
Running Kotlin and Spock tests
1 parent b15232d commit fec8bbe

11 files changed

Lines changed: 465 additions & 74 deletions

File tree

java-extension/com.microsoft.java.test.plugin/src/main/java/com/microsoft/java/test/plugin/launchers/JUnitLaunchConfigurationDelegate.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,54 @@ private void addTestItemArgs(List<String> arguments) throws CoreException {
166166
} else {
167167
arguments.add("-test");
168168
arguments.add(resolveMethodTestName(this.args.testNames[0]));
169+
if (StringUtils.isNotBlank(this.args.uniqueId)) {
170+
arguments.add("-uniqueId");
171+
arguments.add(this.args.uniqueId);
172+
}
173+
arguments.add("-test");
174+
final IMethod method = (IMethod) JavaCore.create(this.args.testNames[0]);
175+
String testName = method.getElementName();
176+
if ((this.args.testKind == TestKind.JUnit5 || this.args.testKind == TestKind.JUnit6) &&
177+
method.getParameters().length > 0) {
178+
final ICompilationUnit unit = method.getCompilationUnit();
179+
if (unit == null) {
180+
// binary method
181+
if (method.getDeclaringType() == null) {
182+
throw new CoreException(new Status(IStatus.ERROR, JUnitPlugin.PLUGIN_ID, IStatus.ERROR,
183+
"Cannot get compilation unit of method" + method.getElementName(), null));
184+
}
185+
} else {
186+
final CompilationUnit root = (CompilationUnit) TestSearchUtils.parseToAst(unit,
187+
false /* fromCache */, new NullProgressMonitor());
188+
final MethodDeclaration methodDeclaration = ASTNodeSearchUtil.getMethodDeclarationNode(method,
189+
root);
190+
if (methodDeclaration == null) {
191+
throw new CoreException(new Status(IStatus.ERROR, JUnitPlugin.PLUGIN_ID, IStatus.ERROR,
192+
"Cannot get method declaration of method" + method.getElementName(), null));
193+
}
169194

195+
final List<String> parameters = new LinkedList<>();
196+
for (final Object obj : methodDeclaration.parameters()) {
197+
if (obj instanceof SingleVariableDeclaration) {
198+
final ITypeBinding paramTypeBinding = ((SingleVariableDeclaration) obj).getType()
199+
.resolveBinding();
200+
if (paramTypeBinding == null) {
201+
throw new CoreException(new Status(IStatus.ERROR, JUnitPlugin.PLUGIN_ID,
202+
IStatus.ERROR,
203+
"Cannot set set argument for method" + methodDeclaration.toString(), null));
204+
} else if (paramTypeBinding.isPrimitive()) {
205+
parameters.add(paramTypeBinding.getQualifiedName());
206+
} else {
207+
parameters.add(paramTypeBinding.getBinaryName());
208+
}
209+
}
210+
}
211+
if (parameters.size() > 0) {
212+
testName += "(" + String.join(",", parameters) + ")";
213+
}
214+
}
215+
}
216+
arguments.add(method.getDeclaringType().getFullyQualifiedName() + ':' + testName);
170217
if (StringUtils.isNotBlank(this.args.uniqueId)) {
171218
arguments.add("-uniqueId");
172219
arguments.add(this.args.uniqueId);

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

Lines changed: 143 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,33 @@
1717
import com.microsoft.java.test.plugin.util.TestItemUtils;
1818

1919
import org.eclipse.core.resources.IProject;
20+
import org.eclipse.core.resources.IResource;
21+
import org.eclipse.core.resources.ResourcesPlugin;
2022
import org.eclipse.core.runtime.IPath;
23+
import org.eclipse.jdt.core.IClassFile;
24+
import org.eclipse.jdt.core.IClasspathEntry;
2125
import org.eclipse.jdt.core.IJavaElement;
2226
import org.eclipse.jdt.core.IJavaProject;
2327
import org.eclipse.jdt.core.IPackageFragment;
2428
import org.eclipse.jdt.core.JavaModelException;
29+
import org.eclipse.jdt.internal.core.BinaryMember;
30+
import org.eclipse.jdt.internal.core.BinaryMethod;
31+
import org.eclipse.jdt.internal.core.BinaryType;
32+
import org.eclipse.jdt.internal.core.PackageFragmentRoot;
2533
import org.eclipse.jdt.internal.core.manipulation.JavaElementLabelsCore;
2634
import org.eclipse.jdt.ls.core.internal.JDTUtils;
35+
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
2736
import org.eclipse.jdt.ls.core.internal.ProjectUtils;
37+
import org.eclipse.lsp4j.Position;
2838
import org.eclipse.lsp4j.Range;
39+
import org.objectweb.asm.ClassReader;
40+
import org.objectweb.asm.ClassVisitor;
41+
import org.objectweb.asm.Label;
42+
import org.objectweb.asm.MethodVisitor;
43+
import org.objectweb.asm.Opcodes;
44+
45+
import java.io.ByteArrayInputStream;
46+
import java.io.InputStream;
2947

3048
/**
3149
* Builder class to build {@link com.microsoft.java.test.plugin.model.JavaTestItem}
@@ -36,6 +54,7 @@ public class JavaTestItemBuilder {
3654
private IJavaElement element;
3755
private TestLevel level;
3856
private TestKind kind;
57+
private String displayName;
3958

4059
public JavaTestItemBuilder setJavaElement(IJavaElement element) {
4160
this.element = element;
@@ -52,34 +71,124 @@ public JavaTestItemBuilder setKind(TestKind kind) {
5271
return this;
5372
}
5473

74+
public JavaTestItemBuilder setDisplayName(String displayName) {
75+
this.displayName = displayName;
76+
return this;
77+
}
78+
5579
public JavaTestItem build() throws JavaModelException {
5680
if (this.element == null || this.level == null || this.kind == null) {
5781
throw new IllegalArgumentException("Failed to build Java test item due to missing arguments");
5882
}
5983

60-
final String displayName;
6184
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();
85+
if (this.displayName == null) {
86+
if (this.element instanceof IJavaProject) {
87+
final IJavaProject javaProject = (IJavaProject) this.element;
88+
final IProject project = javaProject.getProject();
89+
if (ProjectUtils.isVisibleProject(project)) {
90+
displayName = project.getName();
91+
} else {
92+
final IPath realPath = ProjectUtils.getProjectRealFolder(project);
93+
displayName = realPath.lastSegment();
94+
uri = realPath.toFile().toURI().toString();
95+
}
96+
} else if (this.element instanceof IPackageFragment &&
97+
((IPackageFragment) this.element).isDefaultPackage()) {
98+
displayName = DEFAULT_PACKAGE_NAME;
99+
final IResource resource = getResource((IPackageFragment) this.element);
100+
if (resource == null || !resource.exists()) {
101+
return null;
102+
}
103+
uri = JDTUtils.getFileURI(resource);
67104
} else {
68-
final IPath realPath = ProjectUtils.getProjectRealFolder(project);
69-
displayName = realPath.lastSegment();
70-
uri = realPath.toFile().toURI().toString();
105+
displayName = JavaElementLabelsCore.getElementLabel(this.element, JavaElementLabelsCore.ALL_DEFAULT);
71106
}
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);
76107
}
108+
Range range = null;
77109
final String fullName = TestItemUtils.parseFullName(this.element, this.level, this.kind);
78110
if (uri == null) {
79-
uri = JDTUtils.getFileURI(this.element.getResource());
111+
IResource resource = this.element.getResource();
112+
if (resource == null && this.element instanceof IPackageFragment) {
113+
resource = getResource((IPackageFragment) this.element);
114+
}
115+
if (resource == null || !resource.exists()) {
116+
return null;
117+
}
118+
if (element instanceof BinaryMember) {
119+
final String[] sources = new String[1];
120+
final int[] lines = {-1};
121+
final IClassFile classFile = ((BinaryMember) element).getClassFile();
122+
try (InputStream is = new ByteArrayInputStream(classFile.getBytes())) {
123+
final ClassReader cr = new ClassReader(is);
124+
if (element instanceof BinaryType) {
125+
cr.accept(new ClassVisitor(Opcodes.ASM9) {
126+
@Override
127+
public void visitSource(String source, String debug) {
128+
sources[0] = source;
129+
}
130+
}, ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
131+
} else if (element instanceof BinaryMethod) {
132+
final String methodDescriptor = ((BinaryMethod) element).getSignature();
133+
cr.accept(new ClassVisitor(Opcodes.ASM9) {
134+
@Override
135+
public void visitSource(String source, String debug) {
136+
sources[0] = source;
137+
}
138+
139+
public MethodVisitor visitMethod(int access, String name, String descriptor,
140+
String signature, String[] exceptions) {
141+
if (name.equals(element.getElementName()) && descriptor.equals(methodDescriptor)) {
142+
return new MethodVisitor(Opcodes.ASM9) {
143+
@Override
144+
public void visitLineNumber(int line, Label start) {
145+
if (lines[0] < 0) {
146+
lines[0] = line;
147+
if (line > 0) {
148+
lines[0]--;
149+
}
150+
}
151+
}
152+
};
153+
}
154+
return null;
155+
}
156+
}, ClassReader.SKIP_FRAMES);
157+
}
158+
} catch (Exception e) {
159+
JavaLanguageServerPlugin.logException(e);
160+
}
161+
if (sources[0] != null) {
162+
final IPackageFragment packageFragment = (IPackageFragment) element
163+
.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
164+
if (packageFragment != null) {
165+
final String packageName = packageFragment.getElementName();
166+
final IJavaProject project = element.getJavaProject();
167+
final String packagePath = packageName.replace('.', '/');
168+
for (final IClasspathEntry entry : project.getRawClasspath()) {
169+
if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
170+
final IPath sourceFolderPath = entry.getPath();
171+
final IPath fullPath = sourceFolderPath.append(packagePath).append(sources[0]);
172+
final IResource candidate = ResourcesPlugin.getWorkspace().getRoot()
173+
.findMember(fullPath);
174+
if (candidate != null && candidate.exists()) {
175+
resource = candidate;
176+
break;
177+
}
178+
}
179+
}
180+
}
181+
}
182+
if (lines[0] > 0) {
183+
final Position line = new Position(lines[0] - 1, 0);
184+
range = new Range(line, line);
185+
}
186+
}
187+
if (uri == null) {
188+
uri = JDTUtils.getFileURI(resource);
189+
}
80190
}
81-
Range range = null;
82-
if (this.level == TestLevel.CLASS || this.level == TestLevel.METHOD) {
191+
if (range == null && (this.level == TestLevel.CLASS || this.level == TestLevel.METHOD)) {
83192
range = TestItemUtils.parseTestItemRange(this.element);
84193
}
85194

@@ -89,4 +198,22 @@ public JavaTestItem build() throws JavaModelException {
89198

90199
return result;
91200
}
201+
202+
private IResource getResource(IPackageFragment packageFragment) {
203+
if (packageFragment == null) {
204+
return null;
205+
}
206+
IResource resource = packageFragment.getResource();
207+
if (resource == null) {
208+
final IJavaElement e = packageFragment.getParent();
209+
if (e instanceof PackageFragmentRoot) {
210+
final PackageFragmentRoot root = (PackageFragmentRoot) e;
211+
resource = root.getResource();
212+
if (resource == null) {
213+
resource = root.resource(root);
214+
}
215+
}
216+
}
217+
return resource;
218+
}
92219
}

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
}

0 commit comments

Comments
 (0)