/* Copyright 2012 GanttProject Team This file is part of GanttProject, an opensource project management tool. GanttProject is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GanttProject 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 for more details. You should have received a copy of the GNU General Public License along with GanttProject. If not, see <http://www.gnu.org/licenses/>. */ package net.sourceforge.ganttproject.task.algorithm; import net.sourceforge.ganttproject.task.Task; import net.sourceforge.ganttproject.task.dependency.TaskDependency; import net.sourceforge.ganttproject.test.task.TaskTestCase; import com.google.common.base.Suppliers; /** * Tests dependency graph behavior * * @author dbarashev */ public class DependencyGraphTest extends TaskTestCase { public void testSimpleChain() throws Exception { Task[] tasks = new Task[] {createTask(), createTask(), createTask()}; TaskDependency[] deps = new TaskDependency[] {createDependency(tasks[2], tasks[1]), createDependency(tasks[1], tasks[0])}; DependencyGraph graph = createGraph(tasks, deps); assertEquals(2, graph.getNode(tasks[2]).getLevel()); assertEquals(1, graph.getNode(tasks[1]).getLevel()); assertEquals(0, graph.getNode(tasks[0]).getLevel()); } public void testIncomingFork() throws Exception { Task[] tasks = new Task[] {createTask(), createTask(), createTask(), createTask()}; TaskDependency[] deps = new TaskDependency[] { createDependency(tasks[1], tasks[0]), createDependency(tasks[3], tasks[2]), createDependency(tasks[3], tasks[1])}; DependencyGraph graph = createGraph(tasks, deps); assertEquals(2, graph.getNode(tasks[3]).getLevel()); assertEquals(1, graph.getNode(tasks[1]).getLevel()); assertEquals(0, graph.getNode(tasks[0]).getLevel()); assertEquals(0, graph.getNode(tasks[2]).getLevel()); } public void testRhombus() throws Exception { Task[] tasks = new Task[] {createTask(), createTask(), createTask(), createTask(), createTask()}; TaskDependency[] deps = new TaskDependency[] { createDependency(tasks[4], tasks[3]), createDependency(tasks[4], tasks[2]), createDependency(tasks[2], tasks[1]), createDependency(tasks[1], tasks[0]), createDependency(tasks[3], tasks[0]) }; DependencyGraph graph = createGraph(tasks, deps); assertEquals(3, graph.getNode(tasks[4]).getLevel()); assertEquals(2, graph.getNode(tasks[2]).getLevel()); assertEquals(1, graph.getNode(tasks[3]).getLevel()); assertEquals(1, graph.getNode(tasks[1]).getLevel()); assertEquals(0, graph.getNode(tasks[0]).getLevel()); } public void testRemoveDependencyWithPropagation() throws Exception { Task[] tasks = new Task[] {createTask(), createTask(), createTask()}; TaskDependency[] deps = new TaskDependency[] {createDependency(tasks[2], tasks[1]), createDependency(tasks[1], tasks[0])}; DependencyGraph graph = createGraph(tasks, deps); assertEquals(2, graph.getNode(tasks[2]).getLevel()); graph.removeDependency(deps[1]); assertEquals(0, graph.getNode(tasks[1]).getLevel()); assertEquals(1, graph.getNode(tasks[2]).getLevel()); } public void testRemoveDependencyWithNoPropagation() throws Exception { Task[] tasks = new Task[] {createTask(), createTask(), createTask(), createTask()}; TaskDependency[] deps = new TaskDependency[] {createDependency(tasks[3], tasks[2]), createDependency(tasks[2], tasks[1]), createDependency(tasks[2], tasks[0])}; DependencyGraph graph = createGraph(tasks, deps); assertEquals(2, graph.getNode(tasks[3]).getLevel()); graph.removeDependency(deps[2]); assertEquals(2, graph.getNode(tasks[3]).getLevel()); } public void testRemoveInheritedDependencies() throws Exception { Task[] tasks = new Task[] {createTask(), createTask(), createTask()}; DependencyGraph graph = createGraph(tasks, null); move(tasks[2], tasks[1], graph); TaskDependency dep = createDependency(tasks[1], tasks[0]); graph.addDependency(dep); assertEquals(1, graph.getNode(tasks[2]).getLevel()); graph.removeDependency(dep); assertEquals(0, graph.getNode(tasks[2]).getLevel()); } public static void move(Task what, Task where, DependencyGraph graph) { where.getManager().getTaskHierarchy().move(what, where); graph.move(what, where); } public void testRemoveNode() throws Exception { Task[] tasks = new Task[] {createTask(), createTask(), createTask()}; TaskDependency[] deps = new TaskDependency[] {createDependency(tasks[2], tasks[1]), createDependency(tasks[1], tasks[0])}; DependencyGraph graph = createGraph(tasks, deps); assertEquals(2, graph.getNode(tasks[2]).getLevel()); graph.removeTask(tasks[1]); assertEquals(0, graph.getNode(tasks[2]).getLevel()); } public void testSimpleSubtask() { Task[] tasks = new Task[] {createTask(), createTask()}; DependencyGraph graph = createGraph(tasks, null); getTaskManager().getTaskHierarchy().move(tasks[1], tasks[0]); graph.move(tasks[1], tasks[0]); assertEquals(1, graph.getNode(tasks[0]).getLevel()); } public void testChainOfSubtasks() throws Exception { Task[] tasks = new Task[] {createTask(), createTask(), createTask()}; getTaskManager().getTaskHierarchy().move(tasks[1], tasks[0]); getTaskManager().getTaskHierarchy().move(tasks[2], tasks[0]); TaskDependency[] deps = new TaskDependency[] {createDependency(tasks[2], tasks[1])}; DependencyGraph graph = createGraph(tasks, deps); assertEquals(0, graph.getNode(tasks[0]).getLevel()); graph.move(tasks[1], tasks[0]); assertEquals(1, graph.getNode(tasks[0]).getLevel()); graph.move(tasks[2], tasks[0]); assertEquals(2, graph.getNode(tasks[0]).getLevel()); } public void testInheritedDependencies() throws Exception { // initially there is task0->task1 dep Task[] tasks = new Task[] {createTask(), createTask(), createTask(), createTask(), createTask()}; TaskDependency[] initialDeps = new TaskDependency[] { createDependency(tasks[1], tasks[0])}; DependencyGraph graph = createGraph(tasks, initialDeps); assertEquals(1, graph.getNode(tasks[1]).getLevel()); // lets move task2 into task1. It gets an implicit inherited dependency task0->task2 getTaskManager().getTaskHierarchy().move(tasks[2], tasks[1]); graph.move(tasks[2], tasks[1]); assertEquals(1, graph.getNode(tasks[2]).getLevel()); assertEquals(2, graph.getNode(tasks[1]).getLevel()); // lets move task4 into task 3 getTaskManager().getTaskHierarchy().move(tasks[4], tasks[3]); graph.move(tasks[4], tasks[3]); assertEquals(0, graph.getNode(tasks[4]).getLevel()); assertEquals(1, graph.getNode(tasks[3]).getLevel()); // lets create a dependency task2->task3 // task4 should get an implicit inherited dependency task2->task4 graph.addDependency(createDependency(tasks[3], tasks[2])); assertEquals(2, graph.getNode(tasks[4]).getLevel()); assertEquals(3, graph.getNode(tasks[3]).getLevel()); } public void testDeepInheritedDependencies() throws Exception { Task[] tasks = new Task[] {createTask(), createTask(), createTask(), createTask()}; DependencyGraph graph = createGraph(tasks, null); getTaskManager().getTaskHierarchy().move(tasks[3], tasks[2]); getTaskManager().getTaskHierarchy().move(tasks[2], tasks[1]); graph.move(tasks[3], tasks[2]); graph.move(tasks[2], tasks[1]); assertEquals(2, graph.getNode(tasks[1]).getLevel()); graph.addDependency(createDependency(tasks[1], tasks[0])); assertEquals(3, graph.getNode(tasks[1]).getLevel()); assertEquals(1, graph.getNode(tasks[3]).getLevel()); assertEquals(2, graph.getNode(tasks[2]).getLevel()); Task task4 = createTask(); Task task5 = createTask(); getTaskManager().getTaskHierarchy().move(task5, task4); graph.addTask(task4); graph.addTask(task5); graph.move(task5, task4); assertEquals(0, graph.getNode(task5).getLevel()); getTaskManager().getTaskHierarchy().move(task4, tasks[1]); graph.move(task4, tasks[1]); assertEquals(1, graph.getNode(task5).getLevel()); assertEquals(graph.getNode(tasks[0]), graph.getNode(task5).getIncoming().get(0).getSrc()); } public void testRemoveSubtaskRemovesImplicitSubSuperTaskDependencies() { Task[] tasks = new Task[] {createTask(), createTask(), createTask()}; DependencyGraph graph = createGraph(tasks, null); getTaskManager().getTaskHierarchy().move(tasks[2], tasks[1]); getTaskManager().getTaskHierarchy().move(tasks[1], tasks[0]); graph.move(tasks[2], tasks[1]); graph.move(tasks[1], tasks[0]); assertEquals(2, graph.getNode(tasks[0]).getLevel()); assertEquals(1, graph.getNode(tasks[1]).getLevel()); assertEquals(0, graph.getNode(tasks[2]).getLevel()); getTaskManager().getTaskHierarchy().move(tasks[1], getTaskManager().getRootTask()); graph.move(tasks[1], null); assertEquals(0, graph.getNode(tasks[0]).getLevel()); assertEquals(1, graph.getNode(tasks[1]).getLevel()); assertEquals(0, graph.getNode(tasks[2]).getLevel()); } public void testRemoveSubtaskAndImplicitInheritedDependencies() throws Exception { Task[] tasks = new Task[] {createTask(), createTask(), createTask(), createTask()}; DependencyGraph graph = createGraph(tasks, null); move(tasks[2], tasks[1], graph); move(tasks[1], tasks[0], graph); graph.addDependency(createDependency(tasks[0], tasks[3])); graph.addDependency(createDependency(tasks[1], tasks[3])); assertEquals(3, graph.getNode(tasks[0]).getLevel()); getTaskManager().getTaskHierarchy().move(tasks[1], getTaskManager().getRootTask()); graph.move(tasks[1], null); assertEquals(1, graph.getNode(tasks[0]).getLevel()); assertEquals(2, graph.getNode(tasks[1]).getLevel()); assertEquals(1, graph.getNode(tasks[2]).getLevel()); assertEquals(0, graph.getNode(tasks[3]).getLevel()); } public void testTransactionRollback() throws Exception { Task[] tasks = new Task[] {createTask(), createTask()}; DependencyGraph graph = createGraph(tasks, null); // We start a transaction and create a dependency graph.startTransaction(); graph.addDependency(createDependency(tasks[0], tasks[1])); assertEquals(2, graph.checkLayerValidity()); assertEquals(1, graph.getNode(tasks[0]).getLevel()); assertEquals(1, graph.getNode(tasks[0]).getIncoming().size()); assertEquals(0, graph.getNode(tasks[1]).getLevel()); assertEquals(1, graph.getNode(tasks[1]).getOutgoing().size()); // Now we rollback transaction and expect that its changes are undone graph.rollbackTransaction(); assertEquals(1, graph.checkLayerValidity()); assertEquals(0, graph.getNode(tasks[0]).getLevel()); assertEquals(0, graph.getNode(tasks[1]).getLevel()); assertTrue(graph.getNode(tasks[0]).getIncoming().isEmpty()); assertTrue(graph.getNode(tasks[1]).getOutgoing().isEmpty()); } private DependencyGraph createGraph(Task[] tasks, TaskDependency[] deps) { DependencyGraph graph = new DependencyGraph(Suppliers.ofInstance(getTaskManager().getTaskHierarchy())); initGraph(graph, tasks, deps); return graph; } private static void initGraph(DependencyGraph graph, Task[] tasks, TaskDependency[] deps) { if (tasks != null) { for (Task t : tasks) { graph.addTask(t); } } if (deps != null) { for (TaskDependency d : deps) { graph.addDependency(d); } } } }