blob: b8e1e85eb1fd587b4c0afa3c05552629ed2b805d [file] [log] [blame]
/*
* Copyright 2015 Open Networking Laboratory
*
* Licensed 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.onlab.util;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Date;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.onlab.junit.TestTools.delay;
/**
* Testing class for manually advancing timer.
*/
public class ManuallyAdvancingTimerTest {
private ManuallyAdvancingTimer timer;
/* Generates unique id's for TestTasks */
private AtomicInteger idGenerator;
/* Tracks TestTasks in order of creation, tasks are automatically added at creation. */
private ArrayList<TestTask> taskList;
/* Total number of tasks run */
private AtomicInteger tasksRunCount;
// FIXME if this class fails first try increasing the real time delay to account for heavy system load.
private static final int REAL_TIME_DELAY = 1;
/**
* Sets up the testing environment.
*/
@Before
public void setup() {
timer = new ManuallyAdvancingTimer();
idGenerator = new AtomicInteger(1);
tasksRunCount = new AtomicInteger(0);
taskList = Lists.newArrayList();
}
/**
* Tests the one time schedule with delay.
*
* @throws Exception throws an exception if the test fails
*/
@Test
public void testScheduleByDelay() throws Exception {
/* Test scheduling in the future as normal. */
timer.schedule(new TestTask(), 10);
timer.advanceTimeMillis(5);
assertFalse(taskList.get(0).hasRun());
timer.advanceTimeMillis(10, REAL_TIME_DELAY);
assertTrue(taskList.get(0).hasRun());
/* Test scheduling with negative numbers */
timer.schedule(new TestTask(), -10);
timer.advanceTimeMillis(5);
assertFalse(taskList.get(1).hasRun());
timer.advanceTimeMillis(10, REAL_TIME_DELAY);
assertTrue(taskList.get(1).hasRun());
/* Reset list, counter and timer for next test */
taskList.clear();
idGenerator.set(1);
tasksRunCount.set(0);
for (int i = 0; i < 50; i++) {
timer.schedule(new TestTask(), i);
}
/* Test that a task scheduled for present is run and not placed in the queue */
assertEquals("Only the first task should have run.", 1, tasksRunCount.get());
for (int i = 2; i <= 50; i++) {
timer.advanceTimeMillis(1, REAL_TIME_DELAY);
assertEquals("One task should be executed per loop", i, tasksRunCount.get());
}
/* Below tests ordered insertion, this will only be done once, it is the same for all schedule methods. */
tasksRunCount.set(0);
for (int i = 0; i < 10; i++) {
timer.schedule(new TestTask(), 500);
}
assertEquals("No new tasks should have been run since run count reset.", 0, tasksRunCount.get());
timer.schedule(new TestTask(), 10);
assertEquals("No new tasks should have been run since run count reset.", 0, tasksRunCount.get());
timer.advanceTimeMillis(10, REAL_TIME_DELAY);
assertEquals("One new tasks should have been run since run count reset.", 1, tasksRunCount.get());
timer.advanceTimeMillis(510, REAL_TIME_DELAY);
assertEquals("Eleven new tasks should have been run since run count reset.", 11, tasksRunCount.get());
}
/**
* Tests scheduling for a particular date or time which may be in the past.
*
* @throws Exception throws an exception if the test fails
*/
@Test
public void testScheduleByDate() throws Exception {
/* Tests basic scheduling for future times. */
timer.schedule(new TestTask(), new Date(10));
timer.advanceTimeMillis(5);
assertFalse(taskList.get(0).hasRun());
timer.advanceTimeMillis(10, REAL_TIME_DELAY);
assertTrue(taskList.get(0).hasRun());
/* Test scheduling with past times numbers */
timer.schedule(new TestTask(), new Date(0));
delay(REAL_TIME_DELAY);
assertTrue(taskList.get(1).hasRun());
/* Tests cancellation on non-periodic events */
TestTask task = new TestTask();
timer.schedule(task, new Date(timer.currentTimeInMillis() + 10));
task.cancel();
timer.advanceTimeMillis(12, REAL_TIME_DELAY);
assertFalse(task.hasRun());
}
/**
* Test scheduling beginning after a delay and recurring periodically.
*
* @throws Exception throws an exception if the test fails
*/
@Test
public void testScheduleByDelayPeriodic() throws Exception {
/* Test straightforward periodic execution */
timer.schedule(new TestTask(), 0, 10);
delay(REAL_TIME_DELAY);
assertEquals("Task should have run once when added.", 1, taskList.get(0).timesRun());
/* Tests whether things that are not added to the queue are scheduled for future executions (ones which execute
immediately on add). */
timer.advanceTimeMillis(10, REAL_TIME_DELAY);
assertEquals("Task should have run once when added.", 2, taskList.get(0).timesRun());
/* Tests whether cancellation works on periodic events. */
taskList.get(0).cancel();
timer.advanceTimeMillis(10, REAL_TIME_DELAY);
assertEquals("The task should not have run another time.", 2, taskList.get(0).timesRun());
TestTask task = new TestTask();
timer.schedule(task, 0, 10);
timer.advanceTimeMillis(100, REAL_TIME_DELAY);
assertEquals("Should have run immeditaley and subsequently once during the larger skip", task.timesRun(), 2);
}
/**
* Test scheduling beginning at a specified date and recurring periodically.
*
* @throws Exception throws an exception if the test fails
*/
@Test
public void testScheduleByDatePeriodic() throws Exception {
/* Test straightforward periodic execution */
timer.schedule(new TestTask(), new Date(timer.currentTimeInMillis()), 10);
delay(REAL_TIME_DELAY);
assertEquals("Task should have run once when added.", 1, taskList.get(0).timesRun());
/* Tests whether things that are not added to the queue are scheduled for future executions (ones which execute
immediately on add). */
timer.advanceTimeMillis(10, REAL_TIME_DELAY);
assertEquals("Task should have run once when added.", 2, taskList.get(0).timesRun());
/* Tests whether cancellation works on periodic events. */
taskList.get(0).cancel();
timer.advanceTimeMillis(10, REAL_TIME_DELAY);
assertEquals("The task should not have run another time.", 2, taskList.get(0).timesRun());
TestTask task = new TestTask();
timer.schedule(task, new Date(timer.currentTimeInMillis()), 10);
timer.advanceTimeMillis(100, REAL_TIME_DELAY);
assertEquals("Should have run immediately and subsequently once during the larger skip", task.timesRun(), 2);
}
/* Schedule at fixed rate runs exactly like the two scheduling methods just tested so tests are not included */
/**
* Timer task with added functions to make it better for testing.
*/
private class TestTask extends TimerTask {
/* Remains true once the task has been run at least once */
private boolean hasRun;
/* Unique id per event. */
private int id;
/* Specifies the number of times an event has run */
private int timesRun;
/**
* Constructor initializes id, timesRun, and id fields.
*/
public TestTask() {
id = idGenerator.getAndIncrement();
timesRun = 0;
hasRun = false;
taskList.add(this);
}
@Override
public void run() {
this.hasRun = true;
tasksRunCount.incrementAndGet();
timesRun++;
}
/**
* Returns whether this event has run.
*
* @return true if the event has run, false otherwise.
*/
public boolean hasRun() {
return hasRun;
}
/**
* Returns the number of times this task has run.
*
* @return an int representing the number of times this task has been run
*/
public int timesRun() {
return timesRun;
}
/**
* Returns the unique identifier of this task.
*
* @return a unique integer identifier
*/
public int getId() {
return id;
}
}
}