Package com.opengamma.engine.exec

Source Code of com.opengamma.engine.exec.PlanExecutorTest

/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.engine.exec;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.Test;
import org.threeten.bp.Instant;

import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.cache.CacheSelectHint;
import com.opengamma.engine.calcnode.CalculationJob;
import com.opengamma.engine.calcnode.CalculationJobItem;
import com.opengamma.engine.calcnode.CalculationJobResult;
import com.opengamma.engine.calcnode.CalculationJobResultItem;
import com.opengamma.engine.calcnode.JobDispatcher;
import com.opengamma.engine.calcnode.JobResultReceiver;
import com.opengamma.engine.exec.DependencyGraphExecutionFuture.Listener;
import com.opengamma.engine.exec.plan.GraphExecutionPlan;
import com.opengamma.engine.exec.plan.PlannedJob;
import com.opengamma.engine.exec.stats.GraphExecutionStatistics;
import com.opengamma.engine.exec.stats.GraphExecutorStatisticsGatherer;
import com.opengamma.engine.exec.stats.TotallingGraphStatisticsGathererProvider;
import com.opengamma.engine.exec.stats.TotallingGraphStatisticsGathererProvider.Statistics;
import com.opengamma.engine.function.EmptyFunctionParameters;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.engine.view.ExecutionLog;
import com.opengamma.engine.view.ExecutionLogMode;
import com.opengamma.engine.view.cycle.SingleComputationCycle;
import com.opengamma.engine.view.impl.ViewProcessContext;
import com.opengamma.id.UniqueId;
import com.opengamma.id.VersionCorrection;
import com.opengamma.util.async.Cancelable;
import com.opengamma.util.test.TestGroup;
import com.opengamma.util.tuple.Pair;

/**
* Tests the {@link PlanExecutor} class.
*/
@Test(groups = TestGroup.UNIT)
public class PlanExecutorTest {

  private static final Logger s_logger = LoggerFactory.getLogger(PlanExecutorTest.class);

  private ViewProcessContext createViewProcessContext(final JobDispatcher jobDispatcher) {
    final ViewProcessContext context = Mockito.mock(ViewProcessContext.class);
    Mockito.when(context.getComputationJobDispatcher()).thenReturn(jobDispatcher);
    Mockito.when(context.getGraphExecutorStatisticsGathererProvider()).thenReturn(new TotallingGraphStatisticsGathererProvider());
    return context;
  }

  private SingleComputationCycle createCycle(final JobDispatcher jobDispatcher) {
    final Instant now = Instant.now();
    final SingleComputationCycle cycle = Mockito.mock(SingleComputationCycle.class);
    Mockito.when(cycle.getUniqueId()).thenReturn(UniqueId.of("Cycle", "Test"));
    Mockito.when(cycle.getValuationTime()).thenReturn(now);
    Mockito.when(cycle.getVersionCorrection()).thenReturn(VersionCorrection.of(now, now));
    final ViewProcessContext context = createViewProcessContext(jobDispatcher);
    Mockito.when(cycle.getViewProcessContext()).thenReturn(context);
    Mockito.when(cycle.getViewProcessId()).thenReturn(UniqueId.of("View", "Test"));
    Mockito.when(cycle.toString()).thenReturn("TEST-CYCLE");
    return cycle;
  }

  private List<CalculationJobItem> createJobItems(final int count) {
    final List<CalculationJobItem> result = new ArrayList<CalculationJobItem>(count);
    for (int i = 0; i < count; i++) {
      result.add(new CalculationJobItem("Func", new EmptyFunctionParameters(), ComputationTargetSpecification.NULL, Collections.<ValueSpecification>emptySet(), Collections
          .<ValueSpecification>emptySet(),
          ExecutionLogMode.INDICATORS));
    }
    return result;
  }

  private List<CalculationJobResultItem> createResultItems(final List<CalculationJobItem> items) {
    final List<CalculationJobResultItem> result = new ArrayList<CalculationJobResultItem>(items.size());
    for (int i = 0; i < items.size(); i++) {
      result.add(new CalculationJobResultItem(Collections.<ValueSpecification>emptySet(), Collections.<ValueSpecification>emptySet(), ExecutionLog.EMPTY));
    }
    return result;
  }

  private CalculationJobResult createJobResult(final CalculationJob job) {
    return new CalculationJobResult(job.getSpecification(), 10L, createResultItems(job.getJobItems()), "Test");
  }

