/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Jan 19, 2011
*/
package com.bigdata.bop.joinGraph;
import java.util.Arrays;
import java.util.Iterator;
import junit.framework.TestCase2;
import com.bigdata.bop.BOp;
import com.bigdata.bop.Constant;
import com.bigdata.bop.IConstraint;
import com.bigdata.bop.IPredicate;
import com.bigdata.bop.IPredicate.Annotations;
import com.bigdata.bop.IVariable;
import com.bigdata.bop.NV;
import com.bigdata.bop.Var;
import com.bigdata.bop.ap.Predicate;
import com.bigdata.bop.constraint.Constraint;
import com.bigdata.bop.constraint.NEConstant;
/**
* Unit tests for {@link PartitionedJoinGroup}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
* @version $Id$
*
* FIXME Add test to ensure that constraints are run regardless as of the last
* join even if their variables are not known to be bound. Also, modify the
* constructor to accept a set of variables which are known to be bound on
* entry into the join group.
*/
public class TestPartitionedJoinGroup extends TestCase2 {
/**
*
*/
public TestPartitionedJoinGroup() {
}
/**
* @param name
*/
public TestPartitionedJoinGroup(String name) {
super(name);
}
public void test_ctor_correctRejection() {
// null source predicate[].
try {
new PartitionedJoinGroup(null/* sourcePreds */, null/* constraints */);
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
// empty source predicate[].
try {
new PartitionedJoinGroup(new IPredicate[0], null/* constraints */);
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
// null element in the source predicate[].
try {
new PartitionedJoinGroup(new IPredicate[1], null/* constraints */);
fail("Expecting: " + IllegalArgumentException.class);
} catch (IllegalArgumentException ex) {
if (log.isInfoEnabled())
log.info("Ignoring expected exception: " + ex);
}
}
/**
* A test based loosely on LUBM Q2. There are no RDF specific constructions
* used here.
*/
public void test_requiredJoins() {
final String rdfType = "rdfType";
final String graduateStudent = "graduateStudent";
final String university = "university";
final String department = "department";
final String memberOf = "memberOf";
final String subOrganizationOf = "subOrganizationOf";
final String undergraduateDegreeFrom = "undergraduateDegreeFrom";
final IPredicate<?>[] preds;
final IPredicate<?> p0, p1, p2, p3, p4, p5;
final IVariable<?> x = Var.var("x");
final IVariable<?> y = Var.var("y");
final IVariable<?> z = Var.var("z");
{
// The name space for the SPO relation.
final String[] relation = new String[] { "spo" };
final long timestamp = System.currentTimeMillis();
int nextId = 0;
// ?x a ub:GraduateStudent .
p0 = new Predicate(new BOp[] { x,
new Constant<String>(rdfType),
new Constant<String>(graduateStudent) },//
new NV(BOp.Annotations.BOP_ID, nextId++),//
new NV(Annotations.TIMESTAMP, timestamp),//
new NV(IPredicate.Annotations.RELATION_NAME, relation)//
);
// ?y a ub:University .
p1 = new Predicate(new BOp[] { y,
new Constant<String>(rdfType),
new Constant<String>(university) },//
new NV(BOp.Annotations.BOP_ID, nextId++),//
new NV(Annotations.TIMESTAMP, timestamp),//
new NV(IPredicate.Annotations.RELATION_NAME, relation)//
);
// ?z a ub:Department .
p2 = new Predicate(new BOp[] { z,
new Constant<String>(rdfType),
new Constant<String>(department) },//
new NV(BOp.Annotations.BOP_ID, nextId++),//
new NV(Annotations.TIMESTAMP, timestamp),//
new NV(IPredicate.Annotations.RELATION_NAME, relation)//
);
// ?x ub:memberOf ?z .
p3 = new Predicate(new BOp[] { x,
new Constant<String>(memberOf), z },//
new NV(BOp.Annotations.BOP_ID, nextId++),//
new NV(Annotations.TIMESTAMP, timestamp),//
new NV(IPredicate.Annotations.RELATION_NAME, relation)//
);
// ?z ub:subOrganizationOf ?y .
p4 = new Predicate(new BOp[] { z,
new Constant<String>(subOrganizationOf), y },//
new NV(BOp.Annotations.BOP_ID, nextId++),//
new NV(Annotations.TIMESTAMP, timestamp),//
new NV(IPredicate.Annotations.RELATION_NAME, relation)//
);
// ?x ub:undergraduateDegreeFrom ?y
p5 = new Predicate(new BOp[] { x,
new Constant<String>(undergraduateDegreeFrom), y },//
new NV(BOp.Annotations.BOP_ID, nextId++),//
new NV(Annotations.TIMESTAMP, timestamp),//
new NV(IPredicate.Annotations.RELATION_NAME, relation)//
);
// the vertices of the join graph (the predicates).
preds = new IPredicate[] { p0, p1, p2, p3, p4, p5 };
}
// Test w/o any constraints.
{
final IConstraint[] constraints = new IConstraint[] {
};
final PartitionedJoinGroup fixture = new PartitionedJoinGroup(
preds, constraints);
// all variables are bound within the join graph.
assertSameIteratorAnyOrder("joinGraphVars", new IVariable[] { x, y,
z }, fixture.getJoinGraphVars().iterator());
// verify all predicates were placed into the join graph.
assertSameIteratorAnyOrder("joinGraph", preds, Arrays.asList(
fixture.getJoinGraph()).iterator());
// there are no constraints.
assertEquals("joinGraphConstraints.size", new IConstraint[] {},
fixture.getJoinGraphConstraints());
// there is no tail plan.
assertEquals("tailPlan", new IPredicate[] {}, fixture.getTailPlan());
}
// Test w/ constraint(s) on the join graph.
{
final IConstraint c1 = Constraint.wrap(new NEConstant(x,
new Constant<String>("Bob")));
final IConstraint c2 = Constraint.wrap(new NEConstant(y,
new Constant<String>("UNCG")));
final IConstraint[] constraints = new IConstraint[] { c1, c2 };
final PartitionedJoinGroup fixture = new PartitionedJoinGroup(
preds, constraints);
// all variables are bound within the join graph.
assertSameIteratorAnyOrder("joinGraphVars", new IVariable[] { x, y,
z }, fixture.getJoinGraphVars().iterator());
// verify all predicates were placed into the join graph.
assertSameIteratorAnyOrder("joinGraph", preds, Arrays.asList(
fixture.getJoinGraph()).iterator());
// verify all constraints were place on the join graph.
assertSameIteratorAnyOrder("joinGraphConstraints", constraints,
Arrays.asList(fixture.getJoinGraphConstraints()).iterator());
/*
* Verify the placement of each constraint for a variety of join
* paths.
*/
{
// final int[] pathIds = BOpUtility.getPredIds(new IPredicate[] {
// p0, p1, p2, p3, p4, p5 });
// final IConstraint[] actual = fixture
// .getJoinGraphConstraints(pathIds);
// System.out.println(Arrays.toString(actual));
// c1 is applied when x is bound. x is bound by p0.
assertEquals(new IConstraint[] { c1 }, fixture
.getJoinGraphConstraints(//
new int[] { p1.getId(), p0.getId() },//
false// pathIsComplete
));
/*
* c1 is applied when x is bound. x is bound by p0. p0 is the
* last predicate in this join path, so c1 is attached to p0.
*/
assertEquals(new IConstraint[] { c1 }, fixture
.getJoinGraphConstraints(//
new int[] { p0.getId()},//
false//pathIsComplete
));
/*
* c2 is applied when y is bound. y is bound by p1. p1 is the
* last predicate in this join path, p1 is the last predicate in
* this join path so c2 is attached to p1.
*/
assertEquals(new IConstraint[] { c2 }, fixture
.getJoinGraphConstraints(//
new int[] { p0.getId(), p1.getId() },//
false// pathIsComplete
));
}
// there is no tail plan.
assertEquals("tailPlan", new IPredicate[] {}, fixture.getTailPlan());
}
}
/**
* A test when there are optional joins involved. In this test, we again
* start with LUBM Q2, but the predicates which would bind <code>z</code>
* are both marked as optional. This should shift the constraint on [z] into
* the tail plan as well.
*/
public void test_withOptionalJoins() {
final String rdfType = "rdfType";
final String graduateStudent = "graduateStudent";
final String university = "university";
final String department = "department";
final String memberOf = "memberOf";
final String subOrganizationOf = "subOrganizationOf";
final String undergraduateDegreeFrom = "undergraduateDegreeFrom";
final IPredicate<?>[] preds;
final IPredicate<?> p0, p1, p2, p3, p4, p5;
final IVariable<?> x = Var.var("x");
final IVariable<?> y = Var.var("y");
final IVariable<?> z = Var.var("z");
{
// The name space for the SPO relation.
final String[] relation = new String[] { "spo" };
final long timestamp = System.currentTimeMillis();
int nextId = 0;
// ?x a ub:GraduateStudent .
p0 = new Predicate(new BOp[] { x,
new Constant<String>(rdfType),
new Constant<String>(graduateStudent) },//
new NV(BOp.Annotations.BOP_ID, nextId++),//
new NV(Annotations.TIMESTAMP, timestamp),//
new NV(IPredicate.Annotations.RELATION_NAME, relation)//
);
// ?y a ub:University .
p1 = new Predicate(new BOp[] { y,
new Constant<String>(rdfType),
new Constant<String>(university) },//
new NV(BOp.Annotations.BOP_ID, nextId++),//
new NV(Annotations.TIMESTAMP, timestamp),//
new NV(IPredicate.Annotations.RELATION_NAME, relation)//
);
// ?z a ub:Department . (optional)
p2 = new Predicate(new BOp[] { z,
new Constant<String>(rdfType),
new Constant<String>(department) },//
new NV(BOp.Annotations.BOP_ID, nextId++),//
new NV(Annotations.TIMESTAMP, timestamp),//
new NV(IPredicate.Annotations.OPTIONAL, true),//
new NV(IPredicate.Annotations.RELATION_NAME, relation)//
);
// ?x ub:memberOf ?z . (optional).
p3 = new Predicate(new BOp[] { x,
new Constant<String>(memberOf), z },//
new NV(BOp.Annotations.BOP_ID, nextId++),//
new NV(Annotations.TIMESTAMP, timestamp),//
new NV(IPredicate.Annotations.OPTIONAL, true),//
new NV(IPredicate.Annotations.RELATION_NAME, relation)//
);
// ?z ub:subOrganizationOf ?y . (optional).
p4 = new Predicate(new BOp[] { z,
new Constant<String>(subOrganizationOf), y },//
new NV(BOp.Annotations.BOP_ID, nextId++),//
new NV(Annotations.TIMESTAMP, timestamp),//
new NV(IPredicate.Annotations.OPTIONAL, true),//
new NV(IPredicate.Annotations.RELATION_NAME, relation)//
);
// ?x ub:undergraduateDegreeFrom ?y
p5 = new Predicate(new BOp[] { x,
new Constant<String>(undergraduateDegreeFrom), y },//
new NV(BOp.Annotations.BOP_ID, nextId++),//
new NV(Annotations.TIMESTAMP, timestamp),//
new NV(IPredicate.Annotations.RELATION_NAME, relation)//
);
// the vertices of the join graph (the predicates).
preds = new IPredicate[] { p0, p1, p2, p3, p4, p5 };
}
// Test w/o any constraints.
{
final IConstraint[] constraints = new IConstraint[] {
};
final PartitionedJoinGroup fixture = new PartitionedJoinGroup(
preds, constraints);
// only {x,y} are bound within the join graph.
assertSameIteratorAnyOrder("joinGraphVars",
new IVariable[] { x, y }, fixture.getJoinGraphVars()
.iterator());
// verify predicates placed into the join graph.
assertSameIteratorAnyOrder("joinGraph", new IPredicate[] { p0, p1,
p5 }, Arrays.asList(fixture.getJoinGraph())
.iterator());
// there are no constraints on the join graph predicates.
assertEquals("joinGraphConstraints.size", 0, fixture
.getJoinGraphConstraints().length);
// {p2, p3,p4} are in the tail plan.
assertEquals("tailPlan", new IPredicate[] { p2, p3, p4 }, fixture
.getTailPlan());
// no constraints were assigned to optional predicate [p2].
assertEquals("", 0,
fixture.getTailPlanConstraints(p2.getId()).length);
// no constraints were assigned to optional predicate [p3].
assertEquals("", 0,
fixture.getTailPlanConstraints(p3.getId()).length);
// no constraints were assigned to optional predicate [p4].
assertEquals("", 0,
fixture.getTailPlanConstraints(p4.getId()).length);
}
// Test w/ constraint(s) on the join graph.
{
final IConstraint c1 = Constraint.wrap(new NEConstant(x,
new Constant<String>("Bob")));
final IConstraint c2 = Constraint.wrap(new NEConstant(y,
new Constant<String>("UNCG")));
final IConstraint c3 = Constraint.wrap(new NEConstant(z,
new Constant<String>("Physics")));
final IConstraint[] constraints = new IConstraint[] { c1, c2, c3 };
final PartitionedJoinGroup fixture = new PartitionedJoinGroup(
preds, constraints);
// only {x,y} are bound within the join graph.
assertSameIteratorAnyOrder("joinGraphVars",
new IVariable[] { x, y }, fixture.getJoinGraphVars()
.iterator());
// verify predicates placed into the join graph.
assertSameIteratorAnyOrder("joinGraph", new IPredicate[] { p0, p1,
p5 }, Arrays.asList(fixture.getJoinGraph())
.iterator());
// verify constraints on the join graph.
assertSameIteratorAnyOrder("joinGraphConstraints",
new IConstraint[] { c1, c2 }, Arrays.asList(
fixture.getJoinGraphConstraints()).iterator());
// {p2,p3,p4} are in the tail plan.
assertEquals("tailPlan", new IPredicate[] { p2, p3, p4 }, fixture
.getTailPlan());
// no constraints were assigned to optional predicate [p2].
assertEquals("", new IConstraint[] {}, fixture
.getTailPlanConstraints(p2.getId()));
// no constraints were assigned to optional predicate [p3].
assertEquals("", new IConstraint[] {}, fixture
.getTailPlanConstraints(p3.getId()));
// the constraint on [z] was assigned to optional predicate [p4].
assertEquals("", new IConstraint[] { c3 }, fixture
.getTailPlanConstraints(p4.getId()));
}
}
// /**
// * @todo test with headPlan (actually, I think that we will remove
// * the head plan from the PartitionedJoinGraph).
// */
// public void test_something_headPlan() {
// fail("write tests");
// }
/**
* Verifies that the iterator visits the specified objects in some arbitrary
* ordering and that the iterator is exhausted once all expected objects
* have been visited. The implementation uses a selection without
* replacement "pattern".
*
* @todo raise into the AbstractTestCase (e.g, TestCase2/3).
*/
@SuppressWarnings("unchecked")
static public void assertSameIteratorAnyOrder(final Object[] expected,
final Iterator actual) {
assertSameIteratorAnyOrder("", expected, actual);
}
/**
* Verifies that the iterator visits the specified objects in some arbitrary
* ordering and that the iterator is exhausted once all expected objects
* have been visited. The implementation uses a selection without
* replacement "pattern".
*/
@SuppressWarnings("unchecked")
static public void assertSameIteratorAnyOrder(final String msg,
final Object[] expected, final Iterator actual) {
// Populate a map that we will use to realize the match and
// selection without replacement logic.
final int nrange = expected.length;
final java.util.Map range = new java.util.HashMap();
for (int j = 0; j < nrange; j++) {
range.put(expected[j], expected[j]);
}
// Do selection without replacement for the objects visited by
// iterator.
for (int j = 0; j < nrange; j++) {
if (!actual.hasNext()) {
fail(msg + ": Index exhausted while expecting more object(s)"
+ ": index=" + j);
}
final Object actualObject = actual.next();
if (range.remove(actualObject) == null) {
fail("Object not expected" + ": index=" + j + ", object="
+ actualObject);
}
}
if (actual.hasNext()) {
fail("Iterator will deliver too many objects.");
}
}
}