/**
* Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.sesame;
import static com.opengamma.util.money.Currency.AUD;
import static com.opengamma.util.money.Currency.GBP;
import static org.testng.AssertJUnit.fail;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.threeten.bp.Instant;
import org.threeten.bp.ZonedDateTime;
import com.codahale.metrics.MetricRegistry;
import com.google.common.base.Optional;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.opengamma.core.id.ExternalSchemes;
import com.opengamma.core.position.impl.SimpleTrade;
import com.opengamma.core.security.impl.SimpleSecurityLink;
import com.opengamma.financial.security.cashflow.CashFlowSecurity;
import com.opengamma.financial.security.equity.EquitySecurity;
import com.opengamma.id.ExternalId;
import com.opengamma.id.UniqueId;
import com.opengamma.service.ServiceContext;
import com.opengamma.service.ThreadLocalServiceContext;
import com.opengamma.service.VersionCorrectionProvider;
import com.opengamma.sesame.cache.CacheProvider;
import com.opengamma.sesame.cache.NoOpCacheInvalidator;
import com.opengamma.sesame.config.FunctionModelConfig;
import com.opengamma.sesame.engine.ComponentMap;
import com.opengamma.sesame.engine.DefaultCacheProvider;
import com.opengamma.sesame.engine.FixedInstantVersionCorrectionProvider;
import com.opengamma.sesame.engine.FunctionService;
import com.opengamma.sesame.engine.ViewFactory;
import com.opengamma.sesame.function.AvailableImplementations;
import com.opengamma.sesame.function.AvailableOutputs;
import com.opengamma.util.ArgumentChecker;
/**
* Helper methods for engine tests.
*/
public class EngineTestUtils {
public static final String EQUITY_NAME = "An equity security";
public static final String CASH_FLOW_NAME = "A cash flow security";
public static final String EQUITY_BLOOMBERG_TICKER = "ACME US Equity";
public static final String EQUITY_ACTIV_SYMBOL = "ACME.";
public static final String CASH_FLOW_BLOOMBERG_TICKER = "TEST US Cash Flow";
public static final String CASH_FLOW_ACTIV_SYMBOL = "CASHFLOW.";
private static final UniqueId EQUITY_TRADE_ID = UniqueId.of("trdId", "321");
private static final UniqueId CASH_FLOW_TRADE_ID = UniqueId.of("trdId", "432");
private static final long MAX_CACHE_ENTRIES = 100_000;
private EngineTestUtils() {
}
public static SimpleTrade createEquityTrade() {
EquitySecurity security = new EquitySecurity("exc", "exc", "compName", AUD);
security.setUniqueId(UniqueId.of("secId", "123"));
security.setName(EQUITY_NAME);
security.addExternalId(ExternalId.of(ExternalSchemes.BLOOMBERG_TICKER, EQUITY_BLOOMBERG_TICKER));
security.addExternalId(ExternalId.of(ExternalSchemes.ACTIVFEED_TICKER, EQUITY_ACTIV_SYMBOL));
SimpleTrade trade = new SimpleTrade();
trade.setQuantity(BigDecimal.ONE);
SimpleSecurityLink securityLink = new SimpleSecurityLink(security.getExternalIdBundle());
securityLink.setTarget(security);
trade.setSecurityLink(securityLink);
trade.setUniqueId(EQUITY_TRADE_ID);
return trade;
}
public static SimpleTrade createCashFlowTrade() {
CashFlowSecurity security = new CashFlowSecurity(GBP, ZonedDateTime.now(), 12345d);
security.setUniqueId(UniqueId.of("secId", "234"));
security.setName(CASH_FLOW_NAME);
security.addExternalId(ExternalId.of(ExternalSchemes.BLOOMBERG_TICKER, CASH_FLOW_BLOOMBERG_TICKER));
security.addExternalId(ExternalId.of(ExternalSchemes.ACTIVFEED_TICKER, CASH_FLOW_ACTIV_SYMBOL));
SimpleTrade trade = new SimpleTrade();
trade.setQuantity(BigDecimal.ONE);
SimpleSecurityLink securityLink = new SimpleSecurityLink(security.getExternalIdBundle());
securityLink.setTarget(security);
trade.setSecurityLink(securityLink);
trade.setUniqueId(CASH_FLOW_TRADE_ID);
return trade;
}
public static ViewFactory createViewFactory(Map<Class<?>, Object> components,
AvailableOutputs availableOutputs,
AvailableImplementations availableImplementations) {
FixedInstantVersionCorrectionProvider versionCorrectionProvider =
new FixedInstantVersionCorrectionProvider(Instant.now());
ServiceContext serviceContext = ServiceContext.of(components)
.with(VersionCorrectionProvider.class, versionCorrectionProvider);
ThreadLocalServiceContext.init(serviceContext);
ExecutorService executor = Executors.newFixedThreadPool(2);
return new ViewFactory(executor,
ComponentMap.of(components),
availableOutputs,
availableImplementations,
FunctionModelConfig.EMPTY,
FunctionService.DEFAULT_SERVICES,
createCacheBuilder(),
new NoOpCacheInvalidator(),
Optional.<MetricRegistry>absent());
}
/**
* @return a cache provider configured for use with the engine
*/
public static CacheProvider createCacheProvider() {
return new DefaultCacheProvider(createCacheBuilder().<Object, Object>build());
}
public static CacheBuilder<Object, Object> createCacheBuilder() {
int nProcessors = Runtime.getRuntime().availableProcessors();
int concurrencyLevel = nProcessors * 8;
return CacheBuilder.newBuilder().maximumSize(MAX_CACHE_ENTRIES).softValues().concurrencyLevel(concurrencyLevel);
}
public static void assertJsonEquals(Map<?, ?> expected, Map<?, ?> actual) {
List<MapDifference> differences = compareMaps(expected, actual);
if (!differences.isEmpty()) {
fail("JSON not equal, differences:\n" + StringUtils.join(differences, "\n") + "\n");
}
}
public static class MapDifference {
private final List<Object> _path;
private final Object _left;
private final Object _right;
public MapDifference(@Nullable Object left, @Nullable Object right, List<Object> path) {
_path = ArgumentChecker.notNull(path, "path");
_left = left;
_right = right;
}
@Override
public String toString() {
String left = (_left instanceof String) ? "\"" + _left + "\"" : Objects.toString(_left);
String right = (_right instanceof String) ? "\"" + _right + "\"" : Objects.toString(_right);
return left + ", " + right + ", " + _path;
}
@Override
public int hashCode() {
return Objects.hash(_path, _left, _right);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
MapDifference other = (MapDifference) obj;
return Objects.equals(this._path, other._path) &&
Objects.equals(this._left, other._left) &&
Objects.equals(this._right, other._right);
}
}
public static List<MapDifference> compareMaps(Map<?, ?> left, Map<?, ?> right) {
List<Object> path = new ArrayList<>();
return compareMaps(left, right, path);
}
@SuppressWarnings("unchecked")
private static List<MapDifference> compareMaps(Object left, Object right, List<Object> path) {
ImmutableList.Builder<MapDifference> differences = ImmutableList.builder();
if (left instanceof Map && right instanceof Map) {
Map leftMap = (Map) left;
Map rightMap = (Map) right;
Set<Object> keys = new TreeSet<Object>(leftMap.keySet());
keys.addAll(rightMap.keySet());
for (Object key : keys) {
Object leftVal = leftMap.get(key);
Object rightVal = rightMap.get(key);
differences.addAll(compareMaps(leftVal, rightVal, newPath(path, key)));
}
} else if (left instanceof Collection && right instanceof Collection) {
List<?> leftList = Lists.newArrayList(((Collection) left));
List<?> rightList = Lists.newArrayList(((Collection) right));
int size = Math.max(leftList.size(), rightList.size());
for (int i = 0; i < size; i++) {
Object leftVal;
Object rightVal;
if (i == leftList.size()) {
leftVal = null;
rightVal = rightList.get(i);
} else if (i == rightList.size()) {
leftVal = leftList.get(i);
rightVal = null;
} else {
leftVal = leftList.get(i);
rightVal = rightList.get(i);
}
differences.addAll(compareMaps(leftVal, rightVal, newPath(path, i)));
}
} else {
if (!Objects.equals(left, right)) {
differences.add(new MapDifference(left, right, path));
}
}
return differences.build();
}
private static List<Object> newPath(List<Object> path, Object nextElement) {
return ImmutableList.builder().addAll(path).add(nextElement).build();
}
}