Author: khmarbaise Date: Fri Mar 14 21:03:29 2014 New Revision: 1577707 URL: http://svn.apache.org/r1577707 Log: [MENFORCER-186] - First working implementation of the RequireSameVersionsReactor rule.
Added: maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSameVersionsReactor.java maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/DefaultTestLogger.java maven/enforcer/trunk/enforcer-rules/src/site/apt/requireSameVersionsReactor.apt.vm maven/enforcer/trunk/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/RequireSameVersionsReactorTest.java Added: maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSameVersionsReactor.java URL: http://svn.apache.org/viewvc/maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSameVersionsReactor.java?rev=1577707&view=auto ============================================================================== --- maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSameVersionsReactor.java (added) +++ maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/RequireSameVersionsReactor.java Fri Mar 14 21:03:29 2014 @@ -0,0 +1,412 @@ +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.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.commons.lang.SystemUtils; +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.logging.Log; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; + +/** + * This rule will check if a multi module build will follow the best practices. + * + * @author Karl-Heinz Marbaise + * @since 1.3.2 + */ +public class RequireSameVersionsReactor + extends AbstractNonCacheableEnforcerRule +{ + private Log logger; + + public void execute( EnforcerRuleHelper helper ) + throws EnforcerRuleException + { + logger = helper.getLog(); + + MavenSession session; + try + { + session = (MavenSession) helper.evaluate( "${session}" ); + } + catch ( ExpressionEvaluationException eee ) + { + throw new EnforcerRuleException( "Unable to retrieve the MavenSession: ", eee ); + } + + @SuppressWarnings( "unchecked" ) + List<MavenProject> sortedProjects = session.getSortedProjects(); + if ( sortedProjects != null && !sortedProjects.isEmpty() ) + { + checkReactor( sortedProjects ); + checkParentsInReactor( sortedProjects ); + checkMissingParentsInReactor( sortedProjects ); + checkParentsPartOfTheReactor( sortedProjects ); + checkDependenciesWithinReactor( sortedProjects ); + } + + } + + private void checkParentsPartOfTheReactor( List<MavenProject> sortedProjects ) + throws EnforcerRuleException + { + List<MavenProject> parentsWhichAreNotPartOfTheReactor = + existParentsWhichAreNotPartOfTheReactor( sortedProjects ); + if ( !parentsWhichAreNotPartOfTheReactor.isEmpty() ) + { + StringBuilder sb = new StringBuilder().append( SystemUtils.LINE_SEPARATOR ); + for ( MavenProject mavenProject : parentsWhichAreNotPartOfTheReactor ) + { + sb.append( " module: " ); + sb.append( mavenProject.getId() ); + sb.append( SystemUtils.LINE_SEPARATOR ); + } + throw new EnforcerRuleException( "Module parents have been found which could not be found in the reactor." + + sb.toString() ); + } + } + + /** + * Convenience method to create a user readable message. + * + * @param sortedProjects The list of reactor projects. + * @throws EnforcerRuleException In case of a violation. + */ + private void checkMissingParentsInReactor( List<MavenProject> sortedProjects ) + throws EnforcerRuleException + { + List<MavenProject> modulesWithoutParentsInReactory = existModulesWithoutParentsInReactor( sortedProjects ); + if ( !modulesWithoutParentsInReactory.isEmpty() ) + { + StringBuilder sb = new StringBuilder().append( SystemUtils.LINE_SEPARATOR ); + for ( MavenProject mavenProject : modulesWithoutParentsInReactory ) + { + sb.append( " module: " ); + sb.append( mavenProject.getId() ); + sb.append( SystemUtils.LINE_SEPARATOR ); + } + throw new EnforcerRuleException( "Reactor contains modules without parents." + sb.toString() ); + } + } + + private void checkDependenciesWithinReactor( List<MavenProject> sortedProjects ) + throws EnforcerRuleException + { + //TODO: After we are sure having consistent version we can simply use the first one? + String reactorVersion = sortedProjects.get( 0 ).getVersion(); + + Map<MavenProject, List<Dependency>> areThereDependenciesWhichAreNotPartOfTheReactor = + areThereDependenciesWhichAreNotPartOfTheReactor( reactorVersion, sortedProjects ); + if ( !areThereDependenciesWhichAreNotPartOfTheReactor.isEmpty() ) + { + StringBuilder sb = new StringBuilder().append( SystemUtils.LINE_SEPARATOR ); + for ( Entry<MavenProject, List<Dependency>> item : areThereDependenciesWhichAreNotPartOfTheReactor.entrySet() ) + { + sb.append( " module: " ); + sb.append( item.getKey().getId() ); + sb.append( SystemUtils.LINE_SEPARATOR ); + for ( Dependency dependency : item.getValue() ) + { + String id = + dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getVersion(); + sb.append( " dependency: " ); + sb.append( id ); + sb.append( SystemUtils.LINE_SEPARATOR ); + } + } + throw new EnforcerRuleException( + "Reactor modules contains dependencies which do not reference the reactor." + + sb.toString() ); + } + } + + /** + * Convenience method to create a user readable message. + * + * @param sortedProjects The list of reactor projects. + * @throws EnforcerRuleException In case of a violation. + */ + private void checkParentsInReactor( List<MavenProject> sortedProjects ) + throws EnforcerRuleException + { + //TODO: After we are sure having consistent version we can simply use the first one? + String reactorVersion = sortedProjects.get( 0 ).getVersion(); + + List<MavenProject> areParentsFromTheReactor = areParentsFromTheReactor( reactorVersion, sortedProjects ); + if ( !areParentsFromTheReactor.isEmpty() ) + { + StringBuilder sb = new StringBuilder().append( SystemUtils.LINE_SEPARATOR ); + for ( MavenProject mavenProject : areParentsFromTheReactor ) + { + sb.append( " --> " ); + sb.append( mavenProject.getId() + " parent:" + mavenProject.getParent().getId() ); + sb.append( SystemUtils.LINE_SEPARATOR ); + } + throw new EnforcerRuleException( "Reactor modules have parents which contain a wrong version." + + sb.toString() ); + } + } + + /** + * Convenience method to create user readable message. + * + * @param sortedProjects The list of reactor projects. + * @throws EnforcerRuleException In case of a violation. + */ + private void checkReactor( List<MavenProject> sortedProjects ) + throws EnforcerRuleException + { + List<MavenProject> consistenceCheckResult = isReactorVersionConsistent( sortedProjects ); + if ( !consistenceCheckResult.isEmpty() ) + { + StringBuilder sb = new StringBuilder().append( SystemUtils.LINE_SEPARATOR ); + for ( MavenProject mavenProject : consistenceCheckResult ) + { + sb.append( " --> " ); + sb.append( mavenProject.getId() ); + sb.append( SystemUtils.LINE_SEPARATOR ); + } + throw new EnforcerRuleException( "The reactor contains different versions." + sb.toString() ); + } + } + + private List<MavenProject> areParentsFromTheReactor( String reactorVersion, List<MavenProject> sortedProjects ) + { + List<MavenProject> result = new ArrayList<MavenProject>(); + + for ( MavenProject mavenProject : sortedProjects ) + { + logger.debug( "Project: " + mavenProject.getId() ); + if ( hasParent( mavenProject ) ) + { + if ( !mavenProject.isExecutionRoot() ) + { + MavenProject parent = mavenProject.getParent(); + if ( !reactorVersion.equals( parent.getVersion() ) ) + { + logger.debug( "The project: " + mavenProject.getId() + + " has a parent which version does not match the other elements in reactor" ); + result.add( mavenProject ); + } + } + } + else + { + //This situation is currently ignored, cause it's handled by existModulesWithoutParentsInReactor() + } + } + + return result; + } + + private List<MavenProject> existParentsWhichAreNotPartOfTheReactor( List<MavenProject> sortedProjects ) + { + List<MavenProject> result = new ArrayList<MavenProject>(); + + for ( MavenProject mavenProject : sortedProjects ) + { + logger.debug( "Project: " + mavenProject.getId() ); + if ( hasParent( mavenProject ) ) + { + if ( !mavenProject.isExecutionRoot() ) + { + MavenProject parent = mavenProject.getParent(); + if ( !isProjectPartOfTheReactor( parent, sortedProjects ) ) + { + result.add( mavenProject ); + } + } + } + } + + return result; + } + + /** + * This will check of the groupId/artifactId can be found in any reactor project. The version will be ignored cause + * versions are checked before. + * + * @param project The project which should be checked if it is contained in the sortedProjects. + * @param sortedProjects The list of existing projects. + * @return true if the project has been found within the list false otherwise. + */ + private boolean isProjectPartOfTheReactor( MavenProject project, List<MavenProject> sortedProjects ) + { + return isGAPartOfTheReactor( project.getGroupId(), project.getArtifactId(), sortedProjects ); + } + + private boolean isDependencyPartOfTheReactor( Dependency dependency, List<MavenProject> sortedProjects ) + { + return isGAPartOfTheReactor( dependency.getGroupId(), dependency.getArtifactId(), sortedProjects ); + } + + private boolean isGAPartOfTheReactor( String groupId, String artifactId, List<MavenProject> sortedProjects ) + { + boolean result = false; + for ( MavenProject mavenProject : sortedProjects ) + { + String parentId = groupId + ":" + artifactId; + String projectId = mavenProject.getGroupId() + ":" + mavenProject.getArtifactId(); + if ( parentId.equals( projectId ) ) + { + result = true; + } + } + return result; + } + + /** + * Assume we have a module which is which is a child of a multi module build but this child does not have a parent. + * This method will exactly search for such cases. + * + * @param projectList The sorted list of the reactor modules. + * @return The resulting list will contain the modules in the reactor which do not have a parent. The list will + * never null. If the list is empty no violation have happened. + */ + private List<MavenProject> existModulesWithoutParentsInReactor( List<MavenProject> sortedProjects ) + { + List<MavenProject> result = new ArrayList<MavenProject>(); + + for ( MavenProject mavenProject : sortedProjects ) + { + logger.debug( "Project: " + mavenProject.getId() ); + if ( !hasParent( mavenProject ) ) + { + if ( mavenProject.isExecutionRoot() ) + { + logger.debug( "The root does not need having a parent." ); + } + else + { + logger.debug( "The module: " + mavenProject.getId() + " has no parent." ); + result.add( mavenProject ); + } + } + } + + return result; + } + + private void addDep( Map<MavenProject, List<Dependency>> result, MavenProject project, Dependency dependency ) + { + if ( result.containsKey( project ) ) + { + List<Dependency> list = result.get( project ); + if ( list == null ) + { + list = new ArrayList<Dependency>(); + } + list.add( dependency ); + result.put( project, list ); + } + else + { + List<Dependency> list = new ArrayList<Dependency>(); + list.add( dependency ); + result.put( project, list ); + } + } + + private Map<MavenProject, List<Dependency>> areThereDependenciesWhichAreNotPartOfTheReactor( String reactorVersion, + List<MavenProject> sortedProjects ) + { + Map<MavenProject, List<Dependency>> result = new HashMap<MavenProject, List<Dependency>>(); + for ( MavenProject mavenProject : sortedProjects ) + { + logger.debug( "Project: " + mavenProject.getId() ); + + @SuppressWarnings( "unchecked" ) + List<Dependency> dependencies = mavenProject.getDependencies(); + if ( hasDependencies( dependencies ) ) + { + for ( Dependency dependency : dependencies ) + { + if ( isDependencyPartOfTheReactor( dependency, sortedProjects ) ) + { + if ( !dependency.getVersion().equals( reactorVersion ) ) + { + addDep( result, mavenProject, dependency ); + } + } + } + } + } + + return result; + } + + /** + * This method will check the following situation within a multi-module build. + * + * <pre> + * <parent> + * <groupId>...</groupId> + * <artifactId>...</artifactId> + * <version>1.0-SNAPSHOT</version> + * </parent> + * + * <version>1.1-SNAPSHOT</version> + * </pre> + * + * @param projectList The sorted list of the reactor modules. + * @return The resulting list will contain the modules in the reactor which do the thing in the example above. The + * list will never null. If the list is empty no violation have happened. + */ + private List<MavenProject> isReactorVersionConsistent( List<MavenProject> projectList ) + { + List<MavenProject> result = new ArrayList<MavenProject>(); + + if ( projectList != null && !projectList.isEmpty() ) + { + //TODO: Check if this the right choice ? + String version = projectList.get( 0 ).getVersion(); + logger.debug( "First version:" + version ); + for ( MavenProject mavenProject : projectList ) + { + logger.debug( " -> checking " + mavenProject.getId() ); + if ( !version.equals( mavenProject.getVersion() ) ) + { + result.add( mavenProject ); + } + } + } + return result; + } + + private boolean hasDependencies( List<Dependency> dependencies ) + { + return dependencies != null && !dependencies.isEmpty(); + } + + private boolean hasParent( MavenProject mavenProject ) + { + return mavenProject.getParent() != null; + } + +} \ No newline at end of file Added: maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/DefaultTestLogger.java URL: http://svn.apache.org/viewvc/maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/DefaultTestLogger.java?rev=1577707&view=auto ============================================================================== --- maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/DefaultTestLogger.java (added) +++ maven/enforcer/trunk/enforcer-rules/src/main/java/org/apache/maven/plugins/enforcer/utils/DefaultTestLogger.java Fri Mar 14 21:03:29 2014 @@ -0,0 +1,104 @@ +package org.apache.maven.plugins.enforcer.utils; + +import org.apache.maven.plugin.logging.Log; + +public class DefaultTestLogger implements Log +{ + + public boolean isDebugEnabled() + { + // TODO Auto-generated method stub + return false; + } + + public void debug( CharSequence content ) + { + // TODO Auto-generated method stub + + } + + public void debug( CharSequence content, Throwable error ) + { + // TODO Auto-generated method stub + + } + + public void debug( Throwable error ) + { + // TODO Auto-generated method stub + + } + + public boolean isInfoEnabled() + { + // TODO Auto-generated method stub + return false; + } + + public void info( CharSequence content ) + { + // TODO Auto-generated method stub + + } + + public void info( CharSequence content, Throwable error ) + { + // TODO Auto-generated method stub + + } + + public void info( Throwable error ) + { + // TODO Auto-generated method stub + + } + + public boolean isWarnEnabled() + { + // TODO Auto-generated method stub + return false; + } + + public void warn( CharSequence content ) + { + // TODO Auto-generated method stub + + } + + public void warn( CharSequence content, Throwable error ) + { + // TODO Auto-generated method stub + + } + + public void warn( Throwable error ) + { + // TODO Auto-generated method stub + + } + + public boolean isErrorEnabled() + { + // TODO Auto-generated method stub + return false; + } + + public void error( CharSequence content ) + { + // TODO Auto-generated method stub + + } + + public void error( CharSequence content, Throwable error ) + { + // TODO Auto-generated method stub + + } + + public void error( Throwable error ) + { + // TODO Auto-generated method stub + + } + +} Added: maven/enforcer/trunk/enforcer-rules/src/site/apt/requireSameVersionsReactor.apt.vm URL: http://svn.apache.org/viewvc/maven/enforcer/trunk/enforcer-rules/src/site/apt/requireSameVersionsReactor.apt.vm?rev=1577707&view=auto ============================================================================== --- maven/enforcer/trunk/enforcer-rules/src/site/apt/requireSameVersionsReactor.apt.vm (added) +++ maven/enforcer/trunk/enforcer-rules/src/site/apt/requireSameVersionsReactor.apt.vm Fri Mar 14 21:03:29 2014 @@ -0,0 +1,130 @@ +~~ 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. + + ------ + Require Same Versions Reactor + ------ + Karl-Heinz Marbaise + ------ + March 2014 + ------ + +Require Same Versions Reactor + + This rule checks that the versions within the reactor are consistent furthermore + it will check that every module within the project contains a parent and that the + parent is part of the reactor build. Furthermore it will all given dependencies + if they are intermodule dependencies that they using the same versions. + + + The following parameters are supported by this rule: + + * message - an optional message to the user if the rule fails. + + [] + + + Sample Plugin Configuration: + ++---+ +<project> + [...] + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-enforcer-plugin</artifactId> + <version>${project.version}</version> + <executions> + <execution> + <id>enforce-no-snapshots</id> + <goals> + <goal>enforce</goal> + </goals> + <configuration> + <rules> + <requireSameVersionsReactor/> + </rules> + <fail>true</fail> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + [...] +</project> ++---+ + + There are different situations within a multi module build which can lead to problems + (for example not working with maven-release-plugin etc.). + This rule is intended to prevent such problems. + + Let us assume we have the following (simple) project structure for a multi module setup. + ++----- + root (pom.xml) + +--- module1 (pom.xml) + +--- module2 (pom.xml) ++----- + + The root <<pom.xml>> looks like this: + ++----- + <groupId>com.mycompany.project</groupId> + <artifactId>parent</artifactId> + <version>1.0-SNAPSHOT</version> + (..) ++----- + + The best practice in Maven is that all childs inherit the version from their parent + and don't define a new version which looks like this: + ++----- + <parent> + <groupId>...</groupId> + <artifactId>...</artifactId> + <version>1.0-SNAPSHOT</version> + </parent> + + <artifactId>module1</artifactId> + (..) ++----- + + But sometimes people mistaken things or violate the best-practice and + this look like this: + ++----- + <parent> + <groupId>...</groupId> + <artifactId>...</artifactId> + <version>1.0-SNAPSHOT</version> + </parent> + + <artifactId>module1</artifactId> + <version>1.1-SNAPSHOT</version> ++----- + + By using this rule you would get a message during the build if you try to build + and example like the above which result in the following output: + ++----- +[WARNING] Rule 0: org.apache.maven.plugins.enforcer.RequireSameVersionsReactor failed with message: +The reactor contains different versions. + --> org.apache.enforcer.example:appasm:pom:1.1.0-SNAPSHOT ++----- + Added: maven/enforcer/trunk/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/RequireSameVersionsReactorTest.java URL: http://svn.apache.org/viewvc/maven/enforcer/trunk/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/RequireSameVersionsReactorTest.java?rev=1577707&view=auto ============================================================================== --- maven/enforcer/trunk/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/RequireSameVersionsReactorTest.java (added) +++ maven/enforcer/trunk/enforcer-rules/src/test/java/org/apache/maven/plugins/enforcer/RequireSameVersionsReactorTest.java Fri Mar 14 21:03:29 2014 @@ -0,0 +1,341 @@ +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 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.logging.Log; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Check requireSameVersionsReactor rule. + * + * @author <a href="mailto:khmarba...@apache.org">Karl Heinz Marbaise</a> + */ +public class RequireSameVersionsReactorTest +{ + private MavenProject project; + + private MavenSession session; + + private EnforcerRuleHelper helper; + + private RequireSameVersionsReactor rule; + + @Before + public void before() + throws ExpressionEvaluationException + { + project = mock( MavenProject.class ); + session = mock( MavenSession.class ); + helper = mock( EnforcerRuleHelper.class ); + when( helper.evaluate( "${project}" ) ).thenReturn( project ); + when( helper.evaluate( "${session}" ) ).thenReturn( session ); + when( helper.getLog() ).thenReturn( mock( Log.class ) ); + + rule = new RequireSameVersionsReactor(); + } + + @Test + public void shouldNotFailWithNoProject() + throws EnforcerRuleException + { + when( session.getSortedProjects() ).thenReturn( Collections.<MavenProject> emptyList() ); + + rule.execute( helper ); + + //intentionally only assertTrue cause we don't expect an exception. + assertTrue( true ); + } + + @Test + public void shouldNotFailWithAValidProject() + throws EnforcerRuleException, ExpressionEvaluationException + { + MavenProject mp1 = createProjectParent(); + MavenProject mp2 = createProjectChild1( mp1 ); + MavenProject mp3 = createProjectChild2( mp1 ); + + assertTrue( mp2.getParent() == mp1 ); + assertTrue( mp3.getParent() == mp1 ); + + List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 ); + when( session.getSortedProjects() ).thenReturn( theList ); + + rule.execute( helper ); + + //intentionally only assertTrue cause we don't expect an exception. + assertTrue( true ); + } + + @Test( expected = EnforcerRuleException.class ) + public void shouldFailWithWrongVersionInOneChild() + throws EnforcerRuleException, ExpressionEvaluationException + { + MavenProject mp1 = createProjectParent(); + MavenProject mp2 = createProjectChild1( mp1 ); + MavenProject mp3 = createProjectChild2WithWrongVersion( mp1 ); + + List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 ); + when( session.getSortedProjects() ).thenReturn( theList ); + + rule.execute( helper ); + + //intentionally no assertTrue() cause we expect getting an exception. + } + + @Test( expected = EnforcerRuleException.class ) + public void shouldFailWithWrongParent() + throws EnforcerRuleException, ExpressionEvaluationException + { + MavenProject mp1 = createProjectParent(); + + MavenProject wrongParentVerison = mock( MavenProject.class ); + when( wrongParentVerison.getGroupId() ).thenReturn( "org.apache.enforcer" ); + when( wrongParentVerison.getArtifactId() ).thenReturn( "m1" ); + when( wrongParentVerison.getVersion() ).thenReturn( "1.1-SNAPSHOT" ); + when( wrongParentVerison.getId() ).thenReturn( "org.apache.enforcer:m1:jar:1.1-SNAPSHOT" ); + when( wrongParentVerison.getDependencies() ).thenReturn( Collections.emptyList() ); + + MavenProject mp2 = createProjectChild2( wrongParentVerison ); + MavenProject mp3 = createProjectChild2( mp1 ); + + List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 ); + when( session.getSortedProjects() ).thenReturn( theList ); + + rule.execute( helper ); + + //intentionally no assertTrue() cause we expect getting an exception. + } + + @Test + public void shouldNotFailWithACompanyParent() + throws EnforcerRuleException, ExpressionEvaluationException + { + MavenProject companyParent = createCompanyParent(); + MavenProject mp1 = createProjectParent( companyParent ); + + MavenProject mp2 = createProjectChild1( mp1 ); + MavenProject mp3 = createProjectChild2( mp1 ); + + List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 ); + when( session.getSortedProjects() ).thenReturn( theList ); + + rule.execute( helper ); + + //intentionally only assertTrue cause we don't expect an exception. + assertTrue( true ); + } + + @Test( expected = EnforcerRuleException.class ) + public void shouldFailWithMissingParentsInReactory() + throws EnforcerRuleException, ExpressionEvaluationException + { + MavenProject mp1 = createProjectParent(); + MavenProject mp2 = createProjectChild1( mp1 ); + MavenProject mp3 = createProjectChild2( null ); + + List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 ); + when( session.getSortedProjects() ).thenReturn( theList ); + + rule.execute( helper ); + + //intentionally only assertTrue cause we don't expect an exception. + assertTrue( true ); + } + + @Test( expected = EnforcerRuleException.class ) + public void shouldFailWithAParentWhichIsNotPartOfTheReactory() + throws EnforcerRuleException, ExpressionEvaluationException + { + MavenProject mp1 = createProjectParent(); + + MavenProject wrongParentVerison = mock( MavenProject.class ); + when( wrongParentVerison.getGroupId() ).thenReturn( "org.apache" ); + when( wrongParentVerison.getArtifactId() ).thenReturn( "m1" ); + when( wrongParentVerison.getVersion() ).thenReturn( "1.0-SNAPSHOT" ); + when( wrongParentVerison.getId() ).thenReturn( "org.apache.enforcer:m1:jar:1.0-SNAPSHOT" ); + when( wrongParentVerison.getDependencies() ).thenReturn( Collections.emptyList() ); + + MavenProject mp2 = createProjectChild2( wrongParentVerison ); + MavenProject mp3 = createProjectChild2( mp1 ); + + List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 ); + when( session.getSortedProjects() ).thenReturn( theList ); + + rule.execute( helper ); + + //intentionally no assertTrue() cause we expect getting an exception. + } + + @Test + public void shouldNotFailWithDependencyInReactory() + throws EnforcerRuleException, ExpressionEvaluationException + { + MavenProject mp1 = createProjectParent(); + MavenProject mp2 = createProjectChild1( mp1 ); + + Dependency goodDependency = createDependency( "org.junit", "junit", "2.0" ); + List<Dependency> depListMP2 = Arrays.asList( goodDependency ); + when( mp2.getDependencies() ).thenReturn( depListMP2 ); + + MavenProject mp3 = createProjectChild2( mp1 ); + Dependency dep1_MP3 = createDependency( "org.apache.commons", "commons-io", "1.0.4" ); + List<Dependency> depListMP3 = Arrays.asList( dep1_MP3 ); + when( mp3.getDependencies() ).thenReturn( depListMP3 ); + + List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 ); + when( session.getSortedProjects() ).thenReturn( theList ); + + rule.execute( helper ); + + //intentionally no assertTrue() cause we expect getting an exception. + assertTrue( true ); + } + + @Test( expected = EnforcerRuleException.class ) + public void shouldFailWithWrongDependencyInReactory() + throws EnforcerRuleException, ExpressionEvaluationException + { + MavenProject mp1 = createProjectParent(); + MavenProject mp2 = createProjectChild1( mp1 ); + + Dependency goodDependency = createDependency( "org.junit", "junit", "2.0" ); + + Dependency wrongDepFromReactory = createDependency( "org.apache.enforcer", "m2", "1.1-SNAPSHOT" ); + List<Dependency> depList = Arrays.asList( goodDependency, wrongDepFromReactory ); + when( mp2.getDependencies() ).thenReturn( depList ); + + MavenProject mp3 = createProjectChild2( mp1 ); + + List<MavenProject> theList = Arrays.asList( mp1, mp2, mp3 ); + when( session.getSortedProjects() ).thenReturn( theList ); + + rule.execute( helper ); + + //intentionally no assertTrue() cause we expect getting an exception. + } + + /** + * This small setup is equivalent to the following situation: + * + * <pre> + * <parent> + * <groupId>...</groupId> + * <artifactId>...</artifactId> + * <version>1.0-SNAPSHOT</version> + * </parent> + * + * <version>1.1-SNAPSHOT</version> + * </pre> + * + * @param parent + * @return Create MavenProject mock. + */ + private MavenProject createProjectChild2WithWrongVersion( MavenProject parent ) + { + MavenProject mp2 = mock( MavenProject.class ); + when( mp2.getParent() ).thenReturn( parent ); + when( mp2.getGroupId() ).thenReturn( "org.apache.enforcer" ); + when( mp2.getArtifactId() ).thenReturn( "m1" ); + when( mp2.getVersion() ).thenReturn( "1.1-SNAPSHOT" ); + when( mp2.getId() ).thenReturn( "org.apache.enforcer:m1:jar:1.1-SNAPSHOT" ); + when( mp2.getDependencies() ).thenReturn( Collections.emptyList() ); + return mp2; + } + + private MavenProject createProjectChild2( MavenProject parent ) + { + MavenProject mp3 = mock( MavenProject.class ); + when( mp3.getParent() ).thenReturn( parent ); + when( mp3.getGroupId() ).thenReturn( "org.apache.enforcer" ); + when( mp3.getArtifactId() ).thenReturn( "m2" ); + when( mp3.getVersion() ).thenReturn( "1.0-SNAPSHOT" ); + when( mp3.getId() ).thenReturn( "org.apache.enforcer:m2:jar:1.0-SNAPSHOT" ); + when( mp3.getDependencies() ).thenReturn( Collections.emptyList() ); + return mp3; + } + + private MavenProject createProjectChild1( MavenProject parent ) + { + MavenProject mp2 = mock( MavenProject.class ); + when( mp2.getParent() ).thenReturn( parent ); + when( mp2.getGroupId() ).thenReturn( "org.apache.enforcer" ); + when( mp2.getArtifactId() ).thenReturn( "m1" ); + when( mp2.getVersion() ).thenReturn( "1.0-SNAPSHOT" ); + when( mp2.getId() ).thenReturn( "org.apache.enforcer:m1:jar:1.0-SNAPSHOT" ); + when( mp2.getDependencies() ).thenReturn( Collections.emptyList() ); + return mp2; + } + + private MavenProject createCompanyParent() + { + MavenProject nonReactoryParent = mock( MavenProject.class ); + when( nonReactoryParent.getGroupId() ).thenReturn( "org.apache.enforcer.parent" ); + when( nonReactoryParent.getArtifactId() ).thenReturn( "parent" ); + when( nonReactoryParent.getVersion() ).thenReturn( "1.1" ); + when( nonReactoryParent.getId() ).thenReturn( "org.apache.enforcer.parent:parent:jar:1.1" ); + when( nonReactoryParent.getDependencies() ).thenReturn( Collections.emptyList() ); + return nonReactoryParent; + } + + private MavenProject createProjectParent( MavenProject nonReactorParent ) + { + MavenProject m = createProjectParent(); + when( m.isExecutionRoot() ).thenReturn( true ); + when( m.getParent() ).thenReturn( nonReactorParent ); + return m; + } + + private MavenProject createProjectParent() + { + MavenProject mp1 = mock( MavenProject.class ); + when( mp1.isExecutionRoot() ).thenReturn( true ); + when( mp1.getParent() ).thenReturn( null ); + when( mp1.getGroupId() ).thenReturn( "org.apache.enforcer" ); + when( mp1.getArtifactId() ).thenReturn( "parent" ); + when( mp1.getVersion() ).thenReturn( "1.0-SNAPSHOT" ); + when( mp1.getId() ).thenReturn( "org.apache.enforcer:parent:pom:1.0-SNAPSHOT" ); + when( mp1.getDependencies() ).thenReturn( Collections.emptyList() ); + return mp1; + } + + private Dependency createDependency( String groupId, String artifactId, String version ) + { + Dependency dep = mock( Dependency.class ); + when( dep.getGroupId() ).thenReturn( groupId ); + when( dep.getArtifactId() ).thenReturn( artifactId ); + when( dep.getVersion() ).thenReturn( version ); + return dep; + } +}