/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.hazelcast.concurrent.lock; import com.hazelcast.internal.serialization.impl.HeapData; import com.hazelcast.nio.serialization.Data; import com.hazelcast.spi.DistributedObjectNamespace; import com.hazelcast.spi.ObjectNamespace; import com.hazelcast.test.HazelcastParallelClassRunner; import com.hazelcast.test.HazelcastTestSupport; import com.hazelcast.test.annotation.ParallelTest; import com.hazelcast.test.annotation.QuickTest; import com.hazelcast.util.scheduler.EntryTaskScheduler; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import java.util.Collection; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @RunWith(HazelcastParallelClassRunner.class) @Category({QuickTest.class, ParallelTest.class}) public class LockStoreImplTest extends HazelcastTestSupport { private static final ObjectNamespace OBJECT_NAME_SPACE = new DistributedObjectNamespace("service", "object"); private static final int BACKUP_COUNT = 0; private static final int ASYNC_BACKUP_COUNT = 0; private LockService mockLockServiceImpl; private EntryTaskScheduler<Data, Integer> mockScheduler; private LockStoreImpl lockStore; private Data key = new HeapData(); private String callerId = "called"; private long threadId = 1; private long referenceId = 1; private long leaseTime = Long.MAX_VALUE; @Before public void setUp() { mockLockServiceImpl = mock(LockService.class); when(mockLockServiceImpl.getMaxLeaseTimeInMillis()).thenReturn(Long.MAX_VALUE); mockScheduler = mock(EntryTaskScheduler.class); lockStore = new LockStoreImpl(mockLockServiceImpl, OBJECT_NAME_SPACE, mockScheduler, BACKUP_COUNT, ASYNC_BACKUP_COUNT); } @Test public void testLock_whenUnlocked_thenReturnTrue() { boolean isLocked = lockAndIncreaseReferenceId(); assertTrue(isLocked); } @Test(expected = IllegalArgumentException.class) public void testLock_whenMaximumLeaseTimeExceeded_thenThrowException() { when(mockLockServiceImpl.getMaxLeaseTimeInMillis()).thenReturn(1L); lockAndIncreaseReferenceId(); } @Test public void testLock_whenLockedBySameThread_thenReturnTrue() { lockAndIncreaseReferenceId(); boolean isLocked = lockAndIncreaseReferenceId(); assertTrue(isLocked); } @Test public void testLock_whenLockedByDifferentThread_thenReturnFalse() { lockAndIncreaseReferenceId(); threadId++; boolean isLocked = lockAndIncreaseReferenceId(); assertFalse(isLocked); } @Test public void testGetRemainingLeaseTime_whenLockDoesNotExist_thenReturnNegativeOne() { long remainingLeaseTime = lockStore.getRemainingLeaseTime(key); assertEquals(-1, remainingLeaseTime); } @Test public void testGetRemainingLeaseTime_whenUnlocked_thenReturnNegativeOne() { lockAndIncreaseReferenceId(); unlockAndIncreaseReferenceId(); long remainingLeaseTime = lockStore.getRemainingLeaseTime(key); assertEquals(-1, remainingLeaseTime); } @Test public void testGetRemainingLeaseTime_whenLocked_thenReturnLeaseTime() { leaseTime = Long.MAX_VALUE / 2; lockAndIncreaseReferenceId(); long remainingLeaseTime = lockStore.getRemainingLeaseTime(key); assertThat(remainingLeaseTime, lessThanOrEqualTo(leaseTime)); assertThat(remainingLeaseTime, greaterThan(0L)); } @Test public void testGetVersion_whenLockDoesNotExist_thenReturnNegativeOne() { int version = lockStore.getVersion(key); assertEquals(-1, version); } @Test public void testGetVersion_whenUnlocked_thenReturnNegativeOne() { lockAndIncreaseReferenceId(); unlockAndIncreaseReferenceId(); int version = lockStore.getVersion(key); assertEquals(-1, version); } @Test public void testGetVersion_whenLockedOnce_thenReturnPositiveOne() { lockAndIncreaseReferenceId(); int version = lockStore.getVersion(key); assertEquals(1, version); } @Test public void testGetVersion_whenLockedTwice_thenReturnPositiveTwo() { lockAndIncreaseReferenceId(); lockAndIncreaseReferenceId(); int version = lockStore.getVersion(key); assertEquals(2, version); } @Test public void testIsLockedBy_whenLockDoesNotExist_thenReturnFalse() { boolean lockedBy = lockStore.isLockedBy(key, callerId, threadId); assertFalse(lockedBy); } @Test public void testIsLockedBy_whenLockedBySameCallerAndSameThread_thenReturnTrue() { lockAndIncreaseReferenceId(); boolean lockedBy = lockStore.isLockedBy(key, callerId, threadId); assertTrue(lockedBy); } @Test public void testIsLockedBy_whenLockedBySameCallerAndDifferentThread_thenReturnFalse() { lockAndIncreaseReferenceId(); long differentThreadId = threadId + 1; boolean lockedBy = lockStore.isLockedBy(key, callerId, differentThreadId); assertFalse(lockedBy); } @Test public void testIsLockedBy_whenLockedByDifferentCallerAndSameThread_thenReturnFalse() { lockAndIncreaseReferenceId(); String differentCaller = callerId + "different"; boolean lockedBy = lockStore.isLockedBy(key, differentCaller, threadId); assertFalse(lockedBy); } @Test public void testIsLockedBy_whenLockedByDifferentCallerAndDifferentThread_thenReturnFalse() { lockAndIncreaseReferenceId(); long differentThreadId = threadId + 1; String differentCaller = callerId + "different"; boolean lockedBy = lockStore.isLockedBy(key, differentCaller, differentThreadId); assertFalse(lockedBy); } @Test public void testIsLocked_whenLockDoesNotExist_thenReturnFalse() { boolean locked = lockStore.isLocked(key); assertFalse(locked); } @Test public void testIsLocked_whenLockedAndUnlocked_thenReturnFalse() { lockAndIncreaseReferenceId(); unlockAndIncreaseReferenceId(); boolean locked = lockStore.isLocked(key); assertFalse(locked); } @Test public void testIsLocked_whenLocked_thenReturnTrue() { lockAndIncreaseReferenceId(); boolean locked = lockStore.isLocked(key); assertTrue(locked); } @Test public void testCanAcquireLock_whenLockDoesNotExist_thenReturnTrue() { boolean canAcquire = lockStore.canAcquireLock(key, callerId, threadId); assertTrue(canAcquire); } @Test public void testCanAcquireLock_whenLockedBySameThreadAndSameCaller_thenReturnTrue() { lockAndIncreaseReferenceId(); boolean canAcquire = lockStore.canAcquireLock(key, callerId, threadId); assertTrue(canAcquire); } @Test public void testCanAquireLock_whenLockedBySameThreadAndDifferentCaller_thenReturnFalse() { lockAndIncreaseReferenceId(); String differentCaller = callerId + "different"; boolean canAcquire = lockStore.canAcquireLock(key, differentCaller, threadId); assertFalse(canAcquire); } @Test public void testCanAcquireLock_whenLockedByDifferentThreadAndSameCaller_thenReturnFalse() { lockAndIncreaseReferenceId(); long differentThreadId = threadId + 1; boolean canAcquire = lockStore.canAcquireLock(key, callerId, differentThreadId); assertFalse(canAcquire); } @Test public void testCanAcquireLock_whenLockedByDifferentThreadAndDifferentCaller_thenReturnFalse() { lockAndIncreaseReferenceId(); long differentThreadId = threadId + 1; String differentCaller = callerId + "different"; boolean canAcquire = lockStore.canAcquireLock(key, differentCaller, differentThreadId); assertFalse(canAcquire); } @Test public void testCanAcquireLock_whenLockedAndUnlocked_thenReturnTrue() { lockAndIncreaseReferenceId(); unlockAndIncreaseReferenceId(); long differentThreadId = threadId + 1; String differentCaller = callerId + "different"; boolean canAcquire = lockStore.canAcquireLock(key, differentCaller, differentThreadId); assertTrue(canAcquire); } @Test public void testForceUnlock_whenLockDoesNotExists_thenReturnFalse() { boolean unlocked = lockStore.forceUnlock(key); assertFalse(unlocked); } @Test public void testForceUnlock_whenLocked_thenReturnTrue() { lockAndIncreaseReferenceId(); boolean unlocked = lockStore.forceUnlock(key); assertTrue(unlocked); } @Test(expected = UnsupportedOperationException.class) public void testGetLocks_returnUnmodifiableCollection() { Collection<LockResource> locks = lockStore.getLocks(); locks.clear(); } @Test public void testGetLocks_whenNoLockExist_thenReturnEmptyCollection() { Collection<LockResource> locks = lockStore.getLocks(); assertThat(locks, is(empty())); } @Test public void testGetLocks_whenLocked_thenReturnCollectionWithSingleItem() { lockAndIncreaseReferenceId(); Collection<LockResource> locks = lockStore.getLocks(); assertThat(locks, hasSize(1)); } @Test public void testGetLockedEntryCount() { lock(); assertEquals(1, lockStore.getLockedEntryCount()); } @Test public void testGetLockCount_whenLockDoesNotExist_thenReturnZero() { int lockCount = lockStore.getLockCount(key); assertThat(lockCount, is(0)); } @Test public void testGetLockCount_whenLockedOnce_thenReturnOne() { lockAndIncreaseReferenceId(); int lockCount = lockStore.getLockCount(key); assertThat(lockCount, is(1)); } @Test public void testGetLockCount_whenLockedTwice_thenReturnTwo() { lockAndIncreaseReferenceId(); lockAndIncreaseReferenceId(); int lockCount = lockStore.getLockCount(key); assertThat(lockCount, is(2)); } @Test public void testGetLockCount_whenLockedTwiceWithTheSameReferenceId_thenReturnOne() { lock(); lockAndIncreaseReferenceId(); int lockCount = lockStore.getLockCount(key); assertThat(lockCount, is(1)); } @Test public void testUnlock_whenLockDoesNotExist_thenReturnFalse() { boolean unlocked = unlockAndIncreaseReferenceId(); assertFalse(unlocked); } @Test public void testUnlock_whenLockedBySameCallerAndSameThreadId_thenReturnTrue() { lockAndIncreaseReferenceId(); boolean unlocked = unlockAndIncreaseReferenceId(); assertTrue(unlocked); } @Test public void testUnlock_whenLockedByDifferentCallerAndSameThreadId_thenReturnFalse() { lockAndIncreaseReferenceId(); callerId += "different"; boolean unlocked = unlockAndIncreaseReferenceId(); assertFalse(unlocked); } @Test public void testIsLocked_whenTxnLockedAndUnlockedWithSameReferenceId_thenReturnFalse() { //see https://github.com/hazelcast/hazelcast/issues/5923 for details txnLock(); unlock(); boolean locked = lockStore.isLocked(key); assertFalse(locked); } @Test public void testUnlock_whenLockedBySameCallerAndDifferentThreadId_thenReturnFalse() { lockAndIncreaseReferenceId(); threadId++; boolean unlocked = unlockAndIncreaseReferenceId(); assertFalse(unlocked); } @Test public void testUnlock_whenLockedByDifferentCallerAndDifferentThreadId_thenReturnFalse() { lockAndIncreaseReferenceId(); threadId++; callerId += "different"; boolean unlocked = unlockAndIncreaseReferenceId(); assertFalse(unlocked); } @Test public void testUnlock_whenLockedTwiceWithSameReferenceIdAndUnlockedOnce_thenReturnTrue() { lock(); lockAndIncreaseReferenceId(); boolean unlocked = unlockAndIncreaseReferenceId(); assertTrue(unlocked); } @Test public void testTxnLock_whenLockDoesNotExist_thenResultTrue() { boolean locked = txnLockAndIncreaseReferenceId(); assertTrue(locked); } @Test public void testTxnLock_whenLockedByDifferentCallerAndSameThreadId_thenReturnFalse() { txnLockAndIncreaseReferenceId(); callerId += "different"; boolean locked = txnLockAndIncreaseReferenceId(); assertFalse(locked); } @Test public void testTxnLock_whenLockedBySameCallerAndDifferentThreadId_thenReturnFalse() { txnLockAndIncreaseReferenceId(); threadId++; boolean locked = txnLockAndIncreaseReferenceId(); assertFalse(locked); } @Test public void testIsBlockReads_whenLockDoesNotExist_thenReturnFalse() { boolean locked = lockStore.shouldBlockReads(key); assertFalse(locked); } @Test public void testIsIsBlockReads_whenNonTxnLocked_thenReturnFalse() { lock(); boolean locked = lockStore.shouldBlockReads(key); assertFalse(locked); } @Test public void testIsBlockReads_whenTxnLocked_thenReturnTrue() { txnLock(); boolean locked = lockStore.shouldBlockReads(key); assertTrue(locked); } @Test public void testIsBlockReads_whenTxnLockedAndUnlocked_thenReturnFalse() { txnLockAndIncreaseReferenceId(); unlock(); boolean locked = lockStore.shouldBlockReads(key); assertFalse(locked); } @Test public void testIsBlockReads_whenTxnLockedAndAttemptedToLockFromAnotherThread_thenReturnTrue() { txnLockAndIncreaseReferenceId(); threadId++; lockAndIncreaseReferenceId(); boolean locked = lockStore.shouldBlockReads(key); assertTrue(locked); } private boolean lock() { return lockStore.lock(key, callerId, threadId, referenceId, leaseTime); } private boolean txnLock() { return lockStore.txnLock(key, callerId, threadId, referenceId, leaseTime, true); } private boolean unlock() { return lockStore.unlock(key, callerId, threadId, referenceId); } private boolean lockAndIncreaseReferenceId() { boolean isLocked = lock(); referenceId++; return isLocked; } private boolean txnLockAndIncreaseReferenceId() { boolean isLocked = txnLock(); referenceId++; return isLocked; } private boolean unlockAndIncreaseReferenceId() { boolean isUnlocked = unlock(); referenceId++; return isUnlocked; } }