package ro.isdc.wro.util.concurrent; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.UUID; import java.util.concurrent.Callable; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ro.isdc.wro.WroRuntimeException; import ro.isdc.wro.config.Context; import ro.isdc.wro.util.StopWatch; /** * @author Alex Objelean */ public class TestTaskExecutor { private static final Logger LOG = LoggerFactory.getLogger(TestTaskExecutor.class); private TaskExecutor<Void> victim; @BeforeClass public static void onBeforeClass() { assertEquals(0, Context.countActive()); } @AfterClass public static void onAfterClass() { assertEquals(0, Context.countActive()); } @Before public void setUp() { victim = new TaskExecutor<Void>() { @Override protected void onException(final Exception e) throws Exception { throw e; } }; } @Test(expected = NullPointerException.class) public void cannotSubmitNullCallables() throws Exception { final Collection<Callable<Void>> callables = null; victim.submit(callables); } private Callable<Void> createSlowCallable(final long millis) { return new Callable<Void>() { public Void call() throws Exception { Thread.sleep(millis); return null; } }; } @Test public void shouldHaveMinimalOverheadWhenRunningASingleTask() throws Exception { final Collection<Callable<Void>> callables = new ArrayList<Callable<Void>>(); final long delay = 100; callables.add(createSlowCallable(delay)); final long start = System.currentTimeMillis(); victim.submit(callables); final long end = System.currentTimeMillis(); final long delta = end - start; LOG.debug("Execution took: {}", delta); // number of milliseconds added by overhead final long overhead = 40; assertTrue(delta < delay + overhead); } @Test public void shouldBeFasterWhenRunningMultipleSlowTasks() throws Exception { final Collection<Callable<Void>> callables = new ArrayList<Callable<Void>>(); final long delay = 100; final int times = 10; for (int i = 0; i < times; i++) { callables.add(createSlowCallable(delay)); } final long start = System.currentTimeMillis(); victim.submit(callables); final long end = System.currentTimeMillis(); final long delta = end - start; LOG.debug("Execution took: {}", delta); assertTrue(delta < delay * times); } @Test(expected = WroRuntimeException.class) public void shouldPropagateOriginalException() throws Exception { final List<Callable<Void>> tasks = new ArrayList<Callable<Void>>(); // add at least two, otherwise no concurrency is applied. tasks.add(createFailingCallable()); tasks.add(createFailingCallable()); victim.submit(tasks); } private Callable<Void> createFailingCallable() { return new Callable<Void>() { public Void call() throws Exception { throw new WroRuntimeException("BOOM!"); } }; } private static void printDate() { LOG.debug(new SimpleDateFormat("HH:ss:S").format(new Date())); } public static void main(final String[] args) throws Exception { final TaskExecutor<String> executor = new TaskExecutor<String>() { @Override protected void onResultAvailable(final String result) throws Exception { LOG.debug("<<< [OK] result available: " + result); LOG.debug("\n===============\n"); printDate(); } }; final List<Callable<String>> tasks = new ArrayList<Callable<String>>(); tasks.add(new Callable<String>() { public String call() throws Exception { printDate(); final String result = Thread.currentThread().getName(); LOG.debug("\n>>> [START]===============\nThread " + result + " started..."); final double sleep = 1000; LOG.debug("Sleeping for: " + sleep); Thread.sleep((long) sleep); return result; } }); final int index = 100; for (int i = 0; i < index; i++) { tasks.add(new Callable<String>() { public String call() throws Exception { printDate(); final String result = Thread.currentThread().getName() + ":" + UUID.randomUUID().toString(); LOG.debug("\n[START]===============\nThread " + result + " started..."); final double sleep = Math.random() * 300; LOG.debug("Sleeping for: " + sleep); Thread.sleep((long) sleep); return result; } }); } final StopWatch watch = new StopWatch(); watch.start("submit tasks"); executor.submit(tasks); watch.stop(); LOG.debug(watch.prettyPrint()); } }