Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
import org.eclipse.pde.core.project.IBundleProjectService;
import org.eclipse.pde.core.target.ITargetDefinition;
import org.eclipse.pde.core.target.ITargetPlatformService;
import org.eclipse.pde.internal.build.IPDEBuildConstants;
import org.eclipse.pde.internal.core.bnd.BndResourceChangeListener;
import org.eclipse.pde.internal.core.bnd.BndWorkspaceServiceFactory;
import org.eclipse.pde.internal.core.builders.FeatureRebuilder;
Expand Down Expand Up @@ -245,11 +244,6 @@ public PDECore() {
* {@link #VERSION} comparator.
*/
public Stream<IPluginModelBase> findPluginsInHost(String id) {
if (IPDEBuildConstants.BUNDLE_OSGI.equals(id)) {
// Do not expose the OSGi framework from the host see
// https://github.com/eclipse-pde/eclipse.pde/issues/2082
return Stream.empty();
}
Map<String, List<IPluginModelBase>> hostPlugins = getHostPlugins();
if (hostPlugins == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,126 @@
package org.eclipse.pde.internal.launching;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.ResolverError;
import org.eclipse.osgi.service.resolver.State;
import org.eclipse.osgi.service.resolver.StateObjectFactory;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.plugin.PluginRegistry;
import org.eclipse.pde.internal.build.BundleHelper;
import org.eclipse.pde.internal.core.DependencyManager;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.launching.launcher.BundleLauncherHelper;
import org.osgi.framework.hooks.resolver.ResolverHook;
import org.osgi.framework.hooks.resolver.ResolverHookFactory;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleRevision;

public class JUnitLaunchRequirements {

private static final StateObjectFactory FACTORY = BundleHelper.getPlatformAdmin().getFactory();
private static final String PDE_JUNIT_RUNTIME = "org.eclipse.pde.junit.runtime"; //$NON-NLS-1$
private static final String JUNIT4_JDT_RUNTIME_PLUGIN = "org.eclipse.jdt.junit4.runtime"; //$NON-NLS-1$
private static final String JUNIT5_JDT_RUNTIME_PLUGIN = "org.eclipse.jdt.junit5.runtime"; //$NON-NLS-1$

public static void addRequiredJunitRuntimePlugins(ILaunchConfiguration configuration, Map<String, List<IPluginModelBase>> collectedModels, Map<IPluginModelBase, String> startLevelMap) throws CoreException {
Collection<String> runtimePlugins = getRequiredJunitRuntimeEclipsePlugins(configuration);
Set<BundleDescription> addedRuntimeBundles = addAbsentRequirements(runtimePlugins, collectedModels, startLevelMap);
Set<BundleDescription> runtimeRequirements = DependencyManager.findRequirementsClosure(addedRuntimeBundles);
Collection<IPluginModelBase> runtimeBundles = getEclipseJunitRuntimePlugins(configuration, collectedModels, startLevelMap);
List<BundleDescription> roots = runtimeBundles.stream().map(p -> p.getBundleDescription()).filter(Objects::nonNull).toList();
Set<BundleDescription> closure = DependencyManager.findRequirementsClosure(roots);
Collection<BundleDescription> runtimeRequirements = filterRequirementsByState(closure, runtimeBundles, configuration);
addAbsentRequirements(runtimeRequirements, collectedModels, startLevelMap);
}

private static Collection<BundleDescription> filterRequirementsByState(Collection<BundleDescription> bundles, Collection<IPluginModelBase> rootBundles, ILaunchConfiguration configuration) throws CoreException {
//lookup that maps a copy to the original description from the bundles parameter
Map<BundleRevision, BundleDescription> descriptionnMap = new IdentityHashMap<>();
Set<BundleRevision> rootSet = rootBundles.stream().map(p -> p.getBundleDescription()).filter(Objects::nonNull).collect(Collectors.toSet());
State state = FACTORY.createState(true);
State targetState = PDECore.getDefault().getModelManager().getState().getState();
List<BundleDescription> resolveRoots = new ArrayList<>();
long id = 1;
for (BundleDescription bundle : bundles) {
BundleDescription copy = FACTORY.createBundleDescription(id++, bundle);
descriptionnMap.put(copy, bundle);
state.addBundle(copy);
if (rootSet.contains(bundle)) {
resolveRoots.add(copy);
}
}
state.setPlatformProperties(targetState.getPlatformProperties());
state.setResolverHookFactory(new ResolverHookFactory() {

@Override
public ResolverHook begin(Collection<BundleRevision> triggers) {
return new ResolverHook() {

@Override
public void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates) {
}

@Override
public void filterResolvable(Collection<BundleRevision> candidates) {
}

@Override
public void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates) {
List<BundleCapability> list = candidates.stream().filter(cp -> isFromDifferentState(cp)).toList();
if (list.isEmpty()) {
//nothing to do here...
return;
}
//iterate in reverse order so we remove lower ranked candidates first ...
for (int i = list.size() - 1; i >= 0 && candidates.size() > 1; i--) {
BundleCapability capability = list.get(i);
candidates.remove(capability);
}
}

private boolean isFromDifferentState(BundleCapability capability) {
BundleRevision resource = capability.getResource();
BundleDescription original = descriptionnMap.get(resource);
if (original != null) {
return original.getContainingState() != targetState;
}
return false;
}

@Override
public void end() {
}
};
}
});
state.resolve(false);
for (BundleDescription rootBundle : resolveRoots) {
ResolverError[] errors = state.getResolverErrors(rootBundle);
if (errors.length > 0) {
throw new CoreException(Status.error(String.format("%s can not be resolved: %s", rootBundle, Arrays.toString(errors)))); //$NON-NLS-1$
}
}
Collection<BundleDescription> closure = DependencyManager.findRequirementsClosure(resolveRoots);
// map back to the originals!
return closure.stream().map(bd -> descriptionnMap.get(bd)).filter(Objects::nonNull).toList();
}

@SuppressWarnings("restriction")
public static Collection<String> getRequiredJunitRuntimeEclipsePlugins(ILaunchConfiguration configuration) {
org.eclipse.jdt.internal.junit.launcher.ITestKind testKind = org.eclipse.jdt.internal.junit.launcher.JUnitLaunchConfigurationConstants.getTestRunnerKind(configuration);
Expand All @@ -59,7 +145,7 @@ public static Collection<String> getRequiredJunitRuntimeEclipsePlugins(ILaunchCo
return List.of(PDE_JUNIT_RUNTIME);
} // Nothing to add for JUnit-3
case org.eclipse.jdt.internal.junit.launcher.TestKindRegistry.JUNIT4_TEST_KIND_ID -> {
return List.of(PDE_JUNIT_RUNTIME,JUNIT4_JDT_RUNTIME_PLUGIN);
return List.of(PDE_JUNIT_RUNTIME, JUNIT4_JDT_RUNTIME_PLUGIN);
}
case org.eclipse.jdt.internal.junit.launcher.TestKindRegistry.JUNIT5_TEST_KIND_ID -> {
return List.of(PDE_JUNIT_RUNTIME, JUNIT5_JDT_RUNTIME_PLUGIN);
Expand All @@ -68,21 +154,27 @@ public static Collection<String> getRequiredJunitRuntimeEclipsePlugins(ILaunchCo
}
}

private static Set<BundleDescription> addAbsentRequirements(Collection<String> requirements, Map<String, List<IPluginModelBase>> collectedModels, Map<IPluginModelBase, String> startLevelMap) throws CoreException {
Set<BundleDescription> addedRequirements = new LinkedHashSet<>();
for (String id : requirements) {
List<IPluginModelBase> models = collectedModels.computeIfAbsent(id, k -> new ArrayList<>());
if (models.stream().noneMatch(p -> p.getBundleDescription().isResolved())) {
IPluginModelBase model = findRequiredPluginInTargetOrHost(PluginRegistry.findModel(id), plugins -> plugins.max(PDECore.VERSION), id);
models.add(model);
BundleLauncherHelper.addDefaultStartingBundle(startLevelMap, model);
addedRequirements.add(model.getBundleDescription());
}
private static Collection<IPluginModelBase> getEclipseJunitRuntimePlugins(ILaunchConfiguration configuration, Map<String, List<IPluginModelBase>> collectedModels, Map<IPluginModelBase, String> startLevelMap) throws CoreException {
Set<IPluginModelBase> descriptions = new LinkedHashSet<>();
for (String id : getRequiredJunitRuntimeEclipsePlugins(configuration)) {
addIfAbsent(id, collectedModels, startLevelMap).ifPresent(descriptions::add);
}
return addedRequirements;
return descriptions;
}

private static Optional<IPluginModelBase> addIfAbsent(String id, Map<String, List<IPluginModelBase>> collectedModels, Map<IPluginModelBase, String> startLevelMap) throws CoreException {
List<IPluginModelBase> models = collectedModels.computeIfAbsent(id, k -> new ArrayList<>());
if (models.stream().noneMatch(m -> m.getBundleDescription().isResolved())) {
IPluginModelBase model = findRequiredPluginInTargetOrHost(PluginRegistry.findModel(id), plugins -> plugins.max(PDECore.VERSION), id);
models.add(model);
BundleLauncherHelper.addDefaultStartingBundle(startLevelMap, model);
return Optional.of(model);
}

return models.stream().filter(m -> m.getBundleDescription().isResolved()).findFirst();
}

private static void addAbsentRequirements(Set<BundleDescription> requirements, Map<String, List<IPluginModelBase>> collectedModels, Map<IPluginModelBase, String> startLevelMap) throws CoreException {
private static void addAbsentRequirements(Collection<BundleDescription> requirements, Map<String, List<IPluginModelBase>> collectedModels, Map<IPluginModelBase, String> startLevelMap) throws CoreException {
for (BundleRevision bundle : requirements) {
String id = bundle.getSymbolicName();
List<IPluginModelBase> models = collectedModels.computeIfAbsent(id, k -> new ArrayList<>());
Expand Down
Loading