package org.infinispan.lock;

import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;

import java.util.concurrent.Callable;

import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.test.MultipleCacheManagersTest;
import org.infinispan.test.TestingUtil;
import org.infinispan.transaction.LockingMode;
import org.testng.annotations.Test;

/**
 * Test for stale remote locks on invalidation mode caches with pessimistic transactions.
 * See https://issues.jboss.org/browse/ISPN-2549.
 *
 * @author anistor@redhat.com
 * @since 5.3
 */
@Test(groups = "functional", testName = "lock.InvalidationModePessimisticLockReleaseTest")
public class InvalidationModePessimisticLockReleaseTest extends MultipleCacheManagersTest {

   @Override
   protected void createCacheManagers() throws Throwable {
      ConfigurationBuilder builder = getDefaultClusteredCacheConfig(CacheMode.INVALIDATION_SYNC, true, true);
      builder.transaction().useSynchronization(false)
            .lockingMode(LockingMode.PESSIMISTIC)
            .locking().lockAcquisitionTimeout(TestingUtil.shortTimeoutMillis())
            .useLockStriping(false);

      createCluster(builder, 2);
      waitForClusterToForm();
   }

   public void testStaleRemoteLocks() throws Exception {
      // put two keys on node 1
      TestingUtil.withTx(tm(1), new Callable<Object>() {
         @Override
         public Object call() throws Exception {
            cache(1).put(1, "val_1");
            cache(1).put(2, "val_2");
            return null;
         }
      });

      assertValue(1, 1, "val_1");
      assertValue(1, 2, "val_2");

      // assert that no locks remain on node 1
      assertFalse(checkLocked(1, 1));
      assertFalse(checkLocked(1, 2));

      // assert that no locks remain on node 0 (need to wait a bit for tx completion notifications to be processed async)
      eventually(new Condition() {
         @Override
         public boolean isSatisfied() throws Exception {
            return !checkLocked(0, 1) && !checkLocked(0, 2);
         }
      });

      // try to modify a key. this should not fail due to residual locks.
      cache(0).put(1, "new_val_1");

      // assert expected values on each node
      assertValue(0, 1, "new_val_1");
      assertValue(1, 1, null);
      assertValue(1, 2, "val_2");
   }

   private void assertValue(int nodeIndex, Object key, Object expectedValue) {
      assertEquals(expectedValue, cache(nodeIndex).get(key));
   }
}
