Added session test infrastructure and startup performance tests
diff --git a/tests/org.eclipse.core.tests.harness/plugin.xml b/tests/org.eclipse.core.tests.harness/plugin.xml
index 8d91898..0f2ef04 100644
--- a/tests/org.eclipse.core.tests.harness/plugin.xml
+++ b/tests/org.eclipse.core.tests.harness/plugin.xml
@@ -10,6 +10,8 @@
     <import plugin="org.eclipse.core.resources"/>
     <import plugin="org.eclipse.core.runtime.compatibility"/> 
     <import plugin="org.junit"/>
+    <import plugin="org.eclipse.pde.junit.runtime" optional="true"/>
+    <import plugin="org.eclipse.test.performance" optional="true"/>
   </requires>
 
   <runtime>
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/harness/EclipseWorkspaceTest.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/harness/EclipseWorkspaceTest.java
index dcadbab..a5cef05 100644
--- a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/harness/EclipseWorkspaceTest.java
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/harness/EclipseWorkspaceTest.java
@@ -11,7 +11,8 @@
 package org.eclipse.core.tests.harness;
 
 import java.io.*;
-import junit.framework.*;
+import junit.framework.Test;
+import junit.framework.TestCase;
 import org.eclipse.core.internal.utils.UniversalUniqueIdentifier;
 import org.eclipse.core.resources.*;
 import org.eclipse.core.runtime.*;
