/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.apache.harmony.luni.tests.java.lang;

import dalvik.annotation.AndroidOnly;
import dalvik.annotation.BrokenTest;
import dalvik.annotation.TestLevel;
import dalvik.annotation.TestTargetNew;
import dalvik.annotation.TestTargetClass;

import java.security.Permission;
import java.util.Vector;

@TestTargetClass(ThreadGroup.class) 
public class ThreadGroupTest extends junit.framework.TestCase implements Thread.UncaughtExceptionHandler {
    
    class MyThread extends Thread {
        public volatile int heartBeat = 0;

        public MyThread(ThreadGroup group, String name)
                throws SecurityException, IllegalThreadStateException {
            super(group, name);
        }

        @Override
        public void run() {
            while (true) {
                heartBeat++;
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    break;
                }
            }
        }

        public boolean isActivelyRunning() {
            long MAX_WAIT = 100;
            return isActivelyRunning(MAX_WAIT);
        }

        public boolean isActivelyRunning(long maxWait) {
            int beat = heartBeat;
            long start = System.currentTimeMillis();
            do {
                Thread.yield();
                int beat2 = heartBeat;
                if (beat != beat2) {
                    return true;
                }
            } while (System.currentTimeMillis() - start < maxWait);
            return false;
        }

    }

    private ThreadGroup rootThreadGroup = null;

    private ThreadGroup initialThreadGroup = null;

    /**
     * @tests java.lang.ThreadGroup#ThreadGroup(java.lang.String)
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "ThreadGroup",
        args = {java.lang.String.class}
    )
    public void test_ConstructorLjava_lang_String() {
        // Test for method java.lang.ThreadGroup(java.lang.String)

        // Unfortunately we have to use other APIs as well as we test the
        // constructor

        ThreadGroup newGroup = null;
        ThreadGroup initial = getInitialThreadGroup();
        final String name = "Test name";
        newGroup = new ThreadGroup(name);
        assertTrue(
                "Has to be possible to create a subgroup of current group using simple constructor",
                newGroup.getParent() == initial);
        assertTrue("Name has to be correct", newGroup.getName().equals(name));

        // cleanup
        newGroup.destroy();
        
        newGroup = new ThreadGroup("");
        assertEquals("", newGroup.getName());
        
        newGroup = new ThreadGroup(null);
        assertNull(newGroup.getName());
        
        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(sm);
        try {
            new ThreadGroup(name);
            fail("Should throw SecurityException");
        } catch (SecurityException e) {
            // expected
        } finally {
           System.setSecurityManager(oldSm);
        }              
    }
    
    /**
     * @tests java.lang.ThreadGroup#ThreadGroup(java.lang.ThreadGroup,
     *        java.lang.String)
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "ThreadGroup",
        args = {java.lang.ThreadGroup.class, java.lang.String.class}
    )
    public void test_ConstructorLjava_lang_ThreadGroupLjava_lang_String() {
        // Test for method java.lang.ThreadGroup(java.lang.ThreadGroup,
        // java.lang.String)

        // Unfortunately we have to use other APIs as well as we test the
        // constructor

        ThreadGroup newGroup = null;

        try {
            newGroup = new ThreadGroup(null, null);
        } catch (NullPointerException e) {
        }
        assertNull("Can't create a ThreadGroup with a null parent",
                newGroup);

        newGroup = new ThreadGroup(getInitialThreadGroup(), null);
        assertTrue("Has to be possible to create a subgroup of current group",
                newGroup.getParent() == Thread.currentThread().getThreadGroup());

        // Lets start all over
        newGroup.destroy();

        newGroup = new ThreadGroup(getRootThreadGroup(), "a name here");
        assertTrue("Has to be possible to create a subgroup of root group",
                newGroup.getParent() == getRootThreadGroup());

        // Lets start all over
        newGroup.destroy();

        try {
            newGroup = new ThreadGroup(newGroup, "a name here");
        } catch (IllegalThreadStateException e) {
            newGroup = null;
        }
        
        assertNull("Can't create a subgroup of a destroyed group",
                newGroup);
        
        try {
            new ThreadGroup(null, "name");
            fail("NullPointerException is not thrown.");
        } catch(NullPointerException npe) {
            //expected
        }
        
        try {
            new ThreadGroup(newGroup, null);
            fail("NullPointerException is not thrown.");
        } catch(NullPointerException npe) {
            //expected
        }

        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(sm);
        try {
            new ThreadGroup(getRootThreadGroup(), "a name here");
            fail("SecurityException was not thrown.");
        } catch (SecurityException e) {
            // expected
        } finally {
           System.setSecurityManager(oldSm);
        }             
    }

    /**
     * @tests java.lang.ThreadGroup#activeCount()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "activeCount",
        args = {}
    )
    public void test_activeCount() {
        // Test for method int java.lang.ThreadGroup.activeCount()
        ThreadGroup tg = new ThreadGroup("activeCount");
        Thread t1 = new Thread(tg, new Runnable() {
            public void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                }
            }
        });
        int beforeCount = tg.activeCount();
        t1.start();
        int afterCount = tg.activeCount();
        assertTrue("count of active threads should be increased", 
                (afterCount - beforeCount) == 1);
        t1.interrupt();
        try {
            t1.join();
        } catch (InterruptedException e) {
        }
        // cleanup
        tg.destroy();
    }

    /**
     * @tests java.lang.ThreadGroup#activeGroupCount()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "activeGroupCount",
        args = {}
    )
    public void test_activeGroupCount() {
     
        ThreadGroup tg = new ThreadGroup("group count");
        assertEquals("Incorrect number of groups",
                0, tg.activeGroupCount());
        Thread t1 = new Thread(tg, new Runnable() {
            public void run() {

            }
        });
        assertEquals("Incorrect number of groups",
                0, tg.activeGroupCount());
        t1.start();
        assertEquals("Incorrect number of groups",
                0, tg.activeGroupCount());
        new ThreadGroup(tg, "test group 1");
        assertEquals("Incorrect number of groups",
                1, tg.activeGroupCount());
        new ThreadGroup(tg, "test group 2");
        assertEquals("Incorrect number of groups",
                2, tg.activeGroupCount());
    }

    /**
     * @tests java.lang.ThreadGroup#allowThreadSuspension(boolean)
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "allowThreadSuspension",
        args = {boolean.class}
    )
    @SuppressWarnings("deprecation")
    public void test_allowThreadSuspensionZ() {
        ThreadGroup tg = new ThreadGroup("thread suspension");
        assertTrue("Thread suspention can not be changed",
                tg.allowThreadSuspension(false));
        assertTrue("Thread suspention can not be changed",
                tg.allowThreadSuspension(true));
    }

    /**
     * @tests java.lang.ThreadGroup#checkAccess()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "checkAccess",
        args = {}
    )
    public void test_checkAccess() {
        // Test for method void java.lang.ThreadGroup.checkAccess()

        final ThreadGroup originalCurrent = getInitialThreadGroup();
        ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");

        SecurityManager currentManager = System.getSecurityManager();
        boolean passed = true;

        try {
            if (currentManager != null) {
                testRoot.checkAccess();
            }
        } catch (SecurityException se) {
            passed = false;
        }

        assertTrue("CheckAccess is no-op with no SecurityManager", passed);

        testRoot.destroy();

        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(sm);
        try {
            testRoot.checkAccess();
            fail("Should throw SecurityException");
        } catch (SecurityException e) {
            // expected
        } finally {
           System.setSecurityManager(oldSm);
        }            
    }

    /*
     * Checks whether the current Thread is in the given list.
     */
    private boolean inListOfThreads(Thread[] threads) {
        for (int i = 0; i < threads.length; i++) {
            if (Thread.currentThread() == threads[i]) {
                return true;
            }
        }
        
        return false;
    }
    
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "enumerate",
        args = {java.lang.Thread[].class}
    )
    public void test_enumerateLThreadArray() {
        int numThreads = initialThreadGroup.activeCount();
        Thread[] listOfThreads = new Thread[numThreads];

        int countThread = initialThreadGroup.enumerate(listOfThreads);
        assertEquals(numThreads, countThread);
        assertTrue("Current thread must be in enumeration of threads",
                inListOfThreads(listOfThreads));
        
        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(sm);
        try {
            initialThreadGroup.enumerate(listOfThreads);
            fail("Should throw SecurityException");
        } catch (SecurityException e) {
            // expected
        } finally {
           System.setSecurityManager(oldSm);
        }        
    }    
    
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "enumerate",
        args = {java.lang.Thread[].class, boolean.class}
    )
    public void test_enumerateLThreadArrayLZ() {
        int numThreads = initialThreadGroup.activeCount();
        Thread[] listOfThreads = new Thread[numThreads];

        int countThread = initialThreadGroup.enumerate(listOfThreads, false);
        assertEquals(numThreads, countThread);

        countThread = initialThreadGroup.enumerate(listOfThreads, true);
        assertEquals(numThreads, countThread);
        assertTrue("Current thread must be in enumeration of threads",
                inListOfThreads(listOfThreads));

        ThreadGroup subGroup = new ThreadGroup(initialThreadGroup, "Test Group 1");
        int subThreadsCount = 3;
        Vector<MyThread> subThreads = populateGroupsWithThreads(subGroup, 
                subThreadsCount);
        
        countThread = initialThreadGroup.enumerate(listOfThreads, true);
        assertEquals(numThreads, countThread);
        assertTrue("Current thread must be in enumeration of threads",
                inListOfThreads(listOfThreads));
        
        for(MyThread thr:subThreads) {
            thr.start();
        }
        // lets give them some time to start
        try {
            Thread.sleep(500);
        } catch (InterruptedException ie) {
            fail("Should not be interrupted");
        }

        int numThreads2 = initialThreadGroup.activeCount();
        listOfThreads = new Thread[numThreads2];
        
        assertEquals(numThreads + subThreadsCount, numThreads2);
        
        countThread = initialThreadGroup.enumerate(listOfThreads, true);
        assertEquals(numThreads2, countThread);
        assertTrue("Current thread must be in enumeration of threads",
                inListOfThreads(listOfThreads));
        
        for(MyThread thr:subThreads) {
            thr.interrupt();
        }
        // lets give them some time to die
        try {
            Thread.sleep(500);
        } catch (InterruptedException ie) {
            fail("Should not be interrupted");
        }       

        int numThreads3 = initialThreadGroup.activeCount();
        listOfThreads = new Thread[numThreads3];
        
        assertEquals(numThreads, numThreads3);
        
        countThread = initialThreadGroup.enumerate(listOfThreads, false);
        assertEquals(numThreads3, countThread);
        assertTrue("Current thread must be in enumeration of threads",
                inListOfThreads(listOfThreads));

        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(sm);
        try {
            initialThreadGroup.enumerate(listOfThreads, true);
            fail("Should throw SecurityException");
        } catch (SecurityException e) {
            // expected
        } finally {
           System.setSecurityManager(oldSm);
        }   
    }
    
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "enumerate",
        args = {java.lang.ThreadGroup[].class}
    )
    @BrokenTest("Fails in CTS environment, but passes in CoreTestRunner")
    public void test_enumerateLThreadGroupArray() {
        int numGroupThreads = initialThreadGroup.activeGroupCount();
        ThreadGroup[] listOfGroups = new ThreadGroup[numGroupThreads];

        int countGroupThread = initialThreadGroup.enumerate(listOfGroups);
        assertEquals(numGroupThreads, countGroupThread);
        
        ThreadGroup[] listOfGroups1 = new ThreadGroup[numGroupThreads + 1];
        countGroupThread = initialThreadGroup.enumerate(listOfGroups1);
        assertEquals(numGroupThreads, countGroupThread);
        assertNull(listOfGroups1[listOfGroups1.length - 1]);
        
        ThreadGroup[] listOfGroups2 = new ThreadGroup[numGroupThreads - 1];
        countGroupThread = initialThreadGroup.enumerate(listOfGroups2);
        assertEquals(numGroupThreads - 1, countGroupThread);
        
        ThreadGroup thrGroup1 = new ThreadGroup("Test Group 1");
        countGroupThread = thrGroup1.enumerate(listOfGroups);
        assertEquals(0, countGroupThread);

        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(sm);
        try {
            initialThreadGroup.enumerate(listOfGroups);
            fail("Should throw SecurityException");
        } catch (SecurityException e) {
            // expected
        } finally {
           System.setSecurityManager(oldSm);
        }   
     }    
    
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "enumerate",
        args = {java.lang.ThreadGroup[].class, boolean.class}
    )
    public void test_enumerateLThreadGroupArrayLZ() {
        ThreadGroup thrGroup = new ThreadGroup("Test Group 1");
        Vector<MyThread> subThreads = populateGroupsWithThreads(thrGroup, 3);
        int numGroupThreads = thrGroup.activeGroupCount();
        ThreadGroup[] listOfGroups = new ThreadGroup[numGroupThreads];
        
        assertEquals(0, thrGroup.enumerate(listOfGroups, true));
        assertEquals(0, thrGroup.enumerate(listOfGroups, false));
        
        for(MyThread thr:subThreads) {
            thr.start();
        }
        
        numGroupThreads = thrGroup.activeGroupCount();
        listOfGroups = new ThreadGroup[numGroupThreads];
        
        assertEquals(0, thrGroup.enumerate(listOfGroups, true));
        assertEquals(0, thrGroup.enumerate(listOfGroups, false));
        
        ThreadGroup subGroup1 = new ThreadGroup(thrGroup, "Test Group 2");
        Vector<MyThread> subThreads1 = populateGroupsWithThreads(subGroup1, 3); 
        numGroupThreads = thrGroup.activeGroupCount();
        listOfGroups = new ThreadGroup[numGroupThreads];
        
        assertEquals(1, thrGroup.enumerate(listOfGroups, true));
        assertEquals(1, thrGroup.enumerate(listOfGroups, false));      
        
        for(MyThread thr:subThreads1) {
            thr.start();
        }
        numGroupThreads = thrGroup.activeGroupCount();
        listOfGroups = new ThreadGroup[numGroupThreads];
            
        assertEquals(1, thrGroup.enumerate(listOfGroups, true));
        assertEquals(1, thrGroup.enumerate(listOfGroups, false));              
        
        for(MyThread thr:subThreads) {
            thr.interrupt();
         }        
        
        ThreadGroup subGroup2 = new ThreadGroup(subGroup1, "Test Group 3");
        Vector<MyThread> subThreads2 = populateGroupsWithThreads(subGroup2, 3); 
        numGroupThreads = thrGroup.activeGroupCount();
        listOfGroups = new ThreadGroup[numGroupThreads];
        
        assertEquals(2, thrGroup.enumerate(listOfGroups, true));
        assertEquals(1, thrGroup.enumerate(listOfGroups, false));  

        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(sm);
        try {
            thrGroup.enumerate(listOfGroups, true);
            fail("Should throw SecurityException");
        } catch (SecurityException e) {
            // expected
        } finally {
           System.setSecurityManager(oldSm);
        }   
    }
    
    /**
     * @tests java.lang.ThreadGroup#destroy()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "destroy",
        args = {}
    )
    public void test_destroy() {
        // Test for method void java.lang.ThreadGroup.destroy()

        final ThreadGroup originalCurrent = getInitialThreadGroup();
        ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
        final int DEPTH = 4;
        final Vector<ThreadGroup> subgroups = buildRandomTreeUnder(testRoot, DEPTH);

        // destroy them all
        testRoot.destroy();

        for (int i = 0; i < subgroups.size(); i++) {
            ThreadGroup child = subgroups.elementAt(i);
            assertEquals("Destroyed child can't have children", 0, child
                    .activeCount());
            boolean passed = false;
            try {
                child.destroy();
            } catch (IllegalThreadStateException e) {
                passed = true;
            }
            
            assertTrue("Destroyed child can't be destroyed again", passed);
        }

        testRoot = new ThreadGroup(originalCurrent, "Test group (daemon)");
        testRoot.setDaemon(true);

        ThreadGroup child = new ThreadGroup(testRoot, "daemon child");

        // If we destroy the last daemon's child, the daemon should get destroyed
        // as well
        child.destroy();

        boolean passed = false;
        try {
            child.destroy();
        } catch (IllegalThreadStateException e) {
            passed = true;
        }
        
        assertTrue("Daemon should have been destroyed already", passed);

        passed = false;
        try {
            testRoot.destroy();
        } catch (IllegalThreadStateException e) {
            passed = true;
        }
        
        assertTrue("Daemon parent should have been destroyed automatically",
                passed);

        assertTrue(
                "Destroyed daemon's child should not be in daemon's list anymore",
                !arrayIncludes(groups(testRoot), child));
        assertTrue("Destroyed daemon should not be in parent's list anymore",
                !arrayIncludes(groups(originalCurrent), testRoot));

        testRoot = new ThreadGroup(originalCurrent, "Test group (daemon)");
        testRoot.setDaemon(true);
        Thread noOp = new Thread(testRoot, null, "no-op thread") {
            @Override
            public void run() {
            }
        };
        noOp.start();

        // Wait for the no-op thread to run inside daemon ThreadGroup
        try {
            noOp.join();
        } catch (InterruptedException ie) {
            fail("Should not be interrupted");
        }
        

        passed = false;
        try {
            child.destroy();
        } catch (IllegalThreadStateException e) {
            passed = true;
        }
        
        assertTrue(
                "Daemon group should have been destroyed already when last thread died",
                passed);

        testRoot = new ThreadGroup(originalCurrent, "Test group (daemon)");
        noOp = new Thread(testRoot, null, "no-op thread") {
            @Override
            public void run() {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ie) {
                    fail("Should not be interrupted");
                }
            }
        };

        // Has to execute the next lines in an interval < the sleep interval of
        // the no-op thread
        noOp.start();
        passed = false;
        try {
            testRoot.destroy();
        } catch (IllegalThreadStateException its) {
            passed = true;
        }
        assertTrue("Can't destroy a ThreadGroup that has threads", passed);

        // But after the thread dies, we have to be able to destroy the thread
        // group
        try {
            noOp.join();
        } catch (InterruptedException ie) {
            fail("Should not be interrupted");
        }
        
        passed = true;
        try {
            testRoot.destroy();
        } catch (IllegalThreadStateException its) {
            passed = false;
        }
        assertTrue(
                "Should be able to destroy a ThreadGroup that has no threads",
                passed);

        ThreadGroup tg = new ThreadGroup("ThreadGroup");
        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(sm);
        try {
            originalCurrent.destroy();
            fail("Should throw SecurityException");
        } catch (SecurityException e) {
            // expected
        } finally {
           System.setSecurityManager(oldSm);
        } 
    }

    /**
     * @tests java.lang.ThreadGroup#destroy()
     */
    @TestTargetNew(
        level = TestLevel.PARTIAL,
        notes = "Verifies IllegalThreadStateException.",
        method = "destroy",
        args = {}
    )
    public void test_destroy_subtest0() {
        ThreadGroup group1 = new ThreadGroup("test_destroy_subtest0");
        group1.destroy();
        try {
            new Thread(group1, "test_destroy_subtest0");
            fail("should throw IllegalThreadStateException");
        } catch (IllegalThreadStateException e) {
        }
    }

    /**
     * @tests java.lang.ThreadGroup#getMaxPriority()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "getMaxPriority",
        args = {}
    )
    public void test_getMaxPriority() {
        // Test for method int java.lang.ThreadGroup.getMaxPriority()
        final ThreadGroup originalCurrent = getInitialThreadGroup();
        ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");

        boolean passed = true;
        try {
            testRoot.setMaxPriority(Thread.MIN_PRIORITY);
        } catch (IllegalArgumentException iae) {
            passed = false;
        }
        assertTrue("Should be able to set priority", passed);

        assertTrue("New value should be the same as we set", testRoot
                .getMaxPriority() == Thread.MIN_PRIORITY);

        testRoot.destroy();

    }

    /**
     * @tests java.lang.ThreadGroup#getName()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "getName",
        args = {}
    )
    public void test_getName() {
        // Test for method java.lang.String java.lang.ThreadGroup.getName()
        final ThreadGroup originalCurrent = getInitialThreadGroup();
        final String name = "Test group";
        final ThreadGroup testRoot = new ThreadGroup(originalCurrent, name);

        assertTrue("Setting a name&getting does not work", testRoot.getName()
                .equals(name));

        testRoot.destroy();

    }

    /**
     * @tests java.lang.ThreadGroup#getParent()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "getParent",
        args = {}
    )
    public void test_getParent() {
        // Test for method java.lang.ThreadGroup
        // java.lang.ThreadGroup.getParent()
        final ThreadGroup originalCurrent = getInitialThreadGroup();
        ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");

        assertTrue("Parent is wrong", testRoot.getParent() == originalCurrent);

        // Create some groups, nested some levels.
        final int TOTAL_DEPTH = 5;
        ThreadGroup current = testRoot;
        Vector<ThreadGroup> groups = new Vector<ThreadGroup>();
        // To maintain the invariant that a thread in the Vector is parent
        // of the next one in the collection (and child of the previous one)
        groups.addElement(testRoot);

        for (int i = 0; i < TOTAL_DEPTH; i++) {
            current = new ThreadGroup(current, "level " + i);
            groups.addElement(current);
        }

        // Now we walk the levels down, checking if parent is ok
        for (int i = 1; i < groups.size(); i++) {
            current = groups.elementAt(i);
            ThreadGroup previous = groups.elementAt(i - 1);
            assertTrue("Parent is wrong", current.getParent() == previous);
        }

        final ThreadGroup[] checkAccessGroup = new ThreadGroup[1];
        class SecurityManagerImpl extends MutableSecurityManager {
            @Override
            public void checkAccess(ThreadGroup group) {
                checkAccessGroup[0] = group;
            }
        }
        SecurityManagerImpl sm = new SecurityManagerImpl();
        //add permission to allow reset of security manager
        sm.addPermission(MutableSecurityManager.SET_SECURITY_MANAGER);

        ThreadGroup parent;
        try {
            // To see if it checks Thread creation with our SecurityManager
            System.setSecurityManager(sm);
            parent = testRoot.getParent();
        } finally {
            // restore original, no side-effects
            System.setSecurityManager(null);
        }
        assertTrue("checkAccess with incorrect group",
                checkAccessGroup[0] == parent);

        testRoot.destroy();
    }

    /**
     * @tests java.lang.ThreadGroup#interrupt()
     */
    private static boolean interrupted = false;
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "interrupt",
        args = {}
    )    
    public void test_interrupt() {

        Thread.setDefaultUncaughtExceptionHandler(this);
        ThreadGroup tg = new ThreadGroup("interrupt");
        Thread t1 = new Thread(tg, new Runnable() {
            public void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    fail("ok");
                }
            }
        });
        assertFalse("Incorrect state of thread", interrupted);
        t1.start();
        assertFalse("Incorrect state of thread", interrupted);
        t1.interrupt();
        try {
            t1.join();
        } catch (InterruptedException e) {
        }
        assertTrue("Incorrect state of thread", interrupted);
        tg.destroy();

        ThreadGroup threadGroup = new ThreadGroup("securityCheck");
        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(sm);
        try {
            threadGroup.interrupt();
            fail("Should throw SecurityException");
        } catch (SecurityException e) {
            // expected
        } finally {
           System.setSecurityManager(oldSm);
        }        
    }

    /**
     * @tests java.lang.ThreadGroup#isDaemon()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "isDaemon",
        args = {}
    )
    public void test_isDaemon() {
        // Test for method boolean java.lang.ThreadGroup.isDaemon()
        daemonTests();
    }

    /**
     * @tests java.lang.ThreadGroup#isDestroyed()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "isDestroyed",
        args = {}
    )
    public void test_isDestroyed() {
        final ThreadGroup originalCurrent = getInitialThreadGroup();
        final ThreadGroup testRoot = new ThreadGroup(originalCurrent,
                "Test group");
        assertFalse("Test group is not destroyed yet",
                testRoot.isDestroyed());
        testRoot.destroy();
        assertTrue("Test group already destroyed",
                testRoot.isDestroyed());
    }

    /**
     * @tests java.lang.ThreadGroup#list()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "list",
        args = {}
    )
    public void test_list() {
        // Test for method void java.lang.ThreadGroup.list()

        final ThreadGroup originalCurrent = getInitialThreadGroup();
        // wipeSideEffectThreads destroy all side effect of threads created in
        // java.lang.Thread
        boolean result = wipeSideEffectThreads(originalCurrent);
        if (result == false) {
            fail("wipe threads in test_list() not successful");
        }
        final ThreadGroup testRoot = new ThreadGroup(originalCurrent,
                "Test group");

        // First save the original System.out
        java.io.PrintStream originalOut = System.out;

        try {
            java.io.ByteArrayOutputStream contentsStream = new java.io.ByteArrayOutputStream(
                    100);
            java.io.PrintStream newOut = new java.io.PrintStream(contentsStream);

            // We have to "redirect" System.out to test the method 'list'
            System.setOut(newOut);

            originalCurrent.list();

            /*
             * The output has to look like this
             *
             * java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main]
             * java.lang.ThreadGroup[name=Test group,maxpri=10]
             *
             */

            String contents = new String(contentsStream.toByteArray());
            boolean passed = (contents.indexOf("ThreadGroup[name=main") != -1) &&
                             (contents.indexOf("Thread[") != -1) &&
                             (contents.indexOf("ThreadGroup[name=Test group") != -1);
            assertTrue("'list()' does not print expected contents. "
                    + "Result from list: "
                    + contents, passed);
            // Do proper cleanup
            testRoot.destroy();

        } finally {
            // No matter what, we need to restore the original System.out
            System.setOut(originalOut);
        }

    }

    /**
     * @tests java.lang.ThreadGroup#parentOf(java.lang.ThreadGroup)
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "parentOf",
        args = {java.lang.ThreadGroup.class}
    )
    public void test_parentOfLjava_lang_ThreadGroup() {
        // Test for method boolean
        // java.lang.ThreadGroup.parentOf(java.lang.ThreadGroup)
        final ThreadGroup originalCurrent = getInitialThreadGroup();
        final ThreadGroup testRoot = new ThreadGroup(originalCurrent,
                "Test group");
        final int DEPTH = 4;
        buildRandomTreeUnder(testRoot, DEPTH);

        final ThreadGroup[] allChildren = allGroups(testRoot);
        for (ThreadGroup element : allChildren) {
            assertTrue("Have to be parentOf all children", testRoot
                    .parentOf(element));
        }

        assertTrue("Have to be parentOf itself", testRoot.parentOf(testRoot));

        testRoot.destroy();
        assertTrue("Parent can't have test group as subgroup anymore",
                !arrayIncludes(groups(testRoot.getParent()), testRoot));

        try {
            System.setSecurityManager(new MutableSecurityManager(MutableSecurityManager.SET_SECURITY_MANAGER));
            assertTrue("Should not be parent", !testRoot
                    .parentOf(originalCurrent));
        } finally {
            System.setSecurityManager(null);
        }
    }

    /**
     * @tests java.lang.ThreadGroup#resume()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "resume",
        args = {}
    )
    @AndroidOnly("RI does implement this method, whereas Android does not")
    @SuppressWarnings("deprecation")
    public void test_resume() {
        ThreadGroup group = new ThreadGroup("Foo");
        
        Thread thread = launchFiveSecondDummyThread(group);

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // Ignore
        }

        // No-op in Android. Must neither have an effect nor throw an exception.
        Thread.State state = thread.getState();
        group.resume();
        assertEquals(state, thread.getState());

        // Security checks are made even though method is not supported.
        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(new ThreadSecurityManager());
        try {
            group.resume();
            fail("Should throw SecurityException");
        } catch (SecurityException e) {
            // expected
        } finally {
            System.setSecurityManager(oldSm);
        }
        
        try {
            thread.join();
        } catch (InterruptedException e) {
            // Ignore
        }
    }

    /**
     * @tests java.lang.ThreadGroup#setDaemon(boolean)
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "setDaemon",
        args = {boolean.class}
    )
    public void test_setDaemonZ() {
        // Test for method void java.lang.ThreadGroup.setDaemon(boolean)
        daemonTests();
        
        final ThreadGroup testRoot = new ThreadGroup("Test group");

        testRoot.setDaemon(true);

        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(sm);
        
        try {
            new ThreadGroup("");
            fail("Should throw SecurityException");
        } catch (SecurityException e) {
            // expected
        } finally {
           System.setSecurityManager(oldSm);
        }
    }

    /**
     * @tests java.lang.ThreadGroup#setMaxPriority(int)
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "setMaxPriority",
        args = {int.class}
    )
    public void test_setMaxPriorityI() {
        // Test for method void java.lang.ThreadGroup.setMaxPriority(int)
        final ThreadGroup originalCurrent = getInitialThreadGroup();
        ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");

        boolean passed;

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        int currentMax = testRoot.getMaxPriority();
        testRoot.setMaxPriority(Thread.MAX_PRIORITY + 1);
        passed = testRoot.getMaxPriority() == currentMax;
        assertTrue(
                "setMaxPriority: Any value higher than the current one is ignored. Before: "
                        + currentMax + " , after: " + testRoot.getMaxPriority(),
                passed);

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        currentMax = testRoot.getMaxPriority();
        testRoot.setMaxPriority(Thread.MIN_PRIORITY - 1);
        passed = testRoot.getMaxPriority() == Thread.MIN_PRIORITY;
        assertTrue(
                "setMaxPriority: Any value smaller than MIN_PRIORITY is adjusted to MIN_PRIORITY. Before: "
                        + currentMax + " , after: " + testRoot.getMaxPriority(),
                passed);

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        testRoot.destroy();
        testRoot = new ThreadGroup(originalCurrent, "Test group");

        // Create some groups, nested some levels. Each level will have maxPrio
        // 1 unit smaller than the parent's. However, there can't be a group
        // with priority < Thread.MIN_PRIORITY
        final int TOTAL_DEPTH = testRoot.getMaxPriority() - Thread.MIN_PRIORITY
                - 2;
        ThreadGroup current = testRoot;
        for (int i = 0; i < TOTAL_DEPTH; i++) {
            current = new ThreadGroup(current, "level " + i);
        }

        // Now we walk the levels down, changing the maxPrio and later verifying
        // that the value is indeed 1 unit smaller than the parent's maxPrio.
        int maxPrio, parentMaxPrio;
        current = testRoot;

        // To maintain the invariant that when we are to modify a child,
        // its maxPriority is always 1 unit smaller than its parent's.
        // We have to set it for the root manually, and the loop does the rest
        // for all the other sub-levels
        current.setMaxPriority(current.getParent().getMaxPriority() - 1);

        for (int i = 0; i < TOTAL_DEPTH; i++) {
            maxPrio = current.getMaxPriority();
            parentMaxPrio = current.getParent().getMaxPriority();

            ThreadGroup[] children = groups(current);
            assertEquals("Can only have 1 subgroup", 1, children.length);
            current = children[0];
            assertTrue(
                    "Had to be 1 unit smaller than parent's priority in iteration="
                            + i + " checking->" + current,
                    maxPrio == parentMaxPrio - 1);
            current.setMaxPriority(maxPrio - 1);

            // The next test is sort of redundant, since in next iteration it
            // will be the parent tGroup, so the test will be done.
            assertTrue("Had to be possible to change max priority", current
                    .getMaxPriority() == maxPrio - 1);
        }

        assertTrue(
                "Priority of leaf child group has to be much smaller than original root group",
                current.getMaxPriority() == testRoot.getMaxPriority()
                        - TOTAL_DEPTH);

        testRoot.destroy();

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        passed = true;
        testRoot = new ThreadGroup(originalCurrent, "Test group");
        try {
            testRoot.setMaxPriority(Thread.MAX_PRIORITY);
        } catch (IllegalArgumentException iae) {
            passed = false;
        }
        assertTrue(
                "Max Priority = Thread.MAX_PRIORITY should be possible if the test is run with default system ThreadGroup as root",
                passed);
        testRoot.destroy();

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        passed = true;
        testRoot = new ThreadGroup(originalCurrent, "Test group");
        System.setSecurityManager(new MutableSecurityManager(MutableSecurityManager.SET_SECURITY_MANAGER));
        try {
            try {
                testRoot.setMaxPriority(Thread.MIN_PRIORITY);
            } catch (IllegalArgumentException iae) {
                passed = false;
            }
        } finally {
            System.setSecurityManager(null);
        }
        assertTrue(
                "Min Priority = Thread.MIN_PRIORITY should be possible, always",
                passed);
        testRoot.destroy();

        try {
            System.setSecurityManager(new MutableSecurityManager(MutableSecurityManager.SET_SECURITY_MANAGER));
            originalCurrent.setMaxPriority(Thread.MAX_PRIORITY);
        } finally {
            System.setSecurityManager(null);
        }

        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(sm);
        
        try {
            testRoot.setMaxPriority(Thread.MAX_PRIORITY);
            fail("Should throw SecurityException");
        } catch (SecurityException e) {
            // expected
        } finally {
           System.setSecurityManager(oldSm);
        }
    }

    /**
     * @tests java.lang.ThreadGroup#stop()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "stop",
        args = {}
    )
    @AndroidOnly("RI does implement this method, whereas Android does not")
    @SuppressWarnings("deprecation")
    public void test_stop() {
        ThreadGroup group = new ThreadGroup("Foo");
        
        Thread thread = launchFiveSecondDummyThread(group);

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // Ignore
        }

        // No-op in Android. Must neither have an effect nor throw an exception.
        Thread.State state = thread.getState();
        group.stop();
        assertEquals(state, thread.getState());

        // Security checks are made even though method is not supported.
        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(new ThreadSecurityManager());
        try {
            group.stop();
            fail("Should throw SecurityException");
        } catch (SecurityException e) {
            // expected
        } finally {
            System.setSecurityManager(oldSm);
        }
        
        try {
            thread.join();
        } catch (InterruptedException e) {
            // Ignore
        }
    }

    private Thread launchFiveSecondDummyThread(ThreadGroup group) {
        Thread thread = new Thread(group, "Bar") {
            public void run() {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    // Ignore
                }
            }
        };
        
        thread.start();
        
        return thread;
    }

    private class ThreadSecurityManager extends SecurityManager {
        public void checkPermission(Permission perm) {
        }
        
        public void checkAccess(Thread t) {
            throw new SecurityException();
        }
    };
    
    /**
     * @tests java.lang.ThreadGroup#suspend()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "suspend",
        args = {}
    )
    @AndroidOnly("RI does implement this method, whereas Android does not")
    @SuppressWarnings("deprecation")
    public void test_suspend() {
        ThreadGroup group = new ThreadGroup("Foo");
        
        Thread thread = launchFiveSecondDummyThread(group);

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // Ignore
        }

        // No-op in Android. Must neither have an effect nor throw an exception.
        Thread.State state = thread.getState();
        group.suspend();
        assertEquals(state, thread.getState());

        // Security checks are made even though method is not supported.
        SecurityManager oldSm = System.getSecurityManager();
        System.setSecurityManager(new ThreadSecurityManager());
        try {
            group.suspend();
            fail("Should throw SecurityException");
        } catch (SecurityException e) {
            // expected
        } finally {
            System.setSecurityManager(oldSm);
        }
        
        try {
            thread.join();
        } catch (InterruptedException e) {
            // Ignore
        }
    }

    /**
     * @tests java.lang.ThreadGroup#toString()
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "toString",
        args = {}
    )
    public void test_toString() {
        // Test for method java.lang.String java.lang.ThreadGroup.toString()
        final ThreadGroup originalCurrent = getInitialThreadGroup();
        final String tGroupName = "Test group";

        // Our own subclass
        class MyThreadGroup extends ThreadGroup {
            // Have to define a constructor since there's no default one
            public MyThreadGroup(ThreadGroup parent, String name) {
                super(parent, name);
            }
        }
        ;

        ThreadGroup testRoot = new MyThreadGroup(originalCurrent, tGroupName);
        final String toString = testRoot.toString();

        StringBuffer expectedResult = new StringBuffer();
        expectedResult.append(testRoot.getClass().getName());
        expectedResult.append("[name=");
        expectedResult.append(tGroupName);
        expectedResult.append(",maxpri=");
        expectedResult.append(testRoot.getMaxPriority());
        expectedResult.append("]");

        String expectedValue = expectedResult.toString();

        assertTrue("toString does not follow the Java language spec.", toString
                .equals(expectedValue));

        testRoot.destroy();
    }

    /**
     * @tests java.lang.ThreadGroup#uncaughtException(java.lang.Thread,
     *        java.lang.Throwable)
     */
    @TestTargetNew(
        level = TestLevel.COMPLETE,
        notes = "",
        method = "uncaughtException",
        args = {java.lang.Thread.class, java.lang.Throwable.class}
    )  
    @SuppressWarnings("deprecation")
    public void test_uncaughtExceptionLjava_lang_ThreadLjava_lang_Throwable() {
        // Test for method void
        // java.lang.ThreadGroup.uncaughtException(java.lang.Thread,
        // java.lang.Throwable)

        final ThreadGroup originalCurrent = getInitialThreadGroup();
 
        // indices for the array defined below
        final int TEST_DEATH = 0;
        final int TEST_OTHER = 1;
        final int TEST_EXCEPTION_IN_UNCAUGHT = 2;
        final int TEST_OTHER_THEN_DEATH = 3;
        final int TEST_FORCING_THROW_THREAD_DEATH = 4;
        final int TEST_KILLING = 5;
        final int TEST_DEATH_AFTER_UNCAUGHT = 6;

        final boolean[] passed = new boolean[] { false, false, false, false,
                false, false, false };

        ThreadGroup testRoot;
        Thread thread;

        // Our own exception class
        class TestException extends RuntimeException {
            private static final long serialVersionUID = 1L;
        }

        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        // - - - - - - -
        testRoot = new ThreadGroup(originalCurrent,
                "Test Forcing a throw of ThreadDeath") {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                if (e instanceof ThreadDeath) {
                    passed[TEST_FORCING_THROW_THREAD_DEATH] = true;
                }
                // always forward, any exception
                super.uncaughtException(t, e);
            }
        };

        // Test if a Thread tells its ThreadGroup about ThreadDeath
        thread = new Thread(testRoot, null, "suicidal thread") {
            @Override
            public void run() {
                throw new ThreadDeath();
            }
        };
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException ie) {
            fail("Should not have been interrupted");
        }
        testRoot.destroy();
        assertTrue(
                "Any thread should notify its ThreadGroup about its own death, even if suicide:"
                        + testRoot, passed[TEST_FORCING_THROW_THREAD_DEATH]);
        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        // - - - - - - -

        testRoot = new ThreadGroup(originalCurrent, "Test ThreadDeath") {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                passed[TEST_DEATH] = false;
                // always forward, any exception
                super.uncaughtException(t, e);
            }
        };

        // Test if a Thread tells its ThreadGroup about ThreadDeath
        passed[TEST_DEATH] = true;
        thread = new Thread(testRoot, null, "no-op thread");
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException ie) {
            fail("Should not have been interrupted");
        }
        testRoot.destroy();
        assertTrue("A thread should not call uncaughtException when it dies:"
                + testRoot, passed[TEST_DEATH]);
        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        // - - - - - - -

        testRoot = new ThreadGroup(originalCurrent, "Test other Exception") {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                if (e instanceof TestException) {
                    passed[TEST_OTHER] = true;
                } else {
                    // only forward exceptions other than our test
                    super.uncaughtException(t, e);
                }
            }
        };

        // Test if a Thread tells its ThreadGroup about an Exception
        thread = new Thread(testRoot, null, "no-op thread") {
            @Override
            public void run() {
                throw new TestException();
            }
        };
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException ie) {
            fail("Should not have been interrupted");
        }
        testRoot.destroy();
        assertTrue(
                "Any thread should notify its ThreadGroup about an uncaught exception:"
                        + testRoot, passed[TEST_OTHER]);
        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        // - - - - - - -

        // Our own uncaught exception class
        class UncaughtException extends TestException {
            private static final long serialVersionUID = 1L;
        }

        testRoot = new ThreadGroup(originalCurrent,
                "Test Exception in uncaught exception") {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                if (e instanceof TestException) {
                    passed[TEST_EXCEPTION_IN_UNCAUGHT] = true;
                    // Let's simulate an error inside our uncaughtException
                    // method.
                    // This should be no-op according to the spec
                    throw new UncaughtException();
                }
                // only forward exceptions other than our test
                super.uncaughtException(t, e);
            }
        };

        // Test if an Exception in uncaughtException is really a no-op
        thread = new Thread(testRoot, null, "no-op thread") {
            @Override
            public void run() {
                try {
                    throw new TestException();
                } catch (UncaughtException ue) {
                    // any exception in my ThreadGroup's uncaughtException must
                    // not be propagated.
                    // If it gets propagated and we detected that, the test failed
                    passed[TEST_EXCEPTION_IN_UNCAUGHT] = false;
                }
            }
        };
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException ie) {
            fail("Should not have been interrupted");
        }
        testRoot.destroy();
        assertTrue(
                "Any uncaughtException in uncaughtException should be no-op:"
                        + testRoot, passed[TEST_EXCEPTION_IN_UNCAUGHT]);
        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        // - - - - - - -

        // This is a mix of 2 of the tests above. It is assumed that ThreadDeath
        // and any random exception do work , tested separately. Now we test
        // if after an uncaughtException is forwarded to the ThreadGroup and
        // the Thread dies, if ThreadDeath is also forwarded. It should be
        // (so that a ThreadGroup can know its Thread died)
        testRoot = new ThreadGroup(originalCurrent,
                "Test Uncaught followed by ThreadDeath") {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                if (e instanceof ThreadDeath) {
                    passed[TEST_DEATH_AFTER_UNCAUGHT] = true;
                }
                if (e instanceof TestException) {
                    passed[TEST_OTHER_THEN_DEATH] = true;
                } else {
                    // only forward exceptions other than our test
                    super.uncaughtException(t, e);
                }
            }
        };

        // Test if a Thread tells its ThreadGroup about an Exception and also
        // ThreadDeath
        thread = new Thread(testRoot, null, "no-op thread") {
            @Override
            public void run() {
                throw new TestException();
            }
        };
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException ie) {
            fail("Should not have been interrupted");
        }
        testRoot.destroy();
    }

    /*
     * @see java.lang.Thread.UncaughtExceptionHandler#uncaughtException(java.lang.Thread, java.lang.Throwable)
     */
    public void uncaughtException(Thread t, Throwable e) {
        interrupted = true;
        Thread.setDefaultUncaughtExceptionHandler(null);
    }

    @Override
    protected void setUp() {
        initialThreadGroup = Thread.currentThread().getThreadGroup();
        rootThreadGroup = initialThreadGroup;
        while (rootThreadGroup.getParent() != null) {
            rootThreadGroup = rootThreadGroup.getParent();
        }
    }

    @Override
    protected void tearDown() {
        try {
            // Give the threads a chance to die.
            Thread.sleep(50);
        } catch (InterruptedException e) {
        }
    }

    private Thread[] threads(ThreadGroup parent) {
        // No API to get the count of immediate children only ?
        int count = parent.activeCount();
        Thread[] all = new Thread[count];
        int actualSize = parent.enumerate(all, false);
        Thread[] result;
        if (actualSize == all.length) {
            result = all;
        } else {
            result = new Thread[actualSize];
            System.arraycopy(all, 0, result, 0, actualSize);
        }

        return result;

    }

    private ThreadGroup getInitialThreadGroup() {
        return initialThreadGroup;
    }

    private ThreadGroup[] allGroups(ThreadGroup parent) {
        int count = parent.activeGroupCount();
        ThreadGroup[] all = new ThreadGroup[count];
        parent.enumerate(all, true);
        return all;
    }

    private void daemonTests() {
        // Test for method void java.lang.ThreadGroup.setDaemon(boolean)

        final ThreadGroup originalCurrent = getInitialThreadGroup();
        final ThreadGroup testRoot = new ThreadGroup(originalCurrent,
                "Test group");

        testRoot.setDaemon(true);
        assertTrue("Setting daemon&getting does not work", testRoot.isDaemon());

        testRoot.setDaemon(false);
        assertTrue("Setting daemon&getting does not work", !testRoot.isDaemon());

        testRoot.destroy();
    }

    private boolean wipeAllThreads(final ThreadGroup aGroup) {
        boolean ok = true;
        Thread[] threads = threads(aGroup);
        for (Thread t : threads) {
            ok = ok && wipeThread(t);
        }

        // Recursively for subgroups (if any)
        ThreadGroup[] children = groups(aGroup);
        for (ThreadGroup element : children) {
            ok = ok && wipeAllThreads(element);
        }
        return ok;
    }

    private boolean wipeSideEffectThreads(ThreadGroup aGroup) {
        boolean ok = true;
        Thread[] threads = threads(aGroup);
        for (Thread t : threads) {
            if (t.getName().equals("SimpleThread")
                    || t.getName().equals("Bogus Name")
                    || t.getName().equals("Testing")
                    || t.getName().equals("foo")
                    || t.getName().equals("Test Group")
                    || t.getName().equals("Squawk")
                    || t.getName().equals("Thread-1")
                    || t.getName().equals("firstOne")
                    || t.getName().equals("secondOne")
                    || t.getName().equals("Thread-16")
                    || t.getName().equals("Thread-14")) {
                ok = ok && wipeThread(t);
            }
        }

        // Recursively for subgroups (if any)
        ThreadGroup[] children = groups(aGroup);

        for (ThreadGroup element : children) {
            ok = ok && wipeSideEffectThreads(element);
            if(element.getName() !=  null) {
                if (element.getName().equals("Test Group")
                    || element.getName().equals("foo")
                    || element.getName().equals("jp")) {
                    element.destroy();
                }
            }
        }
        try {
            // Give the threads a chance to die.
            Thread.sleep(50);
        } catch (InterruptedException e) {
        }
        return ok;
    }

    private void asyncBuildRandomTreeUnder(final ThreadGroup aGroup,
            final int depth, final Vector<ThreadGroup> allCreated) {
        if (depth <= 0) {
            return;
        }

        final int maxImmediateSubgroups = random(3);
        for (int i = 0; i < maxImmediateSubgroups; i++) {
            final int iClone = i;
            final String name = " Depth = " + depth + ",N = " + iClone
                    + ",Vector size at creation: " + allCreated.size();
            // Use concurrency to maximize chance of exposing concurrency bugs
            // in ThreadGroups
            Thread t = new Thread(aGroup, name) {
                @Override
                public void run() {
                    ThreadGroup newGroup = new ThreadGroup(aGroup, name);
                    allCreated.addElement(newGroup);
                    asyncBuildRandomTreeUnder(newGroup, depth - 1, allCreated);
                }
            };
            t.start();
        }

    }

    private Vector<ThreadGroup> asyncBuildRandomTreeUnder(final ThreadGroup aGroup,
            final int depth) {
        Vector<ThreadGroup> result = new Vector<ThreadGroup>();
        asyncBuildRandomTreeUnder(aGroup, depth, result);
        return result;

    }

    private boolean allSuspended(Vector<MyThread> threads) {
        for (int i = 0; i < threads.size(); i++) {
            MyThread t = threads.elementAt(i);
            if (t.isActivelyRunning()) {
                return false;
            }
        }

        return true;

    }

    private ThreadGroup[] groups(ThreadGroup parent) {
        // No API to get the count of immediate children only ?
        int count = parent.activeGroupCount();
        ThreadGroup[] all = new ThreadGroup[count];
        parent.enumerate(all, false);
        // Now we may have nulls in the array, we must find the actual size
        int actualSize = 0;
        for (; actualSize < all.length; actualSize++) {
            if (all[actualSize] == null) {
                break;
            }
        }
        ThreadGroup[] result;
        if (actualSize == all.length) {
            result = all;
        } else {
            result = new ThreadGroup[actualSize];
            System.arraycopy(all, 0, result, 0, actualSize);
        }

        return result;

    }

    private Vector<MyThread> populateGroupsWithThreads(final ThreadGroup aGroup,
            final int threadCount) {
        Vector<MyThread> result = new Vector<MyThread>();
        populateGroupsWithThreads(aGroup, threadCount, result);
        return result;

    }

    private void populateGroupsWithThreads(final ThreadGroup aGroup,
            final int threadCount, final Vector<MyThread> allCreated) {
        for (int i = 0; i < threadCount; i++) {
            final int iClone = i;
            final String name = "(MyThread)N =" + iClone + "/" + threadCount
                    + " ,Vector size at creation: " + allCreated.size();

            MyThread t = new MyThread(aGroup, name);
            allCreated.addElement(t);
        }

        // Recursively for subgroups (if any)
        ThreadGroup[] children = groups(aGroup);
        for (ThreadGroup element : children) {
            populateGroupsWithThreads(element, threadCount, allCreated);
        }

    }

    private int random(int max) {

        return 1 + ((new Object()).hashCode() % max);

    }

    @SuppressWarnings("deprecation")
    private boolean wipeThread(Thread t) {
        t.stop();
        try {
            t.join(1000);
        } catch (InterruptedException ie) {
            fail("Should not have been interrupted");
        }
        // The thread had plenty (subjective) of time to die so there
        // is a problem.
        if (t.isAlive()) {
            return false;
        }

        return true;
    }

    private Vector<ThreadGroup> buildRandomTreeUnder(ThreadGroup aGroup, int depth) {
        Vector<ThreadGroup> result = asyncBuildRandomTreeUnder(aGroup, depth);
        while (true) {
            int sizeBefore = result.size();
            try {
                Thread.sleep(1000);
                int sizeAfter = result.size();
                // If no activity for a while, we assume async building may be
                // done.
                if (sizeBefore == sizeAfter) {
                    // It can only be done if no more threads. Unfortunately we
                    // are relying on this API to work as well.
                    // If it does not, we may loop forever.
                    if (aGroup.activeCount() == 0) {
                        break;
                    }
                }
            } catch (InterruptedException e) {
            }
        }
        return result;

    }

    private boolean arrayIncludes(Object[] array, Object toTest) {
        for (Object element : array) {
            if (element == toTest) {
                return true;
            }
        }

        return false;
    }

    protected void myassertTrue(String msg, boolean b) {
        // This method is defined here just to solve a visibility problem
        // of protected methods with inner types
        assertTrue(msg, b);
    }

    private ThreadGroup getRootThreadGroup() {
        return rootThreadGroup;

    }
    
    SecurityManager sm = new SecurityManager() {

        public void checkPermission(Permission perm) {
        }
        
        public void checkAccess(ThreadGroup g) {
           throw new SecurityException();
        }
    };
}
