Repository: mojohaus/versions
Pull request #1056 (2024-02-25 23:33:19)

Use new Enforcer Api


Patch:
diff --git a/versions-enforcer/pom.xml b/versions-enforcer/pom.xml
index c363b01b..2bac0ac5 100644
--- a/versions-enforcer/pom.xml
+++ b/versions-enforcer/pom.xml
@@ -13,6 +13,7 @@
   <description>Enforcer rules using Versions Maven Plugin</description>
 
   <properties>
+    <!-- used in documentations -->
     <maven-enforcer-plugin-version>${maven-enforcer-plugin.version}</maven-enforcer-plugin-version>
   </properties>
 
diff --git a/versions-enforcer/src/main/java/org/apache/maven/plugins/enforcer/MaxDependencyUpdates.java b/versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdates.java
similarity index 78%
rename from versions-enforcer/src/main/java/org/apache/maven/plugins/enforcer/MaxDependencyUpdates.java
rename to versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdates.java
index 9ec90f8b..e851c34d 100644
--- a/versions-enforcer/src/main/java/org/apache/maven/plugins/enforcer/MaxDependencyUpdates.java
+++ b/versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdates.java
@@ -1,4 +1,4 @@
-package org.apache.maven.plugins.enforcer;
+package org.codehaus.mojo.versions.enforcer;
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -18,20 +18,21 @@ package org.apache.maven.plugins.enforcer;
  * under the License.
  */
 
+import javax.inject.Inject;
+import javax.inject.Named;
+
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.stream.Collectors;
 
 import org.apache.maven.artifact.versioning.ArtifactVersion;
-import org.apache.maven.enforcer.rule.api.EnforcerLevel;
-import org.apache.maven.enforcer.rule.api.EnforcerRule;
-import org.apache.maven.enforcer.rule.api.EnforcerRule2;
+import org.apache.maven.enforcer.rule.api.AbstractEnforcerRule;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleError;
 import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
-import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.model.Dependency;
 import org.apache.maven.plugin.MojoExecution;
@@ -46,8 +47,6 @@ import org.codehaus.mojo.versions.api.VersionRetrievalException;
 import org.codehaus.mojo.versions.api.VersionsHelper;
 import org.codehaus.mojo.versions.model.RuleSet;
 import org.codehaus.mojo.versions.utils.DependencyComparator;
-import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
-import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
 
 import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
@@ -62,7 +61,8 @@ import static org.codehaus.mojo.versions.utils.MavenProjectUtils.extractDependen
 import static org.codehaus.mojo.versions.utils.MavenProjectUtils.extractDependenciesFromPlugins;
 import static org.codehaus.mojo.versions.utils.MavenProjectUtils.extractPluginDependenciesFromPluginsInPluginManagement;
 