@@ -20,6 +21,8 @@
  * Tests that use the Eclipse Platform workspace.
  */
 public class EclipseWorkspaceTest extends TestCase {
+	public static final String PI_HARNESS = "org.eclipse.core.tests.harness"; //$NON-NLS-1$	
+	
 	//constants for nature sets	
 	protected static final String SET_STATE = "org.eclipse.core.tests.resources.stateSet";
 	protected static final String SET_OTHER = "org.eclipse.core.tests.resources.otherSet";
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/PerformanceSessionTestSuite.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/PerformanceSessionTestSuite.java
new file mode 100644
index 0000000..27f9f47
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/PerformanceSessionTestSuite.java
@@ -0,0 +1,137 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session;
+
+import junit.framework.*;
+
+/**
+ * Runs perfomance test cases multiple times (if they don't fail), 
+ * enabling assertions for the first run.
+ */
+public class PerformanceSessionTestSuite extends SessionTestSuite {
+	public static final String PROP_PERFORMANCE = "perf_ctrl";
+
+	/**
+	 * This custom test result allows multiple test runs to show up as a 
+	 * single run. 
+	 */
+	class ConsolidatedTestResult extends TestResult {
+		private boolean failed;
+		private int runs = 0;
+		private boolean started = false;
+		private TestResult target;
+
+		public ConsolidatedTestResult(TestResult target) {
+			this.target = target;
+		}
+
+		public void addError(Test test, Throwable t) {
+			failed = true;
+			target.addError(test, t);
+		}
+
+		public void addFailure(Test test, AssertionFailedError t) {
+			failed = true;
+			target.addFailure(test, t);
+		}
+
+		public void endTest(Test test) {
+			runs++;
+			if (!failed && runs < timesToRun)
+				return;
+			target.endTest(test);
+		}
+
+		public boolean shouldStop() {
+			if (failed)
+				return true;
+			return target.shouldStop();
+		}
+
+		public void startTest(Test test) {
+			// should not try to start again ater failing once
+			if (failed)
+				throw new IllegalStateException();
+			if (started)
+				return;
+			started = true;
+			target.startTest(test);
+		}
+	}
+
+	private int timesToRun;
+
+	public PerformanceSessionTestSuite(String pluginId, int timesToRun) {
+		super(pluginId);
+		this.timesToRun = timesToRun;
+	}
+
+	public PerformanceSessionTestSuite(String pluginId, int timesToRun, Class theClass) {
+		super(pluginId, theClass);
+		this.timesToRun = timesToRun;
+	}
+
+	public PerformanceSessionTestSuite(String pluginId, int timesToRun, Class theClass, String name) {
+		super(pluginId, theClass, name);
+		this.timesToRun = timesToRun;
+	}
+
+	public PerformanceSessionTestSuite(String pluginId, int timesToRun, String name) {
+		super(pluginId, name);
+		this.timesToRun = timesToRun;
+	}
+
+	/* 
+	 * This method will not be needed when the performance testing API
+	 * change to use multiple properties instead of perf_ctrl.
+	 */ 
+	public static String[] parsePerfCtrl() {
+		String[] result = new String[2];
+		String perfCtrl = System.getProperty(PROP_PERFORMANCE);
+		if (perfCtrl == null || perfCtrl.trim().length() == 0)
+			return result;
+		int assertBeginning = perfCtrl.indexOf("assertAgainst=");
+		if (assertBeginning == -1) {
+			result[0] = perfCtrl;
+			return result;
+		}
+		int assertEnding = perfCtrl.indexOf(';', assertBeginning);
+		if (assertEnding == -1)
+			assertEnding = perfCtrl.length();
+		String assertAgainst = perfCtrl.substring(assertBeginning, assertEnding);
+		StringBuffer newPerfCtrl = new StringBuffer();
+		newPerfCtrl.append(perfCtrl.substring(0, assertBeginning));
+		if (assertEnding + 1 < perfCtrl.length())
+			newPerfCtrl.append(perfCtrl.substring(assertEnding + 1));
+		result[0] = newPerfCtrl.toString();
+		result[1] = assertAgainst;
+		return result;
+	}
+
+	protected void runTestCase(TestCase test, TestResult result) {
+		Setup baseSetup = (Setup) getTestRunner().getBaseSetup().clone();
+		// first component contains the property except assertAgainst=*
+		// second component contains only assertAgainst=*
+		String[] perfCtrl = parsePerfCtrl();
+		if (perfCtrl[0] != null)		
+			baseSetup.getSystemProperties().put(PROP_PERFORMANCE, perfCtrl[0]);
+		// run test cases n-1 times
+		ConsolidatedTestResult consolidated = new ConsolidatedTestResult(result);
+		for (int i = 0; !consolidated.shouldStop() && i < timesToRun - 1; i++)
+			getTestRunner().run(test, consolidated, baseSetup);
+		if (consolidated.shouldStop())
+			return;
+		// for the n-th run, enable assertions
+		if (perfCtrl[1] != null)
+			baseSetup.getSystemProperties().put(PROP_PERFORMANCE, perfCtrl[0] + ';' + perfCtrl[1]);
+		getTestRunner().run(test, consolidated, baseSetup);
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/ProcessController.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/ProcessController.java
new file mode 100644
index 0000000..10480b3
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/ProcessController.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session;
+
+import java.io.*;
+import org.eclipse.core.internal.runtime.Policy;
+
+/**
+ * Executes an external process synchronously, allowing the client to define 
+ * a maximum amount of time for the process to complete.
+ */
+public class ProcessController {
+	/**
+	 * Thrown when a process being executed exceeds the maximum amount
+	 * of time allowed for it to complete.
+	 */
+	public class TimeOutException extends Exception {
+		public TimeOutException() {
+			super();
+		}
+
+		public TimeOutException(String message) {
+			super(message);
+		}
+	}
+
+	private boolean finished;
+	private OutputStream forwardStdErr;
+	private OutputStream forwardStdOut;
+	private boolean killed;
+	private String params;
+	private Process process;
+	private long startupTime;
+	private long timeLimit;
+
+	/**
+	 * Constructs an instance of ProcessController. This does not creates an
+	 * OS process. <code>run()</code> does that.
+	 * 
+	 * @param timeout the maximum time the process should take to run 
+	 * @param params the parameters to be passed to the controlled process
+	 */
+	public ProcessController(long timeout, String params) {
+		this.timeLimit = timeout;
+		this.params = params;
+	}
+
+	private void controlProcess() {
+		new Thread() {
+			public void run() {
+				while (!isFinished() && !timedOut())
+					synchronized (this) {
+						try {
+							wait(100);
+						} catch (InterruptedException e) {
+							break;
+						}
+					}
+				kill();
+			}
+		}.start();
+	}
+
+	/**
+	 * Causes the process to start executing. This call will block until the 
+	 * process has completed. If <code>timeout</code> is specified, the 
+	 * process will be interrupted if it takes more than the specified amount 
+	 * of time to complete, causing a <code>TimedOutException</code> to be thrown. 
+	 * Specifying zero as <code>timeout</code> means 
+	 * the process is not time constrained.  
+	 * 
+	 * @return the process exit value
+	 * @throws InterruptedException
+	 * @throws IOException
+	 * @throws TimeOutException if the process did not complete in time
+	 */
+	public int execute() throws InterruptedException, IOException, TimeOutException {
+		this.startupTime = System.currentTimeMillis();
+		// starts the process
+		process = Runtime.getRuntime().exec(params);
+		if (forwardStdErr != null)
+			forwardStream(process.getErrorStream(), forwardStdErr);
+		if (forwardStdOut != null)
+			forwardStream(process.getInputStream(), forwardStdOut);
+		if (timeLimit > 0)
+			// ensures process execution time does not exceed the time limit 
+			controlProcess();
+		try {
+			return process.waitFor();
+		} finally {
+			markFinished();
+			if (wasKilled())
+				throw new TimeOutException();
+		}
+	}
+
+	/**
+	 * Forwards the process standard error output to the given output stream.
+	 * Must be called before execution has started.
+	 * 
+	 * @param err an output stream where to forward the process 
+	 * standard error output to
+	 */
+	public void forwardErrorOutput(OutputStream err) {
+		this.forwardStdErr = err;
+	}
+
+	/**
+	 * Forwards the process standard output to the given output stream.
+	 * Must be called before execution has started.
+	 * 
+	 * @param err an output stream where to forward the process 
+	 * standard output to
+	 */
+	public void forwardOutput(OutputStream out) {
+		this.forwardStdOut = out;
+	}
+
+	private void forwardStream(final InputStream in, final OutputStream out) {
+		new Thread() {
+			public void run() {
+				try {
+					while (!isFinished()) {
+						while (in.available() > 0)
+							out.write(in.read());
+						synchronized (this) {
+							this.wait(100);
+						}
+					}
+					out.flush();
+				} catch (IOException ioe) {
+					//TODO only log/show if debug is on
+					ioe.printStackTrace();
+				} catch (InterruptedException e) {
+					//TODO only log/show if debug is on
+					e.printStackTrace();
+				}
+			}
+		}.start();
+	}
+
+	/**
+	 * Returns the controled process. Will return <code>null</code> before 
+	 * <code>execute</code> is called. 
+	 * 
+	 * @return the underlying process
+	 */
+	public Process getProcess() {
+		return process;
+	}
+
+	private synchronized boolean isFinished() {
+		return finished;
+	}
+
+	/**
+	 * Kills the process. Does nothing if it has been finished already.
+	 */
+	public void kill() {
+		synchronized (this) {
+			if (isFinished())
+				return;
+			killed = true;
+		}
+		Policy.debug("Having to kill the process");
+		process.destroy();
+	}
+
+	private synchronized void markFinished() {
+		finished = true;
+		notifyAll();
+	}
+
+	private synchronized boolean timedOut() {
+		return System.currentTimeMillis() - startupTime > timeLimit;
+	}
+
+	/**
+	 * Returns whether the process was killed due to a time out.
+	 * 
+	 * @return <code>true</code> if the process was killed, 
+	 * <code>false</code> if the completed normally
+	 */
+	public boolean wasKilled() {
+		return killed;
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/RemoteAssertionFailedError.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/RemoteAssertionFailedError.java
new file mode 100644
index 0000000..c3bd877
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/RemoteAssertionFailedError.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import junit.framework.AssertionFailedError;
+
+public class RemoteAssertionFailedError extends AssertionFailedError {
+
+	private Object stackText;
+	private String message;
+
+	public RemoteAssertionFailedError(String message, String stackText) {
+		this.message = message;
+		this.stackText = stackText;
+	}
+
+	public void printStackTrace(PrintWriter stream) {
+		stream.print(stackText);
+	}
+
+	public void printStackTrace(PrintStream stream) {
+		stream.print(stackText);
+	}
+
+	public String getMessage() {
+		return message;
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/RemoteTestException.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/RemoteTestException.java
new file mode 100644
index 0000000..aedd89b
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/RemoteTestException.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+public class RemoteTestException extends Exception {
+
+	private String stackText;
+	private String message;
+
+	public RemoteTestException(String message, String stackText) {
+		this.message = message;
+		this.stackText = stackText;
+	}
+
+	public void printStackTrace(PrintWriter stream) {
+		stream.print(stackText);
+	}
+
+	public void printStackTrace(PrintStream stream) {
+		stream.print(stackText);
+	}
+
+	public String getMessage() {
+		return message;
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/SessionTestRunner.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/SessionTestRunner.java
new file mode 100644
index 0000000..9e49c78
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/SessionTestRunner.java
@@ -0,0 +1,279 @@
+/******************************************************************************* 
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session;
+
+import java.io.*;
+import java.net.*;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import org.eclipse.core.internal.runtime.InternalPlatform;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.tests.harness.EclipseWorkspaceTest;
+
+public class SessionTestRunner {
+
+	class Result {
+		final static int ERROR = 2;
+		final static int FAILURE = 1;
+		final static int SUCCESS = 0;
+		String message;
+		String stackTrace;
+		int type;
+	}
+
+	/**
+	 * Collectors can be used a single time only.
+	 */
+	class ResultCollector implements Runnable {
+		private boolean finished;
+		private Result newResult = new Result();
+		private Result result;
+		ServerSocket serverSocket;
+		private boolean shouldRun = true;
+		private StringBuffer stack;
+
+		ResultCollector() throws IOException {
+			serverSocket = new ServerSocket(0);
+		}
+
+		public int getPort() {
+
+			return serverSocket.getLocalPort();
+		}
+
+		public Result getResult() {
+			return result;
+		}
+
+		public synchronized boolean isFinished() {
+			return finished;
+		}
+
+		private synchronized void markAsFinished() {
+			finished = true;
+			notifyAll();
+		}
+
+		private void processAvailableMessages(BufferedReader messageReader) throws IOException {
+			while (messageReader.ready()) {
+				String message = messageReader.readLine();
+				processMessage(message);
+			}
+		}
+
+		private void processMessage(String message) {
+			if (message.startsWith("%ERROR")) {
+				newResult.type = Result.ERROR;
+				newResult.message = "";
+				return;
+			}
+			if (message.startsWith("%FAILED")) {
+				newResult.type = Result.FAILURE;
+				newResult.message = "";
+				return;
+			}
+			if (message.startsWith("%TRACES")) {
+				stack = new StringBuffer();
+				return;
+			}
+			if (message.startsWith("%TRACEE")) {
+				newResult.stackTrace = stack.toString();
+				stack = null;
+				return;
+			}
+			if (stack != null) {
+				stack.append(message);
+				stack.append(System.getProperty("line.separator"));
+				return;
+			}
+			if (message.startsWith("%RUNTIME")) {
+				result = newResult;
+				return;
+			}
+		}
+
+		public void run() {
+			if (!shouldRun())
+				return;
+			Socket connection = null;
+			try {
+				try {
+					connection = serverSocket.accept();
+				} catch (SocketException se) {
+					if (!shouldRun())
+						// we have been finished without ever getting any connections
+						return;
+					throw se;
+				}
+				BufferedReader messageReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
+				try {
+					// main loop
+					while (true) {
+						synchronized (this) {
+							processAvailableMessages(messageReader);
+							if (!shouldRun())
+								return;
+							this.wait(500);
+						}
+					}
+				} catch (InterruptedException e) {
+					// not expected
+				}
+			} catch (IOException e) {
+				log(e);
+			} finally {
+				// remember we are already finished
+				markAsFinished();
+				// cleanup
+				try {
+					if (connection != null && !connection.isClosed())
+						connection.close();
+				} catch (IOException e) {
+					log(e);
+				}
+				try {
+					serverSocket.close();
+				} catch (IOException e) {
+					log(e);
+				}
+			}
+		}
+
+		private synchronized boolean shouldRun() {
+			return shouldRun;
+		}
+
+		/*
+		 * Politely asks the collector thread to stop and wait until it is finished.
+		 */
+		public void shutdown() {
+			// ask the collector to stop
+			synchronized (this) {
+				if (isFinished())
+					return;
+				shouldRun = false;
+				try {
+					serverSocket.close();
+				} catch (IOException e) {
+					log(e);
+				}
+				notifyAll();
+			}
+			// wait until the collector is done
+			synchronized (this) {
+				while (!isFinished())
+					try {
+						wait(100);
+					} catch (InterruptedException e) {
+						// we don't care
+					}
+			}
+		}
+
+	}
+
+	private String applicationId;
+	private Setup baseSetup;
+	private String pluginId;
+
+	public static void log(IStatus status) {
+		InternalPlatform.getDefault().log(status);
+	}
+
+	public static void log(Throwable e) {
+		log(new Status(IStatus.ERROR, EclipseWorkspaceTest.PI_HARNESS, IStatus.ERROR, "Error", e)); //$NON-NLS-1$
+	}
+
+	public SessionTestRunner(String pluginId, String applicationId) {
+		this.pluginId = pluginId;
+		this.applicationId = applicationId;
+	}
+
+	private Setup createSetup(TestCase test, Setup sessionSetup, int port) {
+		Setup setup = (Setup) (sessionSetup != null ? sessionSetup.clone() : getBaseSetup().clone());
+		setup.setApplication(applicationId);
+		StringBuffer appArgs = new StringBuffer(setup.getApplicationArgs());
+		appArgs.append(" -testpluginname ");
+		appArgs.append(pluginId);
+		appArgs.append(" -test ");
+		appArgs.append(test.getClass().getName());
+		appArgs.append(':');
+		appArgs.append(test.getName());
+		appArgs.append(" -port ");
+		appArgs.append(port);
+		setup.setApplicationArgs(appArgs.toString());
+		return setup;
+	}
+
+	public Setup getBaseSetup() {
+		if (baseSetup == null)
+			return SetupManager.getInstance().getDefaultSetup();
+		return baseSetup;
+	}
+
+	/**
+	 * Runs the setup. Returns a status object indicating the outcome of the operation.  
+	 * @param timeout
+	 * @return a status object indicating the outcome 
+	 */
+	private IStatus launch(String command, long timeout) {
+		IStatus outcome = Status.OK_STATUS;
+		try {
+			ProcessController process = new ProcessController(timeout, command);
+			process.forwardErrorOutput(System.err);
+			process.forwardOutput(System.out);
+			int returnCode = process.execute();
+			if (returnCode != 0)
+				outcome = new Status(IStatus.WARNING, Platform.PI_RUNTIME, returnCode, "Process returned non-zero code: " + returnCode, null);
+		} catch (Exception e) {
+			outcome = new Status(IStatus.ERROR, Platform.PI_RUNTIME, -1, "Error running process", e);
+		}
+		return outcome;
+	}
+
+	public void run(TestCase test, TestResult result, Setup sessionSetup) {
+		result.startTest(test);
+		try {
+			ResultCollector collector = null;
+			try {
+				collector = new ResultCollector();
+			} catch (IOException e) {
+				result.addError(test, e);
+				return;
+			}
+			Setup setup = createSetup(test, sessionSetup, collector.getPort());
+			new Thread(collector).start();
+			result.startTest(test);
+			IStatus status = launch(setup.getCommandLine(), setup.getTimeout());
+			collector.shutdown();
+			// ensure the session ran without any errors
+			if (!status.isOK()) {
+				log(status);
+				result.addError(test, new CoreException(status));
+				return;
+			}
+			Result collected = collector.getResult();
+			if (collected == null)
+				// should never happen
+				result.addError(test, new Exception("Test did not run"));
+			else if (collected.type == Result.FAILURE)
+				result.addFailure(test, new RemoteAssertionFailedError(collected.message, collected.stackTrace));
+			else if (collected.type == Result.ERROR)
+				result.addError(test, new RemoteTestException(collected.message, collected.stackTrace));
+		} finally {
+			result.endTest(test);
+		}
+	}
+
+	public void setBaseSetup(Setup baseSetup) {
+		this.baseSetup = baseSetup;
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/SessionTestSuite.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/SessionTestSuite.java
new file mode 100644
index 0000000..3cc4f5c
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/SessionTestSuite.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session;
+
+import java.util.Enumeration;
+import junit.framework.*;
+
+public class SessionTestSuite extends TestSuite {
+	public static final String CORE_TEST_APPLICATION = "org.eclipse.pde.junit.runtime.coretestapplication"; //$NON-NLS-1$	
+	public static final String UI_TEST_APPLICATION = "org.eclipse.pde.junit.runtime.uitestapplication"; //$NON-NLS-1$	
+	protected String applicationId = CORE_TEST_APPLICATION;
+	protected String pluginId;
+	protected SessionTestRunner testRunner;
+
+	public SessionTestSuite(String pluginId) {
+		super();
+		this.pluginId = pluginId;
+	}
+
+	public SessionTestSuite(String pluginId, Class theClass) {
+		super(theClass);
+		this.pluginId = pluginId;
+	}
+
+	public SessionTestSuite(String pluginId, Class theClass, String name) {
+		super(theClass, name);
+		this.pluginId = pluginId;
+	}
+
+	public SessionTestSuite(String pluginId, String name) {
+		super(name);
+		this.pluginId = pluginId;
+	}
+
+	public String getApplicationId() {
+		return applicationId;
+	}
+
+	protected SessionTestRunner getTestRunner() {
+		if (testRunner == null)
+			testRunner = new SessionTestRunner(pluginId, applicationId);
+		return testRunner;
+	}
+
+	public void runTest(Test test, TestResult result) {
+		if (test instanceof TestCase)
+			runTestCase((TestCase) test, result);
+		else if (test instanceof TestSuite)
+			// find and run the test cases that make up the suite
+			runTestSuite((TestSuite) test, result);
+		else
+			// we don't support session tests for things that are not TestCases 
+			// or TestSuites (e.g. TestDecorators) 
+			test.run(result);
+	}
+
+	protected void runTestCase(TestCase test, TestResult result) {
+		getTestRunner().run(test, result, null);
+	}
+
+	/*
+	 * Traverses the test suite to find individual test cases to be run with the SessionTestRunner.
+	 */
+	protected void runTestSuite(TestSuite suite, TestResult result) {
+		for (Enumeration e = suite.tests(); e.hasMoreElements();) {
+			if (result.shouldStop())
+				break;
+			Test test = (Test) e.nextElement();
+			runTest(test, result);
+		}
+	}
+
+	public void setApplicationId(String applicationId) {
+		this.applicationId = applicationId;
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/Setup.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/Setup.java
new file mode 100644
index 0000000..8fbbbc9
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/Setup.java
@@ -0,0 +1,303 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+import org.eclipse.core.internal.runtime.InternalPlatform;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+public class Setup implements Cloneable {
+	private String allArgs;
+	private String application;
+	private String applicationArgs;
+	private String configuration;
+	private String debugOption;
+	private String devOption;
+	private String id;
+	private String installLocation;
+	private String instanceLocation;
+	private String name;
+	private int numberOfRuns;
+	private Properties systemProperties = new Properties();
+	private int timeout;
+	private String vmArgs;
+	// includes the program file name
+	private String vmLocation;
+
+	public static String getDefaultConfiguration() {
+		return System.getProperty("configuration", System.getProperty(InternalPlatform.PROP_CONFIG_AREA));
+	}
+
+	public static String getDefaultDebugOption() {
+		return System.getProperty("debug", System.getProperty(InternalPlatform.PROP_DEBUG));
+	}
+
+	public static String getDefaultDevOption() {
+		return System.getProperty("dev", System.getProperty(InternalPlatform.PROP_DEV));
+	}
+
+	public static String getDefaultInstallLocation() {
+		String userSet = System.getProperty("install");
+		if (userSet != null)
+			return userSet;
+		String currentInstall = System.getProperty(InternalPlatform.PROP_INSTALL_AREA);
+		if (currentInstall != null)
+			try {
+				return new URI(currentInstall).getPath();
+			} catch (URISyntaxException e) {
+				// nothing to be done
+			}
+		return null;
+	}
+
+	public static String getDefaultInstanceLocation() {
+		return new Path(System.getProperty("java.io.tmpdir")).append("workspace").toOSString();
+	}
+
+	public static Setup getDefaultSetup() {
+		Setup defaultSetup = new Setup();
+		defaultSetup.setVMLocation(Setup.getDefaultVMLocation());
+		defaultSetup.setConfiguration(Setup.getDefaultConfiguration());
+		defaultSetup.setDebugOption(Setup.getDefaultDebugOption());
+		defaultSetup.setDevOption(Setup.getDefaultDevOption());
+		defaultSetup.setInstallLocation(Setup.getDefaultInstallLocation());
+		defaultSetup.setInstanceLocation(Setup.getDefaultInstanceLocation());
+		return defaultSetup;
+	}
+
+	public static String getDefaultVMLocation() {
+		String javaVM = (String) System.getProperties().get("eclipse.vm");
+		if (javaVM != null)
+			return javaVM;
+		javaVM = (String) System.getProperties().get("java.home");
+		if (javaVM == null)
+			return null;
+		//TODO: this is a hack and will not work with some VMs...
+		return new Path(javaVM).append("bin").append("java").toOSString();
+	}
+
+	private void appendApplicationArgs(StringBuffer params) {
+		if (application != null) {
+			params.append(" -application ");
+			params.append(application);
+		}
+
+		if (configuration != null) {
+			params.append(" -configuration \"");
+			params.append(configuration);
+			params.append('"');
+		}
+
+		if (devOption != null) {
+			params.append(" -dev \"");
+			params.append(devOption);
+			params.append('"');
+		}
+
+		if (debugOption != null) {
+			params.append(" -debug \"");
+			params.append(debugOption);
+			params.append('"');
+		}
+
+		if (instanceLocation != null) {
+			params.append(" -data \"");
+			params.append(instanceLocation);
+			params.append('"');
+		}
+
+		// always enable -consolelog TODO should make this configurable 
+		//params.append(" -consolelog");
+
+		// application args
+		if (applicationArgs != null) {
+			params.append(' ');
+			params.append(applicationArgs);
+		}
+	}
+
+	private void appendClassPath(StringBuffer params) {
+		params.append(" -classpath ");
+		IPath classPath = new Path(installLocation).append("startup.jar");
+		params.append(classPath.toOSString());
+	}
+
+	private void appendSystemProperties(StringBuffer command) {
+		for (Iterator iter = systemProperties.entrySet().iterator(); iter.hasNext();) {
+			Map.Entry entry = (Map.Entry) iter.next();
+			command.append(" -D");
+			command.append(entry.getKey());
+			command.append('=');
+			command.append(entry.getValue());
+		}
+
+	}
+
+	private void appendVMArgs(StringBuffer params) {
+		// additional VM args
+		if (vmArgs != null) {
+			params.append(' ');
+			params.append(vmArgs);
+		}
+	}
+
+	/*
+	 *  (non-Javadoc)
+	 * @see java.lang.Object#clone()
+	 */
+	public Object clone() {
+		Setup clone = null;
+		try {
+			clone = (Setup) super.clone();
+			clone.systemProperties = (Properties) systemProperties.clone();
+		} catch (CloneNotSupportedException e) {
+			// just does not happen
+		}
+		return clone;
+	}
+
+	public void copyProperty(String propertyKey) {
+		systemProperties.put(propertyKey, System.getProperty(propertyKey));
+	}
+
+	public String getAllArgs() {
+		return allArgs == null ? "" : allArgs;
+	}
+
+	public String getApplication() {
+		return application;
+	}
+
+	public String getApplicationArgs() {
+		return applicationArgs == null ? "" : applicationArgs;
+	}
+
+	public String getCommandLine() {
+		StringBuffer command = new StringBuffer(vmLocation);
+		appendClassPath(command);
+		appendVMArgs(command);
+		appendSystemProperties(command);
+		command.append(' ');
+		command.append("org.eclipse.core.launcher.Main");
+		appendApplicationArgs(command);
+//		System.out.println("Command line: ");
+//		System.out.print('\t');
+//		System.out.println(command);
+		return command.toString();
+	}
+
+	public String getConfiguration() {
+		return configuration;
+	}
+
+	public String getDebugOption() {
+		return debugOption;
+	}
+
+	public String getDevOption() {
+		return devOption;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public String getInstallLocation() {
+		return installLocation;
+	}
+
+	public String getInstanceLocation() {
+		return instanceLocation;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public int getNumberOfRuns() {
+		return numberOfRuns;
+	}
+
+	public Properties getSystemProperties() {
+		return systemProperties;
+	}
+
+	public int getTimeout() {
+		return timeout;
+	}
+
+	public String getVMArgs() {
+		return vmArgs == null ? "" : vmArgs;
+	}
+
+	public String getVMLocation() {
+		return vmLocation;
+	}
+
+	public void setAllArgs(String allArgs) {
+		this.allArgs = allArgs;
+	}
+
+	public void setApplication(String application) {
+		this.application = application;
+	}
+
+	public void setApplicationArgs(String applicationArgs) {
+		this.applicationArgs = applicationArgs;
+	}
+
+	public void setConfiguration(String configuration) {
+		this.configuration = configuration;
+	}
+
+	public void setDebugOption(String debugOption) {
+		this.debugOption = debugOption;
+	}
+
+	public void setDevOption(String devOption) {
+		this.devOption = devOption;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public void setInstallLocation(String newLocation) {
+		this.installLocation = newLocation;
+	}
+
+	public void setInstanceLocation(String instanceLocation) {
+		this.instanceLocation = instanceLocation;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public void setNumberOfRuns(int repeat) {
+		this.numberOfRuns = repeat;
+	}
+
+	public void setTimeout(int timeout) {
+		this.timeout = timeout;
+	}
+
+	public void setVMArgs(String vmArgs) {
+		this.vmArgs = vmArgs;
+	}
+
+	public void setVMLocation(String vmLocation) {
+		this.vmLocation = vmLocation;
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/SetupManager.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/SetupManager.java
new file mode 100644
index 0000000..ca8e402
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/SetupManager.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import javax.xml.parsers.*;
+import org.eclipse.core.runtime.Platform;
+import org.w3c.dom.*;
+import org.xml.sax.SAXException;
+
+public class SetupManager {
+
+	private static SetupManager instance;
+	private Map setups;
+
+	public synchronized static SetupManager getInstance() {
+		if (instance != null)
+			return instance;
+		instance = new SetupManager();
+		return instance;
+	}
+
+	protected SetupManager() {
+		setups = new HashMap();
+		loadSetups();
+	}
+
+	public Setup getSetup(String setupId) {
+		Setup setup = (Setup) setups.get(setupId);
+		return setup == null ? null : (Setup) setup.clone();
+	}
+
+	private void loadSetup(Node markup) {
+		NamedNodeMap attributes = markup.getAttributes();
+		if (attributes == null)
+			return;;
+		Setup newSetup = new Setup();
+		newSetup.setId(getAttribute(attributes, "id"));
+		newSetup.setName(getAttribute(attributes, "name"));
+		newSetup.setAllArgs(getAttribute(attributes, "allArgs"));
+		newSetup.setApplicationArgs(getAttribute(attributes, "applicationArgs"));
+		newSetup.setVMArgs(getAttribute(attributes, "vmArgs"));
+		newSetup.setVMLocation(getAttribute(attributes, "vmLocation"));
+		newSetup.setInstallLocation(getAttribute(attributes, "installLocation"));
+		newSetup.setInstanceLocation(getAttribute(attributes, "instanceLocation"));
+		String timeout = getAttribute(attributes, "timeout");
+		if (timeout != null)
+			newSetup.setTimeout(Integer.parseInt(timeout));
+		String runs = getAttribute(attributes, "runs");
+		if (runs != null)
+			newSetup.setNumberOfRuns(Integer.parseInt(runs));
+		setups.put(newSetup.getId(), newSetup);
+	}
+
+	private String getAttribute(NamedNodeMap attributes, String name) {
+		Node selected = attributes.getNamedItem(name);
+		return selected == null ? null : selected.getNodeValue();
+	}
+
+	private void loadSetups() {
+		try {
+			File setupFile = new File(System.getProperty("setup", "default-setup.xml"));
+			if (!setupFile.isFile())
+				if (Platform.isRunning() && Platform.inDevelopmentMode()) {
+					System.out.println("No setup descriptions found, only the default setup will be available");
+					return;
+				} else
+					throw new IllegalArgumentException("Setup file '" + setupFile.getAbsolutePath() + "' not found. Ensure you are specifying the path for an existing setup file (e.g. -Dsetup=<path-to-setup-file>)");
+			Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new File(System.getProperty("setup")));
+			NodeList setups = doc.getDocumentElement().getChildNodes();
+			for (int i = 0; i < setups.getLength(); i++)
+				loadSetup(setups.item(i));
+		} catch (SAXException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (ParserConfigurationException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		} catch (FactoryConfigurationError e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	public Setup getDefaultSetup() {
+		String defaultSetupId = System.getProperty("setup");
+		Setup defaultSetup = getSetup(defaultSetupId);
+		if (defaultSetup != null)
+			return defaultSetup;
+		return Setup.getDefaultSetup();
+	}
+
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/UIPerformanceSessionTestSuite.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/UIPerformanceSessionTestSuite.java
new file mode 100644
index 0000000..5abf1b0
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/UIPerformanceSessionTestSuite.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session;
+
+/**
+ * TODO It should live in the UI tests instead.
+ */
+public class UIPerformanceSessionTestSuite extends PerformanceSessionTestSuite {
+
+	public UIPerformanceSessionTestSuite(String pluginId, int timesToRun) {
+		super(pluginId, timesToRun);
+		setApplicationId(SessionTestSuite.UI_TEST_APPLICATION);
+	}
+
+	public UIPerformanceSessionTestSuite(String pluginId, int timesToRun, Class theClass) {
+		super(pluginId, timesToRun, theClass);
+		setApplicationId(SessionTestSuite.UI_TEST_APPLICATION);
+	}
+
+	public UIPerformanceSessionTestSuite(String pluginId, int timesToRun, Class theClass, String name) {
+		super(pluginId, timesToRun, theClass, name);
+		setApplicationId(SessionTestSuite.UI_TEST_APPLICATION);
+	}
+
+	public UIPerformanceSessionTestSuite(String pluginId, int timesToRun, String name) {
+		super(pluginId, timesToRun, name);
+		setApplicationId(SessionTestSuite.UI_TEST_APPLICATION);
+	}
+
+}
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/MultipleRunsTest.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/MultipleRunsTest.java
new file mode 100644
index 0000000..dd902ce
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/MultipleRunsTest.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session.samples;
+
+import junit.framework.*;
+import org.eclipse.core.tests.harness.EclipseWorkspaceTest;
+import org.eclipse.core.tests.session.*;
+import org.eclipse.test.performance.*;
+
+public class MultipleRunsTest extends TestCase {
+	public void testMultipleRuns() {
+		// the test case to run multiple times
+		TestCase test = new SampleSessionTest("testApplicationStartup");
+		SessionTestRunner runner = new SessionTestRunner(EclipseWorkspaceTest.PI_HARNESS, SessionTestSuite.CORE_TEST_APPLICATION);
+		// setup the command line to be passed to the multiple runs so it has the right system properties			
+		Setup baseSetup = SetupManager.getInstance().getDefaultSetup();
+		String[] perfCtrl = PerformanceSessionTestSuite.parsePerfCtrl();
+		if (perfCtrl[0] != null)
+			baseSetup.getSystemProperties().put(PerformanceSessionTestSuite.PROP_PERFORMANCE, perfCtrl[0]);
+		runner.setBaseSetup(baseSetup);
+		// runs the test case several times - only to collect data, won't do any assertions
+		TestResult result = new TestResult();
+		for (int i = 0; i < 5; i++) {
+			runner.run(test, result, null);
+			if (result.failureCount() > 0) {
+				((TestFailure) result.failures().nextElement()).thrownException().printStackTrace();
+				return;
+			}
+			if (result.errorCount() > 0) {
+				((TestFailure) result.errors().nextElement()).thrownException().printStackTrace();
+				return;
+			}
+		}
+		// create a performance meter whose scenario id matches the one used in the test case run
+		// our convention: scenario IDs are <test case class name> + '.' + <test case method name> 
+		PerformanceMeter meter = Performance.getDefault().createPerformanceMeter(test.getClass().getName() + '.' + test.getName());
+		// finally do the assertion
+		Performance.getDefault().assertPerformanceInRelativeBand(meter, Dimension.ELAPSED_PROCESS, -50, 5);
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/MultipleRunsTest2.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/MultipleRunsTest2.java
new file mode 100644
index 0000000..8070aa5
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/MultipleRunsTest2.java
@@ -0,0 +1,23 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session.samples;
+
+import junit.framework.*;
+import org.eclipse.core.tests.harness.EclipseWorkspaceTest;
+import org.eclipse.core.tests.session.PerformanceSessionTestSuite;
+
+public class MultipleRunsTest2 extends TestCase {
+	public static Test suite() {
+		PerformanceSessionTestSuite suite = new PerformanceSessionTestSuite(EclipseWorkspaceTest.PI_HARNESS, 1000);
+		suite.addTest(new TestSuite(SampleSessionTest.class));
+		return suite;
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/SampleSessionTest.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/SampleSessionTest.java
new file mode 100644
index 0000000..8f8d47b
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/SampleSessionTest.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session.samples;
+
+import junit.framework.*;
+import org.eclipse.core.tests.harness.EclipseWorkspaceTest;
+import org.eclipse.core.tests.session.SessionTestSuite;
+import org.eclipse.test.performance.*;
+
+public class SampleSessionTest extends TestCase {
+	public SampleSessionTest(String methodName) {
+		super(methodName);
+	}
+
+	public void testBasic1() {
+		// Everything is fine...
+	}
+
+	public void testBasic2() {
+		fail("Breaking the test " + System.currentTimeMillis());
+	}
+
+	public void testBasic3() {
+		throw new RuntimeException("Will break the test as well " + System.currentTimeMillis());
+	}
+
+	public void testApplicationStartup() {
+		PerformanceMeter meter = Performance.getDefault().createPerformanceMeter(getClass().getName() + ".testPerformance");
+		try {
+			meter.stop();
+			meter.commit();
+			Performance.getDefault().assertPerformanceInRelativeBand(meter, Dimension.ELAPSED_PROCESS, -50, 5);
+		} finally {
+			meter.dispose();
+		}
+	}
+
+	public static Test suite() {
+		TestSuite sameSession = new SessionTestSuite(EclipseWorkspaceTest.PI_HARNESS, SampleSessionTest.class);
+		return sameSession;
+	}
+
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/SampleTests.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/SampleTests.java
new file mode 100644
index 0000000..543b37f
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/SampleTests.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session.samples;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.eclipse.core.tests.harness.EclipseWorkspaceTest;
+import org.eclipse.core.tests.session.SessionTestSuite;
+
+public class SampleTests extends TestSuite {
+	public SampleTests() {
+		addTest(SampleSessionTest.suite());
+		addTest(UISampleSessionTest.suite());
+		TestSuite another = new SessionTestSuite(EclipseWorkspaceTest.PI_HARNESS);
+		another.addTestSuite(SampleSessionTest.class);
+		addTest(another);
+	}
+
+	public static Test suite() {
+		return new SampleTests();
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/UISampleSessionTest.java b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/UISampleSessionTest.java
new file mode 100644
index 0000000..ff541d8
--- /dev/null
+++ b/tests/org.eclipse.core.tests.harness/src/org/eclipse/core/tests/session/samples/UISampleSessionTest.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.session.samples;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import org.eclipse.core.internal.runtime.Policy;
+import org.eclipse.core.tests.harness.EclipseWorkspaceTest;
+import org.eclipse.core.tests.session.SessionTestSuite;
+import org.eclipse.test.performance.*;
+
+public class UISampleSessionTest extends TestCase {
+	public UISampleSessionTest(String methodName) {
+		super(methodName);
+	}
+
+	public void testApplicationStartup() {
+		Policy.debug("Running " + getName());
+		PerformanceMeter meter = Performance.getDefault().createPerformanceMeter(getClass().getName() + ".UIStartup");
+		try {
+			meter.stop();
+			meter.commit();
+			Performance.getDefault().assertPerformanceInRelativeBand(meter, Dimension.ELAPSED_PROCESS, -50, 5);
+		} finally {
+			meter.dispose();
+		}
+	}
+
+	public static Test suite() {
+		SessionTestSuite suite = new SessionTestSuite(EclipseWorkspaceTest.PI_HARNESS);
+		suite.setApplicationId(SessionTestSuite.UI_TEST_APPLICATION);
+		for (int i = 0; i < 3; i++)
+			suite.addTest(new UISampleSessionTest("testApplicationStartup"));
+		return suite;
+	}
+
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.harness/test.xml b/tests/org.eclipse.core.tests.harness/test.xml
index df3e2a2..12200ca 100644
--- a/tests/org.eclipse.core.tests.harness/test.xml
+++ b/tests/org.eclipse.core.tests.harness/test.xml
@@ -57,6 +57,15 @@
       <property name="classname" value="org.eclipse.osgi.tests.AutomatedTests"/>
     </ant>
   </target>
+	
+  <target name="RuntimePerformanceTests" depends="init,cleanup">
+    <ant target="core-test" antfile="${library-file}" dir="${eclipse-home}">
+      <property name="data-dir" value="${runtime_location}"/>
+      <property name="plugin-name" value="org.eclipse.core.tests.runtime"/>
+      <property name="classname" value="org.eclipse.core.tests.runtime.perf.AllTests"/>
+    </ant>
+  </target>  
+	
   
   <target name="OSGiPerformanceTests" depends="init,cleanup">
     <ant target="core-test" antfile="${library-file}" dir="${eclipse-home}">
@@ -157,6 +166,6 @@
   <!--target name="suite" depends="ResourcesTests,RuntimeTests,OSGiTests,PR_1G1N9GZ_SessionTests,PR_1GALH44_SessionTests,Snapshot_SessionTests,SaveManager_SessionTests"/-->
   
   <!-- This target defines the performance tests that need to be run. -->
-  <target name="performance-suite" depends="OSGiPerformanceTests"/>  
+  <target name="performance-suite" depends="RuntimePerformanceTests,OSGiPerformanceTests"/>  
 	
  </project>
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.runtime/plugin.xml b/tests/org.eclipse.core.tests.runtime/plugin.xml
index 0d88c2c..be3d112 100644
--- a/tests/org.eclipse.core.tests.runtime/plugin.xml
+++ b/tests/org.eclipse.core.tests.runtime/plugin.xml
@@ -18,6 +18,7 @@
       <import plugin="org.eclipse.core.resources"/>
       <import plugin="org.eclipse.core.tests.harness"/>
       <import plugin="org.junit"/>
+      <import plugin="org.eclipse.test.performance" optional="true"/>
    </requires>
 
    <extension
diff --git a/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/perf/AllTests.java b/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/perf/AllTests.java
new file mode 100644
index 0000000..3b59cdd
--- /dev/null
+++ b/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/perf/AllTests.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.runtime.perf;
+
+import junit.framework.*;
+import org.eclipse.core.tests.runtime.RuntimeTestsPlugin;
+import org.eclipse.core.tests.session.PerformanceSessionTestSuite;
+import org.eclipse.core.tests.session.UIPerformanceSessionTestSuite;
+
+public class AllTests extends TestCase {
+	public static Test suite() {
+		TestSuite suite = new TestSuite(AllTests.class.getName());
+		suite.addTest(new PerformanceSessionTestSuite(RuntimeTestsPlugin.PI_RUNTIME_TESTS, 5, StartupTest.class));
+		suite.addTest(new UIPerformanceSessionTestSuite(RuntimeTestsPlugin.PI_RUNTIME_TESTS, 5, UIStartupTest.class));		
+		return suite;
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/perf/StartupTest.java b/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/perf/StartupTest.java
new file mode 100644
index 0000000..9785871
--- /dev/null
+++ b/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/perf/StartupTest.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.runtime.perf;
+
+import junit.framework.*;
+import org.eclipse.test.performance.*;
+
+public class StartupTest extends TestCase {
+
+	public static Test suite() {
+		return new TestSuite(StartupTest.class);
+	}
+	
+	public StartupTest(String methodName) {
+		super(methodName);
+	}
+
+	public void testApplicationStartup() {
+		PerformanceMeter meter = Performance.getDefault().createPerformanceMeter(getClass().getName() + '.' + getName());
+		try {
+			meter.stop();
+			meter.commit();
+			Performance.getDefault().assertPerformanceInRelativeBand(meter, Dimension.ELAPSED_PROCESS, -50, 5);
+		} finally {
+			meter.dispose();
+		}
+	}
+}
\ No newline at end of file
diff --git a/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/perf/UIStartupTest.java b/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/perf/UIStartupTest.java
new file mode 100644
index 0000000..a9b562d
--- /dev/null
+++ b/tests/org.eclipse.core.tests.runtime/src/org/eclipse/core/tests/runtime/perf/UIStartupTest.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.core.tests.runtime.perf;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class UIStartupTest extends StartupTest {
+
+	public static Test suite() {
+		return new TestSuite(UIStartupTest.class);
+	}
+
+	public UIStartupTest(String methodName) {
+		super(methodName);
+	}
+}
\ No newline at end of file