Skip to content

Instantly share code, notes, and snippets.

@RaffaeleSgarro
Created November 15, 2022 22:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RaffaeleSgarro/8ea06553e9a2cc4291ca731e1ee373ca to your computer and use it in GitHub Desktop.
Save RaffaeleSgarro/8ea06553e9a2cc4291ca731e1ee373ca to your computer and use it in GitHub Desktop.
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import static org.junit.jupiter.api.Assertions.*;
public class TestCombinationLock {
private ExecutorService threadPool;
@BeforeEach
public void setUp() {
threadPool = Executors.newCachedThreadPool(
new ThreadFactoryBuilder()
.setNameFormat("test-thread-%d")
.setDaemon(true)
.build());
}
@AfterEach
public void tearDown() {
threadPool.shutdown();
}
@Test
public void acquiringLockMustReturnANonNullCombination() throws InterruptedException {
CombinationLock lock = new CombinationLock();
Combination combination = lock.acquire();
assertNotNull(combination);
}
@Test
public void releasingLockWithInvalidCombinationMustThrowException() throws InterruptedException {
CombinationLock lock = new CombinationLock();
Combination combination = lock.acquire();
assertThrows(InvalidLockCombinationException.class, () -> lock.release(Combination.generate()));
}
@Test
public void theRightCombinationMustUnlockTheLock() throws InterruptedException {
CombinationLock lock = new CombinationLock();
Combination combination = lock.acquire();
lock.release(combination);
assertFalse(lock.isLocked());
}
@Test
public void doubleReleaseMustThrowException() throws InterruptedException {
CombinationLock lock = new CombinationLock();
Combination combination = lock.acquire();
lock.release(combination);
assertThrows(DoubleReleaseException.class, () -> lock.release(combination));
}
@Test
public void concurrentThreadsCompetingForTheLockWillWaitForeverAndEachOneWillGetItEventually() {
CombinationLock lock = new CombinationLock();
List<Future<?>> futures = new ArrayList<>();
for (int i = 0; i < 100; i++) {
futures.add(threadPool.submit(() -> {
Combination combination = lock.acquire();
Thread.sleep(1);
lock.release(combination);
return "Consumer terminated";
}));
}
for (Future<?> f : futures) {
try {
f.get();
} catch (InterruptedException e) {
fail("Test interrupted by user");
} catch (ExecutionException e) {
throw new RuntimeException("Unexpected exception in dummy consumer", e.getCause());
}
}
}
@Test
public void concurrentThreadsCompetingForTheLockWillWaitInALoopAndEachOneWillGetItEventually() {
CombinationLock lock = new CombinationLock();
List<Future<?>> futures = new ArrayList<>();
for (int i = 0; i < 100; i++) {
futures.add(threadPool.submit(() -> {
Combination combination;
while ((combination = lock.tryAcquire(1, TimeUnit.SECONDS)) == null) {Thread.sleep(10);}
Thread.sleep(1);
lock.release(combination);
return "Consumer terminated";
}));
}
for (Future<?> f : futures) {
try {
f.get();
} catch (InterruptedException e) {
fail("Test interrupted by user");
} catch (ExecutionException e) {
throw new RuntimeException("Unexpected exception in dummy consumer", e.getCause());
}
}
}
@Test
public void brokenConsumersDoNotBreakTheLockBecauseEachGetsAnExceptionWhenAttemptingAnIllegalAction() {
CombinationLock lock = new CombinationLock();
List<Future<?>> futures = new ArrayList<>();
for (int i = 0; i < 100; i++) {
futures.add(threadPool.submit(() -> {
Combination combination = lock.acquire();
Thread.sleep(1);
lock.release(combination);
Thread.sleep(10);
lock.release(combination);
return "This is a broken consumer and this line should never be reached";
}));
}
for (Future<?> f : futures) {
try {
f.get();
} catch (InterruptedException e) {
fail("Test interrupted by user");
} catch (ExecutionException e) {
if (!(e.getCause() instanceof DoubleReleaseException) && !(e.getCause() instanceof InvalidLockCombinationException)) {
throw new RuntimeException("Unexpected exception in dummy consumer", e.getCause());
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment