diff --git a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/BundleValidationOperation.java b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/BundleValidationOperation.java index f1e91b23234..5465cfbdffb 100644 --- a/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/BundleValidationOperation.java +++ b/ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/BundleValidationOperation.java @@ -31,6 +31,7 @@ import org.eclipse.osgi.util.NLS; import org.eclipse.pde.core.plugin.IPluginModelBase; import org.eclipse.pde.internal.build.BundleHelper; +import org.osgi.framework.hooks.resolver.ResolverHook; public class BundleValidationOperation implements IWorkspaceRunnable { @@ -38,6 +39,7 @@ public class BundleValidationOperation implements IWorkspaceRunnable { private final Set fModels; private final Dictionary[] fProperties; + private final ResolverHook fResolverHook; private State fState; @SuppressWarnings("unchecked") @@ -46,8 +48,13 @@ public BundleValidationOperation(Set models) { } public BundleValidationOperation(Set models, Dictionary[] properties) { + this(models, properties, null); + } + + public BundleValidationOperation(Set models, Dictionary[] properties, ResolverHook resolverHook) { fModels = models; fProperties = properties; + fResolverHook = resolverHook; } @Override @@ -57,6 +64,9 @@ public void run(IProgressMonitor monitor) throws CoreException { } SubMonitor subMonitor = SubMonitor.convert(monitor, fModels.size() + 1); fState = FACTORY.createState(true); + if (fResolverHook != null) { + fState.setResolverHookFactory(c -> fResolverHook); + } long id = 1; for (IPluginModelBase fModel : fModels) { BundleDescription bundle = fModel.getBundleDescription(); diff --git a/ui/org.eclipse.pde.launching/.settings/.api_filters b/ui/org.eclipse.pde.launching/.settings/.api_filters new file mode 100644 index 00000000000..8908bd58687 --- /dev/null +++ b/ui/org.eclipse.pde.launching/.settings/.api_filters @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/PDEMessages.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/PDEMessages.java index 410bb96a0e0..0fb4d7544b9 100644 --- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/PDEMessages.java +++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/PDEMessages.java @@ -36,6 +36,7 @@ public class PDEMessages extends NLS { public static String WorkbenchLauncherConfigurationDelegate_noStartup; public static String JUnitLaunchConfiguration_error_notaplugin; public static String JUnitLaunchConfiguration_error_missingPlugin; + public static String JUnitLaunchConfiguration_error_invalidJunitVersion; public static String OSGiLaunchConfiguration_cannotFindLaunchConfiguration; public static String OSGiLaunchConfiguration_selected; diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/LaunchValidationOperation.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/LaunchValidationOperation.java index 359cdebc4c1..ab5a80e88ae 100644 --- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/LaunchValidationOperation.java +++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/LaunchValidationOperation.java @@ -58,10 +58,14 @@ public LaunchValidationOperation(ILaunchConfiguration configuration, Set[] properties) throws CoreException { + return new BundleValidationOperation(fModels, properties); + } + @SuppressWarnings("unchecked") protected Dictionary[] getPlatformProperties() throws CoreException { IExecutionEnvironment[] envs = getMatchingEnvironments(); diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/pderesources.properties b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/pderesources.properties index 0a0851ce128..b32dc994fe2 100644 --- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/pderesources.properties +++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/pderesources.properties @@ -25,6 +25,7 @@ WorkbenchLauncherConfigurationDelegate_jrePathNotFound = The installation path t WorkbenchLauncherConfigurationDelegate_noStartup = Launching failed. Bootstrap code cannot be found. JUnitLaunchConfiguration_error_notaplugin = Could not launch the JUnit plug-in tests because project ''{0}'' is not a plug-in project. JUnitLaunchConfiguration_error_missingPlugin = Required plug-in ''{0}'' could not be found. +JUnitLaunchConfiguration_error_invalidJunitVersion = {0} {1} is incompatible with JUnit {2} runtime selected for the launch. OSGiLaunchConfiguration_cannotFindLaunchConfiguration=Cannot find the {0} OSGi framework. OSGiLaunchConfiguration_selected=selected diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/JUnitEclipsePluginValidationOperation.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/JUnitEclipsePluginValidationOperation.java new file mode 100644 index 00000000000..70878596d51 --- /dev/null +++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/JUnitEclipsePluginValidationOperation.java @@ -0,0 +1,123 @@ +package org.eclipse.pde.launching; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.osgi.service.resolver.BundleDescription; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.core.plugin.IPluginModelBase; +import org.eclipse.pde.internal.core.BundleValidationOperation; +import org.eclipse.pde.internal.launching.PDELaunchingPlugin; +import org.eclipse.pde.internal.launching.PDEMessages; +import org.eclipse.pde.internal.launching.launcher.EclipsePluginValidationOperation; +import org.osgi.framework.Constants; +import org.osgi.framework.Version; +import org.osgi.framework.VersionRange; +import org.osgi.framework.hooks.resolver.ResolverHook; +import org.osgi.framework.wiring.BundleCapability; +import org.osgi.framework.wiring.BundleRequirement; +import org.osgi.framework.wiring.BundleRevision; + +public final class JUnitEclipsePluginValidationOperation extends EclipsePluginValidationOperation implements ResolverHook { + + private static final VersionRange JUNIT5_VERSION_RANGE = new VersionRange("[1.0.0,6.0.0)"); //$NON-NLS-1$ + + private static final String JUNIT_BUNDLE_PREFIX = "junit"; //$NON-NLS-1$ + + private final Map errors; + private final VersionRange junitVersionRange; + private final int junitVersion; + + @SuppressWarnings("restriction") + public JUnitEclipsePluginValidationOperation(ILaunchConfiguration configuration, Set models, String launchMode) { + super(configuration, models, launchMode); + errors = new HashMap<>(2); + org.eclipse.jdt.internal.junit.launcher.ITestKind testKind = org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants.getTestRunnerKind(configuration); + switch (testKind.getId()) { + case org.eclipse.jdt.internal.junit.launcher.TestKindRegistry.JUNIT5_TEST_KIND_ID -> { + junitVersionRange = JUNIT5_VERSION_RANGE; + junitVersion = 5; + } + default -> { + junitVersionRange = null; + junitVersion = -1; + } + } + } + + protected BundleValidationOperation createOperation(Dictionary[] properties) throws CoreException { + ResolverHook hook = junitVersionRange != null ? this : null; + BundleValidationOperation op = new BundleValidationOperation(fModels, properties, hook); + return op; + } + + @Override + public void filterMatches(BundleRequirement requirement, Collection candidates) { + if (!isOptional(requirement)) { + BundleRevision requirementRevision = requirement.getRevision(); + String requirementName = requirementRevision.getSymbolicName(); + if (!requirementName.startsWith(JUNIT_BUNDLE_PREFIX)) { + Iterator iterator = candidates.iterator(); + while (iterator.hasNext()) { + BundleCapability candidate = iterator.next(); + BundleRevision candidateRevision = candidate.getRevision(); + String name = candidateRevision.getSymbolicName(); + Version version = candidateRevision.getVersion(); + if (!junitVersionRange.includes(version) && name.startsWith(JUNIT_BUNDLE_PREFIX)) { + Version requirementVersion = requirementRevision.getVersion(); + BundleDescription bundle = getState().getBundle(requirementName, requirementVersion); + if (bundle != null) { + String error = NLS.bind(PDEMessages.JUnitLaunchConfiguration_error_invalidJunitVersion, new Object[] {name, version, Integer.valueOf(junitVersion)}); + IStatus[] bundleErrors = errors.computeIfAbsent(bundle, b -> new Status[0]); + if (!Arrays.stream(bundleErrors).map(IStatus::getMessage).anyMatch(m -> error.equals(m))) { + IStatus[] newBundleErrors = Arrays.copyOf(bundleErrors, bundleErrors.length + 1); + newBundleErrors[bundleErrors.length] = Status.error(error); + errors.put(bundle, newBundleErrors); + } + } else { + PDELaunchingPlugin.log(Status.error("Bundle not found: " + requirementName + " " + requirementVersion, new IllegalStateException())); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + } + } + } + } + + private boolean isOptional(BundleRequirement requirement) { + return Constants.RESOLUTION_OPTIONAL.equals(requirement.getDirectives().get(Constants.RESOLUTION_DIRECTIVE)); + } + + @Override + public void filterResolvable(Collection candidates) { + } + + @Override + public void filterSingletonCollisions(BundleCapability singleton, Collection collisionCandidates) { + } + + @Override + public void end() { + } + + @Override + public boolean hasErrors() { + return super.hasErrors() || !errors.isEmpty(); + } + + @Override + public Map getInput() { + Map map = super.getInput(); + map.putAll(errors); + return map; + } + +} diff --git a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/JUnitLaunchConfigurationDelegate.java b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/JUnitLaunchConfigurationDelegate.java index dfd2610cdf1..72b75f980b2 100644 --- a/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/JUnitLaunchConfigurationDelegate.java +++ b/ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/JUnitLaunchConfigurationDelegate.java @@ -74,7 +74,6 @@ import org.eclipse.pde.internal.launching.PDELaunchingPlugin; import org.eclipse.pde.internal.launching.PDEMessages; import org.eclipse.pde.internal.launching.launcher.BundleLauncherHelper; -import org.eclipse.pde.internal.launching.launcher.EclipsePluginValidationOperation; import org.eclipse.pde.internal.launching.launcher.LaunchArgumentsHelper; import org.eclipse.pde.internal.launching.launcher.LaunchConfigurationHelper; import org.eclipse.pde.internal.launching.launcher.LaunchPluginValidator; @@ -610,7 +609,7 @@ protected void validateProjectDependencies(ILaunchConfiguration configuration, I * a progress monitor */ protected void validatePluginDependencies(ILaunchConfiguration configuration, IProgressMonitor monitor) throws CoreException { - EclipsePluginValidationOperation op = new EclipsePluginValidationOperation(configuration, fModels.keySet(), launchMode); + JUnitEclipsePluginValidationOperation op = new JUnitEclipsePluginValidationOperation(configuration, fModels.keySet(), launchMode); LaunchPluginValidator.runValidationOperation(op, monitor); } } diff --git a/ui/org.eclipse.pde.unittest.junit/src/org/eclipse/pde/unittest/junit/launcher/JUnitPluginLaunchConfigurationDelegate.java b/ui/org.eclipse.pde.unittest.junit/src/org/eclipse/pde/unittest/junit/launcher/JUnitPluginLaunchConfigurationDelegate.java index b7569339830..3acd0055cfd 100644 --- a/ui/org.eclipse.pde.unittest.junit/src/org/eclipse/pde/unittest/junit/launcher/JUnitPluginLaunchConfigurationDelegate.java +++ b/ui/org.eclipse.pde.unittest.junit/src/org/eclipse/pde/unittest/junit/launcher/JUnitPluginLaunchConfigurationDelegate.java @@ -87,7 +87,6 @@ import org.eclipse.pde.internal.launching.IPDEConstants; import org.eclipse.pde.internal.launching.JUnitLaunchRequirements; import org.eclipse.pde.internal.launching.launcher.BundleLauncherHelper; -import org.eclipse.pde.internal.launching.launcher.EclipsePluginValidationOperation; import org.eclipse.pde.internal.launching.launcher.LaunchArgumentsHelper; import org.eclipse.pde.internal.launching.launcher.LaunchConfigurationHelper; import org.eclipse.pde.internal.launching.launcher.LaunchPluginValidator; @@ -95,6 +94,7 @@ import org.eclipse.pde.internal.launching.launcher.RequirementHelper; import org.eclipse.pde.internal.launching.launcher.VMHelper; import org.eclipse.pde.launching.IPDELauncherConstants; +import org.eclipse.pde.launching.JUnitEclipsePluginValidationOperation; import org.eclipse.pde.launching.PDESourcePathProvider; import org.eclipse.pde.unittest.junit.JUnitPluginTestPlugin; import org.osgi.framework.Bundle; @@ -1224,8 +1224,8 @@ protected void validateProjectDependencies(ILaunchConfiguration configuration, I */ protected void validatePluginDependencies(ILaunchConfiguration configuration, IProgressMonitor monitor) throws CoreException { - EclipsePluginValidationOperation op = new EclipsePluginValidationOperation(configuration, fModels.keySet(), - launchMode); + JUnitEclipsePluginValidationOperation op = new JUnitEclipsePluginValidationOperation(configuration, + fModels.keySet(), launchMode); LaunchPluginValidator.runValidationOperation(op, monitor); } }