Adding concurrent locks/reclaiming locks tests

Change-Id: Ia5b22d1f4fb83c85a2e14de144afcd1f983a7b4e
diff --git a/src/test/java/com/googlesource/gerrit/plugins/spannerrefdb/LockTest.java b/src/test/java/com/googlesource/gerrit/plugins/spannerrefdb/LockTest.java
index cd5d65c..4f03f64 100644
--- a/src/test/java/com/googlesource/gerrit/plugins/spannerrefdb/LockTest.java
+++ b/src/test/java/com/googlesource/gerrit/plugins/spannerrefdb/LockTest.java
@@ -26,6 +26,10 @@
 import com.google.gerrit.entities.Project;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -117,6 +121,44 @@
         .contains(String.format("Unable to lock ref %s on project %s", REF_NAME, PROJECT_NAME_KEY));
   }
 
+  @Test
+  public void concurrentLocks_OnlyOneSuccess() throws Exception {
+    CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
+    ExecutorService pool = Executors.newFixedThreadPool(2);
+
+    for (int i = 0; i < 10; i++) {
+      Future<Boolean> f1 = pool.submit(() -> tryLockAwaitBarrier(cyclicBarrier));
+      Future<Boolean> f2 = pool.submit(() -> tryLockAwaitBarrier(cyclicBarrier));
+      assertThat(f1.get()).isNotEqualTo(f2.get());
+    }
+    pool.shutdown();
+  }
+
+  @Test
+  public void concurrentReclamations_OnlyOneSuccess() throws Exception {
+    Timestamp staleTimestamp = Timestamp.ofTimeSecondsAndNanos(0, 0);
+    CyclicBarrier cyclicBarrier = new CyclicBarrier(2);
+    ExecutorService pool = Executors.newFixedThreadPool(2);
+
+    for (int i = 0; i < 10; i++) {
+      insertLockRow(staleTimestamp);
+      Future<Boolean> f1 = pool.submit(() -> tryLockAwaitBarrier(cyclicBarrier));
+      Future<Boolean> f2 = pool.submit(() -> tryLockAwaitBarrier(cyclicBarrier));
+      assertThat(f1.get()).isNotEqualTo(f2.get());
+    }
+    pool.shutdown();
+  }
+
+  private boolean tryLockAwaitBarrier(CyclicBarrier barrier) throws Exception {
+    try (AutoCloseable refLock = refDb.lockRef(PROJECT_NAME_KEY, REF_NAME)) {
+      barrier.await();
+      return true;
+    } catch (GlobalRefDbLockException e) {
+      barrier.await();
+    }
+    return false;
+  }
+
   private Timestamp getLockTimestamp(Project.NameKey project, String refName, String column) {
     Struct row = getLockRow(project, refName);
     return row != null ? row.getTimestamp(column) : null;