1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package libcore.java.lang;
18
19import java.lang.Thread.UncaughtExceptionHandler;
20import java.util.concurrent.atomic.AtomicInteger;
21import java.util.concurrent.atomic.AtomicBoolean;
22import java.util.concurrent.locks.ReentrantLock;
23
24import junit.framework.Assert;
25import junit.framework.TestCase;
26
27import org.mockito.InOrder;
28import org.mockito.Mockito;
29
30import libcore.java.lang.ref.FinalizationTester;
31
32public final class ThreadTest extends TestCase {
33    static {
34        System.loadLibrary("javacoretests");
35    }
36
37    /**
38     * getContextClassLoader returned a non-application class loader.
39     * http://code.google.com/p/android/issues/detail?id=5697
40     */
41    public void testJavaContextClassLoader() throws Exception {
42        Assert.assertNotNull("Must have a Java context ClassLoader",
43                Thread.currentThread().getContextClassLoader());
44    }
45
46    public void testLeakingStartedThreads() {
47        final AtomicInteger finalizedThreadsCount = new AtomicInteger();
48        for (int i = 0; true; i++) {
49            try {
50                newThread(finalizedThreadsCount, 1024 << i).start();
51            } catch (OutOfMemoryError expected) {
52                break;
53            }
54        }
55        FinalizationTester.induceFinalization();
56        assertTrue("Started threads were never finalized!", finalizedThreadsCount.get() > 0);
57    }
58
59    public void testLeakingUnstartedThreads() {
60        final AtomicInteger finalizedThreadsCount = new AtomicInteger();
61        for (int i = 0; true; i++) {
62            try {
63                newThread(finalizedThreadsCount, 1024 << i);
64            } catch (OutOfMemoryError expected) {
65                break;
66            }
67        }
68        FinalizationTester.induceFinalization();
69        assertTrue("Unstarted threads were never finalized!", finalizedThreadsCount.get() > 0);
70    }
71
72    public void testThreadSleep() throws Exception {
73        int millis = 1000;
74        long start = System.currentTimeMillis();
75
76        Thread.sleep(millis);
77
78        long elapsed = System.currentTimeMillis() - start;
79        long offBy = Math.abs(elapsed - millis);
80
81        assertTrue("Actual sleep off by " + offBy + " ms", offBy <= 250);
82    }
83
84    public void testThreadInterrupted() throws Exception {
85        Thread.currentThread().interrupt();
86        try {
87            Thread.sleep(0);
88            fail();
89        } catch (InterruptedException e) {
90            assertFalse(Thread.currentThread().isInterrupted());
91        }
92    }
93
94    public void testThreadSleepIllegalArguments() throws Exception {
95
96        try {
97            Thread.sleep(-1);
98            fail();
99        } catch (IllegalArgumentException expected) {
100        }
101
102        try {
103            Thread.sleep(0, -1);
104            fail();
105        } catch (IllegalArgumentException expected) {
106        }
107
108        try {
109            Thread.sleep(0, 1000000);
110            fail();
111        } catch (IllegalArgumentException expected) {
112        }
113    }
114
115    public void testThreadWakeup() throws Exception {
116        WakeupTestThread t1 = new WakeupTestThread();
117        WakeupTestThread t2 = new WakeupTestThread();
118
119        t1.start();
120        t2.start();
121        assertTrue("Threads already finished", !t1.done && !t2.done);
122
123        t1.interrupt();
124        t2.interrupt();
125
126        Thread.sleep(1000);
127        assertTrue("Threads did not finish", t1.done && t2.done);
128    }
129
130    public void testContextClassLoaderIsNotNull() {
131        assertNotNull(Thread.currentThread().getContextClassLoader());
132    }
133
134    public void testContextClassLoaderIsInherited() {
135        Thread other = new Thread();
136        assertSame(Thread.currentThread().getContextClassLoader(), other.getContextClassLoader());
137    }
138
139    public void testUncaughtExceptionPreHandler_calledBeforeDefaultHandler() {
140        UncaughtExceptionHandler initialHandler = Mockito.mock(UncaughtExceptionHandler.class);
141        UncaughtExceptionHandler defaultHandler = Mockito.mock(UncaughtExceptionHandler.class);
142        InOrder inOrder = Mockito.inOrder(initialHandler, defaultHandler);
143
144        UncaughtExceptionHandler originalDefaultHandler
145                = Thread.getDefaultUncaughtExceptionHandler();
146        Thread.setUncaughtExceptionPreHandler(initialHandler);
147        Thread.setDefaultUncaughtExceptionHandler(defaultHandler);
148        try {
149            Thread t = new Thread();
150            Throwable e = new Throwable();
151            t.dispatchUncaughtException(e);
152            inOrder.verify(initialHandler).uncaughtException(t, e);
153            inOrder.verify(defaultHandler).uncaughtException(t, e);
154            inOrder.verifyNoMoreInteractions();
155        } finally {
156            Thread.setDefaultUncaughtExceptionHandler(originalDefaultHandler);
157            Thread.setUncaughtExceptionPreHandler(null);
158        }
159    }
160
161    public void testUncaughtExceptionPreHandler_noDefaultHandler() {
162        UncaughtExceptionHandler initialHandler = Mockito.mock(UncaughtExceptionHandler.class);
163        UncaughtExceptionHandler originalDefaultHandler
164                = Thread.getDefaultUncaughtExceptionHandler();
165        Thread.setUncaughtExceptionPreHandler(initialHandler);
166        Thread.setDefaultUncaughtExceptionHandler(null);
167        try {
168            Thread t = new Thread();
169            Throwable e = new Throwable();
170            t.dispatchUncaughtException(e);
171            Mockito.verify(initialHandler).uncaughtException(t, e);
172            Mockito.verifyNoMoreInteractions(initialHandler);
173        } finally {
174            Thread.setDefaultUncaughtExceptionHandler(originalDefaultHandler);
175            Thread.setUncaughtExceptionPreHandler(null);
176        }
177    }
178
179    /**
180     * Thread.getStackTrace() is broken. http://b/1252043
181     */
182    public void testGetStackTrace() throws Exception {
183        Thread t1 = new Thread("t1") {
184            @Override public void run() {
185                doSomething();
186            }
187            public void doSomething() {
188                try {
189                    Thread.sleep(4000);
190                } catch (InterruptedException ignored) {
191                }
192            }
193        };
194        t1.start();
195        Thread.sleep(1000);
196        StackTraceElement[] traces = t1.getStackTrace();
197        StackTraceElement trace = traces[traces.length - 2];
198
199        // Expect to find MyThread.doSomething in the trace
200        assertTrue(trace.getClassName().contains("ThreadTest")
201                && trace.getMethodName().equals("doSomething"));
202        t1.join();
203    }
204
205    public void testGetAllStackTracesIncludesAllGroups() throws Exception {
206        final AtomicInteger visibleTraces = new AtomicInteger();
207        ThreadGroup group = new ThreadGroup("1");
208        Thread t2 = new Thread(group, "t2") {
209            @Override public void run() {
210                visibleTraces.set(Thread.getAllStackTraces().size());
211            }
212        };
213        t2.start();
214        t2.join();
215
216        // Expect to see the traces of all threads (not just t2)
217        assertTrue("Must have traces for all threads", visibleTraces.get() > 1);
218    }
219
220    // http://b/27748318
221    public void testNativeThreadNames() throws Exception {
222        String testResult = nativeTestNativeThreadNames();
223        // Not using assertNull here because this results in a better error message.
224        if (testResult != null) {
225            fail(testResult);
226        }
227    }
228
229    // http://b/29746125
230    public void testParkUntilWithUnderflowValue() throws Exception {
231        final Thread current = Thread.currentThread();
232
233        // watchdog to unpark the tread in case it will be parked
234        AtomicBoolean afterPark = new AtomicBoolean(false);
235        AtomicBoolean wasParkedForLongTime = new AtomicBoolean(false);
236        Thread watchdog = new Thread() {
237            @Override public void run() {
238                try {
239                    sleep(5000);
240                } catch(InterruptedException expected) {}
241
242                if (!afterPark.get()) {
243                    wasParkedForLongTime.set(true);
244                    current.unpark$();
245                }
246            }
247        };
248        watchdog.start();
249
250        // b/29746125 is caused by underflow: parkUntilArg - System.currentTimeMillis() > 0.
251        // parkUntil$ should return immediately for everyargument that's <=
252        // System.currentTimeMillis().
253        current.parkUntil$(Long.MIN_VALUE);
254        if (wasParkedForLongTime.get()) {
255            fail("Current thread was parked, but was expected to return immediately");
256        }
257        afterPark.set(true);
258        watchdog.interrupt();
259        watchdog.join();
260    }
261
262    /**
263     * Check that call Thread.start for already started thread
264     * throws {@code IllegalThreadStateException}
265     */
266    public void testThreadDoubleStart() {
267        final ReentrantLock lock = new ReentrantLock();
268        Thread thread = new Thread() {
269            public void run() {
270                // Lock should be acquired by the main thread and
271                // this thread should block on this operation.
272                lock.lock();
273            }
274        };
275        // Acquire lock to ensure that new thread is not finished
276        // when we call start() second time.
277        lock.lock();
278        try {
279            thread.start();
280            try {
281                thread.start();
282                fail();
283            } catch (IllegalThreadStateException expected) {
284            }
285        } finally {
286            lock.unlock();
287        }
288        try {
289            thread.join();
290        } catch (InterruptedException ignored) {
291        }
292    }
293
294    /**
295     * Check that call Thread.start for already finished thread
296     * throws {@code IllegalThreadStateException}
297     */
298    public void testThreadRestart() {
299        Thread thread = new Thread();
300        thread.start();
301        try {
302            thread.join();
303        } catch (InterruptedException ignored) {
304        }
305        try {
306            thread.start();
307            fail();
308        } catch (IllegalThreadStateException expected) {
309        }
310    }
311
312    // This method returns {@code null} if all tests pass, or a non-null String containing
313    // failure details if an error occured.
314    private static native String nativeTestNativeThreadNames();
315
316    private Thread newThread(final AtomicInteger finalizedThreadsCount, final int size) {
317        return new Thread() {
318            long[] memoryPressure = new long[size];
319            @Override protected void finalize() throws Throwable {
320                super.finalize();
321                finalizedThreadsCount.incrementAndGet();
322            }
323        };
324    }
325
326    private class WakeupTestThread extends Thread {
327        public boolean done;
328
329        public void run() {
330            done = false;
331
332            // Sleep for a while (1 min)
333            try {
334                Thread.sleep(60000);
335            } catch (InterruptedException ignored) {
336            }
337
338            done = true;
339        }
340    }
341}
342