common-utils/src/test/java/org/warp/commonutils/locks/TransitLockTest.java

157 lines
4.0 KiB
Java

package org.warp.commonutils.locks;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class TransitLockTest {
@Test
public void testTransit() {
var lock = new TransitLock();
lock.transit();
lock.transit();
lock.transit();
}
@Test
public void testPartialTransit() {
var lock = new TransitLock();
lock.startTransit();
lock.endTransit();
lock.startTransit();
lock.endTransit();
lock.startTransit();
lock.endTransit();
}
@Test
public void testAllowTransit() {
var lock = new TransitLock();
lock.disallowTransit();
lock.allowTransit();
lock.transit();
}
@Test
public void testMultiAllowTransit() {
Assertions.assertThrows(IllegalStateException.class, () -> {
var lock = new TransitLock();
lock.allowTransit();
lock.allowTransit();
lock.allowTransit();
lock.transit();
});
}
@Test
public void testMultiDisallowAllowTransit() {
var lock = new TransitLock();
lock.disallowTransit();
lock.allowTransit();
lock.disallowTransit();
lock.disallowTransit();
lock.allowTransit();
lock.allowTransit();
lock.transit();
}
@Test
public void testDisallowTransit() {
var lock = new TransitLock();
lock.disallowTransit();
for (int i = 0; i < 10; i++) {
Assertions.assertThrows(TimeoutException.class, () -> {
CompletableFuture.runAsync(lock::transit).get(5, TimeUnit.MILLISECONDS);
}, "Disallowed transit didn't block a transit");
}
}
@Test
public void testMultiDisallowTransit() {
var lock = new TransitLock();
lock.disallowTransit();
lock.disallowTransit();
lock.disallowTransit();
for (int i = 0; i < 10; i++) {
Assertions.assertThrows(TimeoutException.class, () -> {
CompletableFuture.runAsync(lock::transit).get(5, TimeUnit.MILLISECONDS);
}, "Disallowed transit didn't block a transit");
}
}
@Test
public void testDeadlocks() {
var lock = new TransitLock();
AtomicInteger alreadyRunningTransits = new AtomicInteger();
var pool = Executors.newFixedThreadPool(500);
AtomicReference<Exception> failure = new AtomicReference<>();
for (int i = 0; i < 100; i++) {
int iF = i;
pool.submit(() -> {
try {
for (int j = 0; j < 500; j++) {
if (iF % 2 == 0) {
lock.startTransit();
alreadyRunningTransits.getAndIncrement();
alreadyRunningTransits.decrementAndGet();
lock.endTransit();
} else {
lock.disallowTransit();
Assertions.assertEquals(0, alreadyRunningTransits.get());
lock.allowTransit();
}
}
} catch (Exception e) {
e.printStackTrace();
failure.set(e);
}
});
}
pool.shutdown();
if (failure.get() != null) throw new AssertionError(failure.get());
Assertions.assertDoesNotThrow(() -> pool.awaitTermination(10, TimeUnit.SECONDS));
Assertions.assertTrue(pool.isTerminated());
}
@Test
public void testParallelTransit() {
var lock = new TransitLock();
AtomicInteger alreadyRunningTransits = new AtomicInteger();
var pool = Executors.newFixedThreadPool(100);
AtomicReference<AssertionError> failure = new AtomicReference<>();
for (int i = 0; i < 100; i++) {
pool.submit(() -> {
try {
lock.startTransit();
alreadyRunningTransits.getAndIncrement();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
Assertions.fail(e);
}
alreadyRunningTransits.decrementAndGet();
lock.endTransit();
} catch (AssertionError e) {
e.printStackTrace();
failure.set(e);
}
});
}
lock.disallowTransit();
Assertions.assertEquals(0, alreadyRunningTransits.get());
lock.allowTransit();
pool.shutdown();
if (failure.get() != null) throw failure.get();
Assertions.assertDoesNotThrow(() -> pool.awaitTermination(10, TimeUnit.SECONDS));
Assertions.assertTrue(pool.isTerminated());
}
}