  private GraphExecutionPlan createPlan() {
    final PlannedJob job1 = new PlannedJob(1, createJobItems(1), CacheSelectHint.allShared(), null, null);
    final PlannedJob job2 = new PlannedJob(1, createJobItems(2), CacheSelectHint.allShared(), null, new PlannedJob[] {job1 });
    final PlannedJob job3 = new PlannedJob(0, createJobItems(3), CacheSelectHint.allShared(), new PlannedJob[] {job2 }, null);
    return new GraphExecutionPlan("Default", 0, Arrays.asList(job3), 3, 2d, 10d, 20d);
  }

  private class NormalExecutionJobDispatcher extends JobDispatcher {

    private final Queue<Pair<CalculationJob, JobResultReceiver>> _jobs = new LinkedList<Pair<CalculationJob, JobResultReceiver>>();

    private final Queue<Pair<CalculationJob, CalculationJobResult>> _results = new LinkedList<Pair<CalculationJob, CalculationJobResult>>();

    @Override
    public Cancelable dispatchJob(final CalculationJob job, final JobResultReceiver receiver) {
      s_logger.debug("Dispatching {}", job);
      _jobs.add(Pair.of(job, receiver));
      if (job.getTail() != null) {
        for (CalculationJob tail : job.getTail()) {
          dispatchJob(tail, receiver);
        }
      }
      return Mockito.mock(Cancelable.class);
    }

    protected void notify(final CalculationJob job, final JobResultReceiver receiver) {
      s_logger.debug("Notifying {}", job);
      final CalculationJobResult result = createJobResult(job);
      _results.add(Pair.of(job, result));
      receiver.resultReceived(result);
    }

    public void completeJobs() {
      do {
        Pair<CalculationJob, JobResultReceiver> job = _jobs.poll();
        if (job == null) {
          s_logger.debug("No more jobs");
          return;
        }
        s_logger.debug("Completing {}", job.getFirst());
        notify(job.getFirst(), job.getSecond());
      } while (true);
    }

    public void execute(final PlanExecutor executor) {
      executor.start();
      completeJobs();
    }

    public Pair<CalculationJob, CalculationJobResult> pollResult() {
      return _results.poll();
    }

  }

  public void testStatisticsReporting() {
    final NormalExecutionJobDispatcher dispatcher = new NormalExecutionJobDispatcher();
    final PlanExecutor executor = new PlanExecutor(createCycle(dispatcher), createPlan());
    final GraphExecutorStatisticsGatherer statsGatherer = executor.getCycle().getViewProcessContext().getGraphExecutorStatisticsGathererProvider().getStatisticsGatherer(UniqueId.of("View", "Test"));
    final GraphExecutionStatistics stats = ((Statistics) statsGatherer).getExecutionStatistics().get(0);
    assertEquals(stats.getCalcConfigName(), "Default");
    assertEquals(stats.getProcessedGraphs(), 1L);
    assertEquals(stats.getAverageJobSize(), 2d);
    assertEquals(stats.getAverageJobCycleCost(), 10d);
    assertEquals(stats.getAverageJobDataCost(), 20d);
    dispatcher.execute(executor);
    assertEquals(stats.getExecutedGraphs(), 1L);
    assertEquals(stats.getExecutedNodes(), 6L);
    assertEquals(stats.getExecutionTime(), 30L);
  }

  // Timeout is set just in case "get" blocks rather than returns immediately
  @Test(timeOut = 5000)
  public void testNormalExecution() throws Throwable {
    final NormalExecutionJobDispatcher dispatcher = new NormalExecutionJobDispatcher();
    final PlanExecutor executor = new PlanExecutor(createCycle(dispatcher), createPlan());
    dispatcher.execute(executor);
    final SingleComputationCycle cycle = executor.getCycle();
    Pair<CalculationJob, CalculationJobResult> result = dispatcher.pollResult();
    Mockito.verify(cycle, Mockito.times(1)).jobCompleted(result.getFirst(), result.getSecond());
    result = dispatcher.pollResult();
    Mockito.verify(cycle, Mockito.times(1)).jobCompleted(result.getFirst(), result.getSecond());
    result = dispatcher.pollResult();
    Mockito.verify(cycle, Mockito.times(1)).jobCompleted(result.getFirst(), result.getSecond());
    assertNull(dispatcher.pollResult());
    assertFalse(executor.isCancelled());
    assertTrue(executor.isDone());
    assertEquals(executor.get(1, TimeUnit.SECONDS), "Default");
    assertEquals(executor.get(), "Default");
  }

