Skip to content
Browse files

Fixed: TESTNG-297: TestNG seemingly never stops running while buildin…

…g failed test suite (Gregg Yost)
  • Loading branch information...
1 parent 711b9b6 commit c3b0ec5a056e8fe3fc3b6ef4335794e199c2e6bb cbeust committed Jan 30, 2009
Showing with 91 additions and 30 deletions.
  1. +6 −5 CHANGES.txt
  2. +21 −25 src/main/org/testng/internal/Graph.java
  3. +64 −0 test/src/test/GraphTest.java
View
11 CHANGES.txt
@@ -1,6 +1,12 @@
===========================================================================
5.8.1
+Added: @BeforeMethod can now declare Object[] as a parameter, which will be filled by the parameters of the test method
+Added: IAnnotationTransformer2
+Added: @Test(invocationTimeOut), which lets you set a time out for the total time taken by invocationCount
+Added: IInvokedMethodListener
+Added: -testjar supports jar file with no testng.xml file
+Fixed: TESTNG-297: TestNG seemingly never stops running while building failed test suite (Gregg Yost)
Fixed: TESTNG-285: @Test(sequential=true) works incorrectly for classes with inheritance
Fixed: TESTNG-254: XMLSuite toXML() ignores listeners
Fixed: TESTNG-276: Thread safety problem in Reporter class
@@ -10,16 +16,11 @@ Fixed: EmailableReporter only displayed the first group for each test method
Fixed: time-outs were not working in <test> and <suite>
Fixed: @BeforeTest failing in a base class would not cause subsequent test methods to be skipped
Fixed: TESTNG-195: @AfterMethod has no way of knowing if the current test failed
-Added: @BeforeMethod can now declare Object[] as a parameter, which will be filled by the parameters of the test method
Fixed: TESTNG-249: Overridden test methods were shadowing each other if specified with <include>
Fixed: DataProviders from @Factory-created tests were all invoked from the same instance
Fixed: enabled was not working on configuration methods
-Added: IAnnotationTransformer2
Fixed: IIinvokedMethodListener was not correctly added in TestNG
-Added: @Test(invocationTimeOut), which lets you set a time out for the total time taken by invocationCount
-Added: IInvokedMethodListener
Fixed: NPE in XmlSuite#toXml
-Added: -testjar supports jar file with no testng.xml file
Fixed: TESTNG-231: NullPointerException thrown converting a suite to XML (Mark)
Doc:
View
46 src/main/org/testng/internal/Graph.java
@@ -6,6 +6,7 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -204,39 +205,34 @@ private static void ppp(String s) {
* @return A list of all the predecessors for o
*/
public List<T> findPredecessors(T o) {
- List<T> result = new ArrayList<T>();
// Locate the node
Node<T> node = findNode(o);
-
if (null == node) {
throw new AssertionError("No such node: " + o);
}
- else {
- List<Node<T>> nodesToWalk = new ArrayList<Node<T>>();
-
- // Found the nodes, now find all its predecessors
- for (Node<T> n : getNodes()) {
- T obj = n.getObject();
- if (node.hasPredecessor(obj)) {
- ppp("FOUND PREDECESSOR " + n);
- if (! result.contains(obj)) {
- result.add(0, obj);
- nodesToWalk.add(n);
- }
- }
- }
-
- // Add all the predecessors of the nodes we just found
- for (Node<T> n : nodesToWalk) {
- List<T> r = findPredecessors(n.getObject());
- for (T obj : r) {
- if (! result.contains(obj)) {
- result.add(0, obj);
- }
+
+ // If we found the node, use breadth first search to find all
+ // all of the predecessors of o. "result" is the growing list
+ // of all predecessors. "visited" is the set of items we've
+ // already encountered. "queue" is the queue of items whose
+ // predecessors we haven't yet explored.
+
+ LinkedList<T> result = new LinkedList<T>();
+ Set<T> visited = new HashSet<T>();
+ LinkedList<T> queue = new LinkedList<T>();
+ visited.add(o);
+ queue.addLast(o);
+
+ while (! queue.isEmpty()) {
+ for (T obj : getPredecessors(queue.removeFirst())) {
+ if (! visited.contains(obj)) {
+ visited.add(obj);
+ queue.addLast(obj);
+ result.addFirst(obj);
}
}
}
-
+
return result;
}
View
64 test/src/test/GraphTest.java
@@ -129,4 +129,68 @@ public void findPredecessors() {
|| 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<String>();
+
+ 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 predecessors.size() == (maxDepth * nodesPerDepth);
+ }
+ }
}

0 comments on commit c3b0ec5

Please sign in to comment.
Something went wrong with that request. Please try again.