JUnit testing support Bug fixes and Enhancements - Step 1 (#4303)

Provide  JUnit test real-time output in a `Tests` output view
6.19.x
David Festal 2017-03-10 10:51:36 +01:00 committed by Mario Loriedo
parent 85d3578900
commit 440f92d3bb
29 changed files with 2133 additions and 276 deletions

View File

@ -10,6 +10,17 @@
*******************************************************************************/
package org.eclipse.che.plugin.java.server.rest;
import static java.util.Collections.emptyList;
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.ide.ext.java.shared.dto.classpath.ClasspathEntryDto;
import org.eclipse.jdt.core.IClasspathEntry;
@ -19,35 +30,24 @@ import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaModel;
import org.eclipse.jdt.internal.core.JavaModelManager;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.util.ArrayList;
import java.util.List;
import static java.util.Collections.emptyList;
/**
* Service for getting information about classpath.
*
* @author Valeriy Svydenko
*/
@Path("java/classpath/")
public class ClasspathService {
public class ClasspathService implements ClasspathServiceInterface {
private static final JavaModel model = JavaModelManager.getJavaModelManager().getJavaModel();
/**
* Returns information about classpath.
*
* @param projectPath
* path to the current project
* @param projectPath path to the current project
* @return list of classpath entries
* @throws JavaModelException
* when JavaModel has a failure
* @throws JavaModelException when JavaModel has a failure
*/
@Override
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<ClasspathEntryDto> getClasspath(@QueryParam("projectpath") String projectPath) throws JavaModelException {

View File

@ -0,0 +1,29 @@
/*******************************************************************************
* 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.server.rest;
import java.util.List;
import org.eclipse.che.ide.ext.java.shared.dto.classpath.ClasspathEntryDto;
import org.eclipse.jdt.core.JavaModelException;
import com.google.inject.ImplementedBy;
/**
* Interface for the service which gets information about classpath.
*
* @author Valeriy Svydenko
*/
@ImplementedBy(ClasspathService.class)
public interface ClasspathServiceInterface {
List<ClasspathEntryDto> getClasspath(String projectPath) throws JavaModelException;
}

View File

@ -30,16 +30,85 @@
<artifactId>guice-multibindings</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>
<groupId>org.eclipse.birt.runtime</groupId>
<artifactId>org.eclipse.equinox.common</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-commons-inject</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.lib</groupId>
<artifactId>org-eclipse-jdt-core-repack</artifactId>
<exclusions>
<exclusion>
<artifactId>org.eclipse.jdt.core</artifactId>
<groupId>org.eclipse.tycho</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-java-ext-lang-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-java-ext-lang-shared</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-testing-classpath-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>org.eclipse.core.resources</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/java</sourceDirectory>
<testSourceDirectory>src/test/java</testSourceDirectory>
<outputDirectory>target/classes</outputDirectory>
<testResources>
<testResource>
<directory>src/test/resources</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/**/MavenTestClasspathProviderTest.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit47</artifactId>
<version>${version.surefire.plugin}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>

View File

@ -10,43 +10,50 @@
*******************************************************************************/
package org.eclipse.che.plugin.testing.classpath.maven.server;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import org.eclipse.che.api.core.util.CommandLine;
import org.eclipse.che.api.core.util.LineConsumer;
import org.eclipse.che.api.core.util.ProcessUtil;
import org.eclipse.che.ide.ext.java.shared.dto.classpath.ClasspathEntryDto;
import org.eclipse.che.plugin.java.server.rest.ClasspathServiceInterface;
import org.eclipse.che.plugin.testing.classpath.server.TestClasspathProvider;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
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.JavaModelException;
import com.google.inject.Inject;
/**
* Maven implementation for the test classpath provider.
*
* @author Mirage Abeysekara
* @author David Festal
*/
public class MavenTestClasspathProvider implements TestClasspathProvider {
private ClasspathServiceInterface classpathService;
@Inject
public MavenTestClasspathProvider(ClasspathServiceInterface classpathService) {
this.classpathService = classpathService;
}
/**
* {@inheritDoc}
*/
@Override
public ClassLoader getClassLoader(String projectPath, boolean updateClasspath) throws Exception {
List<URL> classUrls;
public ClassLoader getClassLoader(String projectAbsolutePath, String projectRelativePath, boolean updateClasspath) throws Exception {
try {
if (updateClasspath) {
buildClasspath(projectPath);
}
classUrls = getProjectClasspath(projectPath);
} catch (IOException | InterruptedException e) {
throw new Exception("Failed to build Maven classpath.", e);
return new URLClassLoader(getProjectClasspath(projectAbsolutePath, projectRelativePath, getWorkspaceRoot()), null);
} catch (JavaModelException e) {
throw new Exception("Failed to build the classpath for testing project: " + projectRelativePath, e);
}
return new URLClassLoader(classUrls.toArray(new URL[classUrls.size()]), null);
}
/**
@ -57,35 +64,57 @@ public class MavenTestClasspathProvider implements TestClasspathProvider {
return "maven";
}
private boolean buildClasspath(String projectPath) throws IOException, InterruptedException {
final CommandLine commandLineClassPath = new CommandLine("mvn", "clean", "dependency:build-classpath",
"-Dmdep.outputFile=target/test.classpath.maven");
Process processBuildClassPath = new ProcessBuilder().redirectErrorStream(true).directory(new File(projectPath))
.command(commandLineClassPath.toShellCommand()).start();
ProcessUtil.process(processBuildClassPath, LineConsumer.DEV_NULL, LineConsumer.DEV_NULL);
processBuildClassPath.waitFor();
final CommandLine commandLineTestCompile = new CommandLine("mvn", "test-compile");
Process processTestCompile = new ProcessBuilder().redirectErrorStream(true).directory(new File(projectPath))
.command(commandLineTestCompile.toShellCommand()).start();
ProcessUtil.process(processTestCompile, LineConsumer.DEV_NULL, LineConsumer.DEV_NULL);
return processTestCompile.waitFor() == 0;
private Stream<ClasspathEntryDto> toResolvedClassPath(Stream<ClasspathEntryDto> rawClasspath) {
return rawClasspath.flatMap(dto -> {
if (dto.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
return toResolvedClassPath(dto.getExpandedEntries().stream());
} else {
return Stream.of(dto);
}
});
}
private List<URL> getProjectClasspath(String projectPath) throws IOException {
List<URL> classUrls = new ArrayList<>();
File cpFile = Paths.get(projectPath, "target", "test.classpath.maven").toFile();
FileReader fileReader = new FileReader(cpFile);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line = bufferedReader.readLine();
String[] paths = line.split(":");
for (String path : paths) {
classUrls.add(new File(path).toURI().toURL());
}
bufferedReader.close();
fileReader.close();
classUrls.add(Paths.get(projectPath, "target", "classes").toUri().toURL());
classUrls.add(Paths.get(projectPath, "target", "test-classes").toUri().toURL());
return classUrls;
private IWorkspaceRoot getWorkspaceRoot() {
return ResourcesPlugin.getWorkspace().getRoot();
}
public URL[] getProjectClasspath(String projectAbsolutePath, String projectRelativePath, IWorkspaceRoot root) throws JavaModelException {
Stream<ClasspathEntryDto> rawClasspath = classpathService.getClasspath(projectRelativePath).stream();
Stream<ClasspathEntryDto> resolvedClasspath = toResolvedClassPath(rawClasspath);
return resolvedClasspath.map(dto -> {
try {
String dtoPath = dto.getPath();
IResource res = root.findMember(new Path(dtoPath));
File path;
switch (dto.getEntryKind()) {
case IClasspathEntry.CPE_LIBRARY:
if (res == null) {
path = new File(dtoPath);
} else {
path = res.getLocation().toFile();
}
break;
case IClasspathEntry.CPE_SOURCE:
IPath relativePathFromProjectRoot = new Path(dtoPath).removeFirstSegments(1);
String relativePathFromProjectRootStr = relativePathFromProjectRoot.toString();
switch (relativePathFromProjectRootStr) {
case "src/main/java":
path = Paths.get(projectAbsolutePath, "target", "classes").toFile();
break;
case "src/test/java":
path = Paths.get(projectAbsolutePath, "target", "test-classes").toFile();
break;
default:
path = Paths.get(projectAbsolutePath, "target", "classes").toFile();
}
break;
default:
path = new File(dtoPath);
}
return path.toURI().toURL();
} catch (MalformedURLException e) {
return null;
}
}).filter(url -> url != null).distinct().toArray(URL[]::new);
}
}

View File

@ -0,0 +1,154 @@
/*******************************************************************************
* Copyright (c) 2012-2017 RedHat, Inc
* 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:
* RedHat, Inc - initial test implementation
*******************************************************************************/
package org.eclipse.che.plugin.testing.classpath.maven.server;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertArrayEquals;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.ide.ext.java.shared.dto.classpath.ClasspathEntryDto;
import org.eclipse.che.plugin.java.server.rest.ClasspathServiceInterface;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.JavaModelException;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
/**
* Tests for the Maven implementation for the test classpath provider.
*
* @author David Festal
*/
public class MavenTestClasspathProviderTest {
@Mock
private ClasspathServiceInterface classpathService;
@Mock
private IWorkspaceRoot workspaceRoot;
private MavenTestClasspathProvider classpathProvider;
private static DtoFactory dtoFactory = DtoFactory.getInstance();
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
classpathProvider = new MavenTestClasspathProvider(classpathService);
}
public static class ClasspathEntry {
String fullPath;
boolean external;
int kind;
List<ClasspathEntry> resolvedEntries;
String fileSystemPath;
public ClasspathEntryDto dto() {
return dtoFactory.createDto(ClasspathEntryDto.class)
.withEntryKind(kind)
.withPath(fullPath)
.withExpandedEntries(resolvedEntries.stream().map(ClasspathEntry::dto).collect(Collectors.toList()));
}
}
private ClasspathEntry externalLib(String fullPath) {
ClasspathEntry cp = new ClasspathEntry();
cp.external = true;
cp.fullPath = fullPath;
cp.fileSystemPath = fullPath;
cp.kind = IClasspathEntry.CPE_LIBRARY;
cp.resolvedEntries = Collections.emptyList();
return cp;
}
private ClasspathEntry internalLib(String fullPath, String fileSystemPath) {
ClasspathEntry cp = new ClasspathEntry();
cp.external = false;
cp.fullPath = fullPath;
cp.fileSystemPath = fileSystemPath;
cp.kind = IClasspathEntry.CPE_LIBRARY;
cp.resolvedEntries = Collections.emptyList();
return cp;
}
private ClasspathEntry source(String fullPath) {
ClasspathEntry cp = new ClasspathEntry();
cp.external = false;
cp.fullPath = fullPath;
cp.fileSystemPath = null;
cp.kind = IClasspathEntry.CPE_SOURCE;
cp.resolvedEntries = Collections.emptyList();
return cp;
}
private ClasspathEntry container(String containerPath, List<ClasspathEntry> resolvedEntries) {
ClasspathEntry cp = new ClasspathEntry();
cp.external = false;
cp.fullPath = null;
cp.fileSystemPath = null;
cp.kind = IClasspathEntry.CPE_CONTAINER;
cp.resolvedEntries = resolvedEntries;
return cp;
}
private void buildMocks(List<ClasspathEntry> entries) throws JavaModelException {
when(classpathService.getClasspath(anyString()))
.thenReturn(entries.stream().map(ClasspathEntry::dto).collect(Collectors.toList()));
for (ClasspathEntry entry : entries) {
if (!entry.external && entry.kind == IClasspathEntry.CPE_LIBRARY) {
IPath resourceLocation = new Path(entry.fileSystemPath);
IResource result = mock(IResource.class);
when(result.getLocation())
.thenReturn(resourceLocation);
when(workspaceRoot.findMember(new Path(entry.fullPath)))
.thenReturn(result);
}
}
}
@Test
public void testTypicalMavenProjectClasspath() throws JavaModelException, MalformedURLException {
List<ClasspathEntry> entries =
asList(
externalLib("/home/user/.m2/repository/com/google/guava/guava/20.0/guava-20.0.jar"),
internalLib("exampleProject/lib/internal.jar", "/some/fileSystemPath/internal.jar"),
container("org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER",
asList(externalLib("/home/user/.m2/repository/com/google/collections/google-collections/1.0/google-collections-1.0.jar"),
externalLib("/home/user/.m2/repository/com/google/gwt/gwt-servlet/2.8.0/gwt-servlet-2.8.0.jar"))),
source("exampleProject/src/main/java"),
source("exampleProject/src/test/java"));
buildMocks(entries);
URL[] classpath = classpathProvider.getProjectClasspath("/projects/exampleProject", "exampleProject", workspaceRoot);
assertArrayEquals(new URL[]{
new URL("file:/home/user/.m2/repository/com/google/guava/guava/20.0/guava-20.0.jar"),
new URL("file:/some/fileSystemPath/internal.jar"),
new URL("file:/home/user/.m2/repository/com/google/collections/google-collections/1.0/google-collections-1.0.jar"),
new URL("file:/home/user/.m2/repository/com/google/gwt/gwt-servlet/2.8.0/gwt-servlet-2.8.0.jar"),
new URL("file:/projects/exampleProject/target/classes"),
new URL("file:/projects/exampleProject/target/test-classes")
}, classpath);
}
}

View File

@ -22,8 +22,10 @@ public interface TestClasspathProvider {
/**
* Returns the project class loader for executing test cases.
*
* @param projectPath
* absolute path for the project location.
* @param projectAbsolutePath
* absolute path for the project location on the disk.
* @param projectRelativePath
* path for the project relative to the workspace.
* @param updateClasspath
* calculate the classpath if true. otherwise return existing
* class loader.
@ -31,7 +33,7 @@ public interface TestClasspathProvider {
* @throws Exception
* when classloader creation failed.
*/
ClassLoader getClassLoader(String projectPath, boolean updateClasspath) throws Exception;
ClassLoader getClassLoader(String projectAbsolutePath, String projectRelativePath, boolean updateClasspath) throws Exception;
/**
* String representation of the project type.

View File

@ -10,25 +10,14 @@
*******************************************************************************/
package org.eclipse.che.plugin.testing.junit.ide.action;
import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.FLOAT_MODE;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.PROGRESS;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.SUCCESS;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.api.testing.shared.TestResult;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.filetypes.FileTypeRegistry;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.notification.StatusNotification;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.ide.ext.java.client.action.JavaEditorAction;
import org.eclipse.che.plugin.testing.ide.TestServiceClient;
import org.eclipse.che.plugin.testing.ide.view.TestResultPresenter;
@ -38,14 +27,16 @@ import org.eclipse.che.plugin.testing.junit.ide.JUnitTestResources;
import com.google.inject.Inject;
/**
*
* @author Mirage Abeysekara
* @author David Festal
*/
public class RunAllTestAction extends JavaEditorAction {
public class RunAllTestAction extends JavaEditorAction
implements RunTestActionDelegate.Source {
private final NotificationManager notificationManager;
private TestResultPresenter presenter;
private final TestServiceClient service;
private final NotificationManager notificationManager;
private final TestResultPresenter presenter;
private final TestServiceClient service;
private final RunTestActionDelegate delegate;
@Inject
public RunAllTestAction(JUnitTestResources resources,
@ -56,43 +47,37 @@ public class RunAllTestAction extends JavaEditorAction {
TestServiceClient service,
JUnitTestLocalizationConstant localization) {
super(localization.actionRunAllTitle(), localization.actionRunAllDescription(), resources.testAllIcon(),
editorAgent, fileTypeRegistry);
editorAgent, fileTypeRegistry);
this.notificationManager = notificationManager;
this.editorAgent = editorAgent;
this.presenter = presenter;
this.service = service;
this.delegate = new RunTestActionDelegate(this);
}
@Override
public void actionPerformed(ActionEvent e) {
final StatusNotification notification = new StatusNotification("Running Tests...", PROGRESS, FLOAT_MODE);
notificationManager.notify(notification);
final Project project = appContext.getRootProject();
Map<String, String> parameters = new HashMap<>();
parameters.put("updateClasspath", "true");
Promise<TestResult> testResultPromise = service.getTestResult(project.getPath(), "junit", parameters);
testResultPromise.then(new Operation<TestResult>() {
@Override
public void apply(TestResult result) throws OperationException {
notification.setStatus(SUCCESS);
if (result.isSuccess()) {
notification.setTitle("Test runner executed successfully");
notification.setContent("All tests are passed");
} else {
notification.setTitle("Test runner executed successfully with test failures.");
notification.setContent(result.getFailureCount() + " test(s) failed.\n");
}
presenter.handleResponse(result);
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError exception) throws OperationException {
final String errorMessage = (exception.getMessage() != null) ? exception.getMessage()
: "Failed to run test cases";
notification.setContent(errorMessage);
notification.setStatus(FAIL);
}
});
delegate.doRunTests(e, parameters);
}
@Override
public NotificationManager getNotificationManager() {
return notificationManager;
}
@Override
public AppContext getAppContext() {
return appContext;
}
@Override
public TestServiceClient getService() {
return service;
}
@Override
public TestResultPresenter getPresenter() {
return presenter;
}
}

View File

@ -10,10 +10,6 @@
*******************************************************************************/
package org.eclipse.che.plugin.testing.junit.ide.action;
import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.FLOAT_MODE;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.PROGRESS;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.SUCCESS;
import static org.eclipse.che.ide.workspace.perspectives.project.ProjectPerspective.PROJECT_PERSPECTIVE_ID;
import java.util.Arrays;
@ -22,17 +18,10 @@ import java.util.Map;
import javax.validation.constraints.NotNull;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.api.testing.shared.TestResult;
import org.eclipse.che.ide.api.action.AbstractPerspectiveAction;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.notification.StatusNotification;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.ide.api.resources.VirtualFile;
import org.eclipse.che.ide.api.selection.Selection;
import org.eclipse.che.ide.api.selection.SelectionAgent;
@ -46,16 +35,18 @@ import org.eclipse.che.plugin.testing.junit.ide.JUnitTestResources;
import com.google.inject.Inject;
/**
*
* @author Mirage Abeysekara
* @author David Festal
*/
public class RunClassContextTestAction extends AbstractPerspectiveAction {
public class RunClassContextTestAction extends AbstractPerspectiveAction
implements RunTestActionDelegate.Source {
private final NotificationManager notificationManager;
private final TestResultPresenter presenter;
private final TestServiceClient service;
private final AppContext appContext;
private final SelectionAgent selectionAgent;
private final NotificationManager notificationManager;
private final TestResultPresenter presenter;
private final TestServiceClient service;
private final AppContext appContext;
private final SelectionAgent selectionAgent;
private final RunTestActionDelegate delegate;
@Inject
public RunClassContextTestAction(JUnitTestResources resources,
@ -66,51 +57,26 @@ public class RunClassContextTestAction extends AbstractPerspectiveAction {
SelectionAgent selectionAgent,
JUnitTestLocalizationConstant localization) {
super(Arrays.asList(PROJECT_PERSPECTIVE_ID), localization.actionRunClassContextTitle(),
localization.actionRunClassContextDescription(), null, resources.testIcon());
localization.actionRunClassContextDescription(), null, resources.testIcon());
this.notificationManager = notificationManager;
this.presenter = presenter;
this.service = service;
this.appContext = appContext;
this.selectionAgent = selectionAgent;
this.delegate = new RunTestActionDelegate(this);
}
@Override
public void actionPerformed(ActionEvent e) {
final StatusNotification notification = new StatusNotification("Running Tests...", PROGRESS, FLOAT_MODE);
notificationManager.notify(notification);
final Selection<?> selection = selectionAgent.getSelection();
final Selection< ? > selection = selectionAgent.getSelection();
final Object possibleNode = selection.getHeadElement();
if (possibleNode instanceof FileNode) {
VirtualFile file = ((FileNode) possibleNode).getData();
final Project project = appContext.getRootProject();
VirtualFile file = ((FileNode)possibleNode).getData();
String fqn = JavaUtil.resolveFQN(file);
Map<String, String> parameters = new HashMap<>();
parameters.put("fqn", fqn);
parameters.put("runClass", "true");
parameters.put("updateClasspath", "true");
Promise<TestResult> testResultPromise = service.getTestResult(project.getPath(), "junit", parameters);
testResultPromise.then(new Operation<TestResult>() {
@Override
public void apply(TestResult result) throws OperationException {
notification.setStatus(SUCCESS);
if (result.isSuccess()) {
notification.setTitle("Test runner executed successfully");
notification.setContent("All tests are passed");
} else {
notification.setTitle("Test runner executed successfully with test failures.");
notification.setContent(result.getFailureCount() + " test(s) failed.\n");
}
presenter.handleResponse(result);
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError exception) throws OperationException {
final String errorMessage = (exception.getMessage() != null) ? exception.getMessage()
: "Failed to run test cases";
notification.setContent(errorMessage);
notification.setStatus(FAIL);
}
});
delegate.doRunTests(e, parameters);
}
}
@ -121,7 +87,7 @@ public class RunClassContextTestAction extends AbstractPerspectiveAction {
e.getPresentation().setEnabled(false);
return;
}
final Selection<?> selection = selectionAgent.getSelection();
final Selection< ? > selection = selectionAgent.getSelection();
if (selection == null || selection.isEmpty()) {
e.getPresentation().setEnabled(false);
return;
@ -132,7 +98,27 @@ public class RunClassContextTestAction extends AbstractPerspectiveAction {
}
final Object possibleNode = selection.getHeadElement();
boolean enable = possibleNode instanceof FileNode
&& "java".equals(((FileNode) possibleNode).getData().getExtension());
&& "java".equals(((FileNode)possibleNode).getData().getExtension());
e.getPresentation().setEnabled(enable);
}
@Override
public NotificationManager getNotificationManager() {
return notificationManager;
}
@Override
public AppContext getAppContext() {
return appContext;
}
@Override
public TestServiceClient getService() {
return service;
}
@Override
public TestResultPresenter getPresenter() {
return presenter;
}
}

View File

@ -10,26 +10,15 @@
*******************************************************************************/
package org.eclipse.che.plugin.testing.junit.ide.action;
import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.FLOAT_MODE;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.PROGRESS;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.SUCCESS;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.api.testing.shared.TestResult;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.api.filetypes.FileTypeRegistry;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.notification.StatusNotification;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.ide.api.resources.VirtualFile;
import org.eclipse.che.ide.ext.java.client.action.JavaEditorAction;
import org.eclipse.che.ide.ext.java.client.util.JavaUtil;
@ -41,15 +30,17 @@ import org.eclipse.che.plugin.testing.junit.ide.JUnitTestResources;
import com.google.inject.Inject;
/**
*
* @author Mirage Abeysekara
* @author David Festal
*/
public class RunClassTestAction extends JavaEditorAction {
public class RunClassTestAction extends JavaEditorAction
implements RunTestActionDelegate.Source {
private final NotificationManager notificationManager;
private final EditorAgent editorAgent;
private final TestResultPresenter presenter;
private final TestServiceClient service;
private final NotificationManager notificationManager;
private final EditorAgent editorAgent;
private final TestResultPresenter presenter;
private final TestServiceClient service;
private final RunTestActionDelegate delegate;
@Inject
public RunClassTestAction(JUnitTestResources resources,
@ -60,18 +51,16 @@ public class RunClassTestAction extends JavaEditorAction {
TestServiceClient service,
JUnitTestLocalizationConstant localization) {
super(localization.actionRunClassTitle(), localization.actionRunClassDescription(), resources.testIcon(),
editorAgent, fileTypeRegistry);
editorAgent, fileTypeRegistry);
this.notificationManager = notificationManager;
this.editorAgent = editorAgent;
this.presenter = presenter;
this.service = service;
this.delegate = new RunTestActionDelegate(this);
}
@Override
public void actionPerformed(ActionEvent e) {
final StatusNotification notification = new StatusNotification("Running Tests...", PROGRESS, FLOAT_MODE);
notificationManager.notify(notification);
final Project project = appContext.getRootProject();
EditorPartPresenter editorPart = editorAgent.getActiveEditor();
final VirtualFile file = editorPart.getEditorInput().getFile();
String fqn = JavaUtil.resolveFQN(file);
@ -79,29 +68,7 @@ public class RunClassTestAction extends JavaEditorAction {
parameters.put("fqn", fqn);
parameters.put("runClass", "true");
parameters.put("updateClasspath", "true");
Promise<TestResult> testResultPromise = service.getTestResult(project.getPath(), "junit", parameters);
testResultPromise.then(new Operation<TestResult>() {
@Override
public void apply(TestResult result) throws OperationException {
notification.setStatus(SUCCESS);
if (result.isSuccess()) {
notification.setTitle("Test runner executed successfully");
notification.setContent("All tests are passed");
} else {
notification.setTitle("Test runner executed successfully with test failures.");
notification.setContent(result.getFailureCount() + " test(s) failed.\n");
}
presenter.handleResponse(result);
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError exception) throws OperationException {
final String errorMessage = (exception.getMessage() != null) ? exception.getMessage()
: "Failed to run test cases";
notification.setContent(errorMessage);
notification.setStatus(FAIL);
}
});
delegate.doRunTests(e, parameters);
}
@Override
@ -109,4 +76,24 @@ public class RunClassTestAction extends JavaEditorAction {
super.updateProjectAction(e);
e.getPresentation().setVisible(true);
}
@Override
public NotificationManager getNotificationManager() {
return notificationManager;
}
@Override
public AppContext getAppContext() {
return appContext;
}
@Override
public TestServiceClient getService() {
return service;
}
@Override
public TestResultPresenter getPresenter() {
return presenter;
}
}

View File

@ -0,0 +1,83 @@
/*******************************************************************************
* 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.testing.junit.ide.action;
import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.FLOAT_MODE;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.PROGRESS;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.SUCCESS;
import java.util.Map;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.api.testing.shared.TestResult;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.notification.StatusNotification;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.plugin.testing.ide.TestServiceClient;
import org.eclipse.che.plugin.testing.ide.view.TestResultPresenter;
/**
* @author Mirage Abeysekara
* @author David Festal
*/
public class RunTestActionDelegate {
private final Source source;
public interface Source {
NotificationManager getNotificationManager();
AppContext getAppContext();
TestServiceClient getService();
TestResultPresenter getPresenter();
}
public RunTestActionDelegate(Source source) {
this.source = source;
}
public void doRunTests(ActionEvent e, Map<String, String> parameters) {
final StatusNotification notification = new StatusNotification("Running Tests...", PROGRESS, FLOAT_MODE);
source.getNotificationManager().notify(notification);
final Project project = source.getAppContext().getRootProject();
parameters.put("updateClasspath", "true");
Promise<TestResult> testResultPromise = source.getService().getTestResult(project.getPath(), "junit", parameters, notification);
testResultPromise.then(new Operation<TestResult>() {
@Override
public void apply(TestResult result) throws OperationException {
notification.setStatus(SUCCESS);
if (result.isSuccess()) {
notification.setTitle("Test runner executed successfully");
notification.setContent("All tests are passed");
} else {
notification.setTitle("Test runner executed successfully with test failures.");
notification.setContent(result.getFailureCount() + " test(s) failed.\n");
}
source.getPresenter().handleResponse(result);
}
}).catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError exception) throws OperationException {
final String errorMessage = (exception.getMessage() != null) ? exception.getMessage()
: "Failed to run test cases";
notification.setContent(errorMessage);
notification.setStatus(FAIL);
}
});
}
}

View File

@ -33,6 +33,10 @@
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-core</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto</artifactId>
@ -57,5 +61,27 @@
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-testing-classpath-server</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/**/AbstractTestListener.java</exclude>
<exclude>**/**/OutputTestListener.java</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -14,9 +14,11 @@ import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
@ -30,6 +32,12 @@ import org.eclipse.che.api.testing.shared.TestResult;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.plugin.testing.classpath.server.TestClasspathProvider;
import org.eclipse.che.plugin.testing.classpath.server.TestClasspathRegistry;
import org.eclipse.che.plugin.testing.junit.server.listener.AbstractTestListener;
import org.eclipse.che.plugin.testing.junit.server.listener.OutputTestListener;
import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
import javassist.util.proxy.ProxyFactory;
/**
* JUnit implementation for the test runner service.
@ -45,13 +53,14 @@ import org.eclipse.che.plugin.testing.classpath.server.TestClasspathRegistry;
* </pre>
*
* @author Mirage Abeysekara
* @author David Festal
*/
public class JUnitTestRunner implements 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 ProjectManager projectManager;
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 ProjectManager projectManager;
private TestClasspathRegistry classpathRegistry;
@Inject
@ -74,32 +83,59 @@ public class JUnitTestRunner implements TestRunner {
if (projectManager != null) {
projectType = projectManager.getProject(projectPath).getType();
}
ClassLoader currentClassLoader = this.getClass().getClassLoader();
TestClasspathProvider classpathProvider = classpathRegistry.getTestClasspathProvider(projectType);
projectClassLoader = classpathProvider.getClassLoader(projectAbsolutePath, updateClasspath);
TestResult testResult;
URLClassLoader providedClassLoader = (URLClassLoader)classpathProvider.getClassLoader(projectAbsolutePath, projectPath, updateClasspath);
projectClassLoader = new URLClassLoader(providedClassLoader.getURLs(), null) {
@Override
protected Class< ? > findClass(String name) throws ClassNotFoundException {
if (name.startsWith("javassist.")) {
return currentClassLoader.loadClass(name);
}
return super.findClass(name);
}
};
boolean isJUnit4Compatible = false;
boolean isJUnit3Compatible = false;
try {
Class.forName(JUNIT4X_RUNNER_CLASS, true, projectClassLoader);
if (runClass) {
String fqn = testParameters.get("fqn");
testResult = run4x(fqn);
} else {
testResult = runAll4x(projectAbsolutePath);
}
return testResult;
isJUnit4Compatible = true;
} catch (Exception ignored) {
}
try {
Class.forName(JUNIT3X_RUNNER_CLASS, true, projectClassLoader);
if (runClass) {
String fqn = testParameters.get("fqn");
testResult = run3x(fqn);
} else {
testResult = runAll3x(projectAbsolutePath);
}
return testResult;
isJUnit3Compatible = true;
} catch (Exception ignored) {
}
return null;
boolean useJUnitV3API = false;
if (!isJUnit4Compatible) {
if (!isJUnit3Compatible) {
throw new ClassNotFoundException("JUnit classes not found in the following project classpath: "
+ Arrays.asList(providedClassLoader.getURLs()));
} else {
useJUnitV3API = true;
}
}
String currentWorkingDir = System.getProperty("user.dir");
try {
System.setProperty("user.dir", projectAbsolutePath);
TestResult testResult;
if (runClass) {
String fqn = testParameters.get("fqn");
testResult = useJUnitV3API ? run3x(fqn) : run4x(fqn);
} else {
testResult = useJUnitV3API ? runAll3x(projectAbsolutePath) : runAll4x(projectAbsolutePath);
}
return testResult;
} finally {
System.setProperty("user.dir", currentWorkingDir);
}
}
/**
@ -112,7 +148,7 @@ public class JUnitTestRunner implements TestRunner {
private TestResult run4x(String testClass) throws Exception {
ClassLoader classLoader = projectClassLoader;
Class<?> clsTest = Class.forName(testClass, true, classLoader);
Class< ? > clsTest = Class.forName(testClass, true, classLoader);
return run4xTestClasses(clsTest);
}
@ -129,7 +165,7 @@ public class JUnitTestRunner implements TestRunner {
@SuppressWarnings("rawtypes")
List<Class> testableClasses = new ArrayList<>();
for (String className : testClassNames) {
Class<?> clazz = Class.forName(className, false, projectClassLoader);
Class< ? > clazz = Class.forName(className, false, projectClassLoader);
if (isTestable4x(clazz)) {
testableClasses.add(clazz);
}
@ -137,7 +173,7 @@ public class JUnitTestRunner implements TestRunner {
return run4xTestClasses(testableClasses.toArray(new Class[testableClasses.size()]));
}
private boolean isTestable4x(Class<?> clazz) throws ClassNotFoundException {
private boolean isTestable4x(Class< ? > clazz) throws ClassNotFoundException {
for (Method method : clazz.getDeclaredMethods()) {
for (Annotation annotation : method.getAnnotations()) {
if (annotation.annotationType().getName().equals("org.junit.Test")) {
@ -148,24 +184,107 @@ public class JUnitTestRunner implements TestRunner {
return false;
}
private TestResult run4xTestClasses(Class<?>... classes) throws Exception {
private Object create4xTestListener(ClassLoader loader, Class< ? > listenerClass, AbstractTestListener delegate) throws Exception {
ProxyFactory f = new ProxyFactory();
f.setSuperclass(listenerClass);
f.setFilter(new MethodFilter() {
@Override
public boolean isHandled(Method m) {
String methodName = m.getName();
switch (methodName) {
case "testStarted":
case "testFinished":
case "testFailure":
case "testAssumptionFailure":
return true;
}
return false;
}
});
Class< ? > c = f.createClass();
MethodHandler mi = new MethodHandler() {
@Override
public Object invoke(Object self, Method m, Method method, Object[] args) throws Throwable {
String methodName = m.getName();
Object description = null;
Throwable throwable = null;
switch (methodName) {
case "testStarted":
case "testFinished":
description = args[0];
throwable = null;
break;
case "testFailure":
case "testAssumptionFailure":
description = args[0].getClass().getMethod("getDescription", new Class< ? >[0]).invoke(args[0]);
throwable = (Throwable)args[0].getClass().getMethod("getException", new Class< ? >[0]).invoke(args[0]);
break;
default:
return null;
}
String testKey = (String)description.getClass().getMethod("getDisplayName", new Class< ? >[0]).invoke(description);
String testName = testKey;
switch (methodName) {
case "testStarted":
delegate.startTest(testKey, testName);
break;
case "testFinished":
delegate.endTest(testKey, testName);
break;
case "testFailure":
delegate.addFailure(testKey, throwable);
break;
case "testAssumptionFailure":
delegate.addError(testKey, throwable);
break;
}
return null;
}
};
Object listener = c.getConstructor().newInstance();
((javassist.util.proxy.Proxy)listener).setHandler(mi);
return listener;
}
private TestResult run4xTestClasses(Class< ? >... classes) throws Exception {
ClassLoader classLoader = projectClassLoader;
Class<?> clsJUnitCore = Class.forName("org.junit.runner.JUnitCore", true, classLoader);
Class<?> clsResult = Class.forName("org.junit.runner.Result", true, classLoader);
Class<?> clsFailure = Class.forName("org.junit.runner.notification.Failure", true, classLoader);
Class<?> clsDescription = Class.forName("org.junit.runner.Description", true, classLoader);
Class<?> clsThrowable = Class.forName("java.lang.Throwable", true, classLoader);
Class<?> clsStackTraceElement = Class.forName("java.lang.StackTraceElement", true, classLoader);
Object result = clsJUnitCore.getMethod("runClasses", Class[].class).invoke(null, new Object[] { classes });
Class< ? > clsJUnitCore = Class.forName("org.junit.runner.JUnitCore", true, classLoader);
Class< ? > clsResult = Class.forName("org.junit.runner.Result", true, classLoader);
Class< ? > clsFailure = Class.forName("org.junit.runner.notification.Failure", true, classLoader);
Class< ? > clsDescription = Class.forName("org.junit.runner.Description", true, classLoader);
Class< ? > clsThrowable = Class.forName("java.lang.Throwable", true, classLoader);
Class< ? > clsStackTraceElement = Class.forName("java.lang.StackTraceElement", true, classLoader);
Class< ? > clsTestRunner = Class.forName("org.junit.runner.notification.RunListener", true, classLoader);
Object jUnitCore = clsJUnitCore.getConstructor().newInstance();
Object result;
try (OutputTestListener outputListener = new OutputTestListener(this.getClass().getName() + ".run4xTestClasses")) {
Object testListener = create4xTestListener(classLoader, clsTestRunner, outputListener);
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(projectClassLoader);
clsJUnitCore.getMethod("addListener", clsTestRunner).invoke(jUnitCore, testListener);
result = clsJUnitCore.getMethod("run", Class[].class).invoke(jUnitCore, new Object[]{classes});
} finally {
Thread.currentThread().setContextClassLoader(tccl);
clsJUnitCore.getMethod("removeListener", clsTestRunner).invoke(jUnitCore, testListener);
}
}
TestResult dtoResult = DtoFactory.getInstance().createDto(TestResult.class);
boolean isSuccess = (Boolean) clsResult.getMethod("wasSuccessful").invoke(result);
List<?> failures = (List<?>) clsResult.getMethod("getFailures").invoke(result);
boolean isSuccess = (Boolean)clsResult.getMethod("wasSuccessful").invoke(result);
List< ? > failures = (List< ? >)clsResult.getMethod("getFailures").invoke(result);
List<Failure> jUnitFailures = new ArrayList<>();
for (Object failure : failures) {
Failure dtoFailure = DtoFactory.getInstance().createDto(Failure.class);
String message = (String) clsFailure.getMethod("getMessage").invoke(failure);
String message = (String)clsFailure.getMethod("getMessage").invoke(failure);
Object description = clsFailure.getMethod("getDescription").invoke(failure);
String failClassName = (String) clsDescription.getMethod("getClassName").invoke(description);
String failClassName = (String)clsDescription.getMethod("getClassName").invoke(description);
Object exception = clsFailure.getMethod("getException").invoke(failure);
Object stackTrace = clsThrowable.getMethod("getStackTrace").invoke(exception);
String failMethod = "";
@ -174,15 +293,15 @@ public class JUnitTestRunner implements TestRunner {
int length = Array.getLength(stackTrace);
for (int i = 0; i < length; i++) {
Object stackElement = Array.get(stackTrace, i);
String failClass = (String) clsStackTraceElement.getMethod("getClassName").invoke(stackElement);
String failClass = (String)clsStackTraceElement.getMethod("getClassName").invoke(stackElement);
if (failClass.equals(failClassName)) {
failMethod = (String) clsStackTraceElement.getMethod("getMethodName").invoke(stackElement);
failLine = (Integer) clsStackTraceElement.getMethod("getLineNumber").invoke(stackElement);
failMethod = (String)clsStackTraceElement.getMethod("getMethodName").invoke(stackElement);
failLine = (Integer)clsStackTraceElement.getMethod("getLineNumber").invoke(stackElement);
break;
}
}
}
String trace = (String) clsFailure.getMethod("getTrace").invoke(failure);
String trace = (String)clsFailure.getMethod("getTrace").invoke(failure);
dtoFailure.setFailingClass(failClassName);
dtoFailure.setFailingMethod(failMethod);
dtoFailure.setFailingLine(failLine);
@ -199,7 +318,7 @@ public class JUnitTestRunner implements TestRunner {
private TestResult run3x(String testClass) throws Exception {
ClassLoader classLoader = projectClassLoader;
Class<?> clsTest = Class.forName(testClass, true, classLoader);
Class< ? > clsTest = Class.forName(testClass, true, classLoader);
return run3xTestClasses(clsTest);
}
@ -217,7 +336,7 @@ public class JUnitTestRunner implements TestRunner {
@SuppressWarnings("rawtypes")
List<Class> testableClasses = new ArrayList<>();
for (String className : testClassNames) {
Class<?> clazz = Class.forName(className, false, projectClassLoader);
Class< ? > clazz = Class.forName(className, false, projectClassLoader);
if (isTestable3x(clazz)) {
testableClasses.add(clazz);
}
@ -225,33 +344,97 @@ public class JUnitTestRunner implements TestRunner {
return run3xTestClasses(testableClasses.toArray(new Class[testableClasses.size()]));
}
private boolean isTestable3x(Class<?> clazz) throws ClassNotFoundException {
Class<?> superClass = Class.forName("junit.framework.TestCase", true, projectClassLoader);
private boolean isTestable3x(Class< ? > clazz) throws ClassNotFoundException {
Class< ? > superClass = Class.forName("junit.framework.TestCase", true, projectClassLoader);
return superClass.isAssignableFrom(clazz);
}
private TestResult run3xTestClasses(Class<?>... classes) throws Exception {
private Object create3xTestListener(ClassLoader loader, Class< ? > listenerClass, AbstractTestListener delegate) throws Exception {
ProxyFactory f = new ProxyFactory();
f.setSuperclass(Object.class);
f.setInterfaces(new Class< ? >[]{listenerClass});
f.setFilter(new MethodFilter() {
@Override
public boolean isHandled(Method m) {
String methodName = m.getName();
switch (methodName) {
case "startTest":
case "endTest":
case "addError":
case "addFailure":
return true;
}
return false;
}
});
Class< ? > c = f.createClass();
MethodHandler mi = new MethodHandler() {
public Object invoke(Object self, Method method, Method proceed, Object[] args) throws Throwable {
String testKey = args[0].getClass().toString();
String testName = args[0].getClass().getName();
String methodName = method.getName();
switch (methodName) {
case "startTest":
delegate.startTest(testKey, testName);
break;
case "endTest":
delegate.endTest(testKey, testName);
break;
case "addError":
delegate.addError(testKey, (Throwable)args[1]);
break;
case "addFailure":
delegate.addFailure(testKey, (Throwable)args[1]);
break;
}
return null;
}
};
Object listener = c.getConstructor().newInstance();
((javassist.util.proxy.Proxy)listener).setHandler(mi);
return listener;
}
private TestResult run3xTestClasses(Class< ? >... classes) throws Exception {
ClassLoader classLoader = projectClassLoader;
Class<?> clsTestSuite = Class.forName("junit.framework.TestSuite", true, classLoader);
Class<?> clsTestResult = Class.forName("junit.framework.TestResult", true, classLoader);
Class<?> clsThrowable = Class.forName("java.lang.Throwable", true, classLoader);
Class<?> clsStackTraceElement = Class.forName("java.lang.StackTraceElement", true, classLoader);
Class<?> clsFailure = Class.forName("junit.framework.TestFailure", true, classLoader);
Object testSuite = clsTestSuite.newInstance();
Object testResult = clsTestResult.newInstance();
for (Class<?> testClass : classes) {
clsTestSuite.getMethod("addTestSuite", Class.class).invoke(testSuite, testClass);
Class< ? > clsTestSuite = Class.forName("junit.framework.TestSuite", true, classLoader);
Class< ? > clsTestResult = Class.forName("junit.framework.TestResult", true, classLoader);
Class< ? > clsThrowable = Class.forName("java.lang.Throwable", true, classLoader);
Class< ? > clsStackTraceElement = Class.forName("java.lang.StackTraceElement", true, classLoader);
Class< ? > clsFailure = Class.forName("junit.framework.TestFailure", true, classLoader);
Object testSuite = clsTestSuite.getConstructor().newInstance();
Object testResult = clsTestResult.getConstructor().newInstance();
Class< ? > clsTestListener = Class.forName("junit.framework.TestListener", true, classLoader);
try (OutputTestListener outputListener = new OutputTestListener(this.getClass().getName() + ".run3xTestClasses")) {
Object testListener = create3xTestListener(classLoader, clsTestListener, outputListener);
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(projectClassLoader);
clsTestResult.getMethod("addListener", clsTestListener).invoke(
testResult, testListener);
for (Class< ? > testClass : classes) {
clsTestSuite.getMethod("addTestSuite", Class.class).invoke(testSuite, testClass);
}
clsTestSuite.getMethod("run", clsTestResult).invoke(testSuite, testResult);
} finally {
Thread.currentThread().setContextClassLoader(tccl);
clsTestResult.getMethod("removeListener", clsTestListener).invoke(
testResult, testListener);
}
}
clsTestSuite.getMethod("run", clsTestResult).invoke(testSuite, testResult);
TestResult dtoResult = DtoFactory.getInstance().createDto(TestResult.class);
boolean isSuccess = (Boolean) clsTestResult.getMethod("wasSuccessful").invoke(testResult);
Enumeration<?> failures = (Enumeration<?>) clsTestResult.getMethod("failures").invoke(testResult);
boolean isSuccess = (Boolean)clsTestResult.getMethod("wasSuccessful").invoke(testResult);
Enumeration< ? > failures = (Enumeration< ? >)clsTestResult.getMethod("failures").invoke(testResult);
List<Failure> jUnitFailures = new ArrayList<>();
while (failures.hasMoreElements()) {
Failure dtoFailure = DtoFactory.getInstance().createDto(Failure.class);
Object failure = failures.nextElement();
String message = (String) clsFailure.getMethod("exceptionMessage").invoke(failure);
String trace = (String) clsFailure.getMethod("trace").invoke(failure);
String message = (String)clsFailure.getMethod("exceptionMessage").invoke(failure);
String trace = (String)clsFailure.getMethod("trace").invoke(failure);
Object failClassObject = clsFailure.getMethod("failedTest").invoke(failure);
String failClassName = failClassObject.getClass().getName();
Object exception = clsFailure.getMethod("thrownException").invoke(failure);
@ -262,10 +445,10 @@ public class JUnitTestRunner implements TestRunner {
int length = Array.getLength(stackTrace);
for (int i = 0; i < length; i++) {
Object arrayElement = Array.get(stackTrace, i);
String failClass = (String) clsStackTraceElement.getMethod("getClassName").invoke(arrayElement);
String failClass = (String)clsStackTraceElement.getMethod("getClassName").invoke(arrayElement);
if (failClass.equals(failClassName)) {
failMethod = (String) clsStackTraceElement.getMethod("getMethodName").invoke(arrayElement);
failLine = (Integer) clsStackTraceElement.getMethod("getLineNumber").invoke(arrayElement);
failMethod = (String)clsStackTraceElement.getMethod("getMethodName").invoke(arrayElement);
failLine = (Integer)clsStackTraceElement.getMethod("getLineNumber").invoke(arrayElement);
break;
}
}

View File

@ -0,0 +1,99 @@
/*******************************************************************************
* Copyright (c) 2017 RedHat, Inc.
* 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:
* RedHat, Inc. - initial commit
*******************************************************************************/
package org.eclipse.che.plugin.testing.junit.server.listener;
import java.util.HashMap;
/**
* Listener abstraction for the testing services to report their progress.
*
* @author David Festal
*/
public abstract class AbstractTestListener {
static public class TestSummary {
private int errors;
private int failures;
public TestSummary() {
this.errors = 0;
this.failures = 0;
}
public void addError() {
errors++;
}
public void addFailure() {
failures++;
}
public int getErrors() {
return errors;
}
public int getFailures() {
return failures;
}
public boolean succeeded() {
return failures == 0 && errors == 0;
}
@Override
public String toString() {
return new StringBuilder()
.append(failures)
.append(" failures and ")
.append(errors)
.append(" errors")
.toString();
}
}
HashMap<String, AbstractTestListener.TestSummary> runningTests = new HashMap<>();
public synchronized void startTest(String testKey, String testName) {
runningTests.put(testKey, null);
startedTest(testKey, testName);
}
public synchronized void endTest(String testKey, String testName) {
AbstractTestListener.TestSummary summary = runningTests.remove(testKey);
endedTest(testKey, testName, summary);
}
protected abstract void startedTest(String testKey, String testName);
protected abstract void endedTest(String testKey, String testName, AbstractTestListener.TestSummary summary);
protected abstract void addedFailure(String testKey, Throwable throwable);
protected abstract void addedError(String testKey, Throwable throwable);
private synchronized AbstractTestListener.TestSummary getOrCreateTestSummary(String testKey) {
AbstractTestListener.TestSummary summary = runningTests.get(testKey);
if (summary == null) {
summary = new TestSummary();
runningTests.put(testKey, summary);
}
return summary;
}
public void addFailure(String testKey, Throwable throwable) {
getOrCreateTestSummary(testKey).addFailure();
addedFailure(testKey, throwable);
}
public void addError(String testKey, Throwable throwable) {
getOrCreateTestSummary(testKey).addError();
addedError(testKey, throwable);
}
}

View File

@ -0,0 +1,108 @@
/*******************************************************************************
* Copyright (c) 2017 RedHat, Inc.
* 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:
* RedHat, Inc. - initial commit
*******************************************************************************/
package org.eclipse.che.plugin.testing.junit.server.listener;
import static org.eclipse.che.api.testing.shared.Constants.TESTING_OUTPUT_CHANNEL_NAME;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.eclipse.che.api.core.util.WebsocketMessageConsumer;
import org.eclipse.che.api.testing.server.handler.TestingOutputImpl;
import org.eclipse.che.api.testing.shared.TestingOutput;
import org.eclipse.che.dto.server.DtoFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Listener for the testing services to report their progress to the Che output view.
*
* @author David Festal
*/
public class OutputTestListener extends AbstractTestListener implements AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(AbstractTestListener.class);
private static final String consumeErrorMessage = "An exception occured while trying to send a 'TestingOutput' "
+ "object through web sockets on the following channel: "
+ TESTING_OUTPUT_CHANNEL_NAME;
private WebsocketMessageConsumer<TestingOutput> consumer = new WebsocketMessageConsumer<>(TESTING_OUTPUT_CHANNEL_NAME);
private String stackTraceRoot;
public OutputTestListener(String strackTraceRoot) {
this.stackTraceRoot = strackTraceRoot;
writeLine("Starting Test Session", TestingOutput.LineType.SESSION_START);
}
private void writeLine(String line, TestingOutput.LineType lineType) {
try {
consumer.consume(DtoFactory.cloneDto(new TestingOutputImpl(line, lineType)));
} catch (IOException e) {
LOG.error(consumeErrorMessage, e);
}
}
@Override
public void close() throws Exception {
writeLine("Finished Test Session", TestingOutput.LineType.SESSION_END);
consumer.close();
}
@Override
protected void startedTest(String testKey, String testName) {
writeLine("[Starting Test] " + testName, TestingOutput.LineType.DETAIL);
}
@Override
protected void endedTest(String testKey, String testName, TestSummary summary) {
TestingOutput.LineType lineType;
String detailText;
if (summary == null || summary.succeeded()) {
lineType = TestingOutput.LineType.SUCCESS;
detailText = "successfully";
} else {
detailText = "with " + summary;
if (summary.getErrors() > 0) {
lineType = TestingOutput.LineType.ERROR;
} else {
lineType = TestingOutput.LineType.FAILURE;
}
}
writeLine("[Finished Test] " + testName + " " + detailText, lineType);
}
private void addProblem(String testKey, Throwable throwable, boolean isError) {
StringWriter sw = new StringWriter();
TestingOutput.LineType lineType = isError ? TestingOutput.LineType.ERROR
: TestingOutput.LineType.FAILURE;
try (PrintWriter w = new PrintWriter(sw)) {
throwable.printStackTrace(w);
}
writeLine(" [" + lineType.name() + "]", lineType);
for (String line : sw.getBuffer().toString().split("\\n")) {
if (line.contains(stackTraceRoot)) {
break;
}
writeLine(" " + line, TestingOutput.LineType.DETAIL);
}
}
@Override
protected void addedFailure(String testKey, Throwable throwable) {
addProblem(testKey, throwable, false);
}
@Override
protected void addedError(String testKey, Throwable throwable) {
addProblem(testKey, throwable, true);
}
}

View File

@ -79,7 +79,7 @@ public class TestNGRunner implements TestRunner {
projectType = projectManager.getProject(projectPath).getType();
}
TestClasspathProvider classpathProvider = classpathRegistry.getTestClasspathProvider(projectType);
projectClassLoader = classpathProvider.getClassLoader(projectAbsolutePath, updateClasspath);
projectClassLoader = classpathProvider.getClassLoader(projectAbsolutePath, projectPath, updateClasspath);
TestResult testResult;
if (runClass) {
String fqn = testParameters.get("fqn");

View File

@ -72,6 +72,10 @@
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-java-ext-lang-client</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-machine-ext-client</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.che.plugin</groupId>
<artifactId>che-plugin-maven-shared</artifactId>
@ -117,6 +121,20 @@
</resource>
</resources>
<plugins>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/**/TestingHandler.java</exclude>
<exclude>**/**/ExecutorPromiseMocker.java</exclude>
<exclude>**/**/PromiseMocker.java</exclude>
<exclude>**/**/FunctionAnswer.java</exclude>
<exclude>**/**/MockitoPrinter.java</exclude>
<exclude>**/**/TestServiceClientTest.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>

View File

@ -10,17 +10,40 @@
*******************************************************************************/
package org.eclipse.che.plugin.testing.ide;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.api.promises.client.PromiseProvider;
import org.eclipse.che.api.promises.client.js.Executor;
import org.eclipse.che.api.promises.client.js.Executor.ExecutorBody;
import org.eclipse.che.api.promises.client.js.JsPromiseError;
import org.eclipse.che.api.promises.client.js.RejectFunction;
import org.eclipse.che.api.promises.client.js.ResolveFunction;
import org.eclipse.che.api.testing.shared.TestResult;
import org.eclipse.che.ide.MimeType;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.machine.ExecAgentCommandManager;
import org.eclipse.che.ide.api.macro.MacroProcessor;
import org.eclipse.che.ide.api.notification.StatusNotification;
import org.eclipse.che.ide.dto.DtoFactory;
import org.eclipse.che.ide.extension.machine.client.outputspanel.console.CommandConsoleFactory;
import org.eclipse.che.ide.extension.machine.client.outputspanel.console.CommandOutputConsole;
import org.eclipse.che.ide.extension.machine.client.processes.panel.ProcessesPanelPresenter;
import org.eclipse.che.ide.rest.AsyncRequestFactory;
import org.eclipse.che.ide.rest.DtoUnmarshallerFactory;
import org.eclipse.che.ide.rest.HTTPHeader;
import com.google.gwt.http.client.URL;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@ -28,25 +51,180 @@ import com.google.inject.Singleton;
* Client for calling test services
*
* @author Mirage Abeysekara
* @author David Festal
*/
@Singleton
public class TestServiceClient {
private final AppContext appContext;
private final AsyncRequestFactory asyncRequestFactory;
private final DtoUnmarshallerFactory dtoUnmarshallerFactory;
private final static RegExp mavenCleanBuildPattern =
RegExp.compile("(.*)mvn +clean +install +(\\-f +\\$\\{current\\.project\\.path\\}.*)");
public static final String PROJECT_BUILD_NOT_STARTED_MESSAGE = "The project build could not be started (see Build output). "
+ "Test run is cancelled.\n"
+ "You should probably check the settings of the 'test-compile' command.";
public static final String PROJECT_BUILD_FAILED_MESSAGE = "The project build failed (see Build output). "
+ "Test run is cancelled.\n"
+ "You might want to check the settings of the 'test-compile' command.";
public static final String EXECUTING_TESTS_MESSAGE = "Executing test session.";
private final AppContext appContext;
private final AsyncRequestFactory asyncRequestFactory;
private final DtoUnmarshallerFactory dtoUnmarshallerFactory;
private final CommandManager commandManager;
private final ExecAgentCommandManager execAgentCommandManager;
private final PromiseProvider promiseProvider;
private final MacroProcessor macroProcessor;
private final CommandConsoleFactory commandConsoleFactory;
private final ProcessesPanelPresenter processesPanelPresenter;
@Inject
public TestServiceClient(AppContext appContext,
public TestServiceClient(AppContext appContext,
AsyncRequestFactory asyncRequestFactory,
DtoUnmarshallerFactory dtoUnmarshallerFactory) {
DtoUnmarshallerFactory dtoUnmarshallerFactory,
DtoFactory dtoFactory,
CommandManager commandManager,
ExecAgentCommandManager execAgentCommandManager,
PromiseProvider promiseProvider,
MacroProcessor macroProcessor,
CommandConsoleFactory commandConsoleFactory,
ProcessesPanelPresenter processesPanelPresenter) {
this.appContext = appContext;
this.asyncRequestFactory = asyncRequestFactory;
this.dtoUnmarshallerFactory = dtoUnmarshallerFactory;
this.commandManager = commandManager;
this.execAgentCommandManager = execAgentCommandManager;
this.promiseProvider = promiseProvider;
this.macroProcessor = macroProcessor;
this.commandConsoleFactory = commandConsoleFactory;
this.processesPanelPresenter = processesPanelPresenter;
}
public Promise<CommandImpl> getOrCreateTestCompileCommand() {
List<CommandImpl> commands = commandManager.getCommands();
for (CommandImpl command : commands) {
if (command.getName() != null && command.getName().startsWith("test-compile") && "mvn".equals(command.getType())) {
return promiseProvider.resolve(command);
}
}
for (CommandImpl command : commands) {
if ("build".equals(command.getName()) && "mvn".equals(command.getType())) {
String commandLine = command.getCommandLine();
MatchResult result = mavenCleanBuildPattern.exec(commandLine);
if (result != null) {
String testCompileCommandLine = mavenCleanBuildPattern.replace(commandLine, "$1mvn test-compile $2");
return commandManager.create("test-compile", testCompileCommandLine, "mvn", new HashMap<String, String>());
}
}
}
return promiseProvider.resolve(null);
}
public Promise<TestResult> getTestResult(String projectPath, String testFramework, Map<String, String> parameters) {
return getTestResult(projectPath, testFramework, parameters, null);
}
Promise<TestResult> promiseFromExecutorBody(ExecutorBody<TestResult> executorBody) {
return promiseProvider.create(Executor.create(executorBody));
}
PromiseError promiseFromThrowable(Throwable t) {
return JsPromiseError.create(t);
}
Promise<TestResult> runTestsAfterCompilation(String projectPath,
String testFramework,
Map<String, String> parameters,
StatusNotification statusNotification,
Promise<CommandImpl> compileCommand) {
return compileCommand.thenPromise(command -> {
final Machine machine;
if (command == null) {
machine = null;
} else {
machine = appContext.getDevMachine().getDescriptor();
}
if (machine == null) {
if (statusNotification != null) {
statusNotification.setContent("Executing the tests without preliminary compilation.");
}
return sendTests(projectPath, testFramework, parameters);
}
if (statusNotification != null) {
statusNotification.setContent("Compiling the project before starting the test session.");
}
return promiseFromExecutorBody(new ExecutorBody<TestResult>() {
boolean compiled = false;
@Override
public void apply(final ResolveFunction<TestResult> resolve, RejectFunction reject) {
macroProcessor.expandMacros(command.getCommandLine()).then(new Operation<String>() {
@Override
public void apply(String expandedCommandLine) throws OperationException {
CommandImpl expandedCommand = new CommandImpl(command.getName(), expandedCommandLine,
command.getType(), command.getAttributes());
final CommandOutputConsole console = commandConsoleFactory.create(expandedCommand, machine);
final String machineId = machine.getId();
processesPanelPresenter.addCommandOutput(machineId, console);
execAgentCommandManager.startProcess(machineId, expandedCommand)
.then(startResonse -> {
if (!startResonse.getAlive()) {
reject.apply(promiseFromThrowable(new Throwable(PROJECT_BUILD_NOT_STARTED_MESSAGE)));
}
})
.thenIfProcessStartedEvent(console.getProcessStartedOperation())
.thenIfProcessStdErrEvent(evt -> {
if (evt.getText().contains("BUILD SUCCESS")) {
compiled = true;
}
console.getStdErrOperation().apply(evt);
})
.thenIfProcessStdOutEvent(evt -> {
if (evt.getText().contains("BUILD SUCCESS")) {
compiled = true;
}
console.getStdOutOperation().apply(evt);
})
.thenIfProcessDiedEvent(evt -> {
console.getProcessDiedOperation().apply(evt);
if (compiled) {
if (statusNotification != null) {
statusNotification.setContent(EXECUTING_TESTS_MESSAGE);
}
sendTests(projectPath,
testFramework,
parameters).then(new Operation<TestResult>() {
@Override
public void apply(TestResult result) throws OperationException {
resolve.apply(result);
}
});
} else {
reject.apply(promiseFromThrowable(new Throwable(PROJECT_BUILD_FAILED_MESSAGE)));
}
});
}
});
}
});
});
}
public Promise<TestResult> getTestResult(String projectPath,
String testFramework,
Map<String, String> parameters,
StatusNotification statusNotification) {
return runTestsAfterCompilation(projectPath, testFramework, parameters, statusNotification,
getOrCreateTestCompileCommand());
}
public Promise<TestResult> sendTests(String projectPath, String testFramework, Map<String, String> parameters) {
StringBuilder sb = new StringBuilder();
if (parameters != null) {
for (Map.Entry<String, String> e : parameters.entrySet()) {
@ -57,9 +235,9 @@ public class TestServiceClient {
}
}
String url = appContext.getDevMachine().getWsAgentBaseUrl() + "/che/testing/run/?projectPath=" + projectPath
+ "&testFramework=" + testFramework + "&" + sb.toString();
+ "&testFramework=" + testFramework + "&" + sb.toString();
return asyncRequestFactory.createGetRequest(url).header(HTTPHeader.ACCEPT, MimeType.APPLICATION_JSON)
.send(dtoUnmarshallerFactory.newUnmarshaller(TestResult.class));
.send(dtoUnmarshallerFactory.newUnmarshaller(TestResult.class));
}
}

View File

@ -18,6 +18,7 @@ import java.util.Set;
import org.eclipse.che.ide.api.action.ActionManager;
import org.eclipse.che.ide.api.action.DefaultActionGroup;
import org.eclipse.che.ide.api.extension.Extension;
import org.eclipse.che.plugin.testing.ide.handler.TestingHandler;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@ -34,7 +35,8 @@ public class TestingExtension {
@Inject
public TestingExtension(ActionManager actionManager,
TestLocalizationConstant localization,
Set<TestAction> testActions) {
Set<TestAction> testActions,
TestingHandler testingHandler) {
DefaultActionGroup runMenu = (DefaultActionGroup) actionManager.getAction(GROUP_RUN);
DefaultActionGroup testMainMenu = new DefaultActionGroup(localization.actionGroupMenuName(), true,
actionManager);

View File

@ -0,0 +1,117 @@
/*******************************************************************************
* Copyright (c) 2017 RedHat, Inc.
* 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:
* RedHat, Inc. - initial commit
*******************************************************************************/
package org.eclipse.che.plugin.testing.ide.handler;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.web.bindery.event.shared.EventBus;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.testing.shared.TestingOutput;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.machine.WsAgentStateController;
import org.eclipse.che.ide.api.machine.events.WsAgentStateEvent;
import org.eclipse.che.ide.api.machine.events.WsAgentStateHandler;
import org.eclipse.che.ide.dto.DtoFactory;
import org.eclipse.che.ide.extension.machine.client.outputspanel.console.CommandConsoleFactory;
import org.eclipse.che.ide.extension.machine.client.outputspanel.console.DefaultOutputConsole;
import org.eclipse.che.ide.extension.machine.client.processes.panel.ProcessesPanelPresenter;
import org.eclipse.che.ide.util.loging.Log;
import org.eclipse.che.ide.websocket.MessageBus;
import org.eclipse.che.ide.websocket.WebSocketException;
import org.eclipse.che.ide.websocket.events.MessageHandler;
import static org.eclipse.che.api.testing.shared.Constants.TESTING_OUTPUT_CHANNEL_NAME;;
/**
* Handler which receives messages from the Testing tools.
*
* @author David Festal
*/
@Singleton
public class TestingHandler {
private final EventBus eventBus;
private final DtoFactory factory;
private final ProcessesPanelPresenter processesPanelPresenter;
private final CommandConsoleFactory commandConsoleFactory;
private final AppContext appContext;
@Inject
public TestingHandler(EventBus eventBus,
DtoFactory factory,
WsAgentStateController wsAgentStateController,
ProcessesPanelPresenter processesPanelPresenter,
CommandConsoleFactory commandConsoleFactory,
AppContext appContext) {
this.eventBus = eventBus;
this.factory = factory;
this.processesPanelPresenter = processesPanelPresenter;
this.commandConsoleFactory = commandConsoleFactory;
this.appContext = appContext;
handleOperations(factory, wsAgentStateController);
}
private void handleOperations(final DtoFactory factory, final WsAgentStateController wsAgentStateController) {
eventBus.addHandler(WsAgentStateEvent.TYPE, new WsAgentStateHandler() {
@Override
public void onWsAgentStarted(WsAgentStateEvent event) {
wsAgentStateController.getMessageBus().then(new Operation<MessageBus>() {
@Override
public void apply(MessageBus messageBus) throws OperationException {
handleTestingOutput(messageBus);
}
});
}
@Override
public void onWsAgentStopped(WsAgentStateEvent event) {
}
});
}
private void handleTestingOutput(final MessageBus messageBus) {
final DefaultOutputConsole outputConsole = (DefaultOutputConsole)commandConsoleFactory.create("Tests");
try {
messageBus.subscribe(TESTING_OUTPUT_CHANNEL_NAME, new MessageHandler() {
@Override
public void onMessage(String message) {
Log.info(getClass(), message);
TestingOutput archetypeOutput = factory.createDtoFromJson(message, TestingOutput.class);
switch (archetypeOutput.getState()) {
case SESSION_START:
processesPanelPresenter.addCommandOutput(appContext.getDevMachine().getId(), outputConsole);
outputConsole.clearOutputsButtonClicked();
case DETAIL:
outputConsole.printText(archetypeOutput.getOutput());
break;
case SUCCESS:
outputConsole.printText(archetypeOutput.getOutput(), "green");
break;
case ERROR:
outputConsole.printText(archetypeOutput.getOutput(), "red");
break;
case FAILURE:
outputConsole.printText(archetypeOutput.getOutput(), "darkred");
break;
default:
break;
}
}
});
} catch (WebSocketException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,59 @@
/*******************************************************************************
* Copyright (c) 2017 RedHat, Inc.
* 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:
* RedHat, Inc. - initial commit
*******************************************************************************/package org.eclipse.che.plugin.testing.ide;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import java.util.function.BiFunction;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.api.promises.client.js.Executor;
import org.eclipse.che.api.promises.client.js.RejectFunction;
import org.eclipse.che.api.promises.client.js.ResolveFunction;
import org.mockito.Matchers;
/**
* Utility class that allows mocking a Che Promise from an Executor.ExecutorBody.
*
* @author David Festal
*/
public class ExecutorPromiseMocker<T> extends PromiseMocker<T> {
private final ResolveFunction<T> resolveFunction;
private final RejectFunction rejectFunction;
@SuppressWarnings("unchecked")
public ExecutorPromiseMocker(final Executor.ExecutorBody<T> executorBody,
final BiFunction<T, PromiseMocker<T>, Void> onResolved,
final BiFunction<PromiseError, PromiseMocker<T>, Void> onRejected) {
super();
resolveFunction = (ResolveFunction<T>) mock(ResolveFunction.class);
rejectFunction = mock(RejectFunction.class);
doAnswer(new FunctionAnswer<T, Void>(resolvedValue-> {
onResolved.apply(resolvedValue, this);
return null;
})).when(resolveFunction).apply(Matchers.<T>any());
doAnswer(new FunctionAnswer<PromiseError, Void>(promiseError -> {
onRejected.apply(promiseError, this);
return null;
})).when(rejectFunction).apply(any(PromiseError.class));
}
public ResolveFunction<T> getResolveFunction() {
return resolveFunction;
}
public RejectFunction getRejectFunction() {
return rejectFunction;
}
}

View File

@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2017 RedHat, Inc.
* 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:
* RedHat, Inc. - initial commit
*******************************************************************************/package org.eclipse.che.plugin.testing.ide;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
/**
* Mockito Answer that applies a given function to the first argument of
* the answer InvocationOnMock argument.
*
* @author David Festal
*/
public class FunctionAnswer<ArgumentType, Return> implements Answer<Return> {
private java.util.function.Function<ArgumentType, Return> apply;
public FunctionAnswer(java.util.function.Function<ArgumentType, Return> apply) {
this.apply = apply;
}
@Override
public Return answer(InvocationOnMock invocation) throws Throwable {
@SuppressWarnings("unchecked")
ArgumentType arg = (ArgumentType)invocation.getArguments()[0];
return apply.apply(arg);
}
}

View File

@ -0,0 +1,37 @@
/*******************************************************************************
* Copyright (c) 2017 RedHat, Inc.
* 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:
* RedHat, Inc. - initial commit
*******************************************************************************/package org.eclipse.che.plugin.testing.ide;
import java.util.Arrays;
import org.mockito.Mock;
import org.mockito.internal.debugging.MockitoDebuggerImpl;
/**
* Interface that allows printing the invocations made on
* all mocked / spied fields.
*
* @author David Festal
*/
public interface MockitoPrinter {
default void printInvocationsOnAllMockedFields() {
new MockitoDebuggerImpl().printInvocations(Arrays.asList(this.getClass().getDeclaredFields()).stream().filter(field -> {
return field.isAnnotationPresent(Mock.class);
}).map(field -> {
try {
field.setAccessible(true);
return field.get(this);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
return null;
}
}).filter(field -> field != null).toArray(Object[]::new));
}
}

View File

@ -0,0 +1,77 @@
/*******************************************************************************
* Copyright (c) 2017 RedHat, Inc.
* 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:
* RedHat, Inc. - initial commit
*******************************************************************************/package org.eclipse.che.plugin.testing.ide;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.eclipse.che.api.promises.client.Function;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseError;
import org.mockito.Matchers;
/**
* Mockito mock provider for Che Promise objects.
*
* @author David Festal
*/
public class PromiseMocker<T> {
final private Promise<T> promise;
public PromiseMocker(Promise<T> promise) {
this.promise = promise;
}
@SuppressWarnings("unchecked")
public PromiseMocker() {
this.promise = (Promise<T>) mock(Promise.class);
}
public Promise<T> getPromise() {
return promise;
}
public <B> PromiseMocker<T> applyOnThenPromise(T value) {
when(promise.thenPromise(Matchers.<Function<T, Promise<B>>> any())).then(new FunctionAnswer<Function<T, Promise<B>>, Promise<B>>(function -> {
try {
return function.apply(value);
} catch(Exception e) {
e.printStackTrace();
}
return null;
}));
return this;
}
public PromiseMocker<T> applyOnThenOperation(T value) {
when(promise.then(Matchers.<Operation<T>> any())).then(new FunctionAnswer<Operation<T>, Promise<T>>( op -> {
try {
op.apply(value);
} catch(Exception e) {
e.printStackTrace();
}
return promise;
}));
return this;
}
public PromiseMocker<T> applyOnCatchErrorOperation(PromiseError error) {
when(promise.catchError(Matchers.<Operation<PromiseError>> any())).then(new FunctionAnswer<Operation<PromiseError>, Promise<T>>(op -> {
try {
op.apply(error);
} catch (OperationException e) {
e.printStackTrace();
}
return promise;
}));
return this;
}
}

View File

@ -0,0 +1,466 @@
/*******************************************************************************
* Copyright (c) 2017 RedHat, Inc.
* 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:
* RedHat, Inc. - initial commit
*******************************************************************************/
package org.eclipse.che.plugin.testing.ide;
import static java.util.Arrays.asList;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyMapOf;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.eclipse.che.api.core.model.machine.Command;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.api.machine.shared.dto.execagent.ProcessStartResponseDto;
import org.eclipse.che.api.machine.shared.dto.execagent.event.DtoWithPid;
import org.eclipse.che.api.machine.shared.dto.execagent.event.ProcessDiedEventDto;
import org.eclipse.che.api.machine.shared.dto.execagent.event.ProcessStartedEventDto;
import org.eclipse.che.api.machine.shared.dto.execagent.event.ProcessStdErrEventDto;
import org.eclipse.che.api.machine.shared.dto.execagent.event.ProcessStdOutEventDto;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.api.promises.client.PromiseProvider;
import org.eclipse.che.api.promises.client.js.Executor;
import org.eclipse.che.api.testing.shared.TestResult;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.machine.DevMachine;
import org.eclipse.che.ide.api.machine.ExecAgentCommandManager;
import org.eclipse.che.ide.api.machine.execagent.ExecAgentPromise;
import org.eclipse.che.ide.api.macro.MacroProcessor;
import org.eclipse.che.ide.api.notification.StatusNotification;
import org.eclipse.che.ide.dto.DtoFactory;
import org.eclipse.che.ide.extension.machine.client.outputspanel.console.CommandConsoleFactory;
import org.eclipse.che.ide.extension.machine.client.outputspanel.console.CommandOutputConsole;
import org.eclipse.che.ide.extension.machine.client.processes.panel.ProcessesPanelPresenter;
import org.eclipse.che.ide.rest.AsyncRequestFactory;
import org.eclipse.che.ide.rest.DtoUnmarshallerFactory;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import com.google.gwtmockito.GwtMockitoTestRunner;
/**
* Test for the TestServiceClient class.
*
* @author David Festal
*/
@RunWith(GwtMockitoTestRunner.class)
public class TestServiceClientTest implements MockitoPrinter {
// Context
@Mock
private AppContext appContext;
@Mock
private AsyncRequestFactory asyncRequestFactory;
@Mock
private DtoUnmarshallerFactory dtoUnmarshallerFactory;
@Mock
private CommandManager commandManager;
@Mock
private ExecAgentCommandManager execAgentCommandManager;
@Mock
private PromiseProvider promiseProvider;
@Mock
private MacroProcessor macroProcessor;
@Mock
private CommandConsoleFactory commandConsoleFactory;
@Mock
private ProcessesPanelPresenter processesPanelPresenter;
@Mock
private DtoFactory dtoFactory;
@Mock
private StatusNotification statusNotification;
@Mock
private DevMachine devMachine;
@Mock
private Machine machine;
@Mock
private CommandOutputConsole commandOutputConsole;
private TestServiceClient testServiceClient = null;
@Spy
private final List<DtoWithPid> consoleEvents = new ArrayList<>();
private static final String rootOfProjects = "/projects";
private Map<String, String> parameters = new HashMap<>();
private String testFramework = "junit";
private String projectPath = "sampleProject";
@SuppressWarnings("rawtypes")
private Map<Class< ? >, Operation> operationsOnProcessEvents = new HashMap<>();
@Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
testServiceClient = spy(new TestServiceClient(appContext, asyncRequestFactory, dtoUnmarshallerFactory, dtoFactory, commandManager,
execAgentCommandManager, promiseProvider, macroProcessor, commandConsoleFactory,
processesPanelPresenter));
doReturn(new PromiseMocker<TestResult>().getPromise()).when(testServiceClient).sendTests(anyString(), anyString(),
anyMapOf(String.class, String.class));
doAnswer(new FunctionAnswer<Executor.ExecutorBody<TestResult>, Promise<TestResult>>(executorBody -> {
ExecutorPromiseMocker<TestResult> mocker = new ExecutorPromiseMocker<TestResult>(executorBody,
(testResult, thisMocker) -> {
thisMocker.applyOnThenOperation(testResult);
return null;
},
(promiseError, thisMocker) -> {
thisMocker.applyOnCatchErrorOperation(promiseError);
return null;
});
executorBody.apply(mocker.getResolveFunction(), mocker.getRejectFunction());
return mocker.getPromise();
})).when(testServiceClient).promiseFromExecutorBody(Matchers.<Executor.ExecutorBody<TestResult>> any());
doAnswer(new FunctionAnswer<Throwable, PromiseError>(throwable -> {
PromiseError promiseError = mock(PromiseError.class);
when(promiseError.getCause()).thenReturn(throwable);
return promiseError;
})).when(testServiceClient).promiseFromThrowable(any(Throwable.class));
when(appContext.getDevMachine()).thenReturn(devMachine);
when(machine.getId()).thenReturn("DevMachineId");
doAnswer(new FunctionAnswer<String, Promise<String>>(commandLine -> {
String processedCommandLine = commandLine.replace("${current.project.path}", rootOfProjects + "/" + projectPath);
return new PromiseMocker<String>().applyOnThenOperation(processedCommandLine).getPromise();
})).when(macroProcessor).expandMacros(anyString());
when(commandConsoleFactory.create(any(CommandImpl.class), any(Machine.class))).then(createCall -> {
CommandOutputConsole commandOutputConsole = mock(CommandOutputConsole.class);
when(commandOutputConsole.getProcessStartedOperation()).thenReturn(processStartedEvent -> {
consoleEvents.add(processStartedEvent);
});
when(commandOutputConsole.getProcessDiedOperation()).thenReturn(processDiedEvent -> {
consoleEvents.add(processDiedEvent);
});
when(commandOutputConsole.getStdErrOperation()).thenReturn(processStdErrEvent -> {
consoleEvents.add(processStdErrEvent);
});
when(commandOutputConsole.getStdOutOperation()).thenReturn(processStdOutEvent -> {
consoleEvents.add(processStdOutEvent);
});
return commandOutputConsole;
});
consoleEvents.clear();
when(execAgentCommandManager.startProcess(anyString(), any(Command.class))).then(startProcessCall -> {
@SuppressWarnings("unchecked")
ExecAgentPromise<ProcessStartResponseDto> execAgentPromise =
(ExecAgentPromise<ProcessStartResponseDto>)mock(ExecAgentPromise.class);
class ProcessEventForward<DtoType> extends FunctionAnswer<Operation<DtoType>, ExecAgentPromise<ProcessStartResponseDto>> {
public ProcessEventForward(Class<DtoType> dtoClass) {
super(new java.util.function.Function<Operation<DtoType>, ExecAgentPromise<ProcessStartResponseDto>>() {
@Override
public ExecAgentPromise<ProcessStartResponseDto> apply(Operation<DtoType> op) {
operationsOnProcessEvents.put(dtoClass, op);
return execAgentPromise;
}
});
}
}
when(execAgentPromise.then(any())).then(new ProcessEventForward<>(ProcessStartResponseDto.class));
when(execAgentPromise.thenIfProcessStartedEvent(any())).then(new ProcessEventForward<>(ProcessStartedEventDto.class));
when(execAgentPromise.thenIfProcessDiedEvent(any())).then(new ProcessEventForward<>(ProcessDiedEventDto.class));
when(execAgentPromise.thenIfProcessStdErrEvent(any())).then(new ProcessEventForward<>(ProcessStdErrEventDto.class));
when(execAgentPromise.thenIfProcessStdOutEvent(any())).then(new ProcessEventForward<>(ProcessStdOutEventDto.class));
return execAgentPromise;
});
operationsOnProcessEvents.clear();
}
@SuppressWarnings("unchecked")
private void triggerProcessEvents(DtoWithPid... processEvents) {
for (DtoWithPid event : processEvents) {
operationsOnProcessEvents.entrySet().stream().filter(entry -> {
return entry.getKey().isAssignableFrom(event.getClass());
}).map(Map.Entry::getValue).forEach(op -> {
try {
op.apply(event);
} catch (OperationException e) {
e.printStackTrace();
}
});
}
}
@Test
public void createCompileCommandFromStandardMavenCommands() {
when(commandManager.getCommands()).thenReturn(asList(new CommandImpl("run",
"mvn run -f ${current.project.path}",
"mvn"),
new CommandImpl("build",
"mvn clean install -f ${current.project.path}",
"mvn")));
testServiceClient.getOrCreateTestCompileCommand();
verify(commandManager).create("test-compile",
"mvn test-compile -f ${current.project.path}",
"mvn",
Collections.emptyMap());
}
@Test
public void createCompileCommandFromSCLEnabledMavenBuildCommand() {
when(commandManager.getCommands()).thenReturn(asList(new CommandImpl("build",
"scl enable rh-maven33 'mvn clean install -f ${current.project.path}'",
"mvn")));
testServiceClient.getOrCreateTestCompileCommand();
verify(commandManager).create("test-compile",
"scl enable rh-maven33 'mvn test-compile -f ${current.project.path}'",
"mvn",
Collections.emptyMap());
}
@Test
public void reuseExistingCompileCommand() {
CommandImpl existingCompileCommand = new CommandImpl("test-compile",
"mvn test-compile -f ${current.project.path}",
"mvn");
when(commandManager.getCommands()).thenReturn(asList(new CommandImpl("run",
"mvn run -f ${current.project.path}",
"mvn"),
new CommandImpl("build",
"mvn clean install -f ${current.project.path}",
"mvn"),
existingCompileCommand));
testServiceClient.getOrCreateTestCompileCommand();
verify(promiseProvider).resolve(existingCompileCommand);
}
@Test
public void noBuildCommand() {
when(commandManager.getCommands()).thenReturn(asList(new CommandImpl("customBuild",
"mvn clean install -f ${current.project.path}",
"mvn")));
testServiceClient.getOrCreateTestCompileCommand();
verify(promiseProvider).resolve(null);
}
@Test
public void buildCommandNotAMavenCommand() {
when(commandManager.getCommands()).thenReturn(asList(new CommandImpl("build",
"mvn clean install -f ${current.project.path}",
"someOtherType")));
testServiceClient.getOrCreateTestCompileCommand();
verify(promiseProvider).resolve(null);
}
@Test
public void mavenBuildCommandHasNoCleanInstallPart() {
when(commandManager.getCommands()).thenReturn(asList(new CommandImpl("build",
"mvn clean SomeOtherGoalInTeMiddle install -f ${current.project.path}",
"mvn")));
testServiceClient.getOrCreateTestCompileCommand();
verify(promiseProvider).resolve(null);
}
private Promise<CommandImpl> createCommandPromise(CommandImpl command) {
return new PromiseMocker<CommandImpl>().applyOnThenPromise(command).getPromise();
}
@Test
public void runTestsDirectlyBecauseNoCompilationCommand() {
Promise<CommandImpl> compileCommandPromise = createCommandPromise(null);
testServiceClient.runTestsAfterCompilation(projectPath, testFramework, parameters, statusNotification, compileCommandPromise);
verify(statusNotification).setContent("Executing the tests without preliminary compilation.");
verify(execAgentCommandManager, never()).startProcess(anyString(), Matchers.<Command> any());
verify(testServiceClient).sendTests(projectPath, testFramework, parameters);
}
@Test
public void runTestsDirectlyBecauseNoDevMachine() {
Promise<CommandImpl> compileCommandPromise = createCommandPromise(new CommandImpl("test-compile",
"mvn test-compile -f ${current.project.path}",
"mvn"));
when(devMachine.getDescriptor()).thenReturn(null);
testServiceClient.runTestsAfterCompilation(projectPath, testFramework, parameters, statusNotification, compileCommandPromise);
verify(statusNotification).setContent("Executing the tests without preliminary compilation.");
verify(execAgentCommandManager, never()).startProcess(anyString(), Matchers.<Command> any());
verify(testServiceClient).sendTests(projectPath, testFramework, parameters);
}
private ProcessStartResponseDto processStartResponse(boolean alive) {
ProcessStartResponseDto event = mock(ProcessStartResponseDto.class);
when(event.getAlive()).thenReturn(alive);
return event;
}
private ProcessStartedEventDto processStarted() {
ProcessStartedEventDto event = mock(ProcessStartedEventDto.class);
when(event.toString()).thenReturn("Started");
return event;
}
private ProcessDiedEventDto processDied() {
ProcessDiedEventDto event = mock(ProcessDiedEventDto.class);
when(event.toString()).thenReturn("Died");
return event;
}
private ProcessStdErrEventDto processStdErr(final String text) {
ProcessStdErrEventDto event = mock(ProcessStdErrEventDto.class);
when(event.getText()).thenReturn(text);
when(event.toString()).thenReturn("StdErr - " + text);
return event;
}
private ProcessStdOutEventDto processStdOut(String text) {
ProcessStdOutEventDto event = mock(ProcessStdOutEventDto.class);
when(event.getText()).thenReturn(text);
when(event.toString()).thenReturn("StdOut - " + text);
return event;
}
@Test
public void cancelledTestsBecauseCompilationNotStarted() {
Promise<CommandImpl> compileCommandPromise = createCommandPromise(new CommandImpl(
"test-compile",
"mvn test-compile -f ${current.project.path}",
"mvn"));
when(devMachine.getDescriptor()).thenReturn(machine);
Promise<TestResult> result = testServiceClient.runTestsAfterCompilation(projectPath, testFramework, parameters, statusNotification,
compileCommandPromise);
triggerProcessEvents(processStartResponse(false));
verify(testServiceClient, never()).sendTests(anyString(), anyString(), anyMapOf(String.class, String.class));
verify(statusNotification).setContent("Compiling the project before starting the test session.");
result.catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError promiseError) throws OperationException {
Throwable cause = promiseError.getCause();
Assert.assertNotNull(cause);
Assert.assertEquals(TestServiceClient.PROJECT_BUILD_NOT_STARTED_MESSAGE, cause.getMessage());
}
});
}
@Test
public void cancelledTestsBecauseCompilationFailed() {
Promise<CommandImpl> compileCommandPromise = createCommandPromise(new CommandImpl(
"test-compile",
"mvn test-compile -f ${current.project.path}",
"mvn"));
when(devMachine.getDescriptor()).thenReturn(machine);
Promise<TestResult> result = testServiceClient.runTestsAfterCompilation(projectPath, testFramework, parameters, statusNotification,
compileCommandPromise);
triggerProcessEvents(processStartResponse(true),
processStarted(),
processStdErr("A small warning"),
processStdOut("BUILD FAILURE"),
processDied());
verify(testServiceClient, never()).sendTests(anyString(), anyString(), anyMapOf(String.class, String.class));
verify(statusNotification).setContent("Compiling the project before starting the test session.");
result.catchError(new Operation<PromiseError>() {
@Override
public void apply(PromiseError promiseError) throws OperationException {
Throwable cause = promiseError.getCause();
Assert.assertNotNull(cause);
Assert.assertEquals(TestServiceClient.PROJECT_BUILD_FAILED_MESSAGE, cause.getMessage());
}
});
}
@Test
public void sucessfulTestsAfterCompilation() {
Promise<CommandImpl> compileCommandPromise = createCommandPromise(new CommandImpl(
"test-compile",
"mvn test-compile -f ${current.project.path}",
"mvn"));
when(devMachine.getDescriptor()).thenReturn(machine);
Promise<TestResult> resultPromise = testServiceClient.runTestsAfterCompilation(projectPath, testFramework, parameters,
statusNotification,
compileCommandPromise);
triggerProcessEvents(processStartResponse(true),
processStarted(),
processStdErr("A small warning"),
processStdOut("BUILD SUCCESS"),
processDied());
verify(testServiceClient).sendTests(projectPath, testFramework, parameters);
verify(statusNotification).setContent("Compiling the project before starting the test session.");
verify(execAgentCommandManager).startProcess(
"DevMachineId",
new CommandImpl(
"test-compile",
"mvn test-compile -f " + rootOfProjects + "/" + projectPath,
"mvn"));
verify(statusNotification).setContent(TestServiceClient.EXECUTING_TESTS_MESSAGE);
resultPromise.then(testResult -> {
Assert.assertNotNull(testResult);
});
ArrayList<String> eventStrings = new ArrayList<>();
for (DtoWithPid event : consoleEvents) {
eventStrings.add(event.toString());
}
Assert.assertEquals(eventStrings,
Arrays.asList("Started",
"StdErr - A small warning",
"StdOut - BUILD SUCCESS",
"Died"));
}
}

View File

@ -37,5 +37,17 @@
<directory>src/main/java</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/**/TestingOutput.java</exclude>
<exclude>**/**/Constants.java</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,25 @@
/*******************************************************************************
* Copyright (c) 2017 RedHat, Inc.
* 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:
* RedHat, Inc. - initial commit
*******************************************************************************/
package org.eclipse.che.api.testing.shared;
/**
* @author David Festal
*/
public class Constants {
/** Name of WebSocket channel for the Testing output */
public final static String TESTING_OUTPUT_CHANNEL_NAME = "testing:output";
private Constants() {
}
}

View File

@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2017 RedHat, Inc.
* 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:
* RedHat, Inc. - initial commit
*******************************************************************************/
package org.eclipse.che.api.testing.shared;
import org.eclipse.che.dto.shared.DTO;
/**
* Provide the output of Testing tools.
*
* @author David Festal
*/
@DTO
public interface TestingOutput {
enum LineType {
SESSION_START,
DETAIL,
SUCCESS,
ERROR,
FAILURE,
SESSION_END,
}
/**
* Output line
* @return
*/
String getOutput();
/**
* for a success notification line will be State.SUCCESS
* for a failure notification line will be State.FAILURE
* for an error notification will be State.ERROR
* for all other lines will be State.DETAIL
* @return
*/
LineType getState();
}

View File

@ -111,6 +111,15 @@
</resource>
</resources>
<plugins>
<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/**/TestingOutputImpl.java</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-api-dto-maven-plugin</artifactId>

View File

@ -0,0 +1,37 @@
/*******************************************************************************
* Copyright (c) 2017 RedHat, Inc.
* 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:
* RedHat, Inc. - initial commit
*******************************************************************************/
package org.eclipse.che.api.testing.server.handler;
import org.eclipse.che.api.testing.shared.TestingOutput;
/**
* @author David Festal
*/
public class TestingOutputImpl implements TestingOutput {
public TestingOutputImpl(String output, LineType lineType) {
this.output = output;
this.lineType = lineType;
}
private String output;
private LineType lineType;
@Override
public String getOutput() {
return output;
}
@Override
public LineType getState() {
return lineType;
}
}