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 27 public MyThread(ThreadGroup group, String name) 28 throws SecurityException, IllegalThreadStateException { 29 super(group, name); 30 } 31 32 @Override 33 public void run() { 34 while (true) { 35 heartBeat++; 36 try { 37 Thread.sleep(50); 38 } catch (InterruptedException e) { 39 } 40 } 41 } 42 43 public boolean isActivelyRunning() { 44 long MAX_WAIT = 100; 45 return isActivelyRunning(MAX_WAIT); 46 } 47 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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") 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 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 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 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 863 private ThreadGroup getInitialThreadGroup() { 864 return initialThreadGroup; 865 } 866 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 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 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 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 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 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 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 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 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 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 1043 private int random(int max) { 1044 1045 return 1 + ((new Object()).hashCode() % max); 1046 1047 } 1048 1049 @SuppressWarnings("deprecation") 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 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 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 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 1106 private ThreadGroup getRootThreadGroup() { 1107 return rootThreadGroup; 1108 1109 } 1110 } 1111