From: <tho...@us...> - 2011-01-19 21:22:08
|
Revision: 4132 http://bigdata.svn.sourceforge.net/bigdata/?rev=4132&view=rev Author: thompsonbry Date: 2011-01-19 21:22:01 +0000 (Wed, 19 Jan 2011) Log Message: ----------- More unit tests for the PartitionedJoinGroup. Modified Paths: -------------- branches/QUADS_QUERY_BRANCH/bigdata/src/java/com/bigdata/bop/controller/PartitionedJoinGroup.java branches/QUADS_QUERY_BRANCH/bigdata/src/test/com/bigdata/bop/controller/TestPartitionedJoinGroup.java Modified: branches/QUADS_QUERY_BRANCH/bigdata/src/java/com/bigdata/bop/controller/PartitionedJoinGroup.java =================================================================== --- branches/QUADS_QUERY_BRANCH/bigdata/src/java/com/bigdata/bop/controller/PartitionedJoinGroup.java 2011-01-19 21:21:03 UTC (rev 4131) +++ branches/QUADS_QUERY_BRANCH/bigdata/src/java/com/bigdata/bop/controller/PartitionedJoinGroup.java 2011-01-19 21:22:01 UTC (rev 4132) @@ -31,7 +31,10 @@ * @author <a href="mailto:tho...@us...">Bryan Thompson</a> * * @todo However, how do we manage when there are things like conditional - * routing operators? + * routing operators? [Answer - the CONDITION is raised onto the subquery + * such that we only conditionally run the subquery rather than routing + * out of the subquery if the condition is not satisfied - MikeP is making + * this change.] * * @todo The order of the {@link IPredicate}s in the tail plan is currently * unchanged from their given order (optional joins without constraints @@ -48,38 +51,97 @@ * leave a variable unbound). * * @todo runFirst flag on the expander (for free text search). this should be an - * annotation. this can be a headPlan. [There can be constraints which are - * evaluated against the head plan. They need to get attached to the joins - * generated for the head plan. MikeP writes: There is a free text search - * access path that replaces the actual access path for the predicate, - * which is meaningless in an of itself because the P is magical.] + * annotation. this can be a [headPlan]. [There can be constraints which + * are evaluated against the head plan. They need to get attached to the + * joins generated for the head plan. MikeP writes: There is a free text + * search access path that replaces the actual access path for the + * predicate, which is meaningless in an of itself because the P is + * magical.] * - * @todo inline APs and get rid of DataSetJoin. Rewrite NG and DG first. - * * @todo write a method which returns the set of constraints which should be run * for the last predicate in a given join path (a join path is just an * ordered array of predicates). + * + * FIXME Add a method to generate a runnable query plan from a collection + * of predicates and constraints. This is a bit different for the join + * graph and the optionals in the tail plan. The join graph itself should + * either be a {@link JoinGraph} operator which gets evaluated at run time + * or reordered by whichever optimizer is selected for the query (query + * hints). */ public class PartitionedJoinGroup { -// private final IPredicate<?>[] headPlan; -// -// private final IConstraint[] headPlanConstraints; + /** + * The set of variables bound by the non-optional predicates. + */ + private final Set<IVariable<?>> joinGraphVars = new LinkedHashSet<IVariable<?>>(); - private final IPredicate<?>[] joinGraphPredicates; + /** + * An unordered list of constraints which do not involve ANY variables. + * These constraints should be run first, before the join graph. + * + * @todo integrate into evaluation. + */ + private final List<IConstraint> runFirstConstraints = new LinkedList<IConstraint>(); - private final IConstraint[] joinGraphConstraints; + /** + * The set of the {@link IPredicate}s which have been flagged as + * "run first". These must all be non-optional predicates. They are usually + * special access paths created using an expander which replaces a mock + * access path. For example, free text search. + */ + private final List<IPredicate<?>> headPlan = new LinkedList<IPredicate<?>>(); - private final IPredicate<?>[] tailPlan; + /** + * The set of constraints which can be evaluated with the head plan + * predicates because the variables appearing in those constraints are known + * to become bound within the head plan. + */ + private final List<IConstraint> headPlanConstraints = new LinkedList<IConstraint>(); -// private final IConstraint[] tailPlanConstraints; - + /** + * The set of non-optional predicates which represent the join graph. + */ + private final List<IPredicate<?>> joinGraphPredicates = new LinkedList<IPredicate<?>>(); /** + * The set of constraints which can be evaluated with the join graph + * predicates because the variables appearing in those constraints are known + * to become bound within the join graph. + */ + private final List<IConstraint> joinGraphConstraints = new LinkedList<IConstraint>(); + + /** + * A set of optional predicates which will be run after the join graph. + */ + private final List<IPredicate<?>> tailPlan = new LinkedList<IPredicate<?>>(); + + /** + * An unordered list of those constraints containing at least one variable + * known be bound (and optionally) bound within the tail plan. + */ + private final List<IConstraint> tailPlanConstraints = new LinkedList<IConstraint>(); + + /** + * A map indicating which constraints are run for which predicate in the + * tail plan. The keys are the bopIds of the predicates in the tail plan. + * The values are the sets of constraints to run for that tail. + */ + private final Map<Integer/* predId */, List<IConstraint>> tailPlanConstraintMap = new LinkedHashMap<Integer, List<IConstraint>>(); + + /** + * The set of variables bound by the non-optional predicates. + */ + public Set<IVariable<?>> getJoinGraphVars() { + return joinGraphVars; + } + + /** * The {@link IPredicate}s in the join graph (required joins). */ public IPredicate<?>[] getJoinGraphPredicates() { - return joinGraphPredicates; + return joinGraphPredicates.toArray(new IPredicate[joinGraphPredicates + .size()]); } /** @@ -91,7 +153,8 @@ * query optimizer when it decides on an evaluation order for those joins). */ public IConstraint[] getJoinGraphConstraints() { - return joinGraphConstraints; + return joinGraphConstraints + .toArray(new IConstraint[joinGraphConstraints.size()]); } /** @@ -101,25 +164,60 @@ * tail plan in which their variable(S) MIGHT have been bound. */ public IPredicate<?>[] getTailPlan() { - return tailPlan; + return tailPlan.toArray(new IPredicate[tailPlan.size()]); } -// private PartitionedJoinGroup(// -// final IPredicate<?>[] headPlan,// -// final IConstraint[] headPlanConstraints,// -// final IPredicate<?>[] joinGraphPredicates,// -// final IConstraint[] joinGraphConstraints,// -// final IPredicate<?>[] tailPlan,// -// final IConstraint[] tailPlanConstraints// -// ) { -// this.headPlan = headPlan; -// this.headPlanConstraints = headPlanConstraints; -// this.joinGraphPredicates = joinGraphPredicates; -// this.joinGraphConstraints = joinGraphConstraints; -// this.tailPlan = tailPlan; -// this.tailPlanConstraints = tailPlanConstraints; -// } + /** + * Return the set of {@link IConstraint}s which should be evaluated when an + * identified predicate having SPARQL optional semantics is evaluated. For + * constraints whose variables are not known to be bound when entering the + * tail plan, the constraint should be evaluated at the last predicate for + * which its variables MIGHT become bound. + * + * @param bopId + * The identifier for an {@link IPredicate} appearing in the tail + * plan. + * + * @return The set of constraints to be imposed by the join which evaluates + * that predicate. This will be an empty array if there are no + * constraints which can be imposed when that predicate is + * evaluated. + * + * @throws IllegalArgumentException + * if there is no such predicate in the tail plan. + */ + public IConstraint[] getTailPlanConstraints(final int bopId) { + boolean found = false; + + for (IPredicate<?> p : tailPlan) { + + if (p.getId() == bopId) { + + found = true; + + break; + + } + + } + + if (!found) + throw new IllegalArgumentException( + "No such predicate in tail plan: bopId=" + bopId); + + final List<IConstraint> constraints = tailPlanConstraintMap.get(bopId); + + if (constraints == null) { + + return new IConstraint[0]; + + } + + return constraints.toArray(new IConstraint[constraints.size()]); + + } + /** * Analyze a set of {@link IPredicate}s representing optional and * non-optional joins and a collection of {@link IConstraint}s, partitioning @@ -156,52 +254,6 @@ } /* - * An unordered list of constraints which do not involve ANY variables. - * These constraints should be run first, before the join graph. - * - * @todo add to the class instance fields. - */ - final List<IConstraint> runFirstConstraints = new LinkedList<IConstraint>(); - -// final List<IPredicate<?>> headPlan = new LinkedList<IPredicate<?>>(); -// -// final List<IConstraint> headPlanConstraints = new LinkedList<IConstraint>(); - - /* - * The non-optional predicates. - */ - final List<IPredicate<?>> joinGraphPredicates = new LinkedList<IPredicate<?>>(); - - /* - * The set of variables bound by the non-optional predicates. - */ - final Set<IVariable<?>> joinGraphVars = new LinkedHashSet<IVariable<?>>(); - - /* - * An unordered list of those constraints whose variables are known to - * be bound by the non-optional predicates. - */ - final List<IConstraint> joinGraphConstraints = new LinkedList<IConstraint>(); - - /* - * The predicates representing the optional joins. - */ - final List<IPredicate<?>> tailPlan = new LinkedList<IPredicate<?>>(); - - /* - * An unordered list of those constraints containing at least one - * variable known to NOT be bound by the non-optional predicates. - */ - final List<IConstraint> tailPlanConstraints = new LinkedList<IConstraint>(); - - /* - * Map indicating which constraints are run for which predicate in the - * tail plan. The keys are the bopIds of the predicates in the tail - * plan. The values are the sets of constraints to run for that tail. - */ - final Map<Integer/* predId */, List<IConstraint>> tailPlanConstraintMap = new LinkedHashMap<Integer, List<IConstraint>>(); - - /* * First identify the predicates which correspond to non-optional joins. * All other pipeline operators are inserted into the tail plan in the * order in which they are given. @@ -209,25 +261,38 @@ for (IPredicate<?> p : sourcePreds) { if (p == null) throw new IllegalArgumentException(); - if (!p.isOptional()) { + if (p.isOptional()) { + if (p.getAccessPathExpander() != null + && p.getAccessPathExpander().runFirst()) + throw new IllegalStateException( + "runFirst is not compatible with optional: " + p); + // an optional predicate + tailPlan.add(p); + } else { // non-optional predicate. - joinGraphPredicates.add(p); - // variables which will be bound by the join graph. + if (p.getAccessPathExpander() != null + && p.getAccessPathExpander().runFirst()) { + headPlan.add(p); + } else { + // part of the join graph. + joinGraphPredicates.add(p); + } + /* + * Add to the set of variables which will be bound by the time + * the join graph is done executing. + */ final Iterator<IVariable<?>> vitr = BOpUtility .getArgumentVariables(p); while (vitr.hasNext()) { joinGraphVars.add(vitr.next()); } - } else { - // an optional predicate - tailPlan.add(p); } } /* - * Now break the constraints into two groups - those whose variables are - * bound by the predicates in the join graph (required joins) and those - * having at least one variable bound by an optional join. + * Now break the constraints into different groups based on their + * variables and when those variables are known to be bound (required + * joins) or might be bound (optionals). */ for (IConstraint c : constraints) { boolean allFound = true; @@ -239,7 +304,7 @@ * we should evaluate it as soon as possible. I.e., before the * join graph. */ - runFirstConstraints.add(c); + runFirstConstraints.add(c); // @todo unit test. continue; } while (vitr.hasNext()) { @@ -342,19 +407,6 @@ } - /* - * Assign to instance fields. - */ - // @todo headPlan -// this.headPlan = null; -// this.headPlanConstraints = null; - this.joinGraphPredicates = joinGraphPredicates - .toArray(new IPredicate[joinGraphPredicates.size()]); - this.joinGraphConstraints = joinGraphConstraints - .toArray(new IConstraint[joinGraphConstraints.size()]); - this.tailPlan = tailPlan.toArray(new IPredicate[tailPlan.size()]); - // @todo tailPlanConstraintMap - } - + } Modified: branches/QUADS_QUERY_BRANCH/bigdata/src/test/com/bigdata/bop/controller/TestPartitionedJoinGroup.java =================================================================== --- branches/QUADS_QUERY_BRANCH/bigdata/src/test/com/bigdata/bop/controller/TestPartitionedJoinGroup.java 2011-01-19 21:21:03 UTC (rev 4131) +++ branches/QUADS_QUERY_BRANCH/bigdata/src/test/com/bigdata/bop/controller/TestPartitionedJoinGroup.java 2011-01-19 21:22:01 UTC (rev 4132) @@ -27,9 +27,21 @@ package com.bigdata.bop.controller; +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.IVariable; +import com.bigdata.bop.NV; +import com.bigdata.bop.Var; +import com.bigdata.bop.IPredicate.Annotations; +import com.bigdata.bop.ap.Predicate; +import com.bigdata.bop.constraint.NEConstant; /** * Unit tests for {@link PartitionedJoinGroup}. @@ -84,13 +96,407 @@ } /** - * @todo test with headPlan, tailPlan. - * @todo test association of constraints to optional joins. + * 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.getJoinGraphPredicates()).iterator()); + + // there are no constraints. + assertEquals("joinGraphConstraints.size", 0, fixture + .getJoinGraphConstraints().length); + + // there is no tail plan. + assertEquals("tailPlan", new IPredicate[] {}, fixture.getTailPlan()); + + } + + // Test w/ constraint(s) on the join graph. + { + + final IConstraint c1 = new NEConstant(x, + new Constant<String>("Bob")); + + final IConstraint c2 = 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.getJoinGraphPredicates()).iterator()); + + // verify all constraints were place on the join graph. + assertSameIteratorAnyOrder("joinGraphConstraints", constraints, + Arrays.asList(fixture.getJoinGraphConstraints()).iterator()); + + // 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.getJoinGraphPredicates()) + .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 = new NEConstant(x, + new Constant<String>("Bob")); + + final IConstraint c2 = new NEConstant(y, + new Constant<String>("UNCG")); + + final IConstraint c3 = 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.getJoinGraphPredicates()) + .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. + * + * @todo test for runFirst constraints. + * * @todo test logic to attach constraints to non-optional joins based on a * given join path (not yet written). */ public void test_something() { 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". + */ + @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; + + 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); + + } + + 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."); + + } + + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |