package org.limewire.concurrent;


import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import junit.framework.Test;

import org.limewire.util.BaseTestCase;


public class ThreadExecutorTest extends BaseTestCase {

    public ThreadExecutorTest(String name) {
        super(name);
    }

    public static Test suite() {
        return buildTestSuite(ThreadExecutorTest.class);
    }
    
    public void testThreadStarts() throws Exception {
        Runner r1 = new Runner();
        ThreadExecutor.startThread(r1, "Name");
        assertTrue(r1.getRanLatch().await(100, TimeUnit.MILLISECONDS));
        assertEquals("Name", r1.getName());
    }
    
    public void testReuseThreads() throws Exception {
        Runner r1 = new Runner();
        ThreadExecutor.startThread(r1, "Name1");
        assertTrue(r1.getRanLatch().await(100, TimeUnit.MILLISECONDS));
        assertEquals("Name1", r1.getName());
        Thread.sleep(100); // let the thread rename happen.
        assertEquals("IdleThread", r1.getThread().getName());
        
        Runner r2 = new Runner();
        ThreadExecutor.startThread(r2, "Name2");
        assertTrue(r2.getRanLatch().await(100, TimeUnit.MILLISECONDS));
        assertSame(r1.getThread(), r2.getThread());
        assertEquals("Name2", r2.getName());
    }
    
    public void testThreadLingers() throws Exception {
        Runner r1 = new Runner();
        ThreadExecutor.startThread(r1, "Name");
        assertTrue(r1.getRanLatch().await(100, TimeUnit.MILLISECONDS));
        Thread thread = r1.getThread();
        Thread.sleep(100); // let the thread rename happen.
        assertEquals("IdleThread", r1.getThread().getName());
        assertTrue(thread.isAlive());
        Thread.sleep(3500);
        assertTrue(thread.isAlive());
        Thread.sleep(2000);
        assertFalse(thread.isAlive());
        assertEquals("IdleThread", r1.getThread().getName());
    }
    
    public void testManyThreadsAtOnce() throws Exception {
        CountDownLatch runLatch = new CountDownLatch(1);
        Runner r1 = new Runner(runLatch);
        Runner r2 = new Runner(runLatch);
        ThreadExecutor.startThread(r1, "Name1");
        ThreadExecutor.startThread(r2, "Name2");
        assertTrue(r1.getStartedLatch().await(100, TimeUnit.MILLISECONDS));
        assertTrue(r2.getStartedLatch().await(100, TimeUnit.MILLISECONDS));
        assertFalse(r1.getRanLatch().await(100, TimeUnit.MILLISECONDS));
        assertFalse(r2.getRanLatch().await(100, TimeUnit.MILLISECONDS));
        assertNotSame(r1.getThread(), r2.getThread());
        assertEquals("Name1", r1.getName());        
        assertEquals("Name2", r2.getName());
        runLatch.countDown();
        assertTrue(r1.getRanLatch().await(100, TimeUnit.MILLISECONDS));
        assertTrue(r2.getRanLatch().await(100, TimeUnit.MILLISECONDS));
    }
    
    public void testDaemonThread() throws Exception {
        Runner r1 = new Runner();
        ThreadExecutor.startThread(r1, "Name");
        assertTrue(r1.getRanLatch().await(100, TimeUnit.MILLISECONDS));
        assertTrue(r1.getThread().isDaemon());
    }    
    
    private static class Runner implements Runnable {
        private volatile Thread thread;
        private volatile String name;
        private final CountDownLatch startedLatch = new CountDownLatch(1);
        private final CountDownLatch ranLatch = new CountDownLatch(1);
        private final CountDownLatch runLatch;
    
        public Runner() {
            runLatch = null;
        }
        
        public Runner(CountDownLatch runLatch) {
            this.runLatch = runLatch;
        }
        
        public void run() {
            thread = Thread.currentThread();
            name = thread.getName();
            startedLatch.countDown();
            if(runLatch != null) {
                try {   
                    if(!runLatch.await(10, TimeUnit.SECONDS))
                        fail("never got notified!");
                } catch(InterruptedException ie) {}
            }
            ranLatch.countDown();
        }
        
        public CountDownLatch getRanLatch() {
            return ranLatch;
        }

        public CountDownLatch getStartedLatch() {
            return startedLatch;
        }

        public Thread getThread() {
            return thread;
        }
        
        public String getName() {
            return name;
        }
    }    
}
