blob: 64be77e8cfcf573c8a9500f2198da0e4efd73c48 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.junit.model;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.util.Assert;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.junit.ITestRunListener;
import org.eclipse.jdt.internal.junit.Messages;
import org.eclipse.jdt.internal.junit.launcher.JUnitBaseLaunchConfiguration;
import org.eclipse.jdt.internal.junit.model.TestElement.Status;
import org.eclipse.jdt.internal.junit.ui.JUnitMessages;
import org.eclipse.jdt.internal.junit.ui.JUnitPlugin;
/**
*
*/
public class TestRunSession {
private final IType fLaunchedType;
private final ILaunch fLaunch;
private final String fLaunchConfigName;
private final RemoteTestRunnerClient fTestRunnerClient;
private final ListenerList/*<ITestSessionListener>*/ fSessionListeners;
/**
* The model root.
*/
private final TestRoot fTestRoot;
/**
* Map from testId to testElement.
*/
private final HashMap/*<String, TestElement>*/ fIdToTest;
/**
* The TestSuites for which additional children are expected.
*/
private List/*<IncompleteTestSuite>*/ fIncompleteTestSuites;
/**
* Number of tests started during this test run.
*/
volatile int fStartedCount;
/**
* Number of tests ignored during this test run.
*/
volatile int fIgnoredCount;
/**
* Number of errors during this test run.
*/
volatile int fErrorCount;
/**
* Number of failures during this test run.
*/
volatile int fFailureCount;
/**
* Total number of tests to run.
*/
volatile int fTotalCount;
/**
* Start time in millis.
*/
volatile long fStartTime;
volatile boolean fIsRunning;
volatile boolean fIsStopped;
public TestRunSession(IType launchedType, int port, ILaunch launch) {
Assert.isNotNull(launchedType);
Assert.isNotNull(launch);
fLaunchedType= launchedType;
fLaunch= launch;
ILaunchConfiguration launchConfiguration= launch.getLaunchConfiguration();
if (launchConfiguration != null)
fLaunchConfigName= launchConfiguration.getName();
else
fLaunchConfigName= launchedType.getElementName();
fTestRoot= new TestRoot();
fIdToTest= new HashMap();
fTestRunnerClient= new RemoteTestRunnerClient();
fTestRunnerClient.startListening(new ITestRunListener[] { new TestSessionNotifier() }, port);
fSessionListeners= new ListenerList();
}
public TestRoot getTestRoot() {
return fTestRoot;
}
public IType getLaunchedType() {
return fLaunchedType;
}
public ILaunch getLaunch() {
return fLaunch;
}
public String getTestRunName() {
return fLaunchConfigName;
}
public int getErrorCount() {
return fErrorCount;
}
public int getFailureCount() {
return fFailureCount;
}
public int getStartedCount() {
return fStartedCount;
}
public int getIgnoredCount() {
return fIgnoredCount;
}
public int getTotalCount() {
return fTotalCount;
}
public long getStartTime() {
return fStartTime;
}
/**
* @return <code>true</code> iff the session has been stopped or terminated
*/
public boolean isStopped() {
return fIsStopped;
}
public void addTestSessionListener(ITestSessionListener listener) {
fSessionListeners.add(listener);
}
public void removeTestSessionListener(ITestSessionListener listener) {
fSessionListeners.remove(listener);
}
public void stopTestRun() {
if (isRunning() || ! isKeptAlive())
fIsStopped= true;
fTestRunnerClient.stopTest();
}
/**
* @return <code>true</code> iff the runtime VM of this test session is still alive
*/
public boolean isKeptAlive() {
return fTestRunnerClient.isRunning() && ILaunchManager.DEBUG_MODE.equals(getLaunch().getLaunchMode());
}
/**
* @return <code>true</code> iff this session has been started, but not ended nor stopped nor terminated
*/
public boolean isRunning() {
return fIsRunning;
}
/**
* @param testId
* @param className
* @param testName
* @param launchMode
* @return <code>false</code> iff the rerun could not be started
* @throws CoreException
*/
public boolean rerunTest(String testId, String className, String testName, String launchMode) throws CoreException {
if (isKeptAlive()) {
Status status= ((TestCaseElement) getTestElement(testId)).getStatus();
if (status == Status.ERROR) {
fErrorCount--;
} else if (status == Status.FAILURE) {
fFailureCount--;
}
fTestRunnerClient.rerunTest(testId, className, testName);
return true;
} else if (getLaunch() != null) {
// run the selected test using the previous launch configuration
ILaunchConfiguration launchConfiguration= getLaunch().getLaunchConfiguration();
if (launchConfiguration != null) {
String name= className;
if (testName != null)
name+= "."+testName; //$NON-NLS-1$
String configName= Messages.format(JUnitMessages.TestRunnerViewPart_configName, name);
ILaunchConfigurationWorkingCopy tmp= launchConfiguration.copy(configName);
// fix for bug: 64838 junit view run single test does not use correct class [JUnit]
tmp.setAttribute(IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, className);
// reset the container
tmp.setAttribute(JUnitBaseLaunchConfiguration.LAUNCH_CONTAINER_ATTR, ""); //$NON-NLS-1$
if (testName != null) {
tmp.setAttribute(JUnitBaseLaunchConfiguration.TESTNAME_ATTR, testName);
// String args= "-rerun "+testId;
// tmp.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROGRAM_ARGUMENTS, args);
}
tmp.launch(launchMode, null);
return true;
}
}
return false;
}
public TestElement getTestElement(String id) {
return (TestElement) fIdToTest.get(id);
}
private TestElement addTreeEntry(String treeEntry) {
// format: testId","testName","isSuite","testcount
int index0= treeEntry.indexOf(',');
String id= treeEntry.substring(0, index0);
StringBuffer testNameBuffer= new StringBuffer(100);
int index1= scanTestName(treeEntry, index0 + 1, testNameBuffer);
String testName= testNameBuffer.toString().trim();
int index2= treeEntry.indexOf(',', index1 + 1);
boolean isSuite= treeEntry.substring(index1 + 1, index2).equals("true"); //$NON-NLS-1$
int testCount= Integer.parseInt(treeEntry.substring(index2 + 1));
if (fIncompleteTestSuites.isEmpty()) {
return createTestElement(fTestRoot, id, testName, isSuite, testCount);
} else {
int suiteIndex= fIncompleteTestSuites.size() - 1;
IncompleteTestSuite openSuite= (IncompleteTestSuite) fIncompleteTestSuites.get(suiteIndex);
openSuite.fOutstandingChildren--;
if (openSuite.fOutstandingChildren <= 0)
fIncompleteTestSuites.remove(suiteIndex);
return createTestElement(openSuite.fTestSuiteElement, id, testName, isSuite, testCount);
}
}
private TestElement createTestElement(TestSuiteElement parent, String id, String testName, boolean isSuite, int testCount) {
TestElement testElement;
if (isSuite) {
TestSuiteElement testSuiteElement= new TestSuiteElement(parent, id, testName, testCount);
testElement= testSuiteElement;
if (testCount > 0)
fIncompleteTestSuites.add(new IncompleteTestSuite(testSuiteElement, testCount));
} else {
testElement= new TestCaseElement(parent, id, testName);
}
fIdToTest.put(id, testElement);
return testElement;
}
/**
* Append the test name from <code>s</code> to <code>testName</code>.
*
* @param s the string to scan
* @param start the offset of the first character in <code>s</code>
* @param testName the result
*
* @return the index of the next ','
*/
private int scanTestName(String s, int start, StringBuffer testName) {
boolean inQuote= false;
int i= start;
for (; i < s.length(); i++) {
char c= s.charAt(i);
if (c == '\\' && !inQuote) {
inQuote= true;
continue;
} else if (inQuote) {
inQuote= false;
testName.append(c);
} else if (c == ',')
break;
else
testName.append(c);
}
return i;
}
/**
* An {@link ITestRunListener2} that listens to events from the
* {@link RemoteTestRunnerClient} and translates them into high-level model
* events (broadcasted to {@link ITestSessionListener}s).
*/
private class TestSessionNotifier implements ITestRunListener, ITestRunListener2 {
public void testRunStarted(int testCount) {
fIncompleteTestSuites= new ArrayList();
fStartedCount= 0;
fIgnoredCount= 0;
fFailureCount= 0;
fErrorCount= 0;
fTotalCount= testCount;
fStartTime= System.currentTimeMillis();
fIsRunning= true;
Object[] listeners= fSessionListeners.getListeners();
for (int i= 0; i < listeners.length; ++i) {
((ITestSessionListener) listeners[i]).sessionStarted();
}
}
public void testRunEnded(long elapsedTime) {
fIsRunning= false;
Object[] listeners= fSessionListeners.getListeners();
for (int i= 0; i < listeners.length; ++i) {
((ITestSessionListener) listeners[i]).sessionEnded(elapsedTime);
}
}
public void testRunStopped(long elapsedTime) {
fIsRunning= false;
fIsStopped= true;
Object[] listeners= fSessionListeners.getListeners();
for (int i= 0; i < listeners.length; ++i) {
((ITestSessionListener) listeners[i]).sessionStopped(elapsedTime);
}
}
public void testRunTerminated() {
fIsRunning= false;
fIsStopped= true;
Object[] listeners= fSessionListeners.getListeners();
for (int i= 0; i < listeners.length; ++i) {
((ITestSessionListener) listeners[i]).sessionTerminated();
}
}
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.junit.model.ITestRunListener2#testTreeEntry(java.lang.String)
*/
public void testTreeEntry(String description) {
TestElement testElement= addTreeEntry(description);
Object[] listeners= fSessionListeners.getListeners();
for (int i= 0; i < listeners.length; ++i) {
((ITestSessionListener) listeners[i]).testAdded(testElement);
}
}
public void testStarted(String testId, String testName) {
TestElement testElement= getTestElement(testId);
if (! (testElement instanceof TestCaseElement)) {
logUnexpectedTest(testId, testElement);
return;
}
TestCaseElement testCaseElement= (TestCaseElement) testElement;
setStatus(testCaseElement, Status.RUNNING);
fStartedCount++;
Object[] listeners= fSessionListeners.getListeners();
for (int i= 0; i < listeners.length; ++i) {
((ITestSessionListener) listeners[i]).testStarted(testCaseElement);
}
}
public void testEnded(String testId, String testName) {
TestElement testElement= getTestElement(testId);
if (! (testElement instanceof TestCaseElement)) {
logUnexpectedTest(testId, testElement);
return;
}
TestCaseElement testCaseElement= (TestCaseElement) testElement;
if (testName.startsWith("@Ignore: ")) { //$NON-NLS-1$
testCaseElement.setIgnored(true);
fIgnoredCount++;
}
if (testCaseElement.getStatus() == Status.RUNNING)
setStatus(testCaseElement, Status.OK);
Object[] listeners= fSessionListeners.getListeners();
for (int i= 0; i < listeners.length; ++i) {
((ITestSessionListener) listeners[i]).testEnded(testCaseElement);
}
}
public void testFailed(int status, String testId, String testName, String trace) {
testFailed(status, testId, testName, trace, null, null);
}
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.junit.model.ITestRunListener2#testFailed(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String)
*/
public void testFailed(int statusCode, String testId, String testName, String trace, String expected, String actual) {
TestElement testElement= getTestElement(testId);
if (testElement == null) {
logUnexpectedTest(testId, testElement);
return;
}
Status status= Status.convert(statusCode);
setStatus(testElement, status, trace, nullifyEmpty(expected), nullifyEmpty(actual));
if (statusCode == ITestRunListener.STATUS_ERROR) {
fErrorCount++;
} else {
fFailureCount++;
}
Object[] listeners= fSessionListeners.getListeners();
for (int i= 0; i < listeners.length; ++i) {
((ITestSessionListener) listeners[i]).testFailed(testElement, status, trace, expected, actual);
}
}
private String nullifyEmpty(String string) {
int length= string.length();
if (length == 0)
return null;
else if (string.charAt(length - 1) == '\n')
return string.substring(0, length - 1);
else
return string;
}
public void testReran(String testId, String testClass, String testName, int status, String trace) {
testReran(testId, testClass, testName, status, trace, "", ""); //$NON-NLS-1$ //$NON-NLS-2$
}
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.junit.model.ITestRunListener2#testReran(java.lang.String, java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String)
*/
public void testReran(String testId, String className, String testName, int statusCode, String trace, String expectedResult, String actualResult) {
TestElement testElement= getTestElement(testId);
if (! (testElement instanceof TestCaseElement)) {
logUnexpectedTest(testId, testElement); //TODO: rerun suites?
return;
}
TestCaseElement testCaseElement= (TestCaseElement) testElement;
Status status= Status.convert(statusCode);
if (status == Status.ERROR)
fErrorCount++;
else if (status == Status.FAILURE)
fFailureCount++;
testCaseElement.setStatus(status, trace, nullifyEmpty(expectedResult), nullifyEmpty(actualResult));
Object[] listeners= fSessionListeners.getListeners();
for (int i= 0; i < listeners.length; ++i) {
//TODO: post old & new status?
((ITestSessionListener) listeners[i]).testReran(testCaseElement, status, trace, expectedResult, actualResult);
}
}
private void setStatus(TestElement testElement, Status status) {
testElement.setStatus(status);
}
private void setStatus(TestElement testElement, Status status, String trace, String expected, String actual) {
testElement.setStatus(status, trace, expected, actual);
}
private void logUnexpectedTest(String testId, TestElement testElement) {
JUnitPlugin.log(new Exception("Unexpected TestElement type for testId '" + testId + "': " + testElement)); //$NON-NLS-1$ //$NON-NLS-2$
}
}
private static class IncompleteTestSuite {
public TestSuiteElement fTestSuiteElement;
public int fOutstandingChildren;
public IncompleteTestSuite(TestSuiteElement testSuiteElement, int outstandingChildren) {
fTestSuiteElement= testSuiteElement;
fOutstandingChildren= outstandingChildren;
}
}
public TestElement[] getAllFailedTestElements() {
ArrayList failures= new ArrayList();
addFailures(failures, getTestRoot());
return (TestElement[]) failures.toArray(new TestElement[failures.size()]);
}
private void addFailures(ArrayList failures, TestElement testElement) {
if (testElement.getStatus().isErrorOrFailure()) {
failures.add(testElement);
}
if (testElement instanceof TestSuiteElement) {
TestSuiteElement testSuiteElement= (TestSuiteElement) testElement;
TestElement[] children= testSuiteElement.getChildren();
for (int i= 0; i < children.length; i++) {
addFailures(failures, children[i]);
}
}
}
}