package io.dropwizard.metrics; import static org.assertj.core.api.Assertions.assertThat; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import io.dropwizard.metrics.InstrumentedThreadFactory; import io.dropwizard.metrics.Meter; import io.dropwizard.metrics.MetricRegistry; public class InstrumentedThreadFactoryTest { private static final int THREAD_COUNT = 10; private final ThreadFactory factory = Executors.defaultThreadFactory(); private final MetricRegistry registry = new MetricRegistry(); private final InstrumentedThreadFactory instrumentedFactory = new InstrumentedThreadFactory(factory, registry, "factory"); private final ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT, instrumentedFactory); /** * Tests all parts of the InstrumentedThreadFactory except for termination since that * is currently difficult to do without race conditions. * * TODO: Try not using real threads in a unit test? */ @Test public void reportsThreadInformation() throws Exception { final CountDownLatch latch = new CountDownLatch(THREAD_COUNT); final Object lock = new Object(); final AtomicInteger interrupted = new AtomicInteger(); /* * Implements a runnable that notifies a latch after locking 'lock'. * This asserts that all threads have to enter the critical block before the * testing thread notifies all. * * We have to do this to guarantee that the thread pool has 10 LIVE threads * before we check the 'created' Meter. */ Runnable fastOne = new Runnable() { @Override public void run() { synchronized (lock) { latch.countDown(); try { lock.wait(); } catch (InterruptedException e) { interrupted.incrementAndGet(); } } } }; Meter created = registry.meter("factory.created"); Meter terminated = registry.meter("factory.terminated"); assertThat(created.getCount()).isEqualTo(0); assertThat(terminated.getCount()).isEqualTo(0); // generate demand so the executor service creates the threads through our factory. for (int i = 0; i < THREAD_COUNT + 1; i++) { executor.submit(fastOne); } latch.await(1, TimeUnit.SECONDS); synchronized (lock) { // wake up all threads. lock.notifyAll(); } assertThat(created.getCount()).isEqualTo(10); assertThat(terminated.getCount()).isEqualTo(0); // terminate all threads in the executor service. executor.shutdown(); executor.awaitTermination(1, TimeUnit.SECONDS); // assert that all threads from the factory have been terminated. // TODO: Remove this? // There is no guarantee that all threads have entered the block where they are // counted as terminated by this time. // assertThat(terminated.getCount()).isEqualTo(10); // Check that none of the threads were interrupted. assertThat(interrupted.get()).isEqualTo(0); } }