diff --git a/platform/org.eclipse.platform.testing/.classpath b/platform/org.eclipse.platform.testing/.classpath new file mode 100644 index 00000000000..375961e4d61 --- /dev/null +++ b/platform/org.eclipse.platform.testing/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/platform/org.eclipse.platform.testing/.project b/platform/org.eclipse.platform.testing/.project new file mode 100644 index 00000000000..018450e2322 --- /dev/null +++ b/platform/org.eclipse.platform.testing/.project @@ -0,0 +1,34 @@ + + + org.eclipse.platform.testing + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + org.eclipse.pde.api.tools.apiAnalysisBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + org.eclipse.pde.api.tools.apiAnalysisNature + + diff --git a/platform/org.eclipse.platform.testing/.settings/.api_filters b/platform/org.eclipse.platform.testing/.settings/.api_filters new file mode 100644 index 00000000000..258bc415389 --- /dev/null +++ b/platform/org.eclipse.platform.testing/.settings/.api_filters @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/platform/org.eclipse.platform.testing/.settings/org.eclipse.core.resources.prefs b/platform/org.eclipse.platform.testing/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..99f26c0203a --- /dev/null +++ b/platform/org.eclipse.platform.testing/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/platform/org.eclipse.platform.testing/.settings/org.eclipse.jdt.core.prefs b/platform/org.eclipse.platform.testing/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..23fa13b1705 --- /dev/null +++ b/platform/org.eclipse.platform.testing/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,9 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 +org.eclipse.jdt.core.compiler.compliance=21 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=21 diff --git a/platform/org.eclipse.platform.testing/.settings/org.eclipse.pde.core.prefs b/platform/org.eclipse.platform.testing/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 00000000000..e8ff8be0bab --- /dev/null +++ b/platform/org.eclipse.platform.testing/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +pluginProject.equinox=false +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/platform/org.eclipse.platform.testing/META-INF/MANIFEST.MF b/platform/org.eclipse.platform.testing/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..9f91a0e929a --- /dev/null +++ b/platform/org.eclipse.platform.testing/META-INF/MANIFEST.MF @@ -0,0 +1,17 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Testing Support for Eclipse Applications +Bundle-SymbolicName: org.eclipse.platform.testing +Bundle-Version: 1.0.0.qualifier +Automatic-Module-Name: org.eclipse.platform.testing +Bundle-ActivationPolicy: lazy +Bundle-RequiredExecutionEnvironment: JavaSE-21 +Import-Package: org.eclipse.osgi.service.runnable;version="[1.1.0,2.0.0)", + org.junit.jupiter.api.extension;version="[5.14.0,6.0.0)", + org.junit.platform.launcher;version="[1.14.0,2.0.0)", + org.osgi.framework;version="[1.10.0,2.0.0)", + org.osgi.util.tracker;version="[1.5.0,2.0.0)" +Require-Bundle: org.eclipse.equinox.common;bundle-version="3.20.300", + org.eclipse.e4.ui.workbench3, + org.eclipse.equinox.app +Export-Package: org.eclipse.platform.testing diff --git a/platform/org.eclipse.platform.testing/build.properties b/platform/org.eclipse.platform.testing/build.properties new file mode 100644 index 00000000000..07c72fb6e09 --- /dev/null +++ b/platform/org.eclipse.platform.testing/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . +tycho.pomless.parent = ../../ \ No newline at end of file diff --git a/platform/org.eclipse.platform.testing/src/META-INF/services/org.junit.jupiter.api.extension.Extension b/platform/org.eclipse.platform.testing/src/META-INF/services/org.junit.jupiter.api.extension.Extension new file mode 100644 index 00000000000..12706196285 --- /dev/null +++ b/platform/org.eclipse.platform.testing/src/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -0,0 +1 @@ +org.eclipse.platform.testing.EclipseInvocationInterceptor \ No newline at end of file diff --git a/platform/org.eclipse.platform.testing/src/META-INF/services/org.junit.platform.launcher.LauncherSessionListener b/platform/org.eclipse.platform.testing/src/META-INF/services/org.junit.platform.launcher.LauncherSessionListener new file mode 100644 index 00000000000..a42efbbf175 --- /dev/null +++ b/platform/org.eclipse.platform.testing/src/META-INF/services/org.junit.platform.launcher.LauncherSessionListener @@ -0,0 +1 @@ +org.eclipse.platform.testing.EclipseLauncherSessionListener \ No newline at end of file diff --git a/platform/org.eclipse.platform.testing/src/org/eclipse/platform/testing/EclipseApplicationLauncher.java b/platform/org.eclipse.platform.testing/src/org/eclipse/platform/testing/EclipseApplicationLauncher.java new file mode 100644 index 00000000000..ccff01ad48b --- /dev/null +++ b/platform/org.eclipse.platform.testing/src/org/eclipse/platform/testing/EclipseApplicationLauncher.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2025 Christoph Läubrich 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: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.platform.testing; + +import org.eclipse.osgi.service.runnable.ApplicationLauncher; +import org.eclipse.osgi.service.runnable.ParameterizedRunnable; + +class EclipseApplicationLauncher implements ApplicationLauncher { + + @Override + public void launch(ParameterizedRunnable runnable, Object context) { + + } + + @Override + public void shutdown() { + + } + +} diff --git a/platform/org.eclipse.platform.testing/src/org/eclipse/platform/testing/EclipseInvocationInterceptor.java b/platform/org.eclipse.platform.testing/src/org/eclipse/platform/testing/EclipseInvocationInterceptor.java new file mode 100644 index 00000000000..706017cd613 --- /dev/null +++ b/platform/org.eclipse.platform.testing/src/org/eclipse/platform/testing/EclipseInvocationInterceptor.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2025 Christoph Läubrich 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: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.platform.testing; + +import java.lang.reflect.Method; + +import org.eclipse.pde.api.tools.annotations.NoInstantiate; +import org.eclipse.pde.api.tools.annotations.NoReference; +import org.eclipse.ui.testing.TestableObject; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.InvocationInterceptor; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.util.tracker.ServiceTracker; + +/** + * An {@link InvocationInterceptor} that executes all tests inside the + * {@link TestableObject#runTest(Runnable)} method. + */ +@NoInstantiate +public class EclipseInvocationInterceptor implements InvocationInterceptor { + + private final ServiceTracker testableObjectTracker; + private final TestableObject testableObject; + + /** + * Creates the extension, will be called by JUnit + */ + public EclipseInvocationInterceptor() { + Bundle bundle = FrameworkUtil.getBundle(EclipseLauncherSessionListener.class); + if (bundle == null) { + throw new IllegalStateException("Not running inside an OSGi Framework"); + } + BundleContext bundleContext = bundle.getBundleContext(); + if (bundleContext == null) { + throw new IllegalStateException("Extension Bundle not started"); + } + testableObjectTracker = new ServiceTracker<>(bundleContext, TestableObject.class, null); + testableObjectTracker.open(); + testableObject = testableObjectTracker.getService(); + if (testableObject == null) { + throw new IllegalStateException("Testable Object not found!"); + } + } + + @Override + @NoReference + public void interceptTestMethod(Invocation invocation, ReflectiveInvocationContext invocationContext, + ExtensionContext extensionContext) throws Throwable { + Throwable[] throwable = new Throwable[1]; + TestableObject service = testableObjectTracker.getService(); + if (service != null) { + service.runTest(() -> { + try { + invocation.proceed(); + } catch (Throwable e) { + throwable[0] = e; + } + }); + Throwable t = throwable[0]; + if (t == null) { + return; + } + throw t; + } + } + +} diff --git a/platform/org.eclipse.platform.testing/src/org/eclipse/platform/testing/EclipseLauncherSessionListener.java b/platform/org.eclipse.platform.testing/src/org/eclipse/platform/testing/EclipseLauncherSessionListener.java new file mode 100644 index 00000000000..9d39b57e601 --- /dev/null +++ b/platform/org.eclipse.platform.testing/src/org/eclipse/platform/testing/EclipseLauncherSessionListener.java @@ -0,0 +1,141 @@ +package org.eclipse.platform.testing; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.stream.Collectors; + +import org.eclipse.equinox.app.IApplicationContext; +import org.eclipse.osgi.service.runnable.ApplicationLauncher; +import org.eclipse.osgi.service.runnable.ParameterizedRunnable; +import org.eclipse.pde.api.tools.annotations.NoInstantiate; +import org.eclipse.pde.api.tools.annotations.NoReference; +import org.eclipse.ui.testing.ITestHarness; +import org.eclipse.ui.testing.TestableObject; +import org.junit.platform.launcher.LauncherSession; +import org.junit.platform.launcher.LauncherSessionListener; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.application.ApplicationDescriptor; +import org.osgi.service.application.ApplicationException; +import org.osgi.service.application.ApplicationHandle; +import org.osgi.util.tracker.ServiceTracker; + +/** + * + */ +@NoInstantiate +public class EclipseLauncherSessionListener implements LauncherSessionListener { + + private TestableObject testableObject; + private ApplicationHandle applicationHandle; + private ServiceTracker testableObjectTracker; + private ServiceTracker applicationDescriptorTracker; + private ServiceRegistration applicationService; + private Thread applicationThread; + + + @Override + @NoReference + public void launcherSessionOpened(LauncherSession session) { + Bundle bundle = FrameworkUtil.getBundle(EclipseLauncherSessionListener.class); + if (bundle == null) { + System.err.println("Not running inside OSGi!"); + return; + } + BundleContext bundleContext = bundle.getBundleContext(); + if (bundleContext == null) { + System.err.println("Not started/resolved?"); + return; + } + EclipseApplicationLauncher launcher = new EclipseApplicationLauncher(); + applicationService = bundleContext.registerService(ApplicationLauncher.class, launcher, null); + testableObjectTracker = new ServiceTracker<>(bundleContext, TestableObject.class, null); + applicationDescriptorTracker = new ServiceTracker<>(bundleContext, ApplicationDescriptor.class, null); + applicationDescriptorTracker.open(); + testableObjectTracker.open(); + String testApplication = "org.eclipse.ui.ide.workbench"; // TODO how to access the JUnit config of the + // session?!? + Map tracked = applicationDescriptorTracker.getTracked().entrySet().stream() + .collect(Collectors.toMap(e -> (String) e.getKey().getProperty(ApplicationDescriptor.APPLICATION_PID), + e -> e.getValue())); + ApplicationDescriptor applicationDescriptor = tracked.get(testApplication); + if (applicationDescriptor == null) { + System.err.println("Test application '" + testApplication + "' was not found available applications are:"); + for (String appId : tracked.keySet()) { + System.err.println("\t- " + appId); + } + return; + } + testableObject = testableObjectTracker.getService(); + if (testableObject == null) { + System.err.println("No TestableObject found!"); + return; + } + CountDownLatch latch = new CountDownLatch(1); + testableObject.setTestHarness(new ITestHarness() { + + @Override + public void runTests() { + latch.countDown(); + } + }); + HashMap launchArgs = new HashMap<>(1); + String[] args = new String[0]; + launchArgs.put(IApplicationContext.APPLICATION_ARGS, args); + try { + applicationHandle = applicationDescriptor.launch(launchArgs); + } catch (ApplicationException e) { + e.printStackTrace(); + return; + } + if (applicationHandle instanceof ParameterizedRunnable runable) { + applicationThread = new Thread(new Runnable() { + + @Override + public void run() { + try { + runable.run(args); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + applicationThread.setName("Eclipse-Test-Application [" + testApplication + "]"); + applicationThread.setDaemon(true); + applicationThread.start(); + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + + @Override + @NoReference + public void launcherSessionClosed(LauncherSession session) { + if (testableObject != null) { + testableObject.testingFinished(); + } + if (testableObjectTracker != null) { + testableObjectTracker.close(); + } + if (applicationDescriptorTracker != null) { + applicationDescriptorTracker.close(); + } + if (applicationService != null) { + applicationService.unregister(); + } + if (applicationThread != null) { + try { + applicationThread.join(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + +} diff --git a/team/tests/org.eclipse.compare.tests/META-INF/MANIFEST.MF b/team/tests/org.eclipse.compare.tests/META-INF/MANIFEST.MF index 33ddc5aceee..3dbe4bc16b0 100644 --- a/team/tests/org.eclipse.compare.tests/META-INF/MANIFEST.MF +++ b/team/tests/org.eclipse.compare.tests/META-INF/MANIFEST.MF @@ -13,7 +13,9 @@ Require-Bundle: org.eclipse.compare, org.eclipse.core.tests.resources, org.eclipse.core.tests.harness, org.eclipse.core.filesystem, - org.eclipse.team.ui + org.eclipse.team.ui, + org.eclipse.platform.testing, + org.eclipse.ui.ide.application;bundle-version="1.5.900" Import-Package: org.assertj.core.api, org.assertj.core.api.iterable, org.junit.jupiter.api;version="[5.14.0,6.0.0)", diff --git a/team/tests/org.eclipse.compare.tests/build.properties b/team/tests/org.eclipse.compare.tests/build.properties index 03eb29630d2..2f08ad5f80d 100644 --- a/team/tests/org.eclipse.compare.tests/build.properties +++ b/team/tests/org.eclipse.compare.tests/build.properties @@ -28,4 +28,4 @@ output.. = bin/ # Maven/Tycho pom model adjustments pom.model.property.testClass = org.eclipse.compare.tests.AllCompareTests -pom.model.property.tycho.surefire.useUIHarness = true +pom.model.property.tycho.surefire.useUIHarness = false diff --git a/team/tests/org.eclipse.compare.tests/pom.xml b/team/tests/org.eclipse.compare.tests/pom.xml new file mode 100644 index 00000000000..273199daed4 --- /dev/null +++ b/team/tests/org.eclipse.compare.tests/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + org.eclipse.platform + eclipse.platform.team.tests + 4.38.0-SNAPSHOT + + org.eclipse.compare.tests + eclipse-plugin + 3.8.900-SNAPSHOT + + 6.0.0-SNAPSHOT + + + + + + org.eclipse.tycho + tycho-surefire-plugin + + true + + + + org.eclipse.tycho + tycho-test-plugin + ${tycho.version} + + + execute-tests + + junit-platform + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + + org.junit.platform + junit-platform-console + 1.14.0 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.14.0 + test + + + org.eclipse.platform + org.eclipse.platform.testing + 1.0.0-SNAPSHOT + compile + + +