1cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath/*
2cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *  Licensed to the Apache Software Foundation (ASF) under one or more
3cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *  contributor license agreements.  See the NOTICE file distributed with
4cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *  this work for additional information regarding copyright ownership.
5cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *  The ASF licenses this file to You under the Apache License, Version 2.0
6cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *  (the "License"); you may not use this file except in compliance with
7cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *  the License.  You may obtain a copy of the License at
8cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *
9cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *     http://www.apache.org/licenses/LICENSE-2.0
10cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *
11cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *  Unless required by applicable law or agreed to in writing, software
12cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *  distributed under the License is distributed on an "AS IS" BASIS,
13cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *  See the License for the specific language governing permissions and
15cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath *  limitations under the License.
16cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath */
17cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
18ab762bb740405d0fefcccf4a0899a234f995be13Narayan Kamathpackage org.apache.harmony.tests.java.lang;
19cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
20cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathimport java.util.Vector;
21cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
22cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamathpublic class ThreadGroupTest extends junit.framework.TestCase {
23cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
24071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    private TestThreadDefaultUncaughtExceptionHandler testThreadDefaultUncaughtExceptionHandler;
25071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    private ThreadGroup rootThreadGroup;
26071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    private ThreadGroup initialThreadGroup;
27071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    private Thread.UncaughtExceptionHandler originalThreadDefaultUncaughtExceptionHandler;
28cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
29071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    @Override
30071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    protected void setUp() {
31071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        initialThreadGroup = Thread.currentThread().getThreadGroup();
32071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        rootThreadGroup = initialThreadGroup;
33071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        while (rootThreadGroup.getParent() != null) {
34071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            rootThreadGroup = rootThreadGroup.getParent();
35cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
36cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
37071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        // When running as a CTS test Android will by default treat an uncaught exception as a
38071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        // fatal application error and kill the test. To avoid this the default
39071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        // UncaughtExceptionHandler is replaced for the duration of the test (if one exists). It
40071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        // also allows us to test that ultimately the default handler is called if a ThreadGroup's
41071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        // UncaughtExceptionHandler doesn't handle an exception.
42071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        originalThreadDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
43071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        testThreadDefaultUncaughtExceptionHandler = new TestThreadDefaultUncaughtExceptionHandler();
44071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        Thread.setDefaultUncaughtExceptionHandler(testThreadDefaultUncaughtExceptionHandler);
45cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
46cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
47071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    @Override
48071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    protected void tearDown() {
49071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        // Reset the uncaughtExceptionHandler to what it was when the test began.
50071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        Thread.setDefaultUncaughtExceptionHandler(originalThreadDefaultUncaughtExceptionHandler);
51071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    }
52cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
53071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    // Test for method java.lang.ThreadGroup(java.lang.String)
54cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    public void test_ConstructorLjava_lang_String() {
55071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        // Unfortunately we have to use other APIs as well as we test the constructor
56071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        ThreadGroup initial = initialThreadGroup;
57cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        final String name = "Test name";
58071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        ThreadGroup newGroup = new ThreadGroup(name);
59cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue(
60cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                "Has to be possible to create a subgroup of current group using simple constructor",
61cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                newGroup.getParent() == initial);
62cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue("Name has to be correct", newGroup.getName().equals(name));
63cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
64cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // cleanup
65cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        newGroup.destroy();
66cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
67cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
68071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    // Test for method java.lang.ThreadGroup(java.lang.ThreadGroup, java.lang.String)
69cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    public void test_ConstructorLjava_lang_ThreadGroupLjava_lang_String() {
70071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        // Unfortunately we have to use other APIs as well as we test the constructor
71cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup newGroup = null;
72cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        try {
73cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            newGroup = new ThreadGroup(null, null);
74cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        } catch (NullPointerException e) {
75cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
76071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        assertNull("Can't create a ThreadGroup with a null parent", newGroup);
77cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
78071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        newGroup = new ThreadGroup(initialThreadGroup, null);
79cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue("Has to be possible to create a subgroup of current group",
80cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                newGroup.getParent() == Thread.currentThread().getThreadGroup());
81cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
82cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // Lets start all over
83cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        newGroup.destroy();
84cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
85071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        newGroup = new ThreadGroup(rootThreadGroup, "a name here");
86cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue("Has to be possible to create a subgroup of root group",
87071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                newGroup.getParent() == rootThreadGroup);
88cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
89cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // Lets start all over
90cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        newGroup.destroy();
91cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
92cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        try {
93cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            newGroup = new ThreadGroup(newGroup, "a name here");
94cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        } catch (IllegalThreadStateException e) {
95cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            newGroup = null;
96cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
97071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        assertNull("Can't create a subgroup of a destroyed group", newGroup);
98cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
99cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
100071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    // Test for method int java.lang.ThreadGroup.activeCount()
101cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    public void test_activeCount() {
102cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup tg = new ThreadGroup("activeCount");
103cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        Thread t1 = new Thread(tg, new Runnable() {
104cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            public void run() {
105cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                try {
106cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    Thread.sleep(5000);
107cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                } catch (InterruptedException e) {
108cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                }
109cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
110cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        });
111cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        int count = tg.activeCount();
112cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue("wrong active count: " + count, count == 0);
113cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        t1.start();
114cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        count = tg.activeCount();
115cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue("wrong active count: " + count, count == 1);
116cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        t1.interrupt();
117cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        try {
118cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            t1.join();
119cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        } catch (InterruptedException e) {
120cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
121cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // cleanup
122cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        tg.destroy();
123cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
124cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
125071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    // Test for method void java.lang.ThreadGroup.destroy()
126cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    public void test_destroy() {
127071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final ThreadGroup originalCurrent = initialThreadGroup;
128cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
129cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        final int DEPTH = 4;
130cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        final Vector<ThreadGroup> subgroups = buildRandomTreeUnder(testRoot, DEPTH);
131cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
132cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // destroy them all
133cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.destroy();
134cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
135cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        for (int i = 0; i < subgroups.size(); i++) {
136cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            ThreadGroup child = subgroups.elementAt(i);
137071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            assertEquals("Destroyed child can't have children", 0, child.activeCount());
138cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            boolean passed = false;
139cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            try {
140cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                child.destroy();
141cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            } catch (IllegalThreadStateException e) {
142cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                passed = true;
143cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
144cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            assertTrue("Destroyed child can't be destroyed again", passed);
145cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
146cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
147cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot = new ThreadGroup(originalCurrent, "Test group (daemon)");
148cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.setDaemon(true);
149cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
150cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup child = new ThreadGroup(testRoot, "daemon child");
151cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
152cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // If we destroy the last daemon's child, the daemon should get destroyed
153cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // as well
154cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        child.destroy();
155cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
156cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        boolean passed = false;
157cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        try {
158cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            child.destroy();
159cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        } catch (IllegalThreadStateException e) {
160cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            passed = true;
161cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
162cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue("Daemon should have been destroyed already", passed);
163cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
164cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        passed = false;
165cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        try {
166cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            testRoot.destroy();
167cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        } catch (IllegalThreadStateException e) {
168cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            passed = true;
169cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
170cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue("Daemon parent should have been destroyed automatically",
171cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                passed);
172cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
173cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue(
174cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                "Destroyed daemon's child should not be in daemon's list anymore",
175cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                !arrayIncludes(groups(testRoot), child));
176cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue("Destroyed daemon should not be in parent's list anymore",
177cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                !arrayIncludes(groups(originalCurrent), testRoot));
178cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
179cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot = new ThreadGroup(originalCurrent, "Test group (daemon)");
180cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.setDaemon(true);
181cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        Thread noOp = new Thread(testRoot, null, "no-op thread") {
182cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            @Override
183cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            public void run() {
184cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
185cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        };
186cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        noOp.start();
187cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
188cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // Wait for the no-op thread to run inside daemon ThreadGroup
189071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        waitForThreadToDieUninterrupted(noOp);
190cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
191cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        passed = false;
192cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        try {
193cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            child.destroy();
194cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        } catch (IllegalThreadStateException e) {
195cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            passed = true;
196cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
197071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        assertTrue("Daemon group should have been destroyed already when last thread died", passed);
198cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
199cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot = new ThreadGroup(originalCurrent, "Test group (daemon)");
200cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        noOp = new Thread(testRoot, null, "no-op thread") {
201cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            @Override
202cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            public void run() {
203cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                try {
204cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    Thread.sleep(500);
205cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                } catch (InterruptedException ie) {
206cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    fail("Should not be interrupted");
207cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                }
208cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
209cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        };
210cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
211071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        // Has to execute the next lines in an interval < the sleep interval of the no-op thread
212cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        noOp.start();
213cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        passed = false;
214cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        try {
215cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            testRoot.destroy();
216cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        } catch (IllegalThreadStateException its) {
217cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            passed = true;
218cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
219cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue("Can't destroy a ThreadGroup that has threads", passed);
220cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
221071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        // But after the thread dies, we have to be able to destroy the thread group
222071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        waitForThreadToDieUninterrupted(noOp);
223cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        passed = true;
224cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        try {
225cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            testRoot.destroy();
226cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        } catch (IllegalThreadStateException its) {
227cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            passed = false;
228cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
229071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        assertTrue("Should be able to destroy a ThreadGroup that has no threads", passed);
230cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
231cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
232071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    // Test for method java.lang.ThreadGroup.destroy()
233cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    public void test_destroy_subtest0() {
234cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup group1 = new ThreadGroup("test_destroy_subtest0");
235cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        group1.destroy();
236cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        try {
237cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            new Thread(group1, "test_destroy_subtest0");
238cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            fail("should throw IllegalThreadStateException");
239cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        } catch (IllegalThreadStateException e) {
240cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
241cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
242cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
243071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    // Test for method int java.lang.ThreadGroup.getMaxPriority()
244cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    public void test_getMaxPriority() {
245071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final ThreadGroup originalCurrent = initialThreadGroup;
246cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
247cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
248cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        boolean passed = true;
249cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        try {
250cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            testRoot.setMaxPriority(Thread.MIN_PRIORITY);
251cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        } catch (IllegalArgumentException iae) {
252cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            passed = false;
253cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
254cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue("Should be able to set priority", passed);
255cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
256071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        assertTrue("New value should be the same as we set",
257071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                testRoot.getMaxPriority() == Thread.MIN_PRIORITY);
258cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
259cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.destroy();
260cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
261cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
262071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    // Test for method java.lang.String java.lang.ThreadGroup.getName()
263cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    public void test_getName() {
264071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final ThreadGroup originalCurrent = initialThreadGroup;
265cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        final String name = "Test group";
266cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        final ThreadGroup testRoot = new ThreadGroup(originalCurrent, name);
267cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
268071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        assertTrue("Setting a name&getting does not work", testRoot.getName().equals(name));
269cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
270cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.destroy();
271cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
272cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
273071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    // Test for method java.lang.ThreadGroup java.lang.ThreadGroup.getParent()
274cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    public void test_getParent() {
275071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final ThreadGroup originalCurrent = initialThreadGroup;
276cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
277cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
278cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue("Parent is wrong", testRoot.getParent() == originalCurrent);
279cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
280cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // Create some groups, nested some levels.
281cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        final int TOTAL_DEPTH = 5;
282cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup current = testRoot;
283cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        Vector<ThreadGroup> groups = new Vector<ThreadGroup>();
284cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // To maintain the invariant that a thread in the Vector is parent
285cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // of the next one in the collection (and child of the previous one)
286cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        groups.addElement(testRoot);
287cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
288cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        for (int i = 0; i < TOTAL_DEPTH; i++) {
289cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            current = new ThreadGroup(current, "level " + i);
290cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            groups.addElement(current);
291cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
292cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
293cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // Now we walk the levels down, checking if parent is ok
294cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        for (int i = 1; i < groups.size(); i++) {
295cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            current = groups.elementAt(i);
296cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            ThreadGroup previous = groups.elementAt(i - 1);
297cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            assertTrue("Parent is wrong", current.getParent() == previous);
298cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
299cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
300cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.destroy();
301cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
302cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
303071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    // Test for method void java.lang.ThreadGroup.list()
304cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    public void test_list() {
305071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final ThreadGroup originalCurrent = initialThreadGroup;
306071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
307cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
308cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // First save the original System.out
309cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        java.io.PrintStream originalOut = System.out;
310cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
311cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        try {
312071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            java.io.ByteArrayOutputStream contentsStream = new java.io.ByteArrayOutputStream(100);
313cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            java.io.PrintStream newOut = new java.io.PrintStream(contentsStream);
314cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
315cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            // We have to "redirect" System.out to test the method 'list'
316cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            System.setOut(newOut);
317cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
318cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            originalCurrent.list();
319cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
320cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            /*
321071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller             * The output has to look like this:
322071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller             *
323071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller             * java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main]
324071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller             * java.lang.ThreadGroup[name=Test group,maxpri=10]
325071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller             */
326cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            String contents = new String(contentsStream.toByteArray());
327cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            boolean passed = (contents.indexOf("ThreadGroup[name=main") != -1) &&
328cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    (contents.indexOf("Thread[") != -1) &&
329cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    (contents.indexOf("ThreadGroup[name=Test group") != -1);
330cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            assertTrue("'list()' does not print expected contents. "
331cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    + "Result from list: "
332cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    + contents, passed);
333cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            // Do proper cleanup
334cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            testRoot.destroy();
335cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
336cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        } finally {
337cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            // No matter what, we need to restore the original System.out
338cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            System.setOut(originalOut);
339cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
340cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
341cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
342071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    // Test for method boolean java.lang.ThreadGroup.parentOf(java.lang.ThreadGroup)
343cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    public void test_parentOfLjava_lang_ThreadGroup() {
344071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final ThreadGroup originalCurrent = initialThreadGroup;
345cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        final ThreadGroup testRoot = new ThreadGroup(originalCurrent,
346cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                "Test group");
347cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        final int DEPTH = 4;
348cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        buildRandomTreeUnder(testRoot, DEPTH);
349cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
350cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        final ThreadGroup[] allChildren = allGroups(testRoot);
351cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        for (ThreadGroup element : allChildren) {
352071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            assertTrue("Have to be parentOf all children", testRoot.parentOf(element));
353cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
354cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
355cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue("Have to be parentOf itself", testRoot.parentOf(testRoot));
356cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
357cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.destroy();
358cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue("Parent can't have test group as subgroup anymore",
359cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                !arrayIncludes(groups(testRoot.getParent()), testRoot));
360cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
361cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
362071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    // Test for method boolean java.lang.ThreadGroup.isDaemon() and
363071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    // void java.lang.ThreadGroup.setDaemon(boolean)
364071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    public void test_setDaemon_isDaemon() {
365071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final ThreadGroup originalCurrent = initialThreadGroup;
366071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final ThreadGroup testRoot = new ThreadGroup(originalCurrent,
367071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                "Test group");
368cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
369071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        testRoot.setDaemon(true);
370071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        assertTrue("Setting daemon&getting does not work", testRoot.isDaemon());
371cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
372071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        testRoot.setDaemon(false);
373071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        assertTrue("Setting daemon&getting does not work", !testRoot.isDaemon());
374071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller
375071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        testRoot.destroy();
376cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
377cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
378cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    /*
379cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath     * java.lang.ThreadGroupt#setDaemon(boolean)
380cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath     */
381cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    public void test_setDaemon_Parent_Child() {
382cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup ptg = new ThreadGroup("Parent");
383cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup ctg = new ThreadGroup(ptg, "Child");
384cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
385cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ctg.setDaemon(true);
386cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue(ctg.isDaemon());
387cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
388cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ctg.setDaemon(false);
389cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertFalse(ctg.isDaemon());
390cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
391cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ptg.setDaemon(true);
392cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertFalse(ctg.isDaemon());
393cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
394cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ptg.setDaemon(false);
395cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertFalse(ctg.isDaemon());
396cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
397cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
398071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    // Test for method void java.lang.ThreadGroup.setMaxPriority(int)
399cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    public void test_setMaxPriorityI() {
400071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final ThreadGroup originalCurrent = initialThreadGroup;
401cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup testRoot = new ThreadGroup(originalCurrent, "Test group");
402cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
403cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        boolean passed;
404cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
405cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
406cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
407cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        int currentMax = testRoot.getMaxPriority();
408cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.setMaxPriority(Thread.MAX_PRIORITY + 1);
409cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        passed = testRoot.getMaxPriority() == currentMax;
410cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue(
411cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                "setMaxPriority: Any value higher than the current one is ignored. Before: "
412cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                        + currentMax + " , after: " + testRoot.getMaxPriority(),
413cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                passed);
414cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
415cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
416cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
417cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        currentMax = testRoot.getMaxPriority();
418cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.setMaxPriority(Thread.MIN_PRIORITY - 1);
419cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        passed = testRoot.getMaxPriority() == Thread.MIN_PRIORITY;
420cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue(
421cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                "setMaxPriority: Any value smaller than MIN_PRIORITY is adjusted to MIN_PRIORITY. Before: "
422071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                        + currentMax + " , after: " + testRoot.getMaxPriority(), passed);
423cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
424cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
425cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
426cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.destroy();
427cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot = new ThreadGroup(originalCurrent, "Test group");
428cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
429cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // Create some groups, nested some levels. Each level will have maxPrio
430cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // 1 unit smaller than the parent's. However, there can't be a group
431cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // with priority < Thread.MIN_PRIORITY
432cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        final int TOTAL_DEPTH = testRoot.getMaxPriority() - Thread.MIN_PRIORITY
433cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                - 2;
434cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup current = testRoot;
435cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        for (int i = 0; i < TOTAL_DEPTH; i++) {
436cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            current = new ThreadGroup(current, "level " + i);
437cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
438cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
439cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // Now we walk the levels down, changing the maxPrio and later verifying
440cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // that the value is indeed 1 unit smaller than the parent's maxPrio.
441cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        int maxPrio, parentMaxPrio;
442cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        current = testRoot;
443cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
444cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // To maintain the invariant that when we are to modify a child,
445cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // its maxPriority is always 1 unit smaller than its parent's.
446cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // We have to set it for the root manually, and the loop does the rest
447cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // for all the other sub-levels
448cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        current.setMaxPriority(current.getParent().getMaxPriority() - 1);
449cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
450cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        for (int i = 0; i < TOTAL_DEPTH; i++) {
451cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            maxPrio = current.getMaxPriority();
452cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            parentMaxPrio = current.getParent().getMaxPriority();
453cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
454cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            ThreadGroup[] children = groups(current);
455cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            assertEquals("Can only have 1 subgroup", 1, children.length);
456cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            current = children[0];
457cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            assertTrue(
458cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    "Had to be 1 unit smaller than parent's priority in iteration="
459cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                            + i + " checking->" + current,
460cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    maxPrio == parentMaxPrio - 1);
461cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            current.setMaxPriority(maxPrio - 1);
462cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
463cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            // The next test is sort of redundant, since in next iteration it
464cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            // will be the parent tGroup, so the test will be done.
465cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            assertTrue("Had to be possible to change max priority", current
466cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    .getMaxPriority() == maxPrio - 1);
467cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
468cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
469cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue(
470cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                "Priority of leaf child group has to be much smaller than original root group",
471071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                current.getMaxPriority() == testRoot.getMaxPriority() - TOTAL_DEPTH);
472cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
473cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.destroy();
474cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
475cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
476cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
477cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        passed = true;
478cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot = new ThreadGroup(originalCurrent, "Test group");
479cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        try {
480cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            testRoot.setMaxPriority(Thread.MAX_PRIORITY);
481cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        } catch (IllegalArgumentException iae) {
482cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            passed = false;
483cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
484cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue(
485cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                "Max Priority = Thread.MAX_PRIORITY should be possible if the test is run with default system ThreadGroup as root",
486cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                passed);
487cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.destroy();
488cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
489cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
490071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    /*
491071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread,
492071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * java.lang.Throwable)
493071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * Tests if a Thread tells its ThreadGroup about ThreadDeath.
494cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath     */
495071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    public void test_uncaughtException_threadDeath() {
496071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final boolean[] passed = new boolean[1];
497cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
498071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        ThreadGroup testRoot = new ThreadGroup(rootThreadGroup,
499cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                "Test Forcing a throw of ThreadDeath") {
500cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            @Override
501cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            public void uncaughtException(Thread t, Throwable e) {
502cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                if (e instanceof ThreadDeath) {
503071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                    passed[0] = true;
504cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                }
505cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                // always forward, any exception
506cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                super.uncaughtException(t, e);
507cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
508cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        };
509cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
510071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final ThreadDeath threadDeath = new ThreadDeath();
511071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        Thread thread = new Thread(testRoot, null, "suicidal thread") {
512cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            @Override
513cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            public void run() {
514071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                throw threadDeath;
515cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
516cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        };
517cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        thread.start();
518071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        waitForThreadToDieUninterrupted(thread);
519071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        testThreadDefaultUncaughtExceptionHandler.assertWasCalled(thread, threadDeath);
520071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller
521cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.destroy();
522cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue(
523cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                "Any thread should notify its ThreadGroup about its own death, even if suicide:"
524071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                        + testRoot, passed[0]);
525071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    }
526cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
527071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    /*
528071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread,
529071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * java.lang.Throwable)
530071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * Test if a Thread tells its ThreadGroup about a natural (non-exception) death.
531071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     */
532071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    public void test_uncaughtException_naturalDeath() {
533071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final boolean[] failed = new boolean[1];
534cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
535071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        ThreadGroup testRoot = new ThreadGroup(initialThreadGroup, "Test ThreadDeath") {
536cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            @Override
537cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            public void uncaughtException(Thread t, Throwable e) {
538071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                failed[0] = true;
539071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller
540071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                // always forward any exception
541cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                super.uncaughtException(t, e);
542cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
543cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        };
544cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
545071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        Thread thread = new Thread(testRoot, null, "no-op thread");
546cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        thread.start();
547071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        waitForThreadToDieUninterrupted(thread);
548071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        testThreadDefaultUncaughtExceptionHandler.assertWasNotCalled();
549cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.destroy();
550071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        assertFalse("A thread should not call uncaughtException when it dies:"
551071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                + testRoot, failed[0]);
552071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    }
553cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
554071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    /*
555071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread,
556071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * java.lang.Throwable)
557071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * Test if a Thread tells its ThreadGroup about an Exception
558071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     */
559071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    public void test_uncaughtException_runtimeException() {
560071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        // Our own exception class
561071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        class TestException extends RuntimeException {
562071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            private static final long serialVersionUID = 1L;
563071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        }
564071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller
565071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final boolean[] passed = new boolean[1];
566cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
567071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        ThreadGroup testRoot = new ThreadGroup(initialThreadGroup, "Test other Exception") {
568cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            @Override
569cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            public void uncaughtException(Thread t, Throwable e) {
570cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                if (e instanceof TestException) {
571071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                    passed[0] = true;
572cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                }
573071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                // always forward any exception
574071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                super.uncaughtException(t, e);
575cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
576cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        };
577cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
578071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final TestException testException = new TestException();
579071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        Thread thread = new Thread(testRoot, null, "RuntimeException thread") {
580cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            @Override
581cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            public void run() {
582071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                throw testException;
583cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
584cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        };
585cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        thread.start();
586071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        waitForThreadToDieUninterrupted(thread);
587071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        testThreadDefaultUncaughtExceptionHandler.assertWasCalled(thread, testException);
588cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.destroy();
589cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        assertTrue(
590cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                "Any thread should notify its ThreadGroup about an uncaught exception:"
591071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                        + testRoot, passed[0]);
592071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    }
593cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
594071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    /*
595071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread,
596071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * java.lang.Throwable)
597071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * Test if a handler doesn't pass on the exception to super.uncaughtException that's ok.
598071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     */
599071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    public void test_uncaughtException_exceptionHandledByHandler() {
600071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        // Our own exception class
601071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        class TestException extends RuntimeException {
602cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            private static final long serialVersionUID = 1L;
603cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
604cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
605071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        ThreadGroup testRoot = new ThreadGroup(initialThreadGroup, "Test other Exception") {
606cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            @Override
607cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            public void uncaughtException(Thread t, Throwable e) {
608071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                // Swallow TestException and always forward any other exception
609071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                if (!(e instanceof TestException)) {
610071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                    super.uncaughtException(t, e);
611cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                }
612cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
613cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        };
614cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
615071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        final TestException testException = new TestException();
616071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        Thread thread = new Thread(testRoot, null, "RuntimeException thread") {
617cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            @Override
618cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            public void run() {
619071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                throw testException;
620cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
621cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        };
622cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        thread.start();
623071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        waitForThreadToDieUninterrupted(thread);
624071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        testThreadDefaultUncaughtExceptionHandler.assertWasNotCalled();
625cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.destroy();
626071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    }
627071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller
628071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    /*
629071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * Test for method void java.lang.ThreadGroup.uncaughtException(java.lang.Thread,
630071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * java.lang.Throwable)
631071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     * Tests an exception thrown by the handler itself.
632071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller     */
633071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    public void test_uncaughtException_exceptionInUncaughtException() {
634071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        // Our own uncaught exception classes
635071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        class UncaughtException extends RuntimeException {
636071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            private static final long serialVersionUID = 1L;
637071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        }
638071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller
639071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        ThreadGroup testRoot = new ThreadGroup(initialThreadGroup,
640071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                "Test Exception in uncaught exception") {
641cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            @Override
642cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            public void uncaughtException(Thread t, Throwable e) {
643071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                // This should be no-op according to the spec
644071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                throw new UncaughtException();
645cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
646cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        };
647cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
648071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        Thread thread = new Thread(testRoot, null, "no-op thread") {
649cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            @Override
650cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            public void run() {
651071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller                throw new RuntimeException();
652cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
653cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        };
654cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        thread.start();
655071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        waitForThreadToDieUninterrupted(thread);
656071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        testThreadDefaultUncaughtExceptionHandler.assertWasNotCalled();
657cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        testRoot.destroy();
658cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
659cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
660071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    private static ThreadGroup[] allGroups(ThreadGroup parent) {
661cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        int count = parent.activeGroupCount();
662cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup[] all = new ThreadGroup[count];
663cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        parent.enumerate(all, true);
664cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        return all;
665cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
666cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
667071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    private static void asyncBuildRandomTreeUnder(final ThreadGroup aGroup,
668cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            final int depth, final Vector<ThreadGroup> allCreated) {
669cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        if (depth <= 0) {
670cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            return;
671cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
672cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
673cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        final int maxImmediateSubgroups = random(3);
674cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        for (int i = 0; i < maxImmediateSubgroups; i++) {
675cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            final int iClone = i;
676cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            final String name = " Depth = " + depth + ",N = " + iClone
677cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    + ",Vector size at creation: " + allCreated.size();
678cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            // Use concurrency to maximize chance of exposing concurrency bugs
679cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            // in ThreadGroups
680cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            Thread t = new Thread(aGroup, name) {
681cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                @Override
682cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                public void run() {
683cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    ThreadGroup newGroup = new ThreadGroup(aGroup, name);
684cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    allCreated.addElement(newGroup);
685cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    asyncBuildRandomTreeUnder(newGroup, depth - 1, allCreated);
686cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                }
687cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            };
688cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            t.start();
689cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
690cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
691cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
692cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
693071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    private static Vector<ThreadGroup> asyncBuildRandomTreeUnder(final ThreadGroup aGroup,
694cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            final int depth) {
695cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        Vector<ThreadGroup> result = new Vector<ThreadGroup>();
696cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        asyncBuildRandomTreeUnder(aGroup, depth, result);
697cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        return result;
698cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
699cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
700cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
701071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    private static ThreadGroup[] groups(ThreadGroup parent) {
702cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // No API to get the count of immediate children only ?
703cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        int count = parent.activeGroupCount();
704cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup[] all = new ThreadGroup[count];
705cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        parent.enumerate(all, false);
706cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        // Now we may have nulls in the array, we must find the actual size
707cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        int actualSize = 0;
708cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        for (; actualSize < all.length; actualSize++) {
709cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            if (all[actualSize] == null) {
710cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                break;
711cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
712cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
713cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        ThreadGroup[] result;
714cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        if (actualSize == all.length) {
715cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            result = all;
716cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        } else {
717cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            result = new ThreadGroup[actualSize];
718cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            System.arraycopy(all, 0, result, 0, actualSize);
719cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
720cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
721cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        return result;
722cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
723cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
724cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
725071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    private static int random(int max) {
726cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        return 1 + ((new Object()).hashCode() % max);
727cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
728cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
729071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    private static Vector<ThreadGroup> buildRandomTreeUnder(ThreadGroup aGroup, int depth) {
730cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        Vector<ThreadGroup> result = asyncBuildRandomTreeUnder(aGroup, depth);
731cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        while (true) {
732cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            int sizeBefore = result.size();
733cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            try {
734cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                Thread.sleep(1000);
735cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                int sizeAfter = result.size();
736cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                // If no activity for a while, we assume async building may be
737cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                // done.
738cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                if (sizeBefore == sizeAfter) {
739cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    // It can only be done if no more threads. Unfortunately we
740cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    // are relying on this API to work as well.
741cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    // If it does not, we may loop forever.
742cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    if (aGroup.activeCount() == 0) {
743cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                        break;
744cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                    }
745cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                }
746cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            } catch (InterruptedException e) {
747cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
748cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
749cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        return result;
750cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
751cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
752cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
753071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    private static boolean arrayIncludes(Object[] array, Object toTest) {
754cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        for (Object element : array) {
755cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            if (element == toTest) {
756cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath                return true;
757cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath            }
758cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        }
759cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath        return false;
760cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
761cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
762071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    private static void waitForThreadToDieUninterrupted(Thread thread) {
763071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        try {
764071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            thread.join();
765071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        } catch (InterruptedException ie) {
766071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            fail("Should not have been interrupted");
767071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        }
768cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
769cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
770071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller    private static class TestThreadDefaultUncaughtExceptionHandler
771071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            implements Thread.UncaughtExceptionHandler {
772cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath
773071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        private boolean called;
774071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        private Throwable ex;
775071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        private Thread thread;
776071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller
777071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        @Override
778071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        public void uncaughtException(Thread thread, Throwable ex) {
779071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            this.called = true;
780071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            this.thread = thread;
781071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            this.ex = ex;
782071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        }
783071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller
784071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        public void assertWasCalled(Thread thread, Throwable ex) {
785071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            assertTrue(called);
786071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            assertSame(this.thread, thread);
787071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            assertSame(this.ex, ex);
788071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        }
789071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller
790071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        public void assertWasNotCalled() {
791071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller            assertFalse(called);
792071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller        }
793cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath    }
794071ba8eb49cc6ea63dc913aab145076dbeff0049Neil Fuller
795cb318c6f4fe5b0e20099fa85f1b95ccb2d24119fNarayan Kamath}
796