/**
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.calc.runner;
import static com.opengamma.strata.basics.currency.Currency.USD;
import static com.opengamma.strata.calc.ReportingCurrency.NATURAL;
import static com.opengamma.strata.collect.CollectProjectAssertions.assertThat;
import static com.opengamma.strata.collect.TestHelper.assertThrowsIllegalArg;
import static com.opengamma.strata.collect.TestHelper.date;
import java.time.LocalDate;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.MoreExecutors;
import com.opengamma.strata.basics.CalculationTarget;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.currency.Currency;
import com.opengamma.strata.calc.Column;
import com.opengamma.strata.calc.Measure;
import com.opengamma.strata.calc.Results;
import com.opengamma.strata.calc.TestingMeasures;
import com.opengamma.strata.calc.marketdata.TestId;
import com.opengamma.strata.calc.marketdata.TestObservableId;
import com.opengamma.strata.calc.runner.CalculationTaskTest.TestTarget;
import com.opengamma.strata.collect.result.Result;
import com.opengamma.strata.data.MarketData;
import com.opengamma.strata.data.scenario.ScenarioArray;
import com.opengamma.strata.data.scenario.ScenarioMarketData;
/**
* Test {@link CalculationTaskRunner} and {@link DefaultCalculationTaskRunner}.
*/
@Test
public class DefaultCalculationTaskRunnerTest {
private static final ReferenceData REF_DATA = ReferenceData.standard();
private static final TestTarget TARGET = new TestTarget();
private static final LocalDate VAL_DATE = date(2011, 3, 8);
private static final Set<Measure> MEASURES = ImmutableSet.of(TestingMeasures.PRESENT_VALUE);
//-------------------------------------------------------------------------
/**
* Test that ScenarioArrays containing a single value are unwrapped.
*/
public void unwrapScenarioResults() throws Exception {
ScenarioArray<String> scenarioResult = ScenarioArray.of("foo");
ScenarioResultFunction fn = new ScenarioResultFunction(TestingMeasures.PRESENT_VALUE, scenarioResult);
CalculationTaskCell cell = CalculationTaskCell.of(0, 0, TestingMeasures.PRESENT_VALUE, NATURAL);
CalculationTask task = CalculationTask.of(TARGET, fn, cell);
Column column = Column.of(TestingMeasures.PRESENT_VALUE);
CalculationTasks tasks = CalculationTasks.of(ImmutableList.of(task), ImmutableList.of(column));
// using the direct executor means there is no need to close/shutdown the runner
CalculationTaskRunner test = CalculationTaskRunner.of(MoreExecutors.newDirectExecutorService());
MarketData marketData = MarketData.empty(VAL_DATE);
Results results1 = test.calculate(tasks, marketData, REF_DATA);
Result<?> result1 = results1.get(0, 0);
// Check the result contains the string directly, not the result wrapping the string
assertThat(result1).hasValue("foo");
Results results2 = test.calculateMultiScenario(tasks, ScenarioMarketData.of(1, marketData), REF_DATA);
Result<?> result2 = results2.get(0, 0);
// Check the result contains the scenario result wrapping the string
assertThat(result2).hasValue(scenarioResult);
ResultsListener resultsListener = new ResultsListener();
test.calculateAsync(tasks, marketData, REF_DATA, resultsListener);
CompletableFuture<Results> future = resultsListener.getFuture();
// The future is guaranteed to be done because everything is running on a single thread
assertThat(future.isDone()).isTrue();
Results results3 = future.get();
Result<?> result3 = results3.get(0, 0);
// Check the result contains the string directly, not the result wrapping the string
assertThat(result3).hasValue("foo");
}
/**
* Test that ScenarioArrays containing multiple values are an error.
*/
public void unwrapMultipleScenarioResults() {
ScenarioArray<String> scenarioResult = ScenarioArray.of("foo", "bar");
ScenarioResultFunction fn = new ScenarioResultFunction(TestingMeasures.PAR_RATE, scenarioResult);
CalculationTaskCell cell = CalculationTaskCell.of(0, 0, TestingMeasures.PAR_RATE, NATURAL);
CalculationTask task = CalculationTask.of(TARGET, fn, cell);
Column column = Column.of(TestingMeasures.PAR_RATE);
CalculationTasks tasks = CalculationTasks.of(ImmutableList.of(task), ImmutableList.of(column));
// using the direct executor means there is no need to close/shutdown the runner
CalculationTaskRunner test = CalculationTaskRunner.of(MoreExecutors.newDirectExecutorService());
MarketData marketData = MarketData.empty(VAL_DATE);
assertThrowsIllegalArg(() -> test.calculate(tasks, marketData, REF_DATA));
}
/**
* Test that ScenarioArrays containing a single value are unwrapped when calling calculateAsync().
*/
public void unwrapScenarioResultsAsync() {
ScenarioArray<String> scenarioResult = ScenarioArray.of("foo");
ScenarioResultFunction fn = new ScenarioResultFunction(TestingMeasures.PRESENT_VALUE, scenarioResult);
CalculationTaskCell cell = CalculationTaskCell.of(0, 0, TestingMeasures.PRESENT_VALUE, NATURAL);
CalculationTask task = CalculationTask.of(TARGET, fn, cell);
Column column = Column.of(TestingMeasures.PRESENT_VALUE);
CalculationTasks tasks = CalculationTasks.of(ImmutableList.of(task), ImmutableList.of(column));
// using the direct executor means there is no need to close/shutdown the runner
CalculationTaskRunner test = CalculationTaskRunner.of(MoreExecutors.newDirectExecutorService());
Listener listener = new Listener();
MarketData marketData = MarketData.empty(VAL_DATE);
test.calculateAsync(tasks, marketData, REF_DATA, listener);
CalculationResult calculationResult1 = listener.result;
Result<?> result1 = calculationResult1.getResult();
// Check the result contains the string directly, not the result wrapping the string
assertThat(result1).hasValue("foo");
test.calculateMultiScenarioAsync(tasks, ScenarioMarketData.of(1, marketData), REF_DATA, listener);
CalculationResult calculationResult2 = listener.result;
Result<?> result2 = calculationResult2.getResult();
// Check the result contains the scenario result wrapping the string
assertThat(result2).hasValue(scenarioResult);
}
//-------------------------------------------------------------------------
public static final class TestFunction implements CalculationFunction<TestTarget> {
@Override
public Class<TestTarget> targetType() {
return TestTarget.class;
}
@Override
public Set<Measure> supportedMeasures() {
return MEASURES;
}
@Override
public Currency naturalCurrency(TestTarget trade, ReferenceData refData) {
return USD;
}
@Override
public FunctionRequirements requirements(
TestTarget target,
Set<Measure> measures,
CalculationParameters parameters,
ReferenceData refData) {
return FunctionRequirements.builder()
.valueRequirements(
ImmutableSet.of(
TestId.of("1"),
TestObservableId.of("2")))
.timeSeriesRequirements(TestObservableId.of("3"))
.build();
}
@Override
public Map<Measure, Result<?>> calculate(
TestTarget target,
Set<Measure> measures,
CalculationParameters parameters,
ScenarioMarketData marketData,
ReferenceData refData) {
ScenarioArray<String> array = ScenarioArray.of("bar");
return ImmutableMap.of(TestingMeasures.PRESENT_VALUE, Result.success(array));
}
}
/**
* Tests that running an empty list of tasks completes and returns a set of results with zero rows.
*/
public void runWithNoTasks() {
Column column = Column.of(TestingMeasures.PRESENT_VALUE);
CalculationTasks tasks = CalculationTasks.of(ImmutableList.of(), ImmutableList.of(column));
// using the direct executor means there is no need to close/shutdown the runner
CalculationTaskRunner test = CalculationTaskRunner.of(MoreExecutors.newDirectExecutorService());
MarketData marketData = MarketData.empty(VAL_DATE);
Results results = test.calculate(tasks, marketData, REF_DATA);
assertThat(results.getRowCount()).isEqualTo(0);
assertThat(results.getColumnCount()).isEqualTo(1);
assertThat(results.getColumns().get(0).getMeasure()).isEqualTo(TestingMeasures.PRESENT_VALUE);
}
//-------------------------------------------------------------------------
private static final class ScenarioResultFunction implements CalculationFunction<TestTarget> {
private final Measure measure;
private final ScenarioArray<String> result;
private ScenarioResultFunction(Measure measure, ScenarioArray<String> result) {
this.measure = measure;
this.result = result;
}
@Override
public Class<TestTarget> targetType() {
return TestTarget.class;
}
@Override
public Set<Measure> supportedMeasures() {
return ImmutableSet.of(measure);
}
@Override
public Currency naturalCurrency(TestTarget trade, ReferenceData refData) {
return USD;
}
@Override
public FunctionRequirements requirements(
TestTarget target,
Set<Measure> measures,
CalculationParameters parameters,
ReferenceData refData) {
return FunctionRequirements.empty();
}
@Override
public Map<Measure, Result<?>> calculate(
TestTarget target,
Set<Measure> measures,
CalculationParameters parameters,
ScenarioMarketData marketData,
ReferenceData refData) {
return ImmutableMap.of(measure, Result.success(result));
}
}
//-------------------------------------------------------------------------
private static final class Listener implements CalculationListener {
private CalculationResult result;
@Override
public void resultReceived(CalculationTarget target, CalculationResult result) {
this.result = result;
}
@Override
public void calculationsComplete() {
// Do nothing
}
}
}