/*
* Copyright (C) 2012 Jason Gedge <http://www.gedge.ca>
*
* This file is part of the OpGraph project.
*
* This program 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.
*
* 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 for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ca.gedge.opgraph.dag;
import static org.junit.Assert.*;
import static ca.gedge.CollectionsAssert.assertCollectionEqualsArray;
import java.util.HashMap;
import org.junit.Before;
import org.junit.Test;
import ca.gedge.opgraph.dag.CycleDetectedException;
import ca.gedge.opgraph.dag.DirectedAcyclicGraph;
import ca.gedge.opgraph.dag.SimpleDirectedEdge;
import ca.gedge.opgraph.dag.Vertex;
import ca.gedge.opgraph.dag.VertexNotFoundException;
/**
* Tests {@link DirectedAcyclicGraph}.
*/
public class TestDirectedAcyclicGraph {
/**
* Basic vertex class for testing.
*/
private static class SimpleVertex implements Vertex {
private String name;
public SimpleVertex(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
//
// Test data
//
private HashMap<String, SimpleVertex> vertexMap = new HashMap<String, SimpleVertex>();
private HashMap<String, SimpleDirectedEdge<SimpleVertex>> edgeMap = new HashMap<String, SimpleDirectedEdge<SimpleVertex>>();
@Before
public void setUp() {
char start = 'A';
char end = 'Z';
for(char s = start; s <= end; ++s)
vertexMap.put("" + s, new SimpleVertex("" + s));
for(char u = start; u <= end; ++u) {
for(char v = (char)(u + 1); v <= end; ++v) {
SimpleVertex uV = vertexMap.get("" + u);
SimpleVertex vV = vertexMap.get("" + v);
edgeMap.put(u + "" + v, new SimpleDirectedEdge<SimpleVertex>(uV, vV));
edgeMap.put(v + "" + u, new SimpleDirectedEdge<SimpleVertex>(vV, uV));
}
}
}
/**
* Tests the correctness of topological sorting in a DAG
*/
@Test
public void testTopologicalOrdering() {
DirectedAcyclicGraph<SimpleVertex, SimpleDirectedEdge<SimpleVertex>> dag = new DirectedAcyclicGraph<SimpleVertex, SimpleDirectedEdge<SimpleVertex>>();
dag.add(vertexMap.get("A"));
dag.add(vertexMap.get("B"));
dag.add(vertexMap.get("C"));
dag.add(vertexMap.get("D"));
try {
dag.add(edgeMap.get("DC"));
dag.add(edgeMap.get("CA"));
dag.add(edgeMap.get("AB"));
dag.add(edgeMap.get("DA"));
} catch(VertexNotFoundException exc) {
fail("Vertex not found, but should be: " + exc.getVertex());
} catch(CycleDetectedException exc) {
fail("Adding edge creates cycle, but this shouldn't happen");
}
assertCollectionEqualsArray(dag.getVertices(),
vertexMap.get("A"), vertexMap.get("B"), vertexMap.get("C"), vertexMap.get("D"));
}
/**
* Tests cycle detection in a DAG
*/
@Test(expected=CycleDetectedException.class)
public void testCycleException() throws CycleDetectedException {
DirectedAcyclicGraph<SimpleVertex, SimpleDirectedEdge<SimpleVertex>> dag = new DirectedAcyclicGraph<SimpleVertex, SimpleDirectedEdge<SimpleVertex>>();
dag.add(vertexMap.get("A"));
dag.add(vertexMap.get("B"));
dag.add(vertexMap.get("C"));
dag.add(vertexMap.get("D"));
try {
dag.add(edgeMap.get("DC"));
dag.add(edgeMap.get("CA"));
dag.add(edgeMap.get("AB"));
dag.add(edgeMap.get("DA"));
dag.add(edgeMap.get("BD")); // creates a cycle
} catch(VertexNotFoundException exc) {
fail("Vertex not found, but should be: " + exc.getVertex());
}
}
/**
* Tests incoming/outgoing edges in a DAG
*/
@SuppressWarnings("unchecked")
@Test
public void testIncomingOutgoingEdges() {
DirectedAcyclicGraph<SimpleVertex, SimpleDirectedEdge<SimpleVertex>> dag = new DirectedAcyclicGraph<SimpleVertex, SimpleDirectedEdge<SimpleVertex>>();
dag.add(vertexMap.get("A"));
dag.add(vertexMap.get("B"));
dag.add(vertexMap.get("C"));
dag.add(vertexMap.get("D"));
dag.add(vertexMap.get("E"));
dag.add(vertexMap.get("F"));
dag.add(vertexMap.get("G"));
try {
dag.add(edgeMap.get("DC"));
dag.add(edgeMap.get("CA"));
dag.add(edgeMap.get("AB"));
dag.add(edgeMap.get("AE"));
dag.add(edgeMap.get("DA"));
dag.add(edgeMap.get("EF"));
} catch(CycleDetectedException exc) {
fail("Adding edge creates cycle, but this shouldn't happen");
} catch(VertexNotFoundException exc) {
fail("Vertex not found, but should be: " + exc.getVertex());
}
assertCollectionEqualsArray(dag.getIncomingEdges(vertexMap.get("A")), edgeMap.get("CA"), edgeMap.get("DA"));
assertCollectionEqualsArray(dag.getOutgoingEdges(vertexMap.get("A")), edgeMap.get("AB"), edgeMap.get("AE"));
assertCollectionEqualsArray(dag.getIncomingEdges(vertexMap.get("B")), edgeMap.get("AB"));
assertCollectionEqualsArray(dag.getOutgoingEdges(vertexMap.get("B")));
assertCollectionEqualsArray(dag.getIncomingEdges(vertexMap.get("C")), edgeMap.get("DC"));
assertCollectionEqualsArray(dag.getOutgoingEdges(vertexMap.get("C")), edgeMap.get("CA"));
assertCollectionEqualsArray(dag.getIncomingEdges(vertexMap.get("D")));
assertCollectionEqualsArray(dag.getOutgoingEdges(vertexMap.get("D")), edgeMap.get("DA"), edgeMap.get("DC"));
assertCollectionEqualsArray(dag.getIncomingEdges(vertexMap.get("E")), edgeMap.get("AE"));
assertCollectionEqualsArray(dag.getOutgoingEdges(vertexMap.get("E")), edgeMap.get("EF"));
assertCollectionEqualsArray(dag.getIncomingEdges(vertexMap.get("F")), edgeMap.get("EF"));
assertCollectionEqualsArray(dag.getOutgoingEdges(vertexMap.get("F")));
assertCollectionEqualsArray(dag.getIncomingEdges(vertexMap.get("G")));
assertCollectionEqualsArray(dag.getOutgoingEdges(vertexMap.get("G")));
}
}