You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by mi...@apache.org on 2016/06/22 19:59:48 UTC

maven git commit: [MNG-6049] Add behavior to filter resolved version ranges of an artifact

Repository: maven
Updated Branches:
  refs/heads/master 4e7088ffe -> 1b2451e79


[MNG-6049] Add behavior to filter resolved version ranges of an artifact

The DefaultVersionRangeResolver will be extended with a filter for
version range results.

This commit adds a new interface VersionRangeResultFilter and a
non-filtering DefaultVersionRangeResultFilter.

This closes #70


Project: http://git-wip-us.apache.org/repos/asf/maven/repo
Commit: http://git-wip-us.apache.org/repos/asf/maven/commit/1b2451e7
Tree: http://git-wip-us.apache.org/repos/asf/maven/tree/1b2451e7
Diff: http://git-wip-us.apache.org/repos/asf/maven/diff/1b2451e7

Branch: refs/heads/master
Commit: 1b2451e7949dbb68df5bb973742d9a9a0e84f629
Parents: 4e7088f
Author: barthel <ba...@users.noreply.github.com>
Authored: Thu Apr 30 14:37:52 2015 +0200
Committer: Michael Osipov <mi...@apache.org>
Committed: Wed Jun 22 21:36:09 2016 +0200

----------------------------------------------------------------------
 maven-aether-provider/pom.xml                   |   9 +-
 .../internal/DefaultVersionRangeResolver.java   |  31 +-
 .../DefaultVersionRangeResultFilter.java        |  45 ++
 .../internal/VersionRangeResultFilter.java      |  45 ++
 .../DefaultVersionRangeResolverTest.java        | 455 +++++++++++++++++++
 .../maven/its/mng-3092/maven-metadata.xml       | 104 +++++
 6 files changed, 680 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven/blob/1b2451e7/maven-aether-provider/pom.xml
----------------------------------------------------------------------
diff --git a/maven-aether-provider/pom.xml b/maven-aether-provider/pom.xml
index 6306d4f..4c36ebd 100644
--- a/maven-aether-provider/pom.xml
+++ b/maven-aether-provider/pom.xml
@@ -71,6 +71,10 @@ under the License.
       <artifactId>plexus-utils</artifactId>
     </dependency>
     <dependency>
+      <groupId>org.eclipse.sisu</groupId>
+      <artifactId>org.eclipse.sisu.plexus</artifactId>
+    </dependency>
+    <dependency>
       <groupId>com.google.inject</groupId>
       <artifactId>guice</artifactId>
       <classifier>no_aop</classifier>
@@ -103,11 +107,6 @@ under the License.
       <scope>test</scope>
     </dependency>
     <dependency>
-      <groupId>org.eclipse.sisu</groupId>
-      <artifactId>org.eclipse.sisu.plexus</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
       <groupId>org.mockito</groupId>
       <artifactId>mockito-core</artifactId>
       <version>1.10.19</version>

http://git-wip-us.apache.org/repos/asf/maven/blob/1b2451e7/maven-aether-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResolver.java
----------------------------------------------------------------------
diff --git a/maven-aether-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResolver.java b/maven-aether-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResolver.java
index 43f5d3c..8e53c74 100644
--- a/maven-aether-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResolver.java
+++ b/maven-aether-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResolver.java
@@ -53,9 +53,11 @@ import org.eclipse.aether.version.InvalidVersionSpecificationException;
 import org.eclipse.aether.version.Version;
 import org.eclipse.aether.version.VersionConstraint;
 import org.eclipse.aether.version.VersionScheme;
+import org.eclipse.sisu.Nullable;
 
 import javax.inject.Inject;
 import javax.inject.Named;
+import javax.inject.Singleton;
 import java.io.FileInputStream;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -68,7 +70,8 @@ import java.util.Map;
  * @author Benjamin Bentmann
  */
 @Named
