| /******************************************************************************* |
| * Copyright (c) 2008, 2021 Code 9 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: |
| * Code 9 - initial API and implementation |
| * IBM - ongoing development |
| ******************************************************************************/ |
| package org.eclipse.equinox.p2.tests.publisher.actions; |
| |
| import static org.mockito.ArgumentMatchers.anyBoolean; |
| import static org.mockito.ArgumentMatchers.anyString; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.ArgumentMatchers.nullable; |
| import static org.mockito.Mockito.when; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Enumeration; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipFile; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.equinox.internal.p2.core.helpers.FileUtils; |
| import org.eclipse.equinox.internal.p2.metadata.InstallableUnit; |
| import org.eclipse.equinox.internal.p2.publisher.eclipse.ExecutablesDescriptor; |
| import org.eclipse.equinox.p2.metadata.IArtifactKey; |
| import org.eclipse.equinox.p2.metadata.IInstallableUnit; |
| import org.eclipse.equinox.p2.metadata.IInstallableUnitFragment; |
| import org.eclipse.equinox.p2.metadata.IProvidedCapability; |
| import org.eclipse.equinox.p2.metadata.IRequirement; |
| import org.eclipse.equinox.p2.metadata.Version; |
| import org.eclipse.equinox.p2.metadata.VersionRange; |
| import org.eclipse.equinox.p2.publisher.AbstractPublisherAction; |
| import org.eclipse.equinox.p2.publisher.IPublisherInfo; |
| import org.eclipse.equinox.p2.publisher.IPublisherResult; |
| import org.eclipse.equinox.p2.publisher.eclipse.EquinoxExecutableAction; |
| import org.eclipse.equinox.p2.publisher.eclipse.IBrandingAdvice; |
| import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; |
| import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; |
| import org.eclipse.equinox.p2.tests.TestActivator; |
| import org.eclipse.equinox.p2.tests.publisher.TestArtifactRepository; |
| |
| public class EquinoxExecutableActionTest extends ActionTest { |
| |
| private static final File MAC_EXEC = new File(TestActivator.getTestDataFolder(), "EquinoxExecutableActionTest/macosx/"); //$NON-NLS-1$ |
| private static final File LINUX_EXEC = new File(TestActivator.getTestDataFolder(), "EquinoxExecutableActionTest/linux/"); //$NON-NLS-1$ |
| private static final File WIN_EXEC = new File(TestActivator.getTestDataFolder(), "EquinoxExecutableActionTest/win/"); //$NON-NLS-1$ |
| private final String EXECUTABLE_NAME = "LauncherName"; //$NON-NLS-1$ |
| private String macConfigCocoa = "cocoa.macosx.x86"; //$NON-NLS-1$ |
| private String winConfig = "win32.win32.x86_64"; //$NON-NLS-1$ |
| private String linuxConfig = "linux.gtk.x86_64"; //$NON-NLS-1$ |
| private ExecutablesDescriptor executablesDescriptor; |
| private IArtifactRepository artifactRepository; |
| private Version version = Version.create("1.2.3"); //$NON-NLS-1$ |
| private String id; |
| private String[] expectedExecutablesContents; |
| |
| @Override |
| public void setUp() throws Exception { |
| setupPublisherInfo(); |
| setupPublisherResult(); |
| artifactRepository = new TestArtifactRepository(getAgent()); |
| } |
| |
| public void testMacCocoa() throws Exception { |
| File icon = File.createTempFile(EXECUTABLE_NAME, ".icns"); |
| FileUtils.copyStream(new FileInputStream(new File(MAC_EXEC, "eclipse.app/Contents/Resources/eclipse.icns")), true, new FileOutputStream(icon), true); |
| |
| expectedExecutablesContents = new String[] {"Info.plist", "MacOS/" + EXECUTABLE_NAME, "MacOS/" + EXECUTABLE_NAME + ".ini", "Resources/" + icon.getName()}; |
| testExecutableAction("macCocoa", "macosx", macConfigCocoa, MAC_EXEC, icon); //$NON-NLS-1$//$NON-NLS-2$ |
| |
| // cleanup |
| icon.delete(); |
| } |
| |
| public void testWin() throws Exception { |
| File icon = File.createTempFile(EXECUTABLE_NAME, ".ico"); |
| FileUtils.copyStream(new FileInputStream(new File(WIN_EXEC, "eclipse.ico")), true, new FileOutputStream(icon), true); |
| |
| // FIXME: is there any way to test that the .ico has been replaced? |
| expectedExecutablesContents = new String[] {EXECUTABLE_NAME + ".exe"}; |
| testExecutableAction("win", "win32", winConfig, WIN_EXEC, icon); //$NON-NLS-1$//$NON-NLS-2$ |
| |
| // cleanup |
| icon.delete(); |
| } |
| |
| public void testLinux() throws Exception { |
| File icon = File.createTempFile(EXECUTABLE_NAME, ".xpm"); |
| FileUtils.copyStream(new FileInputStream(new File(LINUX_EXEC, "eclipse.xpm")), true, new FileOutputStream(icon), true); |
| |
| expectedExecutablesContents = new String[] {EXECUTABLE_NAME, "icon.xpm"}; |
| testExecutableAction("linux", "linux", linuxConfig, LINUX_EXEC, icon); //$NON-NLS-1$//$NON-NLS-2$ |
| |
| // cleanup |
| icon.delete(); |
| } |
| |
| private void testExecutableAction(String idBase, final String osArg, String config, File exec, File icon) { |
| id = idBase; |
| when(publisherInfo.getArtifactRepository()).thenReturn(artifactRepository); |
| when(publisherInfo.getArtifactOptions()).thenReturn(IPublisherInfo.A_PUBLISH); |
| when(publisherInfo.getAdvice(anyString(), anyBoolean(), nullable(String.class), nullable(Version.class), |
| eq(IBrandingAdvice.class))).then(invocation -> setupBrandingAdvice(osArg, icon)); |
| executablesDescriptor = ExecutablesDescriptor.createDescriptor(osArg, "eclipse", exec); |
| testAction = new EquinoxExecutableAction(executablesDescriptor, config, idBase, version, flavorArg); |
| testAction.perform(publisherInfo, publisherResult, new NullProgressMonitor()); |
| verifyResults(idBase, config); |
| debug("Completed EquinoxExecutableActionTest " + idBase + " test."); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| private void verifyResults(String idBase, String confSpec) { |
| ArrayList<IInstallableUnit> iuList = new ArrayList<>(publisherResult.getIUs(null, IPublisherResult.ROOT)); |
| assertEquals(3, iuList.size()); |
| verifyEclipseIU(iuList, idBase, confSpec); |
| verifyCU(iuList, idBase, confSpec); |
| verifyExecIU(iuList, idBase, confSpec); |
| } |
| |
| private void verifyCU(ArrayList<IInstallableUnit> iuList, String idBase, String confSpec) { |
| String[] config = AbstractPublisherAction.parseConfigSpec(confSpec); |
| String _ws = config[0]; |
| String _os = config[1]; |
| String _arch = config[2]; |
| for (IInstallableUnit possibleEclipse : iuList) { |
| if (possibleEclipse.getId().equals(flavorArg + idBase + ".executable." + confSpec)) {//$NON-NLS-1$ |
| IInstallableUnitFragment fragment = (IInstallableUnitFragment) possibleEclipse; |
| Collection<IProvidedCapability> providedCapability = fragment.getProvidedCapabilities(); |
| verifyProvidedCapability(providedCapability, IInstallableUnit.NAMESPACE_IU_ID, flavorArg + idBase + ".executable." + confSpec, version); //$NON-NLS-1$ |
| assertTrue(providedCapability.size() == 1); |
| Collection<IRequirement> requiredCapability = fragment.getHost(); |
| verifyRequirement(requiredCapability, IInstallableUnit.NAMESPACE_IU_ID, idBase + ".executable." + confSpec, new VersionRange(version, true, version, true)); //$NON-NLS-1$ |
| assertTrue(requiredCapability.size() == 1); |
| |
| assertTrue(fragment.getFilter().getParameters()[0].toString().indexOf("(osgi.ws=" + _ws + ")") != -1); |
| assertTrue(fragment.getFilter().getParameters()[0].toString().indexOf("(osgi.os=" + _os + ")") != -1); |
| assertTrue(fragment.getFilter().getParameters()[0].toString().indexOf("(osgi.arch=" + _arch + ")") != -1); |
| assertTrue(fragment.getProperty("org.eclipse.equinox.p2.type.fragment").equals("true")); //$NON-NLS-1$ //$NON-NLS-2$ |
| return;//pass |
| } |
| } |
| fail(); |
| } |
| |
| private void verifyEclipseIU(ArrayList<IInstallableUnit> iuList, String idBase, String confSpec) { |
| for (IInstallableUnit possibleEclipse : iuList) { |
| if (possibleEclipse.getId().equals((idBase + ".executable." + confSpec + "." + EXECUTABLE_NAME))) { //$NON-NLS-1$//$NON-NLS-2$ |
| assertEquals(version, possibleEclipse.getVersion()); |
| Collection<IProvidedCapability> providedCapability = possibleEclipse.getProvidedCapabilities(); |
| verifyProvidedCapability(providedCapability, IInstallableUnit.NAMESPACE_IU_ID, idBase + ".executable." + confSpec + "." + EXECUTABLE_NAME, version); //$NON-NLS-1$ //$NON-NLS-2$ |
| assertEquals(1, providedCapability.size()); |
| Collection<IRequirement> req = possibleEclipse.getRequirements(); |
| assertEquals(0, req.size()); |
| return;//pass |
| } |
| } |
| fail("No executable installable unit."); |
| } |
| |
| private void verifyExecIU(ArrayList<IInstallableUnit> iuList, String idBase, String confSpec) { |
| String[] config = AbstractPublisherAction.parseConfigSpec(confSpec); |
| String _ws = config[0]; |
| String _os = config[1]; |
| String _arch = config[2]; |
| for (IInstallableUnit possibleExec : iuList) { |
| if (possibleExec.getId().equals(idBase + ".executable." + confSpec)) { //$NON-NLS-1$ |
| //keep checking |
| assertTrue(possibleExec.getFilter().equals(InstallableUnit.parseFilter("(& (osgi.ws=" + _ws + ")(osgi.os=" + _os + ")(osgi.arch=" + _arch + "))"))); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ |
| IArtifactKey eKey = possibleExec.getArtifacts().iterator().next(); |
| assertTrue(eKey.getClassifier().equals("binary")); //$NON-NLS-1$ |
| assertTrue(eKey.getId().equals(idBase + ".executable." + confSpec)); //$NON-NLS-1$ |
| assertTrue(eKey.getVersion().equals(version)); |
| Collection<IProvidedCapability> providedCapabilities = possibleExec.getProvidedCapabilities(); |
| verifyProvidedCapability(providedCapabilities, IInstallableUnit.NAMESPACE_IU_ID, idBase + ".executable." + confSpec, version); //$NON-NLS-1$ |
| verifyProvidedCapability(providedCapabilities, flavorArg + idBase, idBase + ".executable", version); //$NON-NLS-1$ |
| assertTrue(providedCapabilities.size() == 2); |
| |
| Collection<IRequirement> requiredCapability = possibleExec.getRequirements(); |
| verifyRequirement(requiredCapability, IInstallableUnit.NAMESPACE_IU_ID, "org.eclipse.equinox.launcher." + (idBase.equals("mac") || idBase.equals("macCocoa") ? confSpec.substring(0, confSpec.lastIndexOf(".")) : confSpec), VersionRange.emptyRange); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| assertTrue(requiredCapability.size() == 1); |
| |
| try { |
| checkExecutableContents(eKey); |
| } catch (IOException e) { |
| fail(); |
| } |
| return;//pass |
| } |
| } |
| fail(); |
| } |
| |
| private void checkExecutableContents(IArtifactKey key) throws IOException { |
| File file = File.createTempFile("exec", ".zip"); |
| |
| try (FileOutputStream fos = new FileOutputStream(file)) { |
| IArtifactDescriptor ad = artifactRepository.createArtifactDescriptor(key); |
| IStatus result = artifactRepository.getArtifact(ad, fos, new NullProgressMonitor()); |
| assertTrue("executable not published?", result.isOK()); |
| } |
| |
| try (ZipFile zip = new ZipFile(file)) { |
| for (String path : expectedExecutablesContents) { |
| assertNotNull("executable zip missing " + path, zip.getEntry(path)); |
| } |
| if (key.getId().contains("macosx")) |
| checkInfoPlist(zip); |
| } |
| |
| // cleanup |
| file.delete(); |
| } |
| |
| /** |
| * If present, check that the Info.plist had its various values |
| * properly rewritten. |
| * @param zip file to check for the Info.plist |
| */ |
| private void checkInfoPlist(ZipFile zip) { |
| ZipEntry candidate = null; |
| boolean found = false; |
| for (Enumeration<? extends ZipEntry> iter = zip.entries(); !found && iter.hasMoreElements();) { |
| candidate = iter.nextElement(); |
| found = candidate.getName().endsWith("Info.plist"); |
| } |
| assertTrue(found); |
| try { |
| String contents = readContentsAndClose(zip.getInputStream(candidate)); |
| assertEquals(id, getPlistStringValue(contents, "CFBundleIdentifier")); |
| assertEquals(EXECUTABLE_NAME, getPlistStringValue(contents, "CFBundleExecutable")); |
| assertEquals(EXECUTABLE_NAME, getPlistStringValue(contents, "CFBundleName")); |
| assertEquals(EXECUTABLE_NAME, getPlistStringValue(contents, "CFBundleDisplayName")); |
| assertEquals(version.toString(), getPlistStringValue(contents, "CFBundleVersion")); |
| } catch (IOException e) { |
| fail(); |
| } |
| } |
| |
| private String getPlistStringValue(String contents, String key) { |
| Pattern p = Pattern.compile("<key>" + key + "</key>\\s*<string>([^<]*)</string>"); |
| Matcher m = p.matcher(contents); |
| if (m.find()) { |
| return m.group(1); |
| } |
| return null; |
| } |
| |
| private String readContentsAndClose(InputStream inputStream) throws IOException { |
| StringBuilder sb = new StringBuilder(); |
| try (Reader is = new InputStreamReader(inputStream)) { |
| char[] buf = new char[1024]; |
| int rc; |
| while ((rc = is.read(buf)) >= 0) { |
| sb.append(buf, 0, rc - 1); |
| } |
| return sb.toString(); |
| } |
| } |
| |
| private List<IBrandingAdvice> setupBrandingAdvice(final String osArg, final File icon) { |
| List<IBrandingAdvice> brandingAdvice = new LinkedList<>(); |
| brandingAdvice.add(new IBrandingAdvice() { |
| @Override |
| public boolean isApplicable(String configSpec, boolean includeDefault, String id, Version version) { |
| return true; |
| } |
| |
| @Override |
| public String getOS() { |
| return osArg; |
| } |
| |
| @Override |
| public String[] getIcons() { |
| return icon == null ? null : new String[] {icon.getAbsolutePath()}; |
| } |
| |
| @Override |
| public String getExecutableName() { |
| return EXECUTABLE_NAME; |
| } |
| }); |
| return brandingAdvice; |
| } |
| } |