diff --git a/ui/org.eclipse.pde.junit.runtime/META-INF/MANIFEST.MF b/ui/org.eclipse.pde.junit.runtime/META-INF/MANIFEST.MF
index c61b9161752..9b0dde7b1ac 100644
--- a/ui/org.eclipse.pde.junit.runtime/META-INF/MANIFEST.MF
+++ b/ui/org.eclipse.pde.junit.runtime/META-INF/MANIFEST.MF
@@ -6,12 +6,10 @@ Bundle-Version: 3.7.200.qualifier
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Require-Bundle: org.eclipse.jdt.junit.runtime;bundle-version="[3.5.0,4.0.0)",
- org.junit;bundle-version="3.8.2",
org.eclipse.core.runtime;bundle-version="[3.29.0,4.0.0)",
org.eclipse.ui;bundle-version="[3.2.0,4.0.0)";resolution:=optional
Export-Package: org.eclipse.pde.internal.junit.runtime;x-internal:=true
Bundle-RequiredExecutionEnvironment: JavaSE-17
-Bundle-Activator: org.eclipse.pde.internal.junit.runtime.PDEJUnitRuntimePlugin
Bundle-ActivationPolicy: lazy
Import-Package: org.eclipse.ui.testing;resolution:=optional
Automatic-Module-Name: org.eclipse.pde.junit.runtime
diff --git a/ui/org.eclipse.pde.junit.runtime/README.md b/ui/org.eclipse.pde.junit.runtime/README.md
new file mode 100644
index 00000000000..ddfd75ef99c
--- /dev/null
+++ b/ui/org.eclipse.pde.junit.runtime/README.md
@@ -0,0 +1,18 @@
+This PDE JUnit runtime is added to test-runtimes launched from an Eclipse workspace and allows JUnit tests to be run with an OSGi runtime.
+It supports the following use cases:
+1. Headless tests (no UI, no workbench)
+ Runs NonUIThreadTestApplication with no testable object
+2. e4 UI tests (e4 UI, no workbench)
+ Runs NonUIThreadTestApplication with a testable object from e4 service
+3. UI tests run in the non UI thread (UI, workbench)
+ Runs NonUIThreadTestApplication with a testable object from e4 service or PlatformUI
+4. UI tests run in the UI thread (UI, workbench)
+ Runs UITestApplication with a testable object from e4 service or PlatformUI
+5. Headless tests with no application (no UI, no workbench, no application)
+ Runs directly with no application
+
+If no pde.junit.runtime is available in the Target-Platform the one from the running Eclipse is added to the test-runtime.
+Of course users can target older Eclipse versions (which for example require older Java-versions),
+which is why the requirements of this Plug-in should be as low as possible to make it resolve even in older runtimes.
+
+If changes are made, one should ensure that the requirement's lower-bounds specified in the MANIFEST.MF are still valid.
diff --git a/ui/org.eclipse.pde.junit.runtime/src/org/eclipse/pde/internal/junit/runtime/MultiBundleClassLoader.java b/ui/org.eclipse.pde.junit.runtime/src/org/eclipse/pde/internal/junit/runtime/MultiBundleClassLoader.java
index 7aabec7631b..84e0bf30f2e 100644
--- a/ui/org.eclipse.pde.junit.runtime/src/org/eclipse/pde/internal/junit/runtime/MultiBundleClassLoader.java
+++ b/ui/org.eclipse.pde.junit.runtime/src/org/eclipse/pde/internal/junit/runtime/MultiBundleClassLoader.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2018 IBM Corporation and others.
+ * Copyright (c) 2018, 2023 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -36,8 +36,9 @@ protected Class> findClass(String name) throws ClassNotFoundException {
for (Bundle temp : bundleList) {
try {
Class> c = temp.loadClass(name);
- if (c != null)
+ if (c != null) {
return c;
+ }
} catch (ClassNotFoundException e) {
}
}
@@ -46,9 +47,8 @@ protected Class> findClass(String name) throws ClassNotFoundException {
@Override
protected URL findResource(String name) {
- URL url = null;
for (Bundle temp : bundleList) {
- url = temp.getResource(name);
+ URL url = temp.getResource(name);
if (url != null) {
try {
return FileLocator.resolve(url);
@@ -57,7 +57,7 @@ protected URL findResource(String name) {
}
}
}
- return url;
+ return null;
}
@Override
diff --git a/ui/org.eclipse.pde.junit.runtime/src/org/eclipse/pde/internal/junit/runtime/NonUIThreadTestApplication.java b/ui/org.eclipse.pde.junit.runtime/src/org/eclipse/pde/internal/junit/runtime/NonUIThreadTestApplication.java
index ef7e9276ba9..25773b4d4f6 100644
--- a/ui/org.eclipse.pde.junit.runtime/src/org/eclipse/pde/internal/junit/runtime/NonUIThreadTestApplication.java
+++ b/ui/org.eclipse.pde.junit.runtime/src/org/eclipse/pde/internal/junit/runtime/NonUIThreadTestApplication.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2009, 2015 EclipseSource Corporation and others.
+ * Copyright (c) 2009, 2023 EclipseSource Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -13,6 +13,8 @@
*******************************************************************************/
package org.eclipse.pde.internal.junit.runtime;
+import java.util.Objects;
+
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
@@ -20,7 +22,10 @@
import org.eclipse.core.runtime.Platform;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
-import org.junit.Assert;
+import org.eclipse.ui.testing.TestableObject;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
/**
* A Workbench that runs a test suite specified in the
@@ -32,14 +37,15 @@ public class NonUIThreadTestApplication implements IApplication {
protected IApplication fApplication;
protected Object fTestHarness;
+ protected boolean runInUIThreadAndRequirePlatformUI = false;
+ protected String defaultApplicationId = DEFAULT_HEADLESSAPP;
@Override
public Object start(IApplicationContext context) throws Exception {
String[] args = (String[]) context.getArguments().get(IApplicationContext.APPLICATION_ARGS);
String appId = getApplicationToRun(args);
- IApplication app = getApplication(appId);
- Assert.assertNotNull(app);
+ IApplication app = Objects.requireNonNull(getApplication(appId));
if (!DEFAULT_HEADLESSAPP.equals(appId)) {
// this means we are running a different application, which potentially can be UI application;
@@ -48,11 +54,6 @@ public Object start(IApplicationContext context) throws Exception {
// (see bug 340906 for details)
installPlatformUITestHarness();
}
-
- return runApp(app, context);
- }
-
- protected Object runApp(IApplication app, IApplicationContext context) throws Exception {
fApplication = app;
return fApplication.start(context);
}
@@ -66,27 +67,53 @@ protected Object runApp(IApplication app, IApplicationContext context) throws Ex
*
* @throws Exception
*/
- private void installPlatformUITestHarness() throws Exception {
- Object testableObject = PDEJUnitRuntimePlugin.getDefault().getTestableObject();
+ private void installPlatformUITestHarness() throws ReflectiveOperationException {
+ Object testableObject = getRegisteredTestableObject();
if (testableObject == null) {
- try {
+ try { // If the service doesn't return a testable object ask PlatformUI directly
Class> platformUIClass = Class.forName("org.eclipse.ui.PlatformUI", true, getClass().getClassLoader()); //$NON-NLS-1$
testableObject = platformUIClass.getMethod("getTestableObject").invoke(null); //$NON-NLS-1$
- } catch (ClassNotFoundException e) {
- // PlatformUI is not available
+ } catch (ClassNotFoundException e) { // PlatformUI is not available
+ if (runInUIThreadAndRequirePlatformUI) {
+ throw e;
+ }
}
}
if (testableObject != null) {
- fTestHarness = new PlatformUITestHarness(testableObject, true);
+ fTestHarness = new PlatformUITestHarness(testableObject, !runInUIThreadAndRequirePlatformUI);
+ }
+ }
+
+ /**
+ * Returns a {@link TestableObject} provided by a TestableObject service or {@code null} if no implementation can be found.
+ * The TestableObject is used to hook tests into the application lifecycle.
+ *
+ * It is recommended the testable object is obtained via service instead Workbench#getWorkbenchTestable() to avoid + * the tests having a dependency on the Workbench. + *
+ * @return TestableObject provided via service or {@code null} + */ + private static Object getRegisteredTestableObject() { + BundleContext context = FrameworkUtil.getBundle(NonUIThreadTestApplication.class).getBundleContext(); + ServiceReference> reference = context.getServiceReference("org.eclipse.ui.testing.TestableObject"); //$NON-NLS-1$ + if (reference != null) { + try { + return context.getService(reference); + } finally { + context.ungetService(reference); + } } + return null; } @Override public void stop() { - if (fApplication != null) + if (fApplication != null) { fApplication.stop(); - if (fTestHarness != null) + } + if (fTestHarness != null) { fTestHarness = null; + } } /* @@ -98,8 +125,7 @@ private IApplication getApplication(String appId) throws CoreException { // If no application is specified, the 3.0 default workbench application // is returned. IExtension extension = Platform.getExtensionRegistry().getExtension(Platform.PI_RUNTIME, Platform.PT_APPLICATIONS, appId); - - Assert.assertNotNull(extension); + Objects.requireNonNull(extension); // If the extension does not have the correct grammar, return null. // Otherwise, return the application object. @@ -108,8 +134,9 @@ private IApplication getApplication(String appId) throws CoreException { IConfigurationElement[] runs = elements[0].getChildren("run"); //$NON-NLS-1$ if (runs.length > 0) { Object runnable = runs[0].createExecutableExtension("class"); //$NON-NLS-1$ - if (runnable instanceof IApplication) + if (runnable instanceof IApplication) { return (IApplication) runnable; + } } } return null; @@ -125,18 +152,12 @@ private IApplication getApplication(String appId) throws CoreException { * */ private String getApplicationToRun(String[] args) { - for (int i = 0; i < args.length; i++) { - if (args[i].equals("-testApplication") && i < args.length - 1) //$NON-NLS-1$ - return args[i + 1]; + String testApp = RemotePluginTestRunner.getArgumentValue(args, "-testApplication"); //$NON-NLS-1$ + if (testApp != null) { + return testApp; } IProduct product = Platform.getProduct(); - if (product != null) - return product.getApplication(); - return getDefaultApplicationId(); - } - - protected String getDefaultApplicationId() { - return DEFAULT_HEADLESSAPP; + return product != null ? product.getApplication() : defaultApplicationId; } } diff --git a/ui/org.eclipse.pde.junit.runtime/src/org/eclipse/pde/internal/junit/runtime/PDEJUnitRuntimePlugin.java b/ui/org.eclipse.pde.junit.runtime/src/org/eclipse/pde/internal/junit/runtime/PDEJUnitRuntimePlugin.java deleted file mode 100644 index 22e8747c561..00000000000 --- a/ui/org.eclipse.pde.junit.runtime/src/org/eclipse/pde/internal/junit/runtime/PDEJUnitRuntimePlugin.java +++ /dev/null @@ -1,113 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2013, 2015 IBM Corporation and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.pde.internal.junit.runtime; - -import org.eclipse.ui.plugin.AbstractUIPlugin; -import org.eclipse.ui.testing.TestableObject; -import org.osgi.framework.BundleActivator; -import org.osgi.framework.BundleContext; -import org.osgi.util.tracker.ServiceTracker; - -/** - * The plug-in activator for the PDE JUnit Runtime plug-in. - *- * The PDE JUnit runtime allows JUnit tests to be run with an OSGi runtime. - * It supports the following use cases: - *
- * 1) Headless tests (no UI, no workbench) - * Runs NonUIThreadTestApplication with no testable object - * - * 2) e4 UI tests (e4 UI, no workbench) - * Runs NonUIThreadTestApplication with a testable object from e4 service - * - * 3) UI tests run in the non UI thread (UI, workbench) - * Runs NonUIThreadTestApplication with a testable object from e4 service or PlatformUI - * - * 4) UI tests run in the UI thread (UI, workbench) - * Runs UITestApplication with a testable object from e4 service or PlatformUI - * - * 5) Headless tests with no application (no UI, no workbench, no application) - * Runs directly with no application - *- * @since 4.3 - */ -public class PDEJUnitRuntimePlugin implements BundleActivator { - - /** - * The testable object is accessed via service and a string name to avoid depending on UI code. The - */ - private static final String TESTABLE_OBJECT_SERVICE_NAME = "org.eclipse.ui.testing.TestableObject"; //$NON-NLS-1$ - - /** - * Default instance of the receiver - */ - private static PDEJUnitRuntimePlugin inst; - - /** - * The context within which this plugin was started. - */ - private BundleContext bundleContext; - - private ServiceTracker