• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package org.apache.harmony.luni.tests.java.lang;
19 
20 import java.util.Vector;
21 
22 public class ThreadGroupTest extends junit.framework.TestCase {
23 
24 	class MyThread extends Thread {
25 		public volatile int heartBeat = 0;
26 
MyThread(ThreadGroup group, String name)27 		public MyThread(ThreadGroup group, String name)
28 				throws SecurityException, IllegalThreadStateException {
29 			super(group, name);
30 		}
31 
32 		@Override
run()33         public void run() {
34 			while (true) {
35 				heartBeat++;
36 				try {
37 					Thread.sleep(50);
38 				} catch (InterruptedException e) {
39 				}
40 			}
41 		}
42 
isActivelyRunning()43 		public boolean isActivelyRunning() {
44 			long MAX_WAIT = 100;
45 			return isActivelyRunning(MAX_WAIT);
46 		}
47 
isActivelyRunning(long maxWait)48 		public boolean isActivelyRunning(long maxWait) {
49 			int beat = heartBeat;
50 			long start = System.currentTimeMillis();
51 			do {
52 				Thread.yield();
53 				int beat2 = heartBeat;
54 				if (beat != beat2) {
55                     return true;
56                 }
57 			} while (System.currentTimeMillis() - start < maxWait);
58 			return false;
59 		}
60 
61 	}
62 
63 	private ThreadGroup rootThreadGroup = null;
64 
65 	private ThreadGroup initialThreadGroup = null;
66 
67 	/**
68 	 * @tests java.lang.ThreadGroup#ThreadGroup(java.lang.String)
69 	 */
test_ConstructorLjava_lang_String()70 	public void test_ConstructorLjava_lang_String() {
71 		// Test for method java.lang.ThreadGroup(java.lang.String)
72 
73 		// Unfortunately we have to use other APIs as well as we test the
74 		// constructor
75 
76 		ThreadGroup newGroup = null;
77 		ThreadGroup initial = getInitialThreadGroup();
78 		final String name = "Test name";
79 		newGroup = new ThreadGroup(name);
80 		assertTrue(
81 				"Has to be possible to create a subgroup of current group using simple constructor",
82 				newGroup.getParent() == initial);
83 		assertTrue("Name has to be correct", newGroup.getName().equals(name));
84 
85 		// cleanup
86 		newGroup.destroy();
87 
88 	}
89 
90 	/**
91 	 * @tests java.lang.ThreadGroup#ThreadGroup(java.lang.ThreadGroup,
92 	 *        java.lang.String)
93 	 */
test_ConstructorLjava_lang_ThreadGroupLjava_lang_String()94 	public void test_ConstructorLjava_lang_ThreadGroupLjava_lang_String() {
95 		// Test for method java.lang.ThreadGroup(java.lang.ThreadGroup,
96 		// java.lang.String)
97 
98 		// Unfortunately we have to use other APIs as well as we test the
99 		// constructor
100 
101 		ThreadGroup newGroup = null;
102 
103 		try {
104 			newGroup = new ThreadGroup(null, null);
105 		} catch (NullPointerException e) {
106 		}
107 		assertNull("Can't create a ThreadGroup with a null parent",
108 				newGroup);
109 
110 		newGroup = new ThreadGroup(getInitialThreadGroup(), null);
111 		assertTrue("Has to be possible to create a subgroup of current group",
112 				newGroup.getParent() == Thread.currentThread().getThreadGroup());
113 
114 		// Lets start all over
115 		newGroup.destroy();
116 
117 		newGroup = new ThreadGroup(getRootThreadGroup(), "a name here");
118 		assertTrue("Has to be possible to create a subgroup of root group",
119 				newGroup.getParent() == getRootThreadGroup());
120 
121 		// Lets start all over
122 		newGroup.destroy();
123 
124 		try {
125 			newGroup = new ThreadGroup(newGroup, "a name here");
126 		} catch (IllegalThreadStateException e) {
127 			newGroup = null;
128 		}
129 		;
130 		assertNull("Can't create a subgroup of a destroyed group",
131 				newGroup);
132 	}
133 
134 	/**
135 	 * @tests java.lang.ThreadGroup#activeCount()
136 	 */
test_activeCount()137 	public void test_activeCount() {
138 		// Test for method int java.lang.ThreadGroup.activeCount()
139 		ThreadGroup tg = new ThreadGroup("activeCount");
140 		Thread t1 = new Thread(tg, new Runnable() {
141 			public void run() {
142 				try {
143 					Thread.sleep(5000);
144 				} catch (InterruptedException e) {
145 				}
146 			}
147 		});
148 		int count = tg.activeCount();
149 		assertTrue("wrong active count: " + count, count == 0);
150 		t1.start();
151 		count = tg.activeCount();
152 		assertTrue("wrong active count: " + count, count == 1);
153 		t1.interrupt();
154 		try {
155 			t1.join();
156 		} catch (InterruptedException e) {
157 		}
158 		// cleanup
159 		tg.destroy();
160 	}
161 
162 	/**
163 	 * @tests java.lang.ThreadGroup#destroy()
164 	 */
test_destroy()165 	public void test_destroy() {
166 		// Test for method void java.lang.ThreadGroup.destroy()
167 
168 		final ThreadGroup originalCurrent = getInitialThreadGroup();
169 		ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
170 		final int DEPTH = 4;
171 		final Vector<ThreadGroup> subgroups = buildRandomTreeUnder(testRoot, DEPTH);
172 
173 		// destroy them all
174 		testRoot.destroy();
175 
176 		for (int i = 0; i < subgroups.size(); i++) {
177 			ThreadGroup child = subgroups.elementAt(i);
178 			assertEquals("Destroyed child can't have children", 0, child
179 					.activeCount());
180 			boolean passed = false;
181 			try {
182 				child.destroy();
183 			} catch (IllegalThreadStateException e) {
184 				passed = true;
185 			}
186 			;
187 			assertTrue("Destroyed child can't be destroyed again", passed);
188 		}
189 
190 		testRoot = new ThreadGroup(originalCurrent, "Test group (daemon)");
191 		testRoot.setDaemon(true);
192 
193 		ThreadGroup child = new ThreadGroup(testRoot, "daemon child");
194 
195 		// If we destroy the last daemon's child, the daemon should get destroyed
196 		// as well
197 		child.destroy();
198 
199 		boolean passed = false;
200 		try {
201 			child.destroy();
202 		} catch (IllegalThreadStateException e) {
203 			passed = true;
204 		}
205 		;
206 		assertTrue("Daemon should have been destroyed already", passed);
207 
208 		passed = false;
209 		try {
210 			testRoot.destroy();
211 		} catch (IllegalThreadStateException e) {
212 			passed = true;
213 		}
214 		;
215 		assertTrue("Daemon parent should have been destroyed automatically",
216 				passed);
217 
218 		assertTrue(
219 				"Destroyed daemon's child should not be in daemon's list anymore",
220 				!arrayIncludes(groups(testRoot), child));
221 		assertTrue("Destroyed daemon should not be in parent's list anymore",
222 				!arrayIncludes(groups(originalCurrent), testRoot));
223 
224 		testRoot = new ThreadGroup(originalCurrent, "Test group (daemon)");
225 		testRoot.setDaemon(true);
226 		Thread noOp = new Thread(testRoot, null, "no-op thread") {
227 			@Override
228             public void run() {
229 			}
230 		};
231 		noOp.start();
232 
233 		// Wait for the no-op thread to run inside daemon ThreadGroup
234 		try {
235 			noOp.join();
236 		} catch (InterruptedException ie) {
237 			fail("Should not be interrupted");
238 		}
239 		;
240 
241 		passed = false;
242 		try {
243 			child.destroy();
244 		} catch (IllegalThreadStateException e) {
245 			passed = true;
246 		}
247 		;
248 		assertTrue(
249 				"Daemon group should have been destroyed already when last thread died",
250 				passed);
251 
252 		testRoot = new ThreadGroup(originalCurrent, "Test group (daemon)");
253 		noOp = new Thread(testRoot, null, "no-op thread") {
254 			@Override
255             public void run() {
256 				try {
257 					Thread.sleep(500);
258 				} catch (InterruptedException ie) {
259 					fail("Should not be interrupted");
260 				}
261 			}
262 		};
263 
264 		// Has to execute the next lines in an interval < the sleep interval of
265 		// the no-op thread
266 		noOp.start();
267 		passed = false;
268 		try {
269 			testRoot.destroy();
270 		} catch (IllegalThreadStateException its) {
271 			passed = true;
272 		}
273 		assertTrue("Can't destroy a ThreadGroup that has threads", passed);
274 
275 		// But after the thread dies, we have to be able to destroy the thread
276 		// group
277 		try {
278 			noOp.join();
279 		} catch (InterruptedException ie) {
280 			fail("Should not be interrupted");
281 		}
282 		;
283 		passed = true;
284 		try {
285 			testRoot.destroy();
286 		} catch (IllegalThreadStateException its) {
287 			passed = false;
288 		}
289 		assertTrue(
290 				"Should be able to destroy a ThreadGroup that has no threads",
291 				passed);
292 
293 	}
294 
295 	/**
296 	 * @tests java.lang.ThreadGroup#destroy()
297 	 */
test_destroy_subtest0()298 	public void test_destroy_subtest0() {
299 		ThreadGroup group1 = new ThreadGroup("test_destroy_subtest0");
300 		group1.destroy();
301 		try {
302 			new Thread(group1, "test_destroy_subtest0");
303 			fail("should throw IllegalThreadStateException");
304 		} catch (IllegalThreadStateException e) {
305 		}
306 	}
307 
308 	/**
309 	 * @tests java.lang.ThreadGroup#getMaxPriority()
310 	 */
test_getMaxPriority()311 	public void test_getMaxPriority() {
312 		// Test for method int java.lang.ThreadGroup.getMaxPriority()
313 
314 		final ThreadGroup originalCurrent = getInitialThreadGroup();
315 		ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
316 
317 		boolean passed = true;
318 		try {
319 			testRoot.setMaxPriority(Thread.MIN_PRIORITY);
320 		} catch (IllegalArgumentException iae) {
321 			passed = false;
322 		}
323 		assertTrue("Should be able to set priority", passed);
324 
325 		assertTrue("New value should be the same as we set", testRoot
326 				.getMaxPriority() == Thread.MIN_PRIORITY);
327 
328 		testRoot.destroy();
329 
330 	}
331 
332 	/**
333 	 * @tests java.lang.ThreadGroup#getName()
334 	 */
test_getName()335 	public void test_getName() {
336 		// Test for method java.lang.String java.lang.ThreadGroup.getName()
337 
338 		final ThreadGroup originalCurrent = getInitialThreadGroup();
339 		final String name = "Test group";
340 		final ThreadGroup testRoot = new ThreadGroup(originalCurrent, name);
341 
342 		assertTrue("Setting a name&getting does not work", testRoot.getName()
343 				.equals(name));
344 
345 		testRoot.destroy();
346 
347 	}
348 
349 	/**
350 	 * @tests java.lang.ThreadGroup#getParent()
351 	 */
test_getParent()352 	public void test_getParent() {
353 		// Test for method java.lang.ThreadGroup
354 		// java.lang.ThreadGroup.getParent()
355 
356 		final ThreadGroup originalCurrent = getInitialThreadGroup();
357 		ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
358 
359 		assertTrue("Parent is wrong", testRoot.getParent() == originalCurrent);
360 
361 		// Create some groups, nested some levels.
362 		final int TOTAL_DEPTH = 5;
363 		ThreadGroup current = testRoot;
364 		Vector<ThreadGroup> groups = new Vector<ThreadGroup>();
365 		// To maintain the invariant that a thread in the Vector is parent
366 		// of the next one in the collection (and child of the previous one)
367 		groups.addElement(testRoot);
368 
369 		for (int i = 0; i < TOTAL_DEPTH; i++) {
370 			current = new ThreadGroup(current, "level " + i);
371 			groups.addElement(current);
372 		}
373 
374 		// Now we walk the levels down, checking if parent is ok
375 		for (int i = 1; i < groups.size(); i++) {
376 			current = groups.elementAt(i);
377 			ThreadGroup previous = groups.elementAt(i - 1);
378 			assertTrue("Parent is wrong", current.getParent() == previous);
379 		}
380 
381 		testRoot.destroy();
382 	}
383 
384 	/**
385 	 * @tests java.lang.ThreadGroup#isDaemon()
386 	 */
test_isDaemon()387 	public void test_isDaemon() {
388 		// Test for method boolean java.lang.ThreadGroup.isDaemon()
389 
390 		daemonTests();
391 
392 	}
393 
394 	/**
395 	 * @tests java.lang.ThreadGroup#list()
396 	 */
test_list()397 	public void test_list() {
398 		// Test for method void java.lang.ThreadGroup.list()
399 
400 		final ThreadGroup originalCurrent = getInitialThreadGroup();
401 		// wipeSideEffectThreads destroy all side effect of threads created in
402 		// java.lang.Thread
403 		boolean result = wipeSideEffectThreads(originalCurrent);
404 		if (result == false) {
405             fail("wipe threads in test_list() not successful");
406         }
407 		final ThreadGroup testRoot = new ThreadGroup(originalCurrent,
408 				"Test group");
409 
410 		// First save the original System.out
411 		java.io.PrintStream originalOut = System.out;
412 
413 		try {
414 			java.io.ByteArrayOutputStream contentsStream = new java.io.ByteArrayOutputStream(
415 					100);
416 			java.io.PrintStream newOut = new java.io.PrintStream(contentsStream);
417 
418 			// We have to "redirect" System.out to test the method 'list'
419 			System.setOut(newOut);
420 
421 			originalCurrent.list();
422 
423 			/*
424 			 * The output has to look like this
425 			 *
426 			 * java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main]
427 			 * java.lang.ThreadGroup[name=Test group,maxpri=10]
428 			 *
429 			 */
430 
431             String contents = new String(contentsStream.toByteArray());
432             boolean passed = (contents.indexOf("ThreadGroup[name=main") != -1) &&
433                              (contents.indexOf("Thread[") != -1) &&
434                              (contents.indexOf("ThreadGroup[name=Test group") != -1);
435             assertTrue("'list()' does not print expected contents. "
436                     + "Result from list: "
437                     + contents, passed);
438 			// Do proper cleanup
439 			testRoot.destroy();
440 
441 		} finally {
442 			// No matter what, we need to restore the original System.out
443 			System.setOut(originalOut);
444 		}
445 
446 	}
447 
448 	/**
449 	 * @tests java.lang.ThreadGroup#parentOf(java.lang.ThreadGroup)
450 	 */
test_parentOfLjava_lang_ThreadGroup()451 	public void test_parentOfLjava_lang_ThreadGroup() {
452 		// Test for method boolean
453 		// java.lang.ThreadGroup.parentOf(java.lang.ThreadGroup)
454 
455 		final ThreadGroup originalCurrent = getInitialThreadGroup();
456 		final ThreadGroup testRoot = new ThreadGroup(originalCurrent,
457 				"Test group");
458 		final int DEPTH = 4;
459 		buildRandomTreeUnder(testRoot, DEPTH);
460 
461 		final ThreadGroup[] allChildren = allGroups(testRoot);
462 		for (ThreadGroup element : allChildren) {
463 			assertTrue("Have to be parentOf all children", testRoot
464 					.parentOf(element));
465 		}
466 
467 		assertTrue("Have to be parentOf itself", testRoot.parentOf(testRoot));
468 
469 		testRoot.destroy();
470 		assertTrue("Parent can't have test group as subgroup anymore",
471 				!arrayIncludes(groups(testRoot.getParent()), testRoot));
472 	}
473 
474 	/**
475 	 * @tests java.lang.ThreadGroup#setDaemon(boolean)
476 	 */
test_setDaemonZ()477 	public void test_setDaemonZ() {
478 		// Test for method void java.lang.ThreadGroup.setDaemon(boolean)
479 
480 		daemonTests();
481 
482 	}
483 
484     /*
485      * @tests java.lang.ThreadGroupt#setDaemon(boolean)
486      */
test_setDaemon_Parent_Child()487     public void test_setDaemon_Parent_Child() {
488         ThreadGroup ptg = new ThreadGroup("Parent");
489         ThreadGroup ctg = new ThreadGroup(ptg, "Child");
490 
491         ctg.setDaemon(true);
492         assertTrue(ctg.isDaemon());
493 
494         ctg.setDaemon(false);
495         assertFalse(ctg.isDaemon());
496 
497         ptg.setDaemon(true);
498         assertFalse(ctg.isDaemon());
499 
500         ptg.setDaemon(false);
501         assertFalse(ctg.isDaemon());
502     }
503 
504 	/**
505 	 * @tests java.lang.ThreadGroup#setMaxPriority(int)
506 	 */
test_setMaxPriorityI()507 	public void test_setMaxPriorityI() {
508 		// Test for method void java.lang.ThreadGroup.setMaxPriority(int)
509 
510 		final ThreadGroup originalCurrent = getInitialThreadGroup();
511 		ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
512 
513 		boolean passed;
514 
515 		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
516 
517 		int currentMax = testRoot.getMaxPriority();
518 		testRoot.setMaxPriority(Thread.MAX_PRIORITY + 1);
519 		passed = testRoot.getMaxPriority() == currentMax;
520 		assertTrue(
521 				"setMaxPriority: Any value higher than the current one is ignored. Before: "
522 						+ currentMax + " , after: " + testRoot.getMaxPriority(),
523 				passed);
524 
525 		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
526 
527 		currentMax = testRoot.getMaxPriority();
528 		testRoot.setMaxPriority(Thread.MIN_PRIORITY - 1);
529 		passed = testRoot.getMaxPriority() == Thread.MIN_PRIORITY;
530 		assertTrue(
531 				"setMaxPriority: Any value smaller than MIN_PRIORITY is adjusted to MIN_PRIORITY. Before: "
532 						+ currentMax + " , after: " + testRoot.getMaxPriority(),
533 				passed);
534 
535 		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
536 
537 		testRoot.destroy();
538 		testRoot = new ThreadGroup(originalCurrent, "Test group");
539 
540 		// Create some groups, nested some levels. Each level will have maxPrio
541 		// 1 unit smaller than the parent's. However, there can't be a group
542 		// with priority < Thread.MIN_PRIORITY
543 		final int TOTAL_DEPTH = testRoot.getMaxPriority() - Thread.MIN_PRIORITY
544 				- 2;
545 		ThreadGroup current = testRoot;
546 		for (int i = 0; i < TOTAL_DEPTH; i++) {
547 			current = new ThreadGroup(current, "level " + i);
548 		}
549 
550 		// Now we walk the levels down, changing the maxPrio and later verifying
551 		// that the value is indeed 1 unit smaller than the parent's maxPrio.
552 		int maxPrio, parentMaxPrio;
553 		current = testRoot;
554 
555 		// To maintain the invariant that when we are to modify a child,
556 		// its maxPriority is always 1 unit smaller than its parent's.
557 		// We have to set it for the root manually, and the loop does the rest
558 		// for all the other sub-levels
559 		current.setMaxPriority(current.getParent().getMaxPriority() - 1);
560 
561 		for (int i = 0; i < TOTAL_DEPTH; i++) {
562 			maxPrio = current.getMaxPriority();
563 			parentMaxPrio = current.getParent().getMaxPriority();
564 
565 			ThreadGroup[] children = groups(current);
566 			assertEquals("Can only have 1 subgroup", 1, children.length);
567 			current = children[0];
568 			assertTrue(
569 					"Had to be 1 unit smaller than parent's priority in iteration="
570 							+ i + " checking->" + current,
571 					maxPrio == parentMaxPrio - 1);
572 			current.setMaxPriority(maxPrio - 1);
573 
574 			// The next test is sort of redundant, since in next iteration it
575 			// will be the parent tGroup, so the test will be done.
576 			assertTrue("Had to be possible to change max priority", current
577 					.getMaxPriority() == maxPrio - 1);
578 		}
579 
580 		assertTrue(
581 				"Priority of leaf child group has to be much smaller than original root group",
582 				current.getMaxPriority() == testRoot.getMaxPriority()
583 						- TOTAL_DEPTH);
584 
585 		testRoot.destroy();
586 
587 		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
588 
589 		passed = true;
590 		testRoot = new ThreadGroup(originalCurrent, "Test group");
591 		try {
592 			testRoot.setMaxPriority(Thread.MAX_PRIORITY);
593 		} catch (IllegalArgumentException iae) {
594 			passed = false;
595 		}
596 		assertTrue(
597 				"Max Priority = Thread.MAX_PRIORITY should be possible if the test is run with default system ThreadGroup as root",
598 				passed);
599 		testRoot.destroy();
600 	}
601 
602 	/**
603 	 * @tests java.lang.ThreadGroup#uncaughtException(java.lang.Thread,
604 	 *        java.lang.Throwable)
605 	 */
606 	@SuppressWarnings("deprecation")
test_uncaughtExceptionLjava_lang_ThreadLjava_lang_Throwable()607     public void test_uncaughtExceptionLjava_lang_ThreadLjava_lang_Throwable() {
608 		// Test for method void
609 		// java.lang.ThreadGroup.uncaughtException(java.lang.Thread,
610 		// java.lang.Throwable)
611 
612 		final ThreadGroup originalCurrent = getInitialThreadGroup();
613 
614 		// indices for the array defined below
615 		final int TEST_DEATH = 0;
616 		final int TEST_OTHER = 1;
617 		final int TEST_EXCEPTION_IN_UNCAUGHT = 2;
618 		final int TEST_OTHER_THEN_DEATH = 3;
619 		final int TEST_FORCING_THROW_THREAD_DEATH = 4;
620 		final int TEST_KILLING = 5;
621 		final int TEST_DEATH_AFTER_UNCAUGHT = 6;
622 
623 		final boolean[] passed = new boolean[] { false, false, false, false,
624 				false, false, false };
625 
626 		ThreadGroup testRoot;
627 		Thread thread;
628 
629 		// Our own exception class
630 		class TestException extends RuntimeException {
631             private static final long serialVersionUID = 1L;
632 		}
633 
634 		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
635 		// - - - - - - -
636 		testRoot = new ThreadGroup(originalCurrent,
637 				"Test killing a Thread, forcing it to throw ThreadDeath") {
638 			@Override
639             public void uncaughtException(Thread t, Throwable e) {
640 				if (e instanceof ThreadDeath) {
641                     passed[TEST_KILLING] = true;
642                 }
643 				// always forward, any exception
644 				super.uncaughtException(t, e);
645 			}
646 		};
647 
648 		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
649 		// - - - - - - -
650 		testRoot = new ThreadGroup(originalCurrent,
651 				"Test Forcing a throw of ThreadDeath") {
652 			@Override
653             public void uncaughtException(Thread t, Throwable e) {
654 				if (e instanceof ThreadDeath) {
655                     passed[TEST_FORCING_THROW_THREAD_DEATH] = true;
656                 }
657 				// always forward, any exception
658 				super.uncaughtException(t, e);
659 			}
660 		};
661 
662 		// Test if a Thread tells its ThreadGroup about ThreadDeath
663 		thread = new Thread(testRoot, null, "suicidal thread") {
664 			@Override
665             public void run() {
666 				throw new ThreadDeath();
667 			}
668 		};
669 		thread.start();
670 		try {
671 			thread.join();
672 		} catch (InterruptedException ie) {
673 			fail("Should not have been interrupted");
674 		}
675 		testRoot.destroy();
676 		assertTrue(
677 				"Any thread should notify its ThreadGroup about its own death, even if suicide:"
678 						+ testRoot, passed[TEST_FORCING_THROW_THREAD_DEATH]);
679 
680 		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
681 		// - - - - - - -
682 
683 		testRoot = new ThreadGroup(originalCurrent, "Test ThreadDeath") {
684 			@Override
685             public void uncaughtException(Thread t, Throwable e) {
686 				passed[TEST_DEATH] = false;
687 				// always forward, any exception
688 				super.uncaughtException(t, e);
689 			}
690 		};
691 
692 		// Test if a Thread tells its ThreadGroup about ThreadDeath
693 		passed[TEST_DEATH] = true;
694 		thread = new Thread(testRoot, null, "no-op thread");
695 		thread.start();
696 		try {
697 			thread.join();
698 		} catch (InterruptedException ie) {
699 			fail("Should not have been interrupted");
700 		}
701 		testRoot.destroy();
702 		assertTrue("A thread should not call uncaughtException when it dies:"
703 				+ testRoot, passed[TEST_DEATH]);
704 
705 		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
706 		// - - - - - - -
707 
708 		testRoot = new ThreadGroup(originalCurrent, "Test other Exception") {
709 			@Override
710             public void uncaughtException(Thread t, Throwable e) {
711 				if (e instanceof TestException) {
712                     passed[TEST_OTHER] = true;
713                 } else {
714                     // only forward exceptions other than our test
715 					super.uncaughtException(t, e);
716                 }
717 			}
718 		};
719 
720 		// Test if a Thread tells its ThreadGroup about an Exception
721 		thread = new Thread(testRoot, null, "no-op thread") {
722 			@Override
723             public void run() {
724 				throw new TestException();
725 			}
726 		};
727 		thread.start();
728 		try {
729 			thread.join();
730 		} catch (InterruptedException ie) {
731 			fail("Should not have been interrupted");
732 		}
733 		testRoot.destroy();
734 		assertTrue(
735 				"Any thread should notify its ThreadGroup about an uncaught exception:"
736 						+ testRoot, passed[TEST_OTHER]);
737 
738 		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
739 		// - - - - - - -
740 
741 		// Our own uncaught exception class
742 		class UncaughtException extends TestException {
743             private static final long serialVersionUID = 1L;
744 		}
745 
746 		testRoot = new ThreadGroup(originalCurrent,
747 				"Test Exception in uncaught exception") {
748 			@Override
749             public void uncaughtException(Thread t, Throwable e) {
750 				if (e instanceof TestException) {
751 					passed[TEST_EXCEPTION_IN_UNCAUGHT] = true;
752 					// Let's simulate an error inside our uncaughtException
753 					// method.
754 					// This should be no-op according to the spec
755 					throw new UncaughtException();
756 				}
757                 // only forward exceptions other than our test
758                 super.uncaughtException(t, e);
759 			}
760 		};
761 
762 		// Test if an Exception in uncaughtException is really a no-op
763 		thread = new Thread(testRoot, null, "no-op thread") {
764 			@Override
765             public void run() {
766 				try {
767 					throw new TestException();
768 				} catch (UncaughtException ue) {
769 					// any exception in my ThreadGroup's uncaughtException must
770 					// not be propagated.
771 					// If it gets propagated and we detected that, the test failed
772 					passed[TEST_EXCEPTION_IN_UNCAUGHT] = false;
773 				}
774 			}
775 		};
776 		thread.start();
777 		try {
778 			thread.join();
779 		} catch (InterruptedException ie) {
780 			fail("Should not have been interrupted");
781 		}
782 		testRoot.destroy();
783 		assertTrue(
784 				"Any uncaughtException in uncaughtException should be no-op:"
785 						+ testRoot, passed[TEST_EXCEPTION_IN_UNCAUGHT]);
786 
787 		// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
788 		// - - - - - - -
789 
790 		// This is a mix of 2 of the tests above. It is assumed that ThreadDeath
791 		// and any random exception do work , tested separately. Now we test
792 		// if after an uncaughtException is forwarded to the ThreadGroup and
793 		// the Thread dies, if ThreadDeath is also forwarded. It should be
794 		// (so that a ThreadGroup can know its Thread died)
795 		testRoot = new ThreadGroup(originalCurrent,
796 				"Test Uncaught followed by ThreadDeath") {
797 			@Override
798             public void uncaughtException(Thread t, Throwable e) {
799 				if (e instanceof ThreadDeath) {
800                     passed[TEST_DEATH_AFTER_UNCAUGHT] = true;
801                 }
802 				if (e instanceof TestException) {
803                     passed[TEST_OTHER_THEN_DEATH] = true;
804                 } else {
805                     // only forward exceptions other than our test
806 					super.uncaughtException(t, e);
807                 }
808 			}
809 		};
810 
811 		// Test if a Thread tells its ThreadGroup about an Exception and also
812 		// ThreadDeath
813 		thread = new Thread(testRoot, null, "no-op thread") {
814 			@Override
815             public void run() {
816 				throw new TestException();
817 			}
818 		};
819 		thread.start();
820 		try {
821 			thread.join();
822 		} catch (InterruptedException ie) {
823 			fail("Should not have been interrupted");
824 		}
825 		testRoot.destroy();
826 	}
827 
828 	@Override
setUp()829     protected void setUp() {
830 		initialThreadGroup = Thread.currentThread().getThreadGroup();
831 		rootThreadGroup = initialThreadGroup;
832 		while (rootThreadGroup.getParent() != null) {
833             rootThreadGroup = rootThreadGroup.getParent();
834         }
835 	}
836 
837 	@Override
tearDown()838     protected void tearDown() {
839 		try {
840 			// Give the threads a chance to die.
841 			Thread.sleep(50);
842 		} catch (InterruptedException e) {
843 		}
844 	}
845 
threads(ThreadGroup parent)846 	private Thread[] threads(ThreadGroup parent) {
847 		// No API to get the count of immediate children only ?
848 		int count = parent.activeCount();
849 		Thread[] all = new Thread[count];
850 		int actualSize = parent.enumerate(all, false);
851 		Thread[] result;
852 		if (actualSize == all.length) {
853             result = all;
854         } else {
855 			result = new Thread[actualSize];
856 			System.arraycopy(all, 0, result, 0, actualSize);
857 		}
858 
859 		return result;
860 
861 	}
862 
getInitialThreadGroup()863 	private ThreadGroup getInitialThreadGroup() {
864 		return initialThreadGroup;
865 	}
866 
allGroups(ThreadGroup parent)867 	private ThreadGroup[] allGroups(ThreadGroup parent) {
868 		int count = parent.activeGroupCount();
869 		ThreadGroup[] all = new ThreadGroup[count];
870 		parent.enumerate(all, true);
871 		return all;
872 	}
873 
daemonTests()874 	private void daemonTests() {
875 		// Test for method void java.lang.ThreadGroup.setDaemon(boolean)
876 
877 		final ThreadGroup originalCurrent = getInitialThreadGroup();
878 		final ThreadGroup testRoot = new ThreadGroup(originalCurrent,
879 				"Test group");
880 
881 		testRoot.setDaemon(true);
882 		assertTrue("Setting daemon&getting does not work", testRoot.isDaemon());
883 
884 		testRoot.setDaemon(false);
885 		assertTrue("Setting daemon&getting does not work", !testRoot.isDaemon());
886 
887 		testRoot.destroy();
888 
889 	}
890 
wipeAllThreads(final ThreadGroup aGroup)891 	private boolean wipeAllThreads(final ThreadGroup aGroup) {
892 		boolean ok = true;
893 		Thread[] threads = threads(aGroup);
894 		for (Thread t : threads) {
895 			ok = ok && wipeThread(t);
896 		}
897 
898 		// Recursively for subgroups (if any)
899 		ThreadGroup[] children = groups(aGroup);
900 		for (ThreadGroup element : children) {
901 			ok = ok && wipeAllThreads(element);
902 		}
903 
904 		return ok;
905 
906 	}
907 
wipeSideEffectThreads(ThreadGroup aGroup)908 	private boolean wipeSideEffectThreads(ThreadGroup aGroup) {
909 		boolean ok = true;
910 		Thread[] threads = threads(aGroup);
911 		for (Thread t : threads) {
912 			if (t.getName().equals("SimpleThread")
913 					|| t.getName().equals("Bogus Name")
914 					|| t.getName().equals("Testing")
915 					|| t.getName().equals("foo")
916 					|| t.getName().equals("Test Group")
917 					|| t.getName().equals("Squawk")
918 					|| t.getName().equals("Thread-1")
919 					|| t.getName().equals("firstOne")
920 					|| t.getName().equals("secondOne")
921 					|| t.getName().equals("Thread-16")
922 					|| t.getName().equals("Thread-14")) {
923                 ok = ok && wipeThread(t);
924             }
925 		}
926 
927 		// Recursively for subgroups (if any)
928 		ThreadGroup[] children = groups(aGroup);
929 
930 		for (ThreadGroup element : children) {
931 			ok = ok && wipeSideEffectThreads(element);
932 			if (element.getName().equals("Test Group")
933 					|| element.getName().equals("foo")
934 					|| element.getName().equals("jp")) {
935                 element.destroy();
936             }
937 		}
938 		try {
939 			// Give the threads a chance to die.
940 			Thread.sleep(50);
941 		} catch (InterruptedException e) {
942 		}
943 		return ok;
944 	}
945 
asyncBuildRandomTreeUnder(final ThreadGroup aGroup, final int depth, final Vector<ThreadGroup> allCreated)946 	private void asyncBuildRandomTreeUnder(final ThreadGroup aGroup,
947 			final int depth, final Vector<ThreadGroup> allCreated) {
948 		if (depth <= 0) {
949             return;
950         }
951 
952 		final int maxImmediateSubgroups = random(3);
953 		for (int i = 0; i < maxImmediateSubgroups; i++) {
954 			final int iClone = i;
955 			final String name = " Depth = " + depth + ",N = " + iClone
956 					+ ",Vector size at creation: " + allCreated.size();
957 			// Use concurrency to maximize chance of exposing concurrency bugs
958 			// in ThreadGroups
959 			Thread t = new Thread(aGroup, name) {
960 				@Override
961                 public void run() {
962 					ThreadGroup newGroup = new ThreadGroup(aGroup, name);
963 					allCreated.addElement(newGroup);
964 					asyncBuildRandomTreeUnder(newGroup, depth - 1, allCreated);
965 				}
966 			};
967 			t.start();
968 		}
969 
970 	}
971 
asyncBuildRandomTreeUnder(final ThreadGroup aGroup, final int depth)972 	private Vector<ThreadGroup> asyncBuildRandomTreeUnder(final ThreadGroup aGroup,
973 			final int depth) {
974 		Vector<ThreadGroup> result = new Vector<ThreadGroup>();
975 		asyncBuildRandomTreeUnder(aGroup, depth, result);
976 		return result;
977 
978 	}
979 
allSuspended(Vector<MyThread> threads)980 	private boolean allSuspended(Vector<MyThread> threads) {
981 		for (int i = 0; i < threads.size(); i++) {
982 			MyThread t = threads.elementAt(i);
983 			if (t.isActivelyRunning()) {
984                 return false;
985             }
986 		}
987 
988 		return true;
989 
990 	}
991 
groups(ThreadGroup parent)992 	private ThreadGroup[] groups(ThreadGroup parent) {
993 		// No API to get the count of immediate children only ?
994 		int count = parent.activeGroupCount();
995 		ThreadGroup[] all = new ThreadGroup[count];
996 		parent.enumerate(all, false);
997 		// Now we may have nulls in the array, we must find the actual size
998 		int actualSize = 0;
999 		for (; actualSize < all.length; actualSize++) {
1000 			if (all[actualSize] == null) {
1001                 break;
1002             }
1003 		}
1004 		ThreadGroup[] result;
1005 		if (actualSize == all.length) {
1006             result = all;
1007         } else {
1008 			result = new ThreadGroup[actualSize];
1009 			System.arraycopy(all, 0, result, 0, actualSize);
1010 		}
1011 
1012 		return result;
1013 
1014 	}
1015 
populateGroupsWithThreads(final ThreadGroup aGroup, final int threadCount)1016 	private Vector<MyThread> populateGroupsWithThreads(final ThreadGroup aGroup,
1017 			final int threadCount) {
1018 		Vector<MyThread> result = new Vector<MyThread>();
1019 		populateGroupsWithThreads(aGroup, threadCount, result);
1020 		return result;
1021 
1022 	}
1023 
populateGroupsWithThreads(final ThreadGroup aGroup, final int threadCount, final Vector<MyThread> allCreated)1024 	private void populateGroupsWithThreads(final ThreadGroup aGroup,
1025 			final int threadCount, final Vector<MyThread> allCreated) {
1026 		for (int i = 0; i < threadCount; i++) {
1027 			final int iClone = i;
1028 			final String name = "(MyThread)N =" + iClone + "/" + threadCount
1029 					+ " ,Vector size at creation: " + allCreated.size();
1030 
1031 			MyThread t = new MyThread(aGroup, name);
1032 			allCreated.addElement(t);
1033 		}
1034 
1035 		// Recursively for subgroups (if any)
1036 		ThreadGroup[] children = groups(aGroup);
1037 		for (ThreadGroup element : children) {
1038 			populateGroupsWithThreads(element, threadCount, allCreated);
1039 		}
1040 
1041 	}
1042 
random(int max)1043 	private int random(int max) {
1044 
1045 		return 1 + ((new Object()).hashCode() % max);
1046 
1047 	}
1048 
1049 	@SuppressWarnings("deprecation")
wipeThread(Thread t)1050     private boolean wipeThread(Thread t) {
1051 		t.stop();
1052 		try {
1053 			t.join(1000);
1054 		} catch (InterruptedException ie) {
1055 			fail("Should not have been interrupted");
1056 		}
1057 		// The thread had plenty (subjective) of time to die so there
1058 		// is a problem.
1059 		if (t.isAlive()) {
1060             return false;
1061         }
1062 
1063 		return true;
1064 	}
1065 
buildRandomTreeUnder(ThreadGroup aGroup, int depth)1066 	private Vector<ThreadGroup> buildRandomTreeUnder(ThreadGroup aGroup, int depth) {
1067 		Vector<ThreadGroup> result = asyncBuildRandomTreeUnder(aGroup, depth);
1068 		while (true) {
1069 			int sizeBefore = result.size();
1070 			try {
1071 				Thread.sleep(1000);
1072 				int sizeAfter = result.size();
1073 				// If no activity for a while, we assume async building may be
1074 				// done.
1075 				if (sizeBefore == sizeAfter) {
1076                     // It can only be done if no more threads. Unfortunately we
1077 					// are relying on this API to work as well.
1078 					// If it does not, we may loop forever.
1079 					if (aGroup.activeCount() == 0) {
1080                         break;
1081                     }
1082                 }
1083 			} catch (InterruptedException e) {
1084 			}
1085 		}
1086 		return result;
1087 
1088 	}
1089 
arrayIncludes(Object[] array, Object toTest)1090 	private boolean arrayIncludes(Object[] array, Object toTest) {
1091 		for (Object element : array) {
1092 			if (element == toTest) {
1093                 return true;
1094             }
1095 		}
1096 
1097 		return false;
1098 	}
1099 
myassertTrue(String msg, boolean b)1100 	protected void myassertTrue(String msg, boolean b) {
1101 		// This method is defined here just to solve a visibility problem
1102 		// of protected methods with inner types
1103 		assertTrue(msg, b);
1104 	}
1105 
getRootThreadGroup()1106 	private ThreadGroup getRootThreadGroup() {
1107 		return rootThreadGroup;
1108 
1109 	}
1110 }
1111