package org.jctools.queues.blocking;
import static org.junit.Assert.*;
import org.jctools.queues.blocking.BlockingQueueFactory;
import org.jctools.queues.spec.ConcurrentQueueSpec;
import org.jctools.queues.spec.Ordering;
import org.jctools.queues.spec.Preference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.BlockingQueue;
@RunWith(Parameterized.class)
public class BlockingQueueTest
{
protected final static int CAPACITY = 32768; // better to have a size power of 2 to test boundaries
private BlockingQueue<Integer> q;
private final ConcurrentQueueSpec spec;
@Parameterized.Parameters
public static Collection queues() {
return Arrays.asList(
test(1, 1, CAPACITY, Ordering.FIFO), test(10, 1, CAPACITY, Ordering.FIFO),
test(1, 10, CAPACITY, Ordering.FIFO), test(10, 10, CAPACITY, Ordering.FIFO));
}
private static Object[] test(int producers, int consumers, int capacity, Ordering ordering) {
return new Object[] { new ConcurrentQueueSpec(producers, consumers, capacity, ordering,
Preference.NONE) };
}
public BlockingQueueTest(ConcurrentQueueSpec spec)
{
this.spec = spec;
}
@Before
public void setUp()
{
q = BlockingQueueFactory.newBlockingQueue(spec);
}
@Test
public void testOffer()
{
for (int i = 0; i < CAPACITY; i++)
{
assertTrue(q.offer(i));
assertEquals(i + 1, q.size());
}
if (spec.isBounded())
{
assertFalse(q.offer(0));
}
assertEquals(CAPACITY, q.size());
assertFalse(q.isEmpty());
}
@Test
public void testPoll()
{
testOffer();
assertEquals(CAPACITY, q.size());
for (int i = 0; i < CAPACITY; i++)
{
assertEquals(q.poll(), new Integer(i));
assertEquals(CAPACITY - (i + 1), q.size());
}
assertNull(q.poll());
assertEquals(q.size(), 0);
assertTrue(q.isEmpty());
}
@Test
public void testEmptyQueue()
{
assertNull(q.poll());
assertEquals(q.size(), 0);
assertTrue(q.isEmpty());
}
@Test
public void testClear()
{
testOffer();
q.clear();
testEmptyQueue();
}
@Test
public void testConcurrentOfferPool() throws Exception
{
new Thread()
{
@Override
public void run()
{
for (int i = 0; i < CAPACITY;)
{
Integer r = q.poll();
if (r != null)
{
assertEquals(r, new Integer(i));
i++;
}
}
}
}.start();
// Wait for the thread to warmup
Thread.sleep(100);
// Try to insert to max
for (int i = 0; i < CAPACITY; i++)
{
assertTrue(q.offer(i));
}
while (!q.isEmpty())
{
Thread.sleep(1);
}
}
@Test
public void testNonBlockingPutTake() throws Exception
{
q.put(42);
Integer i = q.take();
assertNotNull(i);
assertEquals(i, new Integer(42));
}
@Test
public void testBlockingTake() throws Exception
{
q.put(0);
Thread take = new Thread()
{
@Override
public void run()
{
try
{
assertEquals(new Integer(0), q.take());
assertEquals(new Integer(1), q.take());
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
take.start();
// Wait for the thread to read 0
int timeout = 30; // timeout ~3s
while (!q.isEmpty() && timeout > 0)
{
Thread.sleep(100);
timeout--;
}
assertTrue(timeout > 0);
assertTrue(take.isAlive());
q.put(1);
// take thread should be unlocked
take.join(3000);
assertFalse(take.isAlive());
}
@Test
public void testBlockingPut() throws Exception
{
if (!spec.isBounded())
{
// Unbounded queues don't block on put()
return;
}
// Fill the queue
testOffer();
Thread put = new Thread()
{
@Override
public void run()
{
try
{
// this should block
q.put(-1);
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
put.start();
Thread.sleep(100);
assertTrue(put.isAlive());
for (int i = 0; i < CAPACITY; i++)
{
q.take();
}
// put(-1) have unlocked
assertEquals(new Integer(-1), q.take());
// put thread should be unlocked
put.join(3000);
assertFalse(put.isAlive());
}
}