Home | History | Annotate | Download | only in util
      1 package org.robolectric.util;
      2 
      3 import static org.assertj.core.api.Assertions.assertThat;
      4 import static org.robolectric.util.Scheduler.IdleState.CONSTANT_IDLE;
      5 import static org.robolectric.util.Scheduler.IdleState.PAUSED;
      6 import static org.robolectric.util.Scheduler.IdleState.UNPAUSED;
      7 
      8 import java.util.ArrayList;
      9 import java.util.List;
     10 import java.util.concurrent.atomic.AtomicLong;
     11 import org.junit.Before;
     12 import org.junit.Test;
     13 import org.junit.runner.RunWith;
     14 import org.junit.runners.JUnit4;
     15 
     16 @RunWith(JUnit4.class)
     17 public class SchedulerTest {
     18   private final Scheduler scheduler = new Scheduler();
     19   private final List<String> transcript = new ArrayList<>();
     20 
     21   private long startTime;
     22 
     23   @Before
     24   public void setUp() throws Exception {
     25     scheduler.pause();
     26     startTime = scheduler.getCurrentTime();
     27   }
     28 
     29   @Test
     30   public void whenIdleStateIsConstantIdle_isPausedReturnsFalse() {
     31     scheduler.setIdleState(CONSTANT_IDLE);
     32     assertThat(scheduler.isPaused()).isFalse();
     33   }
     34 
     35   @Test
     36   public void whenIdleStateIsUnPaused_isPausedReturnsFalse() {
     37     scheduler.setIdleState(UNPAUSED);
     38     assertThat(scheduler.isPaused()).isFalse();
     39   }
     40 
     41   @Test
     42   public void whenIdleStateIsPaused_isPausedReturnsTrue() {
     43     scheduler.setIdleState(PAUSED);
     44     assertThat(scheduler.isPaused()).isTrue();
     45   }
     46 
     47   @Test
     48   public void pause_setsIdleState() {
     49     scheduler.setIdleState(UNPAUSED);
     50     scheduler.pause();
     51     assertThat(scheduler.getIdleState()).isSameAs(PAUSED);
     52   }
     53 
     54   @Test
     55   @SuppressWarnings("deprecation")
     56   public void idleConstantly_setsIdleState() {
     57     scheduler.setIdleState(UNPAUSED);
     58     scheduler.idleConstantly(true);
     59     assertThat(scheduler.getIdleState()).isSameAs(CONSTANT_IDLE);
     60     scheduler.idleConstantly(false);
     61     assertThat(scheduler.getIdleState()).isSameAs(UNPAUSED);
     62   }
     63 
     64   @Test
     65   public void unPause_setsIdleState() {
     66     scheduler.setIdleState(PAUSED);
     67     scheduler.unPause();
     68     assertThat(scheduler.getIdleState()).isSameAs(UNPAUSED);
     69   }
     70 
     71   @Test
     72   public void setIdleStateToUnPause_shouldRunPendingTasks() {
     73     scheduler.postDelayed(new AddToTranscript("one"), 0);
     74     scheduler.postDelayed(new AddToTranscript("two"), 0);
     75     scheduler.postDelayed(new AddToTranscript("three"), 1000);
     76     assertThat(transcript).isEmpty();
     77     final long time = scheduler.getCurrentTime();
     78     scheduler.setIdleState(UNPAUSED);
     79     assertThat(transcript).containsExactly("one", "two");
     80     assertThat(scheduler.getCurrentTime()).as("time").isEqualTo(time);
     81   }
     82 
     83   @Test
     84   public void setIdleStateToConstantIdle_shouldRunAllTasks() {
     85     scheduler.postDelayed(new AddToTranscript("one"), 0);
     86     scheduler.postDelayed(new AddToTranscript("two"), 0);
     87     scheduler.postDelayed(new AddToTranscript("three"), 1000);
     88     assertThat(transcript).isEmpty();
     89     final long time = scheduler.getCurrentTime();
     90     scheduler.setIdleState(CONSTANT_IDLE);
     91     assertThat(transcript).containsExactly("one", "two", "three");
     92     assertThat(scheduler.getCurrentTime()).as("time").isEqualTo(time + 1000);
     93   }
     94 
     95   @Test
     96   public void unPause_shouldRunPendingTasks() {
     97     scheduler.postDelayed(new AddToTranscript("one"), 0);
     98     scheduler.postDelayed(new AddToTranscript("two"), 0);
     99     scheduler.postDelayed(new AddToTranscript("three"), 1000);
    100     assertThat(transcript).isEmpty();
    101     final long time = scheduler.getCurrentTime();
    102     scheduler.unPause();
    103     assertThat(transcript).containsExactly("one", "two");
    104     assertThat(scheduler.getCurrentTime()).as("time").isEqualTo(time);
    105   }
    106 
    107   @Test
    108   @SuppressWarnings("deprecation")
    109   public void idleConstantlyTrue_shouldRunAllTasks() {
    110     scheduler.postDelayed(new AddToTranscript("one"), 0);
    111     scheduler.postDelayed(new AddToTranscript("two"), 0);
    112     scheduler.postDelayed(new AddToTranscript("three"), 1000);
    113     assertThat(transcript).isEmpty();
    114     final long time = scheduler.getCurrentTime();
    115     scheduler.idleConstantly(true);
    116     assertThat(transcript).containsExactly("one", "two", "three");
    117     assertThat(scheduler.getCurrentTime()).as("time").isEqualTo(time + 1000);
    118   }
    119 
    120   @Test
    121   public void advanceTo_shouldAdvanceTimeEvenIfThereIsNoWork() throws Exception {
    122     scheduler.advanceTo(1000);
    123     assertThat(scheduler.getCurrentTime()).isEqualTo(1000);
    124   }
    125 
    126   @Test
    127   public void advanceBy_returnsTrueIffSomeJobWasRun() throws Exception {
    128     scheduler.postDelayed(new AddToTranscript("one"), 0);
    129     scheduler.postDelayed(new AddToTranscript("two"), 0);
    130     scheduler.postDelayed(new AddToTranscript("three"), 1000);
    131 
    132     assertThat(scheduler.advanceBy(0)).isTrue();
    133     assertThat(transcript).containsExactly("one", "two");
    134     transcript.clear();
    135 
    136     assertThat(scheduler.advanceBy(0)).isFalse();
    137     assertThat(transcript).isEmpty();
    138 
    139     assertThat(scheduler.advanceBy(1000)).isTrue();
    140     assertThat(transcript).containsExactly("three");
    141   }
    142 
    143   @Test
    144   public void postDelayed_addsAJobToBeRunInTheFuture() throws Exception {
    145     scheduler.postDelayed(new AddToTranscript("one"), 1000);
    146     scheduler.postDelayed(new AddToTranscript("two"), 2000);
    147     scheduler.postDelayed(new AddToTranscript("three"), 3000);
    148 
    149     scheduler.advanceBy(1000);
    150     assertThat(transcript).containsExactly("one");
    151     transcript.clear();
    152 
    153     scheduler.advanceBy(500);
    154     assertThat(transcript).isEmpty();
    155 
    156     scheduler.advanceBy(501);
    157     assertThat(transcript).containsExactly("two");
    158     transcript.clear();
    159 
    160     scheduler.advanceBy(999);
    161     assertThat(transcript).containsExactly("three");
    162   }
    163 
    164   @Test
    165   public void postDelayed_whileIdlingConstantly_executesImmediately() {
    166     scheduler.setIdleState(CONSTANT_IDLE);
    167     scheduler.postDelayed(new AddToTranscript("one"), 1000);
    168 
    169     assertThat(transcript).containsExactly("one");
    170   }
    171 
    172   @Test
    173   public void postDelayed_whileIdlingConstantly_advancesTime() {
    174     scheduler.setIdleState(CONSTANT_IDLE);
    175     scheduler.postDelayed(new AddToTranscript("one"), 1000);
    176 
    177     assertThat(scheduler.getCurrentTime()).isEqualTo(1000 + startTime);
    178   }
    179 
    180   @Test
    181   public void postAtFrontOfQueue_addsJobAtFrontOfQueue() throws Exception {
    182     scheduler.post(new AddToTranscript("one"));
    183     scheduler.post(new AddToTranscript("two"));
    184     scheduler.postAtFrontOfQueue(new AddToTranscript("three"));
    185 
    186     scheduler.runOneTask();
    187     assertThat(transcript).containsExactly("three");
    188     transcript.clear();
    189 
    190     scheduler.runOneTask();
    191     assertThat(transcript).containsExactly("one");
    192     transcript.clear();
    193 
    194     scheduler.runOneTask();
    195     assertThat(transcript).containsExactly("two");
    196   }
    197 
    198   @Test
    199   public void postAtFrontOfQueue_whenUnpaused_runsJobs() throws Exception {
    200     scheduler.unPause();
    201     scheduler.postAtFrontOfQueue(new AddToTranscript("three"));
    202     assertThat(transcript).containsExactly("three");
    203   }
    204 
    205   @Test
    206   public void postDelayed_whenMoreItemsAreAdded_runsJobs() throws Exception {
    207     scheduler.postDelayed(new Runnable() {
    208       @Override
    209       public void run() {
    210         transcript.add("one");
    211         scheduler.postDelayed(new Runnable() {
    212           @Override
    213           public void run() {
    214             transcript.add("two");
    215             scheduler.postDelayed(new AddToTranscript("three"), 1000);
    216           }
    217         }, 1000);
    218       }
    219     }, 1000);
    220 
    221     scheduler.advanceBy(1000);
    222     assertThat(transcript).containsExactly("one");
    223     transcript.clear();
    224 
    225     scheduler.advanceBy(500);
    226     assertThat(transcript).isEmpty();
    227 
    228     scheduler.advanceBy(501);
    229     assertThat(transcript).containsExactly("two");
    230     transcript.clear();
    231 
    232     scheduler.advanceBy(999);
    233     assertThat(transcript).containsExactly("three");
    234   }
    235 
    236   @Test
    237   public void remove_ShouldRemoveAllInstancesOfRunnableFromQueue() throws Exception {
    238     scheduler.post(new TestRunnable());
    239     TestRunnable runnable = new TestRunnable();
    240     scheduler.post(runnable);
    241     scheduler.post(runnable);
    242     assertThat(scheduler.size()).isEqualTo(3);
    243     scheduler.remove(runnable);
    244     assertThat(scheduler.size()).isEqualTo(1);
    245     scheduler.advanceToLastPostedRunnable();
    246     assertThat(runnable.wasRun).isFalse();
    247   }
    248 
    249   @Test
    250   public void reset_shouldUnPause() throws Exception {
    251     scheduler.pause();
    252 
    253     TestRunnable runnable = new TestRunnable();
    254     scheduler.post(runnable);
    255 
    256     assertThat(runnable.wasRun).isFalse();
    257 
    258     scheduler.reset();
    259     scheduler.post(runnable);
    260     assertThat(runnable.wasRun).isTrue();
    261   }
    262 
    263   @Test
    264   public void reset_shouldClearPendingRunnables() throws Exception {
    265     scheduler.pause();
    266 
    267     TestRunnable runnable1 = new TestRunnable();
    268     scheduler.post(runnable1);
    269 
    270     assertThat(runnable1.wasRun).isFalse();
    271 
    272     scheduler.reset();
    273 
    274     TestRunnable runnable2 = new TestRunnable();
    275     scheduler.post(runnable2);
    276 
    277     assertThat(runnable1.wasRun).isFalse();
    278     assertThat(runnable2.wasRun).isTrue();
    279   }
    280 
    281   @Test
    282   public void nestedPost_whilePaused_doesntAutomaticallyExecute() {
    283     final List<Integer> order = new ArrayList<>();
    284     scheduler.postDelayed(new Runnable() {
    285       @Override
    286       public void run() {
    287         order.add(1);
    288         scheduler.post(new Runnable() {
    289           @Override
    290           public void run() {
    291             order.add(4);
    292           }
    293         });
    294         order.add(2);
    295       }
    296     }, 0);
    297     scheduler.postDelayed(new Runnable() {
    298       @Override
    299       public void run() {
    300         order.add(3);
    301       }
    302     }, 0);
    303     scheduler.runOneTask();
    304 
    305     assertThat(order).as("order:first run").containsExactly(1, 2);
    306     assertThat(scheduler.size()).as("size:first run").isEqualTo(2);
    307     scheduler.runOneTask();
    308     assertThat(order).as("order:second run").containsExactly(1, 2, 3);
    309     assertThat(scheduler.size()).as("size:second run").isEqualTo(1);
    310     scheduler.runOneTask();
    311     assertThat(order).as("order:third run").containsExactly(1, 2, 3, 4);
    312     assertThat(scheduler.size()).as("size:second run").isEqualTo(0);
    313   }
    314 
    315   @Test
    316   public void nestedPost_whileUnpaused_automaticallyExecutes3After() {
    317     final List<Integer> order = new ArrayList<>();
    318     scheduler.unPause();
    319     scheduler.postDelayed(new Runnable() {
    320       @Override
    321       public void run() {
    322         order.add(1);
    323         scheduler.post(new Runnable() {
    324           @Override
    325           public void run() {
    326             order.add(3);
    327           }
    328         });
    329         order.add(2);
    330       }
    331     }, 0);
    332 
    333     assertThat(order).as("order").containsExactly(1, 2, 3);
    334     assertThat(scheduler.size()).as("size").isEqualTo(0);
    335   }
    336 
    337   @Test
    338   public void nestedPostAtFront_whilePaused_runsBeforeSubsequentPost() {
    339     final List<Integer> order = new ArrayList<>();
    340     scheduler.postDelayed(new Runnable() {
    341       @Override
    342       public void run() {
    343         order.add(1);
    344         scheduler.postAtFrontOfQueue(new Runnable() {
    345           @Override
    346           public void run() {
    347             order.add(3);
    348           }
    349         });
    350         order.add(2);
    351       }
    352     }, 0);
    353     scheduler.postDelayed(new Runnable() {
    354       @Override
    355       public void run() {
    356         order.add(4);
    357       }
    358     }, 0);
    359     scheduler.advanceToLastPostedRunnable();
    360     assertThat(order).as("order").containsExactly(1, 2, 3, 4);
    361     assertThat(scheduler.size()).as("size").isEqualTo(0);
    362   }
    363 
    364   @Test
    365   public void nestedPostAtFront_whileUnpaused_runsAfter() {
    366     final List<Integer> order = new ArrayList<>();
    367     scheduler.unPause();
    368     scheduler.postDelayed(new Runnable() {
    369       @Override
    370       public void run() {
    371         order.add(1);
    372         scheduler.postAtFrontOfQueue(new Runnable() {
    373           @Override
    374           public void run() {
    375             order.add(3);
    376           }
    377         });
    378         order.add(2);
    379       }
    380     }, 0);
    381     assertThat(order).as("order").containsExactly(1, 2, 3);
    382     assertThat(scheduler.size()).as("size").isEqualTo(0);
    383   }
    384 
    385   @Test
    386   public void nestedPostDelayed_whileUnpaused_doesntAutomaticallyExecute3() {
    387     final List<Integer> order = new ArrayList<>();
    388     scheduler.unPause();
    389     scheduler.postDelayed(new Runnable() {
    390       @Override
    391       public void run() {
    392         order.add(1);
    393         scheduler.postDelayed(new Runnable() {
    394           @Override
    395           public void run() {
    396             order.add(3);
    397           }
    398         }, 1);
    399         order.add(2);
    400       }
    401     }, 0);
    402 
    403     assertThat(order).as("order:before").containsExactly(1, 2);
    404     assertThat(scheduler.size()).as("size:before").isEqualTo(1);
    405     scheduler.advanceToLastPostedRunnable();
    406     assertThat(order).as("order:after").containsExactly(1, 2, 3);
    407     assertThat(scheduler.size()).as("size:after").isEqualTo(0);
    408     assertThat(scheduler.getCurrentTime()).as("time:after").isEqualTo(1 + startTime);
    409   }
    410 
    411   @Test
    412   public void nestedPostDelayed_whenIdlingConstantly_automaticallyExecutes3After() {
    413     final List<Integer> order = new ArrayList<>();
    414     scheduler.setIdleState(CONSTANT_IDLE);
    415     scheduler.postDelayed(new Runnable() {
    416       @Override
    417       public void run() {
    418         order.add(1);
    419         scheduler.postDelayed(new Runnable() {
    420           @Override
    421           public void run() {
    422             order.add(3);
    423           }
    424         }, 1);
    425         order.add(2);
    426       }
    427     }, 0);
    428 
    429     assertThat(order).as("order").containsExactly(1, 2, 3);
    430     assertThat(scheduler.size()).as("size").isEqualTo(0);
    431     assertThat(scheduler.getCurrentTime()).as("time").isEqualTo(1 + startTime);
    432   }
    433 
    434   @Test
    435   public void post_whenTheRunnableThrows_executesSubsequentRunnables() throws Exception {
    436     final List<Integer> runnablesThatWereRun = new ArrayList<>();
    437     scheduler.post(new Runnable() {
    438       @Override
    439       public void run() {
    440         runnablesThatWereRun.add(1);
    441         throw new RuntimeException("foo");
    442       }
    443     });
    444 
    445     try {
    446       scheduler.unPause();
    447     } catch (RuntimeException ignored) { }
    448 
    449     scheduler.post(new Runnable() {
    450       @Override
    451       public void run() {
    452         runnablesThatWereRun.add(2);
    453       }
    454     });
    455 
    456     assertThat(runnablesThatWereRun).containsExactly(1, 2);
    457   }
    458 
    459   @Test(timeout=1000)
    460   public void schedulerAllowsConcurrentTimeRead_whileLockIsHeld() throws InterruptedException {
    461     final AtomicLong l = new AtomicLong();
    462     Thread t = new Thread("schedulerAllowsConcurrentTimeRead") {
    463       @Override
    464       public void run() {
    465         l.set(scheduler.getCurrentTime());
    466       }
    467     };
    468     // Grab the lock and then start a thread that tries to get the current time. The other thread
    469     // should not deadlock.
    470     synchronized (scheduler) {
    471       t.start();
    472       t.join();
    473     }
    474   }
    475 
    476   @Test(timeout = 1000)
    477   public void schedulerAllowsConcurrentStateRead_whileLockIsHeld() throws InterruptedException {
    478     Thread t = new Thread("schedulerAllowsConcurrentStateRead") {
    479       @Override
    480       public void run() {
    481         scheduler.getIdleState();
    482       }
    483     };
    484     // Grab the lock and then start a thread that tries to get the idle state. The other thread
    485     // should not deadlock.
    486     synchronized (scheduler) {
    487       t.start();
    488       t.join();
    489     }
    490   }
    491 
    492   @Test(timeout = 1000)
    493   public void schedulerAllowsConcurrentIsPaused_whileLockIsHeld() throws InterruptedException {
    494     Thread t = new Thread("schedulerAllowsConcurrentIsPaused") {
    495       @Override
    496       public void run() {
    497         scheduler.isPaused();
    498       }
    499     };
    500     // Grab the lock and then start a thread that tries to get the paused state. The other thread
    501     // should not deadlock.
    502     synchronized (scheduler) {
    503       t.start();
    504       t.join();
    505     }
    506   }
    507 
    508   private class AddToTranscript implements Runnable {
    509     private String event;
    510 
    511     public AddToTranscript(String event) {
    512       this.event = event;
    513     }
    514 
    515     @Override
    516     public void run() {
    517       transcript.add(event);
    518     }
    519   }
    520 }
    521