blob: f16235f14bd8ad6c2a43975e0569cf46adbcf1f2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019, 2022 Julian Honnen 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:
* Julian Honnen <julian.honnen@vector.com> - initial API and implementation
* Andras Peteri <apeteri@b2international.com> - extracted common superclass
* Hannes Wellmann - Bug 577116 - Improve test utility method reusability
* Hannes Wellmann - Bug 578005 - Extend tests to fully cover feature-based launches
*******************************************************************************/
package org.eclipse.pde.ui.tests.launcher;
import static java.util.Collections.emptySet;
import static java.util.Map.ofEntries;
import static org.assertj.core.api.Assertions.assertThat;
import static org.eclipse.pde.internal.core.ICoreConstants.DEFAULT_VERSION;
import static org.eclipse.pde.ui.tests.util.ProjectUtils.createPluginProject;
import static org.eclipse.pde.ui.tests.util.TargetPlatformUtil.bundle;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.debug.core.*;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.pde.core.plugin.IMatchRules;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.core.target.NameVersionDescriptor;
import org.eclipse.pde.internal.core.FeatureModelManager;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.internal.core.feature.FeatureChild;
import org.eclipse.pde.internal.core.feature.WorkspaceFeatureModel;
import org.eclipse.pde.internal.core.ifeature.*;
import org.eclipse.pde.internal.launching.launcher.BundleLauncherHelper;
import org.eclipse.pde.internal.ui.wizards.feature.AbstractCreateFeatureOperation;
import org.eclipse.pde.internal.ui.wizards.feature.FeatureData;
import org.eclipse.pde.launching.IPDELauncherConstants;
import org.eclipse.pde.ui.tests.util.TargetPlatformUtil;
import org.junit.*;
import org.junit.rules.TemporaryFolder;
public class FeatureBasedLaunchTest extends AbstractLaunchTest {
@Rule
public TemporaryFolder folder = new TemporaryFolder();
private Path tpJarDirectory;
@Before
public void setup() throws Exception {
tpJarDirectory = folder.newFolder("TPJarDirectory").toPath();
}
// --- tests ---
@Test
public void testGetMergedBundleMap_autostartLevels() throws Throwable {
TargetPlatformUtil.setRunningPlatformAsTarget();
createFeatureProject(FeatureBasedLaunchTest.class.getName() + "-feature", "1.0.0", f -> {
addIncludedPlugin(f, "javax.inject", DEFAULT_VERSION);
addIncludedPlugin(f, "org.eclipse.core.runtime", DEFAULT_VERSION);
addIncludedPlugin(f, "org.eclipse.ui", DEFAULT_VERSION);
});
ILaunchConfiguration lc = getLaunchConfiguration("feature-based-with-startlevels.launch");
Map<IPluginModelBase, String> bundleMap = BundleLauncherHelper.getMergedBundleMap(lc, false);
Map<String, String> byId = new LinkedHashMap<>();
for (Entry<IPluginModelBase, String> entry : bundleMap.entrySet()) {
byId.put(entry.getKey().getPluginBase().getId(), entry.getValue());
}
assertThat(byId)//
.as("old entry without configuration has defaults").containsEntry("javax.inject", "default:default")
.as("use configured start-levels").containsEntry("org.eclipse.core.runtime", "1:true")
.as("ignore configured start-levels of uncheckedplugin")
.containsEntry("org.eclipse.ui", "default:default");
}
// --- defined feature selection ---
@Test
public void testGetMergedBundleMap_featureSelectionForLocationWorkspace_latestWorkspaceFeature() throws Throwable {
var targetBundles = ofEntries( //
bundle("plugin.a", "1.0.0"), //
bundle("plugin.b", "1.0.0"), //
bundle("plugin.c", "1.0.0"), //
bundle("plugin.d", "1.0.0"));
createFeatureProject("feature.a", "2.0.0", f -> {
addIncludedPlugin(f, "plugin.a", "1.0.0");
});
createFeatureProject("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.b", "1.0.0");
});
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.a", "2.0.0", f -> {
addIncludedPlugin(f, "plugin.c", "1.0.0");
}), //
targetFeature("feature.z", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.d", "1.0.0");
}));
setTargetPlatform(targetBundles, targetFeatures);
ILaunchConfigurationWorkingCopy wc = createFeatureLaunchConfig();
wc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:default"));
wc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE);
assertGetMergedBundleMap(wc, Set.of( //
targetBundle("plugin.a", "1.0.0")));
}
@Test
public void testGetMergedBundleMap_featureSelectionForLocationWorkspaceButNoWorkspaceFeaturePresent_latestExternalFeature()
throws Throwable {
var targetBundles = ofEntries( //
bundle("plugin.a", "1.0.0"), //
bundle("plugin.b", "1.0.0"), //
bundle("plugin.c", "1.0.0"));
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.a", "2.0.0", f -> {
addIncludedPlugin(f, "plugin.a", "1.0.0");
}), //
targetFeature("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.b", "1.0.0");
}), //
targetFeature("feature.z", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.c", "1.0.0");
}));
setTargetPlatform(targetBundles, targetFeatures);
ILaunchConfigurationWorkingCopy wc = createFeatureLaunchConfig();
wc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:default"));
wc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE);
assertGetMergedBundleMap(wc, Set.of( //
targetBundle("plugin.a", "1.0.0")));
}
@Test
public void testGetMergedBundleMap_featureSelectionForLocationExternal_latestExternalFeature() throws Throwable {
var targetBundles = ofEntries( //
bundle("plugin.a", "1.0.0"), //
bundle("plugin.b", "1.0.0"), //
bundle("plugin.c", "1.0.0"), //
bundle("plugin.d", "1.0.0"));
createFeatureProject("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a", "1.0.0");
});
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.a", "2.0.0", f -> {
addIncludedPlugin(f, "plugin.b", "1.0.0");
}), //
targetFeature("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.c", "1.0.0");
}), //
targetFeature("feature.z", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.d", "1.0.0");
}));
setTargetPlatform(targetBundles, targetFeatures);
ILaunchConfigurationWorkingCopy wc = createFeatureLaunchConfig();
wc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:default"));
wc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_EXTERNAL);
assertGetMergedBundleMap(wc, Set.of( //
targetBundle("plugin.b", "1.0.0")));
}
@Test
public void testGetMergedBundleMap_featureSelectionForLocationExternalButNoExternalFeaturePresent_noFeature()
throws Throwable {
var targetBundles = ofEntries( //
bundle("plugin.a", "1.0.0"));
createFeatureProject("feature.a", "2.0.0", f -> {
addIncludedPlugin(f, "plugin.a", "1.0.0");
});
List<NameVersionDescriptor> targetFeatures = List.of();
setTargetPlatform(targetBundles, targetFeatures);
ILaunchConfigurationWorkingCopy wc = createFeatureLaunchConfig();
wc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:default"));
wc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_EXTERNAL);
assertGetMergedBundleMap(wc, emptySet());
}
// --- included plug-ins ---
@Test
public void testGetMergedBundleMap_includedPluginWithDefaultVersion() throws Throwable {
createPluginProject("plugin.a", "1.0.0");
createPluginProject("plugin.a", "1.0.1");
createPluginProject("plugin.b", "1.0.0");
var targetBundles = ofEntries( //
bundle("plugin.a", "2.0.0"), //
bundle("plugin.a", "2.0.1"), //
bundle("plugin.c", "1.0.0"));
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a", DEFAULT_VERSION);
}), //
targetFeature("feature.b", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.b", DEFAULT_VERSION);
}), //
targetFeature("feature.c", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.c", DEFAULT_VERSION);
}));
setTargetPlatform(targetBundles, targetFeatures);
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:workspace"));
assertGetMergedBundleMap("pluginResolution explicit workspace", lc, Set.of( //
workspaceBundle("plugin.a", "1.0.1")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:external"));
assertGetMergedBundleMap("pluginResolution explicit external", lc, Set.of( //
targetBundle("plugin.a", "2.0.1")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION, IPDELauncherConstants.LOCATION_WORKSPACE);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:default"));
assertGetMergedBundleMap("pluginResolution default workspace", lc, Set.of( //
workspaceBundle("plugin.a", "1.0.1")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION, IPDELauncherConstants.LOCATION_EXTERNAL);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:default"));
assertGetMergedBundleMap("pluginResolution default external", lc, Set.of( //
targetBundle("plugin.a", "2.0.1")));
}
// Plug-ins only in secondary location
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.b:external"));
assertGetMergedBundleMap("pluginResolution explicit workspace", lc, Set.of( //
workspaceBundle("plugin.b", "1.0.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.c:workspace"));
assertGetMergedBundleMap("pluginResolution explicit external", lc, Set.of( //
targetBundle("plugin.c", "1.0.0")));
}
}
@Test
public void testGetMergedBundleMap_includedPluginWithSpecificVersion() throws Throwable {
createPluginProject("plugin.a", "1.0.0");
createPluginProject("plugin.a", "1.2.0");
var targetBundles = ofEntries( //
bundle("plugin.a", "1.0.0"), //
bundle("plugin.a", "1.1.0"), //
bundle("plugin.z", "1.0.0"));
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a", "1.0.0");
}), //
targetFeature("feature.b", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a", "2.0.0");
}), //
targetFeature("feature.c", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a", "1.1.0");
}), //
targetFeature("feature.d", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a", "1.2.0");
}), //
targetFeature("feature.e", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a", "1.0.0.someQualifier");
}));
setTargetPlatform(targetBundles, targetFeatures);
// Perfect version-match in primary location (while secondary location
// has matches too)
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:workspace"));
assertGetMergedBundleMap("pluginResolution explicit workspace", lc, Set.of( //
workspaceBundle("plugin.a", "1.0.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:external"));
assertGetMergedBundleMap("pluginResolution explicit external", lc, Set.of( //
targetBundle("plugin.a", "1.0.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION, IPDELauncherConstants.LOCATION_WORKSPACE);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:default"));
assertGetMergedBundleMap("pluginResolution default workspace", lc, Set.of( //
workspaceBundle("plugin.a", "1.0.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION, IPDELauncherConstants.LOCATION_EXTERNAL);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:default"));
assertGetMergedBundleMap("pluginResolution default external", lc, Set.of( //
targetBundle("plugin.a", "1.0.0")));
}
// Unqualified version-match in primary location
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.e:workspace"));
assertGetMergedBundleMap("pluginResolution explicit workspace", lc, Set.of( //
workspaceBundle("plugin.a", "1.0.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.e:external"));
assertGetMergedBundleMap("pluginResolution explicit external", lc, Set.of( //
targetBundle("plugin.a", "1.0.0")));
}
// no version-match at all (for included plug-ins the latest plug-in of
// a location is added if there is no match in the primary location)
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.b:workspace"));
assertGetMergedBundleMap("pluginResolution workspace no match", lc, Set.of( //
workspaceBundle("plugin.a", "1.2.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.b:external"));
assertGetMergedBundleMap("pluginResolution external no match", lc, Set.of( //
targetBundle("plugin.a", "1.1.0")));
}
// Perfect version match only in secondary location (but another version
// is present in the primary too).
// To be able to conveniently override a specific version of a plug-in,
// which is actually included by a (transitive) feature from the TP, by
// a plug-in from the workspace (which happens frequently when
// one has just updated the plug-in version) the latest plug-in from the
// primary location is taken if one is present and none matches the
// specified version exactly (with or without considering qualifiers).
// See also https://bugs.eclipse.org/bugs/show_bug.cgi?id=576887
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.c:workspace"));
assertGetMergedBundleMap("pluginResolution workspace but match is external", lc, Set.of( //
workspaceBundle("plugin.a", "1.2.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.d:external"));
assertGetMergedBundleMap("pluginResolution external but match is in workspace", lc, Set.of( //
targetBundle("plugin.a", "1.1.0")));
}
}
// --- included features ---
@Test
public void testGetMergedBundleMap_includedFeatureWithDefaultVersion() throws Throwable {
// plug-in names contain version-like suffixes to have no chance to
// interfere with conveniences of plug-in resolution
var targetBundles = ofEntries( //
bundle("plugin.a.e.100", "1.0.0"), //
bundle("plugin.a.w.101", "1.0.0"), //
bundle("plugin.z", "1.0.0"));
createFeatureProject("feature.a", "1.0.3", f -> {
addIncludedPlugin(f, "plugin.a.w.101", "1.0.0");
});
createFeatureProject("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.z", "1.0.0");
});
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.z", "1.0.0", f -> {
addIncludedFeature(f, "feature.a", DEFAULT_VERSION);
}), //
targetFeature("feature.a", "1.0.2", f -> {
addIncludedPlugin(f, "plugin.a.e.100", "1.0.0");
}), //
targetFeature("feature.a", "1.0.1", f -> {
addIncludedPlugin(f, "plugin.z", "1.0.0");
}));
setTargetPlatform(targetBundles, targetFeatures);
{
ILaunchConfigurationWorkingCopy lcW = createFeatureLaunchConfig();
lcW.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.z:default"));
lcW.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE);
assertGetMergedBundleMap("feature-location workspace", lcW, Set.of( //
targetBundle("plugin.a.w.101", "1.0.0")));
}
{
ILaunchConfigurationWorkingCopy lcE = createFeatureLaunchConfig();
lcE.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.z:default"));
lcE.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_EXTERNAL);
assertGetMergedBundleMap("feature-location external", lcE, Set.of( //
targetBundle("plugin.a.e.100", "1.0.0")));
}
}
@Test
public void testGetMergedBundleMap_includedFeatureWithSpecificVersion() throws Throwable {
// plug-in names contain version-like suffixes to have no chance to
// interfere with conveniences of plug-in resolution
var targetBundles = ofEntries( //
bundle("plugin.a.w.100", "1.0.0"), //
bundle("plugin.a.w.300", "1.0.0"), //
bundle("plugin.a.e.100", "1.0.0"), //
bundle("plugin.a.e.200", "1.0.0"), //
bundle("plugin.a.e.400", "1.0.0"), //
bundle("plugin.z", "1.0.0"));
createFeatureProject("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a.w.100", "1.0.0");
});
createFeatureProject("feature.a", "3.0.0", f -> {
addIncludedPlugin(f, "plugin.a.w.300", "1.0.0");
});
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a.e.100", "1.0.0");
}), //
targetFeature("feature.a", "2.0.0", f -> {
addIncludedPlugin(f, "plugin.a.e.200", "1.0.0");
}), //
targetFeature("feature.a", "4.0.0", f -> {
addIncludedPlugin(f, "plugin.a.e.400", "1.0.0");
}), //
targetFeature("feature.b", "1.0.0", f -> {
addIncludedFeature(f, "feature.a", "1.0.0");
}), //
targetFeature("feature.c", "1.0.0", f -> {
addIncludedFeature(f, "feature.a", "1.2.0");
}), //
targetFeature("feature.d", "1.0.0", f -> {
addIncludedFeature(f, "feature.a", "2.0.0");
}), //
targetFeature("feature.e", "1.0.0", f -> {
addIncludedFeature(f, "feature.a", "3.0.0");
}), //
targetFeature("feature.f", "1.0.0", f -> {
addIncludedFeature(f, "feature.a", "1.0.0.someQualifier");
}));
setTargetPlatform(targetBundles, targetFeatures);
// Perfect version-match in primary location (while secondary location
// has matches too)
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.b:default"));
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE);
assertGetMergedBundleMap("feature-location workspace", lc, Set.of( //
targetBundle("plugin.a.w.100", "1.0.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.b:default"));
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_EXTERNAL);
assertGetMergedBundleMap("feature-location external", lc, Set.of( //
targetBundle("plugin.a.e.100", "1.0.0")));
}
// Unqualified version-match in primary location
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.f:default"));
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE);
assertGetMergedBundleMap("feature-location workspace", lc, Set.of( //
targetBundle("plugin.a.w.100", "1.0.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.f:default"));
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_EXTERNAL);
assertGetMergedBundleMap("feature-location external", lc, Set.of( //
targetBundle("plugin.a.e.100", "1.0.0")));
}
// no version-match at all (for included features the latest features of
// a location is added if there is no match in the primary location)
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.c:default"));
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE);
assertGetMergedBundleMap("feature-location workspace no match", lc, Set.of( //
targetBundle("plugin.a.w.300", "1.0.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.c:default"));
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_EXTERNAL);
assertGetMergedBundleMap("feature-location external no match", lc, Set.of( //
targetBundle("plugin.a.e.400", "1.0.0")));
}
// Perfect version match only in secondary location (but another version
// is present in the primary too).
// To be able to conveniently override a specific version of a feature,
// which is actually included (transitively) by a feature from the TP,
// by a feature from the workspace (which happens frequently when
// one has just updated the feature version) the latest feature from the
// primary location is taken if one is present and none matches the
// specified version exactly (with or without considering qualifiers).
// See also https://bugs.eclipse.org/bugs/show_bug.cgi?id=576887
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.d:default"));
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE);
assertGetMergedBundleMap("feature-location workspace but exact match is external", lc, Set.of( //
targetBundle("plugin.a.w.300", "1.0.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.e:default"));
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_EXTERNAL);
assertGetMergedBundleMap("feature-location external but exact match is in workspace", lc, Set.of(//
targetBundle("plugin.a.e.400", "1.0.0")));
}
}
// --- required/imported plug-in dependencies ---
@Test
public void testGetMergedBundleMap_requiredPluginWithNoVersion() throws Throwable {
createPluginProject("plugin.a", "1.0.0");
createPluginProject("plugin.a", "1.1.0");
createPluginProject("plugin.b", "1.0.0");
var targetBundles = ofEntries( //
bundle("plugin.a", "2.0.0"), //
bundle("plugin.a", "2.1.0"), //
bundle("plugin.c", "1.0.0"));
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.a", "1.0.0", f -> {
addRequiredPlugin(f, "plugin.a", null, IMatchRules.NONE);
}), //
targetFeature("feature.b", "1.0.0", f -> {
addRequiredPlugin(f, "plugin.b", null, IMatchRules.NONE);
}), //
targetFeature("feature.c", "1.0.0", f -> {
addRequiredPlugin(f, "plugin.c", null, IMatchRules.NONE);
}));
setTargetPlatform(targetBundles, targetFeatures);
// explicit pluginResolution location
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:workspace"));
assertGetMergedBundleMap("pluginResolution explicit workspace", lc, Set.of( //
workspaceBundle("plugin.a", "1.1.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:external"));
assertGetMergedBundleMap("pluginResolution explicit external", lc, Set.of( //
targetBundle("plugin.a", "2.1.0")));
}
// default pluginResolution location
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION, IPDELauncherConstants.LOCATION_WORKSPACE);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:default"));
assertGetMergedBundleMap("pluginResolution default workspace", lc, Set.of( //
workspaceBundle("plugin.a", "1.1.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION, IPDELauncherConstants.LOCATION_EXTERNAL);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:default"));
assertGetMergedBundleMap("pluginResolution default external", lc, Set.of( //
targetBundle("plugin.a", "2.1.0")));
}
// Plug-ins only in secondary location
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.b:external"));
assertGetMergedBundleMap("pluginResolution external but match in workspace", lc, Set.of( //
workspaceBundle("plugin.b", "1.0.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.c:workspace"));
assertGetMergedBundleMap("pluginResolution workspace but match is external", lc, Set.of( //
targetBundle("plugin.c", "1.0.0")));
}
}
@Test
public void testGetMergedBundleMap_requiredPluginWithSpecificVersion1() throws Throwable {
createPluginProject("plugin.a", "1.0.0");
createPluginProject("plugin.a", "1.1.2");
createPluginProject("plugin.a", "2.0.0");
var targetBundles = ofEntries( //
bundle("plugin.a", "1.0.0"), //
bundle("plugin.a", "1.2.3"), //
bundle("plugin.a", "3.0.0"), //
bundle("plugin.z", "1.0.0"));
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.a", "1.0.0", f -> {
addRequiredPlugin(f, "plugin.a", "1.0.0", IMatchRules.NONE);
}), //
targetFeature("feature.b", "1.0.0", f -> {
addRequiredPlugin(f, "plugin.a", "1.0.0", IMatchRules.COMPATIBLE);
}));
setTargetPlatform(targetBundles, targetFeatures);
// MatchRule NONE/COMPATIBLE (behave the same according to VersionUtil)
// and location resolution tests.
// explicit pluginResolution location
{
for (String feature : List.of("feature.a", "feature.b")) {
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of(feature + ":workspace"));
assertGetMergedBundleMap("pluginResolution explicit workspace", lc, Set.of( //
workspaceBundle("plugin.a", "1.1.2")));
}
}
{
for (String feature : List.of("feature.a", "feature.b")) {
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of(feature + ":external"));
assertGetMergedBundleMap("pluginResolution explicit external", lc, Set.of( //
targetBundle("plugin.a", "1.2.3")));
}
}
// default pluginResolution location
{
for (String feature : List.of("feature.a", "feature.b")) {
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION,
IPDELauncherConstants.LOCATION_WORKSPACE);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of(feature + ":default"));
assertGetMergedBundleMap("pluginResolution default workspace", lc, Set.of( //
workspaceBundle("plugin.a", "1.1.2")));
}
}
{
for (String feature : List.of("feature.a", "feature.b")) {
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION,
IPDELauncherConstants.LOCATION_EXTERNAL);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of(feature + ":default"));
assertGetMergedBundleMap("pluginResolution default external", lc, Set.of( //
targetBundle("plugin.a", "1.2.3")));
}
}
}
@Test
public void testGetMergedBundleMap_requiredPluginWithSpecificVersion2() throws Throwable {
createPluginProject("plugin.a", "1.0.0");
createPluginProject("plugin.a", "1.0.1");
createPluginProject("plugin.a", "1.1.0");
createPluginProject("plugin.a", "1.1.2");
createPluginProject("plugin.a", "2.0.0");
var targetBundles = ofEntries( //
bundle("plugin.a", "1.0.0"), //
bundle("plugin.a", "1.0.2"), //
bundle("plugin.a", "1.2.0"), //
bundle("plugin.a", "1.2.3"), //
bundle("plugin.a", "3.0.0"), //
bundle("plugin.z", "1.0.0"));
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.c", "1.0.0", f -> {
addRequiredPlugin(f, "plugin.a", "1.0.0", IMatchRules.EQUIVALENT);
}), //
targetFeature("feature.d", "1.0.0", f -> {
addRequiredPlugin(f, "plugin.a", "1.1.0", IMatchRules.EQUIVALENT);
}), //
targetFeature("feature.e", "1.0.0", f -> {
addRequiredPlugin(f, "plugin.a", "1.2.0", IMatchRules.EQUIVALENT);
}), //
targetFeature("feature.f", "1.0.0", f -> {
addRequiredPlugin(f, "plugin.a", "8.0.0", IMatchRules.EQUIVALENT);
}));
setTargetPlatform(targetBundles, targetFeatures);
// No need to test all match-rules. Just have to check that version
// match rules are obeyed. For the following cases match-rule EQUIVALENT
// is used to check different per-location-availability scenarios.
// match in primary location (while secondary location has matches too)
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.c:workspace"));
assertGetMergedBundleMap("pluginResolution workspace", lc, Set.of( //
workspaceBundle("plugin.a", "1.0.1")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.c:external"));
assertGetMergedBundleMap("pluginResolution external", lc, Set.of( //
targetBundle("plugin.a", "1.0.2")));
}
// match only in secondary location
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.d:external"));
assertGetMergedBundleMap("pluginResolution external but match in workspace", lc, Set.of( //
workspaceBundle("plugin.a", "1.1.2")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.e:workspace"));
assertGetMergedBundleMap("pluginResolution workspace but match is external", lc, Set.of( //
targetBundle("plugin.a", "1.2.3")));
}
// no match at all
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.f:external"));
assertGetMergedBundleMap("pluginResolution external no match", lc, Set.of());
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.f:workspace"));
assertGetMergedBundleMap("pluginResolution workspace no match", lc, Set.of());
}
}
// --- required/imported feature dependencies ---
@Test
public void testGetMergedBundleMap_requiredFeatureWithNoVersion() throws Throwable {
// plug-in names contain version-like suffixes to have no chance to
// interfere with conveniences of plug-in resolution
var targetBundles = ofEntries( //
bundle("plugin.a.w.100", "1.0.0"), //
bundle("plugin.a.w.110", "1.0.0"), //
bundle("plugin.a.e.200", "1.0.0"), //
bundle("plugin.a.e.210", "1.0.0"), //
bundle("plugin.b.w.100", "1.0.0"), //
bundle("plugin.c.e.100", "1.0.0"), //
bundle("plugin.xyz", "1.0.0"));
createFeatureProject("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a.w.100", "1.0.0");
});
createFeatureProject("feature.a", "1.1.0", f -> {
addIncludedPlugin(f, "plugin.a.w.110", "1.0.0");
});
createFeatureProject("feature.b", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.b.w.100", "1.0.0");
});
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.z", "1.0.0", f -> {
addRequiredFeature(f, "feature.a", null, IMatchRules.NONE);
}), //
targetFeature("feature.y", "1.0.0", f -> {
addRequiredFeature(f, "feature.b", null, IMatchRules.NONE);
}), //
targetFeature("feature.x", "1.0.0", f -> {
addRequiredFeature(f, "feature.c", null, IMatchRules.NONE);
}), //
targetFeature("feature.a", "2.0.0", f -> {
addIncludedPlugin(f, "plugin.a.e.200", "1.0.0");
}), //
targetFeature("feature.a", "2.1.0", f -> {
addIncludedPlugin(f, "plugin.a.e.210", "1.0.0");
}), //
targetFeature("feature.c", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.c.e.100", "1.0.0");
}));
setTargetPlatform(targetBundles, targetFeatures);
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.z:default"));
assertGetMergedBundleMap("featureResolution explicit workspace", lc, Set.of( //
targetBundle("plugin.a.w.110", "1.0.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_EXTERNAL);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.z:default"));
assertGetMergedBundleMap("featureResolution explicit external", lc, Set.of( //
targetBundle("plugin.a.e.210", "1.0.0")));
}
// Features only in secondary location
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_EXTERNAL);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.y:default"));
assertGetMergedBundleMap("featureResolution external but match in workspace", lc, Set.of());
// if featureResolution is 'external', workspace features are not
// considered at all
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.x:default"));
assertGetMergedBundleMap("featureResolution workspace but match is external", lc, Set.of( //
targetBundle("plugin.c.e.100", "1.0.0")));
}
}
@Test
public void testGetMergedBundleMap_requiredFeatureWithSpecificVersion1() throws Throwable {
// plug-in names contain version-like suffixes to have no chance to
// interfere with conveniences of plug-in resolution
var targetBundles = ofEntries( //
bundle("plugin.a.w.100", "1.0.0"), //
bundle("plugin.a.w.110", "1.0.0"), //
bundle("plugin.a.w.200", "1.0.0"), //
bundle("plugin.a.e.100", "1.0.0"), //
bundle("plugin.a.e.123", "1.0.0"), //
bundle("plugin.a.e.300", "1.0.0"), //
bundle("plugin.xyz", "1.0.0"));
createFeatureProject("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a.w.100", "1.0.0");
});
createFeatureProject("feature.a", "1.1.0", f -> {
addIncludedPlugin(f, "plugin.a.w.110", "1.0.0");
});
createFeatureProject("feature.a", "2.0.0", f -> {
addIncludedPlugin(f, "plugin.a.w.200", "1.0.0");
});
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.z", "1.0.0", f -> {
addRequiredFeature(f, "feature.a", "1.0.0", IMatchRules.NONE);
}), //
targetFeature("feature.y", "1.0.0", f -> {
addRequiredFeature(f, "feature.a", "1.0.0", IMatchRules.COMPATIBLE);
}), //
targetFeature("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a.e.100", "1.0.0");
}), //
targetFeature("feature.a", "1.2.3", f -> {
addIncludedPlugin(f, "plugin.a.e.123", "1.0.0");
}), //
targetFeature("feature.a", "3.0.0", f -> {
addIncludedPlugin(f, "plugin.a.e.300", "1.0.0");
}));
setTargetPlatform(targetBundles, targetFeatures);
// MatchRule NONE/COMPATIBLE (behave the same according to VersionUtil)
// and location resolution tests.
// explicit pluginResolution location
{
for (String feature : List.of("feature.z", "feature.y")) {
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION,
IPDELauncherConstants.LOCATION_WORKSPACE);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of(feature + ":default"));
assertGetMergedBundleMap("pluginResolution explicit workspace", lc, Set.of( //
targetBundle("plugin.a.w.110", "1.0.0")));
}
}
{
for (String feature : List.of("feature.z", "feature.y")) {
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION,
IPDELauncherConstants.LOCATION_EXTERNAL);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of(feature + ":default"));
assertGetMergedBundleMap("pluginResolution explicit external", lc, Set.of( //
targetBundle("plugin.a.e.123", "1.0.0")));
}
}
}
@Test
public void testGetMergedBundleMap_requiredFeatureWithSpecificVersion2() throws Throwable {
var targetBundles = ofEntries( //
bundle("plugin.a.w.100", "1.0.0"), //
bundle("plugin.a.w.101", "1.0.0"), //
bundle("plugin.a.w.110", "1.0.0"), //
bundle("plugin.a.w.112", "1.0.0"), //
bundle("plugin.a.w.200", "1.0.0"), //
bundle("plugin.a.e.100", "1.0.0"), //
bundle("plugin.a.e.102", "1.0.0"), //
bundle("plugin.a.e.120", "1.0.0"), //
bundle("plugin.a.e.123", "1.0.0"), //
bundle("plugin.a.e.300", "1.0.0"), //
bundle("plugin.xyz", "1.0.0"));
createFeatureProject("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a.w.100", "1.0.0");
});
createFeatureProject("feature.a", "1.0.1", f -> {
addIncludedPlugin(f, "plugin.a.w.101", "1.0.0");
});
createFeatureProject("feature.a", "1.1.0", f -> {
addIncludedPlugin(f, "plugin.a.w.110", "1.0.0");
});
createFeatureProject("feature.a", "1.1.2", f -> {
addIncludedPlugin(f, "plugin.a.w.112", "1.0.0");
});
createFeatureProject("feature.a", "2.0.0", f -> {
addIncludedPlugin(f, "plugin.a.w.200", "1.0.0");
});
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.z", "1.0.0", f -> {
addRequiredFeature(f, "feature.a", "1.0.0", IMatchRules.EQUIVALENT);
}), //
targetFeature("feature.y", "1.0.0", f -> {
addRequiredFeature(f, "feature.a", "1.1.0", IMatchRules.EQUIVALENT);
}), //
targetFeature("feature.x", "1.0.0", f -> {
addRequiredFeature(f, "feature.a", "1.2.0", IMatchRules.EQUIVALENT);
}), //
targetFeature("feature.w", "1.0.0", f -> {
addRequiredFeature(f, "feature.a", "8.0.0", IMatchRules.EQUIVALENT);
}), //
targetFeature("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a.e.100", "1.0.0");
}), //
targetFeature("feature.a", "1.0.2", f -> {
addIncludedPlugin(f, "plugin.a.e.102", "1.0.0");
}), //
targetFeature("feature.a", "1.2.0", f -> {
addIncludedPlugin(f, "plugin.a.e.120", "1.0.0");
}), //
targetFeature("feature.a", "1.2.3", f -> {
addIncludedPlugin(f, "plugin.a.e.123", "1.0.0");
}), //
targetFeature("feature.a", "3.0.0", f -> {
addIncludedPlugin(f, "plugin.a.e.300", "1.0.0");
}));
setTargetPlatform(targetBundles, targetFeatures);
// No need to test all match-rules. Just have to check that version
// match rules are obeyed. For the following cases match-rule EQUIVALENT
// is used to check different per-location-availability scenarios.
// match in primary location (while secondary location has matches too)
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.z:default"));
assertGetMergedBundleMap("featureResolution workspace", lc, Set.of( //
targetBundle("plugin.a.w.101", "1.0.0")));
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_EXTERNAL);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.z:default"));
assertGetMergedBundleMap("featureResolution external", lc, Set.of( //
targetBundle("plugin.a.e.102", "1.0.0")));
}
// match only in secondary location
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_EXTERNAL);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.y:default"));
assertGetMergedBundleMap("featureResolution external but match in workspace", lc, Set.of());
// if featureResolution is 'external', workspace features are not
// considered at all
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.x:default"));
assertGetMergedBundleMap("featureResolution workspace but match is external", lc, Set.of( //
targetBundle("plugin.a.e.123", "1.0.0")));
}
// no match at all
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_EXTERNAL);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.w:default"));
assertGetMergedBundleMap("featureResolution external no match", lc, Set.of());
}
{
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE);
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.w:default"));
assertGetMergedBundleMap("featureResolution workspace no match", lc, Set.of());
}
}
// --- miscellaneous cases ---
@Test
public void testGetMergedBundleMap_includedPluginAndFeatureEnvironmentNotMatchingTargetEnvironment()
throws Throwable {
String thisOS = Platform.getOS();
String otherOS = thisOS.equals(Platform.OS_LINUX) ? Platform.OS_WIN32 : Platform.OS_LINUX;
var targetBundles = ofEntries( //
bundle("plugin.a", "1.0.0"), //
bundle("plugin.b", "1.0.0"), //
bundle("plugin.c", "1.0.0"), //
bundle("plugin.d", "1.0.0"), //
bundle("plugin.z", "1.0.0"));
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.c", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.c", "1.0.0");
}), //
targetFeature("feature.d", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.d", "1.0.0");
}), //
targetFeature("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a", "1.0.0").setOS(thisOS);
addIncludedPlugin(f, "plugin.b", "1.0.0").setOS(otherOS);
addIncludedFeature(f, "feature.c", "1.0.0").setOS(thisOS);
addIncludedFeature(f, "feature.d", "1.0.0").setOS(otherOS);
}));
setTargetPlatform(targetBundles, targetFeatures);
// No need to test all aspects of environment matches, just test if
// environment is considered.
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:external"));
assertGetMergedBundleMap("pluginResolution explicit workspace", lc, Set.of( //
targetBundle("plugin.a", "1.0.0"), //
targetBundle("plugin.c", "1.0.0")));
}
@Test
public void testGetMergedBundleMap_featureEnvironmentNotMatchingTargetEnvironment() throws Throwable {
String thisOS = Platform.getOS();
String otherOS = thisOS.equals(Platform.OS_LINUX) ? Platform.OS_WIN32 : Platform.OS_LINUX;
var targetBundles = ofEntries( //
bundle("plugin.b", "1.0.0"), //
bundle("plugin.b", "1.1.0"), //
bundle("plugin.c", "1.0.0"), //
bundle("plugin.c", "1.1.0"), //
bundle("plugin.z", "1.0.0"));
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.b", "1.0.0", f -> {
f.setOS(thisOS);
addIncludedPlugin(f, "plugin.b", "1.0.0");
}), //
targetFeature("feature.b", "1.1.0", f -> {
f.setOS(otherOS);
addIncludedPlugin(f, "plugin.b", "1.1.0");
}), //
targetFeature("feature.c", "1.0.0", f -> {
f.setOS(thisOS);
addIncludedPlugin(f, "plugin.c", "1.0.0");
}), //
targetFeature("feature.c", "1.1.0", f -> {
f.setOS(otherOS);
addIncludedPlugin(f, "plugin.c", "1.1.0");
}), //
targetFeature("feature.a", "1.0.0", f -> {
addIncludedFeature(f, "feature.b", DEFAULT_VERSION);
addRequiredFeature(f, "feature.c", "1.0.0", IMatchRules.COMPATIBLE);
}));
setTargetPlatform(targetBundles, targetFeatures);
// No need to test all aspects of environment matches, just test if
// environment is considered.
ILaunchConfigurationWorkingCopy lc = createFeatureLaunchConfig();
lc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of("feature.a:external"));
assertGetMergedBundleMap("pluginResolution explicit workspace", lc, Set.of( //
targetBundle("plugin.b", "1.0.0"), //
targetBundle("plugin.c", "1.0.0")));
}
@Test
public void testGetMergedBundleMap_multipleInclusionOfPluginAndFeature() throws Throwable {
var targetBundles = ofEntries( //
bundle("plugin.a", "1.0.0"), //
bundle("plugin.b", "1.0.0"), //
bundle("plugin.z", "1.0.0"));
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a", "1.0.0");
addRequiredPlugin(f, "plugin.a", "1.0.0", IMatchRules.PERFECT);
}), //
targetFeature("feature.b", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.a", "1.0.0");
addRequiredPlugin(f, "plugin.a", "1.0.0", IMatchRules.PERFECT);
}), //
targetFeature("feature.c", "1.0.0", f -> {
addIncludedFeature(f, "feature.z", "1.0.0");
addRequiredFeature(f, "feature.z", "1.0.0", IMatchRules.PERFECT);
}), //
targetFeature("feature.d", "1.0.0", f -> {
addIncludedFeature(f, "feature.z", "1.0.0");
addRequiredFeature(f, "feature.z", "1.0.0", IMatchRules.PERFECT);
}), //
targetFeature("feature.z", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.z", "1.0.0");
}));
setTargetPlatform(targetBundles, targetFeatures);
ILaunchConfigurationWorkingCopy wc = createFeatureLaunchConfig();
wc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of( //
"feature.a:default", //
"feature.b:default", //
"feature.c:default", //
"feature.d:default"));
assertGetMergedBundleMap(wc, Set.of( //
targetBundle("plugin.a", "1.0.0"), //
targetBundle("plugin.z", "1.0.0")));
}
@Test
public void testGetMergedBundleMap_additionalPlugins() throws Throwable {
createPluginProject("plugin.a", "1.0.0");
createPluginProject("plugin.b", "1.0.0");
createPluginProject("plugin.d", "1.0.0");
createPluginProject("plugin.e", "1.0.0");
var targetBundles = ofEntries( //
bundle("plugin.a", "1.0.0"), //
bundle("plugin.b", "1.0.0"), //
bundle("plugin.c", "1.0.0"), //
bundle("plugin.d", "1.0.0"), //
bundle("plugin.e", "1.0.0"), //
bundle("plugin.f", "2.0.0"), //
bundle("plugin.z", "1.0.0"));
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.a", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.d", "1.0.0");
}), //
targetFeature("feature.b", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.e", "1.0.0");
}));
setTargetPlatform(targetBundles, targetFeatures);
ILaunchConfigurationWorkingCopy wc = createFeatureLaunchConfig();
wc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of( //
"feature.a:external", //
"feature.b:workspace"));
wc.setAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION, IPDELauncherConstants.LOCATION_EXTERNAL);
wc.setAttribute(IPDELauncherConstants.ADDITIONAL_PLUGINS, Set.of( //
// id:version:location:enabled:startLeval:autoStart
"plugin.a:1.0.0:default:true:1:true", //
"plugin.b:1.0.0:workspace:true:2:true", //
"plugin.c:1.0.0:workspace:true:3:true", //
"plugin.d:1.0.0:external:true:4:true", // overwrite from feature
"plugin.e:1.0.0:external:true:5:true", // attempted overwrite
"plugin.f:1.0.0:default:true:6:true", // not matching version
"plugin.z:1.0.0:external:false:7:true") // disabled
);
// overwriting the plug-in also included by a feature only works
// if the same primary location is used and both pull in the
// same version. Otherwise two different bundles are added
assertGetMergedBundleMap(wc, Map.of( //
targetBundle("plugin.a", "1.0.0"), "1:true", //
workspaceBundle("plugin.b", "1.0.0"), "2:true", //
targetBundle("plugin.c", "1.0.0"), "3:true", //
targetBundle("plugin.d", "1.0.0"), "4:true", //
targetBundle("plugin.e", "1.0.0"), "5:true", //
workspaceBundle("plugin.e", "1.0.0"), "default:default", //
targetBundle("plugin.f", "2.0.0"), "6:true"));
}
@Test
public void testGetMergedBundleMap_inheritanceOfPluginResolution() throws Throwable {
createPluginProject("plugin.a", "1.0.0");
createPluginProject("plugin.b", "1.0.0");
createPluginProject("plugin.d", "1.0.0");
createPluginProject("plugin.e", "1.0.0");
var targetBundles = ofEntries( //
bundle("plugin.a", "1.0.0"), //
bundle("plugin.b", "1.0.0"), //
bundle("plugin.c", "1.0.0"), //
bundle("plugin.d", "1.0.0"), //
bundle("plugin.e", "1.0.0"), //
bundle("plugin.z", "1.0.0"));
List<NameVersionDescriptor> targetFeatures = List.of( //
targetFeature("feature.a", "1.0.0", f -> {
addIncludedFeature(f, "feature.b", "1.0.0");
addRequiredFeature(f, "feature.c", "", IMatchRules.COMPATIBLE);
addIncludedFeature(f, "feature.d", "1.0.0");
}), //
targetFeature("feature.b", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.b", "1.0.0");
}), //
targetFeature("feature.c", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.c", "1.0.0");
}), //
targetFeature("feature.d", "1.0.0", f -> {
addIncludedPlugin(f, "plugin.d", "1.0.0");
}));
setTargetPlatform(targetBundles, targetFeatures);
ILaunchConfigurationWorkingCopy wc = createFeatureLaunchConfig();
wc.setAttribute(IPDELauncherConstants.SELECTED_FEATURES, Set.of( //
"feature.a:external", //
"feature.d:workspace"));
assertGetMergedBundleMap(wc, Set.of( //
targetBundle("plugin.b", "1.0.0"), //
targetBundle("plugin.c", "1.0.0"), //
workspaceBundle("plugin.d", "1.0.0")));
}
// --- utility methods ---
private static interface CoreConsumer<E> {
void accept(E e) throws CoreException;
}
private static void createFeatureProject(String id, String version, CoreConsumer<IFeature> featureSetup)
throws Exception {
createFeature(id, version, id + "_" + version.replace('.', '_'), featureSetup);
}
private static IFeature createFeature(String id, String version, String projectName,
CoreConsumer<IFeature> featureSetup) throws Exception {
FeatureData featureData = new FeatureData();
featureData.id = id;
featureData.version = version;
IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
IProject project = workspaceRoot.getProject(projectName);
IPath location = workspaceRoot.getLocation().append(project.getName());
IRunnableWithProgress operation = new AbstractCreateFeatureOperation(project, location, featureData, null) {
@Override
protected void configureFeature(IFeature feature, WorkspaceFeatureModel model) throws CoreException {
featureSetup.accept(feature);
}
@Override
protected void openFeatureEditor(IFile manifestFile) {
// don't open in headless tests
}
};
operation.run(new NullProgressMonitor());
flushPendingResourceChangeEvents(project);
FeatureModelManager featureModelManager = PDECore.getDefault().getFeatureModelManager();
return featureModelManager.getFeatureModel(project).getFeature();
}
private static void flushPendingResourceChangeEvents(IProject project) throws CoreException {
// The create-feature operation above creates/modifies the feature.xml,
// but not all resource-change-events generated by the operation are
// handled when the operation exists. If these change-events are not
// handled now they are handled asynchronously by the Autobuild-job. The
// problem is that the Feature-model is reset/reload while processing
// the changes (which first sets all fields to null/zero and then
// re-reads them), but the reload is NOT guarded by corresponding locks.
// So if this happens asynchronously and the model is read inbetween the
// model state could be inconsistent, which occasionally leads to
// test-failure. Furthermore the feature-models are only added to the
// FeatureModelManager while processing the resource-changes.
// Consequently it has to be ensured that all pending resource change
// events are properly processed now and in this thread:
// -> Building the project ensures this
ResourcesPlugin.getWorkspace().run(m -> project.build(IncrementalProjectBuilder.FULL_BUILD, null), null);
}
private static void addRequiredPlugin(IFeature feature, String id, String version, int matchRule)
throws CoreException {
addImport(feature, id, version, matchRule, IFeatureImport.PLUGIN);
}
private static void addRequiredFeature(IFeature feature, String id, String version, int matchRule)
throws CoreException {
addImport(feature, id, version, matchRule, IFeatureImport.FEATURE);
}
private static void addImport(IFeature feature, String id, String version, int matchRule, int type)
throws CoreException {
IFeatureModelFactory factory = feature.getModel().getFactory();
IFeatureImport featureImport = factory.createImport();
featureImport.setId(id);
featureImport.setVersion(version);
featureImport.setMatch(matchRule);
featureImport.setType(type);
feature.addImports(new IFeatureImport[] { featureImport });
}
private static FeatureChild addIncludedFeature(IFeature feature, String id, String version) throws CoreException {
FeatureChild featureChild = (FeatureChild) feature.getModel().getFactory().createChild();
featureChild.setId(id);
featureChild.setVersion(version);
featureChild.setOptional(false);
feature.addIncludedFeatures(new IFeatureChild[] { featureChild });
return featureChild;
}
private static IFeaturePlugin addIncludedPlugin(IFeature feature, String id, String version) throws CoreException {
IFeaturePlugin featurePlugin = feature.getModel().getFactory().createPlugin();
featurePlugin.setId(id);
featurePlugin.setVersion(version);
featurePlugin.setUnpack(false);
feature.addPlugins(new IFeaturePlugin[] { featurePlugin });
return featurePlugin;
}
private NameVersionDescriptor targetFeature(String featureId, String featureVersion,
CoreConsumer<IFeature> featureSetup) throws Throwable {
IFeature feature = createFeature(featureId, featureVersion, "tp-feature-temp-project", featureSetup);
WorkspaceFeatureModel model = (WorkspaceFeatureModel) feature.getModel();
IResource resource = model.getUnderlyingResource();
Path featureDirectory = tpJarDirectory.resolve(Path.of("features", featureId + "_" + featureVersion));
Files.createDirectories(featureDirectory);
Path featureFile = featureDirectory.resolve(resource.getProjectRelativePath().toString());
try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(featureFile));) {
model.save(writer);
}
IProject project = resource.getProject();
project.delete(IResource.FORCE | IResource.ALWAYS_DELETE_PROJECT_CONTENT, null);
return new NameVersionDescriptor(featureId, featureVersion, NameVersionDescriptor.TYPE_FEATURE);
}
private void setTargetPlatform(Map<NameVersionDescriptor, Map<String, String>> bundleDescriptions,
List<NameVersionDescriptor> targetFeatures) throws Exception {
TargetPlatformUtil.setDummyBundlesAsTarget(bundleDescriptions, targetFeatures, tpJarDirectory);
}
private static ILaunchConfigurationWorkingCopy createFeatureLaunchConfig() throws CoreException {
String name = "feature-based-Eclipse-app";
ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager();
ILaunchConfigurationType type = launchManager.getLaunchConfigurationType("org.eclipse.pde.ui.RuntimeWorkbench");
ILaunchConfigurationWorkingCopy wc = type.newInstance(null, name);
wc.setAttribute(IPDELauncherConstants.USE_CUSTOM_FEATURES, true);
wc.setAttribute(IPDELauncherConstants.USE_DEFAULT, false);
wc.setAttribute(IPDELauncherConstants.FEATURE_DEFAULT_LOCATION, IPDELauncherConstants.LOCATION_WORKSPACE);
wc.setAttribute(IPDELauncherConstants.FEATURE_PLUGIN_RESOLUTION, IPDELauncherConstants.LOCATION_WORKSPACE);
return wc;
}
private static void assertGetMergedBundleMap(ILaunchConfiguration launchConfig,
Set<BundleLocationDescriptor> expectedBundles) throws Exception {
assertGetMergedBundleMap(null, launchConfig, expectedBundles);
}
private static void assertGetMergedBundleMap(String message, ILaunchConfiguration launchConfig,
Set<BundleLocationDescriptor> expectedBundles) throws Exception {
Map<BundleLocationDescriptor, String> expectedBundleMap = expectedBundles.stream()
.collect(Collectors.toMap(b -> b, b -> "default:default"));
assertGetMergedBundleMap(message, launchConfig, expectedBundleMap);
}
private static void assertGetMergedBundleMap(ILaunchConfiguration launchConfig,
Map<BundleLocationDescriptor, String> expectedBundleMap) throws Exception {
assertGetMergedBundleMap(null, launchConfig, expectedBundleMap);
}
private static void assertGetMergedBundleMap(String message, ILaunchConfiguration launchConfig,
Map<BundleLocationDescriptor, String> expectedBundleMap) throws Exception {
Map<IPluginModelBase, String> bundleMap = BundleLauncherHelper.getMergedBundleMap(launchConfig, false);
Map<IPluginModelBase, String> expectedPluginMap = new HashMap<>();
expectedBundleMap.forEach((pd, start) -> {
expectedPluginMap.put(pd.findModel(), start);
});
assertPluginMapsEquals(message, expectedPluginMap, bundleMap);
}
}