/******************************************************************************* * Copyright (c) 2013 Bruno Medeiros and other Contributors. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package melnorme.utilbox.concurrency; import static melnorme.utilbox.core.Assert.AssertNamespace.assertFail; import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue; import java.io.IOException; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import org.junit.Test; import melnorme.utilbox.ownership.IDisposable; import melnorme.utilbox.tests.CommonTestExt; public class ExecutorTaskAgent_Test extends CommonTestExt { @Test public void testBasic() throws Exception { runMultipleTimes(10, 100, () -> testBasic$()); } public void testBasic$() throws Exception { try(Tests_ExecutorTaskAgent agent = new Tests_ExecutorTaskAgent("testShutdownNow")) { LatchRunnable firstTask = new LatchRunnable(true); LatchRunnable secondTask = new LatchRunnable(true); agent.submit(firstTask); assertTrue(agent.getSubmittedTaskCount() == 1); Future<?> secondTaskFuture = agent.submit(secondTask); assertTrue(agent.getSubmittedTaskCount() == 2); firstTask.awaitTaskEntry(); assertTrue(secondTaskFuture.isCancelled() == false); List<Runnable> cancelledTasks = agent.shutdownNowAndCancelAll(); assertTrue(cancelledTasks.size() == 1); assertTrue(secondTaskFuture.isCancelled() == true); assertTrue(agent.isShutdown()); Thread.sleep(1); assertTrue(agent.isTerminating() == true); assertTrue(agent.isTerminated() == false); firstTask.releaseAll(); agent.awaitTermination(); assertTrue(agent.isShutdown()); assertTrue(agent.isTerminating() == false); assertTrue(agent.isTerminated()); testShutdownNow_Interrupt(); } } // test that shutdownNow interrupts current task. public void testShutdownNow_Interrupt() throws InterruptedException { try(Tests_ExecutorTaskAgent agent = new Tests_ExecutorTaskAgent("testShutdownNow_Interrupt")) { LatchRunnable firstTask = new LatchRunnable(false); agent.submit(firstTask); Future<?> future = agent.submit(new LatchRunnable(true)); firstTask.awaitTaskEntry(); assertTrue(future.isDone() == false); List<Runnable> cancelledTasks = agent.shutdownNowAndCancelAll(); assertTrue(cancelledTasks.size() == 1); assertTrue(future.isCancelled()); agent.awaitTermination(); } } public static class Tests_ExecutorTaskAgent extends ExecutorTaskAgent implements IDisposable { protected final LinkedBlockingQueue<Throwable> uncaughtExceptions; public Tests_ExecutorTaskAgent(String name) { this(new LinkedBlockingQueue<>(), name); } protected Tests_ExecutorTaskAgent(LinkedBlockingQueue<Throwable> uncaughtExceptions, String name) { super("TestsExecutor." + name, (throwable) -> { if(throwable != null) { uncaughtExceptions.add(throwable); } }); this.uncaughtExceptions = uncaughtExceptions; } @Override public void dispose() { this.shutdownNowAndCancelAll(); } @Override public void awaitTermination() throws InterruptedException { super.awaitTermination(); } } @Test public void testExceptionHandling() throws Exception { runMultipleTimes(10, 100, () -> testExceptionHandling$()); } public void testExceptionHandling$() throws Exception { try(Tests_ExecutorTaskAgent agent = new Tests_ExecutorTaskAgent("testExceptionHandling")) { Future<?> future; future = agent.submit(new LatchRunnable(false)); future.cancel(true); try { future.get(); assertFail(); } catch (CancellationException ce) { // ok } agent.awaitPendingTasks(); assertTrue(agent.uncaughtExceptions.size() == 0); Runnable npeRunnable = () -> { throw new RuntimeException("npeRunnable"); // a RuntimeException, representing an internal error }; Callable<String> normalTask = () -> { throw new IOException("Some expected exception"); }; checkExceptionHandling(agent.uncaughtExceptions, agent, agent.submit(npeRunnable), RuntimeException.class, false); checkExceptionHandling(agent.uncaughtExceptions, agent, agent.submit(normalTask), IOException.class, true); checkExceptionHandling(agent.uncaughtExceptions, agent, agent.submit(() -> { throw new RuntimeException("---"); }), RuntimeException.class, false); agent.execute(npeRunnable); agent.shutdown(); agent.awaitTermination(); while(true) { if(agent.uncaughtExceptions.size() == 1) { break; } Thread.sleep(20); } } } protected void checkExceptionHandling(final LinkedBlockingQueue<Throwable> unexpectedExceptions, ExecutorTaskAgent agent, Future<?> future, Class<? extends Exception> expectedKlass, boolean isExpected) throws InterruptedException { try { future.get(); } catch (ExecutionException ce) { assertTrue(expectedKlass.isInstance(ce.getCause())); // ok } agent.awaitPendingTasks(); if(expectedKlass == null || isExpected) { assertTrue(unexpectedExceptions.size() == 0); return; } else { assertTrue(unexpectedExceptions.size() == 1); Throwable removed = unexpectedExceptions.remove(); assertTrue(expectedKlass.isInstance(removed)); } } }