content: 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());
    }
  }
  