package com.codahale.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;

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);
    }
}

@@ -1006,17 +1006,16 @@
 .%0A     *
- 
 %0A     * 
@@ -1172,21 +1172,34 @@
 wnLatch 
-latch
+allTasksAreCreated
  = new C
@@ -1245,33 +1245,64 @@
 nal 
-Object lock = new Object(
+CountDownLatch allTasksAreCounted = new CountDownLatch(1
 );%0A 
@@ -1376,88 +1376,467 @@
     
-/*%0A         * Implements a runnable that notifies a latch after locking 'lock'.%0A
+Meter created = registry.meter(%22factory.created%22);%0A        Meter terminated = registry.meter(%22factory.terminated%22);%0A%0A        assertThat(created.getCount()).isEqualTo(0);%0A        assertThat(terminated.getCount()).isEqualTo(0);%0A%0A        // generate demand so the executor service creates the threads through our factory.%0A        for (int i = 0; i %3C THREAD_COUNT + 1; i++) %7B%0A            executor.submit(() -%3E %7B%0A                allTasksAreCreated.countDown();%0A%0A  
     
@@ -1840,17 +1840,23 @@
         
-*
+     //
  This as
@@ -1887,57 +1887,21 @@
 ave 
-to enter the critical block before the%0A         *
+wait wail the
  tes
@@ -1933,20 +1933,16 @@
 %0A       
-  *%0A
         
@@ -1942,17 +1942,18 @@
         
-*
+//
  We have
@@ -2027,17 +2027,25 @@
         
-*
+       //
  before 
@@ -2087,242 +2087,61 @@
     
-*/%0A
        
- Runnable fastOne = new Runnable() %7B%0A            @Override%0A            public void run() %7B%0A                synchronized (lock) %7B%0A                    latch.countDown();%0A%0A                    try %7B%0A                        lock.
+try %7B%0A                    allTasksAreCounted.a
 wait
@@ -2160,20 +2160,16 @@
         
- 
-   
 %7D catch 
@@ -2191,28 +2191,24 @@
 eption e) %7B%0A
-    
             
@@ -2270,469 +2270,75 @@
     
-%7D%0A                %7D%0A            %7D%0A        %7D;%0A%0A        Meter created = registry.meter(%22factory.created%22);%0A        Meter terminated = registry.meter(%22factory.terminated%22);%0A%0A        assertThat(created.getCount()).isEqualTo(0);%0A        assertThat(terminated.getCount()).isEqualTo(0);%0A%0A        // generate demand so the executor service creates the threads through our factory.%0A        for (int i = 0; i %3C THREAD_COUNT + 1; i++) %7B%0A            executor.submit(fastOne
+Thread.currentThread().interrupt();%0A                %7D%0A            %7D
 );%0A 
@@ -2359,13 +2359,26 @@
     
-latch
+allTasksAreCreated
 .awa
@@ -2402,25 +2402,24 @@
 CONDS);%0A
-%0A
         
 synchron
@@ -2414,105 +2414,39 @@
     
-synchronized (lock) %7B%0A            // wake up all threads.%0A            lock.notifyAll();%0A        %7D
+allTasksAreCounted.countDown();
 %0A%0A  

Simplify InstrumentedThreadFactoryTest