This is an automated email from the ASF dual-hosted git repository.

gnodet pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven.git


The following commit(s) were added to refs/heads/master by this push:
     new a381970609 [MNG-7969][MNG-7981] Add missing information on Maven 4 
version api to fix an exception (#1355)
a381970609 is described below

commit a3819706093b69a8cf07c8a01ceaddfb6bb1c0b7
Author: Tamas Cservenak <ta...@cservenak.net>
AuthorDate: Tue Dec 19 20:08:55 2023 +0100

    [MNG-7969][MNG-7981] Add missing information on Maven 4 version api to fix 
an exception (#1355)
    
    Co-authored-by: Guillaume Nodet <gno...@gmail.com>
---
 .../org/apache/maven/api/ArtifactCoordinate.java   |   2 +-
 .../main/java/org/apache/maven/api/Session.java    |  11 +
 .../main/java/org/apache/maven/api/Version.java    |   3 -
 .../{VersionRange.java => VersionConstraint.java}  |  26 +-
 .../java/org/apache/maven/api/VersionRange.java    |  31 +-
 .../apache/maven/api/services/VersionParser.java   |  11 +
 .../maven/internal/impl/AbstractSession.java       |   5 +
 .../internal/impl/DefaultArtifactCoordinate.java   |   6 +-
 .../internal/impl/DefaultDependencyCoordinate.java |  10 +-
 .../internal/impl/DefaultDependencyResolver.java   |  11 +-
 .../apache/maven/internal/impl/DefaultProject.java |  13 +-
 .../maven/internal/impl/DefaultVersionParser.java  |  11 +-
 .../impl/DefaultConsumerPomBuilder.java            |   4 +-
 .../org/apache/maven/internal/impl/TestApi.java    |  65 ++-
 .../1.0.3/commons-logging-1.0.3.jar                | Bin
 .../1.0.3/commons-logging-1.0.3.jar.sha1           |   0
 .../1.0.3/commons-logging-1.0.3.pom                |   0
 .../1.0.3/commons-logging-1.0.3.pom.sha1           |   0
 ...logging-1.0.4-javadoc-resources.jar.lastUpdated |   0
 .../1.0.4/commons-logging-1.0.4.jar                | Bin
 .../1.0.4/commons-logging-1.0.4.jar.sha1           |   0
 .../1.0.4/commons-logging-1.0.4.pom                |   0
 .../1.0.4/commons-logging-1.0.4.pom.sha1           |   0
 .../apiv4-repo/junit/junit/4.13.1/junit-4.13.1.jar | Bin 0 -> 382708 bytes
 .../junit/junit/4.13.1/junit-4.13.1.jar.sha1       |   1 +
 .../apiv4-repo/junit/junit/4.13.1/junit-4.13.1.pom | 587 +++++++++++++++++++++
 .../junit/junit/4.13.1/junit-4.13.1.pom.sha1       |   1 +
 .../maven/model/building/DefaultModelBuilder.java  |   6 +-
 .../model/building/DefaultModelBuilderFactory.java |  16 +-
 ...{VersionParser.java => ModelVersionParser.java} |  13 +-
 .../internal/DefaultModelVersionParser.java        | 146 ++++-
 .../repository/internal/AbstractVersionTest.java   |  67 +++
 .../internal/ModelVersionParserTest.java           | 101 ++++
 .../repository/internal/VersionRangeTest.java      | 142 +++++
 .../maven/repository/internal/VersionTest.java     | 453 ++++++++++++++++
 35 files changed, 1687 insertions(+), 55 deletions(-)

diff --git 
a/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinate.java 
b/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinate.java
index 553e60fad3..7eb96410b7 100644
--- 
a/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinate.java
+++ 
b/api/maven-api-core/src/main/java/org/apache/maven/api/ArtifactCoordinate.java
@@ -62,7 +62,7 @@ public interface ArtifactCoordinate {
      * @return the version
      */
     @Nonnull
-    VersionRange getVersion();
+    VersionConstraint getVersion();
 
     /**
      * The extension of the artifact.
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java 
b/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java
index b7f6098868..a643ab9e8b 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java
@@ -463,4 +463,15 @@ public interface Session {
      */
     @Nonnull
     VersionRange parseVersionRange(@Nonnull String versionRange);
+
+    /**
+     * Parses the specified version constraint specification, for example 
"1.0" or "[1.0,2.0)".
+     * <p>
+     * Shortcut for {@code 
getService(VersionParser.class).parseVersionConstraint(...)}.
+     *
+     * @see 
org.apache.maven.api.services.VersionParser#parseVersionConstraint(String)
+     * @throws org.apache.maven.api.services.VersionParserException if the 
parsing failed
+     */
+    @Nonnull
+    VersionConstraint parseVersionConstraint(@Nonnull String 
versionConstraint);
 }
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Version.java 
b/api/maven-api-core/src/main/java/org/apache/maven/api/Version.java
index 367385fa38..ee29cc2b7e 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/Version.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Version.java
@@ -30,9 +30,6 @@ import org.apache.maven.api.annotations.Nonnull;
  */
 @Experimental
 public interface Version extends Comparable<Version> {
-
-    // TODO: add access to the version information
-
     /**
      * Returns a string representation of this version.
      * @return the string representation of this version
diff --git 
a/api/maven-api-core/src/main/java/org/apache/maven/api/VersionRange.java 
b/api/maven-api-core/src/main/java/org/apache/maven/api/VersionConstraint.java
similarity index 60%
copy from 
api/maven-api-core/src/main/java/org/apache/maven/api/VersionRange.java
copy to 
api/maven-api-core/src/main/java/org/apache/maven/api/VersionConstraint.java
index 375ac5f9f9..00633c186b 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/VersionRange.java
+++ 
b/api/maven-api-core/src/main/java/org/apache/maven/api/VersionConstraint.java
@@ -20,19 +20,33 @@ package org.apache.maven.api;
 
 import org.apache.maven.api.annotations.Experimental;
 import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.annotations.Nullable;
 
 /**
- * A range of versions.
+ * Version constraint for dependency. Constraint is either a range ("[1,2)") 
or recommended version ("1.0").
  *
  * @since 4.0.0
  */
 @Experimental
-public interface VersionRange {
+public interface VersionConstraint {
+    /**
+     * Returns the range of this constraint, or {@code null} if none.
+     * <p>
+     * Note: only one, this method or {@link #getRecommendedVersion()} method 
must return non-{@code null} value.
+     */
+    @Nullable
+    VersionRange getVersionRange();
 
-    // TODO: v4: add access to the version information
+    /**
+     * Returns the recommended version of this constraint, or {@code null} if 
none.
+     * <p>
+     * Note: only one, this method or {@link #getVersionRange()} method must 
return non-{@code null} value.
+     */
+    @Nullable
+    Version getRecommendedVersion();
 
     /**
-     * Determines whether the specified version is contained within this range.
+     * Determines whether the specified version is contained within this 
constraint.
      *
      * @param version the version to test, must not be {@code null}
      * @return {@code true} if this range contains the specified version, 
{@code false} otherwise
@@ -40,8 +54,8 @@ public interface VersionRange {
     boolean contains(@Nonnull Version version);
 
     /**
-     * Returns a string representation of this version range
-     * @return the string representation of this version range
+     * Returns a string representation of this version constraint
+     * @return the string representation of this version constraint
      */
     @Nonnull
     String asString();
diff --git 
a/api/maven-api-core/src/main/java/org/apache/maven/api/VersionRange.java 
b/api/maven-api-core/src/main/java/org/apache/maven/api/VersionRange.java
index 375ac5f9f9..6f9ff4427b 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/VersionRange.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/VersionRange.java
@@ -20,6 +20,7 @@ package org.apache.maven.api;
 
 import org.apache.maven.api.annotations.Experimental;
 import org.apache.maven.api.annotations.Nonnull;
+import org.apache.maven.api.annotations.Nullable;
 
 /**
  * A range of versions.
@@ -28,9 +29,6 @@ import org.apache.maven.api.annotations.Nonnull;
  */
 @Experimental
 public interface VersionRange {
-
-    // TODO: v4: add access to the version information
-
     /**
      * Determines whether the specified version is contained within this range.
      *
@@ -39,10 +37,37 @@ public interface VersionRange {
      */
     boolean contains(@Nonnull Version version);
 
+    /**
+     * Returns the upper boundary of this range, or {@code null} if none.
+     */
+    @Nullable
+    Boundary getUpperBoundary();
+
+    /**
+     * Returns the lower boundary of this range, or {@code null} if none.
+     */
+    @Nullable
+    Boundary getLowerBoundary();
+
     /**
      * Returns a string representation of this version range
      * @return the string representation of this version range
      */
     @Nonnull
     String asString();
+
+    /**
+     * Represents range boundary.
+     */
+    interface Boundary {
+        /**
+         * The bounding version.
+         */
+        Version getVersion();
+
+        /**
+         * Returns {@code true} if version is included of the range.
+         */
+        boolean isInclusive();
+    }
 }
diff --git 
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParser.java
 
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParser.java
index 1e9b591392..d344122d7f 100644
--- 
a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParser.java
+++ 
b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionParser.java
@@ -20,6 +20,7 @@ package org.apache.maven.api.services;
 
 import org.apache.maven.api.Service;
 import org.apache.maven.api.Version;
+import org.apache.maven.api.VersionConstraint;
 import org.apache.maven.api.VersionRange;
 import org.apache.maven.api.annotations.Experimental;
 import org.apache.maven.api.annotations.Nonnull;
@@ -53,6 +54,16 @@ public interface VersionParser extends Service {
     @Nonnull
     VersionRange parseVersionRange(@Nonnull String range);
 
+    /**
+     * Parses the specified version constraint specification, for example 
"1.0" or "[1.0,2.0)".
+     *
+     * @param constraint the constraint specification to parse, must not be 
{@code null}
+     * @return the parsed version constraint, never {@code null}
+     * @throws VersionParserException if the range specification violates the 
syntax rules of this scheme
+     */
+    @Nonnull
+    VersionConstraint parseVersionConstraint(@Nonnull String constraint);
+
     /**
      * Checks whether a given artifact version is considered a {@code 
SNAPSHOT} or not.
      */
diff --git 
a/maven-core/src/main/java/org/apache/maven/internal/impl/AbstractSession.java 
b/maven-core/src/main/java/org/apache/maven/internal/impl/AbstractSession.java
index 0ef8cdf384..ce90e19be7 100644
--- 
a/maven-core/src/main/java/org/apache/maven/internal/impl/AbstractSession.java
+++ 
b/maven-core/src/main/java/org/apache/maven/internal/impl/AbstractSession.java
@@ -504,6 +504,11 @@ public abstract class AbstractSession implements 
InternalSession {
         return getService(VersionParser.class).parseVersionRange(versionRange);
     }
 
+    @Override
+    public VersionConstraint parseVersionConstraint(String versionConstraint) {
+        return 
getService(VersionParser.class).parseVersionConstraint(versionConstraint);
+    }
+
     @Override
     public Version resolveVersion(ArtifactCoordinate artifact) {
         return getService(VersionResolver.class).resolve(this, 
artifact).getVersion();
diff --git 
a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultArtifactCoordinate.java
 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultArtifactCoordinate.java
index 0dffe01927..04b4c73e28 100644
--- 
a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultArtifactCoordinate.java
+++ 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultArtifactCoordinate.java
@@ -21,7 +21,7 @@ package org.apache.maven.internal.impl;
 import java.util.Objects;
 
 import org.apache.maven.api.ArtifactCoordinate;
-import org.apache.maven.api.VersionRange;
+import org.apache.maven.api.VersionConstraint;
 import org.apache.maven.api.annotations.Nonnull;
 
 import static org.apache.maven.internal.impl.Utils.nonNull;
@@ -57,8 +57,8 @@ public class DefaultArtifactCoordinate implements 
ArtifactCoordinate {
 
     @Nonnull
     @Override
-    public VersionRange getVersion() {
-        return session.parseVersionRange(coordinate.getVersion());
+    public VersionConstraint getVersion() {
+        return session.parseVersionConstraint(coordinate.getVersion());
     }
 
     @Override
diff --git 
a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyCoordinate.java
 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyCoordinate.java
index 64c2939e98..54121e8da6 100644
--- 
a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyCoordinate.java
+++ 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyCoordinate.java
@@ -20,11 +20,7 @@ package org.apache.maven.internal.impl;
 
 import java.util.Collection;
 
-import org.apache.maven.api.DependencyCoordinate;
-import org.apache.maven.api.Exclusion;
-import org.apache.maven.api.Scope;
-import org.apache.maven.api.Type;
-import org.apache.maven.api.VersionRange;
+import org.apache.maven.api.*;
 import org.apache.maven.api.annotations.Nonnull;
 import org.apache.maven.api.annotations.Nullable;
 import org.apache.maven.api.services.TypeRegistry;
@@ -63,8 +59,8 @@ public class DefaultDependencyCoordinate implements 
DependencyCoordinate {
     }
 
     @Override
-    public VersionRange getVersion() {
-        return 
session.parseVersionRange(dependency.getArtifact().getVersion());
+    public VersionConstraint getVersion() {
+        return 
session.parseVersionConstraint(dependency.getArtifact().getVersion());
     }
 
     @Override
diff --git 
a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolver.java
 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolver.java
index 56f3c5bd53..c5ba1a32b0 100644
--- 
a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolver.java
+++ 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultDependencyResolver.java
@@ -112,8 +112,15 @@ public class DefaultDependencyResolver implements 
DependencyResolver {
         List<ArtifactCoordinate> coordinates =
                 
deps.stream().map(Artifact::toCoordinate).collect(Collectors.toList());
         Map<Artifact, Path> artifacts = session.resolveArtifacts(coordinates);
-        Map<Dependency, Path> dependencies = 
deps.stream().collect(Collectors.toMap(d -> d, artifacts::get));
-        List<Path> paths = new ArrayList<>(dependencies.values());
+        Map<Dependency, Path> dependencies = new LinkedHashMap<>();
+        List<Path> paths = new ArrayList<>();
+        for (Dependency d : deps) {
+            Path path = artifacts.get(d);
+            if (dependencies.put(d, path) != null) {
+                throw new IllegalStateException("Duplicate key");
+            }
+            paths.add(path);
+        }
 
         return new DefaultDependencyResolverResult(
                 collectorResult.getExceptions(), collectorResult.getRoot(), 
nodes, paths, dependencies);
diff --git 
a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java
index c92b060f4b..7b3ae6c198 100644
--- 
a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java
+++ 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProject.java
@@ -26,14 +26,7 @@ import java.util.List;
 import java.util.Optional;
 
 import org.apache.maven.RepositoryUtils;
-import org.apache.maven.api.Artifact;
-import org.apache.maven.api.DependencyCoordinate;
-import org.apache.maven.api.Exclusion;
-import org.apache.maven.api.Project;
-import org.apache.maven.api.RemoteRepository;
-import org.apache.maven.api.Scope;
-import org.apache.maven.api.Type;
-import org.apache.maven.api.VersionRange;
+import org.apache.maven.api.*;
 import org.apache.maven.api.annotations.Nonnull;
 import org.apache.maven.api.annotations.Nullable;
 import org.apache.maven.api.model.DependencyManagement;
@@ -180,8 +173,8 @@ public class DefaultProject implements Project {
             }
 
             @Override
-            public VersionRange getVersion() {
-                return session.parseVersionRange(dependency.getVersion());
+            public VersionConstraint getVersion() {
+                return session.parseVersionConstraint(dependency.getVersion());
             }
 
             @Override
diff --git 
a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultVersionParser.java
 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultVersionParser.java
index 5cc2b106a4..780b9846d6 100644
--- 
a/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultVersionParser.java
+++ 
b/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultVersionParser.java
@@ -25,8 +25,10 @@ import javax.inject.Singleton;
 import java.util.regex.Pattern;
 
 import org.apache.maven.api.Version;
+import org.apache.maven.api.VersionConstraint;
 import org.apache.maven.api.VersionRange;
 import org.apache.maven.api.services.VersionParser;
+import org.apache.maven.model.version.ModelVersionParser;
 
 import static org.apache.maven.internal.impl.Utils.nonNull;
 
@@ -39,10 +41,10 @@ public class DefaultVersionParser implements VersionParser {
     private static final String SNAPSHOT = "SNAPSHOT";
     private static final Pattern SNAPSHOT_TIMESTAMP = 
Pattern.compile("^(.*-)?([0-9]{8}\\.[0-9]{6}-[0-9]+)$");
 
-    private final org.apache.maven.model.version.VersionParser 
modelVersionParser;
+    private final ModelVersionParser modelVersionParser;
 
     @Inject
-    public DefaultVersionParser(org.apache.maven.model.version.VersionParser 
modelVersionParser) {
+    public DefaultVersionParser(ModelVersionParser modelVersionParser) {
         this.modelVersionParser = nonNull(modelVersionParser, 
"modelVersionParser");
     }
 
@@ -56,6 +58,11 @@ public class DefaultVersionParser implements VersionParser {
         return modelVersionParser.parseVersionRange(range);
     }
 
+    @Override
+    public VersionConstraint parseVersionConstraint(String constraint) {
+        return modelVersionParser.parseVersionConstraint(constraint);
+    }
+
     @Override
     public boolean isSnapshot(String version) {
         return checkSnapshot(version);
diff --git 
a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
 
b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
index 5878edf7a2..79f2cb7363 100644
--- 
a/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
+++ 
b/maven-core/src/main/java/org/apache/maven/internal/transformation/impl/DefaultConsumerPomBuilder.java
@@ -63,7 +63,7 @@ import org.apache.maven.model.profile.ProfileSelector;
 import org.apache.maven.model.superpom.SuperPomProvider;
 import org.apache.maven.model.v4.MavenModelVersion;
 import org.apache.maven.model.validation.ModelValidator;
-import org.apache.maven.model.version.VersionParser;
+import org.apache.maven.model.version.ModelVersionParser;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.project.ProjectBuildingRequest;
 import org.apache.maven.project.ProjectModelResolver;
@@ -128,7 +128,7 @@ class DefaultConsumerPomBuilder implements 
ConsumerPomBuilder {
     private SuperPomProvider superPomProvider;
 
     @Inject
-    private VersionParser versionParser;
+    private ModelVersionParser versionParser;
 
     // To break circular dependency
     @Inject
diff --git 
a/maven-core/src/test/java/org/apache/maven/internal/impl/TestApi.java 
b/maven-core/src/test/java/org/apache/maven/internal/impl/TestApi.java
index af6fdc8bb4..f4ce6b1b1b 100644
--- a/maven-core/src/test/java/org/apache/maven/internal/impl/TestApi.java
+++ b/maven-core/src/test/java/org/apache/maven/internal/impl/TestApi.java
@@ -21,15 +21,23 @@ package org.apache.maven.internal.impl;
 import javax.inject.Inject;
 
 import java.nio.file.Path;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
+import java.util.stream.Collectors;
 
 import org.apache.maven.api.Artifact;
 import org.apache.maven.api.ArtifactCoordinate;
+import org.apache.maven.api.Dependency;
 import org.apache.maven.api.Node;
 import org.apache.maven.api.Project;
+import org.apache.maven.api.ResolutionScope;
 import org.apache.maven.api.Session;
+import org.apache.maven.api.services.DependencyResolver;
+import org.apache.maven.api.services.DependencyResolverResult;
 import org.apache.maven.api.services.ProjectBuilder;
 import org.apache.maven.api.services.ProjectBuilderRequest;
 import org.apache.maven.api.services.SettingsBuilder;
@@ -120,7 +128,7 @@ class TestApi {
     }
 
     @Test
-    void testCreateAndResolveArtifact() throws Exception {
+    void testCreateAndResolveArtifact() {
         ArtifactCoordinate coord =
                 session.createArtifactCoordinate("org.codehaus.plexus", 
"plexus-utils", "1.4.5", "pom");
 
@@ -131,21 +139,74 @@ class TestApi {
         Optional<Path> op = session.getArtifactPath(resolved.getKey());
         assertTrue(op.isPresent());
         assertEquals(resolved.getValue(), op.get());
+    }
+
+    @Test
+    void testBuildProject() {
+        Artifact artifact = session.createArtifact("org.codehaus.plexus", 
"plexus-utils", "1.4.5", "pom");
 
         Project project = session.getService(ProjectBuilder.class)
                 .build(ProjectBuilderRequest.builder()
                         .session(session)
-                        .path(op.get())
+                        .path(session.getPathForLocalArtifact(artifact))
                         .processPlugins(false)
                         .resolveDependencies(false)
                         .build())
                 .getProject()
                 .get();
         assertNotNull(project);
+    }
 
+    @Test
+    void testCollectArtifactDependencies() {
         Artifact artifact =
                 session.createArtifact("org.codehaus.plexus", 
"plexus-container-default", "1.0-alpha-32", "jar");
         Node root = session.collectDependencies(artifact);
         assertNotNull(root);
     }
+
+    @Test
+    void testResolveArtifactCoordinateDependencies() {
+        ArtifactCoordinate coord =
+                session.createArtifactCoordinate("org.apache.maven.core.test", 
"test-extension", "1", "jar");
+
+        List<Path> paths = 
session.resolveDependencies(session.createDependencyCoordinate(coord));
+
+        assertNotNull(paths);
+        assertEquals(10, paths.size());
+        
assertTrue(paths.get(0).getFileName().toString().equals("test-extension-1.jar"));
+    }
+
+    @Test
+    void testProjectDependencies() {
+        Artifact pom = session.createArtifact("org.codehaus.plexus", 
"plexus-container-default", "1.0-alpha-32", "pom");
+
+        Project project = session.getService(ProjectBuilder.class)
+                .build(ProjectBuilderRequest.builder()
+                        .session(session)
+                        .path(session.getPathForLocalArtifact(pom))
+                        .processPlugins(false)
+                        .resolveDependencies(false)
+                        .build())
+                .getProject()
+                .get();
+        assertNotNull(project);
+
+        Artifact artifact = 
session.createArtifact("org.apache.maven.core.test", "test-extension", "1", 
"jar");
+        Node root = session.collectDependencies(artifact);
+        assertNotNull(root);
+
+        DependencyResolverResult result =
+                session.getService(DependencyResolver.class).resolve(session, 
project, ResolutionScope.PROJECT_RUNTIME);
+        assertNotNull(result);
+        List<Dependency> deps = new 
ArrayList<>(result.getDependencies().keySet());
+        List<Dependency> deps2 = result.getNodes().stream()
+                .map(Node::getDependency)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+        assertEquals(deps, deps2);
+        for (Dependency dep : deps2) {
+            dep.getVersion();
+        }
+    }
 }
diff --git 
a/maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.3/commons-logging-1.0.3.jar
 
b/maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.3/commons-logging-1.0.3.jar
similarity index 100%
rename from 
maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.3/commons-logging-1.0.3.jar
rename to 
maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.3/commons-logging-1.0.3.jar
diff --git 
a/maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.3/commons-logging-1.0.3.jar.sha1
 
b/maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.3/commons-logging-1.0.3.jar.sha1
similarity index 100%
rename from 
maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.3/commons-logging-1.0.3.jar.sha1
rename to 
maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.3/commons-logging-1.0.3.jar.sha1
diff --git 
a/maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.3/commons-logging-1.0.3.pom
 
b/maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.3/commons-logging-1.0.3.pom
similarity index 100%
rename from 
maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.3/commons-logging-1.0.3.pom
rename to 
maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.3/commons-logging-1.0.3.pom
diff --git 
a/maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.3/commons-logging-1.0.3.pom.sha1
 
b/maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.3/commons-logging-1.0.3.pom.sha1
similarity index 100%
rename from 
maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.3/commons-logging-1.0.3.pom.sha1
rename to 
maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.3/commons-logging-1.0.3.pom.sha1
diff --git 
a/maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.4/commons-logging-1.0.4-javadoc-resources.jar.lastUpdated
 
b/maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4-javadoc-resources.jar.lastUpdated
similarity index 100%
rename from 
maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.4/commons-logging-1.0.4-javadoc-resources.jar.lastUpdated
rename to 
maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4-javadoc-resources.jar.lastUpdated
diff --git 
a/maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.4/commons-logging-1.0.4.jar
 
b/maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar
similarity index 100%
rename from 
maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.4/commons-logging-1.0.4.jar
rename to 
maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar
diff --git 
a/maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.4/commons-logging-1.0.4.jar.sha1
 
b/maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar.sha1
similarity index 100%
rename from 
maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.4/commons-logging-1.0.4.jar.sha1
rename to 
maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar.sha1
diff --git 
a/maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.4/commons-logging-1.0.4.pom
 
b/maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.pom
similarity index 100%
rename from 
maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.4/commons-logging-1.0.4.pom
rename to 
maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.pom
diff --git 
a/maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.4/commons-logging-1.0.4.pom.sha1
 
b/maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.pom.sha1
similarity index 100%
rename from 
maven-core/src/test/resources/apiv4-repo/commons-logging/1.0.4/commons-logging-1.0.4.pom.sha1
rename to 
maven-core/src/test/resources/apiv4-repo/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.pom.sha1
diff --git 
a/maven-core/src/test/resources/apiv4-repo/junit/junit/4.13.1/junit-4.13.1.jar 
b/maven-core/src/test/resources/apiv4-repo/junit/junit/4.13.1/junit-4.13.1.jar
new file mode 100644
index 0000000000..b376ffc16f
Binary files /dev/null and 
b/maven-core/src/test/resources/apiv4-repo/junit/junit/4.13.1/junit-4.13.1.jar 
differ
diff --git 
a/maven-core/src/test/resources/apiv4-repo/junit/junit/4.13.1/junit-4.13.1.jar.sha1
 
b/maven-core/src/test/resources/apiv4-repo/junit/junit/4.13.1/junit-4.13.1.jar.sha1
new file mode 100644
index 0000000000..a0369caaef
--- /dev/null
+++ 
b/maven-core/src/test/resources/apiv4-repo/junit/junit/4.13.1/junit-4.13.1.jar.sha1
@@ -0,0 +1 @@
+cdd00374f1fee76b11e2a9d127405aa3f6be5b6a
\ No newline at end of file
diff --git 
a/maven-core/src/test/resources/apiv4-repo/junit/junit/4.13.1/junit-4.13.1.pom 
b/maven-core/src/test/resources/apiv4-repo/junit/junit/4.13.1/junit-4.13.1.pom
new file mode 100644
index 0000000000..428715884b
--- /dev/null
+++ 
b/maven-core/src/test/resources/apiv4-repo/junit/junit/4.13.1/junit-4.13.1.pom
@@ -0,0 +1,587 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>junit</groupId>
+    <artifactId>junit</artifactId>
+    <version>4.13.1</version>
+
+    <name>JUnit</name>
+    <description>JUnit is a unit testing framework for Java, created by Erich 
Gamma and Kent Beck.</description>
+    <url>http://junit.org</url>
+    <inceptionYear>2002</inceptionYear>
+    <organization>
+        <name>JUnit</name>
+        <url>http://www.junit.org</url>
+    </organization>
+    <licenses>
+        <license>
+            <name>Eclipse Public License 1.0</name>
+            <url>http://www.eclipse.org/legal/epl-v10.html</url>
+            <distribution>repo</distribution>
+        </license>
+    </licenses>
+
+    <developers>
+        <developer>
+            <id>dsaff</id>
+            <name>David Saff</name>
+            <email>da...@saff.net</email>
+        </developer>
+        <developer>
+            <id>kcooney</id>
+            <name>Kevin Cooney</name>
+            <email>kcoo...@google.com</email>
+        </developer>
+        <developer>
+            <id>stefanbirkner</id>
+            <name>Stefan Birkner</name>
+            <email>m...@stefan-birkner.de</email>
+        </developer>
+        <developer>
+            <id>marcphilipp</id>
+            <name>Marc Philipp</name>
+            <email>m...@marcphilipp.de</email>
+        </developer>
+    </developers>
+    <contributors>
+        <contributor>
+            <name>JUnit contributors</name>
+            <organization>JUnit</organization>
+            <email>t...@junit.org</email>
+            <url>https://github.com/junit-team/junit4/graphs/contributors</url>
+            <roles>
+                <role>developers</role>
+            </roles>
+        </contributor>
+    </contributors>
+
+    <prerequisites>
+        <maven>3.0.4</maven>
+    </prerequisites>
+
+    <scm>
+        <connection>scm:git:git://github.com/junit-team/junit4.git</connection>
+        
<developerConnection>scm:git:g...@github.com:junit-team/junit4.git</developerConnection>
+        <url>https://github.com/junit-team/junit4</url>
+      <tag>r4.13.1</tag>
+  </scm>
+    <issueManagement>
+        <system>github</system>
+        <url>https://github.com/junit-team/junit4/issues</url>
+    </issueManagement>
+    <ciManagement>
+        <system>travis</system>
+        <url>https://travis-ci.org/junit-team/junit4</url>
+    </ciManagement>
+    <distributionManagement>
+        
<downloadUrl>https://github.com/junit-team/junit4/wiki/Download-and-Install</downloadUrl>
+        <snapshotRepository>
+            <id>junit-snapshot-repo</id>
+            <name>Nexus Snapshot Repository</name>
+            <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
+        </snapshotRepository>
+        <repository>
+            <id>junit-releases-repo</id>
+            <name>Nexus Release Repository</name>
+            
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
+        </repository>
+        <site>
+            <id>junit.github.io</id>
+            <url>gitsite:g...@github.com/junit-team/junit4.git</url>
+        </site>
+    </distributionManagement>
+
+    <properties>
+        <jdkVersion>1.5</jdkVersion>
+        <surefireVersion>2.19.1</surefireVersion>
+        <hamcrestVersion>1.3</hamcrestVersion>
+        <project.build.sourceEncoding>ISO-8859-1</project.build.sourceEncoding>
+        <arguments />
+        <gpg.keyname>67893CC4</gpg.keyname>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-core</artifactId>
+            <version>${hamcrestVersion}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.hamcrest</groupId>
+            <artifactId>hamcrest-library</artifactId>
+            <version>${hamcrestVersion}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>${project.basedir}/src/main/resources</directory>
+            </resource>
+            <resource>
+                <directory>${project.basedir}</directory>
+                <includes>
+                    <include>LICENSE-junit.txt</include>
+                </includes>
+            </resource>
+        </resources>
+        <plugins>
+            <!--
+            Both "org.apache" and "org.codehaus" are default providers of MOJO 
plugins
+            which are especially dedicated to Maven projects.
+            The MOJO stands for "Maven plain Old Java Object".
+            Each mojo is an executable goal in Maven, and a plugin is a 
distribution of
+            one or more related mojos.
+            For more information see 
http://maven.apache.org/plugin-developers/index.html
+
+            The following plugins are ordered according the Maven build 
lifecycle.
+            
http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html
+            -->
+            <plugin>
+                <!--
+                Checks that the version of user's maven installation is 3.0.4,
+                the JDK is 1.5+, no non-standard repositories are specified in
+                the project, requires only release versions of dependencies of 
other artifacts.
+                -->
+                <artifactId>maven-enforcer-plugin</artifactId>
+                <version>1.4</version>
+                <executions>
+                    <execution>
+                        <id>enforce-versions</id>
+                        <phase>initialize</phase>
+                        <goals>
+                            <goal>enforce</goal>
+                        </goals>
+                        <configuration>
+                            <fail>true</fail>
+                            <rules>
+                                <requireMavenVersion>
+                                    <!-- Some plugin features require a recent 
Maven runtime to work properly -->
+                                    <message>Current version of Maven 
${maven.version} required to build the project
+                                        should be 
${project.prerequisites.maven}, or higher!
+                                    </message>
+                                    
<version>[${project.prerequisites.maven},)</version>
+                                </requireMavenVersion>
+                                <requireJavaVersion>
+                                    <message>Current JDK version 
${java.version} should be ${jdkVersion}, or higher!
+                                    </message>
+                                    <version>${jdkVersion}</version>
+                                </requireJavaVersion>
+                                <requireNoRepositories>
+                                    <message>Best Practice is to never define 
repositories in pom.xml (use a repository
+                                        manager instead).
+                                    </message>
+                                </requireNoRepositories>
+                                <requireReleaseDeps>
+                                    <message>No Snapshots Dependencies 
Allowed!</message>
+                                </requireReleaseDeps>
+                            </rules>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <!--
+                Updates Version#id().
+                -->
+                <groupId>com.google.code.maven-replacer-plugin</groupId>
+                <artifactId>replacer</artifactId>
+                <version>1.5.3</version>
+                <executions>
+                    <execution>
+                        <phase>process-sources</phase>
+                        <goals>
+                            <goal>replace</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <ignoreMissingFile>false</ignoreMissingFile>
+                    
<file>${project.build.sourceDirectory}/junit/runner/Version.java.template</file>
+                    
<outputFile>${project.build.sourceDirectory}/junit/runner/Version.java</outputFile>
+                    <regex>false</regex>
+                    <token>@version@</token>
+                    <value>${project.version}</value>
+                </configuration>
+            </plugin>
+            <plugin><!-- Using jdk 1.5.0_22, package-info.java files are 
compiled correctly. -->
+                <!--
+                java compiler plugin forked in extra process
+                -->
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.3</version>
+                <configuration>
+                    <encoding>${project.build.sourceEncoding}</encoding>
+                    <source>${jdkVersion}</source>
+                    <target>${jdkVersion}</target>
+                    <testSource>${jdkVersion}</testSource>
+                    <testTarget>${jdkVersion}</testTarget>
+                    <compilerVersion>1.5</compilerVersion>
+                    <showDeprecation>true</showDeprecation>
+                    <showWarnings>true</showWarnings>
+                    <debug>true</debug>
+                    <fork>true</fork>
+                    <compilerArgs>
+                        <arg>-Xlint:unchecked</arg>
+                    </compilerArgs>
+                    <maxmem>128m</maxmem>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.mojo</groupId>
+                <artifactId>animal-sniffer-maven-plugin</artifactId>
+                <version>1.14</version>
+                <executions>
+                    <execution>
+                        <id>signature-check</id>
+                        <phase>test</phase>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                        <configuration>
+                            <signature>
+                                <groupId>org.codehaus.mojo.signature</groupId>
+                                <artifactId>java15</artifactId>
+                                <version>1.0</version>
+                            </signature>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <!--
+                A plugin which uses the JUnit framework in order to start
+                our junit suite "AllTests" after the sources are compiled.
+                -->
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>${surefireVersion}</version>
+                <configuration>
+                    <test>org/junit/tests/AllTests.java</test>
+                    <useSystemClassLoader>true</useSystemClassLoader>
+                    <enableAssertions>false</enableAssertions>
+                </configuration>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.maven.surefire</groupId>
+                        <artifactId>surefire-junit47</artifactId>
+                        <version>${surefireVersion}</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+            <plugin>
+                <!--
+                This plugin can package the main artifact's sources 
(src/main/java)
+                in to jar archive. See target/junit-*-sources.jar.
+                -->
+                <artifactId>maven-source-plugin</artifactId>
+                <version>2.4</version>
+            </plugin>
+            <plugin>
+                <!--
+                This plugin can generate Javadoc by a forked
+                process and then package the Javadoc
+                in jar archive target/junit-*-javadoc.jar.
+                -->
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.10.3</version>
+                <configuration>
+                    
<stylesheetfile>${basedir}/src/main/javadoc/stylesheet.css</stylesheetfile>
+                    <show>protected</show>
+                    <author>false</author>
+                    <version>false</version>
+                    <detectLinks>false</detectLinks>
+                    <linksource>true</linksource>
+                    <keywords>true</keywords>
+                    <use>true</use>
+                    <windowtitle>JUnit API</windowtitle>
+                    <encoding>UTF-8</encoding>
+                    <locale>en</locale>
+                    <javadocVersion>${jdkVersion}</javadocVersion>
+                    <javaApiLinks>
+                        <property>
+                            <name>api_${jdkVersion}</name>
+                            
<value>http://docs.oracle.com/javase/${jdkVersion}.0/docs/api/</value>
+                        </property>
+                    </javaApiLinks>
+                    <excludePackageNames>*.internal.*</excludePackageNames>
+                    <verbose>true</verbose>
+                    <minmemory>32m</minmemory>
+                    <maxmemory>128m</maxmemory>
+                    <failOnError>true</failOnError>
+                    <includeDependencySources>true</includeDependencySources>
+                    <dependencySourceIncludes>
+                        
<dependencySourceInclude>org.hamcrest:hamcrest-core:*</dependencySourceInclude>
+                    </dependencySourceIncludes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-release-plugin</artifactId>
+                <version>2.5.2</version>
+                <configuration>
+                    <mavenExecutorId>forked-path</mavenExecutorId>
+                    <useReleaseProfile>false</useReleaseProfile>
+                    <arguments>-Pgenerate-docs,junit-release 
${arguments}</arguments>
+                    <tagNameFormat>r@{project.version}</tagNameFormat>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-site-plugin</artifactId>
+                <version>3.4</version>
+                <dependencies>
+                    <dependency>
+                        <groupId>com.github.stephenc.wagon</groupId>
+                        <artifactId>wagon-gitsite</artifactId>
+                        <version>0.4.1</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.apache.maven.doxia</groupId>
+                        <artifactId>doxia-module-markdown</artifactId>
+                        <version>1.5</version>
+                    </dependency>
+                </dependencies>
+            </plugin>
+            <plugin>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.6</version>
+                <configuration>
+                    <archive>
+                        <addMavenDescriptor>false</addMavenDescriptor>
+                        <manifest>
+                            
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
+                        </manifest>
+                        <manifestEntries>
+                            
<Automatic-Module-Name>junit</Automatic-Module-Name>
+                        </manifestEntries>                        
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-clean-plugin</artifactId>
+                <version>2.6.1</version>
+            </plugin>
+            <plugin>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <version>2.8.2</version>
+            </plugin>
+            <plugin>
+                <artifactId>maven-install-plugin</artifactId>
+                <version>2.5.2</version>
+            </plugin>
+            <plugin>
+                <artifactId>maven-resources-plugin</artifactId>
+                <version>2.7</version>
+            </plugin>
+        </plugins>
+    </build>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <artifactId>maven-project-info-reports-plugin</artifactId>
+                <version>2.8</version>
+                <configuration>
+                    
<dependencyLocationsEnabled>false</dependencyLocationsEnabled>
+                    <!-- waiting for MPIR-267 -->
+                </configuration>
+                <reportSets>
+                    <reportSet>
+                        <reports>
+                            <report>index</report>
+                            <report>dependency-info</report>
+                            <report>modules</report>
+                            <report>license</report>
+                            <report>project-team</report>
+                            <report>scm</report>
+                            <report>issue-tracking</report>
+                            <report>mailing-list</report>
+                            <report>dependency-management</report>
+                            <report>dependencies</report>
+                            <report>dependency-convergence</report>
+                            <report>cim</report>
+                            <report>distribution-management</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+            <plugin>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <version>2.10.3</version>
+                <configuration>
+                    <destDir>javadoc/latest</destDir>
+                    
<stylesheetfile>${basedir}/src/main/javadoc/stylesheet.css</stylesheetfile>
+                    <show>protected</show>
+                    <author>false</author>
+                    <version>false</version>
+                    <detectLinks>false</detectLinks>
+                    <linksource>true</linksource>
+                    <keywords>true</keywords>
+                    <use>true</use>
+                    <windowtitle>JUnit API</windowtitle>
+                    <encoding>UTF-8</encoding>
+                    <locale>en</locale>
+                    <javadocVersion>${jdkVersion}</javadocVersion>
+                    <javaApiLinks>
+                        <property>
+                            <name>api_${jdkVersion}</name>
+                            
<value>http://docs.oracle.com/javase/${jdkVersion}.0/docs/api/</value>
+                        </property>
+                    </javaApiLinks>
+                    
<excludePackageNames>junit.*,*.internal.*</excludePackageNames>
+                    <verbose>true</verbose>
+                    <minmemory>32m</minmemory>
+                    <maxmemory>128m</maxmemory>
+                    <failOnError>true</failOnError>
+                    <includeDependencySources>true</includeDependencySources>
+                    <dependencySourceIncludes>
+                        
<dependencySourceInclude>org.hamcrest:hamcrest-core:*</dependencySourceInclude>
+                    </dependencySourceIncludes>
+                </configuration>
+                <reportSets>
+                    <reportSet>
+                        <reports>
+                            <report>javadoc</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+        </plugins>
+    </reporting>
+
+    <profiles>
+        <profile>
+            <id>junit-release</id>
+            <!--
+            Signs all artifacts before deploying to Maven Central.
+            -->
+            <build>
+                <plugins>
+                    <plugin>
+                        <!--
+                        The goal is to sign all artifacts so that the user may 
verify them before downloading.
+                        The automatic build system may reuire your key ID, and 
passphrase specified using system properties:
+                        -Dgpg.passphrase="<passphrase>" -Dgpg.keyname="<your 
key ID>"
+                        In order to create the key pair, use the command "gpg 
&ndash;&ndash;gen-key".
+                        (&ndash;&ndash; stands for double dash)
+                        -->
+                        <artifactId>maven-gpg-plugin</artifactId>
+                        <version>1.6</version>
+                        <executions>
+                            <execution>
+                                <id>gpg-sign</id>
+                                <phase>verify</phase>
+                                <goals>
+                                    <goal>sign</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>generate-docs</id>
+            <!--
+            Generate the documentation artifacts. 
+            Note: this profile is also required to be active for release
+            builds due to the packaging requirements of the Central repo
+            -->
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-source-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>attach-sources</id>
+                                <phase>prepare-package</phase>
+                                <goals>
+                                    <goal>jar-no-fork</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                    <plugin>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>attach-javadoc</id>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>jar</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        <profile>
+            <id>restrict-doclint</id>
+            <!-- doclint is only supported by JDK 8 -->
+            <activation>
+                <jdk>[1.8,)</jdk>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-compiler-plugin</artifactId>
+                        <configuration>
+                            <compilerArgs>
+                                <arg>-Xlint:unchecked</arg>
+                                
<arg>-Xdoclint:accessibility,reference,syntax</arg>
+                            </compilerArgs>
+                        </configuration>
+                    </plugin>
+                    <plugin>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <configuration>
+                            <additionalparam>-Xdoclint:accessibility 
-Xdoclint:reference</additionalparam>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+            <reporting>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <configuration>
+                            <additionalparam>-Xdoclint:accessibility 
-Xdoclint:reference</additionalparam>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </reporting>
+        </profile>
+        <profile>
+            <id>java9</id>
+            <activation>
+                <jdk>[1.9,)</jdk>
+            </activation>
+            <properties>
+                <!-- JDK 9 minimal source and target versions are 1.6 -->
+                <jdkVersion>1.6</jdkVersion>
+            </properties>
+            <build>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <configuration>
+                            <source>1.6</source>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </build>
+            <reporting>
+                <plugins>
+                    <plugin>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <configuration>
+                            <source>1.6</source>
+                        </configuration>
+                    </plugin>
+                </plugins>
+            </reporting>
+        </profile>
+    </profiles>
+</project>
diff --git 
a/maven-core/src/test/resources/apiv4-repo/junit/junit/4.13.1/junit-4.13.1.pom.sha1
 
b/maven-core/src/test/resources/apiv4-repo/junit/junit/4.13.1/junit-4.13.1.pom.sha1
new file mode 100644
index 0000000000..d843b12bac
--- /dev/null
+++ 
b/maven-core/src/test/resources/apiv4-repo/junit/junit/4.13.1/junit-4.13.1.pom.sha1
@@ -0,0 +1 @@
+643e8b4c40dca9f0b0abd8125d378d9f47d7d69e
\ No newline at end of file
diff --git 
a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
 
b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
index b4c5153718..78c36e8d2d 100644
--- 
a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
+++ 
b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilder.java
@@ -76,7 +76,7 @@ import 
org.apache.maven.model.resolution.WorkspaceModelResolver;
 import org.apache.maven.model.superpom.SuperPomProvider;
 import org.apache.maven.model.validation.DefaultModelValidator;
 import org.apache.maven.model.validation.ModelValidator;
-import org.apache.maven.model.version.VersionParser;
+import org.apache.maven.model.version.ModelVersionParser;
 import org.codehaus.plexus.interpolation.InterpolationException;
 import org.codehaus.plexus.interpolation.MapBasedValueSource;
 import org.codehaus.plexus.interpolation.StringSearchInterpolator;
@@ -110,7 +110,7 @@ public class DefaultModelBuilder implements ModelBuilder {
     private final ProfileActivationFilePathInterpolator 
profileActivationFilePathInterpolator;
     private final ModelVersionProcessor versionProcessor;
     private final ModelSourceTransformer transformer;
-    private final VersionParser versionParser;
+    private final ModelVersionParser versionParser;
 
     @SuppressWarnings("checkstyle:ParameterNumber")
     @Inject
@@ -134,7 +134,7 @@ public class DefaultModelBuilder implements ModelBuilder {
             ProfileActivationFilePathInterpolator 
profileActivationFilePathInterpolator,
             ModelVersionProcessor versionProcessor,
             ModelSourceTransformer transformer,
-            VersionParser versionParser) {
+            ModelVersionParser versionParser) {
         this.modelProcessor = modelProcessor;
         this.modelValidator = modelValidator;
         this.modelNormalizer = modelNormalizer;
diff --git 
a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
 
b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
index de7b99eb6c..7fcdeb30a7 100644
--- 
a/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
+++ 
b/maven-model-builder/src/main/java/org/apache/maven/model/building/DefaultModelBuilderFactory.java
@@ -21,6 +21,7 @@ package org.apache.maven.model.building;
 import java.util.Arrays;
 
 import org.apache.maven.api.Version;
+import org.apache.maven.api.VersionConstraint;
 import org.apache.maven.api.VersionRange;
 import org.apache.maven.api.spi.ModelParser;
 import org.apache.maven.model.Model;
@@ -73,7 +74,7 @@ import 
org.apache.maven.model.superpom.DefaultSuperPomProvider;
 import org.apache.maven.model.superpom.SuperPomProvider;
 import org.apache.maven.model.validation.DefaultModelValidator;
 import org.apache.maven.model.validation.ModelValidator;
-import org.apache.maven.model.version.VersionParser;
+import org.apache.maven.model.version.ModelVersionParser;
 
 import static java.util.Objects.requireNonNull;
 
@@ -106,7 +107,7 @@ public class DefaultModelBuilderFactory {
     private ProfileActivationFilePathInterpolator 
profileActivationFilePathInterpolator;
     private ModelVersionProcessor versionProcessor;
     private ModelSourceTransformer transformer;
-    private VersionParser versionParser;
+    private ModelVersionParser versionParser;
 
     public DefaultModelBuilderFactory setModelProcessor(ModelProcessor 
modelProcessor) {
         this.modelProcessor = modelProcessor;
@@ -214,7 +215,7 @@ public class DefaultModelBuilderFactory {
         return this;
     }
 
-    public DefaultModelBuilderFactory setModelVersionParser(VersionParser 
versionParser) {
+    public DefaultModelBuilderFactory setModelVersionParser(ModelVersionParser 
versionParser) {
         this.versionParser = versionParser;
         return this;
     }
@@ -337,10 +338,10 @@ public class DefaultModelBuilderFactory {
         return new BuildModelSourceTransformer();
     }
 
-    private VersionParser newModelVersionParser() {
+    private ModelVersionParser newModelVersionParser() {
         // This is a limited parser that does not support ranges and compares 
versions as strings
         // in real-life this parser should not be used, but replaced with a 
proper one
-        return new VersionParser() {
+        return new ModelVersionParser() {
             @Override
             public Version parseVersion(String version) {
                 requireNonNull(version, "version");
@@ -361,6 +362,11 @@ public class DefaultModelBuilderFactory {
             public VersionRange parseVersionRange(String range) {
                 throw new IllegalArgumentException("ranges not supported by 
this parser");
             }
+
+            @Override
+            public VersionConstraint parseVersionConstraint(String constraint) 
{
+                throw new IllegalArgumentException("constraint not supported 
by this parser");
+            }
         };
     }
 
diff --git 
a/maven-model-builder/src/main/java/org/apache/maven/model/version/VersionParser.java
 
b/maven-model-builder/src/main/java/org/apache/maven/model/version/ModelVersionParser.java
similarity index 79%
rename from 
maven-model-builder/src/main/java/org/apache/maven/model/version/VersionParser.java
rename to 
maven-model-builder/src/main/java/org/apache/maven/model/version/ModelVersionParser.java
index f6eaebefe8..134be60271 100644
--- 
a/maven-model-builder/src/main/java/org/apache/maven/model/version/VersionParser.java
+++ 
b/maven-model-builder/src/main/java/org/apache/maven/model/version/ModelVersionParser.java
@@ -19,6 +19,7 @@
 package org.apache.maven.model.version;
 
 import org.apache.maven.api.Version;
+import org.apache.maven.api.VersionConstraint;
 import org.apache.maven.api.VersionRange;
 import org.apache.maven.api.annotations.Nonnull;
 import org.apache.maven.api.services.VersionParserException;
@@ -30,7 +31,7 @@ import org.apache.maven.api.services.VersionParserException;
  *
  * @since 4.0.0
  */
-public interface VersionParser {
+public interface ModelVersionParser {
 
     /**
      * Parses the specified version string, for example "1.0".
@@ -51,4 +52,14 @@ public interface VersionParser {
      */
     @Nonnull
     VersionRange parseVersionRange(@Nonnull String range);
+
+    /**
+     * Parses the specified version constraint specification, for example 
"1.0" or "[1.0,2.0)".
+     *
+     * @param constraint the range specification to parse, must not be {@code 
null}
+     * @return the parsed version constraint, never {@code null}
+     * @throws VersionParserException if the range specification violates the 
syntax rules of this scheme
+     */
+    @Nonnull
+    VersionConstraint parseVersionConstraint(@Nonnull String constraint);
 }
diff --git 
a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelVersionParser.java
 
b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelVersionParser.java
index 02338cb728..c5e01b2472 100644
--- 
a/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelVersionParser.java
+++ 
b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/DefaultModelVersionParser.java
@@ -22,12 +22,11 @@ import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
-import java.util.Objects;
-
 import org.apache.maven.api.Version;
+import org.apache.maven.api.VersionConstraint;
 import org.apache.maven.api.VersionRange;
 import org.apache.maven.api.services.VersionParserException;
-import org.apache.maven.model.version.VersionParser;
+import org.apache.maven.model.version.ModelVersionParser;
 import org.eclipse.aether.version.InvalidVersionSpecificationException;
 import org.eclipse.aether.version.VersionScheme;
 
@@ -35,7 +34,7 @@ import static java.util.Objects.requireNonNull;
 
 @Named
 @Singleton
-public class DefaultModelVersionParser implements VersionParser {
+public class DefaultModelVersionParser implements ModelVersionParser {
     private final VersionScheme versionScheme;
 
     @Inject
@@ -55,10 +54,21 @@ public class DefaultModelVersionParser implements 
VersionParser {
         return new DefaultVersionRange(versionScheme, range);
     }
 
+    @Override
+    public VersionConstraint parseVersionConstraint(String constraint) {
+        requireNonNull(constraint, "constraint");
+        return new DefaultVersionConstraint(versionScheme, constraint);
+    }
+
     static class DefaultVersion implements Version {
         private final VersionScheme versionScheme;
         private final org.eclipse.aether.version.Version delegate;
 
+        DefaultVersion(VersionScheme versionScheme, 
org.eclipse.aether.version.Version delegate) {
+            this.versionScheme = versionScheme;
+            this.delegate = delegate;
+        }
+
         DefaultVersion(VersionScheme versionScheme, String delegateValue) {
             this.versionScheme = versionScheme;
             try {
@@ -91,7 +101,7 @@ public class DefaultModelVersionParser implements 
VersionParser {
 
         @Override
         public int hashCode() {
-            return Objects.hash(delegate);
+            return delegate.hashCode();
         }
 
         @Override
@@ -109,6 +119,11 @@ public class DefaultModelVersionParser implements 
VersionParser {
         private final VersionScheme versionScheme;
         private final org.eclipse.aether.version.VersionRange delegate;
 
+        DefaultVersionRange(VersionScheme versionScheme, 
org.eclipse.aether.version.VersionRange delegate) {
+            this.versionScheme = versionScheme;
+            this.delegate = delegate;
+        }
+
         DefaultVersionRange(VersionScheme versionScheme, String delegateValue) 
{
             this.versionScheme = versionScheme;
             try {
@@ -127,14 +142,135 @@ public class DefaultModelVersionParser implements 
VersionParser {
             }
         }
 
+        @Override
+        public Boundary getUpperBoundary() {
+            org.eclipse.aether.version.VersionRange.Bound bound = 
delegate.getUpperBound();
+            if (bound == null) {
+                return null;
+            }
+            return new Boundary() {
+                @Override
+                public Version getVersion() {
+                    return new DefaultVersion(versionScheme, 
bound.getVersion());
+                }
+
+                @Override
+                public boolean isInclusive() {
+                    return bound.isInclusive();
+                }
+            };
+        }
+
+        @Override
+        public Boundary getLowerBoundary() {
+            org.eclipse.aether.version.VersionRange.Bound bound = 
delegate.getLowerBound();
+            if (bound == null) {
+                return null;
+            }
+            return new Boundary() {
+                @Override
+                public Version getVersion() {
+                    return new DefaultVersion(versionScheme, 
bound.getVersion());
+                }
+
+                @Override
+                public boolean isInclusive() {
+                    return bound.isInclusive();
+                }
+            };
+        }
+
+        @Override
+        public String asString() {
+            return delegate.toString();
+        }
+
+        @Override
+        public String toString() {
+            return asString();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            DefaultVersionRange that = (DefaultVersionRange) o;
+            return delegate.equals(that.delegate);
+        }
+
+        @Override
+        public int hashCode() {
+            return delegate.hashCode();
+        }
+    }
+
+    static class DefaultVersionConstraint implements VersionConstraint {
+        private final VersionScheme versionScheme;
+        private final org.eclipse.aether.version.VersionConstraint delegate;
+
+        DefaultVersionConstraint(VersionScheme versionScheme, String 
delegateValue) {
+            this.versionScheme = versionScheme;
+            try {
+                this.delegate = 
versionScheme.parseVersionConstraint(delegateValue);
+            } catch (InvalidVersionSpecificationException e) {
+                throw new VersionParserException("Unable to parse version 
constraint: " + delegateValue, e);
+            }
+        }
+
+        @Override
+        public boolean contains(Version version) {
+            if (version instanceof DefaultVersion) {
+                return delegate.containsVersion(((DefaultVersion) 
version).delegate);
+            } else {
+                return contains(new DefaultVersion(versionScheme, 
version.asString()));
+            }
+        }
+
         @Override
         public String asString() {
             return delegate.toString();
         }
 
+        @Override
+        public VersionRange getVersionRange() {
+            if (delegate.getRange() == null) {
+                return null;
+            }
+            return new DefaultVersionRange(versionScheme, delegate.getRange());
+        }
+
+        @Override
+        public Version getRecommendedVersion() {
+            if (delegate.getVersion() == null) {
+                return null;
+            }
+            return new DefaultVersion(versionScheme, delegate.getVersion());
+        }
+
         @Override
         public String toString() {
             return asString();
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            DefaultVersionConstraint that = (DefaultVersionConstraint) o;
+            return delegate.equals(that.delegate);
+        }
+
+        @Override
+        public int hashCode() {
+            return delegate.hashCode();
+        }
     }
 }
diff --git 
a/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/AbstractVersionTest.java
 
b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/AbstractVersionTest.java
new file mode 100644
index 0000000000..78b6b54bfe
--- /dev/null
+++ 
b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/AbstractVersionTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+package org.apache.maven.repository.internal;
+
+import org.apache.maven.api.Version;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ */
+abstract class AbstractVersionTest {
+
+    protected static final int X_LT_Y = -1;
+
+    protected static final int X_EQ_Y = 0;
+
+    protected static final int X_GT_Y = 1;
+
+    protected abstract Version newVersion(String version);
+
+    protected void assertOrder(int expected, String version1, String version2) 
{
+        Version v1 = newVersion(version1);
+        Version v2 = newVersion(version2);
+
+        if (expected > 0) {
+            assertEquals(1, Integer.signum(v1.compareTo(v2)), "expected " + v1 
+ " > " + v2);
+            assertEquals(-1, Integer.signum(v2.compareTo(v1)), "expected " + 
v2 + " < " + v1);
+            assertNotEquals(v1, v2, "expected " + v1 + " != " + v2);
+            assertNotEquals(v2, v1, "expected " + v2 + " != " + v1);
+        } else if (expected < 0) {
+            assertEquals(-1, Integer.signum(v1.compareTo(v2)), "expected " + 
v1 + " < " + v2);
+            assertEquals(1, Integer.signum(v2.compareTo(v1)), "expected " + v2 
+ " > " + v1);
+            assertNotEquals(v1, v2, "expected " + v1 + " != " + v2);
+            assertNotEquals(v2, v1, "expected " + v2 + " != " + v1);
+        } else {
+            assertEquals(0, v1.compareTo(v2), "expected " + v1 + " == " + v2);
+            assertEquals(0, v2.compareTo(v1), "expected " + v2 + " == " + v1);
+            assertEquals(v1, v2, "expected " + v1 + " == " + v2);
+            assertEquals(v2, v1, "expected " + v2 + " == " + v1);
+            assertEquals(v1.hashCode(), v2.hashCode(), "expected #(" + v1 + ") 
== #(" + v1 + ")");
+        }
+    }
+
+    protected void assertSequence(String... versions) {
+        for (int i = 0; i < versions.length - 1; i++) {
+            for (int j = i + 1; j < versions.length; j++) {
+                assertOrder(X_LT_Y, versions[i], versions[j]);
+            }
+        }
+    }
+}
diff --git 
a/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/ModelVersionParserTest.java
 
b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/ModelVersionParserTest.java
new file mode 100644
index 0000000000..90203267f6
--- /dev/null
+++ 
b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/ModelVersionParserTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+package org.apache.maven.repository.internal;
+
+import org.apache.maven.api.VersionConstraint;
+import org.apache.maven.api.services.VersionParserException;
+import org.apache.maven.model.version.ModelVersionParser;
+import org.eclipse.aether.util.version.GenericVersionScheme;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ */
+public class ModelVersionParserTest {
+
+    private final ModelVersionParser versionParser = new 
DefaultModelVersionParser(new GenericVersionScheme());
+
+    private VersionParserException parseInvalid(String constraint) {
+        try {
+            versionParser.parseVersionConstraint(constraint);
+            fail("expected exception for constraint " + constraint);
+            return null;
+        } catch (VersionParserException e) {
+            return e;
+        }
+    }
+
+    @Test
+    void testEnumeratedVersions() throws VersionParserException {
+        VersionConstraint c = versionParser.parseVersionConstraint("1.0");
+        assertEquals("1.0", c.getRecommendedVersion().toString());
+        assertTrue(c.contains(versionParser.parseVersion("1.0")));
+
+        c = versionParser.parseVersionConstraint("[1.0]");
+        assertNull(c.getRecommendedVersion());
+        assertTrue(c.contains(versionParser.parseVersion("1.0")));
+
+        c = versionParser.parseVersionConstraint("[1.0],[2.0]");
+        assertTrue(c.contains(versionParser.parseVersion("1.0")));
+        assertTrue(c.contains(versionParser.parseVersion("2.0")));
+
+        c = versionParser.parseVersionConstraint("[1.0],[2.0],[3.0]");
+        assertContains(c, "1.0", "2.0", "3.0");
+        assertNotContains(c, "1.5");
+
+        c = versionParser.parseVersionConstraint("[1,3),(3,5)");
+        assertContains(c, "1", "2", "4");
+        assertNotContains(c, "3", "5");
+
+        c = versionParser.parseVersionConstraint("[1,3),(3,)");
+        assertContains(c, "1", "2", "4");
+        assertNotContains(c, "3");
+    }
+
+    private void assertNotContains(VersionConstraint c, String... versions) {
+        assertContains(String.format("%s: %%s should not be contained\n", 
c.toString()), c, false, versions);
+    }
+
+    private void assertContains(String msg, VersionConstraint c, boolean b, 
String... versions) {
+        for (String v : versions) {
+            assertEquals(b, c.contains(versionParser.parseVersion(v)), 
String.format(msg, v));
+        }
+    }
+
+    private void assertContains(VersionConstraint c, String... versions) {
+        assertContains(String.format("%s: %%s should be contained\n", 
c.toString()), c, true, versions);
+    }
+
+    @Test
+    void testInvalid() {
+        parseInvalid("[1,");
+        parseInvalid("[1,2],(3,");
+        parseInvalid("[1,2],3");
+    }
+
+    @Test
+    void testSameUpperAndLowerBound() throws VersionParserException {
+        VersionConstraint c = versionParser.parseVersionConstraint("[1.0]");
+        assertEquals("[1.0,1.0]", c.toString());
+        VersionConstraint c2 = 
versionParser.parseVersionConstraint(c.toString());
+        assertEquals(c, c2);
+        assertTrue(c.contains(versionParser.parseVersion("1.0")));
+    }
+}
diff --git 
a/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/VersionRangeTest.java
 
b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/VersionRangeTest.java
new file mode 100644
index 0000000000..23a982d903
--- /dev/null
+++ 
b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/VersionRangeTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.
+ */
+package org.apache.maven.repository.internal;
+
+import org.apache.maven.api.Version;
+import org.apache.maven.api.VersionRange;
+import org.apache.maven.api.services.VersionParserException;
+import org.apache.maven.model.version.ModelVersionParser;
+import org.eclipse.aether.util.version.GenericVersionScheme;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class VersionRangeTest {
+
+    private ModelVersionParser versionParser = new 
DefaultModelVersionParser(new GenericVersionScheme());
+
+    private Version newVersion(String version) {
+        return versionParser.parseVersion(version);
+    }
+
+    private VersionRange parseValid(String range) {
+        try {
+            return versionParser.parseVersionRange(range);
+        } catch (VersionParserException e) {
+            throw new AssertionError(range + " should be valid but failed to 
parse due to: " + e.getMessage(), e);
+        }
+    }
+
+    private void parseInvalid(String range) {
+        try {
+            versionParser.parseVersionRange(range);
+            fail(range + " should be invalid");
+        } catch (VersionParserException e) {
+            assertTrue(true);
+        }
+    }
+
+    private void assertContains(VersionRange range, String version) {
+        assertTrue(range.contains(newVersion(version)), range + " should 
contain " + version);
+    }
+
+    private void assertNotContains(VersionRange range, String version) {
+        assertFalse(range.contains(newVersion(version)), range + " should not 
contain " + version);
+    }
+
+    @Test
+    void testLowerBoundInclusiveUpperBoundInclusive() {
+        VersionRange range = parseValid("[1,2]");
+        assertContains(range, "1");
+        assertContains(range, "1.1-SNAPSHOT");
+        assertContains(range, "2");
+        assertEquals(range, parseValid(range.toString()));
+    }
+
+    @Test
+    void testLowerBoundInclusiveUpperBoundExclusive() {
+        VersionRange range = parseValid("[1.2.3.4.5,1.2.3.4.6)");
+        assertContains(range, "1.2.3.4.5");
+        assertNotContains(range, "1.2.3.4.6");
+        assertEquals(range, parseValid(range.toString()));
+    }
+
+    @Test
+    void testLowerBoundExclusiveUpperBoundInclusive() {
+        VersionRange range = parseValid("(1a,1b]");
+        assertNotContains(range, "1a");
+        assertContains(range, "1b");
+        assertEquals(range, parseValid(range.toString()));
+    }
+
+    @Test
+    void testLowerBoundExclusiveUpperBoundExclusive() {
+        VersionRange range = parseValid("(1,3)");
+        assertNotContains(range, "1");
+        assertContains(range, "2-SNAPSHOT");
+        assertNotContains(range, "3");
+        assertEquals(range, parseValid(range.toString()));
+    }
+
+    @Test
+    void testSingleVersion() {
+        VersionRange range = parseValid("[1]");
+        assertContains(range, "1");
+        assertEquals(range, parseValid(range.toString()));
+
+        range = parseValid("[1,1]");
+        assertContains(range, "1");
+        assertEquals(range, parseValid(range.toString()));
+    }
+
+    @Test
+    void testSingleWildcardVersion() {
+        VersionRange range = parseValid("[1.2.*]");
+        assertContains(range, "1.2-alpha-1");
+        assertContains(range, "1.2-SNAPSHOT");
+        assertContains(range, "1.2");
+        assertContains(range, "1.2.9999999");
+        assertNotContains(range, "1.3-rc-1");
+        assertEquals(range, parseValid(range.toString()));
+    }
+
+    @Test
+    void testMissingOpenCloseDelimiter() {
+        parseInvalid("1.0");
+    }
+
+    @Test
+    void testMissingOpenDelimiter() {
+        parseInvalid("1.0]");
+        parseInvalid("1.0)");
+    }
+
+    @Test
+    void testMissingCloseDelimiter() {
+        parseInvalid("[1.0");
+        parseInvalid("(1.0");
+    }
+
+    @Test
+    void testTooManyVersions() {
+        parseInvalid("[1,2,3]");
+        parseInvalid("(1,2,3)");
+        parseInvalid("[1,2,3)");
+    }
+}
diff --git 
a/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/VersionTest.java
 
b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/VersionTest.java
new file mode 100644
index 0000000000..d9302037c0
--- /dev/null
+++ 
b/maven-resolver-provider/src/test/java/org/apache/maven/repository/internal/VersionTest.java
@@ -0,0 +1,453 @@
+/*
+ * 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.
+ */
+package org.apache.maven.repository.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Locale;
+import java.util.UUID;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.maven.api.Version;
+import org.apache.maven.model.version.ModelVersionParser;
+import org.eclipse.aether.util.version.GenericVersionScheme;
+import org.junit.jupiter.api.Test;
+
+import static java.util.stream.Collectors.toList;
+import static org.junit.jupiter.api.Assertions.*;
+
+/**
+ */
+public class VersionTest extends AbstractVersionTest {
+    private final ModelVersionParser modelVersionParser = new 
DefaultModelVersionParser(new GenericVersionScheme());
+
+    protected Version newVersion(String version) {
+        return modelVersionParser.parseVersion(version);
+    }
+
+    @Test
+    void testEmptyVersion() {
+        assertOrder(X_EQ_Y, "0", "");
+    }
+
+    @Test
+    void testNumericOrdering() {
+        assertOrder(X_LT_Y, "2", "10");
+        assertOrder(X_LT_Y, "1.2", "1.10");
+        assertOrder(X_LT_Y, "1.0.2", "1.0.10");
+        assertOrder(X_LT_Y, "1.0.0.2", "1.0.0.10");
+        assertOrder(X_LT_Y, "1.0.20101206.111434.1", "1.0.20101206.111435.1");
+        assertOrder(X_LT_Y, "1.0.20101206.111434.2", "1.0.20101206.111434.10");
+    }
+
+    @Test
+    void testDelimiters() {
+        assertOrder(X_EQ_Y, "1.0", "1-0");
+        assertOrder(X_EQ_Y, "1.0", "1_0");
+        assertOrder(X_EQ_Y, "1.a", "1a");
+    }
+
+    @Test
+    void testLeadingZerosAreSemanticallyIrrelevant() {
+        assertOrder(X_EQ_Y, "1", "01");
+        assertOrder(X_EQ_Y, "1.2", "1.002");
+        assertOrder(X_EQ_Y, "1.2.3", "1.2.0003");
+        assertOrder(X_EQ_Y, "1.2.3.4", "1.2.3.00004");
+    }
+
+    @Test
+    void testTrailingZerosAreSemanticallyIrrelevant() {
+        assertOrder(X_EQ_Y, "1", "1.0.0.0.0.0.0.0.0.0.0.0.0.0");
+        assertOrder(X_EQ_Y, "1", "1-0-0-0-0-0-0-0-0-0-0-0-0-0");
+        assertOrder(X_EQ_Y, "1", "1.0-0.0-0.0-0.0-0.0-0.0-0.0");
+        assertOrder(X_EQ_Y, "1", "1.0000000000000");
+        assertOrder(X_EQ_Y, "1.0", "1.0.0");
+    }
+
+    @Test
+    void testTrailingZerosBeforeQualifierAreSemanticallyIrrelevant() {
+        assertOrder(X_EQ_Y, "1.0-ga", "1.0.0-ga");
+        assertOrder(X_EQ_Y, "1.0.ga", "1.0.0.ga");
+        assertOrder(X_EQ_Y, "1.0ga", "1.0.0ga");
+
+        assertOrder(X_EQ_Y, "1.0-alpha", "1.0.0-alpha");
+        assertOrder(X_EQ_Y, "1.0.alpha", "1.0.0.alpha");
+        assertOrder(X_EQ_Y, "1.0alpha", "1.0.0alpha");
+        assertOrder(X_EQ_Y, "1.0-alpha-snapshot", "1.0.0-alpha-snapshot");
+        assertOrder(X_EQ_Y, "1.0.alpha.snapshot", "1.0.0.alpha.snapshot");
+
+        assertOrder(X_EQ_Y, "1.x.0-alpha", "1.x.0.0-alpha");
+        assertOrder(X_EQ_Y, "1.x.0.alpha", "1.x.0.0.alpha");
+        assertOrder(X_EQ_Y, "1.x.0-alpha-snapshot", "1.x.0.0-alpha-snapshot");
+        assertOrder(X_EQ_Y, "1.x.0.alpha.snapshot", "1.x.0.0.alpha.snapshot");
+    }
+
+    @Test
+    void testTrailingDelimitersAreSemanticallyIrrelevant() {
+        assertOrder(X_EQ_Y, "1", "1.............");
+        assertOrder(X_EQ_Y, "1", "1-------------");
+        assertOrder(X_EQ_Y, "1.0", "1.............");
+        assertOrder(X_EQ_Y, "1.0", "1-------------");
+    }
+
+    @Test
+    void testInitialDelimiters() {
+        assertOrder(X_EQ_Y, "0.1", ".1");
+        assertOrder(X_EQ_Y, "0.0.1", "..1");
+        assertOrder(X_EQ_Y, "0.1", "-1");
+        assertOrder(X_EQ_Y, "0.0.1", "--1");
+    }
+
+    @Test
+    void testConsecutiveDelimiters() {
+        assertOrder(X_EQ_Y, "1.0.1", "1..1");
+        assertOrder(X_EQ_Y, "1.0.0.1", "1...1");
+        assertOrder(X_EQ_Y, "1.0.1", "1--1");
+        assertOrder(X_EQ_Y, "1.0.0.1", "1---1");
+    }
+
+    @Test
+    void testUnlimitedNumberOfVersionComponents() {
+        assertOrder(X_GT_Y, "1.0.1.2.3.4.5.6.7.8.9.0.1.2.10", 
"1.0.1.2.3.4.5.6.7.8.9.0.1.2.3");
+    }
+
+    @Test
+    void testUnlimitedNumberOfDigitsInNumericComponent() {
+        assertOrder(X_GT_Y, "1.1234567890123456789012345678901", 
"1.123456789012345678901234567891");
+    }
+
+    @Test
+    void testTransitionFromDigitToLetterAndViceVersaIsEqualivantToDelimiter() {
+        assertOrder(X_EQ_Y, "1alpha10", "1.alpha.10");
+        assertOrder(X_EQ_Y, "1alpha10", "1-alpha-10");
+
+        assertOrder(X_GT_Y, "1.alpha10", "1.alpha2");
+        assertOrder(X_GT_Y, "10alpha", "1alpha");
+    }
+
+    @Test
+    void testWellKnownQualifierOrdering() {
+        assertOrder(X_EQ_Y, "1-alpha1", "1-a1");
+        assertOrder(X_LT_Y, "1-alpha", "1-beta");
+        assertOrder(X_EQ_Y, "1-beta1", "1-b1");
+        assertOrder(X_LT_Y, "1-beta", "1-milestone");
+        assertOrder(X_EQ_Y, "1-milestone1", "1-m1");
+        assertOrder(X_LT_Y, "1-milestone", "1-rc");
+        assertOrder(X_EQ_Y, "1-rc", "1-cr");
+        assertOrder(X_LT_Y, "1-rc", "1-snapshot");
+        assertOrder(X_LT_Y, "1-snapshot", "1");
+        assertOrder(X_EQ_Y, "1", "1-ga");
+        assertOrder(X_EQ_Y, "1", "1.ga.0.ga");
+        assertOrder(X_EQ_Y, "1.0", "1-ga");
+        assertOrder(X_EQ_Y, "1", "1-ga.ga");
+        assertOrder(X_EQ_Y, "1", "1-ga-ga");
+        assertOrder(X_EQ_Y, "A", "A.ga.ga");
+        assertOrder(X_EQ_Y, "A", "A-ga-ga");
+        assertOrder(X_EQ_Y, "1", "1-final");
+        assertOrder(X_EQ_Y, "1", "1-release");
+        assertOrder(X_LT_Y, "1", "1-sp");
+
+        assertOrder(X_LT_Y, "A.rc.1", "A.ga.1");
+        assertOrder(X_GT_Y, "A.sp.1", "A.ga.1");
+        assertOrder(X_LT_Y, "A.rc.x", "A.ga.x");
+        assertOrder(X_GT_Y, "A.sp.x", "A.ga.x");
+    }
+
+    @Test
+    void testWellKnownQualifierVersusUnknownQualifierOrdering() {
+        assertOrder(X_GT_Y, "1-abc", "1-alpha");
+        assertOrder(X_GT_Y, "1-abc", "1-beta");
+        assertOrder(X_GT_Y, "1-abc", "1-milestone");
+        assertOrder(X_GT_Y, "1-abc", "1-rc");
+        assertOrder(X_GT_Y, "1-abc", "1-snapshot");
+        assertOrder(X_GT_Y, "1-abc", "1");
+        assertOrder(X_GT_Y, "1-abc", "1-sp");
+    }
+
+    @Test
+    void 
testWellKnownSingleCharQualifiersOnlyRecognizedIfImmediatelyFollowedByNumber() {
+        assertOrder(X_GT_Y, "1.0a", "1.0");
+        assertOrder(X_GT_Y, "1.0-a", "1.0");
+        assertOrder(X_GT_Y, "1.0.a", "1.0");
+        assertOrder(X_GT_Y, "1.0b", "1.0");
+        assertOrder(X_GT_Y, "1.0-b", "1.0");
+        assertOrder(X_GT_Y, "1.0.b", "1.0");
+        assertOrder(X_GT_Y, "1.0m", "1.0");
+        assertOrder(X_GT_Y, "1.0-m", "1.0");
+        assertOrder(X_GT_Y, "1.0.m", "1.0");
+
+        assertOrder(X_LT_Y, "1.0a1", "1.0");
+        assertOrder(X_LT_Y, "1.0-a1", "1.0");
+        assertOrder(X_LT_Y, "1.0.a1", "1.0");
+        assertOrder(X_LT_Y, "1.0b1", "1.0");
+        assertOrder(X_LT_Y, "1.0-b1", "1.0");
+        assertOrder(X_LT_Y, "1.0.b1", "1.0");
+        assertOrder(X_LT_Y, "1.0m1", "1.0");
+        assertOrder(X_LT_Y, "1.0-m1", "1.0");
+        assertOrder(X_LT_Y, "1.0.m1", "1.0");
+
+        assertOrder(X_GT_Y, "1.0a.1", "1.0");
+        assertOrder(X_GT_Y, "1.0a-1", "1.0");
+        assertOrder(X_GT_Y, "1.0b.1", "1.0");
+        assertOrder(X_GT_Y, "1.0b-1", "1.0");
+        assertOrder(X_GT_Y, "1.0m.1", "1.0");
+        assertOrder(X_GT_Y, "1.0m-1", "1.0");
+    }
+
+    @Test
+    void testUnknownQualifierOrdering() {
+        assertOrder(X_LT_Y, "1-abc", "1-abcd");
+        assertOrder(X_LT_Y, "1-abc", "1-bcd");
+        assertOrder(X_GT_Y, "1-abc", "1-aac");
+    }
+
+    @Test
+    void testCaseInsensitiveOrderingOfQualifiers() {
+        assertOrder(X_EQ_Y, "1.alpha", "1.ALPHA");
+        assertOrder(X_EQ_Y, "1.alpha", "1.Alpha");
+
+        assertOrder(X_EQ_Y, "1.beta", "1.BETA");
+        assertOrder(X_EQ_Y, "1.beta", "1.Beta");
+
+        assertOrder(X_EQ_Y, "1.milestone", "1.MILESTONE");
+        assertOrder(X_EQ_Y, "1.milestone", "1.Milestone");
+
+        assertOrder(X_EQ_Y, "1.rc", "1.RC");
+        assertOrder(X_EQ_Y, "1.rc", "1.Rc");
+        assertOrder(X_EQ_Y, "1.cr", "1.CR");
+        assertOrder(X_EQ_Y, "1.cr", "1.Cr");
+
+        assertOrder(X_EQ_Y, "1.snapshot", "1.SNAPSHOT");
+        assertOrder(X_EQ_Y, "1.snapshot", "1.Snapshot");
+
+        assertOrder(X_EQ_Y, "1.ga", "1.GA");
+        assertOrder(X_EQ_Y, "1.ga", "1.Ga");
+        assertOrder(X_EQ_Y, "1.final", "1.FINAL");
+        assertOrder(X_EQ_Y, "1.final", "1.Final");
+        assertOrder(X_EQ_Y, "1.release", "1.RELEASE");
+        assertOrder(X_EQ_Y, "1.release", "1.Release");
+
+        assertOrder(X_EQ_Y, "1.sp", "1.SP");
+        assertOrder(X_EQ_Y, "1.sp", "1.Sp");
+
+        assertOrder(X_EQ_Y, "1.unknown", "1.UNKNOWN");
+        assertOrder(X_EQ_Y, "1.unknown", "1.Unknown");
+    }
+
+    @Test
+    void testCaseInsensitiveOrderingOfQualifiersIsLocaleIndependent() {
+        Locale orig = Locale.getDefault();
+        try {
+            Locale[] locales = {Locale.ENGLISH, new Locale("tr")};
+            for (Locale locale : locales) {
+                Locale.setDefault(locale);
+                assertOrder(X_EQ_Y, "1-abcdefghijklmnopqrstuvwxyz", 
"1-ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+            }
+        } finally {
+            Locale.setDefault(orig);
+        }
+    }
+
+    @Test
+    void testQualifierVersusNumberOrdering() {
+        assertOrder(X_LT_Y, "1-ga", "1-1");
+        assertOrder(X_LT_Y, "1.ga", "1.1");
+        assertOrder(X_EQ_Y, "1-ga", "1.0");
+        assertOrder(X_EQ_Y, "1.ga", "1.0");
+
+        assertOrder(X_LT_Y, "1-ga-1", "1-0-1");
+        assertOrder(X_LT_Y, "1.ga.1", "1.0.1");
+
+        assertOrder(X_GT_Y, "1.sp", "1.0");
+        assertOrder(X_LT_Y, "1.sp", "1.1");
+
+        assertOrder(X_LT_Y, "1-abc", "1-1");
+        assertOrder(X_LT_Y, "1.abc", "1.1");
+
+        assertOrder(X_LT_Y, "1-xyz", "1-1");
+        assertOrder(X_LT_Y, "1.xyz", "1.1");
+    }
+
+    @Test
+    void testVersionEvolution() {
+        assertSequence(
+                "0.9.9-SNAPSHOT",
+                "0.9.9",
+                "0.9.10-SNAPSHOT",
+                "0.9.10",
+                "1.0-alpha-2-SNAPSHOT",
+                "1.0-alpha-2",
+                "1.0-alpha-10-SNAPSHOT",
+                "1.0-alpha-10",
+                "1.0-beta-1-SNAPSHOT",
+                "1.0-beta-1",
+                "1.0-rc-1-SNAPSHOT",
+                "1.0-rc-1",
+                "1.0-SNAPSHOT",
+                "1.0",
+                "1.0-sp-1-SNAPSHOT",
+                "1.0-sp-1",
+                "1.0.1-alpha-1-SNAPSHOT",
+                "1.0.1-alpha-1",
+                "1.0.1-beta-1-SNAPSHOT",
+                "1.0.1-beta-1",
+                "1.0.1-rc-1-SNAPSHOT",
+                "1.0.1-rc-1",
+                "1.0.1-SNAPSHOT",
+                "1.0.1",
+                "1.1-SNAPSHOT",
+                "1.1");
+
+        assertSequence("1.0-alpha", "1.0", "1.0-1");
+        assertSequence("1.0.alpha", "1.0", "1.0-1");
+        assertSequence("1.0-alpha", "1.0", "1.0.1");
+        assertSequence("1.0.alpha", "1.0", "1.0.1");
+    }
+
+    @Test
+    void testMinimumSegment() {
+        assertOrder(X_LT_Y, "1.min", "1.0-alpha-1");
+        assertOrder(X_LT_Y, "1.min", "1.0-SNAPSHOT");
+        assertOrder(X_LT_Y, "1.min", "1.0");
+        assertOrder(X_LT_Y, "1.min", "1.9999999999");
+
+        assertOrder(X_EQ_Y, "1.min", "1.MIN");
+
+        assertOrder(X_GT_Y, "1.min", "0.99999");
+        assertOrder(X_GT_Y, "1.min", "0.max");
+    }
+
+    @Test
+    void testMaximumSegment() {
+        assertOrder(X_GT_Y, "1.max", "1.0-alpha-1");
+        assertOrder(X_GT_Y, "1.max", "1.0-SNAPSHOT");
+        assertOrder(X_GT_Y, "1.max", "1.0");
+        assertOrder(X_GT_Y, "1.max", "1.9999999999");
+
+        assertOrder(X_EQ_Y, "1.max", "1.MAX");
+
+        assertOrder(X_LT_Y, "1.max", "2.0-alpha-1");
+        assertOrder(X_LT_Y, "1.max", "2.min");
+    }
+
+    /**
+     * UT for <a 
href="https://issues.apache.org/jira/browse/MRESOLVER-314";>MRESOLVER-314</a>.
+     *
+     * Generates random UUID string based versions and tries to sort them. 
While this test is not as reliable
+     * as {@link #testCompareUuidVersionStringStream()}, it covers broader 
range and in case it fails it records
+     * the failed array, so we can investigate more.
+     */
+    @Test
+    void testCompareUuidRandom() {
+        for (int j = 0; j < 32; j++) {
+            ArrayList<Version> versions = new ArrayList<>();
+            for (int i = 0; i < 64; i++) {
+                versions.add(newVersion(UUID.randomUUID().toString()));
+            }
+            try {
+                Collections.sort(versions);
+            } catch (Exception e) {
+                e.printStackTrace(System.err);
+                System.err.println("The UUIDs used");
+                
System.err.println(versions.stream().map(Version::toString).collect(Collectors.joining("\n")));
+                fail("unexpected exception");
+            }
+        }
+    }
+
+    /**
+     * UT for <a 
href="https://issues.apache.org/jira/browse/MRESOLVER-314";>MRESOLVER-314</a>.
+     *
+     * Works on known set that failed before fix, provided by {@link 
#uuidVersionStringStream()}.
+     */
+    @Test
+    void testCompareUuidVersionStringStream() {
+        // this operation below fails with IAEx if comparison is unstable
+        
uuidVersionStringStream().map(this::newVersion).sorted().collect(toList());
+    }
+
+    private Stream<String> uuidVersionStringStream() {
+        return Stream.of(
+                "e3f6b227-e09d-4461-a030-b8c1755834f7",
+                "dfdf5e15-b047-4fee-94e5-3ddf6fe90a0c",
+                "bcc15412-6817-4b64-acef-169d048626f6",
+                "76093f07-ab1c-4cdd-ae92-9bb500ceed84",
+                "7ca8dc9f-4e73-459b-8f30-06aa7972f486",
+                "93fee46b-2715-4abd-877a-4197eb8601aa",
+                "0379da36-84ee-4d06-9388-83d3aa6536b5",
+                "4bb2c7a8-cf68-4ca5-8024-72dc93506da9",
+                "9dcc4cd1-34d2-4499-8dab-3ef8bca9680d",
+                "ea53d552-83ab-4f7d-852d-98951201083d",
+                "0bc420d2-4089-468b-bc54-0a4e2835feed",
+                "318d2433-fe40-4f28-9f3a-4e3d66d9b5fb",
+                "447b456c-81a4-4f24-9d2e-e5091c39cd19",
+                "85741f6e-26fe-40d0-a73a-283315409ab2",
+                "3165b9b2-9f8e-4117-ac70-87056eb45745",
+                "9d534bf3-a3b0-4a19-9809-670934c10752",
+                "86d78bba-d84e-4349-aea6-850721e78188",
+                "06392b8c-e26c-4a83-8ec2-085415bc513d",
+                "1fb13754-90be-42cb-bc7f-9b9211494e92",
+                "3018965c-3330-402a-8075-caa7613ec4fa",
+                "7ecc912b-4938-4411-895e-8ca7cf22ce02",
+                "6580ada2-4764-45a2-9789-98217d7cf5b6",
+                "be9d0de4-4ba7-4fdd-8f76-cb579168c549",
+                "7a8236d6-6bec-4176-b6a1-f869c02183c3",
+                "089f4195-881c-4f9e-8bc1-124531dee977",
+                "46ffda62-768a-4864-9581-cc75eafe1a67",
+                "1d6226f6-dacc-42a9-bd88-7aab1f59df74",
+                "0948ed55-c25e-4319-9801-5f817bac09b5",
+                "2fd52f5e-b856-47ad-9e58-45c1d0ba437b",
+                "6c325bd0-ac6b-4391-a5c5-caa160972fa2",
+                "d213f6be-f56b-42d2-abda-4300742e0add",
+                "efaae115-cc21-4b2e-a150-fb4e0d807736",
+                "30f872e8-9cb5-4b22-b65c-6819ca7a14ba",
+                "d8e5fb54-6e90-4f74-adb3-451abfbe76a8",
+                "b47d62b8-9256-47a1-8e21-21ba9639c212",
+                "b25da555-e1f7-4bc5-92fe-4c895d9c70d8",
+                "088f0de7-5973-4c10-a7ff-9f3cd7718572",
+                "b161de76-e5d5-4224-883b-a749b147d63d",
+                "19b7de96-09fa-4276-843d-c0fbdaf07767",
+                "e0503f73-33fd-4f9c-812f-8cae3a128c28",
+                "b8c57488-a42c-43ed-bfb9-acd112d6b68f",
+                "25997299-0825-4c9b-b0ed-75f935c63fd7",
+                "2b2e2fcd-3988-45af-855b-7646c0cdbfb5",
+                "4e6e16b9-2ae4-4593-b907-1febaf3988dc",
+                "ac8bd519-7fd4-4b85-8154-9dbb87f6cd4f",
+                "61473b39-b620-468b-abcf-16fe6adfd5cb",
+                "18e7a548-3f0b-492b-bc19-dce3eec736fa",
+                "c4d82839-3c46-4eff-b10c-ec0b5bcc600b",
+                "48f6e90f-924b-4859-9763-3ffe661f5af6",
+                "48852d79-ba23-475e-b675-a413b989a2a7",
+                "f7ee0915-ff00-4404-9e9a-6e753d5ff767",
+                "d6462359-a4e2-45ab-aedc-3b1849b0e6ca",
+                "e66228de-d1ed-4973-a108-c181d5059fdb",
+                "d49672a7-177d-475d-aad0-aab0ff4a11b7",
+                "bfa9337a-0489-4cba-b2db-e0d9d2424e4f",
+                "dc9bbe34-3c54-4c0f-a3cd-00e96604ae23",
+                "a8119cf1-9694-4b24-923a-3fc729b5f809",
+                "5d29cf45-3b9c-4697-85b8-86c81c6ec0c9",
+                "e3dcb4c2-a867-40f7-a3b1-fb1058a041e5",
+                "ae240754-2ea2-409a-a92c-648fc7a7b70b",
+                "8c187383-d59b-4e49-8dfd-98aa5f01925a",
+                "9b100ee6-71ed-4746-92c2-b5fb02af7ebd",
+                "f95e94f7-2443-4b2f-a10d-059d8d224dd9",
+                "b558af80-78bc-43c7-b916-d635a23cc4b5");
+    }
+}

Reply via email to