diff options
Diffstat (limited to 'harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TimerTest.java')
-rw-r--r-- | harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TimerTest.java | 986 |
1 files changed, 986 insertions, 0 deletions
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TimerTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TimerTest.java new file mode 100644 index 0000000..85e7c6a --- /dev/null +++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TimerTest.java @@ -0,0 +1,986 @@ +/* + * 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.util; + +import java.lang.Thread.UncaughtExceptionHandler; +import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicReference; +import junit.framework.TestCase; + +public class TimerTest extends TestCase { + + int timerCounter = 0; + + private final Object sync = new Object(); + + /** + * Warning: These tests have the possibility to leave a VM hanging if the + * Timer is not cancelled. + */ + class TimerTestTask extends TimerTask { + int wasRun = 0; + + // Should we sleep for 200 ms each run()? + boolean sleepInRun = false; + + // Should we increment the timerCounter? + boolean incrementCount = false; + + // Should we terminate the timer at a specific timerCounter? + int terminateCount = -1; + + // The timer we belong to + Timer timer = null; + + public TimerTestTask() { + } + + public TimerTestTask(Timer t) { + timer = t; + } + + public void run() { + synchronized (this) { + wasRun++; + } + if (incrementCount) { + timerCounter++; + } + if (terminateCount == timerCounter && timer != null) { + timer.cancel(); + } + if (sleepInRun) { + try { + Thread.sleep(200); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + synchronized (sync) { + sync.notify(); + } + } + + public synchronized int wasRun() { + return wasRun; + } + + public void sleepInRun(boolean sleepInRun) { + this.sleepInRun = sleepInRun; + } + + public void incrementCount(boolean incrementCount) { + this.incrementCount = incrementCount; + } + + public void terminateCount(int terminateCount) { + this.terminateCount = terminateCount; + } + } + + private void awaitRun(TimerTestTask task) throws Exception { + while (task.wasRun() == 0) { + Thread.sleep(150); + } + } + + /** + * java.util.Timer#Timer(boolean) + */ + public void test_ConstructorZ() throws Exception { + Timer t = null; + try { + // Ensure a task is run + t = new Timer(true); + TimerTestTask testTask = new TimerTestTask(); + t.schedule(testTask, 200); + awaitRun(testTask); + t.cancel(); + } finally { + if (t != null) + t.cancel(); + } + + } + + /** + * java.util.Timer#Timer() + */ + public void test_Constructor() throws Exception { + Timer t = null; + try { + // Ensure a task is run + t = new Timer(); + TimerTestTask testTask = new TimerTestTask(); + t.schedule(testTask, 200); + awaitRun(testTask); + t.cancel(); + } finally { + if (t != null) + t.cancel(); + } + + } + + /** + * java.util.Timer#Timer(String, boolean) + */ + public void test_ConstructorSZ() throws Exception { + Timer t = null; + try { + // Ensure a task is run + t = new Timer("test_ConstructorSZThread", true); + TimerTestTask testTask = new TimerTestTask(); + t.schedule(testTask, 200); + awaitRun(testTask); + t.cancel(); + } finally { + if (t != null) + t.cancel(); + } + + try { + new Timer(null, true); + fail(); + } catch (NullPointerException expected) { + } + + try { + new Timer(null, false); + fail(); + } catch (NullPointerException expected) { + } + } + + /** + * java.util.Timer#Timer(String) + */ + public void test_ConstructorS() throws Exception { + Timer t = null; + try { + // Ensure a task is run + t = new Timer("test_ConstructorSThread"); + TimerTestTask testTask = new TimerTestTask(); + t.schedule(testTask, 200); + awaitRun(testTask); + t.cancel(); + } finally { + if (t != null) + t.cancel(); + } + + try { + new Timer(null); + fail(); + } catch (NullPointerException expected) { + } + } + + /** + * java.util.Timer#cancel() + */ + public void test_cancel() throws Exception { + Timer t = null; + try { + // Ensure a task throws an IllegalStateException after cancelled + t = new Timer(); + TimerTestTask testTask = new TimerTestTask(); + t.cancel(); + try { + t.schedule(testTask, 100, 200); + fail("Scheduling a task after Timer.cancel() should throw exception"); + } catch (IllegalStateException expected) { + } + + // Ensure a task is run but not after cancel + t = new Timer(); + testTask = new TimerTestTask(); + t.schedule(testTask, 100, 500); + awaitRun(testTask); + t.cancel(); + synchronized (sync) { + sync.wait(500); + } + assertEquals("TimerTask.run() method should not have been called after cancel", + 1, testTask.wasRun()); + + // Ensure you can call cancel more than once + t = new Timer(); + testTask = new TimerTestTask(); + t.schedule(testTask, 100, 500); + awaitRun(testTask); + t.cancel(); + t.cancel(); + t.cancel(); + synchronized (sync) { + sync.wait(500); + } + assertEquals("TimerTask.run() method should not have been called after cancel", + 1, testTask.wasRun()); + + // Ensure that a call to cancel from within a timer ensures no more + // run + t = new Timer(); + testTask = new TimerTestTask(t); + testTask.incrementCount(true); + testTask.terminateCount(5); // Terminate after 5 runs + t.schedule(testTask, 100, 100); + synchronized (sync) { + sync.wait(200); + assertEquals(1, testTask.wasRun()); + sync.wait(200); + assertEquals(2, testTask.wasRun()); + sync.wait(200); + assertEquals(3, testTask.wasRun()); + sync.wait(200); + assertEquals(4, testTask.wasRun()); + sync.wait(200); + assertEquals(5, testTask.wasRun()); + sync.wait(200); + assertEquals(5, testTask.wasRun()); + } + t.cancel(); + Thread.sleep(200); + } finally { + if (t != null) + t.cancel(); + } + + } + + /** + * java.util.Timer#purge() + */ + public void test_purge() throws Exception { + Timer t = null; + try { + t = new Timer(); + assertEquals(0, t.purge()); + + TimerTestTask[] tasks = new TimerTestTask[100]; + int[] delayTime = { 50, 80, 20, 70, 40, 10, 90, 30, 60 }; + + int j = 0; + for (int i = 0; i < 100; i++) { + tasks[i] = new TimerTestTask(); + t.schedule(tasks[i], delayTime[j++], 200); + if (j == 9) { + j = 0; + } + } + + for (int i = 0; i < 50; i++) { + tasks[i].cancel(); + } + + assertTrue(t.purge() <= 50); + assertEquals(0, t.purge()); + } finally { + if (t != null) { + t.cancel(); + } + } + } + + /** + * java.util.Timer#schedule(java.util.TimerTask, java.util.Date) + */ + public void test_scheduleLjava_util_TimerTaskLjava_util_Date() throws Exception { + Timer t = null; + try { + // Ensure a Timer throws an IllegalStateException after cancelled + t = new Timer(); + TimerTestTask testTask = new TimerTestTask(); + Date d = new Date(System.currentTimeMillis() + 100); + t.cancel(); + try { + t.schedule(testTask, d); + fail("Scheduling a task after Timer.cancel() should throw exception"); + } catch (IllegalStateException expected) { + } + + // Ensure a Timer throws an IllegalStateException if task already + // cancelled + t = new Timer(); + testTask = new TimerTestTask(); + d = new Date(System.currentTimeMillis() + 100); + testTask.cancel(); + try { + t.schedule(testTask, d); + fail("Scheduling a task after cancelling it should throw exception"); + } catch (IllegalStateException expected) { + } + t.cancel(); + + // Ensure a Timer throws an IllegalArgumentException if delay is + // negative + t = new Timer(); + testTask = new TimerTestTask(); + d = new Date(-100); + try { + t.schedule(testTask, d); + fail("Scheduling a task with negative date should throw IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a Timer throws a NullPointerException if the task is null + t = new Timer(); + d = new Date(System.currentTimeMillis() + 100); + try { + t.schedule(null, d); + fail("Scheduling a null task should throw NullPointerException"); + } catch (NullPointerException expected) { + } + t.cancel(); + + // Ensure a Timer throws a NullPointerException if the date is null + t = new Timer(); + testTask = new TimerTestTask(); + try { + t.schedule(testTask, null); + fail("Scheduling a null date should throw NullPointerException"); + } catch (NullPointerException expected) { + } + t.cancel(); + + // Ensure proper sequence of exceptions + t = new Timer(); + d = new Date(-100); + try { + t.schedule(null, d); + fail("Scheduling a null task with negative date should throw IllegalArgumentException first"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a task is run + t = new Timer(); + testTask = new TimerTestTask(); + d = new Date(System.currentTimeMillis() + 200); + t.schedule(testTask, d); + awaitRun(testTask); + t.cancel(); + + // Ensure multiple tasks are run + t = new Timer(); + testTask = new TimerTestTask(); + testTask.incrementCount(true); + d = new Date(System.currentTimeMillis() + 100); + t.schedule(testTask, d); + testTask = new TimerTestTask(); + testTask.incrementCount(true); + d = new Date(System.currentTimeMillis() + 150); + t.schedule(testTask, d); + testTask = new TimerTestTask(); + testTask.incrementCount(true); + d = new Date(System.currentTimeMillis() + 70); + t.schedule(testTask, d); + testTask = new TimerTestTask(); + testTask.incrementCount(true); + d = new Date(System.currentTimeMillis() + 10); + t.schedule(testTask, d); + Thread.sleep(400); + assertTrue("Multiple tasks should have incremented counter 4 times not " + + timerCounter, timerCounter == 4); + t.cancel(); + } finally { + if (t != null) + t.cancel(); + } + } + + /** + * java.util.Timer#schedule(java.util.TimerTask, long) + */ + public void test_scheduleLjava_util_TimerTaskJ() throws Exception { + Timer t = null; + try { + // Ensure a Timer throws an IllegalStateException after cancelled + t = new Timer(); + TimerTestTask testTask = new TimerTestTask(); + t.cancel(); + try { + t.schedule(testTask, 100); + fail("Scheduling a task after Timer.cancel() should throw exception"); + } catch (IllegalStateException expected) { + } + + // Ensure a Timer throws an IllegalStateException if task already + // cancelled + t = new Timer(); + testTask = new TimerTestTask(); + testTask.cancel(); + try { + t.schedule(testTask, 100); + fail("Scheduling a task after cancelling it should throw exception"); + } catch (IllegalStateException expected) { + } + t.cancel(); + + // Ensure a Timer throws an IllegalArgumentException if delay is + // negative + t = new Timer(); + testTask = new TimerTestTask(); + try { + t.schedule(testTask, -100); + fail("Scheduling a task with negative delay should throw IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a Timer throws a NullPointerException if the task is null + t = new Timer(); + try { + t.schedule(null, 10); + fail("Scheduling a null task should throw NullPointerException"); + } catch (NullPointerException expected) { + } + t.cancel(); + + // Ensure proper sequence of exceptions + t = new Timer(); + try { + t.schedule(null, -10); + fail("Scheduling a null task with negative delays should throw IllegalArgumentException first"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a task is run + t = new Timer(); + testTask = new TimerTestTask(); + t.schedule(testTask, 200); + awaitRun(testTask); + t.cancel(); + + // Ensure multiple tasks are run + t = new Timer(); + testTask = new TimerTestTask(); + testTask.incrementCount(true); + t.schedule(testTask, 100); + testTask = new TimerTestTask(); + testTask.incrementCount(true); + t.schedule(testTask, 150); + testTask = new TimerTestTask(); + testTask.incrementCount(true); + t.schedule(testTask, 70); + testTask = new TimerTestTask(); + testTask.incrementCount(true); + t.schedule(testTask, 10); + Thread.sleep(400); + assertTrue("Multiple tasks should have incremented counter 4 times not " + + timerCounter, timerCounter == 4); + t.cancel(); + } finally { + if (t != null) + t.cancel(); + } + } + + /** + * java.util.Timer#schedule(java.util.TimerTask, long, long) + */ + public void test_scheduleLjava_util_TimerTaskJJ() throws Exception { + Timer t = null; + try { + // Ensure a Timer throws an IllegalStateException after cancelled + t = new Timer(); + TimerTestTask testTask = new TimerTestTask(); + t.cancel(); + try { + t.schedule(testTask, 100, 100); + fail("Scheduling a task after Timer.cancel() should throw exception"); + } catch (IllegalStateException expected) { + } + + // Ensure a Timer throws an IllegalStateException if task already + // cancelled + t = new Timer(); + testTask = new TimerTestTask(); + testTask.cancel(); + try { + t.schedule(testTask, 100, 100); + fail("Scheduling a task after cancelling it should throw exception"); + } catch (IllegalStateException expected) { + } + t.cancel(); + + // Ensure a Timer throws an IllegalArgumentException if delay is + // negative + t = new Timer(); + testTask = new TimerTestTask(); + try { + t.schedule(testTask, -100, 100); + fail("Scheduling a task with negative delay should throw IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a Timer throws an IllegalArgumentException if period is + // negative + t = new Timer(); + testTask = new TimerTestTask(); + try { + t.schedule(testTask, 100, -100); + fail("Scheduling a task with negative period should throw IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a Timer throws an IllegalArgumentException if period is + // zero + t = new Timer(); + testTask = new TimerTestTask(); + try { + t.schedule(testTask, 100, 0); + fail("Scheduling a task with 0 period should throw IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a Timer throws a NullPointerException if the task is null + t = new Timer(); + try { + t.schedule(null, 10, 10); + fail("Scheduling a null task should throw NullPointerException"); + } catch (NullPointerException expected) { + } + t.cancel(); + + // Ensure proper sequence of exceptions + t = new Timer(); + try { + t.schedule(null, -10, -10); + fail("Scheduling a null task with negative delays should throw IllegalArgumentException first"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a task is run at least twice + t = new Timer(); + testTask = new TimerTestTask(); + t.schedule(testTask, 100, 100); + Thread.sleep(400); + assertTrue("TimerTask.run() method should have been called at least twice (" + + testTask.wasRun() + ")", testTask.wasRun() >= 2); + t.cancel(); + + // Ensure multiple tasks are run + t = new Timer(); + testTask = new TimerTestTask(); + testTask.incrementCount(true); + t.schedule(testTask, 100, 100); // at least 9 times + testTask = new TimerTestTask(); + testTask.incrementCount(true); + t.schedule(testTask, 200, 100); // at least 7 times + testTask = new TimerTestTask(); + testTask.incrementCount(true); + t.schedule(testTask, 300, 200); // at least 4 times + testTask = new TimerTestTask(); + testTask.incrementCount(true); + t.schedule(testTask, 100, 200); // at least 4 times + Thread.sleep(1200); // Allowed more room for error + assertTrue("Multiple tasks should have incremented counter 24 times not " + + timerCounter, timerCounter >= 24); + t.cancel(); + } finally { + if (t != null) + t.cancel(); + } + } + + /** + * java.util.Timer#schedule(java.util.TimerTask, java.util.Date, + * long) + */ + public void test_scheduleLjava_util_TimerTaskLjava_util_DateJ() throws Exception { + Timer t = null; + try { + // Ensure a Timer throws an IllegalStateException after cancelled + t = new Timer(); + TimerTestTask testTask = new TimerTestTask(); + Date d = new Date(System.currentTimeMillis() + 100); + t.cancel(); + try { + t.schedule(testTask, d, 100); + fail("Scheduling a task after Timer.cancel() should throw exception"); + } catch (IllegalStateException expected) { + } + + // Ensure a Timer throws an IllegalStateException if task already + // cancelled + t = new Timer(); + d = new Date(System.currentTimeMillis() + 100); + testTask = new TimerTestTask(); + testTask.cancel(); + try { + t.schedule(testTask, d, 100); + fail("Scheduling a task after cancelling it should throw exception"); + } catch (IllegalStateException expected) { + } + t.cancel(); + + // Ensure a Timer throws an IllegalArgumentException if delay is + // negative + t = new Timer(); + d = new Date(-100); + testTask = new TimerTestTask(); + try { + t.schedule(testTask, d, 100); + fail("Scheduling a task with negative delay should throw IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a Timer throws an IllegalArgumentException if period is + // negative + t = new Timer(); + d = new Date(System.currentTimeMillis() + 100); + testTask = new TimerTestTask(); + try { + t.schedule(testTask, d, -100); + fail("Scheduling a task with negative period should throw IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a Timer throws a NullPointerException if the task is null + t = new Timer(); + d = new Date(System.currentTimeMillis() + 100); + try { + t.schedule(null, d, 10); + fail("Scheduling a null task should throw NullPointerException"); + } catch (NullPointerException expected) { + } + t.cancel(); + + // Ensure a Timer throws a NullPointerException if the date is null + t = new Timer(); + testTask = new TimerTestTask(); + try { + t.schedule(testTask, null, 10); + fail("Scheduling a null task should throw NullPointerException"); + } catch (NullPointerException expected) { + } + t.cancel(); + + // Ensure proper sequence of exceptions + t = new Timer(); + d = new Date(-100); + try { + t.schedule(null, d, 10); + fail("Scheduling a null task with negative dates should throw IllegalArgumentException first"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a task is run at least twice + t = new Timer(); + d = new Date(System.currentTimeMillis() + 100); + testTask = new TimerTestTask(); + t.schedule(testTask, d, 100); + Thread.sleep(800); + assertTrue("TimerTask.run() method should have been called at least twice (" + + testTask.wasRun() + ")", testTask.wasRun() >= 2); + t.cancel(); + + // Ensure multiple tasks are run + t = new Timer(); + testTask = new TimerTestTask(); + testTask.incrementCount(true); + d = new Date(System.currentTimeMillis() + 100); + t.schedule(testTask, d, 100); // at least 9 times + testTask = new TimerTestTask(); + testTask.incrementCount(true); + d = new Date(System.currentTimeMillis() + 200); + t.schedule(testTask, d, 100); // at least 7 times + testTask = new TimerTestTask(); + testTask.incrementCount(true); + d = new Date(System.currentTimeMillis() + 300); + t.schedule(testTask, d, 200); // at least 4 times + testTask = new TimerTestTask(); + testTask.incrementCount(true); + d = new Date(System.currentTimeMillis() + 100); + t.schedule(testTask, d, 200); // at least 4 times + Thread.sleep(3000); + assertTrue("Multiple tasks should have incremented counter 24 times not " + + timerCounter, timerCounter >= 24); + t.cancel(); + } finally { + if (t != null) + t.cancel(); + } + } + + /** + * java.util.Timer#scheduleAtFixedRate(java.util.TimerTask, long, + * long) + */ + public void test_scheduleAtFixedRateLjava_util_TimerTaskJJ() throws Exception { + Timer t = null; + try { + // Ensure a Timer throws an IllegalStateException after cancelled + t = new Timer(); + TimerTestTask testTask = new TimerTestTask(); + t.cancel(); + try { + t.scheduleAtFixedRate(testTask, 100, 100); + fail("scheduleAtFixedRate after Timer.cancel() should throw exception"); + } catch (IllegalStateException expected) { + } + + // Ensure a Timer throws an IllegalArgumentException if delay is + // negative + t = new Timer(); + testTask = new TimerTestTask(); + try { + t.scheduleAtFixedRate(testTask, -100, 100); + fail("scheduleAtFixedRate with negative delay should throw IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a Timer throws an IllegalArgumentException if period is + // negative + t = new Timer(); + testTask = new TimerTestTask(); + try { + t.scheduleAtFixedRate(testTask, 100, -100); + fail("scheduleAtFixedRate with negative period should throw IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a task is run at least twice + t = new Timer(); + testTask = new TimerTestTask(); + t.scheduleAtFixedRate(testTask, 100, 100); + Thread.sleep(400); + assertTrue("TimerTask.run() method should have been called at least twice (" + + testTask.wasRun() + ")", testTask.wasRun() >= 2); + t.cancel(); + + class SlowThenFastTask extends TimerTask { + int wasRun = 0; + + long startedAt; + + long lastDelta; + + public void run() { + if (wasRun == 0) + startedAt = System.currentTimeMillis(); + lastDelta = System.currentTimeMillis() + - (startedAt + (100 * wasRun)); + wasRun++; + if (wasRun == 2) { + try { + Thread.sleep(200); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + public long lastDelta() { + return lastDelta; + } + + public int wasRun() { + return wasRun; + } + } + + // Ensure multiple tasks are run + t = new Timer(); + SlowThenFastTask slowThenFastTask = new SlowThenFastTask(); + + // at least 9 times even when asleep + t.scheduleAtFixedRate(slowThenFastTask, 100, 100); + Thread.sleep(1000); + long lastDelta = slowThenFastTask.lastDelta(); + assertTrue("Fixed Rate Schedule should catch up, but is off by " + + lastDelta + " ms", slowThenFastTask.lastDelta < 300); + t.cancel(); + } finally { + if (t != null) + t.cancel(); + } + } + + /** + * java.util.Timer#scheduleAtFixedRate(java.util.TimerTask, + * java.util.Date, long) + */ + public void test_scheduleAtFixedRateLjava_util_TimerTaskLjava_util_DateJ() throws Exception { + Timer t = null; + try { + // Ensure a Timer throws an IllegalStateException after cancelled + t = new Timer(); + TimerTestTask testTask = new TimerTestTask(); + t.cancel(); + Date d = new Date(System.currentTimeMillis() + 100); + try { + t.scheduleAtFixedRate(testTask, d, 100); + fail("scheduleAtFixedRate after Timer.cancel() should throw exception"); + } catch (IllegalStateException expected) { + } + + // Ensure a Timer throws an IllegalArgumentException if delay is + // negative + t = new Timer(); + testTask = new TimerTestTask(); + d = new Date(-100); + try { + t.scheduleAtFixedRate(testTask, d, 100); + fail("scheduleAtFixedRate with negative Date should throw IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a Timer throws an IllegalArgumentException if period is + // negative + t = new Timer(); + testTask = new TimerTestTask(); + try { + t.scheduleAtFixedRate(testTask, d, -100); + fail("scheduleAtFixedRate with negative period should throw IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a Timer throws an NullPointerException if date is Null + t = new Timer(); + testTask = new TimerTestTask(); + try { + t.scheduleAtFixedRate(testTask, null, 100); + fail("scheduleAtFixedRate with null date should throw NullPointerException"); + } catch (NullPointerException expected) { + } + t.cancel(); + + // Ensure proper sequence of exceptions + t = new Timer(); + d = new Date(-100); + try { + t.scheduleAtFixedRate(null, d, 10); + fail("Scheduling a null task with negative date should throw IllegalArgumentException first"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure proper sequence of exceptions + t = new Timer(); + try { + t.scheduleAtFixedRate(null, null, -10); + fail("Scheduling a null task & null date & negative period should throw IllegalArgumentException first"); + } catch (IllegalArgumentException expected) { + } + t.cancel(); + + // Ensure a task is run at least twice + t = new Timer(); + testTask = new TimerTestTask(); + d = new Date(System.currentTimeMillis() + 100); + t.scheduleAtFixedRate(testTask, d, 100); + Thread.sleep(400); + assertTrue("TimerTask.run() method should have been called at least twice (" + + testTask.wasRun() + ")", testTask.wasRun() >= 2); + t.cancel(); + + class SlowThenFastTask extends TimerTask { + int wasRun = 0; + + long startedAt; + + long lastDelta; + + public void run() { + if (wasRun == 0) + startedAt = System.currentTimeMillis(); + lastDelta = System.currentTimeMillis() + - (startedAt + (100 * wasRun)); + wasRun++; + if (wasRun == 2) { + try { + Thread.sleep(200); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + public long lastDelta() { + return lastDelta; + } + + public int wasRun() { + return wasRun; + } + } + + // Ensure multiple tasks are run + t = new Timer(); + SlowThenFastTask slowThenFastTask = new SlowThenFastTask(); + d = new Date(System.currentTimeMillis() + 100); + + // at least 9 times even when asleep + t.scheduleAtFixedRate(slowThenFastTask, d, 100); + Thread.sleep(1000); + long lastDelta = slowThenFastTask.lastDelta(); + assertTrue("Fixed Rate Schedule should catch up, but is off by " + + lastDelta + " ms", lastDelta < 300); + t.cancel(); + } finally { + if (t != null) + t.cancel(); + } + } + + /** + * We used to swallow RuntimeExceptions thrown by tasks. Instead, we need to + * let those exceptions bubble up, where they will both notify the thread's + * uncaught exception handler and terminate the timer's thread. + */ + public void testThrowingTaskKillsTimerThread() throws Exception { + final AtomicReference<Thread> threadRef = new AtomicReference<Thread>(); + new Timer().schedule(new TimerTask() { + @Override public void run() { + Thread.currentThread().setUncaughtExceptionHandler(new UncaughtExceptionHandler() { + public void uncaughtException(Thread thread, Throwable ex) {} + }); + threadRef.set(Thread.currentThread()); + throw new RuntimeException("task failure!"); + } + }, 1); + + Thread.sleep(400); + Thread timerThread = threadRef.get(); + assertFalse(timerThread.isAlive()); + } + + protected void setUp() { + timerCounter = 0; + } + + protected void tearDown() { + } +} |