-public class MaxDependencyUpdates implements EnforcerRule2 {
+@Named("maxDependencyUpdates")
+public class MaxDependencyUpdates extends AbstractEnforcerRule {
     /**
      * Maximum allowed number of updates.
      *
@@ -232,7 +232,7 @@ public class MaxDependencyUpdates implements EnforcerRule2 {
      * version numbers. The URI could be either a Wagon URI or a classpath URI
      * (e.g. <code>classpath:///package/sub/package/rules.xml</code>).
      *
-     * 2.14.0
+     * @since 2.14.0
      */
     private String rulesUri;
 
@@ -254,81 +254,78 @@ public class MaxDependencyUpdates implements EnforcerRule2 {
      */
     protected boolean allowSnapshots;
 
-    /**
-     * Retrieves the maven project from metadata
-     * @param ruleHelper EnforcerRuleHelper object
-     * @return maven project
-     */
-    private static MavenProject getMavenProject(EnforcerRuleHelper ruleHelper) {
-        try {
-            return (MavenProject) ruleHelper.evaluate("${project}");
-        } catch (ExpressionEvaluationException e) {
-            throw new RuntimeException("Cannot evaluate project metadata", e);
-        }
+    private final MavenProject project;
+
+    private final RepositorySystem repositorySystem;
+
+    private final org.eclipse.aether.RepositorySystem aetherRepositorySystem;
+
+    private final Map<String, Wagon> wagonMap;
+
+    private final MavenSession mavenSession;
+
+    private final MojoExecution mojoExecution;
+
+    @Inject
+    public MaxDependencyUpdates(
+            MavenProject project,
+            RepositorySystem repositorySystem,
+            org.eclipse.aether.RepositorySystem aetherRepositorySystem,
+            Map<String, Wagon> wagonMap,
+            MavenSession mavenSession,
+            MojoExecution mojoExecution) {
+        this.project = project;
+        this.repositorySystem = repositorySystem;
+        this.aetherRepositorySystem = aetherRepositorySystem;
+        this.wagonMap = wagonMap;
+        this.mavenSession = mavenSession;
+        this.mojoExecution = mojoExecution;
     }
 
     /**
      * Creates the VersionsHelper object
-     * @param ruleHelper EnforcerRuleHelper object
      * @return VersionsHelper object
      */
-    @SuppressWarnings("unchecked")
-    private static VersionsHelper createVersionsHelper(
-            EnforcerRuleHelper ruleHelper, String serverId, String rulesUri, RuleSet ruleSet) {
+    private VersionsHelper createVersionsHelper(String serverId, String rulesUri, RuleSet ruleSet)
+            throws EnforcerRuleError {
         try {
             return new DefaultVersionsHelper.Builder()
-                    .withRepositorySystem(ruleHelper.getComponent(RepositorySystem.class))
-                    .withAetherRepositorySystem(ruleHelper.getComponent(org.eclipse.aether.RepositorySystem.class))
-                    .withWagonMap(ruleHelper.getComponentMap(Wagon.class.getName()).entrySet().stream()
-                            .filter(e -> e.getValue() instanceof Wagon)
-                            .collect(HashMap::new, (m, e) -> m.put(e.getKey(), (Wagon) e.getValue()), HashMap::putAll))
+                    .withRepositorySystem(repositorySystem)
+                    .withAetherRepositorySystem(aetherRepositorySystem)
+                    .withWagonMap(wagonMap)
                     .withServerId(serverId)
                     .withRulesUri(rulesUri)
                     .withRuleSet(ruleSet)
                     .withIgnoredVersions(null)
-                    .withLog(ruleHelper.getLog())
-                    .withMavenSession((MavenSession) ruleHelper.evaluate("${session}"))
-                    .withMojoExecution((MojoExecution) ruleHelper.evaluate("${mojoExecution}"))
+                    .withLog(new PluginLogWrapper(getLog()))
+                    .withMavenSession(mavenSession)
+                    .withMojoExecution(mojoExecution)
                     .build();
-        } catch (ExpressionEvaluationException e) {
-            throw new RuntimeException("Cannot evaluate project metadata", e);
-        } catch (ComponentLookupException | MojoExecutionException e) {
-            throw new RuntimeException("Cannot resolve dependency", e);
+        } catch (MojoExecutionException e) {
+            throw new EnforcerRuleError("Cannot resolve dependency", e);
         }
     }
 
     @Override
-    public boolean isCacheable() {
-        return false;
-    }
-
-    @Override
-    public boolean isResultValid(EnforcerRule enforcerRule) {
-        return false;
-    }
+    public void execute() throws EnforcerRuleException {
 
-    @Override
-    public String getCacheId() {
-        return "Does not matter as not cacheable";
-    }
+        PluginLogWrapper pluginLog = new PluginLogWrapper(getLog());
 
-    @Override
-    public void execute(EnforcerRuleHelper ruleHelper) throws EnforcerRuleException {
         VersionsHelper versionsHelper =
-                createVersionsHelper(ruleHelper, serverId != null ? serverId : "serverId", rulesUri, ruleSet);
-        MavenProject project = getMavenProject(ruleHelper);
+                createVersionsHelper(serverId != null ? serverId : "serverId", rulesUri, ruleSet);
+
         Set<Dependency> dependencies = new TreeSet<>(DependencyComparator.INSTANCE);
         if (processDependencyManagement) {
             try {
                 dependencies.addAll(filterDependencies(
                         extractDependenciesFromDependencyManagement(
-                                project, processDependencyManagementTransitive, ruleHelper.getLog()),
+                                project, processDependencyManagementTransitive, pluginLog),
                         dependencyManagementIncludes,
                         dependencyManagementExcludes,
                         "Dependency Management",
-                        ruleHelper.getLog()));
+                        pluginLog));
             } catch (VersionRetrievalException e) {
-                throw new EnforcerRuleException(e.getMessage());
+                throw new EnforcerRuleError(e.getMessage());
             }
         }
         if (processPluginDependencies) {
@@ -337,7 +334,7 @@ public class MaxDependencyUpdates implements EnforcerRule2 {
                     pluginDependencyIncludes,
                     pluginDependencyExcludes,
                     "Plugin Dependencies",
-                    ruleHelper.getLog()));
+                    pluginLog));
         }
         if (processPluginDependenciesInPluginManagement) {
             dependencies.addAll(filterDependencies(
@@ -345,15 +342,11 @@ public class MaxDependencyUpdates implements EnforcerRule2 {
                     pluginManagementDependencyIncludes,
                     pluginManagementDependencyExcludes,
                     "Plugin Management Dependencies",
-                    ruleHelper.getLog()));
+                    pluginLog));
         }
         if (processDependencies) {
             dependencies.addAll(filterDependencies(
-                    project.getDependencies(),
-                    dependencyIncludes,
-                    dependencyExcludes,
-                    "Dependencies",
-                    ruleHelper.getLog()));
+                    project.getDependencies(), dependencyIncludes, dependencyExcludes, "Dependencies", pluginLog));
         }
         try {
             Optional<Segment> ignoredSegment = ignoreSubIncrementalUpdates
@@ -379,13 +372,7 @@ public class MaxDependencyUpdates implements EnforcerRule2 {
                                 .collect(Collectors.joining(", ")));
             }
         } catch (VersionRetrievalException e) {
-            throw new RuntimeException(e.getMessage(), e);
+            throw new EnforcerRuleError(e);
         }
     }
