package com.limegroup.gnutella.util;
import java.util.ArrayList;
import junit.framework.Test;
import com.limegroup.gnutella.ErrorCallback;
import com.limegroup.gnutella.ErrorService;
import com.limegroup.gnutella.stubs.ErrorCallbackStub;
/**
* Unit tests for SimpleTimer
*/
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("a");
TimerTestTask b=new TimerTestTask("b");
long start=System.currentTimeMillis();
t.schedule(a, 2*T, 2*T);
sleep(T);
t.schedule(b, 2*T, 3*T);
sleep(8*T+T/2);
t.cancel();
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.schedule(new TimerTestTask("c"), 0, T);
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("b2");
TimerTestTask a=new TimerTestTask("a2");
long start=System.currentTimeMillis();
t.schedule(b, 3*T, 3*T);
sleep(T);
t.schedule(a, T, 2*T);
sleep(8*T+T/2);
t.cancel();
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"+i);
t.schedule(tasks[i], 0, 4*T);
}
sleep(5*T);
t.cancel();
for (int i=0; i<tasks.length; i++) {
tasks[i].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("a3", true);
long start = System.currentTimeMillis();
t.schedule(a, T, 2*T);
sleep(T+T/2);
t.cancel();
sleep(3*T);
assertEquals( 1, now.exceptions );
} 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 _runs=new ArrayList();
// Amount of allowed variation, in msecs.
private static long FUDGE_FACTOR=40;
private String _name;
private boolean _throwException;
TimerTestTask(String name) {
this(name, false);
}
TimerTestTask(String name, boolean throwException) {
this._name=name;
this._throwException=throwException;
}
public void run() {
long now=System.currentTimeMillis();
_runs.add(new Long(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 ((Long)_runs.get(i)).longValue();
}
}
}