/* * Copyright (c) 2018 Mockito contributors * This program is made available under the terms of the MIT License. */ package org.mockitoutil.async; import java.util.LinkedList; /** * Streamlines testing async code for Mockito tests. * * Instances of this class are NOT thread safe (intentionally, they are not required to be thread safe) */ public class AsyncTesting { //Sanity limit of threas. Increase it if justified. private final static int MAX_THREADS = 3; private final LinkedList problems = new LinkedList(); private final LinkedList threads = new LinkedList(); private boolean stopping; /** * Schedules execution of runnable with some delay. * Starts thread immediately and returns. * The thread will execute the runnable after given delay in millis. * * @param delayMillis - the delay in millis * @param runnable - to be executed in a thread after delay */ public void runAfter(final int delayMillis, final Runnable runnable) { if (threads.size() == MAX_THREADS) { throw new RuntimeException("Please don't schedule any more threads. Figure out how to test the code with minimum amount of threads"); } Thread t = new Thread() { public void run() { try { Thread.sleep(delayMillis); runnable.run(); } catch (Exception e) { boolean cleanStop = e instanceof InterruptedException && stopping; if (!cleanStop) { problems.add(e); } } } }; System.out.println("[AsyncTesting] Starting thread that will execute the runnable after " + delayMillis + " millis. Threads so far: " + threads.size()); threads.add(t); t.start(); } /** * Interrupts and waits for all threads to complete (using 'join()'). * Rethrows exceptions accumulated by the execution of threads. */ public void cleanUp() { stopping = true; System.out.println("[AsyncTesting] Interrupting and waiting for " + threads.size() + " threads to complete..."); while(!threads.isEmpty()) { Thread t = threads.removeFirst(); try { t.interrupt(); t.join(); } catch (InterruptedException e) { throw new RuntimeException(e); } } if (!problems.isEmpty()) { throw new RuntimeException("Caught " + problems.size() + " exception(s). First one is included as cause", problems.getFirst()); } } }