-@Component( role = VersionRangeResolver.class )
+@Singleton
+@Component( role = VersionRangeResolver.class, hint = "default" )
 public class DefaultVersionRangeResolver
     implements VersionRangeResolver, Service
 {
@@ -88,6 +91,9 @@ public class DefaultVersionRangeResolver
     @Requirement
     private RepositoryEventDispatcher repositoryEventDispatcher;
 
+    @Requirement( role = VersionRangeResultFilter.class, optional = true )
+    private VersionRangeResultFilter versionRangeResultFilter = new DefaultVersionRangeResultFilter();
+
     public DefaultVersionRangeResolver()
     {
         // enable default constructor
@@ -95,12 +101,17 @@ public class DefaultVersionRangeResolver
 
     @Inject
     DefaultVersionRangeResolver( MetadataResolver metadataResolver, SyncContextFactory syncContextFactory,
-                                 RepositoryEventDispatcher repositoryEventDispatcher, LoggerFactory loggerFactory )
+                                 RepositoryEventDispatcher repositoryEventDispatcher, LoggerFactory loggerFactory,
+                                 @Nullable VersionRangeResultFilter versionRangeResultFilter )
     {
         setMetadataResolver( metadataResolver );
         setSyncContextFactory( syncContextFactory );
         setLoggerFactory( loggerFactory );
         setRepositoryEventDispatcher( repositoryEventDispatcher );
+        if ( versionRangeResultFilter != null )
+        {
+            setVersionRangeResultFilter( versionRangeResultFilter );
+        }
     }
 
     public void initService( ServiceLocator locator )
@@ -109,6 +120,11 @@ public class DefaultVersionRangeResolver
         setMetadataResolver( locator.getService( MetadataResolver.class ) );
         setSyncContextFactory( locator.getService( SyncContextFactory.class ) );
         setRepositoryEventDispatcher( locator.getService( RepositoryEventDispatcher.class ) );
+        final VersionRangeResultFilter versionRangeResultFilter = locator.getService( VersionRangeResultFilter.class );
+        if ( versionRangeResultFilter != null )
+        {
+            setVersionRangeResultFilter( versionRangeResultFilter );
+        }
     }
 
     public DefaultVersionRangeResolver setLoggerFactory( LoggerFactory loggerFactory )
@@ -143,6 +159,13 @@ public class DefaultVersionRangeResolver
         return this;
     }
 
+    public DefaultVersionRangeResolver setVersionRangeResultFilter( VersionRangeResultFilter versionRangeResultFilter )
+    {
+        this.versionRangeResultFilter = Validate.notNull( versionRangeResultFilter,
+                "versionRangeResultFilter cannot be null" );
+        return this;
+    }
+
     public VersionRangeResult resolveVersionRange( RepositorySystemSession session, VersionRangeRequest request )
         throws VersionRangeResolutionException
     {
@@ -193,7 +216,7 @@ public class DefaultVersionRangeResolver
             result.setVersions( versions );
         }
 
