package org.jboss.cache.eviction;

import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.textui.TestRunner;
import org.jboss.cache.CacheException;
import org.jboss.cache.DummyTransactionManagerLookup;
import org.jboss.cache.Fqn;
import org.jboss.cache.PropertyConfigurator;
import org.jboss.cache.TreeCache;
import org.jboss.cache.interceptors.EvictionInterceptor;
import org.jboss.cache.misc.TestingUtil;

import javax.transaction.TransactionManager;
import java.util.List;
import java.util.Iterator;

/**
 * Tests the eviction and the possible lack of locking nodes.
 * The configuration is with an aggressive eviction policy, 100 objects 2 seconds interval.
 * <p/>
 * It is possible that the number needs to be changed a little, depending on the machine speed.
 *
 * @author fhenning
 */
public class OptimisticEvictionTest extends TestCase
{

    //Maximum number of runs 2^20
    private static final int NUMBER_OF_RUNS = (1 << 20);
    //Initial number of nodes
    private static final int NUMBER_NODES = 256;

    private Fqn region = Fqn.fromString("testingRegion");
    private TransactionManager txManager;
    private TreeCache cache;

    protected void setUp() throws Exception
    {
        super.setUp();

        txManager = new DummyTransactionManagerLookup().getTransactionManager();
        cache = new TreeCache();
        PropertyConfigurator config = new PropertyConfigurator();
        config.configure(this.cache, "META-INF/optimistic-eviction.xml");
        cache.startService();
    }

    protected void tearDown() throws Exception
    {
        if (cache != null)
        {
            cache.stopService();
            cache = null;
        }
        super.tearDown();
    }

    public void testEvictionError() throws CacheException
    {
        //Initialize the cache via a map
        for (int i = 0; i < NUMBER_NODES; i++)
        {
            cache.put(new Fqn(region, new Integer(i)), new Integer(i), new Integer(i));
        }

        for (int i = 0; i < NUMBER_OF_RUNS; i++)
        {
            try
            {
                txManager.begin();
                cache.get(region, new Integer(i % NUMBER_NODES));
                txManager.commit();
            }
            catch (Exception e)
            {
                fail("Exception caught after run: " + i + ". Cause was " + e.getCause());
            }
        }
    }

    public void testEvictionOccurence() throws Exception
    {
        cache.put("/timeBased/test", "key", "value");
        assertTrue(cache.exists("/timeBased/test"));

        // wait for it to be evicted.
        TestingUtil.sleepThread(3000);
        assertTrue(!cache.exists("/timeBased/test"));
    }

    public void testMultEvictionOccurences() throws Exception
    {
        for (int i=0; i<50; i++)
        {
            cache.put("/testingRegion/test/" + i, "key", "value");
        }

        // allow some time for the eviction thread to finish it's work
        TestingUtil.sleepThread(1000);

        assertEquals(12, cache.getNumberOfNodes()); // root + base + num nodes in region
    }

    public void testInterceptorChain() throws Exception
    {
        List interceptors = cache.getInterceptors();
        System.out.println(interceptors);
        Iterator i = interceptors.iterator();
        boolean found = false;

        while (i.hasNext())
        {
            Object o = i.next();
            if (o instanceof EvictionInterceptor)
            {
                found = true;
            }
        }

        assertTrue("Eviction interceptor should be in interceptor chain.", found);
    }
    
    public void testCompleteRemoval() throws Exception
    {
       String rootStr = "/timeBased/";
       
       // Add a parent, then a child. LRU will evict the parent, 
       // then the child, leaving behind an empty parent
       Fqn parent = Fqn.fromString(rootStr + "parent");
       cache.put(parent, "key", "value");
       cache.put(new Fqn(parent, "child"), "key", "value");
       
       // Give eviction time to run a few times, then confirm parent
       // is completely gone
       TestingUtil.sleepThread(5500);      
       assertFalse("Parent completely removed", cache.exists(parent));            
    }

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

}
