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.tests.java.lang; 19 20 import java.util.Vector; 21 22 public class ThreadGroupTest extends junit.framework.TestCase { 23 24 private TestThreadDefaultUncaughtExceptionHandler testThreadDefaultUncaughtExceptionHandler; 25 private ThreadGroup rootThreadGroup; 26 private ThreadGroup initialThreadGroup; 27 private Thread.UncaughtExceptionHandler originalThreadDefaultUncaughtExceptionHandler; 28 29 @Override 30 protected void setUp() { 31 initialThreadGroup = Thread.currentThread().getThreadGroup(); 32 rootThreadGroup = initialThreadGroup; 33 while (rootThreadGroup.getParent() != null) { 34 rootThreadGroup = rootThreadGroup.getParent(); 35 } 36 37 // When running as a CTS test Android will by default treat an uncaught exception as a 38 // fatal application error and kill the test. To avoid this the default 39 // UncaughtExceptionHandler is replaced for the duration of the test (if one exists). It 40 // also allows us to test that ultimately the default handler is called if a ThreadGroup's 41 // UncaughtExceptionHandler doesn't handle an exception. 42 originalThreadDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); 43 testThreadDefaultUncaughtExceptionHandler = new TestThreadDefaultUncaughtExceptionHandler(); 44 Thread.setDefaultUncaughtExceptionHandler(testThreadDefaultUncaughtExceptionHandler); 45 } 46 47 @Override 48 protected void tearDown() { 49 // Reset the uncaughtExceptionHandler to what it was when the test began. 50 Thread.setDefaultUncaughtExceptionHandler(originalThreadDefaultUncaughtExceptionHandler); 51 } 52 53 // Test for method java.lang.ThreadGroup(java.lang.String) 54 public void test_ConstructorLjava_lang_String() { 55 // Unfortunately we have to use other APIs as well as we test the constructor 56 ThreadGroup initial = initialThreadGroup; 57 final String name = "Test name"; 58 ThreadGroup newGroup = new ThreadGroup(name); 59 assertTrue( 60 "Has to be possible to create a subgroup of current group using simple constructor", 61 newGroup.getParent() == initial); 62 assertTrue("Name has to be correct", newGroup.getName().equals(name)); 63 64 // cleanup 65 newGroup.destroy(); 66 } 67 68 // Test for method java.lang.ThreadGroup(java.lang.ThreadGroup, java.lang.String) 69 public void test_ConstructorLjava_lang_ThreadGroupLjava_lang_String() { 70 // Unfortunately we have to use other APIs as well as we test the constructor 71 ThreadGroup newGroup = null; 72 try { 73 newGroup = new ThreadGroup(null, null); 74 } catch (NullPointerException e) { 75 } 76 assertNull("Can't create a ThreadGroup with a null parent", newGroup); 77 78 newGroup = new ThreadGroup(initialThreadGroup, null); 79 assertTrue("Has to be possible to create a subgroup of current group", 80 newGroup.getParent() == Thread.currentThread().getThreadGroup()); 81 82 // Lets start all over 83 newGroup.destroy(); 84 85 newGroup = new ThreadGroup(rootThreadGroup, "a name here"); 86 assertTrue("Has to be possible to create a subgroup of root group", 87 newGroup.getParent() == rootThreadGroup); 88 89 // Lets start all over 90 newGroup.destroy(); 91 92 try { 93 newGroup = new ThreadGroup(newGroup, "a name here"); 94 } catch (IllegalThreadStateException e) { 95 newGroup = null; 96 } 97 assertNull("Can't create a subgroup of a destroyed group", newGroup); 98 } 99 100 // Test for method int java.lang.ThreadGroup.activeCount() 101 public void test_activeCount() { 102 ThreadGroup tg = new ThreadGroup("activeCount"); 103 Thread t1 = new Thread(tg, new Runnable() { 104 public void run() { 105 try { 106 Thread.sleep(5000); 107 } catch (InterruptedException e) { 108 } 109 } 110 }); 111 int count = tg.activeCount(); 112 assertTrue("wrong active count: " + count, count == 0); 113 t1.start(); 114 count = tg.activeCount(); 115 assertTrue("wrong active count: " + count, count == 1); 116 t1.interrupt(); 117 try { 118 t1.join(); 119 } catch (InterruptedException e) { 120 } 121 // cleanup 122 tg.destroy(); 123 } 124 125 // Test for method void java.lang.ThreadGroup.destroy() 126 public void test_destroy() { 127 final ThreadGroup originalCurrent = initialThreadGroup; 128 ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group"); 129 final int DEPTH = 4; 130 final Vector<ThreadGroup> subgroups = buildRandomTreeUnder(testRoot, DEPTH); 131 132 // destroy them all 133 testRoot.destroy(); 134 135 for (int i = 0; i < subgroups.size(); i++) { 136 ThreadGroup child = subgroups.elementAt(i); 137 assertEquals("Destroyed child can't have children", 0, child.activeCount()); 138 boolean passed = false; 139 try { 140 child.destroy(); 141 } catch (IllegalThreadStateException e) { 142 passed = true; 143 } 144 assertTrue("Destroyed child can't be destroyed again", passed); 145 } 146 147 testRoot = new ThreadGroup(originalCurrent, "Test group (daemon)"); 148 testRoot.setDaemon(true); 149 150 ThreadGroup child = new ThreadGroup(testRoot, "daemon child"); 151 152 // If we destroy the last daemon's child, the daemon should get destroyed 153 // as well 154 child.destroy(); 155 156 boolean passed = false; 157 try { 158 child.destroy(); 159 } catch (IllegalThreadStateException e) { 160 passed = true; 161 } 162 assertTrue("Daemon should have been destroyed already", passed); 163 164 passed = false; 165 try { 166 testRoot.destroy(); 167 } catch (IllegalThreadStateException e) { 168 passed = true; 169 } 170 assertTrue("Daemon parent should have been destroyed automatically", 171 passed); 172 173 assertTrue( 174 "Destroyed daemon's child should not be in daemon's list anymore", 175 !arrayIncludes(groups(testRoot), child)); 176 assertTrue("Destroyed daemon should not be in parent's list anymore", 177 !arrayIncludes(groups(originalCurrent), testRoot)); 178 179 testRoot = new ThreadGroup(originalCurrent, "Test group (daemon)"); 180 testRoot.setDaemon(true); 181 Thread noOp = new Thread(testRoot, null, "no-op thread") { 182 @Override 183 public void run() { 184 } 185 }; 186 noOp.start(); 187 188 // Wait for the no-op thread to run inside daemon ThreadGroup 189 waitForThreadToDieUninterrupted(noOp); 190 191 passed = false; 192 try { 193 child.destroy(); 194 } catch (IllegalThreadStateException e) { 195 passed = true; 196 } 197 assertTrue("Daemon group should have been destroyed already when last thread died", passed); 198 199 testRoot = new ThreadGroup(originalCurrent, "Test group (daemon)"); 200 noOp = new Thread(testRoot, null, "no-op thread") { 201 @Override 202 public void run() { 203 try { 204 Thread.sleep(500); 205 } catch (InterruptedException ie) { 206 fail("Should not be interrupted"); 207 } 208 } 209 }; 210 211 // Has to execute the next lines in an interval < the sleep interval of the no-op thread 212 noOp.start(); 213 passed = false; 214 try { 215 testRoot.destroy(); 216 } catch (IllegalThreadStateException its) { 217 passed = true; 218 } 219 assertTrue("Can't destroy a ThreadGroup that has threads", passed); 220 221 // But after the thread dies, we have to be able to destroy the thread group 222 waitForThreadToDieUninterrupted(noOp); 223 passed = true; 224 try { 225 testRoot.destroy(); 226 } catch (IllegalThreadStateException its) { 227 passed = false; 228 } 229 assertTrue("Should be able to destroy a ThreadGroup that has no threads", passed); 230 } 231 232 // Test for method java.lang.ThreadGroup.destroy() 233 @SuppressWarnings("DeadThread") 234 public void test_destroy_subtest0() { 235 ThreadGroup group1 = new ThreadGroup("test_destroy_subtest0"); 236 group1.destroy(); 237 try { 238 new Thread(group1, "test_destroy_subtest0"); 239 fail("should throw IllegalThreadStateException"); 240 } catch (IllegalThreadStateException e) { 241 } 242 } 243 244 // Test for method int java.lang.ThreadGroup.getMaxPriority() 245 public void test_getMaxPriority() { 246 final ThreadGroup originalCurrent = initialThreadGroup; 247 ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group"); 248 249 boolean passed = true; 250 try { 251 testRoot.setMaxPriority(Thread.MIN_PRIORITY); 252 } catch (IllegalArgumentException iae) { 253 passed = false; 254 } 255 assertTrue("Should be able to set priority", passed); 256 257 assertTrue("New value should be the same as we set", 258 testRoot.getMaxPriority() == Thread.MIN_PRIORITY); 259 260 testRoot.destroy(); 261 } 262 263 // Test for method java.lang.String java.lang.ThreadGroup.getName() 264 public void test_getName() { 265 final ThreadGroup originalCurrent = initialThreadGroup; 266 final String name = "Test group"; 267 final ThreadGroup testRoot = new ThreadGroup(originalCurrent, name); 268 269 assertTrue("Setting a name&getting does not work", testRoot.getName().equals(name)); 270 271 testRoot.destroy(); 272 } 273 274 // Test for method java.lang.ThreadGroup java.lang.ThreadGroup.getParent() 275 public void test_getParent() { 276 final ThreadGroup originalCurrent = initialThreadGroup; 277 ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group"); 278 279 assertTrue("Parent is wrong", testRoot.getParent() == originalCurrent); 280 281 // Create some groups, nested some levels. 282 final int TOTAL_DEPTH = 5; 283 ThreadGroup current = testRoot; 284 Vector<ThreadGroup> groups = new Vector<ThreadGroup>(); 285 // To maintain the invariant that a thread in the Vector is parent 286 // of the next one in the collection (and child of the previous one) 287 groups.addElement(testRoot); 288 289 for (int i = 0; i < TOTAL_DEPTH; i++) { 290 current = new ThreadGroup(current, "level " + i); 291 groups.addElement(current); 292 } 293 294 // Now we walk the levels down, checking if parent is ok 295 for (int i = 1; i < groups.size(); i++) { 296 current = groups.elementAt(i); 297 ThreadGroup previous = groups.elementAt(i - 1); 298 assertTrue("Parent is wrong", current.getParent() == previous); 299 } 300 301 testRoot.destroy(); 302 } 303 304 // Test for method void java.lang.ThreadGroup.list() 305 public void test_list() { 306 final ThreadGroup originalCurrent = initialThreadGroup; 307 final ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group"); 308 309 // First save the original System.out 310 java.io.PrintStream originalOut = System.out; 311 312 try { 313 java.io.ByteArrayOutputStream contentsStream = new java.io.ByteArrayOutputStream(100); 314 java.io.PrintStream newOut = new java.io.PrintStream(contentsStream); 315 316 // We have to "redirect" System.out to test the method 'list' 317 System.setOut(newOut); 318 319 originalCurrent.list(); 320 321 /* 322 * The output has to look like this: 323 * 324 * java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main] 325 * java.lang.ThreadGroup[name=Test group,maxpri=10] 326 */ 327 String contents = new String(contentsStream.toByteArray()); 328 boolean passed = (contents.indexOf("ThreadGroup[name=main") != -1) && 329 (contents.indexOf("Thread[") != -1) && 330 (contents.indexOf("ThreadGroup[name=Test group") != -1); 331 assertTrue("'list()' does not print expected contents. " 332 + "Result from list: " 333 + contents, passed); 334 // Do proper cleanup 335 testRoot.destroy(); 336 337 } finally { 338 // No matter what, we need to restore the original System.out 339 System.setOut(originalOut); 340 } 341 } 342 343 // Test for method boolean java.lang.ThreadGroup.parentOf(java.lang.ThreadGroup) 344 public void test_parentOfLjava_lang_ThreadGroup() { 345 final ThreadGroup originalCurrent = initialThreadGroup; 346 final ThreadGroup testRoot = new ThreadGroup(originalCurrent, 347 "Test group"); 348 final int DEPTH = 4; 349 buildRandomTreeUnder(testRoot, DEPTH); 350 351 final ThreadGroup[] allChildren = allGroups(testRoot); 352 for (ThreadGroup element : allChildren) { 353 assertTrue("Have to be parentOf all children", testRoot.parentOf(element)); 354 } 355 356 assertTrue("Have to be parentOf itself", testRoot.parentOf(testRoot)); 357 358 testRoot.destroy(); 359 assertTrue("Parent can't have test group as subgroup anymore", 360 !arrayIncludes(groups(testRoot.getParent()), testRoot)); 361 } 362 363 // Test for method boolean java.lang.ThreadGroup.isDaemon() and 364 // void java.lang.ThreadGroup.setDaemon(boolean) 365 public void test_setDaemon_isDaemon() { 366 final ThreadGroup originalCurrent = initialThreadGroup; 367 final ThreadGroup testRoot = new ThreadGroup(originalCurrent, 368 "Test group"); 369 370 testRoot.setDaemon(true); 371 assertTrue("Setting daemon&getting does not work", testRoot.isDaemon()); 372 373 testRoot.setDaemon(false); 374 assertTrue("Setting daemon&getting does not work", !testRoot.isDaemon()); 375 376 testRoot.destroy(); 377 } 378 379 /* 380 * java.lang.ThreadGroupt#setDaemon(boolean) 381 */ 382 public void test_setDaemon_Parent_Child() { 383 ThreadGroup ptg = new ThreadGroup("Parent"); 384 ThreadGroup ctg = new ThreadGroup(ptg, "Child"); 385 386 ctg.setDaemon(true); 387 assertTrue(ctg.isDaemon()); 388 389 ctg.setDaemon(false); 390 assertFalse(ctg.isDaemon()); 391 392 ptg.setDaemon(true); 393 assertFalse(ctg.isDaemon()); 394 395 ptg.setDaemon(false); 396 assertFalse(ctg.isDaemon()); 397 } 398 399 // Test for method void java.lang.ThreadGroup.setMaxPriority(int) 400 public void test_setMaxPriorityI() { 401 final ThreadGroup originalCurrent = initialThreadGroup; 402 ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group"); 403 404 boolean passed; 405 406 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 407 408 int currentMax = testRoot.getMaxPriority(); 409 testRoot.setMaxPriority(Thread.MAX_PRIORITY + 1); 410 passed = testRoot.getMaxPriority() == currentMax; 411 assertTrue( 412 "setMaxPriority: Any value higher than the current one is ignored. Before: " 413 + currentMax + " , after: " + testRoot.getMaxPriority(), 414 passed); 415 416 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 417 418 currentMax = testRoot.getMaxPriority(); 419 testRoot.setMaxPriority(Thread.MIN_PRIORITY - 1); 420 passed = testRoot.getMaxPriority() == Thread.MIN_PRIORITY; 421 assertTrue( 422 "setMaxPriority: Any value smaller than MIN_PRIORITY is adjusted to MIN_PRIORITY. Before: " 423 + currentMax + " , after: " + testRoot.getMaxPriority(), passed); 424 425 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 426 427 testRoot.destroy(); 428 testRoot = new ThreadGroup(originalCurrent, "Test group"); 429 430 // Create some groups, nested some levels. Each level will have maxPrio 431 // 1 unit smaller than the parent's. However, there can't be a group 432 // with priority < Thread.MIN_PRIORITY 433 final int TOTAL_DEPTH = testRoot.getMaxPriority() - Thread.MIN_PRIORITY 434 - 2; 435 ThreadGroup current = testRoot; 436 for (int i = 0; i < TOTAL_DEPTH; i++) { 437 current = new ThreadGroup(current, "level " + i); 438 } 439 440 // Now we walk the levels down, changing the maxPrio and later verifying 441 // that the value is indeed 1 unit smaller than the parent's maxPrio. 442 int maxPrio, parentMaxPrio; 443 current = testRoot; 444 445 // To maintain the invariant that when we are to modify a child, 446 // its maxPriority is always 1 unit smaller than its parent's. 447 // We have to set it for the root manually, and the loop does the rest 448 // for all the other sub-levels 449 current.setMaxPriority(current.getParent().getMaxPriority() - 1); 450 451 for (int i = 0; i < TOTAL_DEPTH; i++) { 452 maxPrio = current.getMaxPriority(); 453 parentMaxPrio = current.getParent().getMaxPriority(); 454 455 ThreadGroup[] children = groups(current); 456 assertEquals("Can only have 1 subgroup", 1, children.length); 457 current = children[0]; 458 assertTrue( 459 "Had to be 1 unit smaller than parent's priority in iteration=" 460 + i + " checking->" + current, 461 maxPrio == parentMaxPrio - 1); 462 current.setMaxPriority(maxPrio - 1); 463 464 // The next test is sort of redundant, since in next iteration it 465 // will be the parent tGroup, so the test will be done. 466 assertTrue("Had to be possible to change max priority", current 467 .getMaxPriority() == maxPrio - 1); 468 } 469 470 assertTrue( 471 "Priority of leaf child group has to be much smaller than original root group", 472 current.getMaxPriority() == testRoot.getMaxPriority() - TOTAL_DEPTH); 473 474 testRoot.destroy(); 475 476 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 477 478 passed = true; 479 testRoot = new ThreadGroup(originalCurrent, "Test group"); 480 try { 481 testRoot.setMaxPriority(Thread.MAX_PRIORITY); 482 } catch (IllegalArgumentException iae) { 483 passed = false; 484 } 485 assertTrue( 486 "Max Priority = Thread.MAX_PRIORITY should be possible if the test is run with default system ThreadGroup as root", 487 passed); 488 testRoot.destroy(); 489 } 490 491 /* 492 * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread, 493 * java.lang.Throwable) 494 * Tests if a Thread tells its ThreadGroup about ThreadDeath. 495 */ 496 public void test_uncaughtException_threadDeath() { 497 final boolean[] passed = new boolean[1]; 498 499 ThreadGroup testRoot = new ThreadGroup(rootThreadGroup, 500 "Test Forcing a throw of ThreadDeath") { 501 @Override 502 public void uncaughtException(Thread t, Throwable e) { 503 if (e instanceof ThreadDeath) { 504 passed[0] = true; 505 } 506 // always forward, any exception 507 super.uncaughtException(t, e); 508 } 509 }; 510 511 final ThreadDeath threadDeath = new ThreadDeath(); 512 Thread thread = new Thread(testRoot, null, "suicidal thread") { 513 @Override 514 public void run() { 515 throw threadDeath; 516 } 517 }; 518 thread.start(); 519 waitForThreadToDieUninterrupted(thread); 520 testThreadDefaultUncaughtExceptionHandler.assertWasCalled(thread, threadDeath); 521 522 testRoot.destroy(); 523 assertTrue( 524 "Any thread should notify its ThreadGroup about its own death, even if suicide:" 525 + testRoot, passed[0]); 526 } 527 528 /* 529 * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread, 530 * java.lang.Throwable) 531 * Test if a Thread tells its ThreadGroup about a natural (non-exception) death. 532 */ 533 public void test_uncaughtException_naturalDeath() { 534 final boolean[] failed = new boolean[1]; 535 536 ThreadGroup testRoot = new ThreadGroup(initialThreadGroup, "Test ThreadDeath") { 537 @Override 538 public void uncaughtException(Thread t, Throwable e) { 539 failed[0] = true; 540 541 // always forward any exception 542 super.uncaughtException(t, e); 543 } 544 }; 545 546 Thread thread = new Thread(testRoot, null, "no-op thread"); 547 thread.start(); 548 waitForThreadToDieUninterrupted(thread); 549 testThreadDefaultUncaughtExceptionHandler.assertWasNotCalled(); 550 testRoot.destroy(); 551 assertFalse("A thread should not call uncaughtException when it dies:" 552 + testRoot, failed[0]); 553 } 554 555 /* 556 * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread, 557 * java.lang.Throwable) 558 * Test if a Thread tells its ThreadGroup about an Exception 559 */ 560 public void test_uncaughtException_runtimeException() { 561 // Our own exception class 562 class TestException extends RuntimeException { 563 private static final long serialVersionUID = 1L; 564 } 565 566 final boolean[] passed = new boolean[1]; 567 568 ThreadGroup testRoot = new ThreadGroup(initialThreadGroup, "Test other Exception") { 569 @Override 570 public void uncaughtException(Thread t, Throwable e) { 571 if (e instanceof TestException) { 572 passed[0] = true; 573 } 574 // always forward any exception 575 super.uncaughtException(t, e); 576 } 577 }; 578 579 final TestException testException = new TestException(); 580 Thread thread = new Thread(testRoot, null, "RuntimeException thread") { 581 @Override 582 public void run() { 583 throw testException; 584 } 585 }; 586 thread.start(); 587 waitForThreadToDieUninterrupted(thread); 588 testThreadDefaultUncaughtExceptionHandler.assertWasCalled(thread, testException); 589 testRoot.destroy(); 590 assertTrue( 591 "Any thread should notify its ThreadGroup about an uncaught exception:" 592 + testRoot, passed[0]); 593 } 594 595 /* 596 * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread, 597 * java.lang.Throwable) 598 * Test if a handler doesn't pass on the exception to super.uncaughtException that's ok. 599 */ 600 public void test_uncaughtException_exceptionHandledByHandler() { 601 // Our own exception class 602 class TestException extends RuntimeException { 603 private static final long serialVersionUID = 1L; 604 } 605 606 ThreadGroup testRoot = new ThreadGroup(initialThreadGroup, "Test other Exception") { 607 @Override 608 public void uncaughtException(Thread t, Throwable e) { 609 // Swallow TestException and always forward any other exception 610 if (!(e instanceof TestException)) { 611 super.uncaughtException(t, e); 612 } 613 } 614 }; 615 616 final TestException testException = new TestException(); 617 Thread thread = new Thread(testRoot, null, "RuntimeException thread") { 618 @Override 619 public void run() { 620 throw testException; 621 } 622 }; 623 thread.start(); 624 waitForThreadToDieUninterrupted(thread); 625 testThreadDefaultUncaughtExceptionHandler.assertWasNotCalled(); 626 testRoot.destroy(); 627 } 628 629 /* 630 * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread, 631 * java.lang.Throwable) 632 * Tests an exception thrown by the handler itself. 633 */ 634 public void test_uncaughtException_exceptionInUncaughtException() { 635 // Our own uncaught exception classes 636 class UncaughtException extends RuntimeException { 637 private static final long serialVersionUID = 1L; 638 } 639 640 ThreadGroup testRoot = new ThreadGroup(initialThreadGroup, 641 "Test Exception in uncaught exception") { 642 @Override 643 public void uncaughtException(Thread t, Throwable e) { 644 // This should be no-op according to the spec 645 throw new UncaughtException(); 646 } 647 }; 648 649 Thread thread = new Thread(testRoot, null, "no-op thread") { 650 @Override 651 public void run() { 652 throw new RuntimeException(); 653 } 654 }; 655 thread.start(); 656 waitForThreadToDieUninterrupted(thread); 657 testThreadDefaultUncaughtExceptionHandler.assertWasNotCalled(); 658 testRoot.destroy(); 659 } 660 661 private static ThreadGroup[] allGroups(ThreadGroup parent) { 662 int count = parent.activeGroupCount(); 663 ThreadGroup[] all = new ThreadGroup[count]; 664 parent.enumerate(all, true); 665 return all; 666 } 667 668 private static void asyncBuildRandomTreeUnder(final ThreadGroup aGroup, 669 final int depth, final Vector<ThreadGroup> allCreated) { 670 if (depth <= 0) { 671 return; 672 } 673 674 final int maxImmediateSubgroups = random(3); 675 for (int i = 0; i < maxImmediateSubgroups; i++) { 676 final int iClone = i; 677 final String name = " Depth = " + depth + ",N = " + iClone 678 + ",Vector size at creation: " + allCreated.size(); 679 // Use concurrency to maximize chance of exposing concurrency bugs 680 // in ThreadGroups 681 Thread t = new Thread(aGroup, name) { 682 @Override 683 public void run() { 684 ThreadGroup newGroup = new ThreadGroup(aGroup, name); 685 allCreated.addElement(newGroup); 686 asyncBuildRandomTreeUnder(newGroup, depth - 1, allCreated); 687 } 688 }; 689 t.start(); 690 } 691 692 } 693 694 private static Vector<ThreadGroup> asyncBuildRandomTreeUnder(final ThreadGroup aGroup, 695 final int depth) { 696 Vector<ThreadGroup> result = new Vector<ThreadGroup>(); 697 asyncBuildRandomTreeUnder(aGroup, depth, result); 698 return result; 699 700 } 701 702 private static ThreadGroup[] groups(ThreadGroup parent) { 703 // No API to get the count of immediate children only ? 704 int count = parent.activeGroupCount(); 705 ThreadGroup[] all = new ThreadGroup[count]; 706 parent.enumerate(all, false); 707 // Now we may have nulls in the array, we must find the actual size 708 int actualSize = 0; 709 for (; actualSize < all.length; actualSize++) { 710 if (all[actualSize] == null) { 711 break; 712 } 713 } 714 ThreadGroup[] result; 715 if (actualSize == all.length) { 716 result = all; 717 } else { 718 result = new ThreadGroup[actualSize]; 719 System.arraycopy(all, 0, result, 0, actualSize); 720 } 721 722 return result; 723 724 } 725 726 private static int random(int max) { 727 return 1 + ((new Object()).hashCode() % max); 728 } 729 730 private static Vector<ThreadGroup> buildRandomTreeUnder(ThreadGroup aGroup, int depth) { 731 Vector<ThreadGroup> result = asyncBuildRandomTreeUnder(aGroup, depth); 732 while (true) { 733 int sizeBefore = result.size(); 734 try { 735 Thread.sleep(1000); 736 int sizeAfter = result.size(); 737 // If no activity for a while, we assume async building may be 738 // done. 739 if (sizeBefore == sizeAfter) { 740 // It can only be done if no more threads. Unfortunately we 741 // are relying on this API to work as well. 742 // If it does not, we may loop forever. 743 if (aGroup.activeCount() == 0) { 744 break; 745 } 746 } 747 } catch (InterruptedException e) { 748 } 749 } 750 return result; 751 752 } 753 754 private static boolean arrayIncludes(Object[] array, Object toTest) { 755 for (Object element : array) { 756 if (element == toTest) { 757 return true; 758 } 759 } 760 return false; 761 } 762 763 private static void waitForThreadToDieUninterrupted(Thread thread) { 764 try { 765 thread.join(); 766 } catch (InterruptedException ie) { 767 fail("Should not have been interrupted"); 768 } 769 } 770 771 private static class TestThreadDefaultUncaughtExceptionHandler 772 implements Thread.UncaughtExceptionHandler { 773 774 private boolean called; 775 private Throwable ex; 776 private Thread thread; 777 778 @Override 779 public void uncaughtException(Thread thread, Throwable ex) { 780 this.called = true; 781 this.thread = thread; 782 this.ex = ex; 783 } 784 785 public void assertWasCalled(Thread thread, Throwable ex) { 786 assertTrue(called); 787 assertSame(this.thread, thread); 788 assertSame(this.ex, ex); 789 } 790 791 public void assertWasNotCalled() { 792 assertFalse(called); 793 } 794 } 795 796 } 797