/*
 *  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.tests.java.lang;

import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Map;

public class ThreadTest extends junit.framework.TestCase {

    static class SimpleThread implements Runnable {
        int delay;

        public void run() {
            try {
                synchronized (this) {
                    this.notify();
                    this.wait(delay);
                }
            } catch (InterruptedException e) {
                return;
            }

        }

        public SimpleThread(int d) {
            if (d >= 0)
                delay = d;
        }
    }

    static class YieldThread implements Runnable {
        volatile int delay;

        public void run() {
            int x = 0;
            while (true) {
                ++x;
            }
        }

        public YieldThread(int d) {
            if (d >= 0)
                delay = d;
        }
    }

    static class ResSupThread implements Runnable {
        Thread parent;

        volatile int checkVal = -1;

        public void run() {
            try {
                synchronized (this) {
                    this.notify();
                }
                while (true) {
                    checkVal++;
                    zz();
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                return;
            } catch (BogusException e) {
                try {
                    // Give parent a chance to sleep
                    Thread.sleep(500);
                } catch (InterruptedException x) {
                }
                parent.interrupt();
                while (!Thread.currentThread().isInterrupted()) {
                    // Don't hog the CPU
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException x) {
                        // This is what we've been waiting for...don't throw it
                        // away!
                        break;
                    }
                }
            }
        }

        public void zz() throws BogusException {
        }

        public ResSupThread(Thread t) {
            parent = t;
        }

        public synchronized int getCheckVal() {
            return checkVal;
        }
    }

    static class BogusException extends Throwable {

        private static final long serialVersionUID = 1L;

        public BogusException(String s) {
            super(s);
        }
    }

    Thread st, ct, spinner;

    /**
     * java.lang.Thread#Thread(java.lang.Runnable)
     */
    public void test_ConstructorLjava_lang_Runnable() {
        // Test for method java.lang.Thread(java.lang.Runnable)
        ct = new Thread(new SimpleThread(10));
        ct.start();
    }

    /**
     * java.lang.Thread#Thread(java.lang.Runnable, java.lang.String)
     */
    public void test_ConstructorLjava_lang_RunnableLjava_lang_String() {
        // Test for method java.lang.Thread(java.lang.Runnable,
        // java.lang.String)
        Thread st1 = new Thread(new SimpleThread(1), "SimpleThread1");
        assertEquals("Constructed thread with incorrect thread name", "SimpleThread1", st1
                .getName());
        st1.start();
    }

    /**
     * java.lang.Thread#Thread(java.lang.String)
     */
    public void test_ConstructorLjava_lang_String() {
        // Test for method java.lang.Thread(java.lang.String)
        Thread t = new Thread("Testing");
        assertEquals("Created tread with incorrect name",
                "Testing", t.getName());
        t.start();
    }

    /**
     * java.lang.Thread#Thread(java.lang.ThreadGroup, java.lang.Runnable)
     */
    public void test_ConstructorLjava_lang_ThreadGroupLjava_lang_Runnable() {
        // Test for method java.lang.Thread(java.lang.ThreadGroup,
        // java.lang.Runnable)
        ThreadGroup tg = new ThreadGroup("Test Group1");
        st = new Thread(tg, new SimpleThread(1), "SimpleThread2");
        assertTrue("Returned incorrect thread group", st.getThreadGroup() == tg);
        st.start();
        try {
            st.join();
        } catch (InterruptedException e) {
        }
        tg.destroy();
    }

    /**
     * java.lang.Thread#Thread(java.lang.ThreadGroup, java.lang.Runnable,
     *java.lang.String)
     */
    @SuppressWarnings("DeadThread")
    public void test_ConstructorLjava_lang_ThreadGroupLjava_lang_RunnableLjava_lang_String() {
        // Test for method java.lang.Thread(java.lang.ThreadGroup,
        // java.lang.Runnable, java.lang.String)
        ThreadGroup tg = new ThreadGroup("Test Group2");
        st = new Thread(tg, new SimpleThread(1), "SimpleThread3");
        assertTrue("Constructed incorrect thread", (st.getThreadGroup() == tg)
                && st.getName().equals("SimpleThread3"));
        st.start();
        try {
            st.join();
        } catch (InterruptedException e) {
        }
        tg.destroy();

        Runnable r = new Runnable() {
            public void run() {
            }
        };

        ThreadGroup foo = null;
        try {
            new Thread(foo = new ThreadGroup("foo"), r, null);
            // Should not get here
            fail("Null cannot be accepted as Thread name");
        } catch (NullPointerException npe) {
            assertTrue("Null cannot be accepted as Thread name", true);
            foo.destroy();
        }

    }

    /**
     * java.lang.Thread#Thread(java.lang.ThreadGroup, java.lang.String)
     */
    public void test_ConstructorLjava_lang_ThreadGroupLjava_lang_String() {
        // Test for method java.lang.Thread(java.lang.ThreadGroup,
        // java.lang.String)
        st = new Thread(new SimpleThread(1), "SimpleThread4");
        assertEquals("Returned incorrect thread name",
                "SimpleThread4", st.getName());
        st.start();
    }

    /**
     * java.lang.Thread#activeCount()
     */
    public void test_activeCount() {
        // Test for method int java.lang.Thread.activeCount()
        Thread t = new Thread(new SimpleThread(10));
        int active = 0;
        synchronized (t) {
            t.start();
            active = Thread.activeCount();
        }
        assertTrue("Incorrect activeCount for current group: " + active, active > 1);
        try {
            t.join();
        } catch (InterruptedException e) {
        }
    }

    /**
     * java.lang.Thread#checkAccess()
     */
    public void test_checkAccess() {
        // Test for method void java.lang.Thread.checkAccess()
        ThreadGroup tg = new ThreadGroup("Test Group3");
        try {
            st = new Thread(tg, new SimpleThread(1), "SimpleThread5");
            st.checkAccess();
            assertTrue("CheckAccess passed", true);
        } catch (SecurityException e) {
            fail("CheckAccess failed : " + e.getMessage());
        }
        st.start();
        try {
            st.join();
        } catch (InterruptedException e) {
        }
        tg.destroy();
    }

    /**
     * java.lang.Thread#countStackFrames()
     */
    @SuppressWarnings("deprecation")
    public void test_countStackFrames() {
        /*
         * Thread.countStackFrames() is unpredictable, so we just test that it
         * doesn't throw an exception.
         */
        Thread.currentThread().countStackFrames();
    }

    /**
     * java.lang.Thread#currentThread()
     */
    public void test_currentThread() {
        assertNotNull(Thread.currentThread());
    }

    public void test_destroy_throwsUnsupportedOperationException() {
        try {
            new Thread().destroy();
            fail();
        } catch (UnsupportedOperationException expected) {
        }
    }

    /**
     * java.lang.Thread#enumerate(java.lang.Thread[])
     */
    public void test_enumerate$Ljava_lang_Thread() {
        // Test for method int java.lang.Thread.enumerate(java.lang.Thread [])
        // The test has been updated according to HARMONY-1974 JIRA issue.

        class MyThread extends Thread {
            MyThread(ThreadGroup tg, String name) {
                super(tg, name);
            }

            boolean failed = false;
            String failMessage = null;

            public void run() {
                SimpleThread st1 = null;
                SimpleThread st2 = null;
                ThreadGroup mytg = null;
                Thread firstOne = null;
                Thread secondOne = null;
                try {
                    int arrayLength = 10;
                    Thread[] tarray = new Thread[arrayLength];
                    st1 = new SimpleThread(-1);
                    st2 = new SimpleThread(-1);
                    mytg = new ThreadGroup("jp");
                    firstOne = new Thread(mytg, st1, "firstOne2");
                    secondOne = new Thread(mytg, st2, "secondOne1");
                    int count = Thread.enumerate(tarray);
                    assertEquals("Incorrect value returned1",
                            1, count);
                    synchronized (st1) {
                        firstOne.start();
                        try {
                            st1.wait();
                        } catch (InterruptedException e) {
                        }
                    }
                    count = Thread.enumerate(tarray);
                    assertEquals("Incorrect value returned2",
                            2, count);
                    synchronized (st2) {
                        secondOne.start();
                        try {
                            st2.wait();
                        } catch (InterruptedException e) {
                        }
                    }
                    count = Thread.enumerate(tarray);
                    assertEquals("Incorrect value returned3",
                            3, count);
                } catch (junit.framework.AssertionFailedError e) {
                    failed = true;
                    failMessage = e.getMessage();
                } finally {
                    synchronized (st1) {
                        firstOne.interrupt();
                    }
                    synchronized (st2) {
                        secondOne.interrupt();
                    }
                    try {
                        firstOne.join();
                        secondOne.join();
                    } catch (InterruptedException e) {
                    }
                    mytg.destroy();
                }
            }
        }
        ;

        ThreadGroup tg = new ThreadGroup("tg");
        MyThread t = new MyThread(tg, "top");
        t.start();
        try {
            t.join();
        } catch (InterruptedException e) {
            fail("Unexpected interrupt");
        } finally {
            tg.destroy();
        }
        assertFalse(t.failMessage, t.failed);
    }

    /**
     * java.lang.Thread#getContextClassLoader()
     */
    public void test_getContextClassLoader() {
        // Test for method java.lang.ClassLoader
        // java.lang.Thread.getContextClassLoader()
        Thread t = new Thread();
        assertTrue("Incorrect class loader returned",
                t.getContextClassLoader() == Thread.currentThread()
                        .getContextClassLoader());
        t.start();

    }

    /**
     * java.lang.Thread#getName()
     */
    public void test_getName() {
        // Test for method java.lang.String java.lang.Thread.getName()
        st = new Thread(new SimpleThread(1), "SimpleThread6");
        assertEquals("Returned incorrect thread name",
                "SimpleThread6", st.getName());
        st.start();
    }

    /**
     * java.lang.Thread#getPriority()
     */
    public void test_getPriority() {
        // Test for method int java.lang.Thread.getPriority()
        st = new Thread(new SimpleThread(1));
        st.setPriority(Thread.MAX_PRIORITY);
        assertTrue("Returned incorrect thread priority",
                st.getPriority() == Thread.MAX_PRIORITY);
        st.start();
    }

    /**
     * java.lang.Thread#getThreadGroup()
     */
    public void test_getThreadGroup() {
        // Test for method java.lang.ThreadGroup
        // java.lang.Thread.getThreadGroup()
        ThreadGroup tg = new ThreadGroup("Test Group4");
        st = new Thread(tg, new SimpleThread(1), "SimpleThread8");
        assertTrue("Returned incorrect thread group", st.getThreadGroup() == tg);
        st.start();
        try {
            st.join();
        } catch (InterruptedException e) {
        }
        assertNull("group should be null", st.getThreadGroup());
        assertNotNull("toString() should not be null", st.toString());
        tg.destroy();

        final Object lock = new Object();
        Thread t = new Thread() {
            @Override
            public void run() {
                synchronized (lock) {
                    lock.notifyAll();
                }
            }
        };
        synchronized (lock) {
            t.start();
            try {
                lock.wait();
            } catch (InterruptedException e) {
            }
        }
        int running = 0;
        while (t.isAlive())
            running++;
        ThreadGroup group = t.getThreadGroup();
        assertNull("ThreadGroup is not null", group);
    }

    /**
     * java.lang.Thread#interrupt()
     */
    public void test_interrupt() {
        // Test for method void java.lang.Thread.interrupt()
        final Object lock = new Object();
        class ChildThread1 extends Thread {
            Thread parent;

            boolean sync;

            @Override
            public void run() {
                if (sync) {
                    synchronized (lock) {
                        lock.notify();
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                        }
                    }
                }
                parent.interrupt();
            }

            public ChildThread1(Thread p, String name, boolean sync) {
                super(name);
                parent = p;
                this.sync = sync;
            }
        }
        boolean interrupted = false;
        try {
            ct = new ChildThread1(Thread.currentThread(), "Interrupt Test1",
                    false);
            synchronized (lock) {
                ct.start();
                lock.wait();
            }
        } catch (InterruptedException e) {
            interrupted = true;
        }
        assertTrue("Failed to Interrupt thread1", interrupted);

        interrupted = false;
        try {
            ct = new ChildThread1(Thread.currentThread(), "Interrupt Test2",
                    true);
            synchronized (lock) {
                ct.start();
                lock.wait();
                lock.notify();
            }
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            interrupted = true;
        }
        assertTrue("Failed to Interrupt thread2", interrupted);

    }

    /**
     * java.lang.Thread#interrupted()
     */
    public void test_interrupted() {
        assertFalse("Interrupted returned true for non-interrupted thread", Thread
                .interrupted());
        Thread.currentThread().interrupt();
        assertTrue("Interrupted returned true for non-interrupted thread", Thread.interrupted());
        assertFalse("Failed to clear interrupted flag", Thread.interrupted());
    }

    /**
     * java.lang.Thread#isAlive()
     */
    public void test_isAlive() {
        // Test for method boolean java.lang.Thread.isAlive()
        SimpleThread simple;
        st = new Thread(simple = new SimpleThread(500));
        assertFalse("A thread that wasn't started is alive.", st.isAlive());
        synchronized (simple) {
            st.start();
            try {
                simple.wait();
            } catch (InterruptedException e) {
            }
        }
        assertTrue("Started thread returned false", st.isAlive());
        try {
            st.join();
        } catch (InterruptedException e) {
            fail("Thread did not die");
        }
        assertTrue("Stopped thread returned true", !st.isAlive());
    }

    /**
     * java.lang.Thread#isDaemon()
     */
    public void test_isDaemon() {
        // Test for method boolean java.lang.Thread.isDaemon()
        st = new Thread(new SimpleThread(1), "SimpleThread10");
        assertEquals(Thread.currentThread().isDaemon(), st.isDaemon());
        st.setDaemon(true);
        assertTrue("Daemon thread returned false", st.isDaemon());
        st.start();
        try {
            st.join();
        } catch (InterruptedException ie) {
            fail();
        }
    }

    /**
     * java.lang.Thread#isInterrupted()
     */
    public void test_isInterrupted() {
        // Test for method boolean java.lang.Thread.isInterrupted()
        class SpinThread implements Runnable {
            public volatile boolean done = false;

            public void run() {
                while (!Thread.currentThread().isInterrupted())
                    ;
                while (!done)
                    ;
            }
        }

        SpinThread spin = new SpinThread();
        spinner = new Thread(spin);
        spinner.start();
        Thread.yield();
        try {
            assertTrue("Non-Interrupted thread returned true", !spinner
                    .isInterrupted());
            spinner.interrupt();
            assertTrue("Interrupted thread returned false", spinner
                    .isInterrupted());
            spin.done = true;
        } finally {
            spinner.interrupt();
            spin.done = true;
        }
    }

    /**
     * java.lang.Thread#join()
     */
    public void test_join() {
        // Test for method void java.lang.Thread.join()
        SimpleThread simple;
        try {
            st = new Thread(simple = new SimpleThread(100));
            // cause isAlive() to be compiled by the JIT, as it must be called
            // within 100ms below.
            assertTrue("Thread is alive", !st.isAlive());
            synchronized (simple) {
                st.start();
                simple.wait();
            }
            st.join();
        } catch (InterruptedException e) {
            fail("Join failed ");
        }
        assertTrue("Joined thread is still alive", !st.isAlive());
        boolean result = true;
        Thread th = new Thread("test");
        try {
            th.join();
        } catch (InterruptedException e) {
            result = false;
        }
        assertTrue("Hung joining a non-started thread", result);
        th.start();
    }

    /**
     * java.lang.Thread#join(long)
     */
    public void test_joinJ() {
        // Test for method void java.lang.Thread.join(long)
        SimpleThread simple;
        try {
            st = new Thread(simple = new SimpleThread(1000), "SimpleThread12");
            // cause isAlive() to be compiled by the JIT, as it must be called
            // within 100ms below.
            assertTrue("Thread is alive", !st.isAlive());
            synchronized (simple) {
                st.start();
                simple.wait();
            }
            st.join(10);
        } catch (InterruptedException e) {
            fail("Join failed ");
        }
        assertTrue("Join failed to timeout", st.isAlive());

        st.interrupt();
        try {
            st = new Thread(simple = new SimpleThread(100), "SimpleThread13");
            synchronized (simple) {
                st.start();
                simple.wait();
            }
            st.join(1000);
        } catch (InterruptedException e) {
            fail("Join failed : " + e.getMessage());
            return;
        }
        assertTrue("Joined thread is still alive", !st.isAlive());

        final Object lock = new Object();
        final Thread main = Thread.currentThread();
        Thread killer = new Thread(new Runnable() {
            public void run() {
                try {
                    synchronized (lock) {
                        lock.notify();
                    }
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    return;
                }
                main.interrupt();
            }
        });
        boolean result = true;
        Thread th = new Thread("test");
        try {
            synchronized (lock) {
                killer.start();
                lock.wait();
            }
            th.join(200);
        } catch (InterruptedException e) {
            result = false;
        }
        killer.interrupt();
        assertTrue("Hung joining a non-started thread", result);
        th.start();
    }

    /**
     * java.lang.Thread#join(long, int)
     */
    public void test_joinJI() throws Exception {
        // Test for method void java.lang.Thread.join(long, int)
        SimpleThread simple;
        st = new Thread(simple = new SimpleThread(1000), "Squawk1");
        assertTrue("Thread is alive", !st.isAlive());
        synchronized (simple) {
            st.start();
            simple.wait();
        }

        long firstRead = System.currentTimeMillis();
        st.join(100, 999999);
        long secondRead = System.currentTimeMillis();
        assertTrue("Did not join by appropriate time: " + secondRead + "-"
                + firstRead + "=" + (secondRead - firstRead), secondRead
                - firstRead <= 300);
        assertTrue("Joined thread is not alive", st.isAlive());
        st.interrupt();

        final Object lock = new Object();
        final Thread main = Thread.currentThread();
        Thread killer = new Thread(new Runnable() {
            public void run() {
                try {
                    synchronized (lock) {
                        lock.notify();
                    }
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    return;
                }
                main.interrupt();
            }
        });
        boolean result = true;
        Thread th = new Thread("test");
        try {
            synchronized (lock) {
                killer.start();
                lock.wait();
            }
            th.join(200, 20);
        } catch (InterruptedException e) {
            result = false;
        }
        killer.interrupt();
        assertTrue("Hung joining a non-started thread", result);
        th.start();
    }

    /**
     * java.lang.Thread#run()
     */
    public void test_run() {
        // Test for method void java.lang.Thread.run()
        class RunThread implements Runnable {
            boolean didThreadRun = false;

            public void run() {
                didThreadRun = true;
            }
        }
        RunThread rt = new RunThread();
        Thread t = new Thread(rt);
        try {
            t.start();
            int count = 0;
            while (!rt.didThreadRun && count < 20) {
                Thread.sleep(100);
                count++;
            }
            assertTrue("Thread did not run", rt.didThreadRun);
            t.join();
        } catch (InterruptedException e) {
            assertTrue("Joined thread was interrupted", true);
        }
        assertTrue("Joined thread is still alive", !t.isAlive());
    }

    /**
     * java.lang.Thread#setDaemon(boolean)
     */
    public void test_setDaemonZ() {
        // Test for method void java.lang.Thread.setDaemon(boolean)
        st = new Thread(new SimpleThread(1), "SimpleThread14");
        st.setDaemon(true);
        assertTrue("Failed to set thread as daemon thread", st.isDaemon());
        st.start();
    }

    /**
     * java.lang.Thread#setName(java.lang.String)
     */
    public void test_setNameLjava_lang_String() {
        // Test for method void java.lang.Thread.setName(java.lang.String)
        st = new Thread(new SimpleThread(1), "SimpleThread15");
        st.setName("Bogus Name");
        assertEquals("Failed to set thread name",
                "Bogus Name", st.getName());
        try {
            st.setName(null);
            fail("Null should not be accepted as a valid name");
        } catch (NullPointerException e) {
            // success
            assertTrue("Null should not be accepted as a valid name", true);
        }
        st.start();
    }

    /**
     * java.lang.Thread#setPriority(int)
     */
    public void test_setPriorityI() {
        // Test for method void java.lang.Thread.setPriority(int)
        st = new Thread(new SimpleThread(1));
        st.setPriority(Thread.MAX_PRIORITY);
        assertTrue("Failed to set priority",
                st.getPriority() == Thread.MAX_PRIORITY);
        st.start();
    }

    /**
     * java.lang.Thread#sleep(long)
     */
    public void test_sleepJ() {
        // Test for method void java.lang.Thread.sleep(long)

        // TODO : Test needs enhancing.
        long stime = 0, ftime = 0;
        try {
            stime = System.currentTimeMillis();
            Thread.sleep(1000);
            ftime = System.currentTimeMillis();
        } catch (InterruptedException e) {
            fail("Unexpected interrupt received");
        }
        assertTrue("Failed to sleep long enough", (ftime - stime) >= 800);
    }

    /**
     * java.lang.Thread#sleep(long, int)
     */
    public void test_sleepJI() {
        // Test for method void java.lang.Thread.sleep(long, int)

        // TODO : Test needs revisiting.
        long stime = 0, ftime = 0;
        try {
            stime = System.currentTimeMillis();
            Thread.sleep(1000, 999999);
            ftime = System.currentTimeMillis();
        } catch (InterruptedException e) {
            fail("Unexpected interrupt received");
        }
        long result = ftime - stime;
        assertTrue("Failed to sleep long enough: " + result, result >= 900
                && result <= 1100);
    }

    /**
     * java.lang.Thread#start()
     */
    public void test_start() {
        // Test for method void java.lang.Thread.start()
        try {
            ResSupThread t = new ResSupThread(Thread.currentThread());
            synchronized (t) {
                ct = new Thread(t, "Interrupt Test4");
                ct.start();
                t.wait();
            }
            assertTrue("Thread is not running1", ct.isAlive());
            // Let the child thread get going.
            int orgval = t.getCheckVal();
            Thread.sleep(150);
            assertTrue("Thread is not running2", orgval != t.getCheckVal());
            ct.interrupt();
        } catch (InterruptedException e) {
            fail("Unexpected interrupt occurred");
        }
    }

    /**
     * java.lang.Thread#toString()
     */
    public void test_toString() {
        // Test for method java.lang.String java.lang.Thread.toString()
        ThreadGroup tg = new ThreadGroup("Test Group5");
        st = new Thread(tg, new SimpleThread(1), "SimpleThread17");
        final String stString = st.toString();
        final String expected = "Thread[SimpleThread17,5,Test Group5]";
        assertTrue("Returned incorrect string: " + stString + "\t(expecting :"
                + expected + ")", stString.equals(expected));
        st.start();
        try {
            st.join();
        } catch (InterruptedException e) {
        }
        tg.destroy();
    }

    /**
     * java.lang.Thread#getAllStackTraces()
     */
    public void test_getAllStackTraces() {
        Map<Thread, StackTraceElement[]> stMap = Thread.getAllStackTraces();
        assertNotNull(stMap);
        //TODO add security-based tests
    }

    /**
     * java.lang.Thread#getDefaultUncaughtExceptionHandler
     * java.lang.Thread#setDefaultUncaughtExceptionHandler
     */
    public void test_get_setDefaultUncaughtExceptionHandler() {
        class Handler implements UncaughtExceptionHandler {
            public void uncaughtException(Thread thread, Throwable ex) {
            }
        }

        final Handler handler = new Handler();
        Thread.setDefaultUncaughtExceptionHandler(handler);
        assertSame(handler, Thread.getDefaultUncaughtExceptionHandler());

        Thread.setDefaultUncaughtExceptionHandler(null);
        assertNull(Thread.getDefaultUncaughtExceptionHandler());
        //TODO add security-based tests
    }

    /**
     * java.lang.Thread#getStackTrace()
     */
    public void test_getStackTrace() {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();

        assertNotNull(stackTrace);

        stack_trace_loop:
        {
            for (int i = 0; i < stackTrace.length; i++) {
                StackTraceElement e = stackTrace[i];
                if (getClass().getName().equals(e.getClassName())) {
                    if ("test_getStackTrace".equals(e.getMethodName())) {
                        break stack_trace_loop;
                    }
                }
            }
            fail("class and method not found in stack trace");
        }

        //TODO add security-based tests
    }

    /**
     * java.lang.Thread#getState()
     */
    public void test_getState() {
        Thread.State state = Thread.currentThread().getState();
        assertNotNull(state);
        assertEquals(Thread.State.RUNNABLE, state);
        //TODO add additional state tests
    }

    /**
     * java.lang.Thread#getUncaughtExceptionHandler
     * java.lang.Thread#setUncaughtExceptionHandler
     */
    public void test_get_setUncaughtExceptionHandler() {
        class Handler implements UncaughtExceptionHandler {
            public void uncaughtException(Thread thread, Throwable ex) {
            }
        }

        final Handler handler = new Handler();
        Thread.currentThread().setUncaughtExceptionHandler(handler);
        assertSame(handler, Thread.currentThread().getUncaughtExceptionHandler());

        Thread.currentThread().setUncaughtExceptionHandler(null);

        //TODO add security-based tests
    }

    /**
     * java.lang.Thread#getId()
     */
    public void test_getId() {
        assertTrue("current thread's ID is not positive", Thread.currentThread().getId() > 0);

        //check all the current threads for positive IDs
        Map<Thread, StackTraceElement[]> stMap = Thread.getAllStackTraces();
        for (Thread thread : stMap.keySet()) {
            assertTrue("thread's ID is not positive: " + thread.getName(), thread.getId() > 0);
        }
    }


    @Override
    protected void tearDown() {
        try {
            if (st != null)
                st.interrupt();
        } catch (Exception e) {
        }
        try {
            if (spinner != null)
                spinner.interrupt();
        } catch (Exception e) {
        }
        try {
            if (ct != null)
                ct.interrupt();
        } catch (Exception e) {
        }

        try {
            spinner = null;
            st = null;
            ct = null;
            System.runFinalization();
        } catch (Exception e) {
        }
    }
}
