/** * This file Copyright (c) 2003-2012 Magnolia International * Ltd. (http://www.magnolia-cms.com). All rights reserved. * * * This file is dual-licensed under both the Magnolia * Network Agreement and the GNU General Public License. * You may elect to use one or the other of these licenses. * * This file is distributed in the hope that it will be * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT. * Redistribution, except as permitted by whichever of the GPL * or MNA you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or * modify this file under the terms of the GNU General * Public License, Version 3, as published by the Free Software * Foundation. You should have received a copy of the GNU * General Public License, Version 3 along with this program; * if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * 2. For the Magnolia Network Agreement (MNA), this file * and the accompanying materials are made available under the * terms of the MNA which accompanies this distribution, and * is available at http://www.magnolia-cms.com/mna.html * * Any modifications to this file must keep this entire header * intact. * */ package info.magnolia.module.model.reader; import static org.junit.Assert.*; import info.magnolia.module.model.DependencyDefinition; import info.magnolia.module.model.ModuleDefinition; import info.magnolia.module.model.Version; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Before; import org.junit.Test; /** * * @author gjoseph * @version $Revision: $ ($Author: $) */ public class DependencyCheckerImplTest { private DependencyChecker depChecker; @Before public void setUp() throws Exception { depChecker = new DependencyCheckerImpl(); } @Test public void testSimpleDependenciesAreResolvedAndChecked() throws Exception { final Map modules = buildModulesMapWithDependencyOn("3.0"); depChecker.checkDependencies(modules); // assert(no dependency failures) } @Test public void testDependenciesCanUseLowerBoundInfiniteRanges() throws Exception { final Map modules = buildModulesMapWithDependencyOn("*/4.0"); depChecker.checkDependencies(modules); // assert(no dependency failures) } @Test public void testDependenciesCanUseUpperBoundInfiniteRanges() throws Exception { final Map modules = buildModulesMapWithDependencyOn("1.0/*"); depChecker.checkDependencies(modules); // assert(no dependency failures) } @Test public void testDependenciesCanUseFiniteRanges() throws Exception { final Map modules = buildModulesMapWithDependencyOn("1.0/4.0"); depChecker.checkDependencies(modules); // assert(no dependency failures) } @Test public void testDependenciesShouldBeInvalidIfOutsideOfUpperBound() { final Map modules = buildModulesMapWithDependencyOn("1.0/2.0"); try { depChecker.checkDependencies(modules); fail("should have failed"); } catch (ModuleDependencyException e) { assertEquals("Module module2 (version 1.2.0) is dependent on module1 version 1.0/2.0, but module1 (version 3.0.0) is currently installed.", e.getMessage()); } } @Test public void testDependenciesShouldBeInvalidIfOutsideOfLowerBound() { final Map modules = buildModulesMapWithDependencyOn("4.0/5.0"); try { depChecker.checkDependencies(modules); fail("should have failed"); } catch (ModuleDependencyException e) { assertEquals("Module module2 (version 1.2.0) is dependent on module1 version 4.0/5.0, but module1 (version 3.0.0) is currently installed.", e.getMessage()); } } @Test public void testDependenciesShouldBeInvalidIfOutsideOfUpperBoundWithInfiniteLowerBound() { final Map modules = buildModulesMapWithDependencyOn("*/2.0"); try { depChecker.checkDependencies(modules); fail("should have failed"); } catch (ModuleDependencyException e) { assertEquals("Module module2 (version 1.2.0) is dependent on module1 version */2.0, but module1 (version 3.0.0) is currently installed.", e.getMessage()); } } @Test public void testDependenciesShouldBeInvalidIfOutsideOfLowerBoundWithInfiniteUpperBound() { final Map modules = buildModulesMapWithDependencyOn("4.0/*"); try { depChecker.checkDependencies(modules); fail("should have failed"); } catch (ModuleDependencyException e) { assertEquals("Module module2 (version 1.2.0) is dependent on module1 version 4.0/*, but module1 (version 3.0.0) is currently installed.", e.getMessage()); } } @Test public void testShouldFailWhenDependencyNotFound() { final Map modules = buildModulesMapWithDependencyOn("Q.W"); modules.remove("module1"); try { depChecker.checkDependencies(modules); fail("should have failed"); } catch (ModuleDependencyException e) { assertEquals("Module module2 (version 1.2.0) is dependent on module1 version Q.W, which was not found.", e.getMessage()); } } @Test public void testOptionalDependenciesAreIndeedOptional() throws ModuleDependencyException { final ModuleDefinition modDefB = new ModuleDefinition("mod-b", Version.parseVersion("1"), "fake.Module", null); final ModuleDefinition modDefC = new ModuleDefinition("mod-c", Version.parseVersion("1"), "fake.Module", null); final DependencyDefinition depOnA = new DependencyDefinition(); depOnA.setName("mod-a"); depOnA.setVersion("1"); depOnA.setOptional(true); final DependencyDefinition depOnB = new DependencyDefinition(); depOnB.setName("mod-b"); depOnB.setVersion("1"); modDefC.addDependency(depOnA); modDefC.addDependency(depOnB); // mod-a is not registered in this case final Map<String, ModuleDefinition> map = new HashMap<String, ModuleDefinition>(); map.put(modDefB.getName(), modDefB); map.put(modDefC.getName(), modDefC); depChecker.checkDependencies(map); final List<ModuleDefinition> sorted = depChecker.sortByDependencyLevel(map); assertEquals(2, sorted.size()); assertEquals(modDefB, sorted.get(0)); assertEquals(modDefC, sorted.get(1)); } @Test public void testOptionalDependenciesStillHaveToMatchVersionRanges() throws ModuleDependencyException { final ModuleDefinition modDefB = new ModuleDefinition("mod-b", Version.parseVersion("1.5"), "fake.Module", null); final ModuleDefinition modDefC = new ModuleDefinition("mod-c", Version.parseVersion("1"), "fake.Module", null); final DependencyDefinition depOnB = new DependencyDefinition(); depOnB.setName("mod-b"); depOnB.setVersion("1.0/1.2"); depOnB.setOptional(true); modDefC.addDependency(depOnB); final Map<String, ModuleDefinition> map = new HashMap<String, ModuleDefinition>(); map.put(modDefB.getName(), modDefB); map.put(modDefC.getName(), modDefC); try { depChecker.checkDependencies(map); fail("should have failed"); } catch (ModuleDependencyException e) { assertEquals("Module mod-c (version 1.0.0) is dependent on mod-b version 1.0/1.2, but mod-b (version 1.5.0) is currently installed.", e.getMessage()); } } @Test public void testModulesShouldBeSortedAccordingToDependencies() { final Map modules = new HashMap(); final ModuleDefinition module1 = new ModuleDefinition("module1", Version.parseVersion("3.0"), null, null); final ModuleDefinition module2 = new ModuleDefinition("module2", Version.parseVersion("3.0"), null, null); module2.addDependency(new DependencyDefinition("module1", "3.0", false)); final ModuleDefinition module3 = new ModuleDefinition("module3", Version.parseVersion("3.0"), null, null); module3.addDependency(new DependencyDefinition("module1", "3.0", false)); module3.addDependency(new DependencyDefinition("module2", "3.0", false)); final ModuleDefinition module4 = new ModuleDefinition("module4", Version.parseVersion("3.0"), null, null); module4.addDependency(new DependencyDefinition("module1", "3.0", false)); module4.addDependency(new DependencyDefinition("module3", "3.0", false)); final ModuleDefinition module5 = new ModuleDefinition("module5", Version.parseVersion("3.0"), null, null); module5.addDependency(new DependencyDefinition("module2", "3.0", false)); modules.put("module5", module5); modules.put("module4", module4); modules.put("module3", module3); modules.put("module2", module2); modules.put("module1", module1); final List list = depChecker.sortByDependencyLevel(modules); assertEquals(5, list.size()); assertEquals(module1, list.get(0)); assertEquals(module2, list.get(1)); assertEquals(module3, list.get(2)); assertEquals(module5, list.get(3)); assertEquals(module4, list.get(4)); } @Test public void testNonOptionalDependencyWithUnspecifiedVersionShouldBeTakenIntoAccount() throws Exception { final Map modules = new HashMap(); final ModuleDefinition module1 = new ModuleDefinition("module1", Version.parseVersion("3.0"), null, null); final ModuleDefinition module2 = new ModuleDefinition("module2", Version.parseVersion("3.0"), null, null); module1.addDependency(new DependencyDefinition("module2", null, false)); modules.put("module1", module1); modules.put("module2", module2); depChecker.checkDependencies(modules); final List<ModuleDefinition> sorted = depChecker.sortByDependencyLevel(modules); assertEquals(2, sorted.size()); assertEquals(module2, sorted.get(0)); assertEquals(module1, sorted.get(1)); } @Test public void testOptionalDependencyWithUnspecifiedVersionShouldBeTakenIntoAccount() throws Exception { final Map modules = new HashMap(); final ModuleDefinition module1 = new ModuleDefinition("module1", Version.parseVersion("3.0"), null, null); final ModuleDefinition module2 = new ModuleDefinition("module2", Version.parseVersion("3.0"), null, null); module1.addDependency(new DependencyDefinition("module2", null, false)); modules.put("module1", module1); modules.put("module2", module2); depChecker.checkDependencies(modules); final List<ModuleDefinition> sorted = depChecker.sortByDependencyLevel(modules); assertEquals(2, sorted.size()); assertEquals(module2, sorted.get(0)); assertEquals(module1, sorted.get(1)); } @Test public void testBlowupExplicitelyInCaseOfSelfDependency() { final ModuleDefinition modDefA = new ModuleDefinition("mod-a", Version.parseVersion("1"), "fake.Module", null); modDefA.setDisplayName("Module-A"); final DependencyDefinition depOnSelf = new DependencyDefinition(); depOnSelf.setName("mod-a"); depOnSelf.setVersion("1"); modDefA.addDependency(depOnSelf); final Map<String, ModuleDefinition> map = new HashMap<String, ModuleDefinition>(); map.put(modDefA.getName(), modDefA); try { depChecker.checkDependencies(map); fail("should have failed"); } catch (Throwable e) { assertTrue("Should have failed with a ModuleDependencyException instead of " + e.toString(), ModuleDependencyException.class.equals(e.getClass())); assertEquals("Cyclic dependency between Module-A (version 1.0.0) and Module-A (version 1.0.0)", e.getMessage()); } } @Test public void testCyclicDependenciesBlowupWithAClearExceptionMessage() { final ModuleDefinition modDefA = new ModuleDefinition("mod-a", Version.parseVersion("1"), "fake.Module", null); modDefA.setDisplayName("Module-A"); final ModuleDefinition modDefB = new ModuleDefinition("mod-b", Version.parseVersion("1"), "fake.Module", null); modDefB.setDisplayName("Module-B"); final DependencyDefinition depOnA = new DependencyDefinition(); depOnA.setName("mod-a"); depOnA.setVersion("1"); final DependencyDefinition depOnB = new DependencyDefinition(); depOnB.setName("mod-b"); depOnB.setVersion("1"); modDefA.addDependency(depOnB); modDefB.addDependency(depOnA); final Map<String, ModuleDefinition> map = new HashMap<String, ModuleDefinition>(); map.put(modDefA.getName(), modDefA); map.put(modDefB.getName(), modDefB); try { depChecker.checkDependencies(map); fail("should have failed"); } catch (Throwable e) { assertTrue("Should have failed with a ModuleDependencyException instead of " + e.toString(), ModuleDependencyException.class.equals(e.getClass())); assertEquals("Cyclic dependency between Module-A (version 1.0.0) and Module-B (version 1.0.0)", e.getMessage()); } } @Test public void testCoreIsAlwaysSortedFirst() { final Map modules = new HashMap(); final ModuleDefinition core = new ModuleDefinition("core", Version.parseVersion("1.2.3"), null, null); final ModuleDefinition module1 = new ModuleDefinition("a_module1", Version.parseVersion("3.0"), null, null); final ModuleDefinition module2 = new ModuleDefinition("a_module2", Version.parseVersion("3.0"), null, null); final ModuleDefinition module3 = new ModuleDefinition("a_module3", Version.parseVersion("3.0"), null, null); module2.addDependency(new DependencyDefinition("a_module1", "3.0", false)); module3.addDependency(new DependencyDefinition("core", "3.0", false)); modules.put("a_module3", module3); modules.put("a_module2", module2); modules.put("a_module1", module1); modules.put("core", core); final List list = depChecker.sortByDependencyLevel(modules); assertEquals(4, list.size()); assertEquals(core, list.get(0)); assertEquals(module1, list.get(1)); assertEquals(module2, list.get(2)); assertEquals(module3, list.get(3)); } @Test public void testWebappIsAlwaysSortedLast() { final Map modules = new HashMap(); final ModuleDefinition webapp = new ModuleDefinition("webapp", Version.parseVersion("1.2.3"), null, null); final ModuleDefinition core = new ModuleDefinition("core", Version.parseVersion("1.2.3"), null, null); final ModuleDefinition module1 = new ModuleDefinition("a_module1", Version.parseVersion("3.0"), null, null); final ModuleDefinition module2 = new ModuleDefinition("a_module2", Version.parseVersion("3.0"), null, null); final ModuleDefinition module3 = new ModuleDefinition("a_module3", Version.parseVersion("3.0"), null, null); module2.addDependency(new DependencyDefinition("a_module1", "3.0", false)); module3.addDependency(new DependencyDefinition("core", "3.0", false)); modules.put("a_module3", module3); modules.put("a_module2", module2); modules.put("a_module1", module1); modules.put("core", core); modules.put("webapp", webapp); final List list = depChecker.sortByDependencyLevel(modules); assertEquals(5, list.size()); assertEquals(core, list.get(0)); assertEquals(module1, list.get(1)); assertEquals(module2, list.get(2)); assertEquals(module3, list.get(3)); assertEquals(webapp, list.get(4)); } private Map buildModulesMapWithDependencyOn(String dependencyDefinitionVersion) { final Map modules = new HashMap(); modules.put("module1", new ModuleDefinition("module1", Version.parseVersion("3.0"), null, null)); final ModuleDefinition module2 = new ModuleDefinition("module2", Version.parseVersion("1.2"), null, null); module2.addDependency(new DependencyDefinition("module1", dependencyDefinitionVersion, false)); modules.put("module2", module2); return modules; } }