Skip to content

Commit

Permalink
#4679 add test discovery service and implementation for TestNG and JUnit
Browse files Browse the repository at this point in the history
Signed-off-by: Even Vidolob <[email protected]>
  • Loading branch information
Even Vidolob committed Jun 2, 2017
1 parent 5a126a0 commit c4e7ebc
Show file tree
Hide file tree
Showing 20 changed files with 1,513 additions and 64 deletions.
4 changes: 4 additions & 0 deletions plugins/plugin-testing-java/plugin-testing-classpath/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-project</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-testing</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.lib</groupId>
<artifactId>org-eclipse-jdt-core-repack</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.plugin.java.testing;

import org.eclipse.che.api.testing.server.framework.TestRunner;
import org.eclipse.che.api.testing.shared.TestDetectionContext;
import org.eclipse.che.api.testing.shared.TestPosition;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
*
*/
public abstract class AbstractJavaTestRunner implements TestRunner {

private static final Logger LOG = LoggerFactory.getLogger(AbstractJavaTestRunner.class);

@Override
public List<TestPosition> detectTests(TestDetectionContext context) {
IJavaProject javaProject = getJavaProject(context.getProjectPath());
if (!javaProject.exists()) {
return Collections.emptyList();
}

List<TestPosition> result = new ArrayList<>();
try {
ICompilationUnit compilationUnit = findCompilationUnitByPath(javaProject, context.getFilePath());
if (context.getOffset() == -1) {
addAllTestsMethod(result, compilationUnit);
} else {
IJavaElement element = compilationUnit.getElementAt(context.getOffset());
if (element.getElementType() == IJavaElement.METHOD) {
result.add(createTestPosition((IMethod) element));
} else {
addAllTestsMethod(result, compilationUnit);
}

}
} catch (JavaModelException e) {
LOG.debug("Can't read all methods.", e);
}

return result;
}

private void addAllTestsMethod(List<TestPosition> result, ICompilationUnit compilationUnit) throws JavaModelException {
for (IType type : compilationUnit.getAllTypes()) {
for (IMethod method : type.getMethods()) {
if (isTestMethod(method, compilationUnit)) {
result.add(createTestPosition(method));
}
}
}
}

private TestPosition createTestPosition(IMethod method) throws JavaModelException {
TestPosition testPosition = DtoFactory.newDto(TestPosition.class);

testPosition.setFrameworkName(getName());
testPosition.setTestName(method.getElementName());
ISourceRange sourceRange = method.getNameRange();
testPosition.setTestStartOffset(sourceRange.getOffset());
testPosition.setTestLength(sourceRange.getLength());

return testPosition;
}

protected abstract boolean isTestMethod(IMethod method, ICompilationUnit compilationUnit);

protected IJavaProject getJavaProject(String projectPath) {
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectPath);
return JavaModelManager.getJavaModelManager().getJavaModel().getJavaProject(project);
}

protected ICompilationUnit findCompilationUnitByPath(IJavaProject javaProject, String filePath) {
try {
IClasspathEntry[] resolvedClasspath = javaProject.getResolvedClasspath(false);
IPath packageRootPath = null;
for (IClasspathEntry classpathEntry : resolvedClasspath) {
if (filePath.startsWith(classpathEntry.getPath().toOSString())) {
packageRootPath = classpathEntry.getPath();
break;
}
}

if (packageRootPath == null) {
throw getRuntimeException(filePath);
}

String packagePath = packageRootPath.toOSString();
if (!packagePath.endsWith("/")) {
packagePath += "/";
}

String pathToClass = filePath.substring(packagePath.length());
IJavaElement element = javaProject.findElement(new Path(pathToClass));
if (element != null && element.getElementType() == IJavaElement.COMPILATION_UNIT) {
return (ICompilationUnit) element;
} else {
throw getRuntimeException(filePath);
}
} catch (JavaModelException e) {
throw new RuntimeException("Can't find Compilation Unit.", e);
}
}

private RuntimeException getRuntimeException(String filePath) {
return new RuntimeException("Can't find IClasspathEntry for path " + filePath);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,23 @@
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-lang</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-testing-classpath</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,25 @@
*******************************************************************************/
package org.eclipse.che.plugin.testing.junit.server;

import org.eclipse.che.api.testing.server.framework.TestRunner;
import org.eclipse.che.api.testing.shared.TestExecutionContext;
import org.eclipse.che.api.testing.shared.TestResult;
import org.eclipse.che.commons.lang.execution.ProcessHandler;
import org.eclipse.che.plugin.java.testing.AbstractJavaTestRunner;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.JavaModelException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import java.util.Map;

/**
* JUnit implementation for the test runner service.
*
* <p>
* <pre>
*
* <em>absoluteProjectPath</em> : Absolute path to the project directory
Expand All @@ -33,11 +41,14 @@
* @author Mirage Abeysekara
* @author David Festal
*/
public class JUnitTestRunner implements TestRunner {
public class JUnitTestRunner extends AbstractJavaTestRunner {

private static final Logger LOG = LoggerFactory.getLogger(JUnitTestRunner.class);

private static final String JUNIT4X_RUNNER_CLASS = "org.junit.runner.JUnitCore";
private static final String JUNIT3X_RUNNER_CLASS = "junit.textui.TestRunner";

private static final String JUNIT4X_RUNNER_CLASS = "org.junit.runner.JUnitCore";
private static final String JUNIT3X_RUNNER_CLASS = "junit.textui.TestRunner";
private ClassLoader projectClassLoader;
private static final String JUNIT_TEST_ANNOTATION = "org.junit.Test";

@Inject
public JUnitTestRunner() {
Expand Down Expand Up @@ -66,4 +77,80 @@ public String getName() {
return "junit";
}

@Override
protected boolean isTestMethod(IMethod method, ICompilationUnit compilationUnit) {
try {
if (isTest(method, compilationUnit)) {
return true;
}

if (method.isConstructor()) {
return false;
}
int flags = method.getFlags();
if (!Flags.isPublic(flags)) {
return false;
}

if (Flags.isAbstract(flags)) {
return false;
}
if (Flags.isStatic(flags)) {
return false;
}

if (!method.getElementName().startsWith("test")) { //JUnit 3 case
return false;
}
//TODO add check class hierarchy for JUnit3

return method.getReturnType().equals("V"); // 'V' is void signature

} catch (JavaModelException ignored) {
return false;
}
}

private boolean isTest(IMethod method, ICompilationUnit compilationUnit) {
try {
IAnnotation[] annotations = method.getAnnotations();
IAnnotation test = null;
for (IAnnotation annotation : annotations) {
if (annotation.getElementName().equals("Test")) {
test = annotation;
break;
}
if (annotation.getElementName().equals(JUNIT_TEST_ANNOTATION)) {
return true;
}
}

if (test == null) {
return false;
}

IImportDeclaration[] imports = compilationUnit.getImports();
for (IImportDeclaration importDeclaration : imports) {
if (importDeclaration.getElementName().equals(JUNIT_TEST_ANNOTATION)) {
return true;
}
}

for (IImportDeclaration importDeclaration : imports) {
if (importDeclaration.isOnDemand()) {
String elementName = importDeclaration.getElementName();
elementName = elementName.substring(0, elementName.length() - 3); //remove .*
if (JUNIT_TEST_ANNOTATION.startsWith(elementName)) {
return true;
}

}
}

return false;
} catch (JavaModelException e) {
LOG.info("Can't read method annotations.", e);
return false;
}
}
}
Loading

0 comments on commit c4e7ebc

Please sign in to comment.