1 /* 2 * Copyright (c) 2018 Mockito contributors 3 * This program is made available under the terms of the MIT License. 4 */ 5 package org.mockitoutil.async; 6 7 import java.util.LinkedList; 8 9 /** 10 * Streamlines testing async code for Mockito tests. 11 * 12 * Instances of this class are NOT thread safe (intentionally, they are not required to be thread safe) 13 * 14 * //TODO convert to test rule 15 */ 16 public class AsyncTesting { 17 18 // Sanity limit of threas. Increase it if justified. 19 private static final int MAX_THREADS = 4; 20 21 private final LinkedList<Exception> problems = new LinkedList<Exception>(); 22 private final LinkedList<Thread> threads = new LinkedList<Thread>(); 23 private boolean stopping; 24 25 /** 26 * Schedules execution of runnable with some delay. 27 * Starts thread immediately and returns. 28 * The thread will execute the runnable after given delay in millis. 29 * 30 * @param delayMillis - the delay in millis 31 * @param runnable - to be executed in a thread after delay 32 */ runAfter(final int delayMillis, final Runnable runnable)33 public void runAfter(final int delayMillis, final Runnable runnable) { 34 if (threads.size() == MAX_THREADS) { 35 throw new RuntimeException( 36 "Please don't schedule any more threads. Figure out how to test the code with minimum amount of threads"); 37 } 38 Thread t = 39 new Thread() { 40 public void run() { 41 try { 42 Thread.sleep(delayMillis); 43 runnable.run(); 44 } catch (Exception e) { 45 boolean cleanStop = e instanceof InterruptedException && stopping; 46 if (!cleanStop) { 47 problems.add(e); 48 } 49 } 50 } 51 }; 52 System.out.println( 53 "[AsyncTesting] Starting thread that will execute the runnable after " 54 + delayMillis 55 + " millis. Threads so far: " 56 + threads.size()); 57 threads.add(t); 58 t.start(); 59 } 60 61 /** 62 * Interrupts and waits for all threads to complete (using 'join()'). 63 * Rethrows exceptions accumulated by the execution of threads. 64 */ cleanUp()65 public void cleanUp() { 66 stopping = true; 67 System.out.println( 68 "[AsyncTesting] Interrupting and waiting for " 69 + threads.size() 70 + " threads to complete..."); 71 while (!threads.isEmpty()) { 72 Thread t = threads.removeFirst(); 73 try { 74 t.interrupt(); 75 t.join(); 76 } catch (InterruptedException e) { 77 throw new RuntimeException(e); 78 } 79 } 80 if (!problems.isEmpty()) { 81 throw new RuntimeException( 82 "Caught " + problems.size() + " exception(s). First one is included as cause", 83 problems.getFirst()); 84 } 85 } 86 } 87