@Test
  public void testDoubleAlphaWithBeta() {

    final CompositeObjectSinkAdapter ad = new CompositeObjectSinkAdapter();

    final MvelConstraint lit =
        new MvelConstraintTestUtil(
            "type == \"stilton\"", new ObjectFieldImpl("stilton"), new MockExtractor());

    final AlphaNode al =
        new AlphaNode(buildContext.getNextId(), lit, new MockObjectSource(0), buildContext);

    ad.addObjectSink(al);

    assertNull(ad.otherSinks);
    assertNotNull(ad.hashedFieldIndexes);
    assertEquals(1, ad.hashableSinks.size());
    assertEquals(al, ad.getSinks()[0]);

    final MvelConstraint lit2 =
        new MvelConstraintTestUtil(
            "type == \"cheddar\"", new ObjectFieldImpl("cheddar"), new MockExtractor());

    final AlphaNode al2 =
        new AlphaNode(
            buildContext.getNextId(),
            lit2,
            new MockObjectSource(buildContext.getNextId()),
            buildContext);

    ad.addObjectSink(al2);

    assertNull(ad.otherSinks);
    assertEquals(2, ad.hashableSinks.size());
    assertEquals(al, ad.getSinks()[0]);
    assertEquals(al2, ad.getSinks()[1]);

    // add a beta, just for good measure, make sure it leaves others alone
    final MockBetaNode beta =
        new MockBetaNode(
            buildContext.getNextId(), new MockBetaNode(), new MockObjectSource(), buildContext);
    ad.addObjectSink(beta);
    assertNotNull(ad.otherSinks);
    assertEquals(2, ad.hashableSinks.size());

    assertEquals(1, ad.otherSinks.size());
    assertEquals(beta, ad.otherSinks.getFirst());

    ad.removeObjectSink(beta);
    assertNull(ad.otherSinks);
    assertEquals(2, ad.hashableSinks.size());
  }
  @Test
  public void testAlphaWithPredicate() {
    final CompositeObjectSinkAdapter ad = new CompositeObjectSinkAdapter();
    final AlphaNode al =
        new AlphaNode(
            buildContext.getNextId(), new PredicateConstraint(null, null), null, buildContext);
    ad.addObjectSink(al);

    assertEquals(1, ad.getSinks().length);
    assertEquals(1, ad.otherSinks.size());
    assertEquals(al, ad.otherSinks.getFirst());

    ad.removeObjectSink(al);
    assertEquals(0, ad.getSinks().length);
    assertNull(ad.otherSinks);
  }
  @Test
  public void testBeta() {
    final CompositeObjectSinkAdapter ad = new CompositeObjectSinkAdapter();
    final MockBetaNode beta =
        new MockBetaNode(
            buildContext.getNextId(), new MockBetaNode(), new MockObjectSource(), buildContext);
    ad.addObjectSink(beta);
    assertEquals(1, ad.getSinks().length);
    assertEquals(beta, ad.getSinks()[0]);

    assertEquals(1, ad.otherSinks.size());
    assertEquals(beta, ad.otherSinks.getFirst());

    assertNull(ad.hashableSinks);
    assertNull(ad.hashedFieldIndexes);
    assertNull(ad.hashedSinkMap);

    ad.removeObjectSink(beta);
    assertNull(ad.otherSinks);
    assertEquals(0, ad.getSinks().length);
  }
  @Test
  public void testSingleAlpha() {

    final CompositeObjectSinkAdapter ad = new CompositeObjectSinkAdapter();

    final MvelConstraint lit =
        new MvelConstraintTestUtil(
            "type == \"stilton\"", new ObjectFieldImpl("stilton"), new MockExtractor());

    final AlphaNode al =
        new AlphaNode(buildContext.getNextId(), lit, new MockObjectSource(0), buildContext);

    ad.addObjectSink(al);

    assertNull(ad.otherSinks);
    assertNotNull(ad.hashedFieldIndexes);
    assertEquals(1, ad.hashableSinks.size());
    assertEquals(al, ad.getSinks()[0]);

    ad.removeObjectSink(al);
    assertNull(ad.otherSinks);
    assertNull(ad.hashableSinks);
  }
  @Test
  public void testTripleAlphaObjectCharacterConstraint() {
    final CompositeObjectSinkAdapter ad = new CompositeObjectSinkAdapter();
    InternalReadAccessor extractor =
        store.getReader(Cheese.class, "charObjectType", this.getClass().getClassLoader());

    final MvelConstraint lit =
        new MvelConstraintTestUtil("charObjectType == 65", new LongFieldImpl(65), extractor);

    final AlphaNode al =
        new AlphaNode(
            buildContext.getNextId(),
            lit,
            new MockObjectSource(buildContext.getNextId()),
            buildContext);

    ad.addObjectSink(al);

    assertNull(ad.otherSinks);
    assertNotNull(ad.hashedFieldIndexes);
    assertEquals(1, ad.hashableSinks.size());
    assertEquals(al, ad.getSinks()[0]);

    final MvelConstraint lit2 =
        new MvelConstraintTestUtil("charObjectType == 66", new LongFieldImpl(66), extractor);

    final AlphaNode al2 =
        new AlphaNode(
            buildContext.getNextId(),
            lit2,
            new MockObjectSource(buildContext.getNextId()),
            buildContext);

    ad.addObjectSink(al2);

    assertNull(ad.hashedSinkMap);
    assertEquals(2, ad.hashableSinks.size());

    final MvelConstraint lit3 =
        new MvelConstraintTestUtil("charObjectType == 67", new LongFieldImpl(67), extractor);

    final AlphaNode al3 =
        new AlphaNode(
            buildContext.getNextId(),
            lit3,
            new MockObjectSource(buildContext.getNextId()),
            buildContext);
    ad.addObjectSink(al3);

    // this should now be nicely hashed.
    assertNotNull(ad.hashedSinkMap);
    assertNull(ad.hashableSinks);

    // test propagation
    Cheese cheese = new Cheese();
    cheese.setCharObjectType('B');
    CompositeObjectSinkAdapter.HashKey hashKey = new CompositeObjectSinkAdapter.HashKey();

    // should find this
    hashKey.setValue(extractor.getIndex(), cheese, extractor);
    ObjectSink sink = (ObjectSink) ad.hashedSinkMap.get(hashKey);
    assertSame(al2, sink);

    // should not find this one
    cheese.setCharObjectType('X');
    hashKey.setValue(extractor.getIndex(), cheese, extractor);
    sink = (ObjectSink) ad.hashedSinkMap.get(hashKey);
    assertNull(sink);

    // now remove one, check the hashing is undone
    ad.removeObjectSink(al2);
    assertNotNull(ad.hashableSinks);
    assertEquals(2, ad.hashableSinks.size());
    assertNull(ad.hashedSinkMap);
  }
  @Test
  public void testTripleAlpha() {
    final CompositeObjectSinkAdapter ad = new CompositeObjectSinkAdapter();
    InternalReadAccessor extractor =
        store.getReader(Cheese.class, "type", this.getClass().getClassLoader());

    final MvelConstraint lit =
        new MvelConstraintTestUtil(
            "type == \"stilton\"", new ObjectFieldImpl("stilton"), new MockExtractor());

    final AlphaNode al =
        new AlphaNode(
            buildContext.getNextId(),
            lit,
            new MockObjectSource(buildContext.getNextId()),
            buildContext);

    ad.addObjectSink(al);

    assertNull(ad.otherSinks);
    assertNotNull(ad.hashedFieldIndexes);
    assertEquals(1, ad.hashableSinks.size());
    assertEquals(al, ad.getSinks()[0]);

    final MvelConstraint lit2 =
        new MvelConstraintTestUtil(
            "type == \"cheddar\"", new ObjectFieldImpl("cheddar"), new MockExtractor());

    final AlphaNode al2 =
        new AlphaNode(
            buildContext.getNextId(),
            lit2,
            new MockObjectSource(buildContext.getNextId()),
            buildContext);

    ad.addObjectSink(al2);

    assertNull(ad.hashedSinkMap);
    assertEquals(2, ad.hashableSinks.size());

    final MvelConstraint lit3 =
        new MvelConstraintTestUtil(
            "type == \"stinky\"", new ObjectFieldImpl("stinky"), new MockExtractor());

    final AlphaNode al3 =
        new AlphaNode(
            buildContext.getNextId(),
            lit3,
            new MockObjectSource(buildContext.getNextId()),
            buildContext);
    ad.addObjectSink(al3);

    // this should now be nicely hashed.
    assertNotNull(ad.hashedSinkMap);
    assertNull(ad.hashableSinks);

    // now remove one, check the hashing is undone
    ad.removeObjectSink(al2);
    assertNotNull(ad.hashableSinks);
    assertEquals(2, ad.hashableSinks.size());
    assertNull(ad.hashedSinkMap);
  }