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 public class AsyncTesting { 15 16 //Sanity limit of threas. Increase it if justified. 17 private final static int MAX_THREADS = 3; 18 19 private final LinkedList<Exception> problems = new LinkedList<Exception>(); 20 private final LinkedList<Thread> threads = new LinkedList<Thread>(); 21 private boolean stopping; 22 23 /** 24 * Schedules execution of runnable with some delay. 25 * Starts thread immediately and returns. 26 * The thread will execute the runnable after given delay in millis. 27 * 28 * @param delayMillis - the delay in millis 29 * @param runnable - to be executed in a thread after delay 30 */ runAfter(final int delayMillis, final Runnable runnable)31 public void runAfter(final int delayMillis, final Runnable runnable) { 32 if (threads.size() == MAX_THREADS) { 33 throw new RuntimeException("Please don't schedule any more threads. Figure out how to test the code with minimum amount of threads"); 34 } 35 Thread t = new Thread() { 36 public void run() { 37 try { 38 Thread.sleep(delayMillis); 39 runnable.run(); 40 } catch (Exception e) { 41 boolean cleanStop = e instanceof InterruptedException && stopping; 42 if (!cleanStop) { 43 problems.add(e); 44 } 45 } 46 } 47 }; 48 System.out.println("[AsyncTesting] Starting thread that will execute the runnable after " + delayMillis + " millis. Threads so far: " + threads.size()); 49 threads.add(t); 50 t.start(); 51 } 52 53 /** 54 * Interrupts and waits for all threads to complete (using 'join()'). 55 * Rethrows exceptions accumulated by the execution of threads. 56 */ cleanUp()57 public void cleanUp() { 58 stopping = true; 59 System.out.println("[AsyncTesting] Interrupting and waiting for " + threads.size() + " threads to complete..."); 60 while(!threads.isEmpty()) { 61 Thread t = threads.removeFirst(); 62 try { 63 t.interrupt(); 64 t.join(); 65 } catch (InterruptedException e) { 66 throw new RuntimeException(e); 67 } 68 } 69 if (!problems.isEmpty()) { 70 throw new RuntimeException("Caught " + problems.size() + " exception(s). First one is included as cause", problems.getFirst()); 71 } 72 } 73 } 74