package org.marketcetera.algo; import static org.junit.Assert.*; import java.io.*; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import javax.xml.bind.*; import org.junit.BeforeClass; import org.junit.Test; import org.marketcetera.core.CoreException; import org.marketcetera.core.LoggerConfiguration; import org.marketcetera.core.Validator; import org.marketcetera.module.ExpectedFailure; import org.marketcetera.trade.Factory; import org.marketcetera.trade.NewOrReplaceOrder; import org.marketcetera.trade.OrderSingle; import org.marketcetera.util.test.EqualityAssert; /* $License$ */ /** * Tests broker algorithm classes. * * @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a> * @version $Id: BrokerAlgoTest.java 16901 2014-05-11 16:14:11Z colin $ * @since 2.4.0 */ public class BrokerAlgoTest { /** * Runs once before all tests. * * @throws Exception if an unexpected error occurs */ @BeforeClass public static void once() throws Exception { LoggerConfiguration.logSetup(); context = JAXBContext.newInstance(BrokerAlgoTagSpec.class,BrokerAlgoTag.class,BrokerAlgo.class,BrokerAlgoSpec.class); marshaller = context.createMarshaller(); unmarshaller = context.createUnmarshaller(); unmarshaller.setEventHandler(new ValidationEventHandler() { @Override public boolean handleEvent(ValidationEvent inEvent) { throw new RuntimeException(inEvent.getMessage(), inEvent.getLinkedException()); } }); } /** * Tests serialization/unserialization and marshalling/unmarshalling. * * @throws Exception if an unexpected error occurs */ @Test public void testSerializeAndMarshal() throws Exception { // generate a test algo BrokerAlgoSpec testSpec = generateAlgoSpec(); // bind the test spec with values Set<BrokerAlgoTag> boundTags = generateTagsFor(testSpec); BrokerAlgo boundAlgo = new BrokerAlgo(testSpec, boundTags); ByteArrayOutputStream output = new ByteArrayOutputStream(); ObjectOutputStream testStream = new ObjectOutputStream(output); // serialize the algo testStream.writeObject(boundAlgo); // unserialize and compare ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray()); ObjectInputStream inputStream = new ObjectInputStream(input); BrokerAlgo algoCopy = (BrokerAlgo)inputStream.readObject(); assertEquals(boundAlgo, algoCopy); // check validators assertNotNull(boundAlgo.getAlgoSpec().getValidator()); assertNull(algoCopy.getAlgoSpec().getValidator()); for(BrokerAlgoTagSpec tagSpec : boundAlgo.getAlgoSpec().getAlgoTagSpecs()) { assertNotNull(tagSpec.getValidator()); } for(BrokerAlgoTagSpec tagSpec : algoCopy.getAlgoSpec().getAlgoTagSpecs()) { assertNull(tagSpec.getValidator()); } // marshal StringWriter outputWriter = new StringWriter(); marshaller.marshal(boundAlgo, outputWriter); // unmarshal and compare algoCopy = (BrokerAlgo)unmarshaller.unmarshal(new InputStreamReader(new ByteArrayInputStream(outputWriter.toString().getBytes()))); assertEquals(boundAlgo, algoCopy); } /** * Tests getters and setters. * * @throws Exception if an unexpected error occurs */ @Test public void testTagSpecGettersAndSetters() throws Exception { final BrokerAlgoTagSpec testSpec = new BrokerAlgoTagSpec(); verifyTagSpec(0, null, null, null, false, null, null, testSpec); testSpec.setTag(Integer.MIN_VALUE); verifyTagSpec(Integer.MIN_VALUE, null, null, null, false, null, null, testSpec); testSpec.setTag(Integer.MAX_VALUE); verifyTagSpec(Integer.MAX_VALUE, null, null, null, false, null, null, testSpec); testSpec.setDescription(null); verifyTagSpec(Integer.MAX_VALUE, null, null, null, false, null, null, testSpec); testSpec.setDescription(""); verifyTagSpec(Integer.MAX_VALUE, null, null, null, false, null, null, testSpec); testSpec.setDescription(" "); verifyTagSpec(Integer.MAX_VALUE, null, null, null, false, null, null, testSpec); testSpec.setDescription(" description "); verifyTagSpec(Integer.MAX_VALUE, null, "description", null, false, null, null, testSpec); testSpec.setIsMandatory(true); verifyTagSpec(Integer.MAX_VALUE, null, "description", null, true, null, null, testSpec); testSpec.setPattern(null); verifyTagSpec(Integer.MAX_VALUE, null, "description", null, true, null, null, testSpec); testSpec.setPattern(""); verifyTagSpec(Integer.MAX_VALUE, null, "description", null, true, null, null, testSpec); testSpec.setPattern(" "); verifyTagSpec(Integer.MAX_VALUE, null, "description", null, true, null, null, testSpec); new ExpectedFailure<IllegalArgumentException>() { @Override protected void run() throws Exception { testSpec.setPattern("(not a valid pattern"); } }; testSpec.setPattern(" \\d* "); verifyTagSpec(Integer.MAX_VALUE, null, "description", "\\d*", true, null, null, testSpec); testSpec.setValidator(null); verifyTagSpec(Integer.MAX_VALUE, null, "description", "\\d*", true, null, null, testSpec); testSpec.setValidator(tagValidator); verifyTagSpec(Integer.MAX_VALUE, null, "description", "\\d*", true, null, tagValidator, testSpec); testSpec.setLabel(null); verifyTagSpec(Integer.MAX_VALUE, null, "description", "\\d*", true, null, tagValidator, testSpec); testSpec.setLabel(""); verifyTagSpec(Integer.MAX_VALUE, null, "description", "\\d*", true, null, tagValidator, testSpec); testSpec.setLabel(" "); verifyTagSpec(Integer.MAX_VALUE, null, "description", "\\d*", true, null, tagValidator, testSpec); testSpec.setLabel(" label "); verifyTagSpec(Integer.MAX_VALUE, "label", "description", "\\d*", true, null, tagValidator, testSpec); testSpec.setOptions(null); verifyTagSpec(Integer.MAX_VALUE, "label", "description", "\\d*", true, null, tagValidator, testSpec); Map<String,String> options = new HashMap<String,String>(); for(int i=0;i<3;i++) { options.put("Option " + counter.incrementAndGet(), "value-" + counter.incrementAndGet()); } testSpec.setOptions(options); verifyTagSpec(Integer.MAX_VALUE, "label", "description", "\\d*", true, options, tagValidator, testSpec); } /** * Tests <code>BrokerAlgoTagSpec</code> hashcode and equals. * * @throws Exception if an unexpected error occurs */ @Test public void testTagSpecHashcodeAndEquals() throws Exception { BrokerAlgoTagSpec testSpec = generateTagSpec(); BrokerAlgoTagSpec otherSpec = generateTagSpec(); otherSpec.setTag(testSpec.getTag()); otherSpec.setIsMandatory(!testSpec.getIsMandatory()); otherSpec.setValidator(null); otherSpec.setPattern(null); assertNotNull(testSpec.getValidator()); assertFalse(testSpec.getDescription().equals(otherSpec.getDescription())); assertFalse(testSpec.getPattern().equals(otherSpec.getPattern())); assertFalse(testSpec.getIsMandatory() == otherSpec.getIsMandatory()); assertFalse(testSpec.getValidator().equals(otherSpec.getValidator())); EqualityAssert.assertEquality(testSpec, otherSpec, null, this); } /** * Tests comparable behavior of <code>BrokerAlgoTagSpec</code>. * * @throws Exception if an unexpected error occurs */ @Test public void testTagSpecComparable() throws Exception { BrokerAlgoTagSpec testSpec = generateTagSpec(); BrokerAlgoTagSpec otherSpec = generateTagSpec(); assertTrue(testSpec.getTag() < otherSpec.getTag()); assertEquals(-1, testSpec.compareTo(otherSpec)); assertEquals(1, otherSpec.compareTo(testSpec)); otherSpec.setTag(testSpec.getTag()); assertEquals(0, otherSpec.compareTo(testSpec)); } /** * Tests constructors for <code>BrokerAlgoTag</code>. * * @throws Exception if an unexpected error occurs */ @Test public void testTagConstructors() throws Exception { BrokerAlgoTag testObject = new BrokerAlgoTag(); verifyTag(null, null, testObject); BrokerAlgoTagSpec testSpec = generateTagSpec(); testObject = new BrokerAlgoTag(testSpec); verifyTag(testSpec, null, testObject); testObject = new BrokerAlgoTag(testSpec, "value"); verifyTag(testSpec, "value", testObject); } /** * Tests <code>BrokerAlgoTag</code> getters and setters. * * @throws Exception if an unexpected error occurs */ @Test public void testTagGettersAndSetters() throws Exception { BrokerAlgoTag testObject = new BrokerAlgoTag(); verifyTag(null, null, testObject); BrokerAlgoTagSpec testSpec = generateTagSpec(); testObject.setTagSpec(testSpec); verifyTag(testSpec, null, testObject); testObject.setTagSpec(null); verifyTag(null, null, testObject); testObject.setValue(""); verifyTag(null, null, testObject); testObject.setValue(" "); verifyTag(null, null, testObject); testObject.setValue(" value "); verifyTag(null, "value", testObject); } /** * Tests <code>BrokerAlgoTag</code> equals and hashcode. * * @throws Exception if an unexpected error occurs */ @Test public void testTagEquality() throws Exception { BrokerAlgoTagSpec testSpec1 = generateTagSpec(); BrokerAlgoTagSpec testSpec2 = generateTagSpec(); assertFalse(testSpec1.equals(testSpec2)); BrokerAlgoTag testTag = new BrokerAlgoTag(testSpec1, "value"); BrokerAlgoTag equalTag = new BrokerAlgoTag(testSpec1, "value"); BrokerAlgoTag unequalTag1 = new BrokerAlgoTag(); BrokerAlgoTag unequalTag2 = new BrokerAlgoTag(testSpec1); BrokerAlgoTag unequalTag3 = new BrokerAlgoTag(testSpec1, "othervalue"); BrokerAlgoTag unequalTag4 = new BrokerAlgoTag(testSpec1, null); BrokerAlgoTag unequalTag5 = new BrokerAlgoTag(testSpec2, "value"); EqualityAssert.assertEquality(testTag, equalTag, null, this, unequalTag1, unequalTag2, unequalTag3, unequalTag4, unequalTag5); } /** * Tests <code>BrokerAlgoTag</code> validation. * * @throws Exception if an unexpected error occurs */ @Test public void testTagValidate() throws Exception { BrokerAlgoTagSpec testSpec = generateTagSpec(); testSpec.setValidator(null); testSpec.setPattern(null); testSpec.setIsMandatory(false); testSpec.setLabel("some label"); final BrokerAlgoTag testTag = new BrokerAlgoTag(testSpec); assertNull(testTag.getValue()); testTag.validate(); testSpec.setIsMandatory(true); new ExpectedFailure<CoreException>(Messages.ALGO_TAG_VALUE_REQUIRED, testSpec.getLabel()) { @Override protected void run() throws Exception { testTag.validate(); } }; testTag.setValue("value"); testTag.validate(); testSpec.setPattern("\\d*"); new ExpectedFailure<CoreException>(Messages.ALGO_TAG_VALUE_PATTERN_MISMATCH, testSpec.getLabel(), testTag.getValue()) { @Override protected void run() throws Exception { testTag.validate(); } }; testSpec.setPattern("\\w*"); testTag.validate(); testSpec.setValidator(tagValidator); testTag.validate(); testSpec.setValidator(new Validator<BrokerAlgoTag>() { @Override public void validate(BrokerAlgoTag inData) { throw new CoreException(); } }); new ExpectedFailure<CoreException>() { @Override protected void run() throws Exception { testTag.validate(); } }; } /** * Tests getters and setters of <code>BrokerAlgoSpec</code>. * * @throws Exception if an unexpected error occurs */ @Test public void testAlgoSpecGettersAndSetters() throws Exception { BrokerAlgoSpec testSpec = new BrokerAlgoSpec(); verifyAlgoSpec(null, null, null, testSpec); testSpec.setName(null); verifyAlgoSpec(null, null, null, testSpec); testSpec.setName(""); verifyAlgoSpec(null, null, null, testSpec); testSpec.setName(" "); verifyAlgoSpec(null, null, null, testSpec); testSpec.setName(" name "); verifyAlgoSpec("name", null, null, testSpec); BrokerAlgoTagSpec spec1 = generateTagSpec(); BrokerAlgoTagSpec spec2 = generateTagSpec(); BrokerAlgoTagSpec spec3 = generateTagSpec(); Set<BrokerAlgoTagSpec> specs = new HashSet<BrokerAlgoTagSpec>(); specs.add(spec1); specs.add(spec2); specs.add(spec3); testSpec.setAlgoTagSpecs(specs); verifyAlgoSpec("name", specs, null, testSpec); testSpec.setValidator(null); verifyAlgoSpec("name", specs, null, testSpec); testSpec.setValidator(algoValidator); verifyAlgoSpec("name", specs, algoValidator, testSpec); } /** * Tests <code>BrokerAlgoSpec</code> equals and hashcode. * * @throws Exception if an unexpected error occurs */ @Test public void testAlgoSpecEquality() throws Exception { BrokerAlgoSpec testSpec = generateAlgoSpec(); BrokerAlgoSpec equalSpec = generateAlgoSpec(); assertFalse(testSpec.getAlgoTagSpecs().equals(equalSpec.getAlgoTagSpecs())); equalSpec.setName(testSpec.getName()); EqualityAssert.assertEquality(testSpec, equalSpec, null, this); } /** * Tests <code>BrokerAlgoSpec</code> comparable behavior. * * @throws Exception if an unexpected error occurs */ @Test public void testAlgoComparable() throws Exception { BrokerAlgoSpec testSpec = generateAlgoSpec(); BrokerAlgoSpec otherSpec = generateAlgoSpec(); assertFalse(testSpec.getName().equals(otherSpec.getName())); assertEquals(testSpec.getName().compareTo(otherSpec.getName()), testSpec.compareTo(otherSpec)); assertEquals(otherSpec.getName().compareTo(testSpec.getName()), otherSpec.compareTo(testSpec)); assertEquals(0, testSpec.compareTo(testSpec)); } /** * Tests <code>BrokerAlgo</code> constructors. * * @throws Exception if an unexpected error occurs */ @Test public void testAlgoConstructors() throws Exception { BrokerAlgo testAlgo = new BrokerAlgo(); verifyAlgo(null, null, testAlgo); BrokerAlgoSpec algoSpec = generateAlgoSpec(); testAlgo = new BrokerAlgo(algoSpec); verifyAlgo(algoSpec, null, testAlgo); Set<BrokerAlgoTag> boundTags = generateTagsFor(algoSpec); testAlgo = new BrokerAlgo(algoSpec, boundTags); verifyAlgo(algoSpec, boundTags, testAlgo); } /** * Tests getters and setters for <code>BrokerAlgo</code>. * * @throws Exception if an unexpected error occurs */ @Test public void testAlgoGettersAndSetters() throws Exception { BrokerAlgo testAlgo = new BrokerAlgo(); verifyAlgo(null, null, testAlgo); BrokerAlgoSpec algoSpec = generateAlgoSpec(); testAlgo.setAlgoSpec(algoSpec); verifyAlgo(algoSpec, null, testAlgo); Set<BrokerAlgoTag> boundTags = generateTagsFor(algoSpec); testAlgo.setAlgoTags(boundTags); verifyAlgo(algoSpec, boundTags, testAlgo); } /** * Tests <code>BrokerAlgo</code> hashcode and equals. * * @throws Exception if an unexpected error occurs */ @Test public void testAlgoEquality() throws Exception { BrokerAlgoSpec testSpec1 = generateAlgoSpec(); BrokerAlgoSpec testSpec2 = generateAlgoSpec(); Set<BrokerAlgoTag> boundTags1 = generateTagsFor(testSpec1); Set<BrokerAlgoTag> boundTags2 = generateTagsFor(testSpec2); assertFalse(testSpec1.equals(testSpec2)); BrokerAlgo testAlgo = new BrokerAlgo(testSpec1, boundTags1); BrokerAlgo equalAlgo = new BrokerAlgo(testSpec1, boundTags1); BrokerAlgo unequalAlgo1 = new BrokerAlgo(); BrokerAlgo unequalAlgo2 = new BrokerAlgo(testSpec1); BrokerAlgo unequalAlgo3 = new BrokerAlgo(testSpec1, boundTags2); BrokerAlgo unequalAlgo4 = new BrokerAlgo(testSpec2, boundTags1); EqualityAssert.assertEquality(testAlgo, equalAlgo, null, this, unequalAlgo1, unequalAlgo2, unequalAlgo3, unequalAlgo4); } /** * Tests {@link BrokerAlgo#applyTo(org.marketcetera.trade.NewOrReplaceOrder)} * * @throws Exception if an unexpected error occurs */ @Test public void testAlgoApplyToOrder() throws Exception { BrokerAlgoSpec testSpec = generateAlgoSpec(); final BrokerAlgo testAlgo = new BrokerAlgo(); testAlgo.applyTo(null); // nothing happens Set<BrokerAlgoTag> boundTags = generateTagsFor(testSpec); testAlgo.setAlgoSpec(testSpec); testAlgo.applyTo(null); // still, nothing happens testAlgo.setAlgoTags(boundTags); // now, with bound tags, bad things happen with a null order new ExpectedFailure<NullPointerException>() { @Override protected void run() throws Exception { testAlgo.applyTo(null); } }; // test with empty but non-null bound tags testAlgo.setAlgoTags(new HashSet<BrokerAlgoTag>()); doOneApplyTest(testAlgo, Factory.getInstance().createOrderSingle()); // test with non-empty bound tags testAlgo.setAlgoTags(boundTags); doOneApplyTest(testAlgo, Factory.getInstance().createOrderSingle()); // repeat with replace order doOneApplyTest(testAlgo, Factory.getInstance().createOrderReplace(null)); // create an order with pre-existing custom tags, make sure these are retained OrderSingle order = Factory.getInstance().createOrderSingle(); Map<String,String> extraTags = new HashMap<String,String>(); // make sure there is no overlap between my tags and the algo tags extraTags.put(String.valueOf(counter.incrementAndGet()), "custom-value-" + counter.incrementAndGet()); extraTags.put(String.valueOf(counter.incrementAndGet()), "custom-value-" + counter.incrementAndGet()); order.setCustomFields(extraTags); doOneApplyTest(testAlgo, order); // run again, this time make sure there *is* overlap extraTags.put(String.valueOf(boundTags.iterator().next().getTagSpec().getTag()), "some-new-value-" + counter.incrementAndGet()); order.setCustomFields(extraTags); doOneApplyTest(testAlgo, order); } /** * Tests {@link BrokerAlgo#mapValidatorsFrom(BrokerAlgoSpec)}. * * @throws Exception if an unexpected error occurs */ @Test public void testBrokerAlgoValidatorMapping() throws Exception { BrokerAlgoSpec testAlgoSpec = generateAlgoSpec(); assertNotNull(testAlgoSpec.getValidator()); for(BrokerAlgoTagSpec tagSpec : testAlgoSpec.getAlgoTagSpecs()) { assertNotNull(tagSpec.getValidator()); } // the algo spec above has a non-null validator and each of its tag specs has a non-null validator // now, generate an algo based on this spec Set<BrokerAlgoTag> boundTags = generateTagsFor(testAlgoSpec); BrokerAlgo testAlgo = new BrokerAlgo(testAlgoSpec, boundTags); // at this time, this algo also has non-null validators, so mapping it isn't necessary // run it through a marshal/unmarshal cycle, which will strip its validators StringWriter outputWriter = new StringWriter(); marshaller.marshal(testAlgo, outputWriter); // unmarshal final BrokerAlgo strippedAlgo = (BrokerAlgo)unmarshaller.unmarshal(new InputStreamReader(new ByteArrayInputStream(outputWriter.toString().getBytes()))); // verify that the validators are gone assertNull(strippedAlgo.getAlgoSpec().getValidator()); for(BrokerAlgoTag tag : strippedAlgo.getAlgoTags()) { assertNull(tag.getTagSpec().getValidator()); } // first, map the wrong spec final BrokerAlgoSpec wrongAlgoSpec = generateAlgoSpec(); assertFalse(testAlgoSpec.equals(wrongAlgoSpec)); new ExpectedFailure<CoreException>(Messages.ALGO_SPEC_MISMATCH, testAlgoSpec.getName(), wrongAlgoSpec.getName()) { @Override protected void run() throws Exception { strippedAlgo.mapValidatorsFrom(wrongAlgoSpec); } }; assertNull(strippedAlgo.getAlgoSpec().getValidator()); for(BrokerAlgoTag tag : strippedAlgo.getAlgoTags()) { assertNull(tag.getTagSpec().getValidator()); } // does nothing if the algo spec is null strippedAlgo.setAlgoSpec(null); strippedAlgo.mapValidatorsFrom(testAlgoSpec); for(BrokerAlgoTag tag : strippedAlgo.getAlgoTags()) { assertNull(tag.getTagSpec().getValidator()); } // put the algo spec back strippedAlgo.setAlgoSpec(testAlgoSpec); // remove the algo tags Set<BrokerAlgoTag> unmappedBoundTags = strippedAlgo.getAlgoTags(); strippedAlgo.setAlgoTags(null); // map again strippedAlgo.mapValidatorsFrom(testAlgoSpec); // top-level validator is there, but no validators for the (non-existent) tag specs assertSame(testAlgoSpec.getValidator(), strippedAlgo.getAlgoSpec().getValidator()); assertNull(strippedAlgo.getAlgoTags()); // put the tags back strippedAlgo.setAlgoTags(unmappedBoundTags); // reset for the next test strippedAlgo.getAlgoSpec().setValidator(null); // remove the spec tags Set<BrokerAlgoTagSpec> tagSpecs = testAlgoSpec.getAlgoTagSpecs(); testAlgoSpec.setAlgoTagSpecs(null); // map again strippedAlgo.mapValidatorsFrom(testAlgoSpec); // top-level validator is there, no validators for the tag specs assertSame(testAlgoSpec.getValidator(), strippedAlgo.getAlgoSpec().getValidator()); for(BrokerAlgoTag tag : strippedAlgo.getAlgoTags()) { assertNull(tag.getTagSpec().getValidator()); } // put the tag specs back testAlgoSpec.setAlgoTagSpecs(tagSpecs); // reset for the next test strippedAlgo.getAlgoSpec().setValidator(null); // set a single tag spec validator to null for completeness testAlgoSpec.getAlgoTagSpecs().iterator().next().setValidator(null); // do the successful, complete mapping strippedAlgo.mapValidatorsFrom(testAlgoSpec); verifyAlgo(testAlgoSpec, boundTags, strippedAlgo); } /** * Tests {@link BrokerAlgo#validate()}. * * @throws Exception if an unexpected error occurs */ @Test public void testBrokerAlgoValidation() throws Exception { final BrokerAlgo testAlgo = new BrokerAlgo(); assertNull(testAlgo.getAlgoSpec()); assertNull(testAlgo.getAlgoTags()); // validate with no spec or tags (so, no validators) testAlgo.validate(); BrokerAlgoSpec testAlgoSpec = generateAlgoSpec(); Set<BrokerAlgoTag> boundTags = generateTagsFor(testAlgoSpec); testAlgo.setAlgoTags(boundTags); assertNotNull(testAlgoSpec.getValidator()); testAlgo.setAlgoSpec(testAlgoSpec); // validate with top-level no-op validator and no-op validators for each tag spec testAlgo.validate(); // validate with null top-level validator and no-op validators for each tag spec testAlgoSpec.setValidator(null); testAlgo.validate(); // set the top-level validator to fail testAlgoSpec.setValidator(new Validator<BrokerAlgo>() { @Override public void validate(BrokerAlgo inData) { throw new RuntimeException("this exception is expected"); }}); new ExpectedFailure<RuntimeException>() { @Override protected void run() throws Exception { testAlgo.validate(); } }; testAlgoSpec.setValidator(null); // verify each validator gets called final AtomicBoolean topLevelValidatorCalled = new AtomicBoolean(false); AtomicBoolean[] tagValidatorsCalled = new AtomicBoolean[testAlgoSpec.getAlgoTagSpecs().size()]; for(int i=0;i<tagValidatorsCalled.length;i++) { tagValidatorsCalled[i] = new AtomicBoolean(false); } testAlgoSpec.setValidator(new Validator<BrokerAlgo>() { @Override public void validate(BrokerAlgo inData) { if(inData == null) { return; } topLevelValidatorCalled.set(true); } }); int counter = 0; for(BrokerAlgoTagSpec tagSpec : testAlgoSpec.getAlgoTagSpecs()) { final AtomicBoolean flag = tagValidatorsCalled[counter++]; tagSpec.setValidator(new Validator<BrokerAlgoTag>() { @Override public void validate(BrokerAlgoTag inData) { if(inData == null) { return; } flag.set(true); } }); } testAlgo.validate(); assertTrue(topLevelValidatorCalled.get()); counter = 0; for(AtomicBoolean flag : tagValidatorsCalled) { assertTrue(flag.get()); } // set one tag validator to fail testAlgoSpec.getAlgoTagSpecs().iterator().next().setValidator(new Validator<BrokerAlgoTag>() { @Override public void validate(BrokerAlgoTag inData) { throw new RuntimeException("this exception is expected"); } }); new ExpectedFailure<RuntimeException>() { @Override protected void run() throws Exception { testAlgo.validate(); } }; } /** * Executes a single iteration of a test for {@link BrokerAlgo#applyTo(NewOrReplaceOrder)}. * * @param inTestAlgo a <code>BrokerAlgo</code> value * @param inOrder a <code>NewOrReplaceOrder</code> value */ private void doOneApplyTest(BrokerAlgo inTestAlgo, NewOrReplaceOrder inOrder) { Map<String,String> customFields = inOrder.getCustomFields(); inTestAlgo.applyTo(inOrder); Map<String,String> overlappingTags = new HashMap<String,String>(); for(BrokerAlgoTag tag : inTestAlgo.getAlgoTags()) { assertEquals(inOrder.getCustomFields().get(String.valueOf(tag.getTagSpec().getTag())), tag.getValue()); if(customFields != null && customFields.containsKey(String.valueOf(tag.getTagSpec().getTag()))) { overlappingTags.put(String.valueOf(tag.getTagSpec().getTag()), tag.getValue()); } } if(customFields != null) { // make sure every pre-existing tag is still there for(Map.Entry<String,String> entry : customFields.entrySet()) { assertTrue(entry.getKey() + " missing from " + inOrder.getCustomFields(), inOrder.getCustomFields().containsKey(entry.getKey())); } } // if there was overlap, make sure that the algo tag takes precedence for(Map.Entry<String,String> overlappingTag : overlappingTags.entrySet()) { assertEquals(inOrder.getCustomFields().get(overlappingTag.getKey()), overlappingTag.getValue()); } } /** * Generates bound broker algo tags for the given algo spec. * * @param inAlgoSpec a <code>BrokerAlgoSpec</code> value * @return a <code>Set<BrokerAlgoTag></code> value */ private Set<BrokerAlgoTag> generateTagsFor(BrokerAlgoSpec inAlgoSpec) { if(inAlgoSpec == null || inAlgoSpec.getAlgoTagSpecs() == null) { return null; } Set<BrokerAlgoTag> tags = new HashSet<BrokerAlgoTag>(); for(BrokerAlgoTagSpec tagSpec : inAlgoSpec.getAlgoTagSpecs()) { String value; if(tagSpec.getOptions() != null && !tagSpec.getOptions().isEmpty()) { value = tagSpec.getOptions().entrySet().iterator().next().getValue(); } else { value = "value-" + counter.incrementAndGet(); } BrokerAlgoTag tag = new BrokerAlgoTag(tagSpec, value); tags.add(tag); } return tags; } /** * Verifies that the given <code>BrokerAlgo</code> matches the given expected attributes. * * @param inExpectedAlgoSpec a <code>BrokerAlgoSpec</code> value * @param inExpectedAlgoTags a <code>Set<BrokerAlgoTag></code> value * @param inActualAlgo a <code>BrokerAlgoTag</code> value * @throws Exception if an unexpected error occurs */ private void verifyAlgo(BrokerAlgoSpec inExpectedAlgoSpec, Set<BrokerAlgoTag> inExpectedAlgoTags, BrokerAlgo inActualAlgo) throws Exception { assertNotNull(inActualAlgo.toString()); assertEquals(inExpectedAlgoSpec, inActualAlgo.getAlgoSpec()); assertEquals(inExpectedAlgoTags, inActualAlgo.getAlgoTags()); if(inExpectedAlgoSpec != null) { verifyAlgoSpec(inExpectedAlgoSpec.getName(), inExpectedAlgoSpec.getAlgoTagSpecs(), inExpectedAlgoSpec.getValidator(), inActualAlgo.getAlgoSpec()); } } /** * Verifies that the given <code>BrokerAlgoSpec</code> matches the given expected attributes. * * @param inExpectedName a <code>String</code> value * @param inExpectedTagSpecs a <code>Set<BrokerAlgoTagSpec></code> value * @param inExpectedValidator a <code>Validator<BrokerAlgo></code> value * @param inActualSpec a <code>BrokerAlgoSpec</code> value * @throws Exception if an unexpected error occurs */ private void verifyAlgoSpec(String inExpectedName, Set<BrokerAlgoTagSpec> inExpectedTagSpecs, Validator<BrokerAlgo> inExpectedValidator, BrokerAlgoSpec inActualSpec) throws Exception { assertNotNull(inActualSpec.toString()); assertEquals(inExpectedName, inActualSpec.getName()); assertEquals(inExpectedTagSpecs, inActualSpec.getAlgoTagSpecs()); assertSame(inExpectedValidator, inActualSpec.getValidator()); } /** * Verifies that the given <code>BrokerAlgoTag</code> matches the given expected attributes. * * @param inExpectedTagSpec a <code>BrokerAlgoTagSpec</code> value * @param inExpectedValue a <code>String</code> value * @param inActualTag a <code>BrokerAlgoTag</code> value * @throws Exception if an unexpected error occurs */ private void verifyTag(BrokerAlgoTagSpec inExpectedTagSpec, String inExpectedValue, BrokerAlgoTag inActualTag) throws Exception { assertNotNull(inActualTag.toString()); assertEquals(inExpectedTagSpec, inActualTag.getTagSpec()); assertEquals(inExpectedValue, inActualTag.getValue()); if(inExpectedTagSpec != null) { verifyTagSpec(inExpectedTagSpec.getTag(), inExpectedTagSpec.getLabel(), inExpectedTagSpec.getDescription(), inExpectedTagSpec.getPattern(), inExpectedTagSpec.getIsMandatory(), inExpectedTagSpec.getOptions(), inExpectedTagSpec.getValidator(), inActualTag.getTagSpec()); } } /** * Verifies that the given <code>BrokerAlgoTagSpec</code> matches the given expected attributes. * * @param inExpectedTag an <code>int</code> value * @param inExpectedLabel a <code>String</code> value * @param inExpectedDescription a <code>String</code> value * @param inExpectedPattern a <code>String</code> value * @param inExpectedMandatory a <code>boolean</code> value * @param inExpectedOptions a <code>Map<String,String></code> value * @param inExpectedValidator a <code>Validator<BrokerAlgoTag></code> value * @param inActualTagSpec a <code>BrokerAlgoTagSpec</code> value * @throws Exception if an unexpected error occurs */ private void verifyTagSpec(int inExpectedTag, String inExpectedLabel, String inExpectedDescription, String inExpectedPattern, boolean inExpectedMandatory, Map<String,String> inExpectedOptions, Validator<BrokerAlgoTag> inExpectedValidator, BrokerAlgoTagSpec inActualTagSpec) throws Exception { assertNotNull(inActualTagSpec.toString()); assertEquals(inExpectedLabel, inActualTagSpec.getLabel()); assertEquals(inExpectedTag, inActualTagSpec.getTag()); assertEquals(inExpectedDescription, inActualTagSpec.getDescription()); assertEquals(inExpectedPattern, inActualTagSpec.getPattern()); assertEquals(inExpectedMandatory, inActualTagSpec.getIsMandatory()); assertEquals(inExpectedOptions, inActualTagSpec.getOptions()); assertSame(inExpectedValidator, inActualTagSpec.getValidator()); } /** * Generates a test <code>BrokerAlgoSpec</code> value. * * @return a <code>BrokerAlgoSpec</code> value */ public static BrokerAlgoSpec generateAlgoSpec() { Set<BrokerAlgoTagSpec> tagSpecs = new HashSet<BrokerAlgoTagSpec>(); Map<String,String> options = new HashMap<String,String>(); for(int i=0;i<3;i++) { tagSpecs.add(generateTagSpec()); options.put("Option " + counter.incrementAndGet(), "value-" + counter.incrementAndGet()); } tagSpecs.add(generateTagSpec(tagValidator, options)); return generateAlgoSpec(tagSpecs.toArray(new BrokerAlgoTagSpec[0])); } /** * Generates a test <code>BrokerAlgoSpec</code> value. * * @param inTagSpecs a <code>BrokerAlgoTagSpec[]</code> value * @return a <code>BrokerAlgoSpec</code> value */ public static BrokerAlgoSpec generateAlgoSpec(BrokerAlgoTagSpec...inTagSpecs) { BrokerAlgoSpec spec = new BrokerAlgoSpec(); spec.setName("Test broker algo-" + counter.incrementAndGet()); if(inTagSpecs != null) { spec.setAlgoTagSpecs(new HashSet<BrokerAlgoTagSpec>(Arrays.asList(inTagSpecs))); } spec.setValidator(algoValidator); return spec; } /** * Generates a <code>BrokerAlgoTestSpec</code> value with default values. * * @return a <code>BrokerAlgoTagSpec</code> value */ public static BrokerAlgoTagSpec generateTagSpec() { return generateTagSpec(tagValidator, null); } /** * Generates a <code>BrokerAlgoTagSpec</code> value with default values and the given validator. * * @param inValidator a <code>Validator<BrokerAlgoTag></code> value * @param inOptions a <code>Map<String,String></code> value * @return a <code>BrokerAlgoTagSpec</code> value */ public static BrokerAlgoTagSpec generateTagSpec(Validator<BrokerAlgoTag> inValidator, Map<String,String> inOptions) { BrokerAlgoTagSpec spec = new BrokerAlgoTagSpec(); spec.setDescription("Test broker algo tag spec-" + counter.incrementAndGet()); spec.setTag(counter.incrementAndGet()); spec.setIsMandatory(true); spec.setPattern("^.*$"); spec.setValidator(inValidator); spec.setOptions(inOptions); return spec; } /** * no-op broker algo validator */ private static Validator<BrokerAlgo> algoValidator = new Validator<BrokerAlgo>() { @Override public void validate(BrokerAlgo inData) { } }; /** * no-op broker algo tag validator */ private static Validator<BrokerAlgoTag> tagValidator = new Validator<BrokerAlgoTag>() { @Override public void validate(BrokerAlgoTag inData) { } }; /** * used to uniquely identify test values */ private static final AtomicInteger counter = new AtomicInteger(0); /** * test XML context for marshalling and unmarshalling */ private static JAXBContext context; /** * marshals objects to XML */ private static Marshaller marshaller; /** * unmarshals objects from XML */ private static Unmarshaller unmarshaller; }