// Copyright 2016 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.workspace.maven; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.truth.Truth.assertThat; import static com.google.devtools.build.workspace.maven.ArtifactBuilder.fromCoords; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import org.apache.maven.model.Dependency; import org.apache.maven.model.DependencyManagement; import org.apache.maven.model.Model; import org.apache.maven.model.building.UrlModelSource; import org.eclipse.aether.artifact.DefaultArtifact; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link Resolver}. */ @RunWith(JUnit4.class) public class ResolverTest { private static final String GROUP_ID = "x"; private static final String ARTIFACT_ID = "y"; private static final List<Rule> ALIASES = ImmutableList.of(); @Test public void testGetSha1Url() throws Exception { assertThat(Resolver.getSha1Url("http://example.com/foo.pom", "jar")) .isEqualTo("http://example.com/foo.jar.sha1"); assertThat(Resolver.getSha1Url("http://example.com/foo.pom", "aar")) .isEqualTo("http://example.com/foo.aar.sha1"); } @Test public void testGetSha1UrlOnlyAtEOL() throws Exception { assertThat(Resolver.getSha1Url("http://example.pom/foo.pom", "jar")) .isEqualTo("http://example.pom/foo.jar.sha1"); } @Test public void testArtifactResolution() throws Exception { String coords = "x:y:1.2.3"; DefaultModelResolver modelResolver = mock(DefaultModelResolver.class); when(modelResolver.resolveModel(fromCoords(coords))) .thenReturn(new UrlModelSource(new URL("http://repo.foo.org/maven/x/y/1.2.3/y-1.2.3.pom"))); String httpGetBody = "5fe28b9518e58819180a43a850fbc0dd24b7c050"; Resolver resolver = new Resolver(modelResolver, ALIASES) { protected InputStream httpGet(String url) throws IOException { return new ByteArrayInputStream(httpGetBody.getBytes()); } }; resolver.resolveArtifact(coords); Collection<Rule> rules = resolver.getRules(); assertThat(rules).hasSize(1); Rule rule = rules.iterator().next(); assertThat(rule.name()).isEqualTo("x_y"); assertThat(rule.getRepository()).isEqualTo("http://repo.foo.org/maven/"); assertThat(rule.getSha1()).isEqualTo("5fe28b9518e58819180a43a850fbc0dd24b7c050"); } @Test public void testExtractSha1() { assertThat(Resolver.extractSha1("5fe28b9518e58819180a43a850fbc0dd24b7c050")) .isEqualTo("5fe28b9518e58819180a43a850fbc0dd24b7c050"); assertThat(Resolver.extractSha1("5fe28b9518e58819180a43a850fbc0dd24b7c050\n")) .isEqualTo("5fe28b9518e58819180a43a850fbc0dd24b7c050"); assertThat( Resolver.extractSha1( "83cd2cd674a217ade95a4bb83a8a14f351f48bd0 /home/maven/repository-staging/to-ibiblio/maven2/antlr/antlr/2.7.7/antlr-2.7.7.jar")) .isEqualTo("83cd2cd674a217ade95a4bb83a8a14f351f48bd0"); } private Dependency getDependency(String coordinates) { return getDependency(coordinates, "compile"); } private Dependency getDependency(String coordinates, String scope) { String[] coordinateArray = coordinates.split(":"); Preconditions.checkState(coordinateArray.length == 3); Dependency dependency = new Dependency(); dependency.setGroupId(coordinateArray[0]); dependency.setArtifactId(coordinateArray[1]); dependency.setVersion(coordinateArray[2]); dependency.setScope(scope); return dependency; } private Model mockDepManagementModel(String dependencyManagementDep, String normalDep) { Dependency dmDep = getDependency(dependencyManagementDep); Dependency dep = getDependency(normalDep); Model mockModel = mock(Model.class); DependencyManagement dependencyManagement = new DependencyManagement(); dependencyManagement.addDependency(dmDep); when(mockModel.getDependencyManagement()).thenReturn(dependencyManagement); when(mockModel.getDependencies()).thenReturn(ImmutableList.of(dep)); return mockModel; } @Test public void dependencyManagementWins() throws Exception { Aether aether = mock(Aether.class); when(aether.requestVersionRange(fromCoords("a:b:[1.0]"))).thenReturn(newArrayList("1.0")); when(aether.requestVersionRange(fromCoords("a:b:[2.0,)"))).thenReturn(newArrayList("2.0")); VersionResolver versionResolver = new VersionResolver(aether); Resolver resolver = new Resolver(mock(DefaultModelResolver.class), versionResolver, ALIASES); resolver.traverseDeps( mockDepManagementModel("a:b:[1.0]", "a:b:2.0"), Sets.newHashSet(), Sets.newHashSet(), new Rule(new DefaultArtifact("par:ent:1.2.3"))); Collection<Rule> rules = resolver.getRules(); assertThat(rules).hasSize(1); Rule actual = rules.iterator().next(); assertThat(actual.version()).isEqualTo("1.0"); } @Test public void nonConflictingDepManagement() throws Exception { Aether aether = mock(Aether.class); when(aether.requestVersionRange(fromCoords("a:b:[1.0,4.0]"))) .thenReturn(newArrayList("1.0", "2.0", "3.0", "4.0")); when(aether.requestVersionRange(fromCoords("a:b:[2.0,)"))) .thenReturn(newArrayList("2.0", "3.0")); VersionResolver versionResolver = new VersionResolver(aether); Resolver resolver = new Resolver(mock(DefaultModelResolver.class), versionResolver, ALIASES); resolver.traverseDeps( mockDepManagementModel("a:b:[1.0, 4.0]", "a:b:2.0"), Sets.newHashSet(), Sets.newHashSet(), new Rule(new DefaultArtifact("par:ent:1.2.3"))); Collection<Rule> rules = resolver.getRules(); assertThat(rules).hasSize(1); Rule actual = rules.iterator().next(); assertThat(actual.version()).isEqualTo("2.0"); } @Test public void nonConflictingDepManagementRange() throws Exception { Aether aether = mock(Aether.class); when(aether.requestVersionRange(fromCoords("a:b:[1.0,4.0]"))) .thenReturn(newArrayList("1.0", "1.2", "2.0", "3.0", "4.0")); when(aether.requestVersionRange(fromCoords("a:b:[1.2,3.0]"))) .thenReturn(newArrayList("1.2", "2.0", "3.0")); VersionResolver versionResolver = new VersionResolver(aether); Resolver resolver = new Resolver(mock(DefaultModelResolver.class), versionResolver, ALIASES); resolver.traverseDeps( mockDepManagementModel("a:b:[1.0,4.0]", "a:b:[1.2,3.0]"), Sets.newHashSet(), Sets.newHashSet(), new Rule(new DefaultArtifact("par:ent:1.2.3"))); Collection<Rule> rules = resolver.getRules(); assertThat(rules).hasSize(1); Rule actual = rules.iterator().next(); assertThat(actual.version()).isEqualTo("3.0"); } @Test public void depManagementDoesntAddDeps() throws Exception { Aether aether = mock(Aether.class); when(aether.requestVersionRange(fromCoords("c:d:[2.0,)"))).thenReturn(newArrayList("2.0")); when(aether.requestVersionRange(fromCoords("a:b:[1.0,)"))) .thenReturn(newArrayList("1.0", "2.0")); VersionResolver versionResolver = new VersionResolver(aether); Resolver resolver = new Resolver(mock(DefaultModelResolver.class), versionResolver, ALIASES); resolver.traverseDeps( mockDepManagementModel("a:b:1.0", "c:d:2.0"), Sets.newHashSet(), Sets.newHashSet(), new Rule(new DefaultArtifact("par:ent:1.2.3"))); Collection<Rule> rules = resolver.getRules(); assertThat(rules).hasSize(1); Rule actual = rules.iterator().next(); assertThat(actual.name()).isEqualTo("c_d"); } @Test public void scopesHonoredForRoot() throws Exception { Model mockModel = mock(Model.class); // Only "compile" should go through. when(mockModel.getDependencies()) .thenReturn( ImmutableList.of( getDependency("a:b:1.0", "compile"), getDependency("c:d:1.0", "test"))); Aether aether = mock(Aether.class); when(aether.requestVersionRange(fromCoords("a:b:[1.0,)"))).thenReturn(newArrayList("1.0")); when(aether.requestVersionRange(fromCoords("c:d:[1.0,)"))).thenReturn(newArrayList("1.0")); VersionResolver versionResolver = new VersionResolver(aether); Resolver resolver = new Resolver(mock(DefaultModelResolver.class), versionResolver, ALIASES); resolver.traverseDeps(mockModel, Sets.newHashSet("compile"), Sets.newHashSet(), null); Collection<Rule> rules = resolver.getRules(); assertThat(rules).hasSize(1); Rule actual = rules.iterator().next(); assertThat(actual.name()).isEqualTo("a_b"); } @Test public void scopesIgnoredForNonRoot() throws Exception { Model mockModel = mock(Model.class); // "compile" and "runtime" should go through. when(mockModel.getDependencies()) .thenReturn( ImmutableList.of( getDependency("a:b:1.0", "compile"), getDependency("c:d:1.0", "runtime"), getDependency("e:f:1.0", "test"))); Aether aether = mock(Aether.class); when(aether.requestVersionRange(fromCoords("a:b:[1.0,)"))).thenReturn(newArrayList("1.0")); when(aether.requestVersionRange(fromCoords("c:d:[1.0,)"))).thenReturn(newArrayList("1.0")); when(aether.requestVersionRange(fromCoords("e:f:[1.0,)"))).thenReturn(newArrayList("1.0")); VersionResolver versionResolver = new VersionResolver(aether); Resolver resolver = new Resolver(mock(DefaultModelResolver.class), versionResolver, ALIASES); resolver.traverseDeps( mockModel, Sets.newHashSet("compile"), Sets.newHashSet(), new Rule(new DefaultArtifact("par:ent:1.2.3"))); Collection<Rule> rules = resolver.getRules(); assertThat(rules).hasSize(2); Set<String> names = rules.stream().map(rule -> rule.name()).collect(Collectors.toSet()); assertThat(names).isEqualTo(Sets.newHashSet("a_b", "c_d")); } @Test public void exclusions() throws Exception { Model mockModel = mock(Model.class); when(mockModel.getDependencies()).thenReturn(ImmutableList.of(getDependency("a:b:1.0"))); Resolver resolver = new Resolver(mock(DefaultModelResolver.class), ALIASES); resolver.traverseDeps( mockModel, Sets.newHashSet(), Sets.newHashSet("a:b"), new Rule(new DefaultArtifact("par:ent:1.2.3"))); Collection<Rule> rules = resolver.getRules(); assertThat(rules).isEmpty(); } @Test public void aliasWins() throws Exception { Aether aether = mock(Aether.class); when(aether.requestVersionRange(fromCoords("a:b:[1.0,)"))).thenReturn(newArrayList("1.0")); VersionResolver versionResolver = new VersionResolver(aether); Rule aliasedRule = new Rule(fromCoords("a:b:0"), "c"); Model mockModel = mock(Model.class); when(mockModel.getDependencies()).thenReturn(ImmutableList.of(getDependency("a:b:1.0"))); Resolver resolver = new Resolver( mock(DefaultModelResolver.class), versionResolver, ImmutableList.of(aliasedRule)); resolver.traverseDeps( mockModel, Sets.newHashSet(), Sets.newHashSet(), new Rule(new DefaultArtifact("par:ent:1.2.3"))); Collection<Rule> rules = resolver.getRules(); assertThat(rules).hasSize(2); rules.iterator().next(); Rule actualRule = rules.iterator().next(); assertThat(actualRule).isSameAs(aliasedRule); } }