Repository: mojohaus/versions (java)

Issue:
versions:update-parent 2.16.2 not respecting range when allowDowngrade is set to true
In my project parent pom is set to

```
<parent>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>mojo-parent</artifactId>
    <version>70</version>
</parent>
```

When I invoke 
mvn versions:update-parent -DallowDowngrade=false -DparentVersion=[,79-!)

```
[INFO] --- versions:2.16.2:update-parent (default-cli) @ test-update-parent ---
[INFO] Updating parent from 70 to 78
```
-> works fine.

However, when I invoke
mvn versions:update-parent -D**allowDowngrade=true** -DparentVersion=[,79-!)

```
[INFO] --- versions:2.16.2:update-parent (default-cli) @ test-update-parent ---
[INFO] Updating parent from 78 to 80
```

i.e. it does not respect the range.

With version 2.11.0 both cases work as expected.
Maybe related to https://github.com/mojohaus/versions/issues/670


Pull request:
Resolves #1060: Bugfix in UpdateParentMojo + add tests
Bugfix

Patch:
diff --git a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UpdateParentMojo.java b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UpdateParentMojo.java
index b992255a..32b76113 100644
--- a/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UpdateParentMojo.java
+++ b/versions-maven-plugin/src/main/java/org/codehaus/mojo/versions/UpdateParentMojo.java
@@ -48,6 +48,7 @@ import org.codehaus.mojo.versions.utils.DependencyBuilder;
 import org.codehaus.mojo.versions.utils.SegmentUtils;
 import org.eclipse.aether.RepositorySystem;
 
