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