-
-    @Override
-    public EnforcerLevel getLevel() {
-        // all reported items should be treated as errors
-        return EnforcerLevel.ERROR;
-    }
 }
diff --git a/versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/PluginLogWrapper.java b/versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/PluginLogWrapper.java
new file mode 100644
index 00000000..54ac62df
--- /dev/null
+++ b/versions-enforcer/src/main/java/org/codehaus/mojo/versions/enforcer/PluginLogWrapper.java
@@ -0,0 +1,109 @@
+package org.codehaus.mojo.versions.enforcer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import org.apache.maven.enforcer.rule.api.EnforcerLogger;
+import org.apache.maven.plugin.logging.Log;
+
+/**
+ * Wrapper used to pass {@link EnforcerLogger} as Maven plugin {@link Log}.
+ */
+class PluginLogWrapper implements Log {
+
+    private final EnforcerLogger logger;
+
+    PluginLogWrapper(EnforcerLogger logger) {
+        this.logger = logger;
+    }
+
+    @Override
+    public boolean isDebugEnabled() {
+        return logger.isDebugEnabled();
+    }
+
+    @Override
+    public void debug(CharSequence charSequence) {
+        logger.debug(charSequence);
+    }
+
+    @Override
+    public void debug(CharSequence charSequence, Throwable throwable) {
+        logger.debug(charSequence + throwableToString(throwable));
+    }
+
+    @Override
+    public void debug(Throwable throwable) {
+        logger.debug(throwableToString(throwable));
+    }
+
+    @Override
+    public boolean isInfoEnabled() {
+        return logger.isInfoEnabled();
+    }
+
+    @Override
+    public void info(CharSequence charSequence) {
+        logger.info(charSequence);
+    }
+
+    @Override
+    public void info(CharSequence charSequence, Throwable throwable) {
+
+        logger.info(charSequence + throwableToString(throwable));
+    }
+
+    @Override
+    public void info(Throwable throwable) {
+        logger.info(throwableToString(throwable));
+    }
+
+    @Override
+    public boolean isWarnEnabled() {
+        return logger.isWarnEnabled();
+    }
+
+    @Override
+    public void warn(CharSequence charSequence) {
+        logger.warn(charSequence);
+    }
+
+    @Override
+    public void warn(CharSequence charSequence, Throwable throwable) {
+        logger.warn(charSequence + throwableToString(throwable));
+    }
+
+    @Override
+    public void warn(Throwable throwable) {
+        logger.warn(throwableToString(throwable));
+    }
+
+    @Override
+    public boolean isErrorEnabled() {
+        return logger.isErrorEnabled();
+    }
+
+    @Override
+    public void error(CharSequence charSequence) {
+        logger.error(charSequence);
+    }
+
+    @Override
+    public void error(CharSequence charSequence, Throwable throwable) {
+        logger.error(charSequence + throwableToString(throwable));
+    }
+
+    @Override
+    public void error(Throwable throwable) {
+        logger.error(throwableToString(throwable));
+    }
+
+    private String throwableToString(Throwable throwable) {
+        if (throwable != null) {
+            ByteArrayOutputStream stream = new ByteArrayOutputStream();
+            throwable.printStackTrace(new PrintStream(stream));
+            return System.lineSeparator() + stream;
+        }
+        return "";
+    }
+}


