package org.threadly.concurrent;
import static org.junit.Assert.*;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.SynchronousQueue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.threadly.concurrent.BlockingQueueConsumer.ConsumerAcceptor;
import org.threadly.test.concurrent.TestCondition;
import org.threadly.util.ExceptionUtils;
import org.threadly.util.TestExceptionHandler;
@SuppressWarnings("javadoc")
public class BlockingQueueConsumerTest {
private SynchronousQueue<Object> queue;
private TestAcceptor acceptor;
private BlockingQueueConsumer<Object> queueConsumer;
@Before
public void setup() {
queue = new SynchronousQueue<>();
acceptor = new TestAcceptor();
queueConsumer = new BlockingQueueConsumer<>(new ConfigurableThreadFactory(), queue, acceptor);
}
@After
public void cleanup() {
queueConsumer.stopIfRunning();
queue = null;
acceptor = null;
queueConsumer = null;
}
@SuppressWarnings("unused")
@Test
public void constructorFail() {
try {
new BlockingQueueConsumer<>(null, new SynchronousQueue<>(), new TestAcceptor());
fail("Exception should have been thrown");
} catch (IllegalArgumentException e) {
// expected
}
try {
new BlockingQueueConsumer<>(new ConfigurableThreadFactory(), new SynchronousQueue<>(), null);
fail("Exception should have been thrown");
} catch (IllegalArgumentException e) {
// expected
}
try {
new BlockingQueueConsumer<>(new ConfigurableThreadFactory(), null, new TestAcceptor());
fail("Exception should have been thrown");
} catch (IllegalArgumentException e) {
// expected
}
}
@Test
public void doubleStartTest() {
// start queue
queueConsumer.startIfNotStarted();
assertTrue(queueConsumer.isRunning());
// attempt to start again
queueConsumer.startIfNotStarted();
// should still be running without exception
assertTrue(queueConsumer.isRunning());
}
@Test (expected = IllegalThreadStateException.class)
public void startFail() {
StartingThreadFactory threadFactory = new StartingThreadFactory();
try {
queueConsumer = new BlockingQueueConsumer<>(threadFactory, queue, acceptor);
queueConsumer.start();
} finally {
threadFactory.killThreads();
}
}
@Test
public void doubleStopTest() {
queueConsumer.start();
assertTrue(queueConsumer.isRunning());
queueConsumer.stopIfRunning();
assertFalse(queueConsumer.isRunning());
queueConsumer.stopIfRunning();
assertFalse(queueConsumer.isRunning());
}
@Test
public void consumeTest() throws InterruptedException {
assertFalse(queueConsumer.isRunning());
// start queue
queueConsumer.start();
assertTrue(queueConsumer.isRunning());
Object item = new Object();
queue.put(item);
acceptor.blockTillTrue(); // will throw exception if never got item
assertTrue(acceptor.acceptedItems.get(0) == item);
}
@Test
public void consumeExceptionTest() throws InterruptedException {
final TestExceptionHandler teh = new TestExceptionHandler();
ExceptionUtils.setInheritableExceptionHandler(teh);
final Exception e = new Exception();
BlockingQueueConsumer<Object> queueConsumer = new BlockingQueueConsumer<>(new ConfigurableThreadFactory(),
queue, new ConsumerAcceptor<Object>() {
@Override
public void acceptConsumedItem(Object item) throws Exception {
throw e;
}
});
try {
queueConsumer.start();
Object item = new Object();
queue.put(item);
// will throw exception if test fails
new TestCondition(() -> teh.getLastThrowable() == e).blockTillTrue();
// verify thread did not die
assertTrue(queueConsumer.runningThread.isAlive());
} finally {
queueConsumer.stop();
}
}
private static class TestAcceptor extends TestCondition implements ConsumerAcceptor<Object> {
private final List<Object> acceptedItems = new LinkedList<>();
@Override
public void acceptConsumedItem(Object item) {
synchronized (this) {
acceptedItems.add(item);
}
}
@Override
public boolean get() {
synchronized (this) {
return ! acceptedItems.isEmpty();
}
}
}
}