View Javadoc
1   package auxtestlib;
2   
3   import java.util.Arrays;
4   import java.util.HashSet;
5   import java.util.Iterator;
6   import java.util.Set;
7   
8   /**
9    * The thread count test helper ensures that all threads started during the
10   * test case are finished by the end of the test case.
11   */
12  public class ThreadCountTestHelper extends AbstractTestHelper {
13  	/**
14  	 * Creates a new helper.
15  	 * @throws Exception initialization failed
16  	 */
17  	public ThreadCountTestHelper() throws Exception {
18  		super();
19  	}
20  
21  	/**
22  	 * Known threads when the test case started.
23  	 */
24  	private static Set<Thread> knownThreads;
25  
26  	@Override
27  	protected void mySetUp() throws Exception {
28  		/*
29  		 * Nothing to do.
30  		 */
31  	}
32  
33  	@Override
34  	protected void myTearDown() throws Exception {
35  		/*
36  		 * Nothing to do.
37  		 */
38  	}
39  
40  	@Override
41  	protected void myCleanUp() throws Exception {
42  		/*
43  		 * If there is a list of known threads, ensure that it matches the
44  		 * list of known threads.
45  		 */
46  		Set<Thread> known = knownThreads;
47  		knownThreads = null;
48  		if (known != null) {
49  			Set<Thread> current = allThreads();
50  			Set<Thread> remaining = new HashSet<>(current);
51  			remaining.removeAll(known);
52  			
53  			if (remaining.size() > 0) {
54  				StringBuffer names = new StringBuffer();
55  				for (Thread t : remaining) {
56  					if (names.length() != 0) {
57  						names.append(", ");
58  					}
59  					
60  					names.append("'" + t.getName() + "' (" + t.getState()
61  							+ ")");
62  				}
63  				
64  				throw new Exception("" + remaining.size() + " threads "
65  						+ "remaing after execution of test case: " + names
66  						+ ".");
67  			}
68  		}
69  	}
70  
71  	@Override
72  	protected void myPrepareFixture() throws Exception {
73  		knownThreads = allThreads();
74  	}
75  	
76  	/**
77  	 * Obtains the set of all running threads.
78  	 * @return all running threads
79  	 */
80  	private Set<Thread> allThreads() {
81  		ThreadGroup g = Thread.currentThread().getThreadGroup();
82  		
83  		/*
84  		 * Get the top thread group.
85  		 */
86  		for( ; g.getParent() != null; g = g.getParent()) ;
87  		
88  		/*
89  		 * Enumerate all threads. Keep recursing until we get what may look
90  		 * like a consistent snapshot. Of course, it is not really a very
91  		 * consistent one but...
92  		 */
93  		int tcount = g.activeCount();
94  		int tcountOld;
95  		Thread t[];
96  		do {
97  			t = new Thread[tcount + 1];
98  			tcountOld = tcount;
99  			tcount = g.enumerate(t);
100 		} while (tcountOld != tcount);
101 		
102 		Set<Thread> all = new HashSet<>(Arrays.asList(t));
103 		Iterator<Thread> it = all.iterator();
104 		while (it.hasNext()) {
105 			Thread ct = it.next();
106 			if (ct == null) {
107 				/*
108 				 * We can end up with nulls here which is somewhat weird but
109 				 * this stuff has a high degree of natural weirdness. 
110 				 */
111 				it.remove();
112 				continue;
113 			}
114 			
115 			Thread.State st = ct.getState();
116 			assert st != null;
117 			if (!ct.isAlive() && st != Thread.State.NEW) {
118 				it.remove();
119 			}
120 		}
121 		
122 		return all;
123 	}
124 }