package org.marketcetera.marketdata.bogus;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.marketcetera.marketdata.Capability.DIVIDEND;
import static org.marketcetera.marketdata.Capability.EVENT_BOUNDARY;
import static org.marketcetera.marketdata.Capability.LATEST_TICK;
import static org.marketcetera.marketdata.Capability.LEVEL_2;
import static org.marketcetera.marketdata.Capability.MARKET_STAT;
import static org.marketcetera.marketdata.Capability.OPEN_BOOK;
import static org.marketcetera.marketdata.Capability.TOP_OF_BOOK;
import static org.marketcetera.marketdata.Capability.TOTAL_VIEW;
import static org.marketcetera.marketdata.Capability.UNKNOWN;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import org.junit.Test;
import org.marketcetera.event.HasInstrument;
import org.marketcetera.marketdata.Capability;
import org.marketcetera.marketdata.Content;
import org.marketcetera.marketdata.MarketDataFeedTestBase;
import org.marketcetera.marketdata.MarketDataModuleTestBase;
import org.marketcetera.marketdata.MarketDataRequestBuilder;
import org.marketcetera.module.DataFlowID;
import org.marketcetera.module.DataRequest;
import org.marketcetera.module.ModuleException;
import org.marketcetera.module.ModuleFactory;
import org.marketcetera.module.ModuleURN;
import org.marketcetera.module.SinkDataListener;
/* $License$ */
/**
* Tests {@link BogusFeedModule}.
*
* @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a>
* @version $Id: BogusFeedModuleTest.java 16912 2014-05-16 23:35:10Z colin $
* @since 1.0.0
*/
public class BogusFeedModuleTest
extends MarketDataModuleTestBase
{
/**
* Tests a deadlock scenario that occurred for Bogus feed.
*
* @throws Exception if an error occurs
*/
@Test
public void deadlock()
throws Exception
{
final Set<String> symbols = Collections.synchronizedSet(new HashSet<String>());
moduleManager.addSinkListener(new SinkDataListener() {
@Override
public void receivedData(DataFlowID inFlowID,
Object inData) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
if(inData instanceof HasInstrument) {
symbols.add(((HasInstrument)inData).getInstrument().getSymbol());
}
}
});
assertTrue(symbols.isEmpty());
DataFlowID id1 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(getInstanceURN(),
MarketDataRequestBuilder.newRequest().withSymbols("IBM").create()) });
Thread.sleep(2000);
DataFlowID id2 = moduleManager.createDataFlow(new DataRequest[] { new DataRequest(getInstanceURN(),
MarketDataRequestBuilder.newRequest().withSymbols("GOOG").withContent(Content.MARKET_STAT).create()) });
Thread.sleep(5000);
moduleManager.cancel(id1);
moduleManager.cancel(id2);
assertTrue(symbols.contains("IBM"));
assertTrue(symbols.contains("GOOG"));
}
/**
* Tests another deadlock that shows up with Bogus.
*
* @throws Exception if an error occurs
*/
@Test(timeout=15000)
public void yetAnotherDeadlock()
throws Exception
{
final List<DataFlowID> createdFlows = new ArrayList<DataFlowID>();
try {
final List<Exception> caughtExceptions = new ArrayList<Exception>();
final List<Object> receivedData = new ArrayList<Object>();
// use this to guarantee that only about 1 or so additional data flows will be requested (I say or so because the flag isn't synchronized, but that's ok)
final boolean[] dataRequested = new boolean[] { false };
assertTrue(caughtExceptions.isEmpty());
assertTrue(receivedData.isEmpty());
moduleManager.addSinkListener(new SinkDataListener() {
@Override
public void receivedData(DataFlowID inFlowID,
Object inData)
{
try {
if(!dataRequested[0]) {
// this call will hang if the deadlock has not been repaired
createdFlows.add(moduleManager.createDataFlow(new DataRequest[] { new DataRequest(BogusFeedModuleFactory.INSTANCE_URN,
MarketDataRequestBuilder.newRequest().withSymbols("GOOG").create()) }));
dataRequested[0] = true;
}
receivedData.add(inData);
} catch (ModuleException e) {
caughtExceptions.add(e);
}
}
});
createdFlows.add(moduleManager.createDataFlow(new DataRequest[] { new DataRequest(BogusFeedModuleFactory.INSTANCE_URN,
MarketDataRequestBuilder.newRequest().withSymbols("IBM").create()) }));
// need to infinite loop here since the deadlock is between the market data delivery thread and the new ExecutorThread
MarketDataFeedTestBase.wait(new Callable<Boolean>() {
@Override
public Boolean call()
throws Exception
{
return !receivedData.isEmpty();
}
});
assertTrue(caughtExceptions.isEmpty());
assertFalse(receivedData.isEmpty());
} finally {
for(DataFlowID id : createdFlows) {
moduleManager.cancel(id);
}
}
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.MarketDataModuleTestBase#getFactory()
*/
@Override
protected ModuleFactory getFactory()
{
return new BogusFeedModuleFactory();
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.MarketDataModuleTestBase#getInstanceURN()
*/
@Override
protected ModuleURN getInstanceURN()
{
return BogusFeedModuleFactory.INSTANCE_URN;
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.MarketDataModuleTestBase#getExpectedCapabilities()
*/
@Override
protected Capability[] getExpectedCapabilities()
{
return new Capability[] { TOP_OF_BOOK,LEVEL_2,OPEN_BOOK,TOTAL_VIEW,LATEST_TICK,MARKET_STAT,DIVIDEND,EVENT_BOUNDARY };
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.MarketDataModuleTestBase#getUnexpectedCapability()
*/
@Override
protected Capability getUnexpectedCapability()
{
return UNKNOWN;
}
/* (non-Javadoc)
* @see org.marketcetera.marketdata.MarketDataModuleTestBase#getProvider()
*/
@Override
protected String getProvider()
{
return BogusFeedModuleFactory.IDENTIFIER;
}
}