Created
March 6, 2012 19:53
-
-
Save benjchristensen/1988624 to your computer and use it in GitHub Desktop.
MockTimer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* A mock for the java.util.Timer.schedule() method that allows manually incrementing time | |
* so as to have deterministic unit tests. | |
*/ | |
private static class MockTimer implements ScheduledTimer { | |
private final ArrayList<ATask> tasks = new ArrayList<ATask>(); | |
@Override | |
public synchronized void schedule(TimerTask task, int delay, int period) { | |
tasks.add(new ATask(task, delay, period)); | |
} | |
/** | |
* Increment time by X. Note that incrementing by multiples of delay or period time will NOT execute multiple times. | |
* <p> | |
* You must call incrementTime multiple times each increment being larger than 'period' on subsequent calls to cause multiple executions. | |
* <p> | |
* This is because executing multiple times in a tight-loop would not achieve the correct behavior, such as batching, since it will all execute "now" not after intervals of time. | |
* | |
* @param timeInMilliseconds | |
*/ | |
public synchronized void incrementTime(int timeInMilliseconds) { | |
for (ATask t : tasks) { | |
t.incrementTime(timeInMilliseconds); | |
} | |
} | |
private static class ATask { | |
final TimerTask task; | |
final int delay; | |
final int period; | |
// our relative time that we'll use | |
volatile int time = 0; | |
volatile int executionCount = 0; | |
private ATask(TimerTask task, int delay, int period) { | |
this.task = task; | |
this.delay = delay; | |
this.period = period; | |
} | |
public synchronized void incrementTime(int timeInMilliseconds) { | |
time += timeInMilliseconds; | |
if (task != null) { | |
if (executionCount == 0) { | |
System.out.println("ExecutionCount 0 => Time: " + time + " Delay: " + delay); | |
if (time >= delay) { | |
// first execution, we're past the delay time | |
executeTask(); | |
} | |
} else { | |
System.out.println("ExecutionCount 1+ => Time: " + time + " Period: " + period); | |
if (time >= period) { | |
// subsequent executions, we're past the interval time | |
executeTask(); | |
} | |
} | |
} | |
} | |
private synchronized void executeTask() { | |
System.out.println("Executing task ..."); | |
task.run(); | |
this.time = 0; // we reset time after each execution | |
this.executionCount++; | |
System.out.println("executionCount: " + executionCount); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private static interface ScheduledTimer { | |
void schedule(TimerTask collapseTask, int delay, int period); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I needed to mock out java.util.Timer to make some unit tests deterministic and couldn't find any quick solution (that didn't involve statically altering System.currentTimeMillis() which sounded painful) so hacked this code together.
It only handles a very limited use case, but it works for what it was designed to do.
Example of how the unit test looks while using Thread.sleep (non-deterministic):
Here is how the same unit test looks with Thread.sleep() replaced with MockTimer.incrementTime() which makes it deterministic.