/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.toolkit.modules.concurrency.internal;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import de.rcenvironment.toolkit.modules.concurrency.api.AsyncCallbackExceptionPolicy;
import de.rcenvironment.toolkit.modules.concurrency.api.AsyncOrderedExecutionQueue;
/**
* {@link AsyncOrderedExecutionQueueImpl} unit tests.
*
* @author Robert Mischke
*/
public class AsyncOrderedExecutionQueueImplTest extends AbstractConcurrencyModuleTest {
private static final int MEDIUM_TEST_TIMEOUT = 10000;
private static final int SHORT_TEST_TIMEOUT = 1000;
private final Log log = LogFactory.getLog(getClass());
private AsyncOrderedExecutionQueue queue;
/**
* Common test setup.
*/
@Before
public void setUp() {
getThreadPoolManagement().reset();
queue = getConcurrencyUtilsFactory().createAsyncOrderedExecutionQueue(AsyncCallbackExceptionPolicy.LOG_AND_PROCEED);
}
/**
* Tests canceling a filled queue.
*
* @throws TimeoutException if canceling hangs unexpectedly
*/
@Test
public void testCancel() throws TimeoutException {
final int jobCount = 10;
final int jobDuration = 500;
final AtomicInteger executed = new AtomicInteger();
for (int i = 0; i < jobCount; i++) {
queue.enqueue(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(jobDuration);
executed.incrementAndGet();
} catch (InterruptedException e) {
log.error(e);
}
}
});
}
try {
Thread.sleep(jobDuration / 2);
} catch (InterruptedException e) {
Assert.fail(e.toString());
}
assertEquals(0, executed.get());
queue.cancelAndWaitForLastRunningTask();
assertEquals(1, executed.get());
}
/**
* Tests that the cancel operation does not hang if the queue is already empty.
*
* @throws TimeoutException should not happen, as the test timeout is shorter
*/
@Test(timeout = SHORT_TEST_TIMEOUT)
public void testCancelOnEmptyQueueDoesNotBlock() throws TimeoutException {
queue.cancelAndWaitForLastRunningTask();
}
/**
* Tests canceling a filled queue.
*
* @throws TimeoutException if canceling hangs unexpectedly
*/
@Test
public void testCancelFromInsideTaskExecution() throws TimeoutException {
final int totalJobCount = 10;
final int iterationToCancelIn = 5; // one-based index
final int waitForCompletionTime = 200;
final AtomicInteger executed = new AtomicInteger();
for (int i = 1; i <= totalJobCount; i++) {
final int i2 = i;
queue.enqueue(new Runnable() {
@Override
public void run() {
executed.incrementAndGet();
if (i2 == iterationToCancelIn) {
queue.enqueue(new Runnable() {
@Override
public void run() {
// crude but functional way to trigger test failure
executed.addAndGet(totalJobCount);
}
});
queue.cancelAsync();
queue.enqueue(new Runnable() {
@Override
public void run() {
// crude but functional way to trigger test failure
executed.addAndGet(totalJobCount);
}
});
}
}
});
}
try {
Thread.sleep(waitForCompletionTime);
} catch (InterruptedException e) {
Assert.fail(e.toString());
}
// test expectation: no other task was executed after the one that cancel() was called from
assertEquals(iterationToCancelIn, executed.get());
}
/**
* Verifies that the queue has sufficient mass throughput.
*
* @throws InterruptedException on test interruption
*/
@Test
public void massThroughput() throws InterruptedException {
final int count = 1 * 1000 * 1000;
final CountDownLatch cdl = new CountDownLatch(count);
for (int i = 0; i < count; i++) {
queue.enqueue(new Runnable() {
@Override
public void run() {
cdl.countDown();
}
});
}
assertTrue(cdl.await(MEDIUM_TEST_TIMEOUT, TimeUnit.MILLISECONDS));
}
}