From d5533523986db4d1943b66420b8c5da835dac78a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20Mar=C3=A9chal?= Date: Mon, 11 Mar 2024 13:31:59 +0100 Subject: [PATCH 1/3] Add support for different reference policies --- .../model/RequirePropertyDiverges.java | 105 +++++++++++++++++- 1 file changed, 101 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/codehaus/mojo/extraenforcer/model/RequirePropertyDiverges.java b/src/main/java/org/codehaus/mojo/extraenforcer/model/RequirePropertyDiverges.java index 2634a3f..e8857d9 100644 --- a/src/main/java/org/codehaus/mojo/extraenforcer/model/RequirePropertyDiverges.java +++ b/src/main/java/org/codehaus/mojo/extraenforcer/model/RequirePropertyDiverges.java @@ -66,6 +66,8 @@ public class RequirePropertyDiverges extends AbstractEnforcerRule { */ private String regex = null; + private String reference = "DEFINING"; + private static final String RULE_NAME = StringUtils.lowercaseFirstLetter(RequirePropertyDiverges.class.getSimpleName()); @@ -89,13 +91,15 @@ public void execute() throws EnforcerRuleException { Object propValue = getPropertyValue(); checkPropValueNotBlank(propValue); + ParentReference parentReference = getParentReference(); + getLog().debug(() -> getRuleName() + ": checking property '" + property + "' for project " + project); - final MavenProject parent = findDefiningParent(project); + final MavenProject parent = findParent(project, parentReference); - // fail fast if the defining parent could not be found due to a bug in the rule + // fail fast if the reference parent could not be found due to a bug in the rule if (parent == null) { - throw new IllegalStateException("Failed to find parent POM which defines the current rule"); + throw new IllegalStateException("Failed to find reference POM which defines the current value"); } if (project.equals(parent)) { @@ -112,7 +116,7 @@ public void execute() throws EnforcerRuleException { } /** - * Checks the value of the project against the one given in the defining ancestor project. + * Checks the value of the project against the one given in the reference ancestor project. * * @param project * @param parent @@ -153,6 +157,26 @@ void checkAgainstRegex(Object propValue) throws EnforcerRuleException { } } + /** + * Finds the ancestor project which defines the reference value. + * + * @param project to inspect + * @param reference project to diverge from + * @return the defining ancestor project. + */ + final MavenProject findParent(final MavenProject project, ParentReference reference) { + switch (reference) { + case BASE: + return findBaseParent(project); + case DEFINING: + return findDefiningParent(project); + case PARENT: + return findDirectParent(project); + default: + throw new IllegalArgumentException("Unhandled ParentReference: " + reference.name()); + } + } + /** * Finds the ancestor project which defines the rule. * @@ -176,6 +200,32 @@ final MavenProject findDefiningParent(final MavenProject project) { return parent; } + /** + * Finds the direct ancestor project. + * + * @param project to inspect + * @return the top-most local project or current project if it has no parent. + * + */ + final MavenProject findDirectParent(MavenProject project) { + MavenProject parent = project.getParent(); + return parent == null ? project : parent; + } + + /** + * Finds the top-most local ancestor project. + * + * @param project to inspect + * @return the top-most local project. + */ + final MavenProject findBaseParent(final MavenProject project) { + MavenProject current = project; + while (current.getParentFile() != null) { + current = project.getParent(); + } + return current; + } + /** * Creates a {@link Xpp3Dom} which corresponds to the configuration of the invocation. * @@ -337,6 +387,31 @@ void checkPropValueNotBlank(Object propValue) throws EnforcerRuleException { } } + /** + * Extracted for easier testability. + * + * @return the ParentReference constant. + * @throws EnforcerRuleException if null or no ParentReference matches (case-insensitively) + */ + ParentReference getParentReference() throws EnforcerRuleException { + return getParentReference(reference); + } + + /** + * Extracted for easier testability. + * + * @param parentReferenceName name of the ParentReference. + * @return the ParentReference constant. + * @throws EnforcerRuleException if null or no ParentReference matches (case-insensitively) + */ + ParentReference getParentReference(String parentReferenceName) throws EnforcerRuleException { + try { + return ParentReference.valueOf(StringUtils.upperCase(StringUtils.strip(parentReferenceName))); + } catch (IllegalArgumentException iae) { + throw new EnforcerRuleError("Unknown parent reference value: " + parentReferenceName, iae); + } + } + /** * Either return the submitted errorMessage or replace it with the custom message set in the rule extended * by the property name. @@ -375,6 +450,13 @@ void setMessage(String message) { this.message = message; } + /** + * @param reference the reference to set + */ + void setReference(String reference) { + this.reference = reference; + } + /** * Creates the DOM of the invoking rule, but returns the children alphabetically sorted. */ @@ -416,4 +498,19 @@ private void addChildrenToRuleDom() { } } } + + enum ParentReference { + /** + * The top-most local project (i.e.: the highest parent whose POM does not come from a repository) + */ + BASE, + /** + * The parent where the current requirePropertyDiverges rule is defined + */ + DEFINING, + /** + * The immediate parent (same as ${project.parent}) + */ + PARENT + } } From a00e19fb706e7a203a39a81aef72ffe9f7a0a7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20Mar=C3=A9chal?= Date: Thu, 14 Mar 2024 07:58:05 +0100 Subject: [PATCH 2/3] Update site doc --- src/site/apt/requirePropertyDiverges.apt.vm | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/site/apt/requirePropertyDiverges.apt.vm b/src/site/apt/requirePropertyDiverges.apt.vm index b41e101..a49ebec 100644 --- a/src/site/apt/requirePropertyDiverges.apt.vm +++ b/src/site/apt/requirePropertyDiverges.apt.vm @@ -40,6 +40,14 @@ Require Property Diverges * <> - name of the property which should diverge. Must be given. + * <> - parent level to use as evaluation context for the reference value: + + * DEFINING - the rule-defining POM. Default historical behaviour. + + * PARENT - the direct parent of the current POM. The rule is skipped if the POM has no parent. + + * BASE - the top-most local POM of the project. The rule is skipped if the current POM is the top-most POM. + * <> - match the property value to a given regular expression. When not given, this rule checks that the property in the child does not equal the one from the defining ancestor. @@ -47,7 +55,7 @@ Require Property Diverges [] Note that certain properties (e.g. <<>> or <<>> - are automatically extended with the child's <<>>, so using + are automatically extended with the child's <<>> (see {{{https://maven.apache.org/ref/current/maven-model-builder/#inheritance-assembly}Inheritance assembly}}), so using a regex will help in this case, see sample below. Another caveat: properties defined in the <<>> section will be From 7a131f1a8f8f954043b48f80bc02e52698bfb539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre=20Mar=C3=A9chal?= Date: Thu, 14 Mar 2024 21:10:03 +0100 Subject: [PATCH 3/3] Add tests --- .../model/RequirePropertyDiverges.java | 2 +- .../model/RequirePropertyDivergesTest.java | 85 ++++++++++++++++++- 2 files changed, 85 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/codehaus/mojo/extraenforcer/model/RequirePropertyDiverges.java b/src/main/java/org/codehaus/mojo/extraenforcer/model/RequirePropertyDiverges.java index e8857d9..6d1c46d 100644 --- a/src/main/java/org/codehaus/mojo/extraenforcer/model/RequirePropertyDiverges.java +++ b/src/main/java/org/codehaus/mojo/extraenforcer/model/RequirePropertyDiverges.java @@ -401,7 +401,7 @@ ParentReference getParentReference() throws EnforcerRuleException { * Extracted for easier testability. * * @param parentReferenceName name of the ParentReference. - * @return the ParentReference constant. + * @return the ParentReference enum constant. * @throws EnforcerRuleException if null or no ParentReference matches (case-insensitively) */ ParentReference getParentReference(String parentReferenceName) throws EnforcerRuleException { diff --git a/src/test/java/org/codehaus/mojo/extraenforcer/model/RequirePropertyDivergesTest.java b/src/test/java/org/codehaus/mojo/extraenforcer/model/RequirePropertyDivergesTest.java index 31b02fe..7d06839 100644 --- a/src/test/java/org/codehaus/mojo/extraenforcer/model/RequirePropertyDivergesTest.java +++ b/src/test/java/org/codehaus/mojo/extraenforcer/model/RequirePropertyDivergesTest.java @@ -19,6 +19,8 @@ * under the License. */ +import java.io.File; + import org.apache.maven.enforcer.rule.api.EnforcerLogger; import org.apache.maven.enforcer.rule.api.EnforcerRuleException; import org.apache.maven.model.Build; @@ -222,6 +224,87 @@ public void testCheckAgainstParentValue() throws EnforcerRuleException, Expressi testCheckAgainstParentValue("company.parent-pom", "company.project1"); } + @Test(expected = NullPointerException.class) + public void testGetParentReferenceNull() throws EnforcerRuleException { + RequirePropertyDiverges instance = createMockRule(mock(MavenProject.class)); + instance.getParentReference(null); + } + + @Test(expected = EnforcerRuleException.class) + public void testGetParentReferenceEmpty() throws EnforcerRuleException { + RequirePropertyDiverges instance = createMockRule(mock(MavenProject.class)); + instance.getParentReference(""); + } + + @Test + public void testGetParentReferenceKnownValues() throws EnforcerRuleException { + RequirePropertyDiverges instance = createMockRule(mock(MavenProject.class)); + assertEquals(RequirePropertyDiverges.ParentReference.BASE, instance.getParentReference("BaSE")); + assertEquals(RequirePropertyDiverges.ParentReference.DEFINING, instance.getParentReference("Defining")); + assertEquals(RequirePropertyDiverges.ParentReference.PARENT, instance.getParentReference("PaRenT")); + } + + @Test(expected = EnforcerRuleException.class) + public void testGetParentReferenceUnknownValue() throws EnforcerRuleException { + RequirePropertyDiverges instance = createMockRule(mock(MavenProject.class)); + instance.getParentReference("BOGUS"); + } + + @Test + public void testGetParentReferenceDefault() throws EnforcerRuleException { + RequirePropertyDiverges instance = createMockRule(mock(MavenProject.class)); + assertEquals(RequirePropertyDiverges.ParentReference.DEFINING, instance.getParentReference()); + } + + @Test + public void testFindParentDefining() throws EnforcerRuleException { + RequirePropertyDiverges instance = createMockRule(mock(MavenProject.class)); + MavenProject root = createParentProject(); + MavenProject base = createMavenProject("company", "main-pom"); + MavenProject child = createMavenProject("company", "child"); + child.setParent(base); + base.setParent(root); + // Rule is defined in "root", expect root always + assertEquals(root, instance.findParent(root, RequirePropertyDiverges.ParentReference.DEFINING)); + assertEquals(root, instance.findParent(base, RequirePropertyDiverges.ParentReference.DEFINING)); + assertEquals(root, instance.findParent(child, RequirePropertyDiverges.ParentReference.DEFINING)); + } + + @Test + public void testFindParentParent() throws EnforcerRuleException { + RequirePropertyDiverges instance = createMockRule(mock(MavenProject.class)); + MavenProject root = createParentProject(); + MavenProject base = createMavenProject("company", "main-pom"); + MavenProject child = createMavenProject("company", "child"); + child.setParent(base); + base.setParent(root); + // "root" has no parent, expect root (self) + assertEquals(root, instance.findParent(root, RequirePropertyDiverges.ParentReference.PARENT)); + // base's parent is root, expect root + assertEquals(root, instance.findParent(base, RequirePropertyDiverges.ParentReference.PARENT)); + // child's parent is base, expect base + assertEquals(base, instance.findParent(child, RequirePropertyDiverges.ParentReference.PARENT)); + } + + @Test + public void testFindParentBase() throws EnforcerRuleException { + RequirePropertyDiverges instance = createMockRule(mock(MavenProject.class)); + MavenProject root = createParentProject(); + MavenProject base = createMavenProject("company", "main-pom"); + base.setParentFile(null); + MavenProject child = createMavenProject("company", "child"); + child.setParent(base); + child.setParentFile(mock(File.class)); + base.setParent(root); + + // "root" is not local, expect root (self) + assertEquals(root, instance.findParent(root, RequirePropertyDiverges.ParentReference.BASE)); + // base is the top-most local (has no parentFile), expect base (self) + assertEquals(base, instance.findParent(base, RequirePropertyDiverges.ParentReference.BASE)); + // child has a local parent, expect base + assertEquals(base, instance.findParent(child, RequirePropertyDiverges.ParentReference.BASE)); + } + void testCheckAgainstParentValue(final String parentGroupId, final String childGroupId) throws ExpressionEvaluationException, EnforcerRuleException { MavenProject project = createMavenProject(childGroupId, "child"); @@ -251,7 +334,7 @@ static MavenProject createMavenProject(final String groupId, final String artifa } void setUpHelper(final MavenProject project, final String propertyValue) throws RuntimeException { - // when(helper.evaluate("${project}")).thenReturn(project); + // when(helper.evaluate("${project}")).thenReturn(project); try { when(evaluator.evaluate("${checkedProperty}")).thenReturn(propertyValue); } catch (ExpressionEvaluationException e) {