package org.icij.extract.queue;
import org.icij.kaxxa.concurrent.BooleanSealableLatch;
import org.icij.kaxxa.concurrent.SealableLatch;
import org.icij.extract.document.Document;
import org.icij.extract.document.DocumentFactory;
import org.icij.extract.document.PathIdentifier;
import org.icij.extract.queue.ArrayDocumentQueue;
import org.icij.extract.queue.DocumentQueue;
import org.icij.extract.queue.DocumentQueueDrainer;
import org.icij.time.HumanDuration;
import org.junit.*;
import java.nio.file.Paths;
import java.util.*;
import java.util.function.Consumer;
public class DocumentQueueDrainerTest {
private final DocumentFactory factory = new DocumentFactory().withIdentifier(new PathIdentifier());
private static class MockConsumer implements Consumer<Document> {
private final Deque<Document> accepted = new ArrayDeque<>();
@Override
public void accept(final Document document) {
accepted.add(document);
}
Deque<Document> getAccepted() {
return accepted;
}
}
private DocumentQueue createQueue() {
final DocumentQueue queue = new ArrayDocumentQueue(26);
for (char a = 'a'; a <= 'z'; a++) {
queue.add(factory.create(Paths.get(Character.toString(a))));
}
return queue;
}
@Test
public void testDefaultPollTimeoutIs0() {
final DocumentQueue queue = createQueue();
final Consumer<Document> consumer = new MockConsumer();
final DocumentQueueDrainer drainer = new DocumentQueueDrainer(queue, consumer);
Assert.assertEquals(0, drainer.getPollTimeout().getSeconds());
}
@Test
public void testDrainsQueue() throws Throwable {
final DocumentQueue queue = createQueue();
final MockConsumer consumer = new MockConsumer();
final DocumentQueueDrainer drainer = new DocumentQueueDrainer(queue, consumer);
final long drained = drainer.drain().get();
final Queue<Document> accepted = consumer.getAccepted();
Assert.assertEquals(26, drained);
Assert.assertEquals(26, accepted.size());
Assert.assertEquals(0, queue.size());
for (char a = 'a'; a <= 'z'; a++) {
Assert.assertEquals(Character.toString(a), accepted.poll().toString());
}
}
@Test
public void testDrainsQueueUntilPoison() throws Throwable {
final DocumentQueue queue = createQueue();
final MockConsumer consumer = new MockConsumer();
final DocumentQueueDrainer drainer = new DocumentQueueDrainer(queue, consumer);
final Document poison = factory.create(Paths.get("c"));
final long drained = drainer.drain(poison).get();
final Queue<Document> accepted = consumer.getAccepted();
Assert.assertEquals(2, drained);
Assert.assertEquals(2, accepted.size());
Assert.assertEquals("a", accepted.poll().toString());
Assert.assertEquals("b", accepted.poll().toString());
Assert.assertEquals(23, queue.size());
}
@Test
public void testClearPollTimeout() throws Throwable {
final DocumentQueue queue = createQueue();
final MockConsumer consumer = new MockConsumer();
final DocumentQueueDrainer drainer = new DocumentQueueDrainer(queue, consumer);
final Document poison = factory.create(Paths.get("1"));
drainer.clearPollTimeout();
Assert.assertNull(drainer.getPollTimeout());
// The drainer should wait indefinitely if the timeout is null.
new Timer().schedule(new TimerTask() {
@Override
public void run() {
queue.add(factory.create(Paths.get("0")));
queue.add(poison);
}
}, 1000);
final long drained = drainer.drain(poison).get();
final Deque<Document> accepted = consumer.getAccepted();
Assert.assertEquals(27, drained);
Assert.assertEquals(27, accepted.size());
Assert.assertEquals(0, queue.size());
Assert.assertEquals("0", accepted.getLast().toString());
}
@Test
public void testSetPollTimeout() throws Throwable {
final DocumentQueue queue = createQueue();
final MockConsumer consumer = new MockConsumer();
final DocumentQueueDrainer drainer = new DocumentQueueDrainer(queue, consumer);
drainer.setPollTimeout(HumanDuration.parse("2s"));
Assert.assertEquals(2, drainer.getPollTimeout().getSeconds());
// The drainer should wait up to 2s.
new Timer().schedule(new TimerTask() {
@Override
public void run() {
queue.add(factory.create(Paths.get("0")));
}
}, 1000);
final long drained = drainer.drain().get();
final Deque<Document> accepted = consumer.getAccepted();
Assert.assertEquals(27, drained);
Assert.assertEquals(27, accepted.size());
Assert.assertEquals(0, queue.size());
Assert.assertEquals("0", accepted.getLast().toString());
}
@Test
public void testSetLatch() throws Throwable {
final DocumentQueue queue = createQueue();
final MockConsumer consumer = new MockConsumer();
final DocumentQueueDrainer drainer = new DocumentQueueDrainer(queue, consumer);
final SealableLatch latch = new BooleanSealableLatch();
drainer.setLatch(latch);
Assert.assertEquals(latch, drainer.getLatch());
// The drainer should wait for a signal.
new Timer().schedule(new TimerTask() {
@Override
public void run() {
try {
queue.put(factory.create(Paths.get("0")));
latch.signal();
queue.put(factory.create(Paths.get("1")));
Thread.sleep(1000);
} catch (InterruptedException e) {
Assert.fail(e.getMessage());
}
latch.seal();
latch.signal();
}
}, 1000);
final long drained = drainer.drain().get();
final Deque<Document> accepted = consumer.getAccepted();
Assert.assertEquals(28, drained);
Assert.assertEquals(28, accepted.size());
Assert.assertEquals(0, queue.size());
Assert.assertEquals("1", accepted.pollLast().toString());
Assert.assertEquals("0", accepted.pollLast().toString());
drainer.clearLatch();
Assert.assertNull(drainer.getLatch());
}
}