/* * Copyright 2015-present 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 = 10; /** * Sets up the testing environment. */ @Before public void setup() { timer = new ManuallyAdvancingTimer(true); 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; } } }