package org.jboss.cache.lock;

import static org.testng.AssertJUnit.assertTrue;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.jboss.cache.Fqn;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

@Test(groups = { "functional" })
public class StripedLockTest
{
   private StripedLock stripedLock;

   @BeforeMethod(alwaysRun = true)
   public void setUp()
   {
      stripedLock = new StripedLock();
   }

   public void testHashingDistribution()
   {
      // ensure even bucket distribution of lock stripes
      List<Fqn> fqns = createRandomFqns(1000);

      Map<ReentrantReadWriteLock, Integer> distribution = new HashMap<ReentrantReadWriteLock, Integer>();

      for (Fqn f : fqns)
      {
         ReentrantReadWriteLock lock = stripedLock.getLock(f);
         if (distribution.containsKey(lock))
         {
            int count = distribution.get(lock) + 1;
            distribution.put(lock, count);
         }
         else
         {
            distribution.put(lock, 1);
         }
      }

      System.out.println(distribution);

      // cannot be larger than the number of locks
      System.out.println("dist size: " + distribution.size());
      System.out.println("num shared locks: " + stripedLock.sharedLocks.length);
      assertTrue(distribution.size() <= stripedLock.sharedLocks.length);
      // assume at least a 2/3rd spread
      assertTrue(distribution.size() * 1.5 >= stripedLock.sharedLocks.length);
   }

   private List<Fqn> createRandomFqns(int number)
   {
      List<Fqn> f = new ArrayList<Fqn>(number);
      Random r = new Random();

      while (f.size() < number)
      {
         Fqn fqn = Fqn.fromString("/" + ((char) (65 + r.nextInt(26))) + "/" + ((char) (65 + r.nextInt(26))) + "/" + ((char) (65 + r.nextInt(26))));
         f.add(fqn);
      }

      System.out.println("Fqns: " + f);

      return f;
   }
}
