package org.marketcetera.strategy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.marketcetera.module.Messages.*;
import static org.marketcetera.strategy.Language.RUBY;
import static org.marketcetera.strategy.Status.*;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Callable;
import javax.management.AttributeChangeNotification;
import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationListener;
import org.junit.Test;
import org.marketcetera.core.Util;
import org.marketcetera.marketdata.MarketDataFeedTestBase;
import org.marketcetera.marketdata.MarketDataRequestBuilder;
import org.marketcetera.marketdata.bogus.BogusFeedModuleFactory;
import org.marketcetera.module.*;
import org.marketcetera.util.log.SLF4JLoggerProxy;
import org.marketcetera.util.test.UnicodeData;
/* $License$ */
/**
* Tests {@link StrategyModule}.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: StrategyModuleTest.java 16633 2013-07-17 01:26:26Z colin $
* @since 1.0.0
*/
public class StrategyModuleTest
extends StrategyTestBase
{
/**
* Tests the function of the cancel method.
*
* @throws Exception if an error occurs
*/
@Test
public void cancel()
throws Exception
{
// TODO complete this test when order and suggestion creation is added
// create an external module
// ask it to subscribe to strategy orders
// trigger the strategy to produce orders
// test that it receives orders
// cancel the request
// verify that the module is no longer receiving orders even though the strategy is still producing them
// repeat test with suggestions
}
/**
* Tests starting and stopping of strategies.
*
* @throws Exception if an error occurs
*/
@Test
public void lifecycle()
throws Exception
{
assertTrue(moduleManager.getModuleInstances(StrategyModuleFactory.PROVIDER_URN).isEmpty());
ModuleURN strategy = moduleManager.createModule(StrategyModuleFactory.PROVIDER_URN,
"MyStategy",
RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
new Properties(),
false,
outputURN);
assertEquals(1,
moduleManager.getModuleInstances(StrategyModuleFactory.PROVIDER_URN).size());
assertEquals(strategy,
moduleManager.getModuleInstances(StrategyModuleFactory.PROVIDER_URN).get(0));
assertFalse(moduleManager.getModuleInfo(strategy).getState().isStarted());
startStrategy(strategy);
assertTrue(moduleManager.getModuleInfo(strategy).getState().isStarted());
stopStrategy(strategy);
assertEquals(1,
moduleManager.getModuleInstances(StrategyModuleFactory.PROVIDER_URN).size());
assertEquals(strategy,
moduleManager.getModuleInstances(StrategyModuleFactory.PROVIDER_URN).get(0));
moduleManager.deleteModule(strategy);
assertTrue(moduleManager.getModuleInstances(StrategyModuleFactory.PROVIDER_URN).isEmpty());
}
/**
* Tests the case where after an uncompiling strategy is fixed and restarted, it cannot be
* stopped if the strategy originally requested orders to be routed to the ORS.
*
* @throws Exception if an error occurs
*/
@Test
public void uncompilingRoutedStrategy()
throws Exception
{
// create a strategy written to a file that does not compile
String badStrategy = "require 'java'\n" +
"java_import org.marketcetera.strategy.ruby.Strategy\n" +
"java_import java.math.BigDecimal\n" +
"class MyStrategy < Strategy\n" +
" this just won't compile\n" +
"end\n";
String goodStrategy = "require 'java'\n" +
"java_import org.marketcetera.strategy.ruby.Strategy\n" +
"java_import java.math.BigDecimal\n" +
"class MyStrategy < Strategy\n" +
" TEST = BigDecimal.new(\"1\")\n" +
"end\n";
// start with the bad strategy
File strategyFile = File.createTempFile("strategy",
".rb");
strategyFile.deleteOnExit();
BufferedWriter writer = new BufferedWriter(new FileWriter(strategyFile));
writer.write(badStrategy);
writer.close();
final ModuleURN strategyURN = moduleManager.createModule(StrategyModuleFactory.PROVIDER_URN,
"MyStategy",
"MyStrategy",
RUBY,
strategyFile,
new Properties(),
true,
outputURN);
new ExpectedFailure<ModuleException>() {
@Override
protected void run()
throws Exception
{
moduleManager.start(strategyURN);
}
};
verifyStrategyReady(strategyURN);
verifyStrategyStatus(strategyURN,
FAILED);
// "correct" the strategy
writer = new BufferedWriter(new FileWriter(strategyFile));
writer.write(goodStrategy);
writer.close();
// start it again
moduleManager.start(strategyURN);
verifyStrategyReady(strategyURN);
verifyStrategyStatus(strategyURN,
RUNNING);
// stop it
moduleManager.stop(strategyURN);
verifyStrategyStopped(strategyURN);
}
/**
* Tests processing of parameters with strategy module construction.
*
* @throws Exception if an error occurs
*/
@Test
public void create()
throws Exception
{
doWrongParameterCountTest((Object[])null);
doWrongParameterCountTest(new Object[0]);
doWrongParameterCountTest(RubyLanguageTest.STRATEGY_NAME);
doWrongParameterCountTest(RubyLanguageTest.STRATEGY_NAME,
RUBY);
doWrongParameterCountTest(RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY);
doWrongParameterCountTest(RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
new Properties());
doWrongParameterCountTest(RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
new Properties(),
"false");
// muddle types
doWrongTypeParameterTest(0,
this,
RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
new Properties(),
false,
outputURN);
doWrongTypeParameterTest(1,
"MyStrategyURN",
this,
RUBY,
RubyLanguageTest.STRATEGY,
new Properties(),
false,
outputURN);
doWrongTypeParameterTest(2,
"MyStrategyURN",
RubyLanguageTest.STRATEGY_NAME,
this,
RubyLanguageTest.STRATEGY,
new Properties(),
false,
outputURN);
doWrongTypeParameterTest(3,
"MyStrategyURN",
RubyLanguageTest.STRATEGY_NAME,
RUBY,
this,
new Properties(),
false,
outputURN);
doWrongTypeParameterTest(4,
"MyStrategyURN",
RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
this,
false,
outputURN);
doWrongTypeParameterTest(5,
"MyStrategyURN",
RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
new Properties(),
this,
outputURN);
doWrongTypeParameterTest(6,
"MyStrategyURN",
RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
new Properties(),
false,
this);
// create a good 'un just to prove we can
ModuleURN strategy = createStrategy(RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
new Properties(),
false,
outputURN);
assertEquals(1,
moduleManager.getModuleInstances(StrategyModuleFactory.PROVIDER_URN).size());
assertEquals(strategy,
moduleManager.getModuleInstances(StrategyModuleFactory.PROVIDER_URN).get(0));
}
/**
* Tests processing of data requests.
*
* @throws Exception if an error occurs
*/
@Test
public void requestData()
throws Exception
{
ModuleURN strategy = createStrategy(RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
new Properties(),
false,
outputURN);
assertTrue(moduleManager.getModuleInfo(strategy).getState().isStarted());
// try some badly formed requests
// null payload
new ExpectedFailure<IllegalRequestParameterValue>(ILLEGAL_REQ_PARM_VALUE,
strategy.toString(),
null) {
@Override
protected void run()
throws Exception
{
moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
null) });
}
};
// String payload, but the wrong contents
final String badPayload = "some stuff-" + System.nanoTime();
new ExpectedFailure<IllegalRequestParameterValue>(ILLEGAL_REQ_PARM_VALUE,
strategy.toString(),
badPayload) {
@Override
protected void run()
throws Exception
{
moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
badPayload) });
}
};
// wrong payload type
new ExpectedFailure<UnsupportedRequestParameterType>(UNSUPPORTED_REQ_PARM_TYPE,
strategy.toString(),
moduleManager.getClass().getName()) {
@Override
protected void run()
throws Exception
{
moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
moduleManager) });
}
};
// correct String payload
DataFlowID flowID1 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
"OrDeRs") });
DataFlowID flowID2 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
"SuGgEsTiOnS") });
DataFlowID flowID3 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
"eVeNtS") });
DataFlowID flowID4 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
"NoTiFiCaTiOnS") });
DataFlowID flowID5 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
"LoG") });
DataFlowID flowID6 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
"AlL") });
// correct RequestType payload
DataFlowID flowID7 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
OutputType.ORDERS) });
DataFlowID flowID8 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
OutputType.SUGGESTIONS) });
DataFlowID flowID9 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
OutputType.EVENTS) });
DataFlowID flowID10 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
OutputType.NOTIFICATIONS) });
DataFlowID flowID11 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
OutputType.LOG) });
DataFlowID flowID12 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(StrategyModuleFactory.PROVIDER_URN,
OutputType.ALL) });
// TODO insert some code here to:
// 1) have a test module make the above data requests
// 2) have the strategy emit a trade suggestion and an order (verified in the module described in #1)
moduleManager.cancel(flowID1);
moduleManager.cancel(flowID2);
moduleManager.cancel(flowID3);
moduleManager.cancel(flowID4);
moduleManager.cancel(flowID5);
moduleManager.cancel(flowID6);
moduleManager.cancel(flowID7);
moduleManager.cancel(flowID8);
moduleManager.cancel(flowID9);
moduleManager.cancel(flowID10);
moduleManager.cancel(flowID11);
moduleManager.cancel(flowID12);
}
@Test
public void receiveData()
throws Exception
{
// set up a strategy and plumb it externally with a market data provider
ModuleURN strategy = createStrategy(RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
new Properties(),
false,
outputURN);
// plumb the market data provider externally to the strategy module - normally, this would be done internally, but, for this test,
// it is sufficient that the data is flowing, it doesn't matter how it gets there
DataFlowID dataFlowID = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(BogusFeedModuleFactory.PROVIDER_URN,
MarketDataRequestBuilder.newRequest().withExchange("Exchange").withSymbols("GOOG").create()),
new DataRequest(strategy) },
false);
// TODO when the strategy services come on-line, use them to measure the data coming in, for now, take a little nap and let the data flow
Thread.sleep(5000);
// stop the data flow
moduleManager.cancel(dataFlowID);
}
/**
* Tests passing null as each parameter.
*
* @throws Exception if an error occurs
*/
@Test
public void nullParameterTest()
throws Exception
{
final int[] index = new int[1];
for(index[0]=0;index[0]<6;index[0]++) {
// parameters 1, 5, 6, and 7 are optional, so nulls are allowed
if(index[0] == 0 ||
index[0] == 4 ||
index[0] == 5 ||
index[0] == 6) {
verifyStrategyStartsAndStops((index[0]==0 ? null : "MyStrategy"),
(index[0]==1 ? null : RubyLanguageTest.STRATEGY_NAME),
(index[0]==2 ? null : RUBY),
(index[0]==3 ? null : RubyLanguageTest.STRATEGY),
(index[0]==4 ? null : new Properties()),
(index[0]==5 ? null : false),
(index[0]==6 ? null : outputURN));
} else {
new ExpectedFailure<ModuleCreationException>(NULL_PARAMETER_ERROR,
index[0] + 1,
expectedTypes[index[0]].getName()) {
@Override
protected void run()
throws Exception
{
verifyStrategyStartsAndStops((index[0]==0 ? null : "MyStrategy"),
(index[0]==1 ? null : RubyLanguageTest.STRATEGY_NAME),
(index[0]==2 ? null : RUBY),
(index[0]==3 ? null : RubyLanguageTest.STRATEGY),
(index[0]==4 ? null : new Properties()),
(index[0]==5 ? null : false),
(index[0]==6 ? null : outputURN));
}
};
}
}
}
/**
* Tests permutations of an instance name.
*
* @throws Exception if an error occurs
*/
@Test
public void instanceParameterTest()
throws Exception
{
final String emptyInstance = "";
new ExpectedFailure<ModuleCreationException>(EMPTY_INSTANCE_ERROR) {
@Override
protected void run()
throws Exception
{
verifyStrategyStartsAndStops(emptyInstance,
RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
null,
null,
null);
}
};
verifyStrategyStartsAndStops("MyStrategyInstance",
RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
null,
null,
null);
}
/**
* Tests permutations of strategy name.
*
* @throws Exception if an error occurs
*/
@Test
public void nameParameterTest()
throws Exception
{
final String emptyName = "";
new ExpectedFailure<ModuleCreationException>(EMPTY_NAME_ERROR) {
@Override
protected void run()
throws Exception
{
verifyStrategyStartsAndStops(emptyName,
RUBY,
RubyLanguageTest.STRATEGY,
null,
null,
null);
}
};
verifyStrategyStartsAndStops(RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
null,
null,
null);
}
/**
* Tests various ways to specify a strategy language.
*
* @throws Exception if an error occurs
*/
@Test
public void languageParameterTest()
throws Exception
{
final String invalidLanguage = "Language-" + System.currentTimeMillis();
String validLanguage = RUBY.toString();
String validMixedCaseLanguage = "RuBy";
new ExpectedFailure<ModuleCreationException>(INVALID_LANGUAGE_ERROR,
invalidLanguage) {
@Override
protected void run()
throws Exception
{
verifyStrategyStartsAndStops(RubyLanguageTest.STRATEGY_NAME,
invalidLanguage,
RubyLanguageTest.STRATEGY,
null,
null,
null);
}
};
verifyStrategyStartsAndStops(RubyLanguageTest.STRATEGY_NAME,
validLanguage,
RubyLanguageTest.STRATEGY,
null,
null,
null);
// test again with a mixed case string
verifyStrategyStartsAndStops(RubyLanguageTest.STRATEGY_NAME,
validMixedCaseLanguage,
RubyLanguageTest.STRATEGY,
null,
null,
null);
}
/**
* Tests permutations of files passed as strategy source.
*
* @throws Exception if error occurs
*/
@Test
public void fileParameterTest()
throws Exception
{
// error conditions here are a non-existent file and a non-readable file
// non-existent file is easy, but non-readable is kinda platform-dependent (exists but cannot be read)
final File badFile = new File("this-file-really-should-not-exist-" + System.nanoTime());
new ExpectedFailure<ModuleCreationException>(FILE_DOES_NOT_EXIST_OR_IS_NOT_READABLE,
badFile.getAbsolutePath()) {
@Override
protected void run()
throws Exception
{
verifyStrategyStartsAndStops(RubyLanguageTest.STRATEGY_NAME,
RUBY,
badFile,
null,
null,
null);
}
};
}
/**
* Tests permutations of properties parameter.
*
* @throws Exception if error occurs
*/
@Test
public void propertiesParameterTest()
throws Exception
{
Properties properties = new Properties();
// empty properties
verifyStrategyStartsAndStops(RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
properties,
null,
null);
// non-empty properties
properties.setProperty("some-key",
"some value " + System.nanoTime());
verifyStrategyStartsAndStops(RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
properties,
null,
null);
}
/**
* Tests various error states of the URN parameters.
*
* @throws Exception if error occurs
*/
@Test
public void urnParameterTest()
throws Exception
{
// invalid URN
final ModuleURN invalidURN = new ModuleURN("metc:something:something");
// valid URN, but not started
final ModuleURN validUnstartedURN = moduleManager.createModule(MockRecorderModule.Factory.PROVIDER_URN);
// valid URN, started, but not receiver
final ModuleURN validURNNotReceiver = BogusFeedModuleFactory.INSTANCE_URN;
assertFalse(moduleManager.getModuleInfo(BogusFeedModuleFactory.INSTANCE_URN).isReceiver());
// test the above URNs for orders
// first, invalid URN
new ExpectedFailure<ModuleNotFoundException>(MODULE_NOT_FOUND,
invalidURN.toString()) {
@Override
protected void run()
throws Exception
{
verifyStrategyStartsAndStops(RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
null,
null,
invalidURN);
}
};
// next, valid, unstarted URN
new ExpectedFailure<ModuleStateException>(DATAFLOW_FAILED_PCPT_MODULE_STATE_INCORRECT,
validUnstartedURN.toString(), ExpectedFailure.IGNORE, ExpectedFailure.IGNORE) {
@Override
protected void run()
throws Exception
{
verifyStrategyStartsAndStops(RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
null,
null,
validUnstartedURN);
}
};
// last, valid, started URN, but not a data-receiver
new ExpectedFailure<DataFlowException>(MODULE_NOT_RECEIVER,
validURNNotReceiver.toString()) {
@Override
protected void run()
throws Exception
{
verifyStrategyStartsAndStops(RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
null,
null,
validURNNotReceiver);
}
};
}
/**
* Tests the ability to set strategy attributes with its MXBean interface.
*
* @throws Exception if an error occurs
*/
@Test
public void mxBean()
throws Exception
{
// create a valid, stopped URN
ModuleURN stoppedURN = moduleManager.createModule(MockRecorderModule.Factory.PROVIDER_URN);
assertFalse(moduleManager.getModuleInfo(stoppedURN).getState().isStarted());
// create our test data for the values to pass to the setDestination MXBean setters (suggestion and order)
// the values are null, invalid, stopped, empty, and started
final String[] urnStrings = new String[] { null, "this is not a URN", stoppedURN.getValue(), "", outputURN.getValue() };
// create the test data for the starting point for the strategy module
// the values are null and started (no stopped because then the strategy module itself would be un-startable before we even got around to
// testing the setters - no point in that
final ModuleURN[] urns = new ModuleURN[] { null, outputURN };
// create parameters test data
// empty
Properties emptyProperties = new Properties();
// non-empty
Properties nonEmptyProperties = new Properties();
nonEmptyProperties.setProperty("key1",
"value1");
// non-ascii
Properties nonASCIIProperties = new Properties();
nonASCIIProperties.setProperty("key1",
UnicodeData.HELLO_GR);
// group the parameters together
final String[] parameterStrings = new String[] { null, "ab:c=d::ef:", Util.propertiesToString(emptyProperties),
Util.propertiesToString(nonEmptyProperties),Util.propertiesToString(nonASCIIProperties) };
final Properties[] parameters = new Properties[] { null, emptyProperties, nonEmptyProperties, nonASCIIProperties };
// cycle through all the permutations for the starting point for outputs (2 values) and the value to set the destinations to
// (5 values) and new parameters (4 values) and parameters starting point (3 values), and route or not route (2 values) and change to route or not route (2 values),
// for a total of 480 test cases (2*5*4*3*2*2) while this bit of code may not be the most legible, it's easy to see that 480 test
// conditions would be a fair bit more verbose
for(int urnStringIndex=0;urnStringIndex<urnStrings.length;urnStringIndex++) {
for(int urnIndex=0;urnIndex<urns.length;urnIndex++) {
for(int parameterStringIndex=0;parameterStringIndex<parameterStrings.length;parameterStringIndex++) {
for(int parameterIndex=0;parameterIndex<parameters.length;parameterIndex++) {
for(int startRoutingIndex=0;startRoutingIndex<=1;startRoutingIndex++) {
for(int changeRoutingIndex=0;changeRoutingIndex<=1;changeRoutingIndex++) {
final int urnStringCounter = urnStringIndex;
final int urnCounter = urnIndex;
final int parmaterStringCounter = parameterStringIndex;
final int parameterCounter = parameterIndex;
final int startRoutingCounter = startRoutingIndex;
final int changeRoutingCounter = changeRoutingIndex;
SLF4JLoggerProxy.debug(this,
"Testing permutation: {} {} {} {} {} {}",
urnStringIndex,urnIndex,parameterStringIndex,parameterIndex,startRoutingIndex,changeRoutingIndex);
if(urnStringIndex == 1) { // invalid URN
new ExpectedFailure<InvalidURNException>(INVALID_URN_SCHEME) {
@Override
protected void run()
throws Exception
{
doOneMXInterfaceTest(urnStrings[urnStringCounter],
urns[urnCounter],
parameterStrings[parmaterStringCounter],
parameters[parameterCounter],
(startRoutingCounter == 0 ? false : true),
(changeRoutingCounter == 0 ? false : true));
}
};
continue;
}
if(urnStringIndex == 2) { // stopped URN
new ExpectedFailure<ModuleStateException>(DATAFLOW_FAILED_PCPT_MODULE_STATE_INCORRECT) {
@Override
protected void run()
throws Exception
{
doOneMXInterfaceTest(urnStrings[urnStringCounter],
urns[urnCounter],
parameterStrings[parmaterStringCounter],
parameters[parameterCounter],
(startRoutingCounter == 0 ? false : true),
(changeRoutingCounter == 0 ? false : true));
}
};
continue;
}
doOneMXInterfaceTest(urnStrings[urnStringIndex],
urns[urnIndex],
parameterStrings[parmaterStringCounter],
parameters[parameterCounter],
(startRoutingCounter == 0 ? false : true),
(changeRoutingCounter == 0 ? false : true));
}
}
}
}
}
}
}
/**
* Tests what happens when a module which is not a data-emitter is passed as an OrdersURN.
*
* @throws Exception if an error occurs
*/
@Test
public void ordersNonEmitter()
throws Exception
{
ModuleURN dataSink = SinkModuleFactory.INSTANCE_URN;
assertTrue(moduleManager.getModuleInfo(dataSink).getState().isStarted());
assertFalse(moduleManager.getModuleInfo(dataSink).isEmitter());
ModuleURN strategy = moduleManager.createModule(StrategyModuleFactory.PROVIDER_URN,
"MyStrategy",
RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
null,
null,
dataSink);
startStrategy(strategy);
assertTrue(moduleManager.getModuleInfo(strategy).getState().isStarted());
stopStrategy(strategy);
moduleManager.deleteModule(strategy);
}
/**
* Tests what happens if duplicate instance names are specified.
*
* @throws Exception if an error occurs
*/
@Test
public void duplicateInstanceNames()
throws Exception
{
ModuleURN strategy1 = moduleManager.createModule(StrategyModuleFactory.PROVIDER_URN,
"MyNewStrategy",
RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
null,
null,
null);
// try to create a strategy with the same specified instance name
new ExpectedFailure<ModuleCreationException>(DUPLICATE_MODULE_URN,
"metc:strategy:system:MyNewStrategy") {
@Override
protected void run()
throws Exception
{
moduleManager.createModule(StrategyModuleFactory.PROVIDER_URN,
"MyNewStrategy",
RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
null,
null,
null);
}
};
// delete the first one
moduleManager.deleteModule(strategy1);
// now it can be created again with the same name
strategy1 = moduleManager.createModule(StrategyModuleFactory.PROVIDER_URN,
"MyNewStrategy",
RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
null,
null,
null);
moduleManager.deleteModule(strategy1);
}
/**
* Tests that starting or stopping a strategy succeeds or fails depending on the strategy state.
*
* @throws Exception if an error occurs
*/
@Test
public void strategyStateChanges()
throws Exception
{
final Properties parameters = new Properties();
parameters.setProperty("shouldLoopOnStart",
"true");
parameters.setProperty("shouldLoopOnStop",
"true");
verifyPropertyNull("loopDone");
verifyPropertyNull("onStartBegins");
// need to manually start the strategy because it will be in "STARTING" status for a long long time
final ModuleURN strategyURN = moduleManager.createModule(StrategyModuleFactory.PROVIDER_URN,
null,
RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
parameters,
null,
null);
final List<Exception> thrownExceptions = new ArrayList<Exception>();
// start the strategy in another thread
Thread helperThread = new Thread(new Runnable() {
@Override
public void run()
{
try {
moduleManager.start(strategyURN);
} catch (ModuleException e) {
thrownExceptions.add(e);
}
}});
helperThread.start();
// wait until the strategy enters "STARTING"
MarketDataFeedTestBase.wait(new Callable<Boolean>(){
@Override
public Boolean call()
throws Exception
{
try {
return getStatus(strategyURN).equals(STARTING) &&
AbstractRunningStrategy.getProperty("onStartBegins") != null;
} catch (Exception e) {
return false;
}
}
});
// strategy is now looping
// reset start counter
AbstractRunningStrategy.setProperty("onStartBegins",
null);
// test to see what happens if the strategy is started again by the moduleManager
new ExpectedFailure<ModuleStateException>(MODULE_NOT_STARTED_STATE_INCORRECT,
strategyURN.toString(),
ExpectedFailure.IGNORE,
ExpectedFailure.IGNORE) {
@Override
protected void run()
throws Exception
{
moduleManager.start(strategyURN);
}
};
// release the running strategy (or it will keep running beyond the end of the test)
AbstractRunningStrategy.setProperty("shouldStopLoop",
"true");
helperThread.join();
assertTrue(thrownExceptions.isEmpty());
// wait for the strategy to become ready
verifyStrategyReady(strategyURN);
verifyStrategyStatus(strategyURN,
RUNNING);
// try to start again
new ExpectedFailure<ModuleStateException>(MODULE_NOT_STARTED_STATE_INCORRECT,
strategyURN.toString(),
ExpectedFailure.IGNORE,
ExpectedFailure.IGNORE) {
@Override
protected void run()
throws Exception
{
moduleManager.start(strategyURN);
}
};
// change status to STOPPING
// make sure the strategy loops in onStop so we have time to play with it
// reset all our flags and counters
setPropertiesToNull();
helperThread = new Thread(new Runnable() {
@Override
public void run()
{
try {
moduleManager.stop(strategyURN);
} catch (Exception e) {
thrownExceptions.add(e);
}
}
});
helperThread.start();
// wait until the strategy enters "STOPPING"
MarketDataFeedTestBase.wait(new Callable<Boolean>(){
@Override
public Boolean call()
throws Exception
{
return getStatus(strategyURN).equals(STOPPING) &&
AbstractRunningStrategy.getProperty("onStopBegins") != null;
}
});
// strategy is now looping
// reset stop counter
AbstractRunningStrategy.setProperty("onStopBegins",
null);
// module is listed as stopped
assertFalse(moduleManager.getModuleInfo(strategyURN).getState().isStarted());
// test stopping
new ExpectedFailure<ModuleStateException>(MODULE_NOT_STOPPED_STATE_INCORRECT,
strategyURN.toString(),
ExpectedFailure.IGNORE,
ExpectedFailure.IGNORE) {
@Override
protected void run()
throws Exception
{
moduleManager.stop(strategyURN);
}
};
// test starting
new ExpectedFailure<ModuleStateException>(MODULE_NOT_STARTED_STATE_INCORRECT,
strategyURN.toString(),
ExpectedFailure.IGNORE,
ExpectedFailure.IGNORE) {
@Override
protected void run()
throws Exception
{
moduleManager.start(strategyURN);
}
};
// let the strategy stop
AbstractRunningStrategy.setProperty("shouldStopLoop",
"true");
helperThread.join();
assertTrue(thrownExceptions.isEmpty());
// wait for the strategy to stop
verifyStrategyStopped(strategyURN);
verifyStrategyStatus(strategyURN,
STOPPED);
// now the strategy can start again
moduleManager.start(strategyURN);
verifyStrategyReady(strategyURN);
moduleManager.stop(strategyURN);
}
/**
* Tests strategy status changes through the strategy lifecycle.
*
* @throws Exception if an error occurs
*/
@Test
public void statusNotification()
throws Exception
{
// create a strategy (but don't start it yet)
ModuleURN strategyURN = moduleManager.createModule(StrategyModuleFactory.PROVIDER_URN,
"MyStategy",
RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
null,
null,
null);
// not started yet
assertFalse(moduleManager.getModuleInfo(strategyURN).getState().isStarted());
// can get the JMX interface for an unstarted strategy
StrategyMXBean strategyInterface = getMXProxy(strategyURN);
final List<String> statusChanges = new ArrayList<String>();
// add a subscriber
NotificationListener subscriber = new NotificationListener() {
@Override
public void handleNotification(Notification inNotification,
Object inHandback)
{
if(inNotification instanceof AttributeChangeNotification) {
AttributeChangeNotification change = (AttributeChangeNotification)inNotification;
statusChanges.add(change.getOldValue() + "->" + change.getNewValue());
}
}};
((NotificationEmitter)strategyInterface).addNotificationListener(subscriber,
null,
null);
assertTrue(statusChanges.isEmpty());
// start the module
moduleManager.start(strategyURN);
verifyStrategyReady(strategyURN);
// verify that the strategy changes were received (UNSTARTED->COMPILING, COMPILING->STARTING, STARTING->RUNNING)
assertEquals(3,
statusChanges.size());
assertEquals("UNSTARTED->COMPILING",
statusChanges.get(0));
assertEquals("COMPILING->STARTING",
statusChanges.get(1));
assertEquals("STARTING->RUNNING",
statusChanges.get(2));
// disconnect the listener
((NotificationEmitter)strategyInterface).removeNotificationListener(subscriber);
// empty the list
statusChanges.clear();
moduleManager.stop(strategyURN);
// make sure the change list hasn't grown
assertTrue(statusChanges.isEmpty());
}
/**
* Executes a single permutation of a strategy attribute get/set test.
*
* @param inOutputDestination a <code>String</code> value or null
* @param inOutputStart a <code>ModuleURN</code> value or null
* @param inNewParameters a <code>String</code> value containing a properly formatted properties string or null
* @param inStartingParameters a <code>Properties</code> value containing the starting parameters value or null
* @param inStartingRouteToORS a <code>boolean</code> value indicating whether the strategy should initially route to the ORS or not
* @param inNewRouting a <code>boolean</code> value indicating whether the strategy should be changed to route to the ORS or not
* @return a <code>ModuleURN</code> value containing the strategy module guaranteed to be started
* @throws Exception if the strategy module could not be started or another error occurs
*/
private ModuleURN doOneMXInterfaceTest(String inOutputDestination,
ModuleURN inOutputStart,
String inNewParameters,
Properties inStartingParameters,
boolean inStartingRouteToORS,
boolean inNewRouting)
throws Exception
{
ModuleURN strategy = createStrategy(RubyLanguageTest.STRATEGY_NAME,
RUBY,
RubyLanguageTest.STRATEGY,
inStartingParameters,
inStartingRouteToORS,
inOutputStart);
StrategyMXBean mxBeanInterface = getMXProxy(strategy);
verifyStrategyStatus(strategy,
RUNNING);
if(inOutputStart == null) {
assertNull(mxBeanInterface.getOutputDestination());
} else {
assertEquals(inOutputStart.getValue(),
mxBeanInterface.getOutputDestination());
}
if(inStartingParameters == null ||
inStartingParameters.isEmpty()) {
assertNull(mxBeanInterface.getParameters());
} else {
String propertiesString = mxBeanInterface.getParameters();
Properties actualProperties = Util.propertiesFromString(propertiesString);
assertEquals(actualProperties,
inStartingParameters);
}
assertEquals(inStartingRouteToORS,
mxBeanInterface.isRoutingOrdersToORS());
assertEquals(RubyLanguageTest.STRATEGY_NAME,
mxBeanInterface.getName());
assertEquals(RUBY, mxBeanInterface.getLanguage());
// make the change
mxBeanInterface.setOutputDestination(inOutputDestination);
mxBeanInterface.setParameters(inNewParameters);
mxBeanInterface.setRoutingOrdersToORS(inNewRouting);
// test the change
if(inOutputDestination == null ||
inOutputDestination.isEmpty()) {
assertNull(mxBeanInterface.getOutputDestination());
} else {
assertEquals(inOutputDestination,
mxBeanInterface.getOutputDestination());
}
if(inNewParameters == null ||
inNewParameters.isEmpty()) {
assertNull(mxBeanInterface.getParameters());
} else {
String propertiesString = mxBeanInterface.getParameters();
Properties actualProperties = Util.propertiesFromString(propertiesString);
Properties expectedProperties = Util.propertiesFromString(inNewParameters);
assertEquals(actualProperties,
expectedProperties);
}
assertEquals(inNewRouting,
mxBeanInterface.isRoutingOrdersToORS());
// cycle the module
stopStrategy(strategy);
verifyStrategyStatus(strategy,
STOPPED);
startStrategy(strategy);
verifyStrategyStatus(strategy,
RUNNING);
return strategy;
}
/**
* Tries to create a strategy module with the given set of parameters.
*
* <p>It is expected that one of the strategies is of the wrong type.
*
* @param inParameters an <code>int</code> value indicating the index of the first bad parameter
* @throws Exception if an error occurs
*/
private void doWrongTypeParameterTest(int badParameter,
final Object...inParameters)
throws Exception
{
// special case for parameter 3 (2 by 0-indexed counting, of course), language - the module factory framework cannot check for us
if(badParameter != 2) {
new ExpectedFailure<ModuleCreationException>(CANNOT_CREATE_MODULE_WRONG_PARAM_TYPE,
StrategyModuleFactory.PROVIDER_URN.toString(),
badParameter,
expectedTypes[badParameter].getName(),
inParameters[badParameter].getClass().getName()) {
@Override
protected void run()
throws Exception
{
moduleManager.createModule(StrategyModuleFactory.PROVIDER_URN,
inParameters);
}
};
}
new ExpectedFailure<ModuleCreationException>(PARAMETER_TYPE_ERROR,
badParameter + 1,
expectedTypes[badParameter].getName(),
inParameters[badParameter].getClass().getName()) {
@Override
protected void run()
throws Exception
{
factory.create(inParameters);
}
};
}
/**
* Tests that an incorrect parameter count is properly handled.
*
* @param inParameters an <code>Object...</code> value
* @throws Exception if an error occurs
*/
private void doWrongParameterCountTest(final Object...inParameters)
throws Exception
{
if(inParameters != null) {
assertFalse("This test is supposed to test an incorrect number of parameters",
inParameters.length == 7);
}
new ExpectedFailure<ModuleCreationException>(CANNOT_CREATE_MODULE_WRONG_PARAM_NUM,
StrategyModuleFactory.PROVIDER_URN.toString(),
7,
(inParameters == null) ? 0 : inParameters.length) {
@Override
protected void run()
throws Exception
{
moduleManager.createModule(StrategyModuleFactory.PROVIDER_URN,
inParameters);
}
};
new ExpectedFailure<ModuleCreationException>(PARAMETER_COUNT_ERROR) {
@Override
protected void run()
throws Exception
{
factory.create(inParameters);
}
};
}
/**
* should match the signature of {@link StrategyModule#StrategyModule(ModuleURN, String, Language, File, Properties, boolean, ModuleURN)}.
*/
private static final Class<?>[] expectedTypes = new Class<?>[] { String.class, String.class, Language.class, File.class, Properties.class, Boolean.class, ModuleURN.class };
}