blob: 038e8b840ea5a6edfcf9ac6712ad4a296fdaf1f3 [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
* Hannes Wellmann - Bug 577116: Improve test utility method reusability
* Hannes Wellmann - Bug 577385: Add tests for Plug-in based Eclipse-App launches
* Hannes Wellmann - Bug 544838 - Unify and generalize test bundle/feature creation
*******************************************************************************/
package org.eclipse.pde.ui.tests.util;
import static java.util.Map.entry;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.Map.Entry;
import java.util.function.Predicate;
import java.util.jar.*;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.pde.core.target.*;
import org.eclipse.pde.internal.core.PDECore;
import org.eclipse.pde.ui.tests.runtime.TestUtils;
import org.junit.rules.TestRule;
import org.osgi.framework.*;
public class TargetPlatformUtil {
public static final ITargetPlatformService TPS = PDECore.getDefault().acquireService(ITargetPlatformService.class);
public static final TestRule RESTORE_CURRENT_TARGET_DEFINITION_AFTER = TestUtils.getThrowingTestRule(
TPS::getWorkspaceTargetDefinition, //
beforeTarget -> {
if (beforeTarget != TPS.getWorkspaceTargetDefinition()) {
TargetPlatformUtil.loadAndSetTargetForWorkspace(beforeTarget);
}
});
private static final String RUNNING_PLATFORM_TARGET_NAME = TargetPlatformUtil.class + "_RunningPlatformTarget";
public static void setRunningPlatformAsTarget() throws CoreException, InterruptedException {
setRunningPlatformSubSetAsTarget(RUNNING_PLATFORM_TARGET_NAME, null);
}
public static void setRunningPlatformSubSetAsTarget(String name, Predicate<Bundle> bundleFilter)
throws CoreException, InterruptedException {
ITargetDefinition currentTarget = TPS.getWorkspaceTargetDefinition();
if (name.equals(currentTarget.getName())) {
return;
}
List<ITargetLocation> bundleContainers = new ArrayList<>();
List<NameVersionDescriptor> included = addRunningPlatformBundles(bundleContainers, bundleFilter);
createAndSetTargetForWorkspace(name, bundleContainers, included);
}
public static void setRunningPlatformWithDummyBundlesAsTarget(Predicate<Bundle> rpBundleFilter,
Map<NameVersionDescriptor, Map<String, String>> bundles, Collection<NameVersionDescriptor> features,
Path jarDirectory) throws Exception {
Set<ITargetLocation> locations = new LinkedHashSet<>();
List<NameVersionDescriptor> rpBundles = addRunningPlatformBundles(locations, rpBundleFilter);
locations.add(createDummyBundlesLocation(bundles, jarDirectory));
createAndSetTargetForWorkspace(null, locations, concat(rpBundles, bundles.keySet(), features));
}
public static void loadAndSetTargetForWorkspace(ITargetDefinition target) throws InterruptedException {
Job job = new LoadTargetDefinitionJob(target);
job.schedule();
job.join();
IStatus result = job.getResult();
if (!result.isOK()) {
throw new AssertionError(result.getMessage(), result.getException());
}
}
private static List<NameVersionDescriptor> addRunningPlatformBundles(Collection<ITargetLocation> bundleContainers,
Predicate<Bundle> bundleFilter) {
Bundle[] installedBundles = FrameworkUtil.getBundle(TargetPlatformUtil.class).getBundleContext().getBundles();
List<Bundle> targetBundles = Arrays.asList(installedBundles);
if (bundleFilter != null) {
targetBundles = targetBundles.stream().filter(bundleFilter).collect(Collectors.toList());
}
var containerDirs = targetBundles.stream().map(FileLocator::getBundleFileLocation).map(Optional::orElseThrow)
.map(File::getParentFile).distinct();
containerDirs.map(dir -> TPS.newDirectoryLocation(dir.getAbsolutePath())).forEach(bundleContainers::add);
return targetBundles.stream()
.map(b -> new NameVersionDescriptor(b.getSymbolicName(), b.getVersion().toString()))
.collect(Collectors.toList());
}
public static void createAndSetTargetForWorkspace(String name, Collection<ITargetLocation> locations,
Collection<NameVersionDescriptor> includedBundles) throws InterruptedException {
ITargetDefinition targetDefinition = TPS.newTarget();
targetDefinition.setName(name);
targetDefinition.setArch(Platform.getOSArch());
targetDefinition.setOS(Platform.getOS());
targetDefinition.setWS(Platform.getWS());
targetDefinition.setNL(Platform.getNL());
targetDefinition.setTargetLocations(locations.toArray(ITargetLocation[]::new));
// only include intended target bundles to speed up resolution in cases
// where direction-location points into large bundle-pools with many
// other bundles, e.g. when one uses a Eclipse provisioned by Oomph
if (includedBundles != null) {
targetDefinition.setIncluded(includedBundles.toArray(NameVersionDescriptor[]::new));
}
loadAndSetTargetForWorkspace(targetDefinition);
}
public static void setDummyBundlesAsTarget(Map<NameVersionDescriptor, Map<String, String>> bundles,
Collection<NameVersionDescriptor> features, Path jarDirectory) throws Exception {
ITargetLocation location = createDummyBundlesLocation(bundles, jarDirectory);
createAndSetTargetForWorkspace(null, List.of(location), concat(bundles.keySet(), features));
}
private static ITargetLocation createDummyBundlesLocation(Map<NameVersionDescriptor, Map<String, String>> plugins,
Path jarDirectory) throws IOException {
Path pluginsDirectory = jarDirectory.resolve("plugins");
Files.createDirectories(pluginsDirectory);
for (Entry<NameVersionDescriptor, Map<String, String>> entry : plugins.entrySet()) {
NameVersionDescriptor bundleNameVersion = entry.getKey();
Map<String, String> extraAttributes = entry.getValue();
Manifest manifest = createDummyBundleManifest(bundleNameVersion.getId(), bundleNameVersion.getVersion());
if (extraAttributes != null) {
extraAttributes.forEach(manifest.getMainAttributes()::putValue);
}
Attributes mainAttributes = manifest.getMainAttributes();
String bundleSymbolicName = Objects.requireNonNull(mainAttributes.getValue(Constants.BUNDLE_SYMBOLICNAME));
String bundleVersion = Objects.requireNonNull(mainAttributes.getValue(Constants.BUNDLE_VERSION));
Path jarPath = pluginsDirectory.resolve(bundleSymbolicName + "_" + bundleVersion + ".jar");
try (ZipOutputStream out = new ZipOutputStream(Files.newOutputStream(jarPath));) {
out.putNextEntry(new ZipEntry(JarFile.MANIFEST_NAME));
manifest.write(out);
}
}
return TPS.newDirectoryLocation(jarDirectory.toString());
}
private static Manifest createDummyBundleManifest(String bundleSymbolicName, String bundleVersion) {
Manifest manifest = new Manifest();
Attributes mainAttributes = manifest.getMainAttributes();
mainAttributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
mainAttributes.putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
mainAttributes.putValue(Constants.BUNDLE_SYMBOLICNAME, bundleSymbolicName);
mainAttributes.putValue(Constants.BUNDLE_VERSION, bundleVersion);
mainAttributes.putValue(Constants.BUNDLE_NAME, bundleSymbolicName.replace('.', ' '));
mainAttributes.putValue("Automatic-Module-Name", bundleSymbolicName);
return manifest;
}
@SafeVarargs
public static Entry<NameVersionDescriptor, Map<String, String>> bundle(String id, String version,
Entry<String, String>... additionalManifestEntries) {
return entry(new NameVersionDescriptor(id, version), Map.ofEntries(additionalManifestEntries));
}
public static String version(String version) {
return ";" + Constants.VERSION_ATTRIBUTE + "=\"" + version + "\"";
}
public static String bundleVersion(String lowerBound, String upperBound) {
return ";" + Constants.BUNDLE_VERSION_ATTRIBUTE + "=\"[" + lowerBound + "," + upperBound + ")\"";
}
public static String resolution(String resolutionType) {
return ";" + Constants.RESOLUTION_DIRECTIVE + ":=\"" + resolutionType + "\"";
}
@SafeVarargs
private static <T> Set<T> concat(Collection<T>... colls) {
return Arrays.stream(colls).flatMap(Collection::stream).collect(Collectors.toCollection(LinkedHashSet::new));
}
}