package org.robolectric.util;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import static org.assertj.core.api.Assertions.assertThat;
import static org.robolectric.util.Scheduler.IdleState.*;
@RunWith(JUnit4.class)
public class SchedulerTest {
private final Scheduler scheduler = new Scheduler();
private final List<String> transcript = new ArrayList<>();
private long startTime;
@Before
public void setUp() throws Exception {
scheduler.pause();
startTime = scheduler.getCurrentTime();
}
@Test
public void whenIdleStateIsConstantIdle_isPausedReturnsFalse() {
scheduler.setIdleState(CONSTANT_IDLE);
assertThat(scheduler.isPaused()).isFalse();
}
@Test
public void whenIdleStateIsUnPaused_isPausedReturnsFalse() {
scheduler.setIdleState(UNPAUSED);
assertThat(scheduler.isPaused()).isFalse();
}
@Test
public void whenIdleStateIsPaused_isPausedReturnsTrue() {
scheduler.setIdleState(PAUSED);
assertThat(scheduler.isPaused()).isTrue();
}
@Test
public void pause_setsIdleState() {
scheduler.setIdleState(UNPAUSED);
scheduler.pause();
assertThat(scheduler.getIdleState()).isSameAs(PAUSED);
}
@Test
@SuppressWarnings("deprecation")
public void idleConstantly_setsIdleState() {
scheduler.setIdleState(UNPAUSED);
scheduler.idleConstantly(true);
assertThat(scheduler.getIdleState()).isSameAs(CONSTANT_IDLE);
scheduler.idleConstantly(false);
assertThat(scheduler.getIdleState()).isSameAs(UNPAUSED);
}
@Test
public void unPause_setsIdleState() {
scheduler.setIdleState(PAUSED);
scheduler.unPause();
assertThat(scheduler.getIdleState()).isSameAs(UNPAUSED);
}
@Test
public void setIdleStateToUnPause_shouldRunPendingTasks() {
scheduler.postDelayed(new AddToTranscript("one"), 0);
scheduler.postDelayed(new AddToTranscript("two"), 0);
scheduler.postDelayed(new AddToTranscript("three"), 1000);
assertThat(transcript).isEmpty();
final long time = scheduler.getCurrentTime();
scheduler.setIdleState(UNPAUSED);
assertThat(transcript).containsExactly("one", "two");
assertThat(scheduler.getCurrentTime()).as("time").isEqualTo(time);
}
@Test
public void setIdleStateToConstantIdle_shouldRunAllTasks() {
scheduler.postDelayed(new AddToTranscript("one"), 0);
scheduler.postDelayed(new AddToTranscript("two"), 0);
scheduler.postDelayed(new AddToTranscript("three"), 1000);
assertThat(transcript).isEmpty();
final long time = scheduler.getCurrentTime();
scheduler.setIdleState(CONSTANT_IDLE);
assertThat(transcript).containsExactly("one", "two", "three");
assertThat(scheduler.getCurrentTime()).as("time").isEqualTo(time + 1000);
}
@Test
public void unPause_shouldRunPendingTasks() {
scheduler.postDelayed(new AddToTranscript("one"), 0);
scheduler.postDelayed(new AddToTranscript("two"), 0);
scheduler.postDelayed(new AddToTranscript("three"), 1000);
assertThat(transcript).isEmpty();
final long time = scheduler.getCurrentTime();
scheduler.unPause();
assertThat(transcript).containsExactly("one", "two");
assertThat(scheduler.getCurrentTime()).as("time").isEqualTo(time);
}
@Test
@SuppressWarnings("deprecation")
public void idleConstantlyTrue_shouldRunAllTasks() {
scheduler.postDelayed(new AddToTranscript("one"), 0);
scheduler.postDelayed(new AddToTranscript("two"), 0);
scheduler.postDelayed(new AddToTranscript("three"), 1000);
assertThat(transcript).isEmpty();
final long time = scheduler.getCurrentTime();
scheduler.idleConstantly(true);
assertThat(transcript).containsExactly("one", "two", "three");
assertThat(scheduler.getCurrentTime()).as("time").isEqualTo(time + 1000);
}
@Test
public void advanceTo_shouldAdvanceTimeEvenIfThereIsNoWork() throws Exception {
scheduler.advanceTo(1000);
assertThat(scheduler.getCurrentTime()).isEqualTo(1000);
}
@Test
public void advanceBy_returnsTrueIffSomeJobWasRun() throws Exception {
scheduler.postDelayed(new AddToTranscript("one"), 0);
scheduler.postDelayed(new AddToTranscript("two"), 0);
scheduler.postDelayed(new AddToTranscript("three"), 1000);
assertThat(scheduler.advanceBy(0)).isTrue();
assertThat(transcript).containsExactly("one", "two");
transcript.clear();
assertThat(scheduler.advanceBy(0)).isFalse();
assertThat(transcript).isEmpty();
assertThat(scheduler.advanceBy(1000)).isTrue();
assertThat(transcript).containsExactly("three");
}
@Test
public void postDelayed_addsAJobToBeRunInTheFuture() throws Exception {
scheduler.postDelayed(new AddToTranscript("one"), 1000);
scheduler.postDelayed(new AddToTranscript("two"), 2000);
scheduler.postDelayed(new AddToTranscript("three"), 3000);
scheduler.advanceBy(1000);
assertThat(transcript).containsExactly("one");
transcript.clear();
scheduler.advanceBy(500);
assertThat(transcript).isEmpty();
scheduler.advanceBy(501);
assertThat(transcript).containsExactly("two");
transcript.clear();
scheduler.advanceBy(999);
assertThat(transcript).containsExactly("three");
}
@Test
public void postDelayed_whileIdlingConstantly_executesImmediately() {
scheduler.setIdleState(CONSTANT_IDLE);
scheduler.postDelayed(new AddToTranscript("one"), 1000);
assertThat(transcript).containsExactly("one");
}
@Test
public void postDelayed_whileIdlingConstantly_advancesTime() {
scheduler.setIdleState(CONSTANT_IDLE);
scheduler.postDelayed(new AddToTranscript("one"), 1000);
assertThat(scheduler.getCurrentTime()).isEqualTo(1000 + startTime);
}
@Test
public void postAtFrontOfQueue_addsJobAtFrontOfQueue() throws Exception {
scheduler.post(new AddToTranscript("one"));
scheduler.post(new AddToTranscript("two"));
scheduler.postAtFrontOfQueue(new AddToTranscript("three"));
scheduler.runOneTask();
assertThat(transcript).containsExactly("three");
transcript.clear();
scheduler.runOneTask();
assertThat(transcript).containsExactly("one");
transcript.clear();
scheduler.runOneTask();
assertThat(transcript).containsExactly("two");
}
@Test
public void postAtFrontOfQueue_whenUnpaused_runsJobs() throws Exception {
scheduler.unPause();
scheduler.postAtFrontOfQueue(new AddToTranscript("three"));
assertThat(transcript).containsExactly("three");
}
@Test
public void postDelayed_whenMoreItemsAreAdded_runsJobs() throws Exception {
scheduler.postDelayed(new Runnable() {
@Override
public void run() {
transcript.add("one");
scheduler.postDelayed(new Runnable() {
@Override
public void run() {
transcript.add("two");
scheduler.postDelayed(new AddToTranscript("three"), 1000);
}
}, 1000);
}
}, 1000);
scheduler.advanceBy(1000);
assertThat(transcript).containsExactly("one");
transcript.clear();
scheduler.advanceBy(500);
assertThat(transcript).isEmpty();
scheduler.advanceBy(501);
assertThat(transcript).containsExactly("two");
transcript.clear();
scheduler.advanceBy(999);
assertThat(transcript).containsExactly("three");
}
@Test
public void remove_ShouldRemoveAllInstancesOfRunnableFromQueue() throws Exception {
scheduler.post(new TestRunnable());
TestRunnable runnable = new TestRunnable();
scheduler.post(runnable);
scheduler.post(runnable);
assertThat(scheduler.size()).isEqualTo(3);
scheduler.remove(runnable);
assertThat(scheduler.size()).isEqualTo(1);
scheduler.advanceToLastPostedRunnable();
assertThat(runnable.wasRun).isFalse();
}
@Test
public void reset_shouldUnPause() throws Exception {
scheduler.pause();
TestRunnable runnable = new TestRunnable();
scheduler.post(runnable);
assertThat(runnable.wasRun).isFalse();
scheduler.reset();
scheduler.post(runnable);
assertThat(runnable.wasRun).isTrue();
}
@Test
public void reset_shouldClearPendingRunnables() throws Exception {
scheduler.pause();
TestRunnable runnable1 = new TestRunnable();
scheduler.post(runnable1);
assertThat(runnable1.wasRun).isFalse();
scheduler.reset();
TestRunnable runnable2 = new TestRunnable();
scheduler.post(runnable2);
assertThat(runnable1.wasRun).isFalse();
assertThat(runnable2.wasRun).isTrue();
}
@Test
public void nestedPost_whilePaused_doesntAutomaticallyExecute() {
final List<Integer> order = new ArrayList<>();
scheduler.postDelayed(new Runnable() {
@Override
public void run() {
order.add(1);
scheduler.post(new Runnable() {
@Override
public void run() {
order.add(4);
}
});
order.add(2);
}
}, 0);
scheduler.postDelayed(new Runnable() {
@Override
public void run() {
order.add(3);
}
}, 0);
scheduler.runOneTask();
assertThat(order).as("order:first run").containsExactly(1, 2);
assertThat(scheduler.size()).as("size:first run").isEqualTo(2);
scheduler.runOneTask();
assertThat(order).as("order:second run").containsExactly(1, 2, 3);
assertThat(scheduler.size()).as("size:second run").isEqualTo(1);
scheduler.runOneTask();
assertThat(order).as("order:third run").containsExactly(1, 2, 3, 4);
assertThat(scheduler.size()).as("size:second run").isEqualTo(0);
}
@Test
public void nestedPost_whileUnpaused_automaticallyExecutes3After() {
final List<Integer> order = new ArrayList<>();
scheduler.unPause();
scheduler.postDelayed(new Runnable() {
@Override
public void run() {
order.add(1);
scheduler.post(new Runnable() {
@Override
public void run() {
order.add(3);
}
});
order.add(2);
}
}, 0);
assertThat(order).as("order").containsExactly(1, 2, 3);
assertThat(scheduler.size()).as("size").isEqualTo(0);
}
@Test
public void nestedPostAtFront_whilePaused_runsBeforeSubsequentPost() {
final List<Integer> order = new ArrayList<>();
scheduler.postDelayed(new Runnable() {
@Override
public void run() {
order.add(1);
scheduler.postAtFrontOfQueue(new Runnable() {
@Override
public void run() {
order.add(3);
}
});
order.add(2);
}
}, 0);
scheduler.postDelayed(new Runnable() {
@Override
public void run() {
order.add(4);
}
}, 0);
scheduler.advanceToLastPostedRunnable();
assertThat(order).as("order").containsExactly(1, 2, 3, 4);
assertThat(scheduler.size()).as("size").isEqualTo(0);
}
@Test
public void nestedPostAtFront_whileUnpaused_runsAfter() {
final List<Integer> order = new ArrayList<>();
scheduler.unPause();
scheduler.postDelayed(new Runnable() {
@Override
public void run() {
order.add(1);
scheduler.postAtFrontOfQueue(new Runnable() {
@Override
public void run() {
order.add(3);
}
});
order.add(2);
}
}, 0);
assertThat(order).as("order").containsExactly(1, 2, 3);
assertThat(scheduler.size()).as("size").isEqualTo(0);
}
@Test
public void nestedPostDelayed_whileUnpaused_doesntAutomaticallyExecute3() {
final List<Integer> order = new ArrayList<>();
scheduler.unPause();
scheduler.postDelayed(new Runnable() {
@Override
public void run() {
order.add(1);
scheduler.postDelayed(new Runnable() {
@Override
public void run() {
order.add(3);
}
}, 1);
order.add(2);
}
}, 0);
assertThat(order).as("order:before").containsExactly(1, 2);
assertThat(scheduler.size()).as("size:before").isEqualTo(1);
scheduler.advanceToLastPostedRunnable();
assertThat(order).as("order:after").containsExactly(1, 2, 3);
assertThat(scheduler.size()).as("size:after").isEqualTo(0);
assertThat(scheduler.getCurrentTime()).as("time:after").isEqualTo(1 + startTime);
}
@Test
public void nestedPostDelayed_whenIdlingConstantly_automaticallyExecutes3After() {
final List<Integer> order = new ArrayList<>();
scheduler.setIdleState(CONSTANT_IDLE);
scheduler.postDelayed(new Runnable() {
@Override
public void run() {
order.add(1);
scheduler.postDelayed(new Runnable() {
@Override
public void run() {
order.add(3);
}
}, 1);
order.add(2);
}
}, 0);
assertThat(order).as("order").containsExactly(1, 2, 3);
assertThat(scheduler.size()).as("size").isEqualTo(0);
assertThat(scheduler.getCurrentTime()).as("time").isEqualTo(1 + startTime);
}
@Test
public void post_whenTheRunnableThrows_executesSubsequentRunnables() throws Exception {
final List<Integer> runnablesThatWereRun = new ArrayList<>();
scheduler.post(new Runnable() {
@Override
public void run() {
runnablesThatWereRun.add(1);
throw new RuntimeException("foo");
}
});
try {
scheduler.unPause();
} catch (RuntimeException ignored) { }
scheduler.post(new Runnable() {
@Override
public void run() {
runnablesThatWereRun.add(2);
}
});
assertThat(runnablesThatWereRun).containsExactly(1, 2);
}
@Test(timeout=1000)
public void schedulerAllowsConcurrentTimeRead_whileLockIsHeld() throws InterruptedException {
final AtomicLong l = new AtomicLong();
Thread t = new Thread("schedulerAllowsConcurrentTimeRead") {
@Override
public void run() {
l.set(scheduler.getCurrentTime());
}
};
// Grab the lock and then start a thread that tries to get the current time. The other thread
// should not deadlock.
synchronized (scheduler) {
t.start();
t.join();
}
}
@Test(timeout = 1000)
public void schedulerAllowsConcurrentStateRead_whileLockIsHeld() throws InterruptedException {
Thread t = new Thread("schedulerAllowsConcurrentStateRead") {
@Override
public void run() {
scheduler.getIdleState();
}
};
// Grab the lock and then start a thread that tries to get the idle state. The other thread
// should not deadlock.
synchronized (scheduler) {
t.start();
t.join();
}
}
@Test(timeout = 1000)
public void schedulerAllowsConcurrentIsPaused_whileLockIsHeld() throws InterruptedException {
Thread t = new Thread("schedulerAllowsConcurrentIsPaused") {
@Override
public void run() {
scheduler.isPaused();
}
};
// Grab the lock and then start a thread that tries to get the paused state. The other thread
// should not deadlock.
synchronized (scheduler) {
t.start();
t.join();
}
}
private class AddToTranscript implements Runnable {
private String event;
public AddToTranscript(String event) {
this.event = event;
}
@Override
public void run() {
transcript.add(event);
}
}
}