/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.cache.loader;

import junit.textui.TestRunner;
import org.jboss.cache.Fqn;
import org.jboss.cache.TreeCache;
import org.jboss.cache.misc.TestingUtil;

import java.io.File;
import java.util.Random;

/**
 * Tests the cache loader infrastructure
 *
 * @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
 */
public class CacheLoaderPerfTest extends AbstractCacheLoaderTestBase
{
    private TreeCache cache;
    private long totalTime = 0;

    private Random random = new Random();

    private static final int NUM_TIMES_PER_THREAD = 1;
    private static final int NUM_THREADS = 1;
    private static final String threadNamePrefix = "TesterThread-";

    protected void setUp() throws Exception
    {
        if (cache != null) tearDown();
        String tmpLocation = System.getProperty("java.io.tmpdir", ".") + File.separator + "JBossCache-CacheLoaderPerfTest";
        cache = new TreeCache();
        cache.setCacheLoaderConfiguration(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.FileCacheLoader", "location=" + tmpLocation, false, false, false));
        File dir = new File(tmpLocation);
        if (!dir.exists()) dir.mkdirs();
        cache.startService();
    }

    protected void tearDown() throws Exception
    {
        if (cache != null)
        {
            cache.remove("/");
            cache.stopService();
            cache = null;
            totalTime = 0;

            // go a gc() to clear up unused threads from each test
            System.gc();
            TestingUtil.sleepThread(1000);
        }
    }

    public void testPut() throws Exception
    {
        Thread threads[] = new Thread[NUM_THREADS];

        for (int i=0; i<NUM_THREADS; i++)
        {
            threads[i] = new Thread()
            {
              public void run()
              {
                try
                {
                    Fqn base = new Fqn(new Object[]{"Threads", getName()});
                    long start = System.currentTimeMillis();
                    for (int j=0; j<NUM_TIMES_PER_THREAD; j++)
                    {
                        cache.put(base, "key"+j, "value"+j);
                    }
                    long timeTaken = System.currentTimeMillis() - start;
                    totalTime += timeTaken;
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
              }
            };
        }

        // start threads
        startThreads(threads);

        // wait for all the threads to join
        waitForThreads(threads);

        printResults("testPut()");
    }

    public void testRemove() throws Exception
    {
        Thread threads[] = new Thread[NUM_THREADS];

        for (int i = 0; i < NUM_THREADS; i++)
        {
            threads[i] = new Thread()
            {
                public void run()
                {
                    try
                    {
                        Fqn fqn = Fqn.fromString("/Threads/" + getName());
                        long start = System.currentTimeMillis();
                        for (int j = 0; j < NUM_TIMES_PER_THREAD; j++)
                        {
                            cache.remove(fqn, "key" + j);
                            cache.evict(fqn);
                        }
                        long timeTaken = System.currentTimeMillis() - start;
                        totalTime += timeTaken;
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            };
        }

        // add some stuff in the cache.
        populateCache();

        // start threads
        startThreads(threads);

        // wait for all the threads to join
        waitForThreads(threads);

        printResults("testRemove()");
    }

    public void testGet() throws Exception
    {
        Thread threads[] = new Thread[NUM_THREADS];

        for (int i = 0; i < NUM_THREADS; i++)
        {
            threads[i] = new Thread()
            {
                public void run()
                {
                    try
                    {
                        Fqn fqn = Fqn.fromString("/Threads/" + getName());
                        long start = System.currentTimeMillis();
                        for (int j = 0; j < NUM_TIMES_PER_THREAD; j++)
                        {
                            cache.get(fqn, "key"+j);
                            cache.evict(fqn);
                        }
                        long timeTaken = System.currentTimeMillis() - start;
                        totalTime += timeTaken;
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            };
        }

        // add some stuff in the cache.
        populateCache();

        // start threads
        startThreads(threads);

        // wait for all the threads to join
        waitForThreads(threads);

        // printResults
        printResults("testGet()");
    }

    private void populateCache() throws Exception
    {
        // add some stuff in the cache.
        for (int i = 0; i < NUM_THREADS; i++)
        {
            Fqn fqn = Fqn.fromString("/Threads/" + threadNamePrefix + i);
            for (int j = 0; j < NUM_TIMES_PER_THREAD; j++)
            {
                cache.put(fqn, "key" + j, "value");
            }
            cache.evict(fqn);
        }
    }

    private void startThreads(Thread[] threads)
    {
        for (int i = 0; i < NUM_THREADS; i++)
        {
            // add a random delay here.
            randomSleep();
            threads[i].setName(threadNamePrefix + i);
            threads[i].start();
        }
    }

    private void waitForThreads(Thread[] threads)
    {
        for (int i = 0; i < NUM_THREADS; i++)
        {
            try
            {
                threads[i].join();
            }
            catch (InterruptedException e)
            {
            }
        }
    }

    private void randomSleep()
    {
        TestingUtil.sleepThread(random.nextInt(1000));
    }

    private void printResults(String name)
    {
        System.out.println("------------------------------------");
        System.out.println("  (all times in ms)");
        System.out.println();
        System.out.println("  Test name:         " + name);
        System.out.println("  NumThreads:        " + NUM_THREADS);
        System.out.println("  NumLoopsPerThread: " + NUM_TIMES_PER_THREAD);
        System.out.println("  Total time:        " + totalTime);
        System.out.println("  Avg time:          " + (totalTime / (NUM_TIMES_PER_THREAD * NUM_THREADS)));
        System.out.println("  Avg time for 1k:   " + ((1000*totalTime) / (NUM_TIMES_PER_THREAD * NUM_THREADS)));
        System.out.println("------------------------------------");
    }

    public static void main(String[] args)
    {
        TestRunner.run(CacheLoaderPerfTest.class);
    }
}
