package com.rcpcompany.updatesite.tests; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.io.IOUtils; import org.apache.commons.vfs2.FileContent; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.FileSystemException; import org.apache.commons.vfs2.FileSystemManager; import org.apache.commons.vfs2.VFS; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.URIUtil; import org.eclipse.equinox.internal.p2.rollback.FormerState; import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.core.ProvisionException; import org.eclipse.equinox.p2.engine.IEngine; import org.eclipse.equinox.p2.engine.IProfile; import org.eclipse.equinox.p2.engine.IProfileRegistry; import org.eclipse.equinox.p2.engine.IProvisioningPlan; import org.eclipse.equinox.p2.engine.ProvisioningContext; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.planner.IPlanner; import org.eclipse.equinox.p2.planner.IProfileChangeRequest; import org.eclipse.equinox.p2.query.IQuery; import org.eclipse.equinox.p2.query.IQueryResult; import org.eclipse.equinox.p2.query.QueryUtil; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.Timeout; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import com.rcpcompany.test.utils.MonitoredMonitor; import com.rcpcompany.utils.basic.TSStatusUtils; /** * Tests that various different installations can be updated to this version of the workbench. * <p> * The implementation creates a backup profile and uses this to backup the original configuration to allow us to revert * the configuration afterwards * * @author Tonny Madsen, The RCP Company */ @RunWith(Parameterized.class) public class InstallationIntegrationTest { private static final String FEATURE_SUFFIX = ".feature.group"; @Rule public Timeout globalTimeout = new Timeout(20 * 60 * 1000); // 20 minutes max per method tested String[] FEATURE_IDS = new String[] { // "com.rcpcompany.uibindings.debug.feature", "com.rcpcompany.uibindings.feature", "com.rcpcompany.uibindings.financial.feature", "com.rcpcompany.uibindings.grid.feature", "com.rcpcompany.uibindings.navigator.feature", "com.rcpcompany.utils.feature", // "com.rcpcompany.utils.rap.feature", }; /** * Path to the installation area for the package. */ private URI myInstallAreaURI; /** * Main repository as an URI. */ private URI REPO; /** * The URL for the installation package as supported by Apache Commons VFS */ private final String myInstallationURL; /** * The relative path inside the installation package to the Product root (usually "eclipse"). */ private final String myProductPath; /** * The relative path inside the installation package to the P2 root (usually "eclipse/p2"). */ private final String myP2Path; /** * The p2 profile ID */ private String myP2Profile; private final String what; @Parameters public static List<Object[]> data() { /* * Directories where products might be found. * * Only used for "file:" lines... */ final String folderList[] = { "/Eclipse/Downloads", "D:\\Downloads" }; /* * The lost of the known products what should be installed into * * Format: VFS URL for package file/dir, internal path for product root, internal path for p2 root, p2 profile * (null means use the only defined) */ final Object[][] bruttoList = new Object[][] { // { "tgz:file:eclipse-rcp-juno-macosx-cocoa-x86_64.tar.gz", ARCH.MACOSX, // "eclipse", "eclipse/p2", // null }, { "file:eclipse-SDK-3.8-macosx-cocoa-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-SDK-3.8-win32-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-SDK-3.8.1-macosx-cocoa-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-SDK-3.8.2-linux-gtk-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-SDK-3.8.2-macosx-cocoa-x86_64", "eclipse", "eclipse/p2", null }, // { "file:eclipse-SDK-3.8.2-solaris-gtk", ARCH.SOLARIS_64, "eclipse", "eclipse/p2", null // }, { "file:eclipse-SDK-3.8.2-win32-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-java-kepler-R-win32-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-jee-juno-SR2-macosx-cocoa-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-jee-juno-SR2-win32-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-jee-kepler-R-macosx-cocoa-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-jee-kepler-R-win32-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-platform-3.8.2-macosx-cocoa-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-platform-3.8.2-win32-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-rcp-indigo-SR2-macosx-cocoa-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-rcp-juno-SR1-macosx-cocoa-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-rcp-juno-SR1-win32-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-rcp-juno-SR2-macosx-cocoa-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-rcp-juno-SR2-win32-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-rcp-juno-macosx-cocoa-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-rcp-kepler-R-macosx-cocoa-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-rcp-kepler-R-win32-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-reporting-kepler-R-win32-x86_64", "eclipse", "eclipse/p2", null }, { "file:eclipse-standard-kepler-R-macosx-cocoa-x86_64", "eclipse", "eclipse/p2", null }, // { "file:eclipse-rcp-kepler-R-macosx-cocoa-x86_64", "eclipse", "eclipse/p2", null }, }; final List<Object[]> found = new ArrayList<Object[]>(); for (final Object[] l : bruttoList) { String url = (String) l[0]; final String productPath = (String) l[1]; final String p2Path = (String) l[2]; final String p2Profile = (String) l[3]; final int i = url.indexOf("file:"); if (i != -1) { final String filename = url.substring(i + 5); String ff = null; for (final String folder : folderList) { final File f = new File(folder + "/" + filename); if (f.exists()) { ff = f.toURI().toString(); break; } } if (ff == null) { continue; } url = url.substring(0, i) + ff; } found.add(new Object[] { url, productPath, p2Path, p2Profile }); } return found; } public InstallationIntegrationTest(String installaionURL, String productPath, String p2Path, String p2Profile) { myInstallationURL = installaionURL; myProductPath = productPath; myP2Path = p2Path; myP2Profile = p2Profile; what = myInstallationURL; System.out.println("*** " + what); } private IProvisioningAgent myAgent = null; private IProfileRegistry myProfileRegistry; private IMetadataRepositoryManager myMetadataManager; private IArtifactRepositoryManager myArtifactManager; private IPlanner myPlanner; private IEngine myEngine; private IProfile myProfile; private long myProfileTimestamp; private IProfile myBackupProfile; private URI myProductURI; @Before public void setupManager() { System.out.println(" - Setup"); final String prop = System.getProperty("REPO"); assertNotNull("No REPO property specified", prop); final File f = new File(prop); assertTrue("Repository does not exist: " + f, f.exists()); REPO = f.toURI(); assertNotNull(what + ": location defined", DSLocation.get()); assertEquals(what + ": Only file: location protocol is supported", "file", DSLocation.get().getURL() .getProtocol()); assertNotNull(what + ": agent provider defined", DSAgent.get()); assertEquals(what + ": location r/w", false, DSLocation.get().isReadOnly()); if (myInstallationURL.startsWith("file:")) { /* * So we are talking a a plain folder in the file system... test it... */ myInstallAreaURI = URI.create(myInstallationURL); } else { /* * Find a place to install the product */ try { myInstallAreaURI = DSLocation.get().getDataArea("install-area").toURI(); } catch (final Exception ex) { fail(what + ": getDataAea: " + ex); return; } deleteArea(myInstallAreaURI); /* * Install the existing product into this place */ try { final FileSystemManager fsManager = VFS.getManager(); final FileObject archive = fsManager.resolveFile(myInstallationURL); // List the children of the archive file System.out.println(" - Unpacking installation"); unpack(archive); } catch (final FileSystemException ex) { fail(what + ": VFS: " + ex); return; } catch (final Exception ex) { fail(what + ": " + ex); return; } } assertTrue(what + ": " + myInstallAreaURI + " not a plain directory", new File(myInstallAreaURI).isDirectory()); myProductURI = URIUtil.append(myInstallAreaURI, myProductPath); /* * Now open the area as a P2 area */ final URI myP2URI = URIUtil.append(myInstallAreaURI, myP2Path); assertTrue(what + ": " + myP2URI + " not a plain directory", new File(myP2URI).isDirectory()); try { myAgent = DSAgent.get().createAgent(myP2URI); } catch (final ProvisionException ex) { fail(what + ": createAgent: " + ex); return; } try { // From the director app myAgent.registerService(IProvisioningAgent.INSTALLER_AGENT, DSAgent.get().createAgent(null)); } catch (final ProvisionException ex) { fail(what + ": createAgent: " + ex); return; } myProfileRegistry = (IProfileRegistry) myAgent.getService(IProfileRegistry.SERVICE_NAME); assertNotNull(myProfileRegistry); myMetadataManager = (IMetadataRepositoryManager) myAgent.getService(IMetadataRepositoryManager.SERVICE_NAME); assertNotNull(myMetadataManager); myArtifactManager = (IArtifactRepositoryManager) myAgent.getService(IArtifactRepositoryManager.SERVICE_NAME); assertNotNull(myArtifactManager); myPlanner = (IPlanner) myAgent.getService(IPlanner.SERVICE_NAME); assertNotNull(myPlanner); myEngine = (IEngine) myAgent.getService(IEngine.SERVICE_NAME); assertNotNull(myEngine); // From the director app myAgent.registerService("eclipse.p2.profile", myP2Profile); final IProfile[] profiles = myProfileRegistry.getProfiles(); if (myP2Profile == null) { assertTrue(profiles != null && profiles.length == 1); myP2Profile = profiles[0].getProfileId(); } myProfile = myProfileRegistry.getProfile(myP2Profile); if (myProfile == null) { String profileDesc = ""; for (final IProfile p : profiles) { if (profileDesc.length() > 0) { profileDesc += ", "; } profileDesc += p.getProfileId(); } fail(what + ": Expected profile '" + myP2Profile + "' but found [" + profileDesc + "]"); } /* * Find the current timestamp for the profile */ final long[] profileTimestamps = myProfileRegistry.listProfileTimestamps(myP2Profile); myProfileTimestamp = 0; for (final long ts : profileTimestamps) { if (myProfileTimestamp < ts) { myProfileTimestamp = ts; } } /* * Make a backup of the main profile */ myBackupProfile = myProfileRegistry.getProfile(myP2Profile, myProfileTimestamp); // if (myBackupProfile == null) { // try { // myBackupProfile = myProfileRegistry.addProfile("TEST-BACKUP"); // } catch (final ProvisionException ex) { // fail(what + ": addProfile: " + ex); // return; // } // } assertNotNull(myBackupProfile); // copyProfile(myProfile, myBackupProfile); } /** * Copies all provisioning from one profile to another. * * @param from * the profile to copy from * @param to * the profile to copy to */ private void copyProfile(IProfile from, IProfile to) { System.out.println(" - Copy profile " + from.getProfileId() + "[" + from.getTimestamp() + "] -> " + to.getProfileId() + "[" + to.getTimestamp() + "]"); @SuppressWarnings("restriction") final IProfileChangeRequest deltaChangeRequest = FormerState.generateProfileDeltaChangeRequest(to, from); assertNotNull(deltaChangeRequest); final String productPath = myProductURI.getPath(); deltaChangeRequest.setProfileProperty(IProfile.PROP_INSTALL_FOLDER, productPath); deltaChangeRequest.setProfileProperty(IProfile.PROP_CACHE, productPath); final ProvisioningContext context = new ProvisioningContext(myAgent); final IProvisioningPlan plan = myPlanner.getProvisioningPlan(deltaChangeRequest, context, new MonitoredMonitor()); assertNotNull(plan); IStatus status = plan.getStatus(); assertTrue(what + ": Cannot plan backup:\n" + TSStatusUtils.toString(status), status.isOK()); status = myEngine.perform(plan, new MonitoredMonitor()); assertTrue(what + ": Cannot install backup:\n" + TSStatusUtils.toString(status), status.isOK()); } @After public void cleanup() { if (myProfile != null && myBackupProfile != null) { copyProfile(myBackupProfile, myProfile); } if (myAgent != null) { myAgent.stop(); } if (!myInstallationURL.startsWith("file:")) { deleteArea(myInstallAreaURI); } } /** * Tests whether the product can be updated with {@link #FEATURE_IDS}. * <p> * Only the planner is run - the product is not actually updated. */ @Test public void testUpdate() { try { System.out.println(" - Adding repository location: " + REPO); myMetadataManager.loadRepository(REPO, null); myArtifactManager.addRepository(REPO); final List<IInstallableUnit> ius = findFeaturse(); updateProperties(); installFeature(ius); // assertBundleInfo(); } catch (final Exception ex) { fail(what + ": " + ex); } finally { myMetadataManager.removeRepository(REPO); myArtifactManager.removeRepository(REPO); } } public List<IInstallableUnit> findFeaturse() { final List<IInstallableUnit> l = new ArrayList<IInstallableUnit>(); for (final String f : FEATURE_IDS) { System.out.println(" - Finding feature " + f); final IQuery<IInstallableUnit> query = QueryUtil.createIUQuery(f + FEATURE_SUFFIX); final IQueryResult<IInstallableUnit> result = myMetadataManager.query(query, new MonitoredMonitor()); final Set<IInstallableUnit> set = result.toSet(); assertEquals(what + ": did not find one result: " + set, 1, set.size()); final IInstallableUnit iu = set.iterator().next(); assertNotNull(iu); l.add(iu); } return l; } public void updateProperties() { final String productPath = URIUtil.append(myInstallAreaURI, myProductPath).getPath(); final Map<String, String> properties = myProfile.getProperties(); if (productPath.equals(properties.get(IProfile.PROP_CACHE)) && productPath.equals(properties.get(IProfile.PROP_INSTALL_FOLDER))) return; System.out.println(" - Updating install properties"); final IProfileChangeRequest request = myPlanner.createChangeRequest(myProfile); assertNotNull(request); request.setProfileProperty(IProfile.PROP_INSTALL_FOLDER, productPath); request.setProfileProperty(IProfile.PROP_CACHE, productPath); //request.setProfileProperty(IProfile.PROP_ROAMING, "false"); //$NON-NLS-1$ final ProvisioningContext context = new ProvisioningContext(myAgent); final IProvisioningPlan plan = myPlanner.getProvisioningPlan(request, context, new MonitoredMonitor()); assertNotNull(plan); IStatus status = plan.getStatus(); assertTrue(what + ": Cannot plan for update of properties:\n" + TSStatusUtils.toString(status), status.isOK()); /* * Execute plan */ status = myEngine.perform(plan, new MonitoredMonitor()); assertTrue(what + ": Cannot update properties:\n" + TSStatusUtils.toString(status), status.isOK()); } public void installFeature(final List<IInstallableUnit> ius) { System.out.println(" - Installing iu"); final IProfileChangeRequest request = myPlanner.createChangeRequest(myProfile); assertNotNull(request); for (final IInstallableUnit iu : ius) { request.add(iu); } final ProvisioningContext context = new ProvisioningContext(myAgent); /* * Only download from our own repository */ final URI[] repositories = new URI[] { REPO }; context.setMetadataRepositories(repositories); context.setArtifactRepositories(repositories); /* * Create a plan... */ final IProvisioningPlan plan = myPlanner.getProvisioningPlan(request, context, new MonitoredMonitor()); assertNotNull(plan); IStatus status = plan.getStatus(); assertTrue(what + ": Cannot plan:\n" + TSStatusUtils.toString(status), status.isOK()); /* * Do test installation */ status = myEngine.perform(plan, new MonitoredMonitor()); assertTrue(what + ": Cannot install:\n" + TSStatusUtils.toString(status), status.isOK()); } private void unpack(final FileObject archive) throws FileSystemException, FileNotFoundException, IOException { for (final FileObject fo : archive.getChildren()) { if (!fo.isReadable()) { continue; } switch (fo.getType()) { case FOLDER: unpack(fo); break; case FILE: final FileContent fc = fo.getContent(); final InputStream is = fc.getInputStream(); final File f = new File(myInstallAreaURI + "/" + fo.getName().getPath()); f.getParentFile().mkdirs(); final OutputStream os = new FileOutputStream(f); try { IOUtils.copyLarge(is, os); } finally { IOUtils.closeQuietly(is); IOUtils.closeQuietly(os); } break; case IMAGINARY: case FILE_OR_FOLDER: break; } } } private void deleteArea(URI installAreaURI) { if (installAreaURI == null) return; final File file = new File(installAreaURI); if (!file.exists()) return; deleteFile(file); if (!file.exists()) return; fail(what + ": files still exists - not deleted:\n" + listFiles(file)); } private String listFiles(File file) { try { if (!file.exists()) return ""; if (file.isDirectory()) { final File[] files = file.listFiles(); if (files.length == 0) return file.getCanonicalPath(); String l = ""; for (final File f : files) { if (l.length() > 0) { l += "\n"; } l += listFiles(f); } } return file.getCanonicalPath(); } catch (final IOException ex) { return "" + ex; } } private void deleteFile(File file) { if (file.isDirectory()) { for (final File f : file.listFiles()) { deleteFile(f); } } assertTrue(what + ": cannot delete file: " + file, file.delete()); } }