+import static java.util.stream.StreamSupport.stream;
 import static org.apache.commons.lang3.StringUtils.isBlank;
 
 /**
@@ -164,12 +165,9 @@ public class UpdateParentMojo extends AbstractVersionsUpdaterMojo {
             throw new MojoExecutionException("skipResolution is only valid if parentVersion is set");
         }
 
-        String initialVersion = Optional.ofNullable(parentVersion)
-                .orElse(getProject().getParent().getVersion());
         try {
-            ArtifactVersion artifactVersion = skipResolution
-                    ? DefaultArtifactVersionCache.of(parentVersion)
-                    : resolveTargetVersion(initialVersion);
+            ArtifactVersion artifactVersion =
+                    skipResolution ? DefaultArtifactVersionCache.of(parentVersion) : resolveTargetVersion();
             if (artifactVersion != null) {
                 getLog().info("Updating parent from " + getProject().getParent().getVersion() + " to "
                         + artifactVersion);
@@ -190,25 +188,25 @@ public class UpdateParentMojo extends AbstractVersionsUpdaterMojo {
                 }
             }
         } catch (InvalidVersionSpecificationException e) {
-            throw new MojoExecutionException("Invalid version range specification: " + initialVersion, e);
+            throw new MojoExecutionException("Invalid version range specification: " + parentVersion, e);
         } catch (InvalidSegmentException e) {
-            throw new MojoExecutionException("Invalid segment specification for version " + initialVersion, e);
+            throw new MojoExecutionException("Invalid segment specification for version " + parentVersion, e);
         }
     }
 
-    protected ArtifactVersion resolveTargetVersion(String initialVersion)
+    protected ArtifactVersion resolveTargetVersion()
             throws MojoExecutionException, VersionRetrievalException, InvalidVersionSpecificationException,
                     InvalidSegmentException {
         Artifact artifact = getHelper()
                 .createDependencyArtifact(DependencyBuilder.newBuilder()
                         .withGroupId(getProject().getParent().getGroupId())
                         .withArtifactId(getProject().getParent().getArtifactId())
-                        .withVersion(initialVersion)
+                        .withVersion(getProject().getParent().getVersion())
                         .withType("pom")
                         .build());
 
-        VersionRange targetVersionRange = VersionRange.createFromVersionSpec(initialVersion);
-        if (targetVersionRange.getRecommendedVersion() != null) {
+        VersionRange targetVersionRange = VersionRange.createFromVersionSpec(parentVersion);
+        if (targetVersionRange != null && targetVersionRange.getRecommendedVersion() != null) {
             targetVersionRange = targetVersionRange.restrict(
                     VersionRange.createFromVersionSpec("[" + targetVersionRange.getRecommendedVersion() + ",)"));
         }
@@ -219,27 +217,35 @@ public class UpdateParentMojo extends AbstractVersionsUpdaterMojo {
 
         // currentVersion (set to parentVersion here) is not included in the version range for searching upgrades
         // unless we set allowDowngrade to true
-        for (ArtifactVersion candidate : reverse(versions.getNewerVersions(
-                initialVersion, unchangedSegment, allowSnapshots, !isBlank(parentVersion) || allowDowngrade))) {
-            if (allowDowngrade
-                    || targetVersionRange == null
-                    || ArtifactVersions.isVersionInRange(candidate, targetVersionRange)) {
-                if (shouldApplyUpdate(artifact, getProject().getParent().getVersion(), candidate, forceUpdate)) {
-                    return candidate;
-                } else {
-                    getLog().debug("Update not applied. Exiting.");
-                    return null;
-                }
-            }
-        }
 
-        if (versions.isEmpty(allowSnapshots)) {
-            getLog().info("No versions found");
-        } else {
-            getLog().info("The parent project is the latest version");
-        }
-
-        return null;
+        VersionRange finalTargetVersionRange = targetVersionRange;
+        return stream(
+                        reverse(versions.getNewerVersions(
+                                        getProject().getParent().getVersion(),
+                                        unchangedSegment,
+                                        allowSnapshots,
+                                        allowDowngrade))
+                                .spliterator(),
+                        false)
+                .filter(v -> finalTargetVersionRange == null
+                        || ArtifactVersions.isVersionInRange(v, finalTargetVersionRange))
+                .findFirst()
+                .map(candidate -> {
+                    if (shouldApplyUpdate(artifact, getProject().getParent().getVersion(), candidate, forceUpdate)) {
+                        return candidate;
+                    } else {
+                        getLog().debug("Update not applied. Exiting.");
+                        return null;
+                    }
+                })
+                .orElseGet(() -> {
+                    if (versions.isEmpty(allowSnapshots)) {
+                        getLog().info("No versions found");
+                    } else {
+                        getLog().info("The parent project is the latest version");
+                    }
+                    return null;
+                });
     }
 
     private static <T> Iterable<T> reverse(T[] array) {


Test patch:
diff --git a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UpdateParentMojoTest.java b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UpdateParentMojoTest.java
index bedfd48b..d8d2a14a 100644
--- a/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UpdateParentMojoTest.java
+++ b/versions-maven-plugin/src/test/java/org/codehaus/mojo/versions/UpdateParentMojoTest.java
@@ -76,6 +76,7 @@ public class UpdateParentMojoTest {
                 put("dummy-parent2", new String[] {"1.0", "2.0", "3.0", "3.0-alpha-1", "3.0-beta-1"});
                 put("test-incremental", new String[] {"1.0.0", "1.1.0", "1.1.1", "2.0.0"});
                 put("unknown-artifact", new String[0]);
+                put("mojo-parent", new String[] {"70", "78", "86"});
             }
         });
     }
@@ -186,10 +187,10 @@ public class UpdateParentMojoTest {
             {
                 setGroupId("default-group");
                 setArtifactId("parent-artifact");
+                setVersion("[1.0.1-SNAPSHOT,)");
             }
         });
-
-        ArtifactVersion newVersion = mojo.resolveTargetVersion("[1.0.1-SNAPSHOT,)");
+        ArtifactVersion newVersion = mojo.resolveTargetVersion();
         assertThat(newVersion, notNullValue());
         assertThat(newVersion.toString(), is("1.0.0"));
     }
@@ -199,7 +200,8 @@ public class UpdateParentMojoTest {
             throws MojoExecutionException, VersionRetrievalException, InvalidVersionSpecificationException,
                     InvalidSegmentException {
         mojo.allowDowngrade = false;
-        ArtifactVersion newVersion = mojo.resolveTargetVersion("[1.0.1-SNAPSHOT,)");
+        mojo.parentVersion = "[1.0.1-SNAPSHOT,)";
+        ArtifactVersion newVersion = mojo.resolveTargetVersion();
         assertThat(newVersion, nullValue());
     }
 
@@ -212,10 +214,11 @@ public class UpdateParentMojoTest {
             {
                 setGroupId("default-group");
                 setArtifactId("issue-670-artifact");
+                setVersion("0.0.1-1");
             }
         });
 
-        ArtifactVersion newVersion = mojo.resolveTargetVersion("0.0.1-1");
+        ArtifactVersion newVersion = mojo.resolveTargetVersion();
         assertThat(newVersion, notNullValue());
         assertThat(newVersion.toString(), is("0.0.1-1-impl-SNAPSHOT"));
     }
@@ -253,10 +256,11 @@ public class UpdateParentMojoTest {
             {
                 setGroupId("default-group");
                 setArtifactId("parent-artifact");
+                setVersion("0.9.0");
             }
         });
         setVariableValueToObject(mojo, "ignoredVersions", singleton("1.0.0"));
-        assertThat(mojo.resolveTargetVersion("0.9.0"), nullValue());
+        assertThat(mojo.resolveTargetVersion(), nullValue());
     }
 
     @Test
@@ -337,8 +341,8 @@ public class UpdateParentMojoTest {
         mojo.allowMajorUpdates = false;
         mojo.allowMinorUpdates = true;
         mojo.allowIncrementalUpdates = true;
-
-        ArtifactVersion newVersion = mojo.resolveTargetVersion("0.8.0");
+        mojo.parentVersion = "0.8.0";
+        ArtifactVersion newVersion = mojo.resolveTargetVersion();
 
         assertThat(newVersion, notNullValue());
         assertThat(newVersion.toString(), is("0.9.0"));
@@ -352,13 +356,13 @@ public class UpdateParentMojoTest {
             {
                 setGroupId("default-group");
                 setArtifactId("test-incremental");
+                setVersion("1.1.0");
             }
         });
         mojo.allowMajorUpdates = false;
         mojo.allowMinorUpdates = false;
         mojo.allowIncrementalUpdates = true;
-
-        ArtifactVersion newVersion = mojo.resolveTargetVersion("1.1.0");
+        ArtifactVersion newVersion = mojo.resolveTargetVersion();
 
         assertThat(newVersion, notNullValue());
         assertThat(newVersion.toString(), is("1.1.1"));
@@ -407,4 +411,52 @@ public class UpdateParentMojoTest {
         }
         assertThat(changeRecorder.getChanges(), empty());
     }
+
+    /*
+     * Reproduces issue 1060
+     */
+    @Test
+    public void testIssue1060VersionRangeAllowDowngradeFalse()
+            throws MojoExecutionException, XMLStreamException, MojoFailureException, VersionRetrievalException {
+        mojo.getProject().setParent(new MavenProject() {
+            {
+                setGroupId("default-group");
+                setArtifactId("dummy-parent2");
+                setVersion("2.0");
+            }
+        });
+        mojo.parentVersion = "[,2.9-!)";
+        mojo.allowDowngrade = false;
+        try (MockedStatic<PomHelper> pomHelper = mockStatic(PomHelper.class)) {
+            pomHelper
+                    .when(() -> PomHelper.setProjectParentVersion(any(), any()))
+                    .thenReturn(true);
+            mojo.update(null);
+        }
+        assertThat(changeRecorder.getChanges(), empty());
+    }
+
+    /*
+     * Reproduces issue 1060
+     */
+    @Test
+    public void testIssue1060VersionRangeAllowDowngradeTrue()
+            throws MojoExecutionException, XMLStreamException, MojoFailureException, VersionRetrievalException {
+        mojo.getProject().setParent(new MavenProject() {
+            {
+                setGroupId("default-group");
+                setArtifactId("mojo-parent");
+                setVersion("78");
+            }
+        });
+        mojo.parentVersion = "[,79-!)";
+        mojo.allowDowngrade = true;
+        try (MockedStatic<PomHelper> pomHelper = mockStatic(PomHelper.class)) {
+            pomHelper
+                    .when(() -> PomHelper.setProjectParentVersion(any(), any()))
+                    .thenReturn(true);
+            mojo.update(null);
+        }
+        assertThat(changeRecorder.getChanges(), empty());
+    }
 }