package org.limewire.concurrent;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import junit.framework.Test;
import org.limewire.service.ErrorCallback;
import org.limewire.service.ErrorCallbackStub;
import org.limewire.service.ErrorService;
import org.limewire.util.BaseTestCase;
/**
* Unit tests for <code>SimpleTimer</code>.
*/
public class SimpleTimerTest extends BaseTestCase {
private long T=100;
public SimpleTimerTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(SimpleTimerTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
//Tests when the first item schedule goes first
public void testFirstGoesFirst() {
SimpleTimer t=new SimpleTimer(false); //not daemon: test thread dies
sleep(T); //make timer thread block
TimerTestTask a=new TimerTestTask();
TimerTestTask b=new TimerTestTask();
long start=System.currentTimeMillis();
t.scheduleWithFixedDelay(a, 2*T, 2*T, TimeUnit.MILLISECONDS);
sleep(T);
t.scheduleWithFixedDelay(b, 2*T, 3*T, TimeUnit.MILLISECONDS);
sleep(8*T+T/2);
t.shutdown();
sleep(3*T); //to check that cancel really worked
a.checkMatch(start+2*T, 4, 2*T);
b.checkMatch(start+3*T, 3, 3*T);
try {
t.scheduleWithFixedDelay(new TimerTestTask(), 0, T, TimeUnit.MILLISECONDS);
fail("illegalstateexception should have been thrown");
} catch (IllegalStateException pass) { }
}
//Tests when the second item scheduled goes first
public void testSecondGoesFirst() {
SimpleTimer t=new SimpleTimer(false); //not daemon: test thread dies
TimerTestTask b=new TimerTestTask();
TimerTestTask a=new TimerTestTask();
long start=System.currentTimeMillis();
t.scheduleWithFixedDelay(b, 3*T, 3*T, TimeUnit.MILLISECONDS);
sleep(T);
t.scheduleWithFixedDelay(a, T, 2*T, TimeUnit.MILLISECONDS);
sleep(8*T+T/2);
t.shutdown();
sleep(3*T); //to check that cancel really worked
a.checkMatch(start+2*T, 4, 2*T);
b.checkMatch(start+3*T, 3, 3*T);
}
//Test the priority queue with many tasks
public void testManyTasks() {
SimpleTimer t=new SimpleTimer(true);
TimerTestTask[] tasks=new TimerTestTask[12];
long start=System.currentTimeMillis();
for (int i=0; i<tasks.length; i++) {
tasks[i]=new TimerTestTask();
t.scheduleWithFixedDelay(tasks[i], 0, 4*T, TimeUnit.MILLISECONDS);
}
sleep(5*T);
t.shutdown();
for (TimerTestTask task : tasks) {
task.checkMatch(start, 2, 4 * T);
}
}
public void testExceptionIsCaught() {
ErrorCallback old = ErrorService.getErrorCallback();
ErrorCallbackStub now = new ErrorCallbackStub();
try {
SimpleTimer t = new SimpleTimer(false);
ErrorService.setErrorCallback(now);
TimerTestTask a = new TimerTestTask(true);
t.scheduleWithFixedDelay(a, T, 2*T, TimeUnit.MILLISECONDS);
sleep(T+T/2);
t.shutdown();
sleep(3*T);
assertEquals( 1, now.getExceptionCount() );
} finally {
ErrorService.setErrorCallback(old);
}
}
void sleep(long msecs) {
try { Thread.sleep(msecs); } catch (InterruptedException ignored) { }
}
private static class TimerTestTask implements Runnable {
//The system times this was executed, as Long.
private ArrayList<Long> _runs=new ArrayList<Long>();
// Amount of allowed variation, in msecs.
private static long FUDGE_FACTOR=40;
private boolean _throwException;
TimerTestTask() {
this(false);
}
TimerTestTask(boolean throwException) {
this._throwException=throwException;
}
public void run() {
long now=System.currentTimeMillis();
_runs.add(now);
if (_throwException)
throw new IndexOutOfBoundsException();
}
//Checks that this' execution times approximately match.
//Runs must be 2 or greater
void checkMatch(long start, int runs, long period) {
assertEquals(runs, _runs.size());
assertEquals("start times not equal", start, get(0), FUDGE_FACTOR);
for (int i=1; i<runs; i++) {
long actualPeriod=get(i)-get(i-1);
assertEquals("bad spacing in runs",
period, actualPeriod, FUDGE_FACTOR);
}
}
long get(int i) {
return _runs.get(i);
}
}
}