/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.engine.view.worker;
import static org.testng.Assert.assertEquals;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.Test;
import org.threeten.bp.Instant;
import com.opengamma.core.change.BasicChangeManager;
import com.opengamma.core.change.ChangeManager;
import com.opengamma.core.change.ChangeType;
import com.opengamma.core.position.Portfolio;
import com.opengamma.engine.ComputationTargetResolver;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.depgraph.DependencyGraph;
import com.opengamma.engine.depgraph.builder.TestDependencyGraphBuilder;
import com.opengamma.engine.function.CompiledFunctionService;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.marketdata.spec.MarketData;
import com.opengamma.engine.target.ComputationTargetReference;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.engine.view.ViewComputationResultModel;
import com.opengamma.engine.view.ViewDefinition;
import com.opengamma.engine.view.compilation.CompiledViewCalculationConfiguration;
import com.opengamma.engine.view.compilation.CompiledViewCalculationConfigurationImpl;
import com.opengamma.engine.view.compilation.CompiledViewDefinitionWithGraphs;
import com.opengamma.engine.view.compilation.CompiledViewDefinitionWithGraphsImpl;
import com.opengamma.engine.view.cycle.ViewCycle;
import com.opengamma.engine.view.cycle.ViewCycleMetadata;
import com.opengamma.engine.view.execution.ExecutionFlags;
import com.opengamma.engine.view.execution.ExecutionOptions;
import com.opengamma.engine.view.execution.ViewCycleExecutionOptions;
import com.opengamma.engine.view.execution.ViewExecutionOptions;
import com.opengamma.engine.view.impl.ViewProcessContext;
import com.opengamma.id.ObjectId;
import com.opengamma.id.UniqueId;
import com.opengamma.id.VersionCorrection;
import com.opengamma.util.function.BiFunction;
import com.opengamma.util.test.TestGroup;
/**
* Tests the {@link ParallelRecompilationViewProcessWorker} inner classes when running against an infinite sequence at resolver LATEST/LATEST.
*/
@Test(groups = TestGroup.UNIT_SLOW)
public class ParallelRecompilationInfiniteLatestTest extends AbstractParallelRecompilationTest {
private static final Logger s_logger = LoggerFactory.getLogger(ParallelRecompilationInfiniteLatestTest.class);
private CompiledViewDefinitionWithGraphs compiledViewDefinition(final ViewDefinition viewDefinition, final Map<ComputationTargetReference, UniqueId> resolutions) {
final VersionCorrection versionCorrection = VersionCorrection.of(Instant.now(), Instant.now());
final DependencyGraph graph = new TestDependencyGraphBuilder("Default").buildGraph();
final Portfolio portfolio = Mockito.mock(Portfolio.class);
return new CompiledViewDefinitionWithGraphsImpl(versionCorrection, "view-id", viewDefinition, Collections.singleton(graph), new HashMap<ComputationTargetReference, UniqueId>(resolutions),
portfolio, 0, Collections.<CompiledViewCalculationConfiguration>singleton(CompiledViewCalculationConfigurationImpl.of(graph)), null, null);
}
private ViewProcessWorkerFactory workerFactory(final ExecutorService executor, final Map<ComputationTargetReference, UniqueId> resolutions) {
final Random rand = new Random();
return new ViewProcessWorkerFactory() {
@Override
public ViewProcessWorker createWorker(final ViewProcessWorkerContext context, final ViewExecutionOptions executionOptions, final ViewDefinition viewDefinition) {
final ViewProcessWorker worker = Mockito.mock(ViewProcessWorker.class);
final AtomicBoolean terminated = new AtomicBoolean();
Mockito.doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
terminated.set(true);
return null;
}
}).when(worker).terminate();
executor.submit(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(rand.nextInt(500) + 350);
context.viewDefinitionCompiled(Mockito.mock(ViewExecutionDataProvider.class), compiledViewDefinition(viewDefinition, resolutions));
while (!terminated.get()) {
context.cycleStarted(Mockito.mock(ViewCycleMetadata.class));
Thread.sleep(rand.nextInt(300) + 50);
context.cycleFragmentCompleted(Mockito.mock(ViewComputationResultModel.class), viewDefinition);
Thread.sleep(rand.nextInt(300) + 50);
context.cycleCompleted(Mockito.mock(ViewCycle.class));
Thread.sleep(rand.nextInt(300) + 50);
}
} catch (InterruptedException e) {
s_logger.debug("Interrupted", e);
} catch (RuntimeException e) {
s_logger.error("Caught exception", e);
}
}
});
return worker;
}
};
}
private static class MockContext implements ViewProcessWorkerContext {
private final ViewProcessContext _context;
private final LinkedBlockingQueue<String> _events = new LinkedBlockingQueue<String>();
public MockContext(final ViewProcessContext context) {
_context = context;
}
public String event() throws InterruptedException {
return _events.poll(5000, TimeUnit.MILLISECONDS);
}
@Override
public ViewProcessContext getProcessContext() {
return _context;
}
@Override
public void viewDefinitionCompiled(ViewExecutionDataProvider dataProvider, CompiledViewDefinitionWithGraphs compiled) {
_events.add("view definition compiled");
}
@Override
public void viewDefinitionCompilationFailed(Instant compilationTime, Exception exception) {
_events.add("view definition compilation failed");
}
@Override
public void cycleStarted(ViewCycleMetadata cycleMetadata) {
_events.add("cycle started");
}
@Override
public void cycleFragmentCompleted(ViewComputationResultModel result, ViewDefinition viewDefinition) {
_events.add("cycle fragment completed");
}
@Override
public void cycleCompleted(ViewCycle cycle) {
_events.add("cycle completed");
}
@Override
public void cycleExecutionFailed(ViewCycleExecutionOptions options, Exception exception) {
_events.add("cycle execution failed");
}
@Override
public void workerCompleted() {
_events.add("worker completed");
}
}
@Override
protected void testImpl(final BiFunction<ParallelRecompilationViewProcessWorker, ViewExecutionOptions, Void> callback) throws InterruptedException {
final ExecutorService executor = Executors.newCachedThreadPool();
try {
final Map<ComputationTargetReference, UniqueId> resolutions = new HashMap<ComputationTargetReference, UniqueId>();
resolutions.put(new ComputationTargetSpecification(ComputationTargetType.PORTFOLIO, UniqueId.of("Test", "0")), UniqueId.of("Test", "0", "0"));
final ChangeManager changeManager = new BasicChangeManager();
final ComputationTargetResolver targetResolver = Mockito.mock(ComputationTargetResolver.class);
Mockito.when(targetResolver.changeManager()).thenReturn(changeManager);
final FunctionCompilationContext ctx = new FunctionCompilationContext();
ctx.setRawComputationTargetResolver(targetResolver);
final CompiledFunctionService cfs = Mockito.mock(CompiledFunctionService.class);
Mockito.when(cfs.getFunctionCompilationContext()).thenReturn(ctx);
final ViewProcessContext vpContext = Mockito.mock(ViewProcessContext.class);
Mockito.when(vpContext.getFunctionCompilationService()).thenReturn(cfs);
final MockContext context = new MockContext(vpContext);
final ViewExecutionOptions options = ExecutionOptions.infinite(MarketData.live(), ExecutionFlags.none().ignoreCompilationValidity().get());
final ViewDefinition viewDefinition = Mockito.mock(ViewDefinition.class);
final ParallelRecompilationViewProcessWorker worker = new ParallelRecompilationViewProcessWorker(workerFactory(executor, resolutions), context, options, viewDefinition);
callback.apply(worker, options);
s_logger.debug("Waiting for initial compilation");
assertEquals(context.event(), "view definition compiled"); // From primary worker
for (int j = 0; j < 5; j++) {
// Expect a sequence of operations
for (int i = 0; i < 3; i++) {
s_logger.debug("Waiting for cycle to start");
assertEquals(context.event(), "cycle started"); // From primary worker
s_logger.info("Cycle started");
assertEquals(context.event(), "cycle fragment completed");
s_logger.info("Cycle fragment completed");
assertEquals(context.event(), "cycle completed");
s_logger.info("Cycle completed");
}
// Signal change ...
s_logger.debug("Signalling change");
resolutions.put(new ComputationTargetSpecification(ComputationTargetType.PORTFOLIO, UniqueId.of("Test", "0")), UniqueId.of("Test", "0", Integer.toString(j + 1)));
changeManager.entityChanged(ChangeType.CHANGED, ObjectId.of("Test", "0"), Instant.now(), Instant.now(), Instant.now());
s_logger.info("Change signalled");
// ... and expect a view definition compiled to interrupt the sequence
String event = context.event();
for (int i = 0; i < 20; i++) {
if (event.equals("cycle started")) {
s_logger.info("Legacy cycle started");
event = context.event();
if (event.equals("cycle fragment completed")) {
s_logger.info("Legacy fragment completed");
event = context.event();
if (event.equals("cycle completed")) {
s_logger.info("Legacy cycle completed");
event = context.event();
} else {
break;
}
} else {
break;
}
} else {
break;
}
}
assertEquals(event, "view definition compiled");
s_logger.info("New compilation");
}
} finally {
executor.shutdownNow();
}
}
}