  public void testDuplicateJobNotifications() {
    final NormalExecutionJobDispatcher dispatcher = new NormalExecutionJobDispatcher() {
      @Override
      public void notify(final CalculationJob job, final JobResultReceiver receiver) {
        super.notify(job, receiver);
        super.notify(job, receiver);
      }
    };
    final PlanExecutor executor = new PlanExecutor(createCycle(dispatcher), createPlan());
    dispatcher.execute(executor);
    final SingleComputationCycle cycle = executor.getCycle();
    Pair<CalculationJob, CalculationJobResult> result = dispatcher.pollResult();
    Mockito.verify(cycle, Mockito.times(1)).jobCompleted(result.getFirst(), result.getSecond());
    assertNotNull(dispatcher.pollResult()); // Second one will be discarded
    result = dispatcher.pollResult();
    Mockito.verify(cycle, Mockito.times(1)).jobCompleted(result.getFirst(), result.getSecond());
    assertNotNull(dispatcher.pollResult()); // Duplicate will be discarded
    result = dispatcher.pollResult();
    Mockito.verify(cycle, Mockito.times(1)).jobCompleted(result.getFirst(), result.getSecond());
    assertNotNull(dispatcher.pollResult()); // Duplicate will be discarded
    assertNull(dispatcher.pollResult());
  }

  @Test(expectedExceptions = IllegalStateException.class)
  public void testDuplicateStart() {
    final PlanExecutor executor = new PlanExecutor(createCycle(new JobDispatcher()), createPlan());
    executor.start();
    executor.start();
  }

  public void testCancelStoppingJobs() {
    final AtomicBoolean canceled = new AtomicBoolean();
    final JobDispatcher dispatcher = new JobDispatcher() {
      @Override
      public Cancelable dispatchJob(final CalculationJob job, final JobResultReceiver receiver) {
        return new Cancelable() {
          @Override
          public boolean cancel(final boolean mayInterruptIfRunning) {
            assertTrue(mayInterruptIfRunning);
            canceled.set(true);
            return true;
          }
        };
      }
    };
    final PlanExecutor executor = new PlanExecutor(createCycle(dispatcher), createPlan());
    executor.start();
    assertFalse(canceled.get());
    assertTrue(executor.cancel(true));
    assertTrue(canceled.get());
    assertTrue(executor.isCancelled());
    assertTrue(executor.isDone());
  }

  public void testCancelNotStoppingJobs() {
    final NormalExecutionJobDispatcher dispatcher = new NormalExecutionJobDispatcher();
    final PlanExecutor executor = new PlanExecutor(createCycle(dispatcher), createPlan());
    executor.start();
    assertTrue(executor.cancel(true));
    dispatcher.completeJobs();
    assertTrue(executor.isCancelled());
    assertTrue(executor.isDone());
    // The first job (and its tail) should have attempted to complete, but the cycle not notified
    final SingleComputationCycle cycle = executor.getCycle();
    Pair<CalculationJob, CalculationJobResult> result = dispatcher.pollResult();
    Mockito.verify(cycle, Mockito.never()).jobCompleted(result.getFirst(), result.getSecond());
    result = dispatcher.pollResult();
    Mockito.verify(cycle, Mockito.never()).jobCompleted(result.getFirst(), result.getSecond());
    // No third job
    assertNull(dispatcher.pollResult());
  }

  public void testCancelDuringDispatch() {
    final Cancelable handle = Mockito.mock(Cancelable.class);
    final AtomicReference<PlanExecutor> executor = new AtomicReference<PlanExecutor>();
    final JobDispatcher dispatcher = new JobDispatcher() {
      @Override
      public Cancelable dispatchJob(final CalculationJob job, final JobResultReceiver receiver) {
        executor.get().cancel(true);
        return handle;
      }
    };
    executor.set(new PlanExecutor(createCycle(dispatcher), createPlan()));
    executor.get().start();
    assertTrue(executor.get().isCancelled());
    assertTrue(executor.get().isDone());
    Mockito.verify(handle).cancel(true);
  }

  public void testCancelAfterCompletion() {
    final NormalExecutionJobDispatcher dispatcher = new NormalExecutionJobDispatcher();
    final PlanExecutor executor = new PlanExecutor(createCycle(dispatcher), createPlan());
    dispatcher.execute(executor);
    assertFalse(executor.isCancelled());
    assertFalse(executor.cancel(true));
    assertFalse(executor.isCancelled());
  }

  public void testCancelBeforeJobSubmission() {
    final JobDispatcher dispatcher = Mockito.mock(JobDispatcher.class);
    final PlanExecutor executor = new PlanExecutor(createCycle(dispatcher), createPlan()) {
      @Override
      protected void submitExecutableJobs() {
        cancel(true);
        super.submitExecutableJobs();
      }
    };
    assertFalse(executor.isCancelled());
    executor.start();
    assertTrue(executor.isCancelled());
    Mockito.verifyZeroInteractions(dispatcher);
  }

