package test;
|
|
import org.testng.Assert;
|
import org.testng.TestNGException;
|
import org.testng.annotations.Test;
|
import org.testng.internal.Graph;
|
import org.testng.internal.Tarjan;
|
|
import java.util.List;
|
|
|
/**
|
* This class
|
*
|
* @author cbeust
|
*/
|
public class GraphTest {
|
|
@Test
|
public void sort() {
|
Graph<String> g = new Graph<>();
|
g.addNode("3");
|
g.addNode("1");
|
g.addNode("2.2");
|
g.addNode("independent");
|
g.addNode("2.1");
|
g.addNode("2");
|
|
g.addPredecessor("3", "2");
|
g.addPredecessor("3", "2.1");
|
g.addPredecessor("3", "2.2");
|
g.addPredecessor("2", "1");
|
g.addPredecessor("2.1", "1");
|
g.addPredecessor("2.2", "1");
|
|
g.topologicalSort();
|
List<String> l = g.getStrictlySortedNodes();
|
int i = 0;
|
Assert.assertTrue("1".equals(l.get(i)));
|
i++;
|
Assert.assertTrue("2".equals(l.get(i)) || "2.1".equals(l.get(i)) || "2.2".equals(l.get(i)));
|
i++;
|
Assert.assertTrue("2".equals(l.get(i)) || "2.1".equals(l.get(i)) || "2.2".equals(l.get(i)));
|
i++;
|
Assert.assertTrue("2".equals(l.get(i)) || "2.1".equals(l.get(i)) || "2.2".equals(l.get(i)));
|
i++;
|
Assert.assertTrue("3".equals(l.get(i)));
|
|
Assert.assertTrue(1 == g.getIndependentNodes().size());
|
}
|
|
@Test(expectedExceptions = org.testng.TestNGException.class)
|
public void cycleShouldFail() {
|
Graph<String> g = createCyclicGraph();
|
g.topologicalSort();
|
}
|
|
@Test
|
public void cycleShouldBeCorrect() {
|
Graph<String> g = null;
|
try {
|
g = createCyclicGraph();
|
g.topologicalSort();
|
}
|
catch(TestNGException ex) {
|
Tarjan<String> t = new Tarjan<>(g, "1");
|
Assert.assertEquals(t.getCycle().size(), 3);
|
}
|
|
}
|
|
private Graph<String> createCyclicGraph() {
|
Graph<String> g = new Graph<>();
|
g.addNode("3");
|
g.addNode("2");
|
g.addNode("1");
|
|
g.addPredecessor("3", "2");
|
g.addPredecessor("2", "1");
|
g.addPredecessor("1", "3");
|
|
return g;
|
}
|
|
@Test
|
public void findPredecessors() {
|
Graph<String> g = new Graph<>();
|
g.addNode("3");
|
g.addNode("1");
|
g.addNode("2.2");
|
g.addNode("independent");
|
g.addNode("2.1");
|
g.addNode("2");
|
|
// 1 -> 2.1, 2.2, 2.3 --> 3
|
g.addPredecessor("3", "2");
|
g.addPredecessor("3", "2.1");
|
g.addPredecessor("3", "2.2");
|
g.addPredecessor("2", "1");
|
g.addPredecessor("2.1", "1");
|
g.addPredecessor("2.2", "1");
|
|
// Invoke sort to make sure there is no side effect
|
g.topologicalSort();
|
|
//
|
// Test findPredecessors
|
//
|
{
|
List<String> predecessors = g.findPredecessors("2");
|
Assert.assertTrue(predecessors.size() == 1);
|
Assert.assertTrue(predecessors.get(0).equals("1"));
|
}
|
|
{
|
List<String> predecessors = g.findPredecessors("3");
|
|
Assert.assertTrue(predecessors.size() == 4);
|
Assert.assertTrue(predecessors.get(0).equals("1"));
|
Assert.assertTrue(predecessors.get(1).equals("2.1")
|
|| predecessors.get(1).equals("2.2")
|
|| predecessors.get(1).equals("2"));
|
Assert.assertTrue(predecessors.get(2).equals("2.1")
|
|| predecessors.get(2).equals("2.2")
|
|| predecessors.get(2).equals("2"));
|
Assert.assertTrue(predecessors.get(3).equals("2.1")
|
|| predecessors.get(3).equals("2.2")
|
|| predecessors.get(3).equals("2"));
|
}
|
}
|
|
// Using an earlier implementation of Graph.findPrecessors, finding
|
// predecessors in certain kinds of graphs where many nodes have the
|
// same predecessors could be very slow, since the old implementation
|
// would explore the same nodes repeatedly. This situation could
|
// happen when test methods are organized in several groups, with
|
// dependsOnGroups annotations so that each method in one group depends
|
// on all of the methods in another group. If there were several
|
// such groups depending on each other in a chain, the combinatorics
|
// of the old method became excessive. This test builds a 72-node graph that
|
// emulates this situation, then call Graph.findPredecessors. The old
|
// implementation run this in 70+ seconds on my computer, the new implementation
|
// takes a few milliseconds. In practice, with larger graphs, the former
|
// slowness could get very extreme, taking hours or more to complete
|
// in some real user situations.
|
//
|
@Test(timeOut = 5000) // If this takes more than 5 seconds we've definitely regressed.
|
public void findPredecessorsTiming() {
|
Graph<String> g = new Graph<>();
|
|
final String rootNode = "myroot";
|
final String independentNode = "independent";
|
g.addNode(rootNode);
|
g.addNode(independentNode);
|
|
final int maxDepth = 7;
|
final int nodesPerDepth = 10; // must be < 100
|
//
|
// Add maxDepth groups of new nodes, where each group contains nodesPerDepth
|
// nodes, and each node in a group a depth N has each node in the group
|
// at depth (N-1) as a predecessor.
|
//
|
for (int depth = 1; depth <= maxDepth; depth++) {
|
for (int i = 0; i < nodesPerDepth; i++) {
|
String newNode = String.valueOf(i + (100 * depth));
|
g.addNode(newNode);
|
if (depth == 1) {
|
continue;
|
}
|
for (int j = 0; j < nodesPerDepth; j++) {
|
String prevNode = String.valueOf(j + (100 * (depth - 1)));
|
g.addPredecessor(newNode, prevNode);
|
}
|
}
|
}
|
|
// Finally, make all of the nodes in the group at depth maxDepth
|
// be predecessors of rootNode.
|
//
|
for (int i = 0; i < nodesPerDepth; i++) {
|
String node = String.valueOf(i + (100 * maxDepth));
|
g.addPredecessor(rootNode, node);
|
}
|
|
// Now we're done building the graph, which has (maxDepth * nodesPerDepth) + 2
|
// nodes. rootNode has all of the other nodes except independentNode
|
// as predecessors.
|
|
//
|
// Test findPredecessors
|
//
|
{
|
List<String> predecessors = g.findPredecessors(rootNode);
|
Assert.assertTrue(predecessors.size() == (maxDepth * nodesPerDepth));
|
}
|
}
|
}
|