| /******************************************************************************* |
| * Copyright (c) 2011, 2021 Sonatype, Inc. 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: |
| * Sonatype, Inc. - initial API and implementation |
| * IBM Corporation - ongoing enhancements |
| * Hannes Wellmann - Bug 577629 - Unify project creation/deletion in tests |
| * Hannes Wellmann - Bug 577541 - Clean up ClasspathHelper and TargetWeaver and create tests |
| *******************************************************************************/ |
| package org.eclipse.pde.ui.tests.classpathresolver; |
| |
| import static org.eclipse.pde.ui.tests.launcher.AbstractLaunchTest.findTargetModel; |
| import static org.eclipse.pde.ui.tests.launcher.AbstractLaunchTest.findWorkspaceModel; |
| import static org.eclipse.pde.ui.tests.util.TargetPlatformUtil.bundle; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNull; |
| |
| import java.io.*; |
| import java.lang.reflect.Field; |
| import java.net.URL; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.util.*; |
| import java.util.Map.Entry; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.debug.core.sourcelookup.ISourceContainer; |
| import org.eclipse.debug.core.sourcelookup.containers.DirectorySourceContainer; |
| import org.eclipse.jdt.core.*; |
| import org.eclipse.jdt.launching.sourcelookup.containers.JavaProjectSourceContainer; |
| import org.eclipse.pde.core.IBundleClasspathResolver; |
| import org.eclipse.pde.core.plugin.IPluginModelBase; |
| import org.eclipse.pde.core.target.NameVersionDescriptor; |
| import org.eclipse.pde.internal.core.ClasspathHelper; |
| import org.eclipse.pde.internal.core.TargetWeaver; |
| import org.eclipse.pde.internal.launching.sourcelookup.PDESourceLookupDirector; |
| import org.eclipse.pde.internal.launching.sourcelookup.PDESourceLookupQuery; |
| import org.eclipse.pde.ui.tests.util.ProjectUtils; |
| import org.eclipse.pde.ui.tests.util.TargetPlatformUtil; |
| import org.junit.*; |
| import org.junit.rules.TemporaryFolder; |
| import org.junit.rules.TestRule; |
| import org.osgi.framework.Bundle; |
| |
| /** |
| * Tests {@link IBundleClasspathResolver} API to extend how the classpath and |
| * source lookup path is created. |
| * |
| */ |
| public class ClasspathResolverTest { |
| |
| @ClassRule |
| public static final TestRule CLEAR_WORKSPACE = ProjectUtils.DELETE_ALL_WORKSPACE_PROJECTS_BEFORE_AND_AFTER; |
| @ClassRule |
| public static final TestRule RESTORE_TARGET_DEFINITION = TargetPlatformUtil.RESTORE_CURRENT_TARGET_DEFINITION_AFTER; |
| |
| private static IProject project; |
| |
| /** |
| * The project name and bundle symbolic name of the test project |
| */ |
| public static final String bundleName = "classpathresolver"; |
| private static final String HOST_BUNDLE_ID = "org.eclipse.pde.core"; |
| |
| @BeforeClass |
| public static void setUpBeforeClass() throws Exception { |
| project = ProjectUtils.importTestProject("tests/projects/" + bundleName); |
| // create workspace plug-ins with same id like a running-platform bundle |
| Bundle hostBundle = Platform.getBundle(HOST_BUNDLE_ID); |
| createWorkspacePluginProjects( // |
| bundle(hostBundle.getSymbolicName(), "2.0.0"), // |
| bundle(hostBundle.getSymbolicName(), hostBundle.getVersion().toString())); |
| } |
| |
| @Rule |
| public TemporaryFolder tempFolder = new TemporaryFolder(); |
| |
| private Path mockedPlatformDevPropertiesFile; |
| private String originalPlatformDevPropertiesURL; |
| |
| @Before |
| public void setUp() throws Exception { |
| mockedPlatformDevPropertiesFile = tempFolder.newFile("test-platform-dev.properties").toPath(); |
| String mockDevPropertiesURL = mockedPlatformDevPropertiesFile.toUri().toURL().toString(); |
| originalPlatformDevPropertiesURL = setPlatformDevPropertiesURL(mockDevPropertiesURL); |
| } |
| |
| @After |
| public void tearDown() throws ReflectiveOperationException { |
| setPlatformDevPropertiesURL(originalPlatformDevPropertiesURL); |
| } |
| |
| private class _PDESourceLookupQuery extends PDESourceLookupQuery { |
| |
| public _PDESourceLookupQuery(PDESourceLookupDirector director, Object object) { |
| super(director, object); |
| } |
| |
| @Override // Make super.getSourceContainers() visible |
| public ISourceContainer[] getSourceContainers(String location, String id) throws CoreException { |
| return super.getSourceContainers(location, id); |
| } |
| } |
| |
| /** |
| * Checks that a created dev properties file will recognise the modified |
| * classpath |
| */ |
| @Test |
| public void testGetDevProperties() throws Exception { |
| mockTPWithRunningPlatformAndBundles(); // running-platform only |
| |
| File devProperties = tempFolder.newFile("dev.properties").getCanonicalFile(); |
| String devPropertiesURL = ClasspathHelper.getDevEntriesProperties(devProperties.getPath(), false); |
| |
| Properties properties = loadProperties(devPropertiesURL); |
| |
| String expectedDevCP = project.getFolder("cpe").getLocation().toPortableString(); |
| assertEquals(expectedDevCP, properties.get(bundleName)); |
| assertEquals(expectedDevCP, properties.get(bundleName + ";1.0.0.qualifier")); |
| } |
| |
| /** |
| * Checks that the source lookup path of a project is updated from the API |
| */ |
| @Test |
| public void testSourceLookupPath() throws Exception { |
| mockTPWithRunningPlatformAndBundles(); // running-platform only |
| |
| PDESourceLookupDirector d = new PDESourceLookupDirector(); |
| _PDESourceLookupQuery q = new _PDESourceLookupQuery(d, project); |
| |
| ISourceContainer[] containers = q.getSourceContainers(project.getLocation().toOSString(), bundleName); |
| |
| assertEquals(2, containers.length); |
| assertEquals(JavaCore.create(project), ((JavaProjectSourceContainer) containers[0]).getJavaProject()); |
| assertEquals(project.getFolder("cpe").getLocation().toFile(), |
| ((DirectorySourceContainer) containers[1]).getDirectory()); |
| } |
| |
| @Test |
| public void testGetDevProperties_workspacePlugin_devEntryWithAndWithoutVersion() throws Exception { |
| |
| getHostBundleAndMockDevProperties(); |
| |
| mockTPWithBundles(); // empty TP |
| |
| IPluginModelBase wsModel = findWorkspaceModel(HOST_BUNDLE_ID, "2.0.0"); |
| |
| Properties devProperties = createDevEntryProperties(List.of(wsModel)); |
| |
| assertEquals("true", devProperties.getProperty("@ignoredot@")); |
| assertEquals("bin", devProperties.getProperty(HOST_BUNDLE_ID)); |
| assertEquals("bin", devProperties.getProperty(HOST_BUNDLE_ID + ";2.0.0")); |
| assertEquals(3, devProperties.size()); // assert no more entries |
| } |
| |
| @Test |
| public void testGetDevProperties_workspacePluginWithSameVersionLikeHostBundle_devEntryWithAndWithoutVersion() |
| throws Exception { |
| |
| Bundle hostBundle = getHostBundleAndMockDevProperties(); |
| String hostBundleVersion = hostBundle.getVersion().toString(); |
| |
| mockTPWithBundles(); // empty TP |
| |
| IPluginModelBase wsModel = findWorkspaceModel(HOST_BUNDLE_ID, hostBundleVersion); |
| |
| Properties devProperties = createDevEntryProperties(List.of(wsModel)); |
| |
| assertEquals("true", devProperties.getProperty("@ignoredot@")); |
| assertEquals("bin", devProperties.getProperty(HOST_BUNDLE_ID)); |
| assertEquals("bin", devProperties.getProperty(HOST_BUNDLE_ID + ";" + hostBundleVersion)); |
| assertEquals(3, devProperties.size()); // assert no more entries |
| } |
| |
| @Test |
| public void testGetDevProperties_bundleFromRunningPlatform_wovenDevEntryWithAndWithoutVersion() throws Exception { |
| |
| Bundle hostBundle = getHostBundleAndMockDevProperties(); |
| String hostBundleVersion = hostBundle.getVersion().toString(); |
| |
| mockTPWithRunningPlatformAndBundles(); // running-platform only |
| |
| IPluginModelBase hostModel = findTargetModel(HOST_BUNDLE_ID, hostBundleVersion); |
| |
| Properties devProperties = createDevEntryProperties(List.of(hostModel)); |
| |
| assertEquals("true", devProperties.getProperty("@ignoredot@")); |
| assertEquals("devPath2", devProperties.getProperty(HOST_BUNDLE_ID)); |
| assertEquals("devPath2", devProperties.getProperty(HOST_BUNDLE_ID + ";" + hostBundleVersion)); |
| assertEquals(3, devProperties.size()); // assert no more entries |
| } |
| |
| @Test |
| public void testGetDevProperties_jarTPBundle_noDevEntries() throws Exception { |
| |
| getHostBundleAndMockDevProperties(); |
| |
| // pretend there is only a jar-bundle in the TP that has the same |
| // name and version like a woven plug-in from the host |
| mockTPWithBundles( // |
| bundle(HOST_BUNDLE_ID, "1.0.0")); |
| |
| IPluginModelBase tpModel = findTargetModel(HOST_BUNDLE_ID, "1.0.0"); |
| |
| Properties devProperties = createDevEntryProperties(List.of(tpModel)); |
| |
| assertEquals("true", devProperties.getProperty("@ignoredot@")); |
| assertNull(devProperties.getProperty(HOST_BUNDLE_ID)); |
| assertEquals(1, devProperties.size()); // assert no more entries |
| } |
| |
| @Test |
| public void testGetDevProperties_jarTPBundleWithSameVersionLikeHostBundle_noDevEntries() throws Exception { |
| |
| Bundle hostBundle = getHostBundleAndMockDevProperties(); |
| String hostBundleVersion = hostBundle.getVersion().toString(); |
| |
| // pretend there is only a jar-bundle in the TP that has the same |
| // name and version like a woven plug-in from the host |
| mockTPWithBundles(// |
| bundle(HOST_BUNDLE_ID, hostBundleVersion)); |
| |
| IPluginModelBase hostModel = findTargetModel(HOST_BUNDLE_ID, hostBundleVersion); |
| |
| Properties devProperties = createDevEntryProperties(List.of(hostModel)); |
| |
| assertEquals("true", devProperties.getProperty("@ignoredot@")); |
| assertNull(devProperties.getProperty(HOST_BUNDLE_ID)); |
| assertEquals(1, devProperties.size()); // assert no more entries |
| } |
| |
| @Test |
| public void testGetDevProperties_workspaceAndJarTPBundle_oneEmptyDevEntryAndOneWithAndWithoutVersion() |
| throws Exception { |
| |
| getHostBundleAndMockDevProperties(); |
| |
| mockTPWithBundles( // |
| bundle(HOST_BUNDLE_ID, "1.0.0")); |
| |
| IPluginModelBase hostModel = findTargetModel(HOST_BUNDLE_ID, "1.0.0"); |
| IPluginModelBase wsModel = findWorkspaceModel(HOST_BUNDLE_ID, "2.0.0"); |
| |
| Properties devProperties = createDevEntryProperties(List.of(hostModel, wsModel)); |
| |
| assertEquals("true", devProperties.getProperty("@ignoredot@")); |
| assertEquals("bin", devProperties.getProperty(HOST_BUNDLE_ID)); // last |
| assertEquals("", devProperties.getProperty(HOST_BUNDLE_ID + ";1.0.0")); |
| assertEquals("bin", devProperties.getProperty(HOST_BUNDLE_ID + ";2.0.0")); |
| assertEquals(4, devProperties.size()); // assert no more entries |
| } |
| |
| @Test |
| public void testGetDevProperties_HostAndJarBundle_oneEmptyDevEntryAndOneWithAndWithoutVersion() throws Exception { |
| |
| Bundle hostBundle = getHostBundleAndMockDevProperties(); |
| String hostBundleVersion = hostBundle.getVersion().toString(); |
| |
| mockTPWithRunningPlatformAndBundles( // |
| bundle(HOST_BUNDLE_ID, "1.0.0")); |
| |
| IPluginModelBase hostModel = findTargetModel(HOST_BUNDLE_ID, hostBundleVersion); |
| IPluginModelBase tpModel = findTargetModel(HOST_BUNDLE_ID, "1.0.0"); |
| |
| Properties devProperties = createDevEntryProperties(List.of(tpModel, hostModel)); |
| |
| assertEquals("true", devProperties.getProperty("@ignoredot@")); |
| assertEquals("devPath2", devProperties.getProperty(HOST_BUNDLE_ID)); // last |
| assertEquals("", devProperties.getProperty(HOST_BUNDLE_ID + ";1.0.0")); |
| assertEquals("devPath2", devProperties.getProperty(HOST_BUNDLE_ID + ";" + hostBundleVersion)); |
| assertEquals(4, devProperties.size()); // assert no more entries |
| } |
| |
| @Test |
| public void testGetDevProperties_workspaceAndHostBundle_twoDevEntriesWithAndWithoutVersion() throws Exception { |
| |
| Bundle hostBundle = getHostBundleAndMockDevProperties(); |
| String hostBundleVersion = hostBundle.getVersion().toString(); |
| |
| mockTPWithRunningPlatformAndBundles(); // running-platform only |
| |
| IPluginModelBase hostModel = findTargetModel(HOST_BUNDLE_ID, hostBundleVersion); |
| IPluginModelBase wsModel = findWorkspaceModel(HOST_BUNDLE_ID, "2.0.0"); |
| |
| Properties devProperties = createDevEntryProperties(List.of(hostModel, wsModel)); |
| |
| assertEquals("true", devProperties.getProperty("@ignoredot@")); |
| assertEquals("bin", devProperties.getProperty(HOST_BUNDLE_ID)); // last |
| assertEquals("bin", devProperties.getProperty(HOST_BUNDLE_ID + ";2.0.0")); |
| assertEquals("devPath2", devProperties.getProperty(HOST_BUNDLE_ID + ";" + hostBundleVersion)); |
| assertEquals(4, devProperties.size()); // assert no more entries |
| } |
| |
| @Test |
| public void testGetDevProperties_mixedWorkspaceAndHostAndJarTPBundle_onlyUsedPlatformBundles() throws Exception { |
| |
| Bundle hostBundle = getHostBundleAndMockDevProperties(); |
| String hostBundleVersion = hostBundle.getVersion().toString(); |
| |
| mockTPWithRunningPlatformAndBundles( // |
| bundle(HOST_BUNDLE_ID, "1.0.0")); |
| |
| IPluginModelBase hostModel = findTargetModel(HOST_BUNDLE_ID, hostBundleVersion); |
| IPluginModelBase tpModel = findTargetModel(HOST_BUNDLE_ID, "1.0.0"); |
| IPluginModelBase wsModel = findWorkspaceModel(HOST_BUNDLE_ID, "2.0.0"); |
| |
| Properties devProperties = createDevEntryProperties(List.of(hostModel, wsModel, tpModel)); |
| |
| assertEquals("true", devProperties.getProperty("@ignoredot@")); |
| // jar-bundle from tp should not be considered for non-version entry |
| assertEquals("bin", devProperties.getProperty(HOST_BUNDLE_ID)); |
| assertEquals("", devProperties.getProperty(HOST_BUNDLE_ID + ";1.0.0")); |
| assertEquals("bin", devProperties.getProperty(HOST_BUNDLE_ID + ";2.0.0")); |
| assertEquals("devPath2", devProperties.getProperty(HOST_BUNDLE_ID + ";" + hostBundleVersion)); |
| assertEquals(5, devProperties.size()); // assert no more entries |
| } |
| |
| // --- utility methods --- |
| |
| private static String setPlatformDevPropertiesURL(String string) throws ReflectiveOperationException { |
| // trigger properties reload on next use |
| setStaticField(TargetWeaver.class, "fgDevProperties", null); |
| return setStaticField(TargetWeaver.class, "fgDevPropertiesURL", string); |
| } |
| |
| private static <V> V setStaticField(Class<?> cl, String fieldName, V newValue) throws ReflectiveOperationException { |
| Field field = cl.getDeclaredField(fieldName); |
| field.trySetAccessible(); |
| @SuppressWarnings("unchecked") |
| V oldValue = (V) field.get(null); |
| field.set(null, newValue); |
| return oldValue; |
| } |
| |
| @SafeVarargs |
| private static void createWorkspacePluginProjects( |
| Entry<NameVersionDescriptor, Map<String, String>>... workspacePlugins) throws CoreException { |
| Set<NameVersionDescriptor> descriptions = Map.ofEntries(workspacePlugins).keySet(); |
| List<IProject> pluginProjects = ProjectUtils.createWorkspacePluginProjects(descriptions); |
| while (pluginProjects.stream().anyMatch(ClasspathResolverTest::isUpdatePending)) { |
| Thread.yield(); // await async classpath update of projects |
| } |
| } |
| |
| private static boolean isUpdatePending(IProject project) { |
| IJavaProject jProject = JavaCore.create(project); |
| try { |
| IPath outputLocation = jProject.getOutputLocation(); |
| return outputLocation == null || project.findMember(outputLocation.removeFirstSegments(1)) == null; |
| } catch (JavaModelException e) { |
| return false; |
| } |
| } |
| |
| private Bundle getHostBundleAndMockDevProperties() throws IOException { |
| Bundle hostBundle = Platform.getBundle(HOST_BUNDLE_ID); |
| Files.write(mockedPlatformDevPropertiesFile, List.of( // |
| HOST_BUNDLE_ID + "=devPath1", // |
| HOST_BUNDLE_ID + ";" + hostBundle.getVersion() + "=devPath2", // |
| "some.other.plugin=devPath3", // |
| "some.other.plugin;2.0.0=devPath4")); |
| return hostBundle; |
| } |
| |
| @SafeVarargs |
| private void mockTPWithBundles(Entry<NameVersionDescriptor, Map<String, String>>... bundles) throws Exception { |
| Path jarsDirectory = tempFolder.newFolder("TPJarsDirectory").toPath(); |
| TargetPlatformUtil.setDummyBundlesAsTarget(Map.ofEntries(bundles), List.of(), jarsDirectory); |
| } |
| |
| @SafeVarargs |
| private void mockTPWithRunningPlatformAndBundles( |
| Entry<NameVersionDescriptor, Map<String, String>>... additionalBundles) throws Exception { |
| Path jarsDirectory = tempFolder.newFolder("TPJarsDirectory").toPath(); |
| TargetPlatformUtil.setRunningPlatformWithDummyBundlesAsTarget(b -> b.getSymbolicName().equals(HOST_BUNDLE_ID), |
| Map.ofEntries(additionalBundles), Set.of(), jarsDirectory); |
| } |
| |
| private Properties createDevEntryProperties(List<IPluginModelBase> launchedBundles) |
| throws IOException, CoreException { |
| File devPropertiesFile = tempFolder.newFile("dev.properties").getCanonicalFile(); |
| Map<String, List<IPluginModelBase>> bundlesMap = Map.of(HOST_BUNDLE_ID, launchedBundles); |
| String devPropertiesURL = ClasspathHelper.getDevEntriesProperties(devPropertiesFile.getPath(), bundlesMap); |
| return loadProperties(devPropertiesURL); |
| } |
| |
| private static Properties loadProperties(String devPropertiesURL) throws IOException { |
| File propertiesFile = new File(new URL(devPropertiesURL).getPath()); |
| Properties devProperties = new Properties(); |
| try (InputStream stream = new FileInputStream(propertiesFile)) { |
| devProperties.load(stream); |
| } |
| return devProperties; |
| } |
| } |