/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.core.clientapi.agent.metadata.test; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.testng.annotations.Test; import org.rhq.core.clientapi.agent.metadata.PluginDependencyGraph; import org.rhq.core.clientapi.agent.metadata.PluginDependencyGraph.PluginDependency; /** * Test PluginDependencyGraph. * * @author John Mazzitelli */ @Test public class PluginDependencyGraphTest { public void testOptionalPluginsDeployment() { PluginDependencyGraph graph; List<String> order; // let's assume we have a dependency graph like this: // plugin A depends on plugin B (required) // plugin B depends on plugin D and C (required) // plugin C depends on plugin E and F (optional) // plugin D depends on plugin E and F (optional) // plugin E does not depend on any other plugin // plugin F does not depend on any other plugin // plugin G depends on plugin F (required) // plugin Z does not depend on any other plugin // the deployment order should be: Z F G E D C B A graph = new PluginDependencyGraph(); addPlugin(graph, "A", "B"); addPlugin(graph, "B", "D", "C"); addPluginWithOptionalDeps(graph, "C", "E", "F"); addPluginWithOptionalDeps(graph, "D", "E", "F"); addPlugin(graph, "E"); addPlugin(graph, "F"); addPlugin(graph, "G", "F"); addPlugin(graph, "Z"); assert graph.isComplete(null); order = graph.getDeploymentOrder(); assert order.get(0).equals("Z") : order; assert order.get(1).equals("F") : order; assert order.get(2).equals("G") : order; assert order.get(3).equals("E") : order; assert order.get(4).equals("D") : order; assert order.get(5).equals("C") : order; assert order.get(6).equals("B") : order; assert order.get(7).equals("A") : order; List<String> dependents = graph.getOptionalDependents("E"); assert dependents.contains("C") : dependents; assert dependents.contains("D") : dependents; assert dependents.size() == 2 : dependents; dependents = graph.getOptionalDependents("F"); assert dependents.contains("C") : dependents; assert dependents.contains("D") : dependents; assert dependents.size() == 2 : dependents; Collection<String> dependents2 = graph.getAllDependents("F"); assert dependents2.contains("A") : dependents2; assert dependents2.contains("B") : dependents2; assert dependents2.contains("C") : dependents2; assert dependents2.contains("D") : dependents2; assert dependents2.contains("G") : dependents2; assert dependents2.size() == 5 : dependents2; // plugin A depends on plugin B (required) // plugin B depends on plugin D and C (required) // plugin C depends on plugin E and F (optional) Collection<String> dependencies3 = graph.getAllDependencies("F"); assert dependencies3.isEmpty() : dependencies3; dependencies3 = graph.getAllDependencies("A"); assert dependencies3.contains("B") : dependencies3; assert dependencies3.contains("D") : dependencies3; assert dependencies3.contains("C") : dependencies3; assert dependencies3.contains("E") : dependencies3; assert dependencies3.contains("F") : dependencies3; assert dependencies3.size() == 5 : dependencies3; dependencies3 = graph.getAllDependencies("B"); assert dependencies3.contains("D") : dependencies3; assert dependencies3.contains("C") : dependencies3; assert dependencies3.contains("E") : dependencies3; assert dependencies3.contains("F") : dependencies3; assert dependencies3.size() == 4 : dependencies3; dependencies3 = graph.getAllDependencies("C"); assert dependencies3.contains("E") : dependencies3; assert dependencies3.contains("F") : dependencies3; assert dependencies3.size() == 2 : dependencies3; // Use the same dependency graph, but do not deploy plugin F. // With F missing, G will fail because it required F graph = new PluginDependencyGraph(); addPlugin(graph, "A", "B"); addPlugin(graph, "B", "D", "C"); addPluginWithOptionalDeps(graph, "C", "E", "F"); addPluginWithOptionalDeps(graph, "D", "E", "F"); addPlugin(graph, "E"); //addPlugin(graph, "F"); PLUGIN F IS GOING TO BE MISSING FROM THIS GRAPH! addPlugin(graph, "G", "F"); addPlugin(graph, "Z"); assert !graph.isComplete(null) : "Plugin F was missing, so G should have failed"; // Use the same dependency graph, but do not deploy plugin F AND make G optionally depend on F. // With F missing, but all dependencies on it being optional, this graph should be complete. // The deployment order in this case should be: Z G E D C B A graph = new PluginDependencyGraph(); addPlugin(graph, "A", "B"); addPlugin(graph, "B", "D", "C"); addPluginWithOptionalDeps(graph, "C", "E", "F"); addPluginWithOptionalDeps(graph, "D", "E", "F"); addPlugin(graph, "E"); //addPlugin(graph, "F"); PLUGIN F IS GOING TO BE MISSING FROM THIS GRAPH! addPluginWithOptionalDeps(graph, "G", "F"); // G is optionally dependent on F addPlugin(graph, "Z"); assert graph.isComplete(null); order = graph.getDeploymentOrder(); assert order.get(0).equals("Z") : order; assert order.get(1).equals("G") : order; assert order.get(2).equals("E") : order; assert order.get(3).equals("D") : order; assert order.get(4).equals("C") : order; assert order.get(5).equals("B") : order; assert order.get(6).equals("A") : order; dependents = graph.getOptionalDependents("E"); assert dependents.contains("C") : dependents; assert dependents.contains("D") : dependents; assert dependents.size() == 2 : dependents; dependents = graph.getOptionalDependents("F"); assert dependents.contains("C") : dependents; assert dependents.contains("D") : dependents; assert dependents.contains("G") : dependents; assert dependents.size() == 3 : dependents; } public void testTypicalDeployment() { PluginDependencyGraph graph; List<String> order; graph = new PluginDependencyGraph(); addPlugin(graph, "apache"); addPlugin(graph, "Platforms"); addPlugin(graph, "JBossAS", "JMX", "Tomcat"); addPlugin(graph, "Tomcat", "JMX"); addPlugin(graph, "Hibernate", "JMX"); addPlugin(graph, "JMX"); addPlugin(graph, "JONAgent"); assert graph.isComplete(null); order = graph.getDeploymentOrder(); assert order.indexOf("JMX") < order.indexOf("JBossAS") : order; assert order.indexOf("JMX") < order.indexOf("Tomcat") : order; assert order.indexOf("JMX") < order.indexOf("Hibernate") : order; assert order.indexOf("Tomcat") < order.indexOf("JBossAS") : order; assert graph.getPlugins().size() == 7 : graph; assert order.size() == 7 : order; } public void testCustomPluginDependsOnAgentGraphWithOthers() { PluginDependencyGraph graph; List<String> order; graph = new PluginDependencyGraph(); addPlugin(graph, "apache"); addPlugin(graph, "Platforms"); addPlugin(graph, "CustomPlugin", "JONAgent"); addPlugin(graph, "JMX"); addPlugin(graph, "JONAgent"); assert graph.isComplete(null); order = graph.getDeploymentOrder(); assert order.indexOf("JONAgent") < order.indexOf("CustomPlugin") : order; assert graph.getPlugins().size() == 5 : graph; assert order.size() == 5 : order; } public void testAnotherDependencyGraph() { PluginDependencyGraph graph; List<String> order; graph = new PluginDependencyGraph(); addPlugin(graph, "CustomPlugin", "JONAgent"); addPlugin(graph, "JMX"); addPlugin(graph, "JONAgent"); assert graph.isComplete(null); order = graph.getDeploymentOrder(); assert order.indexOf("JONAgent") < order.indexOf("CustomPlugin") : order; assert order.size() == 3 : order; assert graph.getPlugins().size() == 3 : graph; } public void testSimpleGraph() { PluginDependencyGraph graph; List<String> order; // a graph with no plugins that have any dependencies graph = new PluginDependencyGraph(); addPlugin(graph, "A"); addPlugin(graph, "B"); addPlugin(graph, "C"); assert graph.isComplete(null); order = graph.getDeploymentOrder(); assert order.contains("A") : order; assert order.contains("B") : order; assert order.contains("C") : order; // a graph with no plugins that have any dependencies is ordered by plugin name graph = new PluginDependencyGraph(); addPlugin(graph, "C"); addPlugin(graph, "A"); addPlugin(graph, "B"); assert graph.isComplete(null); order = graph.getDeploymentOrder(); assert order.contains("A") : order; assert order.contains("B") : order; assert order.contains("C") : order; } public void testDependencyGraph() { PluginDependencyGraph graph; List<String> order; // let's assume we have a dependency graph like this: // plugin A depends on plugin B // plugin B depends on plugin C // plugin C does not depend on any other plugin // the deployment order should be: C B A graph = new PluginDependencyGraph(); addPlugin(graph, "A", "B"); addPlugin(graph, "B", "C"); addPlugin(graph, "C"); assert graph.isComplete(null); order = graph.getDeploymentOrder(); assert order.get(0).equals("C") : order; assert order.get(1).equals("B") : order; assert order.get(2).equals("A") : order; // add them in a different order and see the dependency order doesn't change graph = new PluginDependencyGraph(); addPlugin(graph, "C"); addPlugin(graph, "B", "C"); addPlugin(graph, "A", "B"); assert graph.isComplete(null); order = graph.getDeploymentOrder(); assert order.get(0).equals("C") : order; assert order.get(1).equals("B") : order; assert order.get(2).equals("A") : order; // add them in a different order and see the dependency order doesn't change graph = new PluginDependencyGraph(); addPlugin(graph, "B", "C"); addPlugin(graph, "C"); addPlugin(graph, "A", "B"); assert graph.isComplete(null); order = graph.getDeploymentOrder(); assert order.get(0).equals("C") : order; assert order.get(1).equals("B") : order; assert order.get(2).equals("A") : order; Collection<String> dependents = graph.getAllDependents("unknownPlugin"); assert dependents.isEmpty() : dependents; dependents = graph.getAllDependents("A"); assert dependents.isEmpty() : dependents; dependents = graph.getAllDependents("B"); assert dependents.contains("A") : dependents; assert dependents.size() == 1 : dependents; dependents = graph.getAllDependents("C"); assert dependents.contains("A") : dependents; assert dependents.contains("B") : dependents; assert dependents.size() == 2 : dependents; Collection<String> dependencies = graph.getAllDependencies("unknownPlugin"); assert dependencies.isEmpty() : dependencies; dependencies = graph.getAllDependencies("C"); assert dependencies.isEmpty() : dependencies; dependencies = graph.getAllDependencies("B"); assert dependencies.contains("C") : dependencies; assert dependencies.size() == 1 : dependencies; dependencies = graph.getAllDependencies("A"); assert dependencies.contains("C") : dependencies; assert dependencies.contains("B") : dependencies; assert dependencies.size() == 2 : dependencies; } public void testComplexDependencyGraph() { PluginDependencyGraph graph; List<String> order; // let's assume we have a dependency graph like this: // plugin A depends on plugin B // plugin B depends on plugin D and C // plugin C depends on plugin E and F // plugin D depends on plugin E and F // plugin E does not depend on any other plugin // plugin F does not depend on any other plugin // plugin G depends on plugin F // plugin Z does not depend on any other plugin // the deployment order should be: Z F G E D C B A graph = new PluginDependencyGraph(); addPlugin(graph, "A", "B"); addPlugin(graph, "B", "D", "C"); addPlugin(graph, "C", "E", "F"); addPlugin(graph, "D", "E", "F"); addPlugin(graph, "E"); addPlugin(graph, "F"); addPlugin(graph, "G", "F"); addPlugin(graph, "Z"); assert graph.isComplete(null); order = graph.getDeploymentOrder(); assert order.get(0).equals("Z") : order; assert order.get(1).equals("F") : order; assert order.get(2).equals("G") : order; assert order.get(3).equals("E") : order; assert order.get(4).equals("D") : order; assert order.get(5).equals("C") : order; assert order.get(6).equals("B") : order; assert order.get(7).equals("A") : order; // add them in a different order and see the dependency order doesn't change graph = new PluginDependencyGraph(); graph = new PluginDependencyGraph(); addPlugin(graph, "E"); addPlugin(graph, "D", "E", "F"); addPlugin(graph, "B", "C", "D"); addPlugin(graph, "A", "B"); addPlugin(graph, "G", "F"); addPlugin(graph, "Z"); addPlugin(graph, "F"); addPlugin(graph, "C", "E", "F"); assert graph.isComplete(null); order = graph.getDeploymentOrder(); assert order.get(0).equals("Z") : order; assert order.get(1).equals("F") : order; assert order.get(2).equals("G") : order; assert order.get(3).equals("E") : order; assert order.get(4).equals("D") : order; assert order.get(5).equals("C") : order; assert order.get(6).equals("B") : order; assert order.get(7).equals("A") : order; } public void testBadGraph() { PluginDependencyGraph graph = new PluginDependencyGraph(); StringBuilder error = new StringBuilder(); addPlugin(graph, "A", "B"); assert graph.getPlugins().size() == 1; assert graph.getPlugins().contains("A"); assert !graph.isComplete(error); assert error.indexOf("[B]") > -1; try { graph.getDeploymentOrder(); assert false : "The deployment isn't possible yet - missing plugin B"; } catch (IllegalArgumentException expected) { } addPlugin(graph, "B", "C"); assert graph.getPlugins().size() == 2; assert graph.getPlugins().contains("A"); assert graph.getPlugins().contains("B"); error.setLength(0); assert !graph.isComplete(error); assert error.indexOf("[C]") > -1; try { graph.getDeploymentOrder(); assert false : "The deployment isn't possible yet - missing plugin C"; } catch (IllegalArgumentException expected) { } addPlugin(graph, "C"); // this completes the dependency graph assert graph.getPlugins().size() == 3; assert graph.getPlugins().contains("A"); assert graph.getPlugins().contains("B"); assert graph.getPlugins().contains("C"); error.setLength(0); assert graph.isComplete(error); assert error.length() == 0; List<String> order = graph.getDeploymentOrder(); assert order.get(0).equals("C") : order; assert order.get(1).equals("B") : order; assert order.get(2).equals("A") : order; } public void testReduceGraph() { PluginDependencyGraph graph = new PluginDependencyGraph(); StringBuilder error = new StringBuilder(); // plugin 1 is standalone // plugin 2 depends on plugin 1 (which exists) // plugin 3 depends on plugins 1 and 2 (they both exist) // plugin A depends on plugins 1 and B (they both exist, but B's dep is missing) // plugin B depends on plugins C (which is missing) // plugin Z depends on several non-existent but optional plugins addPlugin(graph, "1"); addPlugin(graph, "2", "1"); addPlugin(graph, "3", "1", "2"); addPlugin(graph, "A", "1", "B"); addPlugin(graph, "B", "C"); addPluginWithOptionalDeps(graph, "Z", "X", "Y"); // sanity check - make sure our graph isn't complete - we are missing C assert graph.getPlugins().size() == 6; assert graph.getPlugins().contains("1"); assert graph.getPlugins().contains("2"); assert graph.getPlugins().contains("3"); assert graph.getPlugins().contains("A"); assert graph.getPlugins().contains("B"); assert graph.getPlugins().contains("Z"); assert !graph.isComplete(error); assert error.indexOf("[C]") > -1; // reduce the graph - only 1,2,3,Z should be left since all of their required dependencies are satified graph = graph.reduceGraph(); assert graph.getPlugins().size() == 4 : graph; assert graph.getPlugins().contains("1") : graph; assert graph.getPlugins().contains("2") : graph; assert graph.getPlugins().contains("3") : graph; assert graph.getPlugins().contains("Z") : graph; error.setLength(0); assert graph.isComplete(error); assert error.length() == 0; List<String> order = graph.getDeploymentOrder(); assert order.get(0).equals("Z") : order; assert order.get(1).equals("1") : order; assert order.get(2).equals("2") : order; assert order.get(3).equals("3") : order; } public void testCatchCircularDependency() { PluginDependencyGraph graph; // try to add the following to the graph, which should fail due to the circular dependency // Plugin A depends on Plugin B // Plugin B depends on Plugin A graph = new PluginDependencyGraph(); addPlugin(graph, "A", "B"); addPlugin(graph, "B", "A"); assert graph.getPlugins().size() == 2; assertCircularDependency(graph); // try to add the following to the graph, which should fail due to the circular dependency // Plugin A depends on Plugin B // Plugin B depends on Plugin C // Plugin C depends on Plugin A graph = new PluginDependencyGraph(); addPlugin(graph, "A", "B"); addPlugin(graph, "B", "C"); addPlugin(graph, "C", "A"); assert graph.getPlugins().size() == 3; assertCircularDependency(graph); // try to add the following to the graph, which should fail due to the circular dependency // Plugin A depends on Plugin B // Plugin B depends on Plugin C // Plugin C depends on Plugin D // Plugin D depends on Plugin B graph = new PluginDependencyGraph(); addPlugin(graph, "A", "B"); addPlugin(graph, "B", "C"); addPlugin(graph, "C", "D"); addPlugin(graph, "D", "B"); assert graph.getPlugins().size() == 4; assertCircularDependency(graph); // try to add the following to the graph, which should fail due to the circular dependency // Plugin A depends on Plugin B, C // Plugin B depends on Plugin C, D, E // Plugin C depends on nothing // Plugin D depends on Plugin F, G // Plugin E depends on Plugin F // Plugin F depends on Plugin H // Plugin G depends on Plugin A // circular! D->G->A->B->D // Plugin H depends on nothing graph = new PluginDependencyGraph(); addPlugin(graph, "A", "B", "C"); addPlugin(graph, "B", "C", "D", "E"); addPlugin(graph, "C"); addPlugin(graph, "D", "F", "G"); addPlugin(graph, "E", "F"); addPlugin(graph, "F", "H"); addPlugin(graph, "G"); // let's first see this work addPlugin(graph, "H"); assert graph.getPlugins().size() == 8; assert graph.isComplete(null); addPlugin(graph, "G", "A"); // now blow it up assertCircularDependency(graph); } private void assertCircularDependency(PluginDependencyGraph graph) { try { graph.getDeploymentOrder(); assert false : "The deployment isn't possible yet - there is a circular dependency that should have been caught"; } catch (IllegalStateException expected) { } try { graph.isComplete(null); assert false : "The deployment isn't possible yet - there is a circular dependency that should have been caught"; } catch (IllegalStateException expected) { } } private void addPlugin(PluginDependencyGraph graph, String pluginName, String... dependencyNames) { List<PluginDependency> dependencies = new ArrayList<PluginDependency>(); for (String name : dependencyNames) { dependencies.add(new PluginDependency(name, false, true)); } graph.addPlugin(pluginName, dependencies); } private void addPluginWithOptionalDeps(PluginDependencyGraph graph, String pluginName, String... dependencyNames) { List<PluginDependency> dependencies = new ArrayList<PluginDependency>(); for (String name : dependencyNames) { dependencies.add(new PluginDependency(name, false, false)); } graph.addPlugin(pluginName, dependencies); } }