  public void testAddListenerBeforeCompletion() throws Throwable {
    final NormalExecutionJobDispatcher dispatcher = new NormalExecutionJobDispatcher();
    final PlanExecutor executor = new PlanExecutor(createCycle(dispatcher), createPlan());
    final AtomicReference<String> result = new AtomicReference<String>();
    executor.setListener(new Listener() {
      @Override
      public void graphCompleted(final String calculationConfiguration) {
        assertEquals(result.getAndSet(calculationConfiguration), null);
      }
    });
    dispatcher.execute(executor);
    assertEquals(result.get(), "Default");
  }

  public void testAddListenerAfterCompletion() throws Throwable {
    final NormalExecutionJobDispatcher dispatcher = new NormalExecutionJobDispatcher();
    final PlanExecutor executor = new PlanExecutor(createCycle(dispatcher), createPlan());
    dispatcher.execute(executor);
    final AtomicReference<String> result = new AtomicReference<String>();
    executor.setListener(new Listener() {
      @Override
      public void graphCompleted(final String calculationConfiguration) {
        assertEquals(result.getAndSet(calculationConfiguration), null);
      }
    });
    assertEquals(result.get(), "Default");
  }

  // Timeout is set just in case "get" decides to block
  @Test(timeOut = 5000, expectedExceptions = CancellationException.class)
  public void testCancelDuringGet() throws Throwable {
    final ExecutorService threads = Executors.newSingleThreadExecutor();
    try {
      final PlanExecutor executor = new PlanExecutor(createCycle(new JobDispatcher()), createPlan());
      executor.start();
      threads.submit(new Runnable() {
        @Override
        public void run() {
          executor.cancel(true);
        }
      });
      executor.get();
    } finally {
      threads.shutdownNow();
      threads.awaitTermination(3, TimeUnit.SECONDS);
    }
  }

  // Timeout is set just in case "get" decides to block
  @Test(timeOut = 5000, expectedExceptions = CancellationException.class)
  public void testCancelDuringGetWithTimeout() throws Throwable {
    final ExecutorService threads = Executors.newSingleThreadExecutor();
    try {
      final PlanExecutor executor = new PlanExecutor(createCycle(new JobDispatcher()), createPlan());
      executor.start();
      threads.submit(new Runnable() {
        @Override
        public void run() {
          executor.cancel(true);
        }
      });
      executor.get(5, TimeUnit.SECONDS);
    } finally {
      threads.shutdownNow();
      threads.awaitTermination(3, TimeUnit.SECONDS);
    }
  }

  // Timeout is set just in case "get" decides to block
  @Test(timeOut = 5000)
  public void testGet() throws Throwable {
    final ExecutorService threads = Executors.newSingleThreadExecutor();
    try {
      final NormalExecutionJobDispatcher dispatcher = new NormalExecutionJobDispatcher();
      final PlanExecutor executor = new PlanExecutor(createCycle(dispatcher), createPlan());
      threads.submit(new Runnable() {
        @Override
        public void run() {
          dispatcher.execute(executor);
        }
      });
      assertEquals(executor.get(), "Default");
    } finally {
      threads.shutdownNow();
      threads.awaitTermination(3, TimeUnit.SECONDS);
    }
  }

  // Timeout is set just in case "get" decides to block
  @Test(timeOut = 5000)
  public void testGetWithTimeout() throws Throwable {
    final ExecutorService threads = Executors.newSingleThreadExecutor();
    try {
      final NormalExecutionJobDispatcher dispatcher = new NormalExecutionJobDispatcher();
      final PlanExecutor executor = new PlanExecutor(createCycle(dispatcher), createPlan());
      threads.submit(new Runnable() {
        @Override
        public void run() {
          dispatcher.execute(executor);
        }
      });
      assertEquals(executor.get(5, TimeUnit.SECONDS), "Default");
    } finally {
      threads.shutdownNow();
      threads.awaitTermination(3, TimeUnit.SECONDS);
    }
  }

  // Timeout is set just in case "get" decides to block
  @Test(timeOut = 5000, expectedExceptions = TimeoutException.class)
  public void testGetWithElapsedTimeout() throws Throwable {
    final ExecutorService threads = Executors.newSingleThreadExecutor();
    try {
      final NormalExecutionJobDispatcher dispatcher = new NormalExecutionJobDispatcher();
      final PlanExecutor executor = new PlanExecutor(createCycle(dispatcher), createPlan());
      executor.get(1, TimeUnit.SECONDS);
    } finally {
      threads.shutdownNow();
      threads.awaitTermination(3, TimeUnit.SECONDS);
    }
  }

  public void testToString() {
    final PlanExecutor executor = new PlanExecutor(createCycle(new JobDispatcher()), createPlan());
    assertEquals(executor.toString(), "ExecutingGraph-Default for TEST-CYCLE");
  }

}
TOP

Related Classes of com.opengamma.engine.exec.PlanExecutorTest

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.