diff options
| author | Lucas Bullen | 2018-07-21 15:17:10 +0000 |
|---|---|---|
| committer | Lucas Bullen | 2018-07-21 15:17:10 +0000 |
| commit | 027e4f6d9a27b081a724e7c781f2baa07f576ba7 (patch) | |
| tree | f1118d97ac9b19695325da7d86c64e66703d285f | |
| parent | 21df6c58ad51eafc007fb99b53e71ba72d229839 (diff) | |
| download | eclipse.platform.releng-027e4f6d9a27b081a724e7c781f2baa07f576ba7.tar.gz eclipse.platform.releng-027e4f6d9a27b081a724e7c781f2baa07f576ba7.tar.xz eclipse.platform.releng-027e4f6d9a27b081a724e7c781f2baa07f576ba7.zip | |
Revert "Bug 531057- 2nd Attempt JUnit 5 support for tests"Y20180725-2200Y20180723-0635Y20180723-0615I20180730-2240I20180730-2000I20180730-0800I20180729-2000I20180729-0800I20180728-1500I20180728-0805I20180727-2000I20180726-2000I20180725-2000I20180724-2000I20180723-2000I20180722-2000I20180721-1500
This reverts commit 21df6c58ad51eafc007fb99b53e71ba72d229839.
Change-Id: I5503479bc39482ee18e69da222dd98f4c54a2245
6 files changed, 482 insertions, 1060 deletions
diff --git a/bundles/org.eclipse.test/META-INF/MANIFEST.MF b/bundles/org.eclipse.test/META-INF/MANIFEST.MF index 5b9f6073..d9d11e99 100644 --- a/bundles/org.eclipse.test/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.test/META-INF/MANIFEST.MF @@ -7,16 +7,11 @@ Eclipse-BundleShape: dir 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 5d681aa0..00000000 --- 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 28fe5380..00000000 --- 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 a653ebee..1a6b77d7 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.InputStream; 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.graphics.ImageLoader; 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 @@ import org.osgi.framework.wiring.BundleWiring; * 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 class EclipseTestRunner { */ 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 @@ public class EclipseTestRunner { 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=<testSuiteName> - * -testPluginName<containingpluginName> - * -formatter=<classname>(,<path>) + * -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 <classname> 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 @@ public class EclipseTestRunner { 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 @@ public class EclipseTestRunner { 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 @@ public class EclipseTestRunner { // 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; } - 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(); - try { - Thread.currentThread().setContextClassLoader(ClassLoaderTools.getJUnit5Classloader(getPlatformEngines())); - final Launcher launcher = LauncherFactory.create(); - - 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); + createAndStoreFormatter(formatterString); + } catch (BuildException be) { + System.err.println(be.getMessage()); + return ERRORS; } - 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; + if (className == null) + throw new IllegalArgumentException("Test class name not specified"); - public ExecutionListener() { - this.executionContainedFailures = false; - } + JUnitTest t = new JUnitTest(className); - public boolean didExecutionContainedFailures() { - return executionContainedFailures; - } + t.setProperties(props); - @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 @@ public class EclipseTestRunner { // 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 @@ public class EclipseTestRunner { 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 class EclipseTestRunner { } } + 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 @@ public class EclipseTestRunner { 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 31e1da3c..00000000 --- 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 62b12887..966b0d04 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 @@ import org.eclipse.ui.testing.TestableObject; /** * 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 @@ public class UITestApplication implements IPlatformRunnable, ITestHarness, IApp } return Integer.valueOf(fTestRunnerResult); } - - + + /* * return the application to run, or null if not even the default application * is found. @@ -75,11 +77,11 @@ public class UITestApplication implements IPlatformRunnable, ITestHarness, IApp // 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 @@ public class UITestApplication implements IPlatformRunnable, ITestHarness, IApp 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 @@ public class UITestApplication implements IPlatformRunnable, ITestHarness, IApp } 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 @@ public class UITestApplication implements IPlatformRunnable, ITestHarness, IApp } 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 @@ public class UITestApplication implements IPlatformRunnable, ITestHarness, IApp * 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 @@ public class UITestApplication implements IPlatformRunnable, ITestHarness, IApp @Override public void stop() { // TODO Auto-generated method stub - + } - + } |