Test patch:
diff --git a/versions-enforcer/src/test/java/org/apache/maven/plugins/enforcer/MaxDependencyUpdatesTest.java b/versions-enforcer/src/test/java/org/apache/maven/plugins/enforcer/MaxDependencyUpdatesTest.java
deleted file mode 100644
index 09ad4567..00000000
--- a/versions-enforcer/src/test/java/org/apache/maven/plugins/enforcer/MaxDependencyUpdatesTest.java
+++ /dev/null
@@ -1,269 +0,0 @@
-package org.apache.maven.plugins.enforcer;
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import java.util.HashMap;
-
-import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
-import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
-import org.apache.maven.plugin.MojoExecution;
-import org.apache.maven.project.MavenProject;
-import org.apache.maven.repository.RepositorySystem;
-import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
-import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
-import org.junit.Test;
-import org.mockito.ArgumentMatchers;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
-import static java.util.Collections.singletonMap;
-import static org.codehaus.mojo.versions.utils.DependencyBuilder.dependencyWith;
-import static org.codehaus.mojo.versions.utils.MockUtils.mockAetherRepositorySystem;
-import static org.codehaus.mojo.versions.utils.MockUtils.mockMavenSession;
-import static org.codehaus.mojo.versions.utils.MockUtils.mockRepositorySystem;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.Assert.fail;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-public class MaxDependencyUpdatesTest {
-    private static EnforcerRuleHelper mockRuleHelper(
-            MavenProject mavenProject, org.eclipse.aether.RepositorySystem aetherRepositorySystem)
-            throws ExpressionEvaluationException, ComponentLookupException {
-        EnforcerRuleHelper ruleHelper = mock(EnforcerRuleHelper.class);
-        when(ruleHelper.evaluate(anyString()))
-                .then((a) -> "${project}".equals(a.getArgument(0))
-                        ? mavenProject
-                        : "${session}".equals(a.getArgument(0))
-                                ? mockMavenSession()
-                                : "${mojoExecution}".equals(a.getArgument(0)) ? mock(MojoExecution.class) : null);
-        when(ruleHelper.getComponent(ArgumentMatchers.<Class<?>>any()))
-                .then((a) -> a.getArgument(0) == RepositorySystem.class
-                        ? mockRepositorySystem()
-                        : a.getArgument(0) == org.eclipse.aether.RepositorySystem.class
-                                ? aetherRepositorySystem
-                                : null);
-        return ruleHelper;
-    }
-
-    @Test
-    public void testRuleFailsByMaxUpdatesExceeded() throws ExpressionEvaluationException, ComponentLookupException {
-        EnforcerRuleHelper ruleHelper = mockRuleHelper(
-                new MavenProject() {
-                    {
-                        setDependencies(asList(
-                                dependencyWith("group", "artifactA", "1.0.0"),
-                                dependencyWith("group", "artifactB", "1.0.0")));
-                    }
-                },
-                mockAetherRepositorySystem(new HashMap<String, String[]>() {
-                    {
-                        put("artifactA", new String[] {"1.0.0", "2.0.0"});
-                        put("artifactB", new String[] {"1.0.0", "2.0.0"});
-                    }
-                }));
-
-        try {
-            new MaxDependencyUpdates() {
-                {
-                    maxUpdates = 1;
-                }
-            }.execute(ruleHelper);
-
-            fail("EnforcerRuleException should have been thrown");
-        } catch (EnforcerRuleException e) {
-            assertThat(e.getMessage(), containsString("More than 1 upgradable artifacts detected"));
-        }
-    }
-
-    @Test
-    public void testRulePassesByMaxUpdatesNotExceeded() throws ExpressionEvaluationException, ComponentLookupException {
-        EnforcerRuleHelper ruleHelper = mockRuleHelper(
-                new MavenProject() {
-                    {
-                        setDependencies(singletonList(dependencyWith("group", "artifactA", "1.0.0")));
-                    }
-                },
-                mockAetherRepositorySystem(singletonMap("artifactA", new String[] {"1.0.0", "2.0.0"})));
-
-        try {
-            new MaxDependencyUpdates() {
-                {
-                    maxUpdates = 1;
-                }
-            }.execute(ruleHelper);
-        } catch (EnforcerRuleException e) {
-            fail("No EnforcerRuleException should have been thrown");
-        }
-    }
-
-    @Test
-    public void testRulePassesByMaxUpdatesNotExceededDependencyIncludes()
-            throws ExpressionEvaluationException, ComponentLookupException {
-        EnforcerRuleHelper ruleHelper = mockRuleHelper(
-                new MavenProject() {
-                    {
-                        setDependencies(asList(
-                                dependencyWith("group", "artifactA", "1.0.0"),
-                                dependencyWith("group", "artifactB", "1.0.0")));
-                    }
-                },
-                mockAetherRepositorySystem(new HashMap<String, String[]>() {
-                    {
-                        put("artifactA", new String[] {"1.0.0", "2.0.0"});
-                        put("artifactB", new String[] {"1.0.0"});
-                    }
-                }));
-
-        try {
-            new MaxDependencyUpdates() {
-                {
-                    dependencyIncludes = singletonList("group:artifactB");
-                }
-            }.execute(ruleHelper);
-        } catch (EnforcerRuleException e) {
-            fail("No EnforcerRuleException should have been thrown");
-        }
-    }
-
-    @Test
-    public void testRulePassesByMaxUpdatesNotExceededDependencyExcludes()
-            throws ExpressionEvaluationException, ComponentLookupException {
-        EnforcerRuleHelper ruleHelper = mockRuleHelper(
-                new MavenProject() {
-                    {
-                        setDependencies(asList(
-                                dependencyWith("group", "artifactA", "1.0.0"),
-                                dependencyWith("group", "artifactB", "1.0.0")));
-                    }
-                },
-                mockAetherRepositorySystem(new HashMap<String, String[]>() {
-                    {
-                        put("artifactA", new String[] {"1.0.0", "2.0.0"});
-                        put("artifactB", new String[] {"1.0.0"});
-                    }
-                }));
-
-        try {
-            new MaxDependencyUpdates() {
-                {
-                    dependencyExcludes = singletonList("group:artifactA");
-                }
-            }.execute(ruleHelper);
-        } catch (EnforcerRuleException e) {
-            fail("No EnforcerRuleException should have been thrown");
-        }
-    }
-
-    @Test
-    public void testRulePassesByMaxUpdatesNotExceededDependencyIncludesExcludes()
-            throws ExpressionEvaluationException, ComponentLookupException {
-        EnforcerRuleHelper ruleHelper = mockRuleHelper(
-                new MavenProject() {
-                    {
-                        setDependencies(asList(
-                                dependencyWith("group", "artifactA", "1.0.0"),
-                                dependencyWith("group", "artifactB", "1.0.0")));
-                    }
-                },
-                mockAetherRepositorySystem(new HashMap<String, String[]>() {
-                    {
-                        put("artifactA", new String[] {"1.0.0", "2.0.0"});
-                        put("artifactB", new String[] {"1.0.0"});
-                    }
-                }));
-
-        try {
-            new MaxDependencyUpdates() {
-                {
-                    dependencyIncludes = singletonList("group:*");
-                    dependencyExcludes = singletonList("group:artifactA");
-                }
-            }.execute(ruleHelper);
-        } catch (EnforcerRuleException e) {
-            fail("No EnforcerRuleException should have been thrown");
-        }
-    }
-
-    @Test
-    public void testIgnoreSubIncrementalUpdates() throws ExpressionEvaluationException, ComponentLookupException {
-        EnforcerRuleHelper ruleHelper = mockRuleHelper(
-                new MavenProject() {
-                    {
-                        setDependencies(singletonList(dependencyWith("group", "artifactA", "1.0.0")));
-                    }
-                },
-                mockAetherRepositorySystem(singletonMap("artifactA", new String[] {"1.0.0", "1.0.0-1"})));
-
-        try {
-            new MaxDependencyUpdates() {
-                {
-                    ignoreSubIncrementalUpdates = true;
-                }
-            }.execute(ruleHelper);
-        } catch (EnforcerRuleException e) {
-            fail("No EnforcerRuleException should have been thrown");
-        }
-    }
-
-    @Test
-    public void testIgnoreIncrementalUpdates() throws ExpressionEvaluationException, ComponentLookupException {
-        EnforcerRuleHelper ruleHelper = mockRuleHelper(
-                new MavenProject() {
-                    {
-                        setDependencies(singletonList(dependencyWith("group", "artifactA", "1.0.0")));
-                    }
-                },
-                mockAetherRepositorySystem(singletonMap("artifactA", new String[] {"1.0.0", "1.0.0-1", "1.0.1"})));
-
-        try {
-            new MaxDependencyUpdates() {
-                {
-                    ignoreIncrementalUpdates = true;
-                }
-            }.execute(ruleHelper);
-        } catch (EnforcerRuleException e) {
-            fail("No EnforcerRuleException should have been thrown");
-        }
-    }
-
-    @Test
-    public void testIgnoreMinorUpdates() throws ExpressionEvaluationException, ComponentLookupException {
-        EnforcerRuleHelper ruleHelper = mockRuleHelper(
-                new MavenProject() {
-                    {
-                        setDependencies(asList(dependencyWith("group", "artifactA", "1.0.0")));
-                    }
-                },
-                mockAetherRepositorySystem(
-                        singletonMap("artifactA", new String[] {"1.0.0", "1.0.0-1", "1.0.1", "1.1.0"})));
-
-        try {
-            new MaxDependencyUpdates() {
-                {
-                    ignoreMinorUpdates = true;
-                }
-            }.execute(ruleHelper);
-        } catch (EnforcerRuleException e) {
-            fail("No EnforcerRuleException should have been thrown");
-        }
-    }
-}
diff --git a/versions-enforcer/src/test/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdatesTest.java b/versions-enforcer/src/test/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdatesTest.java
new file mode 100644
index 00000000..d7219d2c
--- /dev/null
+++ b/versions-enforcer/src/test/java/org/codehaus/mojo/versions/enforcer/MaxDependencyUpdatesTest.java
@@ -0,0 +1,258 @@
+package org.codehaus.mojo.versions.enforcer;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.HashMap;
+
+import org.apache.maven.enforcer.rule.api.EnforcerLogger;
+import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.repository.RepositorySystem;
+import org.codehaus.mojo.versions.utils.DependencyBuilder;
+import org.codehaus.mojo.versions.utils.MockUtils;
+import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
+import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class MaxDependencyUpdatesTest {
+
+    @Mock
+    private EnforcerLogger enforcerLogger;
+
+    @Mock
+    private MavenProject project;
+
+    @Mock
+    private MavenSession mavenSession;
+
+    @Mock
+    private RepositorySystem repositorySystem;
+
+    @Mock
+    private org.eclipse.aether.RepositorySystem aetherRepositorySystem;
+
+    @InjectMocks
+    private MaxDependencyUpdates maxDependencyUpdates;
+
+    @Before
+    public void setup() {
+        maxDependencyUpdates.setLog(enforcerLogger);
+        MockUtils.prepareRepositorySystemMock(repositorySystem);
+    }
+
+    @Test
+    public void testRuleFailsByMaxUpdatesExceeded() throws ExpressionEvaluationException, ComponentLookupException {
+        when(project.getDependencies())
+                .thenReturn(asList(
+                        DependencyBuilder.newBuilder()
+                                .withGroupId("group")
+                                .withArtifactId("artifactA")
+                                .withVersion("1.0.0")
+                                .build(),
+                        DependencyBuilder.newBuilder()
+                                .withGroupId("group")
+                                .withArtifactId("artifactB")
+                                .withVersion("1.0.0")
+                                .build()));
+
+        MockUtils.prepareAetherRepositorySystemMockForVersionRange(
+                aetherRepositorySystem, new HashMap<String, String[]>() {
+                    {
+                        put("artifactA", new String[] {"1.0.0", "2.0.0"});
+                        put("artifactB", new String[] {"1.0.0", "2.0.0"});
+                    }
+                });
+
+        try {
+            maxDependencyUpdates.maxUpdates = 1;
+            maxDependencyUpdates.execute();
+
+            fail("EnforcerRuleException should have been thrown");
+        } catch (EnforcerRuleException e) {
+            assertThat(e.getMessage(), containsString("More than 1 upgradable artifacts detected"));
+        }
+    }
+
+    @Test
+    public void testRulePassesByMaxUpdatesNotExceeded() throws Exception {
+
+        when(project.getDependencies())
+                .thenReturn(singletonList(DependencyBuilder.newBuilder()
+                        .withGroupId("group")
+                        .withArtifactId("artifactA")
+                        .withVersion("1.0.0")
+                        .build()));
+
+        MockUtils.prepareAetherRepositorySystemMockForVersionRange(
+                aetherRepositorySystem, singletonMap("artifactA", new String[] {"1.0.0", "2.0.0"}));
+
+        maxDependencyUpdates.maxUpdates = 1;
+        maxDependencyUpdates.execute();
+    }
+
+    @Test
+    public void testRulePassesByMaxUpdatesNotExceededDependencyIncludes() throws Exception {
+
+        when(project.getDependencies())
+                .thenReturn(asList(
+                        DependencyBuilder.newBuilder()
+                                .withGroupId("group")
+                                .withArtifactId("artifactA")
+                                .withVersion("1.0.0")
+                                .build(),
+                        DependencyBuilder.newBuilder()
+                                .withGroupId("group")
+                                .withArtifactId("artifactB")
+                                .withVersion("1.0.0")
+                                .build()));
+
+        MockUtils.prepareAetherRepositorySystemMockForVersionRange(
+                aetherRepositorySystem, new HashMap<String, String[]>() {
+                    {
+                        put("artifactA", new String[] {"1.0.0", "2.0.0"});
+                        put("artifactB", new String[] {"1.0.0"});
+                    }
+                });
+
+        maxDependencyUpdates.dependencyIncludes = singletonList("group:artifactB");
+        maxDependencyUpdates.execute();
+    }
+
+    @Test
+    public void testRulePassesByMaxUpdatesNotExceededDependencyExcludes() throws Exception {
+
+        when(project.getDependencies())
+                .thenReturn(asList(
+                        DependencyBuilder.newBuilder()
+                                .withGroupId("group")
+                                .withArtifactId("artifactA")
+                                .withVersion("1.0.0")
+                                .build(),
+                        DependencyBuilder.newBuilder()
+                                .withGroupId("group")
+                                .withArtifactId("artifactB")
+                                .withVersion("1.0.0")
+                                .build()));
+
+        MockUtils.prepareAetherRepositorySystemMockForVersionRange(
+                aetherRepositorySystem, new HashMap<String, String[]>() {
+                    {
+                        put("artifactA", new String[] {"1.0.0", "2.0.0"});
+                        put("artifactB", new String[] {"1.0.0"});
+                    }
+                });
+
+        maxDependencyUpdates.dependencyExcludes = singletonList("group:artifactA");
+        maxDependencyUpdates.execute();
+    }
+
+    @Test
+    public void testRulePassesByMaxUpdatesNotExceededDependencyIncludesExcludes() throws Exception {
+
+        when(project.getDependencies())
+                .thenReturn(asList(
+                        DependencyBuilder.newBuilder()
+                                .withGroupId("group")
+                                .withArtifactId("artifactA")
+                                .withVersion("1.0.0")
+                                .build(),
+                        DependencyBuilder.newBuilder()
+                                .withGroupId("group")
+                                .withArtifactId("artifactB")
+                                .withVersion("1.0.0")
+                                .build()));
+
+        MockUtils.prepareAetherRepositorySystemMockForVersionRange(
+                aetherRepositorySystem, new HashMap<String, String[]>() {
+                    {
+                        put("artifactA", new String[] {"1.0.0", "2.0.0"});
+                        put("artifactB", new String[] {"1.0.0"});
+                    }
+                });
+
+        maxDependencyUpdates.dependencyIncludes = singletonList("group:*");
+        maxDependencyUpdates.dependencyExcludes = singletonList("group:artifactA");
+        maxDependencyUpdates.execute();
+    }
+
+    @Test
+    public void testIgnoreSubIncrementalUpdates() throws Exception {
+
+        when(project.getDependencies())
+                .thenReturn(singletonList(DependencyBuilder.newBuilder()
+                        .withGroupId("group")
+                        .withArtifactId("artifactA")
+                        .withVersion("1.0.0")
+                        .build()));
+
+        MockUtils.prepareAetherRepositorySystemMockForVersionRange(
+                aetherRepositorySystem, singletonMap("artifactA", new String[] {"1.0.0", "1.0.0-1"}));
+
+        maxDependencyUpdates.ignoreSubIncrementalUpdates = true;
+        maxDependencyUpdates.execute();
+    }
+
+    @Test
+    public void testIgnoreIncrementalUpdates() throws Exception {
+        when(project.getDependencies())
+                .thenReturn(singletonList(DependencyBuilder.newBuilder()
+                        .withGroupId("group")
+                        .withArtifactId("artifactA")
+                        .withVersion("1.0.0")
+                        .build()));
+
+        MockUtils.prepareAetherRepositorySystemMockForVersionRange(
+                aetherRepositorySystem, singletonMap("artifactA", new String[] {"1.0.0", "1.0.0-1", "1.0.1"}));
+
+        maxDependencyUpdates.ignoreIncrementalUpdates = true;
+        maxDependencyUpdates.execute();
+    }
+
+    @Test
+    public void testIgnoreMinorUpdates() throws Exception {
+
+        when(project.getDependencies())
+                .thenReturn(singletonList(DependencyBuilder.newBuilder()
+                        .withGroupId("group")
+                        .withArtifactId("artifactA")
+                        .withVersion("1.0.0")
+                        .build()));
+
+        MockUtils.prepareAetherRepositorySystemMockForVersionRange(
+                aetherRepositorySystem, singletonMap("artifactA", new String[] {"1.0.0", "1.0.0-1", "1.0.1", "1.1.0"}));
+
+        maxDependencyUpdates.ignoreMinorUpdates = true;
+        maxDependencyUpdates.execute();
+    }
+}
diff --git a/versions-test/src/main/java/org/codehaus/mojo/versions/utils/MockUtils.java b/versions-test/src/main/java/org/codehaus/mojo/versions/utils/MockUtils.java
index e08b9322..aea5a1d4 100644
--- a/versions-test/src/main/java/org/codehaus/mojo/versions/utils/MockUtils.java
+++ b/versions-test/src/main/java/org/codehaus/mojo/versions/utils/MockUtils.java
@@ -74,20 +74,9 @@ public class MockUtils {
      */
     public static org.eclipse.aether.RepositorySystem mockAetherRepositorySystem(Map<String, String[]> versionMap) {
         org.eclipse.aether.RepositorySystem repositorySystem = mock(org.eclipse.aether.RepositorySystem.class);
+        prepareAetherRepositorySystemMockForVersionRange(repositorySystem, versionMap);
+
         try {
-            when(repositorySystem.resolveVersionRange(any(), any(VersionRangeRequest.class)))
-                    .then(invocation -> {
-                        VersionRangeRequest request = invocation.getArgument(1);
-                        return versionMap.entrySet().stream()
-                                .filter(e ->
-                                        e.getKey().equals(request.getArtifact().getArtifactId()))
-                                .findAny()
-                                .map(e -> Arrays.stream(e.getValue())
-                                        .map(VersionStub::new)
-                                        .collect(() -> new ArrayList<Version>(), ArrayList::add, ArrayList::addAll))
-                                .map(versions -> new VersionRangeResult(request).setVersions(versions))
-                                .orElse(null); // should tell us if we haven't populated all cases in the test
-                    });
             when(repositorySystem.resolveArtifact(any(RepositorySystemSession.class), any(ArtifactRequest.class)))
                     .then(invocation -> {
                         ArtifactRequest request = invocation.getArgument(1);
@@ -101,13 +90,34 @@ public class MockUtils {
                         copiedArtifact.setFile(mock(File.class));
                         return new ArtifactResult(request).setArtifact(copiedArtifact);
                     });
-        } catch (VersionRangeResolutionException | ArtifactResolutionException e) {
+        } catch (ArtifactResolutionException e) {
             throw new RuntimeException(e);
         }
 
         return repositorySystem;
     }
 
+    public static void prepareAetherRepositorySystemMockForVersionRange(
+            org.eclipse.aether.RepositorySystem repositorySystem, Map<String, String[]> versionMap) {
+        try {
+            when(repositorySystem.resolveVersionRange(any(), any(VersionRangeRequest.class)))
+                    .then(invocation -> {
+                        VersionRangeRequest request = invocation.getArgument(1);
+                        return versionMap.entrySet().stream()
+                                .filter(e ->
+                                        e.getKey().equals(request.getArtifact().getArtifactId()))
+                                .findAny()
+                                .map(e -> Arrays.stream(e.getValue())
+                                        .map(VersionStub::new)
+                                        .collect(() -> new ArrayList<Version>(), ArrayList::add, ArrayList::addAll))
+                                .map(versions -> new VersionRangeResult(request).setVersions(versions))
+                                .orElse(null); // should tell us if we haven't populated all cases in the test
+                    });
+        } catch (VersionRangeResolutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     public static I18N mockI18N() {
         I18N i18n = mock(I18N.class);
         when(i18n.getString(anyString(), any(), anyString())).thenAnswer(invocation -> invocation.getArgument(2));
@@ -128,6 +138,11 @@ public class MockUtils {
 
     public static RepositorySystem mockRepositorySystem() {
         RepositorySystem repositorySystem = mock(RepositorySystem.class);
+        prepareRepositorySystemMock(repositorySystem);
+        return repositorySystem;
+    }
+
+    public static void prepareRepositorySystemMock(RepositorySystem repositorySystem) {
         when(repositorySystem.createDependencyArtifact(any(Dependency.class))).thenAnswer(invocation -> {
             Dependency dependency = invocation.getArgument(0);
             return new DefaultArtifact(
@@ -139,7 +154,6 @@ public class MockUtils {
                     dependency.getClassifier(),
                     new DefaultArtifactHandlerStub("default"));
         });
-        return repositorySystem;
     }
 
     /**