package org.jboss.cache;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.jboss.cache.CacheException;
import org.jboss.cache.Fqn;
import org.jboss.cache.TreeCache;
import org.jboss.cache.lock.IsolationLevel;
import org.jboss.cache.lock.TimeoutException;
import org.jboss.cache.transaction.DummyTransactionManager;

import javax.transaction.NotSupportedException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import java.util.HashMap;

/**
 * Simple functional tests for TreeCache
 * @author Bela Ban
 * @version $Id: TreeCacheFunctionalTest.java,v 1.1 2005/07/08 05:58:07 msurtani Exp $
 */
public class TreeCacheFunctionalTest extends TestCase {
   TreeCache cache=null;
   Transaction tx=null;
   final Fqn FQN=Fqn.fromString("/myNode");
   final String KEY="key";
   final String VALUE="value";
   Exception ex;


   protected void setUp() throws Exception {
      super.setUp();
      cache=new TreeCache();
      cache.setCacheMode(TreeCache.LOCAL);
      cache.setTransactionManagerLookupClass("org.jboss.cache.DummyTransactionManagerLookup");
      cache.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
      cache.createService();
      cache.startService();
      ex=null;
   }

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


   public void testPut() throws CacheException {
      cache.put("/a/b/c", "age", new Integer(38));
      assertEquals(cache.get("/a/b/c", "age"), new Integer(38));
      assertNotNull(cache.get("/a/b/c"));
      assertEquals(0, cache.getNumberOfLocksHeld());
      assertEquals(0, cache.getLockTable().size());
   }



   public void testPutNullKey() throws CacheException {
      Object key=null;
      cache.put("/a/b/c", key, "val");
      System.out.println("value of /a/b/c " + cache.print("/a/b/c"));
   }

   public void testPutNullValue() throws CacheException {
      Object val=null;
      cache.put("/a/b/c", "key", val);
      System.out.println("value of /a/b/c " + cache.print("/a/b/c"));
   }

   public void testPutNullKeyAndValues() throws CacheException {
      Object key=null, val=null;
      cache.put("/a/b/c", key, val);
      System.out.println("value of /a/b/c " + cache.print("/a/b/c"));
   }

   public void testPutMapsWithNullValues() throws CacheException {
      HashMap map=new HashMap();
      map.put("key", null);
      map.put(null, "val");
      map.put("a", "b");
      map.put(null, null);
      cache.put("/a/b/c", map);
      System.out.println("value of /a/b/c " + cache.print("/a/b/c"));
   }

   public void testPutKeys() throws CacheException {
      cache.put("/a/b/c", "age", new Integer(38));
      cache.put("/a/b/c", "name", "Bela");
      assertEquals(cache.get("/a/b/c", "age"), new Integer(38));
      assertNotNull(cache.get("/a/b/c"));
      assertEquals(cache.getKeys("/a/b/c").size(), 2);
      assertEquals(cache.exists("/a/b/c"), true);
      assertEquals(0, cache.getNumberOfLocksHeld());
      assertEquals(0, cache.getLockTable().size());
   }

   public void testRemove() throws CacheException {
      cache.put("/a/b/c", null);
      cache.put("/a/b/c/1", null);
      cache.put("/a/b/c/2", null);
      cache.put("/a/b/c/3", null);
      cache.put("/a/b/c/3/a/b/c", null);

      cache.remove("/a/b/c");
      assertEquals(0, cache.getNumberOfLocksHeld());
      assertEquals(0, cache.getLockTable().size());
   }


   public void testFailFastPut() throws Exception {
      cache.put(FQN, KEY, VALUE);
      cache.putFailFast(Fqn.fromString("/a/b/c"), KEY, VALUE, 0);
      cache.putFailFast(Fqn.fromString("/a/b/c"), KEY, VALUE, 100);
      cache.putFailFast(Fqn.fromString("/1/2/3"), KEY, VALUE, 100);
      cache.putFailFast(Fqn.fromString("/1/2/3"), KEY, VALUE, 0);
      System.out.println("cache: " + cache.printLockInfo());
   }


   public void testFailFastWith2Transactions() throws Exception, NotSupportedException {
      tx=startTransaction();
      cache.put("/a/b/c", KEY, VALUE);

      class Loser extends Thread {
         public void run() {
            Transaction trans=null;
            try {
               trans=startTransaction();
               cache.putFailFast(Fqn.fromString("/a/b/c"), KEY, VALUE, 500);
               fail("this should fail as /a/b/c is held by another thread");
            }
            catch(TimeoutException timeoutEx) {
               System.out.println("got TimeoutException (as expected)");
            }
            catch(Exception e) {
               ex=e;
            }
         }
      }

      System.out.println("locks before loser: " + cache.printLockInfo());
      Loser loser=new Loser();
      loser.start();
      loser.join();
      tx.commit();
   }


   Transaction startTransaction() throws SystemException, NotSupportedException {
      DummyTransactionManager mgr=DummyTransactionManager.getInstance();
      mgr.begin();
      Transaction tmptx=mgr.getTransaction();
      return tmptx;
   }



   public static Test suite() {
      return new TestSuite(TreeCacheFunctionalTest.class);
   }

   //public static void main(String[] args) {
     // junit.textui.TestRunner.run(suite());
   //}

}
