Revert "Bug 531057- 2nd Attempt JUnit 5 support for tests"

This reverts commit 21df6c58ad51eafc007fb99b53e71ba72d229839.

Change-Id: I5503479bc39482ee18e69da222dd98f4c54a2245
diff --git a/bundles/org.eclipse.test/META-INF/MANIFEST.MF b/bundles/org.eclipse.test/META-INF/MANIFEST.MF
index 5b9f607..d9d11e9 100644
--- a/bundles/org.eclipse.test/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.test/META-INF/MANIFEST.MF
@@ -7,16 +7,11 @@
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
 Require-Bundle: org.apache.ant,
+ org.junit;bundle-version="4.12.0",
  org.eclipse.ui,
  org.eclipse.core.runtime,
  org.eclipse.ui.ide.application,
- org.eclipse.equinox.app,
- org.junit.jupiter.api,
- org.junit.jupiter.engine,
- org.junit.platform.commons,
- org.junit.platform.engine,
- org.junit.platform.launcher,
- org.junit.vintage.engine
+ org.eclipse.equinox.app
 Bundle-ActivationPolicy: lazy
 Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 Export-Package: org.eclipse.test
diff --git a/bundles/org.eclipse.test/src/org/eclipse/test/AbstractJUnitResultFormatter.java b/bundles/org.eclipse.test/src/org/eclipse/test/AbstractJUnitResultFormatter.java
deleted file mode 100644
index 5d681aa..0000000
--- a/bundles/org.eclipse.test/src/org/eclipse/test/AbstractJUnitResultFormatter.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2018 Red Hat Inc. 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:
- *     Lucas Bullen (Red Hat Inc.) - initial API and implementation
- *******************************************************************************/
-package org.eclipse.test;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.Closeable;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.io.Writer;
-import java.nio.BufferOverflowException;
-import java.nio.ByteBuffer;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Objects;
-import java.util.Optional;
-
-import org.apache.tools.ant.Project;
-import org.apache.tools.ant.taskdefs.optional.junitlauncher.TestExecutionContext;
-import org.apache.tools.ant.taskdefs.optional.junitlauncher.TestResultFormatter;
-import org.apache.tools.ant.util.FileUtils;
-import org.junit.platform.engine.TestSource;
-import org.junit.platform.engine.support.descriptor.ClassSource;
-import org.junit.platform.launcher.TestIdentifier;
-import org.junit.platform.launcher.TestPlan;
-
-/**
- * Contains some common behaviour that's used by our internal {@link TestResultFormatter}s
- */
-abstract class AbstractJUnitResultFormatter implements TestResultFormatter {
-
-
-	protected static String NEW_LINE = System.getProperty("line.separator");
-	protected TestExecutionContext context;
-
-	private SysOutErrContentStore sysOutStore;
-	private SysOutErrContentStore sysErrStore;
-
-	@Override
-	public void sysOutAvailable(final byte[] data) {
-		if (this.sysOutStore == null) {
-			this.sysOutStore = new SysOutErrContentStore(true);
-		}
-		try {
-			this.sysOutStore.store(data);
-		} catch (IOException e) {
-			handleException(e);
-			return;
-		}
-	}
-
-	@Override
-	public void sysErrAvailable(final byte[] data) {
-		if (this.sysErrStore == null) {
-			this.sysErrStore = new SysOutErrContentStore(false);
-		}
-		try {
-			this.sysErrStore.store(data);
-		} catch (IOException e) {
-			handleException(e);
-			return;
-		}
-	}
-
-	@Override
-	public void setContext(final TestExecutionContext context) {
-		this.context = context;
-	}
-
-	/**
-	 * @return Returns true if there's any stdout data, that was generated during the
-	 * tests, is available for use. Else returns false.
-	 */
-	boolean hasSysOut() {
-		return this.sysOutStore != null && this.sysOutStore.hasData();
-	}
-
-	/**
-	 * @return Returns true if there's any stderr data, that was generated during the
-	 * tests, is available for use. Else returns false.
-	 */
-	boolean hasSysErr() {
-		return this.sysErrStore != null && this.sysErrStore.hasData();
-	}
-
-	/**
-	 * @return Returns a {@link Reader} for reading any stdout data that was generated
-	 * during the test execution. It is expected that the {@link #hasSysOut()} be first
-	 * called to see if any such data is available and only if there is, then this method
-	 * be called
-	 * @throws IOException If there's any I/O problem while creating the {@link Reader}
-	 */
-	Reader getSysOutReader() throws IOException {
-		return this.sysOutStore.getReader();
-	}
-
-	/**
-	 * @return Returns a {@link Reader} for reading any stderr data that was generated
-	 * during the test execution. It is expected that the {@link #hasSysErr()} be first
-	 * called to see if any such data is available and only if there is, then this method
-	 * be called
-	 * @throws IOException If there's any I/O problem while creating the {@link Reader}
-	 */
-	Reader getSysErrReader() throws IOException {
-		return this.sysErrStore.getReader();
-	}
-
-	/**
-	 * Writes out any stdout data that was generated during the
-	 * test execution. If there was no such data then this method just returns.
-	 *
-	 * @param writer The {@link Writer} to use. Cannot be null.
-	 * @throws IOException If any I/O problem occurs during writing the data
-	 */
-	void writeSysOut(final Writer writer) throws IOException {
-		Objects.requireNonNull(writer, "Writer cannot be null");
-		this.writeFrom(this.sysOutStore, writer);
-	}
-
-	/**
-	 * Writes out any stderr data that was generated during the
-	 * test execution. If there was no such data then this method just returns.
-	 *
-	 * @param writer The {@link Writer} to use. Cannot be null.
-	 * @throws IOException If any I/O problem occurs during writing the data
-	 */
-	void writeSysErr(final Writer writer) throws IOException {
-		Objects.requireNonNull(writer, "Writer cannot be null");
-		this.writeFrom(this.sysErrStore, writer);
-	}
-
-	static Optional<TestIdentifier> traverseAndFindTestClass(final TestPlan testPlan, final TestIdentifier testIdentifier) {
-		if (isTestClass(testIdentifier).isPresent()) {
-			return Optional.of(testIdentifier);
-		}
-		final Optional<TestIdentifier> parent = testPlan.getParent(testIdentifier);
-		return parent.isPresent() ? traverseAndFindTestClass(testPlan, parent.get()) : Optional.empty();
-	}
-
-	static Optional<ClassSource> isTestClass(final TestIdentifier testIdentifier) {
-		if (testIdentifier == null) {
-			return Optional.empty();
-		}
-		final Optional<TestSource> source = testIdentifier.getSource();
-		if (!source.isPresent()) {
-			return Optional.empty();
-		}
-		final TestSource testSource = source.get();
-		if (testSource instanceof ClassSource) {
-			return Optional.of((ClassSource) testSource);
-		}
-		return Optional.empty();
-	}
-
-	private void writeFrom(final SysOutErrContentStore store, final Writer writer) throws IOException {
-		final char[] chars = new char[1024];
-		int numRead = -1;
-		try (final Reader reader = store.getReader()) {
-			while ((numRead = reader.read(chars)) != -1) {
-				writer.write(chars, 0, numRead);
-			}
-		}
-	}
-
-	@Override
-	public void close() throws IOException {
-		FileUtils.close(this.sysOutStore);
-		FileUtils.close(this.sysErrStore);
-	}
-
-	protected void handleException(final Throwable t) {
-		// we currently just log it and move on.
-		this.context.getProject().ifPresent((p) -> p.log("Exception in listener "
-				+ AbstractJUnitResultFormatter.this.getClass().getName(), t, Project.MSG_DEBUG));
-	}
-
-
-	/*
-    A "store" for sysout/syserr content that gets sent to the AbstractJUnitResultFormatter.
-    This store first uses a relatively decent sized in-memory buffer for storing the sysout/syserr
-    content. This in-memory buffer will be used as long as it can fit in the new content that
-    keeps coming in. When the size limit is reached, this store switches to a file based store
-    by creating a temporarily file and writing out the already in-memory held buffer content
-    and any new content that keeps arriving to this store. Once the file has been created,
-    the in-memory buffer will never be used any more and in fact is destroyed as soon as the
-    file is created.
-    Instances of this class are not thread-safe and users of this class are expected to use necessary thread
-    safety guarantees, if they want to use an instance of this class by multiple threads.
-	 */
-	private static final class SysOutErrContentStore implements Closeable {
-		private static final int DEFAULT_CAPACITY_IN_BYTES = 50 * 1024; // 50 KB
-		private static final Reader EMPTY_READER = new Reader() {
-			@Override
-			public int read(final char[] cbuf, final int off, final int len) throws IOException {
-				return -1;
-			}
-
-			@Override
-			public void close() throws IOException {
-			}
-		};
-
-		private final String tmpFileSuffix;
-		private ByteBuffer inMemoryStore = ByteBuffer.allocate(DEFAULT_CAPACITY_IN_BYTES);
-		private boolean usingFileStore = false;
-		private Path filePath;
-		private FileOutputStream fileOutputStream;
-
-		SysOutErrContentStore(final boolean isSysOut) {
-			this.tmpFileSuffix = isSysOut ? ".sysout" : ".syserr";
-		}
-
-		void store(final byte[] data) throws IOException {
-			if (this.usingFileStore) {
-				this.storeToFile(data, 0, data.length);
-				return;
-			}
-			// we haven't yet created a file store and the data can fit in memory,
-			// so we write it in our buffer
-			try {
-				this.inMemoryStore.put(data);
-				return;
-			} catch (BufferOverflowException boe) {
-				// the buffer capacity can't hold this incoming data, so this
-				// incoming data hasn't been transferred to the buffer. let's
-				// now fall back to a file store
-				this.usingFileStore = true;
-			}
-			// since the content couldn't be transferred into in-memory buffer,
-			// we now create a file and transfer already (previously) stored in-memory
-			// content into that file, before finally transferring this new content
-			// into the file too. We then finally discard this in-memory buffer and
-			// just keep using the file store instead
-			this.fileOutputStream = createFileStore();
-			// first the existing in-memory content
-			storeToFile(this.inMemoryStore.array(), 0, this.inMemoryStore.position());
-			storeToFile(data, 0, data.length);
-			// discard the in-memory store
-			this.inMemoryStore = null;
-		}
-
-		private void storeToFile(final byte[] data, final int offset, final int length) throws IOException {
-			if (this.fileOutputStream == null) {
-				// no backing file was created so we can't do anything
-				return;
-			}
-			this.fileOutputStream.write(data, offset, length);
-		}
-
-		private FileOutputStream createFileStore() throws IOException {
-			this.filePath = Files.createTempFile(null, this.tmpFileSuffix);
-			this.filePath.toFile().deleteOnExit();
-			return new FileOutputStream(this.filePath.toFile());
-		}
-
-		/*
-		 * Returns a Reader for reading the sysout/syserr content. If there's no data
-		 * available in this store, then this returns a Reader which when used for read operations,
-		 * will immediately indicate an EOF.
-		 */
-		Reader getReader() throws IOException {
-			if (this.usingFileStore && this.filePath != null) {
-				// we use a FileReader here so that we can use the system default character
-				// encoding for reading the contents on sysout/syserr stream, since that's the
-				// encoding that System.out/System.err uses to write out the messages
-				return new BufferedReader(new FileReader(this.filePath.toFile()));
-			}
-			if (this.inMemoryStore != null) {
-				return new InputStreamReader(new ByteArrayInputStream(this.inMemoryStore.array(), 0, this.inMemoryStore.position()));
-			}
-			// no data to read, so we return an "empty" reader
-			return EMPTY_READER;
-		}
-
-		/*
-		 *  Returns true if this store has any data (either in-memory or in a file). Else
-		 *  returns false.
-		 */
-		boolean hasData() {
-			if (this.inMemoryStore != null && this.inMemoryStore.position() > 0) {
-				return true;
-			}
-			if (this.usingFileStore && this.filePath != null) {
-				return true;
-			}
-			return false;
-		}
-
-		@Override
-		public void close() throws IOException {
-			this.inMemoryStore = null;
-			FileUtils.close(this.fileOutputStream);
-			FileUtils.delete(this.filePath.toFile());
-		}
-	}
-}
diff --git a/bundles/org.eclipse.test/src/org/eclipse/test/ClassLoaderTools.java b/bundles/org.eclipse.test/src/org/eclipse/test/ClassLoaderTools.java
deleted file mode 100644
index 28fe538..0000000
--- a/bundles/org.eclipse.test/src/org/eclipse/test/ClassLoaderTools.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2018 Red Hat Inc. 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:
- *     Lucas Bullen (Red Hat Inc.) - initial API and implementation
- *******************************************************************************/
-package org.eclipse.test;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URI;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Vector;
-
-import org.eclipse.core.runtime.Platform;
-import org.eclipse.osgi.internal.framework.EquinoxBundle;
-import org.osgi.framework.Bundle;
-
-@SuppressWarnings("restriction")
-class ClassLoaderTools {
-	public static ClassLoader getPluginClassLoader(String getfTestPluginName, ClassLoader currentTCCL) {
-		Bundle bundle = Platform.getBundle(getfTestPluginName);
-		if (bundle == null) {
-			throw new IllegalArgumentException("Bundle \"" + getfTestPluginName + "\" not found. Possible causes include missing dependencies, too restrictive version ranges, or a non-matching required execution environment."); //$NON-NLS-1$ //$NON-NLS-2$
-		}
-		return new TestBundleClassLoader(bundle, currentTCCL);
-	}
-
-	public static String getClassPlugin(String className) {
-		int index = className.lastIndexOf('.');
-		String plugin = null;
-		while (index != -1) {
-			plugin = className.substring(0, index);
-			if(Platform.getBundle(plugin) != null) {
-				break;
-			}
-			index = className.lastIndexOf('.', index-1);
-		}
-		return plugin;
-	}
-
-	public static ClassLoader getJUnit5Classloader(List<String> platformEngine) {
-		List<Bundle> platformEngineBundles = new ArrayList<>();
-		for (Iterator<String> iterator = platformEngine.iterator(); iterator.hasNext();) {
-			String string = iterator.next();
-			Bundle bundle = Platform.getBundle(string);
-			platformEngineBundles.add(bundle);
-		}
-		return new MultiBundleClassLoader(platformEngineBundles);
-	}
-
-	static class TestBundleClassLoader extends ClassLoader {
-		protected Bundle bundle;
-		protected ClassLoader currentTCCL;
-
-		public TestBundleClassLoader(Bundle target, ClassLoader currentTCCL) {
-			this.bundle = target;
-			this.currentTCCL = currentTCCL;
-		}
-
-		@Override
-		protected Class<?> findClass(String name) throws ClassNotFoundException {
-			try {
-				return bundle.loadClass(name);
-			} catch (ClassNotFoundException e) {
-				return currentTCCL.loadClass(name);
-			}
-		}
-
-		@Override
-		protected URL findResource(String name) {
-			URL url = bundle.getResource(name);
-			if(url == null) {
-				url = currentTCCL.getResource(name);
-			}
-			return url;
-		}
-
-		@SuppressWarnings({"rawtypes", "unchecked"})
-		@Override
-		protected Enumeration findResources(String name) throws IOException {
-			Enumeration enumeration = bundle.getResources(name);
-			if(enumeration == null) {
-				enumeration = currentTCCL.getResources(name);
-			}
-			return enumeration;
-		}
-
-		@Override
-		public Enumeration<URL> getResources(String res) throws IOException {
-			Enumeration<URL> urls = currentTCCL.getResources(res);
-			if(urls.hasMoreElements())
-				return urls;
-
-			List<URL> resources = new ArrayList<>(6);
-			String location = null;
-			URL url = null;
-			if (bundle instanceof EquinoxBundle) {
-				location = ((EquinoxBundle) bundle).getLocation();
-			}
-			if (location != null && location.startsWith("reference:")) { //$NON-NLS-1$
-				location = location.substring(10, location.length());
-				URI uri = URI.create(location);
-				String newPath =( uri.getPath() == null ? "" : uri.getPath()) + "bin" + '/' + res; //$NON-NLS-1$
-				URI newUri = uri.resolve(newPath).normalize();
-				if(newUri.isAbsolute())
-					url = newUri.toURL();
-			}
-			if (url != null) {
-				File f = new File(url.getFile());
-				if (f.exists())
-					resources.add(url);
-			}
-			else
-				return Collections.emptyEnumeration();
-
-			return Collections.enumeration(resources);
-		}
-	}
-
-	static class MultiBundleClassLoader extends ClassLoader {
-		private List<Bundle> bundleList;
-
-		public MultiBundleClassLoader(List<Bundle> platformEngineBundles) {
-			this.bundleList = platformEngineBundles;
-
-		}
-		public Class<?> findClasss(String name) throws ClassNotFoundException {
-			return findClass(name);
-		}
-		@Override
-		protected Class<?> findClass(String name) throws ClassNotFoundException {
-			Class<?> c = null;
-			for (Bundle temp : bundleList) {
-				try {
-					c = temp.loadClass(name);
-					if (c != null)
-						return c;
-				} catch (ClassNotFoundException e) {
-				}
-			}
-			return c;
-		}
-
-		@Override
-		protected URL findResource(String name) {
-			URL url = null;
-			for (Bundle temp : bundleList) {
-				url = temp.getResource(name);
-				if (url != null)
-					return url;
-			}
-			return url;
-		}
-
-		@SuppressWarnings({"rawtypes", "unchecked"})
-		@Override
-		protected Enumeration findResources(String name) throws IOException {
-			Enumeration enumFinal = null;
-			for (int i = 0; i < bundleList.size(); i++) {
-				if (i == 0) {
-					enumFinal = bundleList.get(i).getResources(name);
-					continue;
-				}
-				Enumeration e2 = bundleList.get(i).getResources(name);
-				Vector temp = new Vector();
-				while (enumFinal != null && enumFinal.hasMoreElements()) {
-					temp.add(enumFinal.nextElement());
-				}
-				while (e2 != null && e2.hasMoreElements()) {
-					temp.add(e2.nextElement());
-				}
-				enumFinal = temp.elements();
-			}
-			return enumFinal;
-		}
-	}
-}
diff --git a/bundles/org.eclipse.test/src/org/eclipse/test/EclipseTestRunner.java b/bundles/org.eclipse.test/src/org/eclipse/test/EclipseTestRunner.java
index a653ebe..1a6b77d 100644
--- a/bundles/org.eclipse.test/src/org/eclipse/test/EclipseTestRunner.java
+++ b/bundles/org.eclipse.test/src/org/eclipse/test/EclipseTestRunner.java
@@ -8,12 +8,9 @@
  * Contributors:
  *     IBM Corporation - initial API and implementation
  *     Anthony Dahanne  <anthony.dahanne@compuware.com> - enhance ETF to be able to launch several tests in several bundles - https://bugs.eclipse.org/330613