-        return result;
+        return versionRangeResultFilter.filterVersionRangeResult( result );
     }
 
     private Map<String, ArtifactRepository> getVersions( RepositorySystemSession session, VersionRangeResult result,
@@ -301,4 +324,4 @@ public class DefaultVersionRangeResolver
         repositoryEventDispatcher.dispatch( event.build() );
     }
 
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/maven/blob/1b2451e7/maven-aether-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResultFilter.java
----------------------------------------------------------------------
diff --git a/maven-aether-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResultFilter.java b/maven-aether-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResultFilter.java
new file mode 100644
index 0000000..5ecaf8f
--- /dev/null
+++ b/maven-aether-provider/src/main/java/org/apache/maven/repository/internal/DefaultVersionRangeResultFilter.java
@@ -0,0 +1,45 @@
+package org.apache.maven.repository.internal;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import org.eclipse.aether.resolution.VersionRangeResolutionException;
+import org.eclipse.aether.resolution.VersionRangeResult;
+
+/**
+ * Non filtering implementation of {@link VersionRangeResultFilter}.
+ *
+ * <p>
+ * This implementation reflects the Apache Maven default version range handling and don't filter anything out of
+ * {@link VersionRangeResult}.
+ * </p>
+ *
+ * @author barthel
+ * @since 3.4.0
+ */
+public class DefaultVersionRangeResultFilter implements VersionRangeResultFilter
+{
+
+    @Override
+    public VersionRangeResult filterVersionRangeResult( VersionRangeResult versionRangeResult )
+            throws VersionRangeResolutionException
+    {
+        return versionRangeResult;
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven/blob/1b2451e7/maven-aether-provider/src/main/java/org/apache/maven/repository/internal/VersionRangeResultFilter.java
----------------------------------------------------------------------
diff --git a/maven-aether-provider/src/main/java/org/apache/maven/repository/internal/VersionRangeResultFilter.java b/maven-aether-provider/src/main/java/org/apache/maven/repository/internal/VersionRangeResultFilter.java
new file mode 100644
index 0000000..64e1dbb
--- /dev/null
+++ b/maven-aether-provider/src/main/java/org/apache/maven/repository/internal/VersionRangeResultFilter.java
@@ -0,0 +1,45 @@
+package org.apache.maven.repository.internal;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import org.eclipse.aether.resolution.VersionRangeResolutionException;
+import org.eclipse.aether.resolution.VersionRangeResult;
+
+/**
+ * Filters the resolved versions provided by {@link VersionRangeResult#getVersions()}.
+ *
+ * @author barthel
+ * @since 3.4.0
+ */
+public interface VersionRangeResultFilter
+{
+
+    /**
+     *
+     * @param versionRangeResult The version range result, must not be {@code null}
+     * @return A filtered version range result, never {@code null}
+     * @throws VersionRangeResolutionException If the requested range could not be parsed. Note that an empty range does
+     * not raise an exception.
+     * @see VersionRangeResult#getVersions()
+     */
+    VersionRangeResult filterVersionRangeResult( VersionRangeResult versionRangeResult )
+            throws VersionRangeResolutionException;
+
+}

http://git-wip-us.apache.org/repos/asf/maven/blob/1b2451e7/maven-aether-provider/src/test/java/org/apache/maven/repository/internal/DefaultVersionRangeResolverTest.java
----------------------------------------------------------------------
diff --git a/maven-aether-provider/src/test/java/org/apache/maven/repository/internal/DefaultVersionRangeResolverTest.java b/maven-aether-provider/src/test/java/org/apache/maven/repository/internal/DefaultVersionRangeResolverTest.java
new file mode 100644
index 0000000..0e7b3dd
--- /dev/null
+++ b/maven-aether-provider/src/test/java/org/apache/maven/repository/internal/DefaultVersionRangeResolverTest.java
@@ -0,0 +1,455 @@
+package org.apache.maven.repository.internal;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import java.util.Iterator;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.impl.VersionRangeResolver;
+import org.eclipse.aether.resolution.VersionRangeRequest;
+import org.eclipse.aether.resolution.VersionRangeResolutionException;
+import org.eclipse.aether.resolution.VersionRangeResult;
+import org.eclipse.aether.util.version.GenericVersionScheme;
+import org.eclipse.aether.version.Version;
+import org.eclipse.aether.version.VersionScheme;
+
+/**
+ * Tests the {@link DefaultVersionRangeResolver} based on 'virtual' repository data stored at
+ * {@literal /maven-aether-provider/src/test/resources/repo/org/apache/maven/its/mng-3092/maven-metadata.xml}
+ * <p>
+ * Note: Information about the version scheme: {@link org.eclipse.aether.util.version.GenericVersionScheme}.<br/>
+ * Design document for dependency version ranges: <a
+ * href="https://cwiki.apache.org/confluence/display/MAVENOLD/Dependency+Mediation+and+Conflict+Resolution"
+ * >https://cwiki.apache.org/confluence/display/MAVENOLD/Dependency+Mediation+and+Conflict+Resolution</a>
+ * </p>
+ */
+public class DefaultVersionRangeResolverTest
+        extends AbstractRepositoryTestCase
+{
+    private final VersionScheme versionScheme = new GenericVersionScheme();
+
+    private DefaultVersionRangeResolver sut;
+
+    private VersionRangeRequest request;
+
+    @Override
+    protected void setUp()
+            throws Exception
+    {
+        super.setUp();
+        // be sure we're testing the right class, i.e. DefaultVersionRangeResolver.class
+        sut = ( DefaultVersionRangeResolver ) lookup( VersionRangeResolver.class, "default" );
+        request = new VersionRangeRequest();
+        request.addRepository( newTestRepository() );
+    }
+
+    @Override
+    protected void tearDown()
+            throws Exception
+    {
+        sut = null;
+        super.tearDown();
+    }
+
+    /**
+     * Test resolves version range {@code (,2.0.0]} (x &lt;= 2.0.0).
+     * <p>
+     * The expected version range starts with the lowest version {@code 1.0.0-SNAPSHOT} and ends with the highest
+     * inclusive version {@code 2.0.0}.
+     * </p>
+     *
+     * @throws Exception
+     */
+    public void testLeftOpenRightInclusive()
+            throws Exception
+    {
+        final Version expectedLowestVersion = versionScheme.parseVersion( "1.0.0-SNAPSHOT" );
+        final Version expectedHighestVersion = versionScheme.parseVersion( "2.0.0" );
+        final Version expectedReleaseVersion = versionScheme.parseVersion( "1.3.0" );
+        final Version expectedSnapshotVersion = versionScheme.parseVersion( "1.2.1-SNAPSHOT" );
+
+        request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "mng-3092", "jar", "(,2.0.0]" ) );
+
+        final VersionRangeResult result = sut.resolveVersionRange( session, request );
+        assertNotNull( result );
+        assertEquals( expectedLowestVersion, result.getLowestVersion() );
+        assertEquals( expectedHighestVersion, result.getHighestVersion() );
+        assertEquals( 34, result.getVersions().size() );
+        assertTrue( result.getVersions().contains( expectedReleaseVersion ) );
+        assertTrue( result.getVersions().contains( expectedSnapshotVersion ) );
+    }
+
+    /**
+     * Test resolves version range {@code 1.2.0}.
+     * <p>
+     * The passed value is a 'soft' requirement on {@code 1.2.0} and <b>not</b> a real version range pattern. The
+     * resolver does nothing but insert the passed value into {@link VersionRangeResult}.
+     * </p>
+     *
+     * @throws Exception
+     */
+    public void testSoft()
+            throws Exception
+    {
+        final Version expectedVersion = versionScheme.parseVersion( "1.2.0" );
+
+        request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "mng-3092", "jar", "1.2.0" ) );
+
+        final VersionRangeResult result = sut.resolveVersionRange( session, request );
+        assertNotNull( result );
+        assertEquals( expectedVersion, result.getLowestVersion() );
+        assertEquals( expectedVersion, result.getHighestVersion() );
+        assertEquals( 1, result.getVersions().size() );
+    }
+
+    /**
+     * Test resolves version range {@code 1.2.4} for a <b>unknown</b> version.
+     * <p>
+     * The passed value is a 'soft' requirement on {@code 1.2.4} and <b>not</b> a real version range pattern. The
+     * resolver does nothing but insert the passed value into {@link VersionRangeResult}.
+     * </p>
+     *
+     * @throws Exception
+     */
+    public void testSoft_unknown()
+            throws Exception
+    {
+        final Version expectedVersion = versionScheme.parseVersion( "1.2.4" );
+
+        request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "mng-3092", "jar", "1.2.4" ) );
+
+        final VersionRangeResult result = sut.resolveVersionRange( session, request );
+        assertNotNull( result );
+        assertEquals( expectedVersion, result.getLowestVersion() );
+        assertEquals( expectedVersion, result.getHighestVersion() );
+        assertEquals( 1, result.getVersions().size() );
+    }
+
+    /**
+     * Test resolves version range {@code [1.2.0]}.
+     * <p>
+     * The passed value is a 'hard' requirement on {@code 1.2.0}.
+     * </p>
+     *
+     * @throws Exception
+     */
+    public void testHard()
+            throws Exception
+    {
+        final Version expectedVersion = versionScheme.parseVersion( "1.2.0" );
+
+        request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "mng-3092", "jar", "[1.2.0]" ) );
+
+        final VersionRangeResult result = sut.resolveVersionRange( session, request );
+        assertNotNull( result );
+        assertEquals( expectedVersion, result.getLowestVersion() );
+        assertEquals( expectedVersion, result.getHighestVersion() );
+        assertEquals( 1, result.getVersions().size() );
+    }
+
+    /**
+     * Test resolves version range {@code [1.2.4]} for a <b>unknown</b> version.
+     * <p>
+     * The passed value is a 'hard' requirement on the unknown version {@code 1.2.4}. The resolver does nothing but
+     * insert the passed value into {@link VersionRangeResult}.
+     * </p>
+     *
+     * @throws Exception
+     */
+    public void testHard_unknown()
+            throws Exception
+    {
+
+        request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "mng-3092", "jar", "[1.2.4]" ) );
+
+        final VersionRangeResult result = sut.resolveVersionRange( session, request );
+        assertNotNull( result );
+        assertNull( result.getLowestVersion() );
+        assertNull( result.getHighestVersion() );
+        assertTrue( result.getVersions().isEmpty() );
+    }
+
+    /**
+     * Test resolves version range {@code [1.2]}.
+     * <p>
+     * Based on javadoc of {@link GenericVersionScheme}:<br>
+     * <blockquote> An empty segment/string is equivalent to 0. </blockquote>
+     * </p>
+     *
+     * @see GenericVersionScheme
+     * @throws Exception
+     */
+    public void testHard_short()
+            throws Exception
+    {
+        final Version expectedVersion = versionScheme.parseVersion( "1.2.0" );
+
+        request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "mng-3092", "jar", "[1.2]" ) );
+
+        final VersionRangeResult result = sut.resolveVersionRange( session, request );
+        assertNotNull( result );
+        assertEquals( expectedVersion, result.getLowestVersion() );
+        assertEquals( expectedVersion, result.getHighestVersion() );
+        assertEquals( 1, result.getVersions().size() );
+    }
+
+    /**
+     * Test resolves version range {@code [1.2.*]}.
+     * <p>
+     * Based on javadoc of {@link GenericVersionScheme}:<br>
+     * <blockquote> In addition to the above mentioned qualifiers, the tokens "min" and "max" may be used as final
+     * version segment to denote the smallest/greatest version having a given prefix. For example, "1.2.min" denotes the
+     * smallest version in the 1.2 line, "1.2.max" denotes the greatest version in the 1.2 line. A version range of the
+     * form "[M.N.*]" is short for "[M.N.min, M.N.max]". </blockquote>
+     * </p>
+     *
+     * @see GenericVersionScheme
+     * @throws Exception
+     */
+    public void testHard_wildcard()
+            throws Exception
+    {
+        final Version expectedLowestVersion = versionScheme.parseVersion( "1.2.0-SNAPSHOT" );
+        final Version expectedHighestVersion = versionScheme.parseVersion( "1.2.3" );
+        final Version expectedSnapshotVersion = versionScheme.parseVersion( "1.2.2-SNAPSHOT" );
+        final Version expectedReleaseVersion = versionScheme.parseVersion( "1.2.1" );
+
+        request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "mng-3092", "jar", "[1.2.*]" ) );
+
+        final VersionRangeResult result = sut.resolveVersionRange( session, request );
+        assertNotNull( result );
+        assertEquals( expectedLowestVersion, result.getLowestVersion() );
+        assertEquals( expectedHighestVersion, result.getHighestVersion() );
+        assertEquals( 8, result.getVersions().size() );
+        assertTrue( result.getVersions().contains( expectedSnapshotVersion ) );
+        assertTrue( result.getVersions().contains( expectedReleaseVersion ) );
+    }
+
+    /**
+     * Test resolves version range {@code [1.0.0,2.0.0]} (1.0.0 &lt;= x &lt;= 2.0.0).
+     * <p>
+     * The expected version range starts with the lowest version {@code 1.0.0} and ends with the highest inclusive
+     * version {@code 2.0.0}.
+     * </p>
+     *
+     * @throws Exception
+     */
+    public void testLeftInclusiveRightInclusive()
+            throws Exception
+    {
+        final Version expectedLowestVersion = versionScheme.parseVersion( "1.0.0" );
+        final Version expectedHighestVersion = versionScheme.parseVersion( "2.0.0" );
+        final Version expectedSnapshotVersion = versionScheme.parseVersion( "1.3.1-SNAPSHOT" );
+        final Version expectedReleaseVersion = versionScheme.parseVersion( "1.3.1" );
+
+        request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "mng-3092", "jar", "[1.0.0,2.0.0]" ) );
+
+        final VersionRangeResult result = sut.resolveVersionRange( session, request );
+        assertNotNull( result );
+        assertEquals( expectedLowestVersion, result.getLowestVersion() );
+        assertEquals( expectedHighestVersion, result.getHighestVersion() );
+        assertEquals( 33, result.getVersions().size() );
+        assertTrue( result.getVersions().contains( expectedSnapshotVersion ) );
+        assertTrue( result.getVersions().contains( expectedReleaseVersion ) );
+    }
+
+    /**
+     * Test resolves version range {@code [1.0.0,2.0.0)} (1.0.0 &lt;= x &lt; 2.0.0).
+     * <p>
+     * The expected version range starts with the lowest version {@code 1.0.0} and ends with the highest inclusive
+     * version {@code 2.0.0-SNAPSHOT}.
+     * </p>
+     * <p>
+     * Note: {@code 2.0.0-SNAPSHOT} is 'lower' than {@code 2.0.0} and will be part of this version range.
+     * </p>
+     *
+     * @throws Exception
+     */
+    public void testLeftInclusiveRightExclusive()
+            throws Exception
+    {
+        final Version expectedLowestVersion = versionScheme.parseVersion( "1.0.0" );
+        final Version expectedHighestVersion = versionScheme.parseVersion( "2.0.0-SNAPSHOT" );
+        final Version expectedSnapshotVersion = versionScheme.parseVersion( "1.3.1-SNAPSHOT" );
+
+        request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "mng-3092", "jar", "[1.0.0,2.0.0)" ) );
+
+        final VersionRangeResult result = sut.resolveVersionRange( session, request );
+        assertNotNull( result );
+        assertEquals( expectedLowestVersion, result.getLowestVersion() );
+        assertEquals( 32, result.getVersions().size() );
+        assertEquals( expectedHighestVersion, result.getHighestVersion() );
+        assertTrue( result.getVersions().contains( expectedSnapshotVersion ) );
+    }
+
+    /**
+     * Test resolves version range {@code (1.0.0,2.0.0]} (1.0.0 &lt; x &lt;= 2.0.0).
+     * <p>
+     * The expected version range starts with the lowest version {@code 1.0.1-SNAPSHOT} and ends with the highest
+     * inclusive version {@code 2.0.0}.
+     * </p>
+     * <p>
+     * Note: The version {@code 1.0.0} will be excluded by pattern and {@code 1.0.1-SNAPSHOT} is the lowest version
+     * instead.
+     * </p>
+     *
+     * @throws Exception
+     */
+    public void testLeftExclusiveRightInclusive()
+            throws Exception
+    {
+        final Version expectedLowestVersion = versionScheme.parseVersion( "1.0.1-SNAPSHOT" );
+        final Version expectedHighestVersion = versionScheme.parseVersion( "2.0.0" );
+        final Version expectedSnapshotVersion = versionScheme.parseVersion( "1.3.1-SNAPSHOT" );
+
+        request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "mng-3092", "jar", "(1.0.0,2.0.0]" ) );
+
+        final VersionRangeResult result = sut.resolveVersionRange( session, request );
+        assertNotNull( result );
+        assertEquals( expectedLowestVersion, result.getLowestVersion() );
+        assertEquals( expectedHighestVersion, result.getHighestVersion() );
+        assertEquals( 32, result.getVersions().size() );
+        assertTrue( result.getVersions().contains( expectedSnapshotVersion ) );
+    }
+
+    /**
+     * Test resolves version range {@code (1.0.0,2.0.0)} (1.0.0 &lt; x &lt; 2.0.0).
+     * <p>
+     * The expected version range starts with the lowest version {@code 1.0.1-SNAPSHOT} and ends with the highest
+     * inclusive version {@code 2.0.0-SNAPSHOT}.
+     * </p>
+     *
+     * @throws Exception
+     */
+    public void testLeftExclusiveRightExclusive()
+            throws Exception
+    {
+        final Version expectedLowestVersion = versionScheme.parseVersion( "1.0.1-SNAPSHOT" );
+        final Version expectedHighestVersion = versionScheme.parseVersion( "2.0.0-SNAPSHOT" );
+        final Version expectedSnapshotVersion = versionScheme.parseVersion( "1.3.1-SNAPSHOT" );
+
+        request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "mng-3092", "jar", "(1.0.0,2.0.0)" ) );
+
+        final VersionRangeResult result = sut.resolveVersionRange( session, request );
+        assertNotNull( result );
+        assertEquals( expectedLowestVersion, result.getLowestVersion() );
+        assertEquals( expectedHighestVersion, result.getHighestVersion() );
+        assertEquals( 31, result.getVersions().size() );
+        assertTrue( result.getVersions().contains( expectedSnapshotVersion ) );
+    }
+
+    /**
+     * Test resolves version range {@code [1.0.0,)} (x &lt; 1.0.0).
+     * <p>
+     * The expected version range starts with the lowest version {@code 1.0.0} and ends with the highest inclusive
+     * version {@code 3.1.0-SNAPSHOT}.
+     * </p>
+     *
+     * @throws Exception
+     */
+    public void testLeftInclusiveRightOpen()
+            throws Exception
+    {
+        final Version expectedLowestVersion = versionScheme.parseVersion( "1.0.0" );
+        final Version expectedHighestVersion = versionScheme.parseVersion( "3.1.0-SNAPSHOT" );
+        final Version expectedReleaseVersion = versionScheme.parseVersion( "2.0.0" );
+
+        request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "mng-3092", "jar", "[1.0.0,)" ) );
+
+        final VersionRangeResult result = sut.resolveVersionRange( session, request );
+        assertNotNull( result );
+        assertEquals( expectedLowestVersion, result.getLowestVersion() );
+        assertEquals( expectedHighestVersion, result.getHighestVersion() );
+        assertEquals( 69, result.getVersions().size() );
+        assertTrue( result.getVersions().contains( expectedReleaseVersion ) );
+    }
+
+    /**
+     * Test filter of resolved version range {@code (,2.0.0]} (x &lt;= 2.0.0).
+     * <p>
+     * The expected versions are only non {@code SNAPSHOT}. The version range starts with the lowest version
+     * {@code 1.0.0} and ends with the highest inclusive version {@code 2.0.0}.
+     * </p>
+     *
+     * @throws Exception
+     */
+    public void testVersionRangeResultFilter()
+            throws Exception
+    {
+        final Version expectedLowestVersion = versionScheme.parseVersion( "1.0.0" );
+        final Version expectedHighestVersion = versionScheme.parseVersion( "2.0.0" );
+
+        request.setArtifact( new DefaultArtifact( "org.apache.maven.its", "mng-3092", "jar", "(,2.0.0]" ) );
+
+        sut.setVersionRangeResultFilter( new TestOnlyVersionRangeResultFilter() );
+        final VersionRangeResult result = sut.resolveVersionRange( session, request );
+
+        assertNotNull( result );
+        assertEquals( expectedLowestVersion, result.getLowestVersion() );
+        assertEquals( expectedHighestVersion, result.getHighestVersion() );
+        assertEquals( 17, result.getVersions().size() );
+        for ( Iterator<Version> it = result.getVersions().iterator(); it.hasNext(); )
+        {
+            // XXX: better way to identify a SNAPSHOT version
+            if ( String.valueOf( it.next() ).endsWith( "SNAPSHOT" ) )
+            {
+                fail( "Non filtered SNAPSHOT version in version range result." );
+            }
+        }
+    }
+
+    /**
+     * Test error handling if invalid {@link VersionRangeResultFilter} will be set.
+     *
+     * @throws Exception
+     */
+    public void testInvalidVersionRangeResultFilter()
+            throws Exception
+    {
+        try
+        {
+            sut.setVersionRangeResultFilter( null );
+            fail( "Exception expected by set invalid version range result filter." );
+        }
+        catch ( NullPointerException iae )
+        {
+          assertEquals( "versionRangeResultFilter cannot be null", iae.getMessage() );
+        }
+    }
+
+    private final class TestOnlyVersionRangeResultFilter implements VersionRangeResultFilter
+    {
+
+        @Override
+        public VersionRangeResult filterVersionRangeResult( VersionRangeResult versionRangeResult )
+                throws VersionRangeResolutionException
+        {
+            for ( Iterator<Version> it = versionRangeResult.getVersions().iterator(); it.hasNext(); )
+            {
+                // XXX: better way to identify a SNAPSHOT version
+                if ( String.valueOf( it.next() ).endsWith( "SNAPSHOT" ) )
+                {
+                    it.remove();
+                }
+            }
+            return versionRangeResult;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven/blob/1b2451e7/maven-aether-provider/src/test/resources/repo/org/apache/maven/its/mng-3092/maven-metadata.xml
----------------------------------------------------------------------
diff --git a/maven-aether-provider/src/test/resources/repo/org/apache/maven/its/mng-3092/maven-metadata.xml b/maven-aether-provider/src/test/resources/repo/org/apache/maven/its/mng-3092/maven-metadata.xml
new file mode 100644
index 0000000..65ab4b0
--- /dev/null
+++ b/maven-aether-provider/src/test/resources/repo/org/apache/maven/its/mng-3092/maven-metadata.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you 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.
+-->
+
+<metadata xmlns="http://maven.apache.org/METADATA/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/METADATA/1.1.0 http://maven.apache.org/xsd/metadata-1.1.0.xsd">
+  <groupId>ut.simple</groupId>
+  <artifactId>parent</artifactId>
+  <versioning>
+    <latest>3.1.0-SNAPSHOT</latest>
+    <release>3.0.1</release>
+    <versions>
+      <version>1.0.0-SNAPSHOT</version>
+      <version>1.0.0</version>
+      <version>1.0.1-SNAPSHOT</version>
+      <version>1.0.1</version>
+      <version>1.0.2-SNAPSHOT</version>
+      <version>1.0.2</version>
+      <version>1.0.3-SNAPSHOT</version>
+      <version>1.0.3</version>
+      <version>1.1.0-SNAPSHOT</version>
+      <version>1.1.0</version>
+      <version>1.1.1-SNAPSHOT</version>
+      <version>1.1.1</version>
+      <version>1.1.2-SNAPSHOT</version>
+      <version>1.1.2</version>
+      <version>1.1.3-SNAPSHOT</version>
+      <version>1.1.3</version>
+      <version>1.2.0-SNAPSHOT</version>
+      <version>1.2.0</version>
+      <version>1.2.1-SNAPSHOT</version>
+      <version>1.2.1</version>
+      <version>1.2.2-SNAPSHOT</version>
+      <version>1.2.2</version>
+      <version>1.2.3-SNAPSHOT</version>
+      <version>1.2.3</version>
+      <version>1.3.0-SNAPSHOT</version>
+      <version>1.3.0</version>
+      <version>1.3.1-SNAPSHOT</version>
+      <version>1.3.1</version>
+      <version>1.3.2-SNAPSHOT</version>
+      <version>1.3.2</version>
+      <version>1.3.3-SNAPSHOT</version>
+      <version>1.3.3</version>
+      <version>2.0.0-SNAPSHOT</version>
+      <version>2.0.0</version>
+      <version>2.0.1-SNAPSHOT</version>
+      <version>2.0.1</version>
+      <version>2.0.2-SNAPSHOT</version>
+      <version>2.0.2</version>
+      <version>2.0.3-SNAPSHOT</version>
+      <version>2.0.3</version>
+      <version>2.1.0-SNAPSHOT</version>
+      <version>2.1.0</version>
+      <version>2.1.1-SNAPSHOT</version>
+      <version>2.1.1</version>
+      <version>2.1.2-SNAPSHOT</version>
+      <version>2.1.2</version>
+      <version>2.1.3-SNAPSHOT</version>
+      <version>2.1.3</version>
+      <version>2.2.0-SNAPSHOT</version>
+      <version>2.2.0</version>
+      <version>2.2.1-SNAPSHOT</version>
+      <version>2.2.1</version>
+      <version>2.2.2-SNAPSHOT</version>
+      <version>2.2.2</version>
+      <version>2.2.3-SNAPSHOT</version>
+      <version>2.2.3</version>
+      <version>2.3.0-SNAPSHOT</version>
+      <version>2.3.0</version>
+      <version>2.3.1-SNAPSHOT</version>
+      <version>2.3.1</version>
+      <version>2.3.2-SNAPSHOT</version>
+      <version>2.3.2</version>
+      <version>2.3.3-SNAPSHOT</version>
+      <version>2.3.3</version>
+      <version>3.0.0-SNAPSHOT</version>
+      <version>3.0.0</version>
+      <version>3.0.1-SNAPSHOT</version>
+      <version>3.0.1</version>
+      <version>3.0.2-SNAPSHOT</version>
+      <version>3.1.0-SNAPSHOT</version>
+    </versions>
+    <lastUpdated>20150401000001</lastUpdated>
+  </versioning>
+</metadata>
+