package org.threadly.concurrent;
import static org.junit.Assert.*;
import static org.threadly.TestConstants.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.TimeoutException;
import org.junit.BeforeClass;
import org.junit.Test;
import org.threadly.ThreadlyTestUtil;
import org.threadly.concurrent.future.ListenableFuture;
import org.threadly.test.concurrent.AsyncVerifier;
import org.threadly.test.concurrent.TestRunnable;
import org.threadly.util.SuppressedStackRuntimeException;
@SuppressWarnings("javadoc")
public abstract class SubmitterExecutorInterfaceTest {
protected abstract SubmitterExecutorFactory getSubmitterExecutorFactory();
@BeforeClass
public static void setupClass() {
ThreadlyTestUtil.setIgnoreExceptionHandler();
}
protected List<TestRunnable> executeTestRunnables(Executor executor, int runnableSleepTime) {
List<TestRunnable> runnables = new ArrayList<>(TEST_QTY);
for (int i = 0; i < TEST_QTY; i++) {
TestRunnable tr = new TestRunnable(runnableSleepTime);
executor.execute(tr);
runnables.add(tr);
}
return runnables;
}
@Test
public void executeTest() {
SubmitterExecutorFactory factory = getSubmitterExecutorFactory();
try {
SubmitterExecutor executor = factory.makeSubmitterExecutor(2, false);
List<TestRunnable> runnables = executeTestRunnables(executor, 0);
// verify execution
Iterator<TestRunnable> it = runnables.iterator();
while (it.hasNext()) {
TestRunnable tr = it.next();
tr.blockTillFinished();
assertEquals(1, tr.getRunCount());
}
// run one more time now that all workers are already running
it = runnables.iterator();
while (it.hasNext()) {
executor.execute(it.next());
}
// verify second execution
it = runnables.iterator();
while (it.hasNext()) {
TestRunnable tr = it.next();
tr.blockTillFinished(1000, 2);
assertEquals(2, tr.getRunCount());
}
} finally {
factory.shutdown();
}
}
@Test
public void executeInOrderTest() throws InterruptedException, TimeoutException {
SubmitterExecutorFactory factory = getSubmitterExecutorFactory();
try {
SubmitterExecutor executor = factory.makeSubmitterExecutor(1, false);
final AsyncVerifier av = new AsyncVerifier();
TestRunnable lastRun = null;
int testQty = 0;
while (testQty < TEST_QTY) {
testQty++;
final TestRunnable fLastRun = lastRun;
lastRun = new TestRunnable() {
@Override
public void handleRunStart() {
if (fLastRun != null) {
av.assertTrue(fLastRun.ranOnce());
}
av.signalComplete();
}
};
executor.execute(lastRun);
}
av.waitForTest(10 * 1000, testQty);
} finally {
factory.shutdown();
}
}
@Test
public void executeWithFailureRunnableTest() {
SubmitterExecutorFactory factory = getSubmitterExecutorFactory();
try {
SubmitterExecutor executor = factory.makeSubmitterExecutor(TEST_QTY, false);
List<TestRunnable> runnables = new ArrayList<>(TEST_QTY);
for (int i = 0; i < TEST_QTY; i++) {
if (i % 2 == 0) {
// add a failure runnable
executor.execute(new TestRuntimeFailureRunnable());
}
TestRunnable tr = new TestRunnable();
executor.execute(tr);
runnables.add(tr);
}
// verify execution
Iterator<TestRunnable> it = runnables.iterator();
while (it.hasNext()) {
TestRunnable tr = it.next();
tr.blockTillFinished();
assertEquals(1, tr.getRunCount());
}
} finally {
factory.shutdown();
}
}
@Test (expected = IllegalArgumentException.class)
public void executeFail() {
SubmitterExecutorFactory factory = getSubmitterExecutorFactory();
try {
SubmitterExecutor executor = factory.makeSubmitterExecutor(1, false);
executor.execute(null);
fail("Execption should have thrown");
} finally {
factory.shutdown();
}
}
@Test
public void submitRunnableTest() throws InterruptedException, ExecutionException {
SubmitterExecutorFactory factory = getSubmitterExecutorFactory();
try {
SubmitterExecutor executor = factory.makeSubmitterExecutor(TEST_QTY, false);
List<TestRunnable> runnables = new ArrayList<>(TEST_QTY);
List<Future<?>> futures = new ArrayList<>(TEST_QTY);
for (int i = 0; i < TEST_QTY; i++) {
TestRunnable tr = new TestRunnable();
Future<?> future = executor.submit(tr);
assertNotNull(future);
runnables.add(tr);
futures.add(future);
}
// verify execution
Iterator<TestRunnable> it = runnables.iterator();
while (it.hasNext()) {
TestRunnable tr = it.next();
tr.blockTillFinished();
assertEquals(1, tr.getRunCount());
}
// run one more time now that all workers are already running
it = runnables.iterator();
while (it.hasNext()) {
executor.submit(it.next());
}
// verify second execution
it = runnables.iterator();
while (it.hasNext()) {
TestRunnable tr = it.next();
tr.blockTillFinished(1000, 2);
assertEquals(2, tr.getRunCount());
}
Iterator<Future<?>> futureIt = futures.iterator();
while (futureIt.hasNext()) {
Future<?> future = futureIt.next();
assertTrue(future.isDone());
assertNull(future.get());
}
} finally {
factory.shutdown();
}
}
@Test
public void submitRunnableExceptionTest() throws InterruptedException {
SubmitterExecutorFactory factory = getSubmitterExecutorFactory();
try {
SubmitterExecutor executor = factory.makeSubmitterExecutor(TEST_QTY, false);
RuntimeException failure = new SuppressedStackRuntimeException();
TestRuntimeFailureRunnable tr = new TestRuntimeFailureRunnable(failure);
ListenableFuture<?> future = executor.submit(tr);
// no exception should propagate
try {
future.get();
fail("Exception should have thrown");
} catch (ExecutionException e) {
assertTrue(e.getCause() == failure);
}
} finally {
factory.shutdown();
}
}
@Test
public void submitRunnableWithResultTest() throws InterruptedException, ExecutionException {
SubmitterExecutorFactory factory = getSubmitterExecutorFactory();
try {
SubmitterExecutor executor = factory.makeSubmitterExecutor(TEST_QTY, false);
List<TestRunnable> runnables = new ArrayList<>(TEST_QTY);
List<Future<TestRunnable>> futures = new ArrayList<>(TEST_QTY);
for (int i = 0; i < TEST_QTY; i++) {
TestRunnable tr = new TestRunnable();
Future<TestRunnable> future = executor.submit(tr, tr);
assertNotNull(future);
runnables.add(tr);
futures.add(future);
}
// verify execution
Iterator<TestRunnable> it = runnables.iterator();
while (it.hasNext()) {
TestRunnable tr = it.next();
tr.blockTillFinished();
assertEquals(1, tr.getRunCount());
}
// run one more time now that all workers are already running
it = runnables.iterator();
while (it.hasNext()) {
executor.submit(it.next());
}
// verify second execution
it = runnables.iterator();
while (it.hasNext()) {
TestRunnable tr = it.next();
tr.blockTillFinished(1000, 2);
assertEquals(2, tr.getRunCount());
}
it = runnables.iterator();
Iterator<Future<TestRunnable>> futureIt = futures.iterator();
while (futureIt.hasNext()) {
Future<?> future = futureIt.next();
assertTrue(future.isDone());
assertTrue(future.get() == it.next());
}
} finally {
factory.shutdown();
}
}
@Test
public void submitRunnableWithResultExceptionTest() throws InterruptedException {
SubmitterExecutorFactory factory = getSubmitterExecutorFactory();
try {
SubmitterExecutor executor = factory.makeSubmitterExecutor(TEST_QTY, false);
RuntimeException failure = new SuppressedStackRuntimeException();
TestRuntimeFailureRunnable tr = new TestRuntimeFailureRunnable(failure);
ListenableFuture<?> future = executor.submit(tr, new Object());
// no exception should propagate
tr.blockTillFinished();
assertTrue(future.isDone());
try {
future.get();
fail("Exception should have thrown");
} catch (ExecutionException e) {
assertTrue(e.getCause() == failure);
}
} finally {
factory.shutdown();
}
}
@Test
public void submitCallableTest() throws InterruptedException, ExecutionException {
SubmitterExecutorFactory factory = getSubmitterExecutorFactory();
try {
SubmitterExecutor executor = factory.makeSubmitterExecutor(TEST_QTY, false);
List<TestCallable> callables = new ArrayList<>(TEST_QTY);
List<Future<Object>> futures = new ArrayList<>(TEST_QTY);
for (int i = 0; i < TEST_QTY; i++) {
TestCallable tc = new TestCallable();
Future<Object> future = executor.submit(tc);
assertNotNull(future);
callables.add(tc);
futures.add(future);
}
// verify execution
Iterator<TestCallable> it = callables.iterator();
while (it.hasNext()) {
TestCallable tc = it.next();
tc.blockTillTrue();
assertTrue(tc.isDone());
}
it = callables.iterator();
Iterator<Future<Object>> futureIt = futures.iterator();
while (futureIt.hasNext()) {
Future<Object> future = futureIt.next();
TestCallable tc = it.next();
assertTrue(tc.getReturnedResult() == future.get());
assertTrue(future.isDone());
}
} finally {
factory.shutdown();
}
}
@Test
public void submitCallableExceptionTest() throws InterruptedException {
SubmitterExecutorFactory factory = getSubmitterExecutorFactory();
try {
SubmitterExecutor executor = factory.makeSubmitterExecutor(TEST_QTY, false);
final RuntimeException failure = new SuppressedStackRuntimeException();
ListenableFuture<?> future = executor.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
throw failure;
}
});
// no exception should propagate
try {
future.get();
fail("Exception should have thrown");
} catch (ExecutionException e) {
assertTrue(e.getCause() == failure);
}
} finally {
factory.shutdown();
}
}
@Test (expected = IllegalArgumentException.class)
public void submitRunnableFail() {
SubmitterExecutorFactory factory = getSubmitterExecutorFactory();
try {
SubmitterExecutor executor = factory.makeSubmitterExecutor(1, false);
executor.submit((Runnable)null);
fail("Execption should have thrown");
} finally {
factory.shutdown();
}
}
@Test (expected = IllegalArgumentException.class)
public void submitRunnableWithResultFail() {
SubmitterExecutorFactory factory = getSubmitterExecutorFactory();
try {
SubmitterExecutor executor = factory.makeSubmitterExecutor(1, false);
executor.submit((Runnable)null, new Object());
fail("Execption should have thrown");
} finally {
factory.shutdown();
}
}
@Test (expected = IllegalArgumentException.class)
public void submitCallableFail() {
SubmitterExecutorFactory factory = getSubmitterExecutorFactory();
try {
SubmitterExecutor executor = factory.makeSubmitterExecutor(1, false);
executor.submit((Callable<Void>)null);
fail("Execption should have thrown");
} finally {
factory.shutdown();
}
}
public interface SubmitterExecutorFactory {
public SubmitterExecutor makeSubmitterExecutor(int poolSize, boolean prestartIfAvailable);
public void shutdown();
}
}