- *     Lucas Bullen (Red Hat Inc.) - JUnit 5 support
  *******************************************************************************/
 package org.eclipse.test;
 
-import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
-
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -24,31 +21,33 @@
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.Date;
+import java.util.Dictionary;
 import java.util.Enumeration;
 import java.util.Hashtable;
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Optional;
 import java.util.Properties;
 import java.util.Timer;
 import java.util.TimerTask;
+import java.util.Vector;
 
+import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.Project;
-import org.apache.tools.ant.taskdefs.optional.junitlauncher.TestExecutionContext;
+import org.apache.tools.ant.taskdefs.optional.junit.JUnitResultFormatter;
+import org.apache.tools.ant.taskdefs.optional.junit.JUnitTest;
 import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Path;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.ManifestElement;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.SWTException;
 import org.eclipse.swt.graphics.GC;
@@ -58,16 +57,14 @@
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Shell;
-import org.junit.platform.engine.TestExecutionResult;
-import org.junit.platform.launcher.Launcher;
-import org.junit.platform.launcher.LauncherDiscoveryRequest;
-import org.junit.platform.launcher.TestExecutionListener;
-import org.junit.platform.launcher.TestIdentifier;
-import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
-import org.junit.platform.launcher.core.LauncherFactory;
 import org.osgi.framework.Bundle;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
 
 /**
  * A TestRunner for JUnit that supports Ant JUnitResultFormatters and running
@@ -76,7 +73,20 @@
  * formatter=org.apache.tools.ant.taskdefs.optional.junit
  * .XMLJUnitResultFormatter
  */
