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