/* * Capsule * Copyright (c) 2014-2016, Parallel Universe Software Co. All rights reserved. * * This program and the accompanying materials are licensed under the terms * of the Eclipse Public License v1.0, available at * http://www.eclipse.org/legal/epl-v10.html */ import capsule.DependencyManager; import co.paralleluniverse.capsule.Jar; import co.paralleluniverse.capsule.test.CapsuleTestUtils; import static co.paralleluniverse.capsule.test.CapsuleTestUtils.*; import com.google.common.jimfs.Jimfs; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import static java.nio.charset.StandardCharsets.UTF_8; import java.nio.file.FileSystem; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.maven.model.Dependency; import org.apache.maven.model.Exclusion; import org.apache.maven.model.Model; import org.apache.maven.model.io.xpp3.MavenXpp3Writer; import org.junit.Test; import static org.junit.Assert.*; import static com.google.common.truth.Truth.*; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.junit.Before; import static org.mockito.Mockito.*; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; /** * * @author pron */ public class MavenCapsuleTest { private final FileSystem fs = Jimfs.newFileSystem(); private final Path cache = fs.getPath("/cache"); private final Path tmp = fs.getPath("/tmp"); private Map<String, List<Path>> deps; private Properties props; @Before public void setUp() throws Exception { deps = null; props = new Properties(System.getProperties()); setProperties(props); setCacheDir(cache); } @Test public void whenNoNameAndPomTakeIdFromPom() throws Exception { Model pom = newPom(); pom.setGroupId("com.acme"); pom.setArtifactId("foo"); pom.setVersion("1.0"); Jar jar = newCapsuleJar() .setAttribute("Application-Class", "com.acme.Foo") .setAttribute("Extract-Capsule", "false") .addEntry("foo.jar", emptyInputStream()) .addEntry("lib/a.jar", emptyInputStream()) .addEntry("pom.xml", toInputStream(pom)); Capsule capsule = newCapsule(jar); assertEquals("com.acme.foo_1.0", capsule.getAppId()); } @Test public void testPomDependencies() throws Exception { List<String> ds = list("com.acme:bar:1.2", "com.acme:baz:3.4:jdk8(org.asd:qqq,com.gogo:bad,com.wha:*)"); Model pom = newPom(); pom.setGroupId("com.acme"); pom.setArtifactId("foo"); pom.setVersion("1.0"); for (String d : ds) pom.addDependency(coordsToDependency(d)); Jar jar = newCapsuleJar() .setAttribute("Application-Class", "com.acme.Foo") .addEntry("foo.jar", emptyInputStream()) .addEntry("pom.xml", toInputStream(pom)); Capsule capsule = newCapsule(jar); List<Object> deps = capsule.getAttribute(Capsule.ATTR_DEPENDENCIES); for (String d : ds) assert_().that(deps).contains(DependencyManager.toDependency(d, "jar")); } //<editor-fold defaultstate="collapsed" desc="POM Utilities"> /////////// POM Utilities /////////////////////////////////// private Model newPom() { return new Model(); } private InputStream toInputStream(Model model) { try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { new MavenXpp3Writer().write(baos, model); return new ByteArrayInputStream(baos.toByteArray()); } catch (IOException e) { throw new AssertionError(e); } } private static final Pattern PAT_DEPENDENCY = Pattern.compile("(?<groupId>[^:\\(\\)]+):(?<artifactId>[^:\\(\\)]+)(:(?<version>[^:\\(\\)]*))?(:(?<classifier>[^:\\(\\)]+))?(\\((?<exclusions>[^\\(\\)]*)\\))?"); static Dependency coordsToDependency(String depString) { final Matcher m = PAT_DEPENDENCY.matcher(depString); if (!m.matches()) throw new IllegalArgumentException("Could not parse dependency: " + depString); final Dependency d = new Dependency(); d.setGroupId(m.group("groupId")); d.setArtifactId(m.group("artifactId")); String version = m.group("version"); if (version == null || version.isEmpty()) version = "[0,)"; d.setVersion(version); d.setClassifier(m.group("classifier")); d.setScope("runtime"); for (Exclusion ex : getExclusions(depString)) d.addExclusion(ex); return d; } static Collection<Exclusion> getExclusions(String depString) { final Matcher m = PAT_DEPENDENCY.matcher(depString); if (!m.matches()) throw new IllegalArgumentException("Could not parse dependency: " + depString); if (m.group("exclusions") == null || m.group("exclusions").isEmpty()) return Collections.emptyList(); final List<String> exclusionPatterns = Arrays.asList(m.group("exclusions").split(",")); final List<Exclusion> exclusions = new ArrayList<Exclusion>(); for (String expat : exclusionPatterns) { String[] coords = expat.trim().split(":"); if (coords.length != 2) throw new IllegalArgumentException("Illegal exclusion dependency coordinates: " + depString + " (in exclusion " + expat + ")"); Exclusion ex = new Exclusion(); ex.setGroupId(coords[0]); ex.setArtifactId(coords[1]); exclusions.add(ex); } return exclusions; } //</editor-fold> //<editor-fold defaultstate="collapsed" desc="Dependency Manager Utilities"> /////////// Dependency Manager Utilities /////////////////////////////////// private Path mockDep(DependencyManager dm, String dep, String type) { return mockDep(dm, dep, type, dep).get(0); } private List<Path> mockDep(DependencyManager dm, String dep, String type, String... artifacts) { List<Path> as = new ArrayList<>(artifacts.length); for (String a : artifacts) as.add(artifact(a, type)); if (deps == null) this.deps = new HashMap<>(); deps.put(dep, as); when(dm.resolveDependency(dep, type)).thenReturn(as); when(dm.resolveDependencies(anyList(), eq(type))).thenAnswer(new Answer<List<Path>>() { @Override public List<Path> answer(InvocationOnMock invocation) throws Throwable { List<String> coords = (List<String>) invocation.getArguments()[0]; List<Path> res = new ArrayList<>(); for (String dep : coords) res.addAll(deps.get(dep)); return res; } }); return as; } private Path artifact(String x, String type) { String[] coords = x.split(":"); String group = coords[0]; String artifact = coords[1]; String artifactDir = artifact.split("-")[0]; // arbitrary String version = coords[2] + (coords.length > 3 ? "-" + coords[3] : ""); return cache.resolve("deps").resolve(group).resolve(artifactDir).resolve(artifact + "-" + version + '.' + type); } //</editor-fold> //<editor-fold defaultstate="collapsed" desc="Utilities"> /////////// Utilities /////////////////////////////////// // may be called once per test (always writes jar into /capsule.jar) private Capsule newCapsule(Jar jar) { return (Capsule) CapsuleTestUtils.newCapsule(jar, path("capsule.jar")); } private Jar newCapsuleJar() { return new Jar() .setAttribute("Manifest-Version", "1.0") .setAttribute("Premain-Class", "Capsule") .setAttribute("Main-Class", "Capsule") .setListAttribute("Caplets", list("MavenCapsule")); } private Path path(String first, String... more) { return fs.getPath(first, more); } @SafeVarargs private static <T> List<T> list(T... xs) { return Arrays.asList(xs); } private InputStream emptyInputStream() { return Jar.toInputStream("", UTF_8); } //</editor-fold> }