-public class EclipseTestRunner {
+public class EclipseTestRunner implements TestListener {
+	class TestFailedException extends Exception {
+
+		private static final long serialVersionUID = 6009335074727417445L;
+
+		TestFailedException(String message) {
+			super(message);
+		}
+
+		TestFailedException(Throwable e) {
+			super(e);
+		}
+	}
+
 	static class ThreadDump extends Exception {
 
 		private static final long serialVersionUID = 1L;
@@ -122,6 +132,7 @@
 	 */
 	public static final int ERRORS = 2;
 
+	private static final String SUITE_METHODNAME = "suite";
 	/**
 	 * SECONDS_BEFORE_TIMEOUT_BUFFER is the time we allow ourselves to take stack
 	 * traces, get a screen shot, delay "SECONDS_BETWEEN_DUMPS", then do it again.
@@ -139,20 +150,68 @@
 	private static final int SECONDS_BETWEEN_DUMPS = 5;
 
 	/**
+	 * The current test result
+	 */
+	private TestResult fTestResult;
+	/**
+	 * The name of the plugin containing the test
+	 */
+	private String fTestPluginName;
+	/**
+	 * The corresponding testsuite.
+	 */
+	private Test fSuite;
+	/**
+	 * Formatters from the command line.
+	 */
+	private static Vector<JUnitResultFormatter> fgFromCmdLine = new Vector<>();
+	/**
+	 * Holds the registered formatters.
+	 */
+	private Vector<JUnitResultFormatter> formatters = new Vector<>();
+	/**
+	 * Do we stop on errors.
+	 */
+	private boolean fHaltOnError = false;
+	/**
+	 * Do we stop on test failures.
+	 */
+	private boolean fHaltOnFailure = false;
+	/**
+	 * The TestSuite we are currently running.
+	 */
+	private JUnitTest fJunitTest;
+	/**
+	 * output written during the test
+	 */
+	private PrintStream fSystemError;
+	/**
+	 * Error output during the test
+	 */
+	private PrintStream fSystemOut;
+	/**
+	 * Exception caught in constructor.
+	 */
+	private Exception fException;
+	/**
+	 * Returncode
+	 */
+	private int fRetCode = SUCCESS;
+
+	/**
 	 * The main entry point (the parameters are not yet consistent with the Ant
 	 * JUnitTestRunner, but eventually they should be). Parameters
 	 *
 	 * <pre>
-	 * -className=&lt;testSuiteName&gt;
-	 * -testPluginName&lt;containingpluginName&gt;
-	 * -formatter=&lt;classname&gt;(,&lt;path&gt;)
+	 * -className: the name of the testSuite
+	 * -testPluginName: the name of the containing plugin
+	 * haltOnError: halt test on errors?
+	 * haltOnFailure: halt test on failures?
+	 * -testlistener listenerClass: deprecated
+	 * 		print a warning that this option is deprecated
+	 * formatter: a JUnitResultFormatter given as classname,filename.
+	 *  	If filename is ommitted, System.out is assumed.
 	 * </pre>
-	 * Where &lt;classname&gt; is the formatter classname, currently ignored as only
-	 * LegacyXmlResultFormatter is used. The path is either the path to the
-	 * result file and should include the file extension (xml) if a single test
-	 * is being run or should be the path to the result directory where result
-	 * files should be created if multiple tests are being run. If no path is
-	 * given, the standard output is used.
 	 */
 	public static void main(String[] args) throws IOException {
 		System.exit(run(args));
@@ -163,10 +222,13 @@
 		String classesNames = null;
 		String testPluginName = null;
 		String testPluginsNames = null;
-		String resultPathString = null;
+		String formatterString = null;
 		String timeoutString = null;
 		String junitReportOutput = null;
 
+		boolean haltError = false;
+		boolean haltFail = false;
+
 		Properties props = new Properties();
 
 		int startArgs = 0;
@@ -200,19 +262,19 @@
 					junitReportOutput = args[i + 1];
 				i++;
 			} else if (args[i].startsWith("haltOnError=")) {
-				System.err.println("The haltOnError option is no longer supported");
+				haltError = Project.toBoolean(args[i].substring(12));
 			} else if (args[i].startsWith("haltOnFailure=")) {
-				System.err.println("The haltOnFailure option is no longer supported");
+				haltFail = Project.toBoolean(args[i].substring(14));
 			} else if (args[i].startsWith("formatter=")) {
-				String formatterString = args[i].substring(10);
-				int seperatorIndex = formatterString.indexOf(',');
-				resultPathString = formatterString.substring(seperatorIndex + 1);
+				formatterString = args[i].substring(10);
 			} else if (args[i].startsWith("propsfile=")) {
 				try (FileInputStream in = new FileInputStream(args[i].substring(10))) {
 					props.load(in);
 				}
 			} else if (args[i].equals("-testlistener")) {
-				System.err.println("The testlistener option is no longer supported");
+				System.err
+						.println("The -testlistener option is no longer supported\nuse the formatter= option instead");
+				return ERRORS;
 			} else if (args[i].equals("-timeout")) {
 				if (i < args.length - 1)
 					timeoutString = args[i + 1];
@@ -245,133 +307,44 @@
 			// names
 			String[] testPlugins = testPluginsNames.split(",");
 			String[] suiteClasses = classesNames.split(",");
+			try {
+				createAndStoreFormatter(formatterString, suiteClasses);
+			} catch (BuildException be) {
+				System.err.println(be.getMessage());
+				return ERRORS;
+			}
 			int returnCode = 0;
 			int j = 0;
-			EclipseTestRunner runner = new EclipseTestRunner();
 			for (String oneClassName : suiteClasses) {
-				int result = runner.runTests(props, testPlugins[j], oneClassName, resultPathString, true);
+				JUnitTest t = new JUnitTest(oneClassName);
+				t.setProperties(props);
+				EclipseTestRunner runner = new EclipseTestRunner(t, testPlugins[j], haltError, haltFail);
+				transferFormatters(runner, j);
+				runner.run();
 				j++;
-				if(result != 0) {
-					returnCode = result;
+				if (runner.getRetCode() != 0) {
+					returnCode = runner.getRetCode();
 				}
 			}
 			return returnCode;
 		}
+		try {
+			createAndStoreFormatter(formatterString);
+		} catch (BuildException be) {
+			System.err.println(be.getMessage());
+			return ERRORS;
+		}
 		if (className == null)
 			throw new IllegalArgumentException("Test class name not specified");
-		EclipseTestRunner runner = new EclipseTestRunner();
-		return runner.runTests(props, testPluginName, className, resultPathString, false);
-	}
 
-	private int runTests(Properties props, String testPluginName, String testClassName, String resultPath, boolean multiTest) {
-		ClassLoader currentTCCL = Thread.currentThread().getContextClassLoader();
-		ExecutionListener executionListener = new ExecutionListener();
-		if(testPluginName == null) {
-			testPluginName = ClassLoaderTools.getClassPlugin(testClassName);
-		}
-		if(testPluginName == null)
-			throw new IllegalArgumentException("Test class not found");
-		LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
-				.selectors(selectClass(testClassName))
-				.build();
+		JUnitTest t = new JUnitTest(className);
 
-		try {
-			Thread.currentThread().setContextClassLoader(ClassLoaderTools.getJUnit5Classloader(getPlatformEngines()));
-			final Launcher launcher = LauncherFactory.create();
+		t.setProperties(props);
 
-			Thread.currentThread().setContextClassLoader(ClassLoaderTools.getPluginClassLoader(testPluginName, currentTCCL));
-			try(LegacyXmlResultFormatter legacyXmlResultFormatter = new LegacyXmlResultFormatter()){
-				try (OutputStream fileOutputStream = getResultOutputStream(resultPath,testClassName,multiTest)){
-					legacyXmlResultFormatter.setDestination(fileOutputStream);
-					legacyXmlResultFormatter.setContext(new ExecutionContext(props));
-					launcher.execute(request, legacyXmlResultFormatter, executionListener);
-				}
-			} catch (IOException e) {
-				e.printStackTrace();
-				return ERRORS;
-			}
-		} finally {
-			Thread.currentThread().setContextClassLoader(currentTCCL);
-		}
-		return executionListener.didExecutionContainedFailures() ? FAILURES : SUCCESS;
-	}
-
-	private OutputStream getResultOutputStream(String resultPathString, String testClassName, boolean multiTest) throws IOException {
-		if(resultPathString == null)
-			return System.out;
-		File resultFile;
-		if(multiTest) {
-			Path resultDirectoryPath = new Path(resultPathString);
-			File testDirectory = resultDirectoryPath.toFile();
-			if(!testDirectory.exists())
-				testDirectory.mkdirs();
-			resultFile = resultDirectoryPath.append("TEST-"+testClassName+".xml").toFile();
-		}else {
-			resultFile = new Path(resultPathString).toFile();
-			File resultDirectory = resultFile.getParentFile();
-			if(!resultDirectory.exists())
-				resultDirectory.mkdirs();
-		}
-		if(!resultFile.exists()) {
-			resultFile.createNewFile();
-		}
-		return new FileOutputStream(resultFile);
-	}
-
-
-	private List<String> getPlatformEngines(){
-		List<String> platformEngines = new ArrayList<>();
-		Bundle bundle = FrameworkUtil.getBundle(getClass());
-		Bundle[] bundles = bundle.getBundleContext().getBundles();
-		for (Bundle iBundle : bundles) {
-			try {
-				BundleWiring bundleWiring = Platform.getBundle(iBundle.getSymbolicName()).adapt(BundleWiring.class);
-				Collection<String> listResources = bundleWiring.listResources("META-INF/services", "org.junit.platform.engine.TestEngine", BundleWiring.LISTRESOURCES_LOCAL);
-				if (!listResources.isEmpty())
-					platformEngines.add(iBundle.getSymbolicName());
-			} catch (Exception e) {
-				// check the next bundle
-			}
-		}
-		return platformEngines;
-	}
-
-	private final class ExecutionListener implements TestExecutionListener {
-		private boolean executionContainedFailures;
-
-		public ExecutionListener() {
-			this.executionContainedFailures = false;
-		}
-
-		public boolean didExecutionContainedFailures() {
-			return executionContainedFailures;
-		}
-
-		@Override
-		public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {
-			if(testExecutionResult.getStatus() == org.junit.platform.engine.TestExecutionResult.Status.FAILED) {
-				executionContainedFailures = true;
-			}
-		}
-	}
-
-	private final class ExecutionContext implements TestExecutionContext {
-
-		private final Properties props;
-
-		ExecutionContext(Properties props) {
-			this.props = props;
-		}
-
-		@Override
-		public Properties getProperties() {
-			return this.props;
-		}
-
-		@Override
-		public Optional<Project> getProject() {
-			return null;
-		}
+		EclipseTestRunner runner = new EclipseTestRunner(t, testPluginName, haltError, haltFail);
+		transferFormatters(runner);
+		runner.run();
+		return runner.getRetCode();
 	}
 
 	/**
@@ -431,7 +404,7 @@
 						// Dump all stacks:
 						dumpStackTraces(num, System.err);
 						dumpStackTraces(num, System.out); // System.err could be blocked, see
-						// https://bugs.eclipse.org/506304
+															// https://bugs.eclipse.org/506304
 						logStackTraces(num);              // make this available in the log, see bug 533367
 
 						if (!dumpSwtDisplay(num)) {
@@ -522,7 +495,7 @@
 
 									dumpDisplayState(System.err);
 									dumpDisplayState(System.out); // System.err could be blocked, see
-									// https://bugs.eclipse.org/506304
+																	// https://bugs.eclipse.org/506304
 
 									// Take a screenshot:
 									GC gc = new GC(display);
@@ -579,6 +552,307 @@
 		}
 	}
 
+	public EclipseTestRunner(JUnitTest test, String testPluginName, boolean haltOnError, boolean haltOnFailure) {
+		fJunitTest = test;
+		fTestPluginName = testPluginName;
+		fHaltOnError = haltOnError;
+		fHaltOnFailure = haltOnFailure;
+
+		try {
+			fSuite = getTest(test.getName());
+		} catch (Exception e) {
+			fRetCode = ERRORS;
+			fException = e;
+		}
+	}
+
+	protected Test getTest(String suiteClassName) throws TestFailedException {
+		if (suiteClassName.isEmpty()) {
+			clearStatus();
+			return null;
+		}
+		Class<?> testClass = null;
+		try {
+			testClass = loadSuiteClass(suiteClassName);
+		} catch (ClassNotFoundException e) {
+			if (e.getCause() != null) {
+				runFailed(e.getCause());
+			}
+			String clazz = e.getMessage();
+			if (clazz == null)
+				clazz = suiteClassName;
+			runFailed("Class not found \"" + clazz + "\"");
+			return null;
+		} catch (Exception e) {
+			runFailed(e);
+			return null;
+		}
+		Method suiteMethod = null;
+		try {
+			suiteMethod = testClass.getMethod(SUITE_METHODNAME);
+		} catch (Exception e) {
+			// try to extract a test suite automatically
+			clearStatus();
+			return new junit.framework.JUnit4TestAdapter(testClass);
+		}
+		if (!Modifier.isStatic(suiteMethod.getModifiers())) {
+			runFailed("suite() method must be static");
+			return null;
+		}
+		Test test = null;
+		try {
+			test = (Test) suiteMethod.invoke(null); // static method
+			if (test == null)
+				return test;
+		} catch (InvocationTargetException e) {
+			runFailed("Failed to invoke suite():" + e.getTargetException().toString());
+			return null;
+		} catch (IllegalAccessException e) {
+			runFailed("Failed to invoke suite():" + e.toString());
+			return null;
+		}
+		clearStatus();
+		return test;
+	}
+
+	protected void runFailed(String message) throws TestFailedException {
+		System.err.println(message);
+		throw new TestFailedException(message);
+	}
+
+	protected void runFailed(Throwable e) throws TestFailedException {
+		e.printStackTrace();
+		throw new TestFailedException(e);
+	}
+
+	protected void clearStatus() {
+	}
+
+	/**
+	 * Loads the class either with the system class loader or a plugin class loader
+	 * if a plugin name was specified
+	 */
+	protected Class<?> loadSuiteClass(String suiteClassName) throws ClassNotFoundException {
+		if (fTestPluginName == null)
+			return Class.forName(suiteClassName);
+		Bundle bundle = Platform.getBundle(fTestPluginName);
+		if (bundle == null) {
+			throw new ClassNotFoundException(suiteClassName,
+					new Exception("Could not find plugin \"" + fTestPluginName + "\""));
+		}
+
+		// is the plugin a fragment?
+		Dictionary<String, String> headers = bundle.getHeaders();
+		String hostHeader = headers.get(Constants.FRAGMENT_HOST);
+		if (hostHeader != null) {
+			// we are a fragment for sure
+			// we need to find which is our host
+			ManifestElement[] hostElement = null;
+			try {
+				hostElement = ManifestElement.parseHeader(Constants.FRAGMENT_HOST, hostHeader);
+			} catch (BundleException e) {
+				throw new RuntimeException("Could not find host for fragment:" + fTestPluginName, e);
+			}
+			Bundle host = Platform.getBundle(hostElement[0].getValue());
+			// we really want to get the host not the fragment
+			bundle = host;
+		}
+
+		return bundle.loadClass(suiteClassName);
+	}
+
+	public void run() {
+		fTestResult = new TestResult();
+		fTestResult.addListener(this);
+		for (int i = 0; i < formatters.size(); i++) {
+			fTestResult.addListener(formatters.elementAt(i));
+		}
+
+		long start = System.currentTimeMillis();
+		fireStartTestSuite();
+
+		if (fException != null) { // had an exception in the constructor
+			for (int i = 0; i < formatters.size(); i++) {
+				formatters.elementAt(i).addError(null, fException);
+			}
+			fJunitTest.setCounts(1, 0, 1);
+			fJunitTest.setRunTime(0);
+		} else {
+			ByteArrayOutputStream errStrm = new ByteArrayOutputStream();
+			fSystemError = new PrintStream(errStrm);
+
+			ByteArrayOutputStream outStrm = new ByteArrayOutputStream();
+			fSystemOut = new PrintStream(outStrm);
+
+			try {
+				// pm.snapshot(1); // before
+				fSuite.run(fTestResult);
+			} finally {
+				// pm.snapshot(2); // after
+				fSystemError.close();
+				fSystemError = null;
+				fSystemOut.close();
+				fSystemOut = null;
+				sendOutAndErr(new String(outStrm.toByteArray()), new String(errStrm.toByteArray()));
+				fJunitTest.setCounts(fTestResult.runCount(), fTestResult.failureCount(), fTestResult.errorCount());
+				fJunitTest.setRunTime(System.currentTimeMillis() - start);
+			}
+		}
+		fireEndTestSuite();
+
+		if (fRetCode != SUCCESS || fTestResult.errorCount() != 0) {
+			fRetCode = ERRORS;
+		} else if (fTestResult.failureCount() != 0) {
+			fRetCode = FAILURES;
+		}
+
+		// pm.upload(getClass().getName());
+	}
+
+	/**
+	 * Returns what System.exit() would return in the standalone version.
+	 *
+	 * @return 2 if errors occurred, 1 if tests failed else 0.
+	 */
+	public int getRetCode() {
+		return fRetCode;
+	}
+
+	@Override
+	public void startTest(Test t) {
+	}
+
+	@Override
+	public void endTest(Test test) {
+	}
+
+	@Override
+	public void addFailure(Test test, AssertionFailedError t) {
+		if (fHaltOnFailure) {
+			fTestResult.stop();
+		}
+	}
+
+	@Override
+	public void addError(Test test, Throwable t) {
+		if (fHaltOnError) {
+			fTestResult.stop();
+		}
+	}
+
+	private void fireStartTestSuite() {
+		for (int i = 0; i < formatters.size(); i++) {
+			formatters.elementAt(i).startTestSuite(fJunitTest);
+		}
+	}
+
+	private void fireEndTestSuite() {
+		for (int i = 0; i < formatters.size(); i++) {
+			formatters.elementAt(i).endTestSuite(fJunitTest);
+		}
+	}
+
+	public void addFormatter(JUnitResultFormatter f) {
+		formatters.addElement(f);
+	}
+
+	/**
+	 * Line format is: formatter=<classname>(,<pathname>)?
+	 */
+	private static void createAndStoreFormatter(String line) throws BuildException {
+		String formatterClassName = null;
+		File formatterFile = null;
+
+		int pos = line.indexOf(',');
+		if (pos == -1) {
+			formatterClassName = line;
+		} else {
+			formatterClassName = line.substring(0, pos);
+			formatterFile = new File(line.substring(pos + 1)); // the method is
+																// package
+																// visible
+		}
+		fgFromCmdLine.addElement(createFormatter(formatterClassName, formatterFile));
+	}
+
+	/**
+	 * Line format is: formatter=<pathname>
+	 */
+	private static void createAndStoreFormatter(String line, String... suiteClassesNames) throws BuildException {
+		String formatterClassName = null;
+		File formatterFile = null;
+
+		int pos = line.indexOf(',');
+		if (pos == -1) {
+			formatterClassName = line;
+		} else {
+			formatterClassName = line.substring(0, pos);
+		}
+		File outputDirectory = new File(line.substring(pos + 1));
+		outputDirectory.mkdir();
+		for (String suiteClassName : suiteClassesNames) {
+
+			String pathname = "TEST-" + suiteClassName + ".xml";
+			if (outputDirectory.exists()) {
+				pathname = outputDirectory.getAbsolutePath() + "/" + pathname;
+			}
+			formatterFile = new File(pathname);
+			fgFromCmdLine.addElement(createFormatter(formatterClassName, formatterFile));
+		}
+
+	}
+
+	private static void transferFormatters(EclipseTestRunner runner, int j) {
+		runner.addFormatter(fgFromCmdLine.elementAt(j));
+	}
+
+	private static void transferFormatters(EclipseTestRunner runner) {
+		for (int i = 0; i < fgFromCmdLine.size(); i++) {
+			runner.addFormatter(fgFromCmdLine.elementAt(i));
+		}
+	}
+
+	/*
+	 * DUPLICATED from FormatterElement, since it is package visible only
+	 */
+	private static JUnitResultFormatter createFormatter(String classname, File outfile) throws BuildException {
+		OutputStream out = System.out;
+
+		if (classname == null) {
+			throw new BuildException("you must specify type or classname");
+		}
+		Class<?> f = null;
+		try {
+			f = EclipseTestRunner.class.getClassLoader().loadClass(classname);
+		} catch (ClassNotFoundException e) {
+			throw new BuildException(e);
+		}
+
+		Object o = null;
+		try {
+			o = f.getDeclaredConstructor().newInstance();
+		} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
+				| NoSuchMethodException | SecurityException e) {
+			throw new BuildException(e);
+		}
+
+		if (!(o instanceof JUnitResultFormatter)) {
+			throw new BuildException(classname + " is not a JUnitResultFormatter");
+		}
+
+		JUnitResultFormatter r = (JUnitResultFormatter) o;
+
+		if (outfile != null) {
+			try {
+				out = new FileOutputStream(outfile);
+			} catch (java.io.IOException e) {
+				throw new BuildException(e);
+			}
+		}
+		r.setOutput(out);
+		return r;
+	}
+
 	public static void dumpAwtScreenshot(String screenshotFile) {
 		try {
 			URL location = AwtScreenshot.class.getProtectionDomain().getCodeSource().getLocation();
@@ -622,4 +896,23 @@
 			e.printStackTrace();
 		}
 	}
+
+	private void sendOutAndErr(String out, String err) {
+		for (JUnitResultFormatter formatter : formatters) {
+			formatter.setSystemOutput(out);
+			formatter.setSystemError(err);
+		}
+	}
+
+	protected void handleOutput(String line) {
+		if (fSystemOut != null) {
+			fSystemOut.println(line);
+		}
+	}
+
+	protected void handleErrorOutput(String line) {
+		if (fSystemError != null) {
+			fSystemError.println(line);
+		}
+	}
 }
diff --git a/bundles/org.eclipse.test/src/org/eclipse/test/LegacyXmlResultFormatter.java b/bundles/org.eclipse.test/src/org/eclipse/test/LegacyXmlResultFormatter.java
deleted file mode 100644
index 31e1da3..0000000
--- a/bundles/org.eclipse.test/src/org/eclipse/test/LegacyXmlResultFormatter.java
+++ /dev/null
@@ -1,374 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2018 Red Hat Inc. 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:
- *     Lucas Bullen (Red Hat Inc.) - initial API and implementation
- *******************************************************************************/
-package org.eclipse.test;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.util.Date;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Properties;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicLong;
-
-import javax.xml.stream.XMLOutputFactory;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.XMLStreamWriter;
-
-import org.apache.tools.ant.taskdefs.optional.junitlauncher.TestResultFormatter;
-import org.apache.tools.ant.util.DOMElementWriter;
-import org.apache.tools.ant.util.DateUtils;
-import org.junit.platform.commons.util.ExceptionUtils;
-import org.junit.platform.engine.TestExecutionResult;
-import org.junit.platform.engine.TestSource;
-import org.junit.platform.engine.reporting.ReportEntry;
-import org.junit.platform.engine.support.descriptor.ClassSource;
-import org.junit.platform.launcher.TestIdentifier;
-import org.junit.platform.launcher.TestPlan;
-
-/**
- * A {@link TestResultFormatter} which generates an XML report of the tests. The generated XML reports
- * conforms to the schema of the XML that was generated by the {@code junit} task's XML
- * report formatter and can be used by the {@code junitreport} task
- */
-public class LegacyXmlResultFormatter extends AbstractJUnitResultFormatter {
-
-	private static final double ONE_SECOND = 1000.0;
-
-	OutputStream outputStream;
-	final Map<TestIdentifier, Stats> testIds = new ConcurrentHashMap<>();
-	final Map<TestIdentifier, Optional<String>> skipped = new ConcurrentHashMap<>();
-	final Map<TestIdentifier, Optional<Throwable>> failed = new ConcurrentHashMap<>();
-	final Map<TestIdentifier, Optional<Throwable>> aborted = new ConcurrentHashMap<>();
-
-	TestPlan testPlan;
-	long testPlanStartedAt = -1;
-	long testPlanEndedAt = -1;
-	final AtomicLong numTestsRun = new AtomicLong(0);
-	final AtomicLong numTestsFailed = new AtomicLong(0);
-	final AtomicLong numTestsSkipped = new AtomicLong(0);
-	final AtomicLong numTestsAborted = new AtomicLong(0);
-
-
-	@Override
-	public void testPlanExecutionStarted(final TestPlan plan) {
-		this.testPlan = plan;
-		this.testPlanStartedAt = System.currentTimeMillis();
-	}
-
-	@Override
-	public void testPlanExecutionFinished(final TestPlan plan) {
-		this.testPlanEndedAt = System.currentTimeMillis();
-		// format and print out the result
-		try {
-			new XMLReportWriter().write();
-		} catch (IOException | XMLStreamException e) {
-			handleException(e);
-			return;
-		}
-	}
-
-	@Override
-	public void dynamicTestRegistered(final TestIdentifier testIdentifier) {
-		// nothing to do
-	}
-
-	@Override
-	public void executionSkipped(final TestIdentifier testIdentifier, final String reason) {
-		final long currentTime = System.currentTimeMillis();
-		this.numTestsSkipped.incrementAndGet();
-		this.skipped.put(testIdentifier, Optional.ofNullable(reason));
-		// a skipped test is considered started and ended now
-		final Stats stats = new Stats(testIdentifier, currentTime);
-		stats.endedAt = currentTime;
-		this.testIds.put(testIdentifier, stats);
-	}
-
-	@Override
-	public void executionStarted(final TestIdentifier testIdentifier) {
-		final long currentTime = System.currentTimeMillis();
-		this.testIds.putIfAbsent(testIdentifier, new Stats(testIdentifier, currentTime));
-		if (testIdentifier.isTest()) {
-			this.numTestsRun.incrementAndGet();
-		}
-	}
-
-	@Override
-	public void executionFinished(final TestIdentifier testIdentifier, final TestExecutionResult testExecutionResult) {
-		final long currentTime = System.currentTimeMillis();
-		final Stats stats = this.testIds.get(testIdentifier);
-		if (stats != null) {
-			stats.endedAt = currentTime;
-		}
-		switch (testExecutionResult.getStatus()) {
-		case SUCCESSFUL: {
-			break;
-		}
-		case ABORTED: {
-			this.numTestsAborted.incrementAndGet();
-			this.aborted.put(testIdentifier, testExecutionResult.getThrowable());
-			break;
-		}
-		case FAILED: {
-			this.numTestsFailed.incrementAndGet();
-			this.failed.put(testIdentifier, testExecutionResult.getThrowable());
-			break;
-		}
-		}
-	}
-
-	@Override
-	public void reportingEntryPublished(final TestIdentifier testIdentifier, final ReportEntry entry) {
-		// nothing to do
-	}
-
-	@Override
-	public void setDestination(final OutputStream os) {
-		this.outputStream = os;
-	}
-
-	private final class Stats {
-		final long startedAt;
-		long endedAt;
-
-		Stats(final TestIdentifier testIdentifier, final long startedAt) {
-			this.startedAt = startedAt;
-		}
-	}
-
-	private final class XMLReportWriter {
-
-		private static final String ELEM_TESTSUITE = "testsuite";
-		private static final String ELEM_PROPERTIES = "properties";
-		private static final String ELEM_PROPERTY = "property";
-		private static final String ELEM_TESTCASE = "testcase";
-		private static final String ELEM_SKIPPED = "skipped";
-		private static final String ELEM_FAILURE = "failure";
-		private static final String ELEM_ABORTED = "aborted";
-		private static final String ELEM_SYSTEM_OUT = "system-out";
-		private static final String ELEM_SYSTEM_ERR = "system-err";
-
-
-		private static final String ATTR_CLASSNAME = "classname";
-		private static final String ATTR_NAME = "name";
-		private static final String ATTR_VALUE = "value";
-		private static final String ATTR_TIME = "time";
-		private static final String ATTR_TIMESTAMP = "timestamp";
-		private static final String ATTR_NUM_ABORTED = "aborted";
-		private static final String ATTR_NUM_FAILURES = "failures";
-		private static final String ATTR_NUM_TESTS = "tests";
-		private static final String ATTR_NUM_SKIPPED = "skipped";
-		private static final String ATTR_MESSAGE = "message";
-		private static final String ATTR_TYPE = "type";
-
-		public XMLReportWriter() {
-			// TODO Auto-generated constructor stub
-		}
-
-		void write() throws XMLStreamException, IOException {
-			final XMLStreamWriter writer = XMLOutputFactory.newFactory().createXMLStreamWriter(outputStream, "UTF-8");
-			try {
-				writer.writeStartDocument();
-				writeTestSuite(writer);
-				writer.writeEndDocument();
-			} finally {
-				writer.close();
-			}
-		}
-
-		void writeTestSuite(final XMLStreamWriter writer) throws XMLStreamException, IOException {
-			// write the testsuite element
-			writer.writeStartElement(ELEM_TESTSUITE);
-			final String testsuiteName = determineTestSuiteName();
-			writer.writeAttribute(ATTR_NAME, testsuiteName);
-			// time taken for the tests execution
-			writer.writeAttribute(ATTR_TIME, String.valueOf((testPlanEndedAt - testPlanStartedAt) / ONE_SECOND));
-			// add the timestamp of report generation
-			final String timestamp = DateUtils.format(new Date(), DateUtils.ISO8601_DATETIME_PATTERN);
-			writer.writeAttribute(ATTR_TIMESTAMP, timestamp);
-			writer.writeAttribute(ATTR_NUM_TESTS, String.valueOf(numTestsRun.longValue()));
-			writer.writeAttribute(ATTR_NUM_FAILURES, String.valueOf(numTestsFailed.longValue()));
-			writer.writeAttribute(ATTR_NUM_SKIPPED, String.valueOf(numTestsSkipped.longValue()));
-			writer.writeAttribute(ATTR_NUM_ABORTED, String.valueOf(numTestsAborted.longValue()));
-
-			// write the properties
-			writeProperties(writer);
-			// write the tests
-			writeTestCase(writer);
-			writeSysOut(writer);
-			writeSysErr(writer);
-			// end the testsuite
-			writer.writeEndElement();
-		}
-
-		void writeProperties(final XMLStreamWriter writer) throws XMLStreamException {
-			final Properties properties = LegacyXmlResultFormatter.this.context.getProperties();
-			if (properties == null || properties.isEmpty()) {
-				return;
-			}
-			writer.writeStartElement(ELEM_PROPERTIES);
-			for (final String prop : properties.stringPropertyNames()) {
-				writer.writeStartElement(ELEM_PROPERTY);
-				writer.writeAttribute(ATTR_NAME, prop);
-				writer.writeAttribute(ATTR_VALUE, properties.getProperty(prop));
-				writer.writeEndElement();
-			}
-			writer.writeEndElement();
-		}
-
-		void writeTestCase(final XMLStreamWriter writer) throws XMLStreamException {
-			for (final Map.Entry<TestIdentifier, Stats> entry : testIds.entrySet()) {
-				final TestIdentifier testId = entry.getKey();
-				if (!testId.isTest()) {
-					// only interested in test methods
-					continue;
-				}
-				// find the parent class of this test method
-				final Optional<TestIdentifier> parent = testPlan.getParent(testId);
-				if (!parent.isPresent()) {
-					continue;
-				}
-				final String classname = parent.get().getLegacyReportingName();
-				writer.writeStartElement(ELEM_TESTCASE);
-				writer.writeAttribute(ATTR_CLASSNAME, classname);
-				writer.writeAttribute(ATTR_NAME, testId.getDisplayName());
-				final Stats stats = entry.getValue();
-				writer.writeAttribute(ATTR_TIME, String.valueOf((stats.endedAt - stats.startedAt) / ONE_SECOND));
-				// skipped element if the test was skipped
-				writeSkipped(writer, testId);
-				// failed element if the test failed
-				writeFailed(writer, testId);
-				// aborted element if the test was aborted
-				writeAborted(writer, testId);
-
-				writer.writeEndElement();
-			}
-		}
-
-		private void writeSkipped(final XMLStreamWriter writer, final TestIdentifier testIdentifier) throws XMLStreamException {
-			if (!skipped.containsKey(testIdentifier)) {
-				return;
-			}
-			writer.writeStartElement(ELEM_SKIPPED);
-			final Optional<String> reason = skipped.get(testIdentifier);
-			if (reason.isPresent()) {
-				writer.writeAttribute(ATTR_MESSAGE, reason.get());
-			}
-			writer.writeEndElement();
-		}
-
-		private void writeFailed(final XMLStreamWriter writer, final TestIdentifier testIdentifier) throws XMLStreamException {
-			if (!failed.containsKey(testIdentifier)) {
-				return;
-			}
-			writer.writeStartElement(ELEM_FAILURE);
-			final Optional<Throwable> cause = failed.get(testIdentifier);
-			if (cause.isPresent()) {
-				final Throwable t = cause.get();
-				final String message = t.getMessage();
-				if (message != null && !message.trim().isEmpty()) {
-					writer.writeAttribute(ATTR_MESSAGE, message);
-				}
-				writer.writeAttribute(ATTR_TYPE, t.getClass().getName());
-				writer.writeCharacters(ExceptionUtils.readStackTrace(t));
-			}
-			writer.writeEndElement();
-		}
-
-		private void writeAborted(final XMLStreamWriter writer, final TestIdentifier testIdentifier) throws XMLStreamException {
-			if (!aborted.containsKey(testIdentifier)) {
-				return;
-			}
-			writer.writeStartElement(ELEM_ABORTED);
-			final Optional<Throwable> cause = aborted.get(testIdentifier);
-			if (cause.isPresent()) {
-				final Throwable t = cause.get();
-				final String message = t.getMessage();
-				if (message != null && !message.trim().isEmpty()) {
-					writer.writeAttribute(ATTR_MESSAGE, message);
-				}
-				writer.writeAttribute(ATTR_TYPE, t.getClass().getName());
-			}
-			writer.writeEndElement();
-		}
-
-		private void writeSysOut(final XMLStreamWriter writer) throws XMLStreamException, IOException {
-			if (!LegacyXmlResultFormatter.this.hasSysOut()) {
-				return;
-			}
-			writer.writeStartElement(ELEM_SYSTEM_OUT);
-			try (final Reader reader = LegacyXmlResultFormatter.this.getSysOutReader()) {
-				writeCharactersFrom(reader, writer);
-			}
-			writer.writeEndElement();
-		}
-
-		private void writeSysErr(final XMLStreamWriter writer) throws XMLStreamException, IOException {
-			if (!LegacyXmlResultFormatter.this.hasSysErr()) {
-				return;
-			}
-			writer.writeStartElement(ELEM_SYSTEM_ERR);
-			try (final Reader reader = LegacyXmlResultFormatter.this.getSysErrReader()) {
-				writeCharactersFrom(reader, writer);
-			}
-			writer.writeEndElement();
-		}
-
-		private void writeCharactersFrom(final Reader reader, final XMLStreamWriter writer) throws IOException, XMLStreamException {
-			final char[] chars = new char[1024];
-			int numRead = -1;
-			while ((numRead = reader.read(chars)) != -1) {
-				// although it's called a DOMElementWriter, the encode method is just a
-				// straight forward XML util method which doesn't concern about whether
-				// DOM, SAX, StAX semantics.
-				// TODO: Perhaps make it a static method
-				final String encoded = new DOMElementWriter().encode(new String(chars, 0, numRead));
-				writer.writeCharacters(encoded);
-			}
-		}
-
-		private String determineTestSuiteName() {
-			// this is really a hack to try and match the expectations of the XML report in JUnit4.x
-			// world. In JUnit5, the TestPlan doesn't have a name and a TestPlan (for which this is a
-			// listener) can have numerous tests within it
-			final Set<TestIdentifier> roots = testPlan.getRoots();
-			if (roots.isEmpty()) {
-				return "UNKNOWN";
-			}
-			for (final TestIdentifier root : roots) {
-				final Optional<ClassSource> classSource = findFirstClassSource(root);
-				if (classSource.isPresent()) {
-					return classSource.get().getClassName();
-				}
-			}
-			return "UNKNOWN";
-		}
-
-		private Optional<ClassSource> findFirstClassSource(final TestIdentifier root) {
-			if (root.getSource().isPresent()) {
-				final TestSource source = root.getSource().get();
-				if (source instanceof ClassSource) {
-					return Optional.of((ClassSource) source);
-				}
-			}
-			for (final TestIdentifier child : testPlan.getChildren(root)) {
-				final Optional<ClassSource> classSource = findFirstClassSource(child);
-				if (classSource.isPresent()) {
-					return classSource;
-				}
-			}
-			return Optional.empty();
-		}
-	}
-
-}
diff --git a/bundles/org.eclipse.test/src/org/eclipse/test/UITestApplication.java b/bundles/org.eclipse.test/src/org/eclipse/test/UITestApplication.java
index 62b1288..966b0d0 100644
--- a/bundles/org.eclipse.test/src/org/eclipse/test/UITestApplication.java
+++ b/bundles/org.eclipse.test/src/org/eclipse/test/UITestApplication.java
@@ -10,17 +10,19 @@
  *******************************************************************************/
 package org.eclipse.test;
 
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
 import java.io.IOException;
 
+import junit.framework.Assert;
+
+import org.eclipse.equinox.app.IApplication;
+import org.eclipse.equinox.app.IApplicationContext;
+
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IConfigurationElement;
 import org.eclipse.core.runtime.IExtension;
 import org.eclipse.core.runtime.IPlatformRunnable;
 import org.eclipse.core.runtime.Platform;
-import org.eclipse.equinox.app.IApplication;
-import org.eclipse.equinox.app.IApplicationContext;
+
 import org.eclipse.ui.IWindowListener;
 import org.eclipse.ui.IWorkbench;
 import org.eclipse.ui.IWorkbenchWindow;
@@ -30,27 +32,27 @@
 
 /**
  * A Workbench that runs a test suite specified in the command line arguments.
- *
+ * 
  * @deprecated As using deprecated materials
- */
+ */ 
 @Deprecated
 public class UITestApplication  implements IPlatformRunnable, ITestHarness, IApplication {
 
 	private static final String DEFAULT_APP_3_0 = "org.eclipse.ui.ide.workbench"; //$NON-NLS-1$
 	private static final String DEFAULT_APP_PRE_3_0 = "org.eclipse.ui.workbench"; //$NON-NLS-1$
-
+	
 	private boolean fInDeprecatedMode = false;
 	private TestableObject fTestableObject;
 	int fTestRunnerResult = -1;
 	private IApplicationContext appContext;
-
-
+	
+	
 	@Override
 	public Object run(final Object args) throws Exception {
 		// Get the application to test
 		Object application = getApplication((String[])args);
-		assertNotNull(application);
-
+		Assert.assertNotNull(application);
+		
 		Object result;
 		if (fInDeprecatedMode && (application instanceof IPlatformRunnable)) {
 			result = runDeprecatedApplication((IPlatformRunnable)application, args);
@@ -63,8 +65,8 @@
 		}
 		return Integer.valueOf(fTestRunnerResult);
 	}
-
-
+	
+	
 	/*
 	 * return the application to run, or null if not even the default application
 	 * is found.
@@ -75,11 +77,11 @@
 		// If no application is specified, the 3.0 default workbench application
 		// is returned.
 		IExtension extension =
-				Platform.getExtensionRegistry().getExtension(
-						Platform.PI_RUNTIME,
-						Platform.PT_APPLICATIONS,
-						getApplicationToRun(args));
-
+		Platform.getExtensionRegistry().getExtension(
+				Platform.PI_RUNTIME,
+				Platform.PT_APPLICATIONS,
+				getApplicationToRun(args));
+		
 		// If no 3.0 extension can be found, search the registry
 		// for the pre-3.0 default workbench application, i.e. org.eclipse ui.workbench
 		// Set the deprecated flag to true
@@ -90,9 +92,9 @@
 					DEFAULT_APP_PRE_3_0);
 			fInDeprecatedMode = true;
 		}
-
-		assertNotNull(extension);
-
+		
+		Assert.assertNotNull(extension);
+		
 		// If the extension does not have the correct grammar, return null.
 		// Otherwise, return the application object.
 		IConfigurationElement[] elements = extension.getConfigurationElements();
@@ -108,13 +110,13 @@
 		}
 		return null;
 	}
-
+	
 	/**
 	 * The -testApplication argument specifies the application to be run.
 	 * If the PDE JUnit launcher did not set this argument, then return
 	 * the name of the default application.
 	 * In 3.0, the default is the "org.eclipse.ui.ide.worbench" application.
-	 *
+	 * 
 	 */
 	private String getApplicationToRun(String[] args) {
 		for (int i = 0; i < args.length; i++) {
@@ -123,21 +125,21 @@
 		}
 		return DEFAULT_APP_3_0;
 	}
-
+	
 	/**
 	 * In 3.0 mode
-	 *
+	 * 
 	 */
 	private Object runApplication(Object application, Object args) throws Exception {
 		fTestableObject = PlatformUI.getTestableObject();
 		fTestableObject.setTestHarness(this);
 		if (application instanceof IPlatformRunnable) {
 			return ((IPlatformRunnable) application).run(args);
-		}
+		} 
 		return ((IApplication) application).start(appContext);
-
+		
 	}
-
+	
 	/*
 	 * If we are in pre-3.0 mode, then the application to run is
 	 * "org.eclipse.ui.workbench" Therefore, we safely cast the runnable object
@@ -146,11 +148,11 @@
 	 * done, we explicitly call close() on the workbench.
 	 */
 	private Object runDeprecatedApplication(
-			IPlatformRunnable object,
-			final Object args)
-					throws Exception {
+		IPlatformRunnable object,
+		final Object args)
+		throws Exception {
 
-		assertNotNull(object instanceof IWorkbench);
+		Assert.assertTrue(object instanceof IWorkbench);
 
 		final IWorkbench workbench = (IWorkbench) object;
 		// the 'started' flag is used so that we only run tests when the window
@@ -212,8 +214,8 @@
 	@Override
 	public void stop() {
 		// TODO Auto-generated method stub
-
+		
 	}
-
+	
 }