| /* |
| * 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.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.concurrent.atomic.AtomicLong; |
| 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, 200); // at least 4 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() + 500); |
| t.schedule(testTask, d, 400); // at least 2 times |
| testTask = new TimerTestTask(); |
| testTask.incrementCount(true); |
| d = new Date(System.currentTimeMillis() + 100); |
| t.schedule(testTask, d, 400); // at least 2 times |
| Thread.sleep(3000); |
| assertTrue("Multiple tasks should have incremented counter 12 times not " |
| + timerCounter, timerCounter >= 12); |
| 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() { |
| @Override 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()); |
| } |
| |
| private class CheckIfExecutedOnTime extends TimerTask { |
| private static final int TOLERANCE_TIME = 100; |
| private final AtomicBoolean executedOnTime; |
| |
| static final int SLEEPING_TIME = 10 * TOLERANCE_TIME; |
| |
| private CheckIfExecutedOnTime(AtomicBoolean executedOnTime) { |
| this.executedOnTime = executedOnTime; |
| } |
| |
| @Override |
| public void run() { |
| // We'll schedule one after the other to execute immediately, the first one with |
| // {@code executedOnTime == null}. Ensure that the second |
| // is delayed by at most the time spent by the first one, plus some tolerance. |
| if (executedOnTime != null && |
| System.currentTimeMillis() |
| <= scheduledExecutionTime() + SLEEPING_TIME + TOLERANCE_TIME) { |
| executedOnTime.set(true); |
| } else { |
| try { |
| Thread.sleep(SLEEPING_TIME); |
| } catch (InterruptedException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| } |
| }; |
| |
| public void testOverdueTaskExecutesImmediately() throws Exception { |
| Timer t = new Timer(); |
| Date date = new Date(System.currentTimeMillis()); |
| t.schedule(new CheckIfExecutedOnTime(null), date); |
| AtomicBoolean actuallyExecutedOnTime = new AtomicBoolean(); |
| // Scheduled to execute right now but won't do as the other task is sleeping. Check that |
| // this one executes as soon as the other one finishes. |
| t.schedule(new CheckIfExecutedOnTime(actuallyExecutedOnTime), date); |
| // Only the first one sleeps, this will be the two tasks plenty of time to finish. |
| Thread.sleep(2 * CheckIfExecutedOnTime.SLEEPING_TIME); |
| t.cancel(); |
| assertTrue(actuallyExecutedOnTime.get()); |
| } |
| |
| public void testCanBeCancelledEvenIfTaskKeepsItPermanentlyBusy() throws Exception { |
| final int timeSleeping = 200; |
| Timer t = new Timer(); |
| final AtomicLong counter = new AtomicLong(); |
| TimerTask task = new TimerTask() { |
| @Override |
| public void run() { |
| try { |
| counter.incrementAndGet(); |
| Thread.sleep(timeSleeping); |
| } catch (InterruptedException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| }; |
| // Keep the thread busy by scheduling execution twice as fast than the task can execute. |
| t.scheduleAtFixedRate(task, 1 /* delay */, timeSleeping / 2 /* rate */); |
| Thread.sleep(timeSleeping * 8); |
| // Check the task was actually running. |
| assertTrue(counter.get() > 0); |
| t.cancel(); |
| // Allow some time to finish. |
| Thread.sleep(2 * timeSleeping); |
| try { |
| t.schedule( |
| new TimerTask() { |
| @Override |
| public void run() { |
| |
| } |
| }, |
| 1 /* delay */); |
| fail("timer should be cancelled, and not accept new schedulings"); |
| } catch (IllegalStateException expected) { |
| // Expected. |
| } |
| } |
| |
| public void testTaskNotCancelledWhenTimerCancelled() throws Exception { |
| final int timeSleeping = 200; |
| Timer t = new Timer(); |
| final AtomicLong counter = new AtomicLong(); |
| TimerTask task = new TimerTask() { |
| @Override |
| public void run() { |
| try { |
| counter.incrementAndGet(); |
| Thread.sleep(timeSleeping); |
| } catch (InterruptedException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| }; |
| t.scheduleAtFixedRate(task, 1 /* delay */, 100 /* rate */); |
| Thread.sleep(1000); |
| t.cancel(); |
| // Returns true as the task wasn't cancelled before. |
| assertTrue(task.cancel()); |
| } |
| |
| public void testTaskNotCancelledWhenTimerCancelledAndPurged() throws Exception { |
| final int timeSleeping = 200; |
| Timer t = new Timer(); |
| final AtomicLong counter = new AtomicLong(); |
| TimerTask task = new TimerTask() { |
| @Override |
| public void run() { |
| try { |
| counter.incrementAndGet(); |
| Thread.sleep(timeSleeping); |
| } catch (InterruptedException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| }; |
| t.scheduleAtFixedRate(task, 1 /* delay */, 100 /* rate */); |
| Thread.sleep(1000); |
| t.cancel(); |
| t.purge(); |
| // Returns true as the task wasn't cancelled before. |
| assertTrue(task.cancel()); |
| } |
| |
| private static class IncrementCounterTaskAndPossiblyThrowAfter extends TimerTask { |
| |
| private final AtomicLong counter; |
| private final int incrementAmount; |
| private final boolean willThrow; |
| |
| |
| IncrementCounterTaskAndPossiblyThrowAfter( |
| AtomicLong counter, int incrementAmount, boolean willThrow) { |
| this.counter = counter; |
| this.incrementAmount = incrementAmount; |
| this.willThrow = willThrow; |
| } |
| |
| @Override |
| public void run() { |
| counter.addAndGet(incrementAmount); |
| if (willThrow) { |
| throw new IllegalStateException("TimerTask runtime exception from run()"); |
| } |
| } |
| } |
| |
| private static class SwallowUncaughtExceptionHandler implements UncaughtExceptionHandler { |
| CountDownLatch latch = new CountDownLatch(1); |
| @Override |
| public void uncaughtException(Thread thread, Throwable ex) { |
| latch.countDown(); |
| } |
| |
| void waitForException(long millis) throws InterruptedException { |
| if(!latch.await(millis, TimeUnit.MILLISECONDS)) { |
| throw new AssertionError("Expected exception thrown from timer thread"); |
| } |
| } |
| } |
| |
| public void testTimerCancelledAfterException() throws Exception { |
| UncaughtExceptionHandler excHandler = Thread.getDefaultUncaughtExceptionHandler(); |
| // Install an uncaught exception handler because we are |
| // deliberately causing the timer thread to die in this test (which will cause CTS tests |
| // to fail). |
| SwallowUncaughtExceptionHandler swallowUncaughtExceptionHandler = |
| new SwallowUncaughtExceptionHandler(); |
| Thread.setDefaultUncaughtExceptionHandler(swallowUncaughtExceptionHandler); |
| try { |
| Timer t = new Timer(); |
| final AtomicLong counter = new AtomicLong(); |
| |
| // Schedule tasks to run: |
| // A) {In 1 millis} Increment a counter by 1 and throw an exception |
| // B) {In 100 millis} Increment a counter by 1000 (but it's not intended to be executed |
| // because of the previous exception). |
| // We want A and B to be scheduled before A runs. |
| // We add them in reverse order. |
| // We have ~99 millis after scheduling B to schedule A. If A ran before we scheduled B |
| // we would get an exception when we came to schedule B. |
| TimerTask taskThatDoesntThrow = new IncrementCounterTaskAndPossiblyThrowAfter( |
| counter, |
| 1000, /* incrementAmount */ |
| false /* willThrow */); |
| TimerTask taskThatThrows = new IncrementCounterTaskAndPossiblyThrowAfter( |
| counter, |
| 1, /* incrementAmount */ |
| true /* willThrow */); |
| t.schedule(taskThatDoesntThrow, 100 /* delay */); |
| t.scheduleAtFixedRate(taskThatThrows, 1 /* delay */, 100 /* period */); |
| |
| swallowUncaughtExceptionHandler.waitForException(1000); |
| // Check the counter wasn't increased more than once (ie, the exception killed the |
| // execution thread). |
| assertEquals("Counter should be 1, and is: " + counter.get(), 1, counter.get()); |
| |
| assertTrue("The timer should not cancel the tasks", taskThatDoesntThrow.cancel()); |
| assertTrue("The timer should not cancel the tasks", taskThatThrows.cancel()); |
| |
| TimerTask otherTask = new TimerTask() { |
| @Override |
| public void run() { |
| counter.incrementAndGet(); |
| } |
| }; |
| |
| try { |
| t.schedule(otherTask, 1); |
| fail("Timer should be cancelled and no new tasks should be allowed"); |
| } catch (Exception expected) { |
| // Expected. |
| } |
| } finally { |
| Thread.setDefaultUncaughtExceptionHandler(excHandler); |
| } |
| } |
| |
| public void testTimerCancelledAfterExceptionAndTasksNotCancelledAfterPurge() throws Exception { |
| UncaughtExceptionHandler excHandler = Thread.getDefaultUncaughtExceptionHandler(); |
| // Install an uncaught exception handler because we are |
| // deliberately causing the timer thread to die in this test (which will cause CTS tests |
| // to fail). |
| SwallowUncaughtExceptionHandler swallowUncaughtExceptionHandler = |
| new SwallowUncaughtExceptionHandler(); |
| Thread.setDefaultUncaughtExceptionHandler(swallowUncaughtExceptionHandler); |
| try { |
| Timer t = new Timer(); |
| final AtomicLong counter = new AtomicLong(); |
| |
| // Schedule tasks to run: |
| // A) {In 1 millis} Increment a counter by 1 and throw an exception |
| // B) {In 100 millis} Increment a counter by 1000 (but it's not intended to be executed |
| // because of the previous exception). |
| // We want A and B to be scheduled before A runs. |
| // We add them in reverse order. |
| // We have ~99 millis after scheduling B to schedule A. If A ran before we scheduled B |
| // we would get an exception when we came to schedule B. |
| |
| TimerTask taskThatDoesntThrow = new IncrementCounterTaskAndPossiblyThrowAfter( |
| counter, |
| 1000, /* incrementAmount */ |
| false /* willThrow */); |
| TimerTask taskThatThrows = new IncrementCounterTaskAndPossiblyThrowAfter( |
| counter, |
| 1, /* incrementAmount */ |
| true /* willThrow */); |
| t.schedule(taskThatDoesntThrow, 100 /* delay */); |
| t.scheduleAtFixedRate(taskThatThrows, 1 /* delay */, 100 /* period */); |
| swallowUncaughtExceptionHandler.waitForException(1000); |
| // Check the counter wasn't increased more than once (ie, the exception killed the |
| // execution thread). |
| assertEquals("Counter should be 1, and is: " + counter.get(), 1, counter.get()); |
| t.purge(); |
| assertTrue("The timer should not cancel the tasks", taskThatDoesntThrow.cancel()); |
| assertTrue("The timer should not cancel the tasks", taskThatThrows.cancel()); |
| |
| TimerTask otherTask = new TimerTask() { |
| @Override |
| public void run() { |
| counter.incrementAndGet(); |
| } |
| }; |
| |
| try { |
| t.schedule(otherTask, 1); |
| fail("Timer should be cancelled and no new tasks should be allowed"); |
| } catch (Exception expected) { |
| // Expected. |
| } |
| } finally { |
| Thread.setDefaultUncaughtExceptionHandler(excHandler); |
| } |
| } |
| |
| public void testTimerCancelledTasksRemovedFromQueue() throws Exception { |
| Timer t = new Timer(); |
| TimerTask task1 = new TimerTask() { |
| @Override |
| public void run() { |
| } |
| }; |
| t.scheduleAtFixedRate(task1, 1 /* delay */, 10 /* period */); |
| |
| task1.cancel(); |
| // As the rate is 10, the timer will try to schedule it before the purge and remove it. |
| Thread.sleep(500); |
| assertEquals(0, t.purge()); |
| } |
| |
| protected void setUp() { |
| timerCounter = 0; |
| } |
| |
| protected void tearDown() { |
| } |
| } |