/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.pig.test; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; import org.apache.pig.impl.plan.*; import org.apache.pig.impl.plan.optimizer.*; import org.junit.Test; /** * Test the generic operator classes (Operator, OperatorPlan, * PlanVisitor). Also includes tests for optimizer framework, since that * can use the same generic test operators. */ public class TestOperatorPlan extends junit.framework.TestCase { private int mNextKey = 0; private static final String SCOPE = "RULE"; private static NodeIdGenerator nodeIdGen = NodeIdGenerator.getGenerator(); public static int MAX_OPTIMIZATION_ITERATIONS = 250; abstract class TOperator extends Operator implements Comparable { protected String mName; TOperator(String name) { super(new OperatorKey("", mNextKey++)); mName = name; } public int compareTo(Object o) { if (!(o instanceof TOperator)) { return -1; } TOperator other = (TOperator)o; return mName.compareTo(other.mName); } } class SingleOperator extends TOperator { SingleOperator(String name) { super(name); } public boolean supportsMultipleInputs() { return false; } public boolean supportsMultipleOutputs() { return false; } @Override public void visit(PlanVisitor v) throws VisitorException { ((TVisitor)v).visit(this); } public String name() { //return this.getClass().getName() + " " + mName return mName; } } class MultiOperator extends TOperator { MultiOperator(String name) { super(name); } public boolean supportsMultipleInputs() { return true; } public boolean supportsMultipleOutputs() { return true; } public void visit(PlanVisitor v) throws VisitorException { ((TVisitor)v).visit(this); } public String name() { //return this.getClass().getName() + " " + mName; return mName; } } class MultiInputSingleOutputOperator extends TOperator { MultiInputSingleOutputOperator(String name) { super(name); } public boolean supportsMultipleInputs() { return true; } public boolean supportsMultipleOutputs() { return false; } @Override public void visit(PlanVisitor v) throws VisitorException { ((TVisitor)v).visit(this); } public String name() { //return this.getClass().getName() + " " + mName return mName; } } class MultiOutputSingleInputOperator extends TOperator { MultiOutputSingleInputOperator(String name) { super(name); } public boolean supportsMultipleInputs() { return false; } public boolean supportsMultipleOutputs() { return true; } @Override public void visit(PlanVisitor v) throws VisitorException { ((TVisitor)v).visit(this); } public String name() { //return this.getClass().getName() + " " + mName return mName; } } class TPlan extends OperatorPlan<TOperator> { public String display() { StringBuilder buf = new StringBuilder(); buf.append("Nodes: "); // Guarantee a sorting TreeSet<TOperator> ts = new TreeSet(mOps.keySet()); for (TOperator op : ts) { buf.append(op.name()); buf.append(' '); } buf.append("FromEdges: "); ts = new TreeSet(mFromEdges.keySet()); Iterator<TOperator> i = ts.iterator(); while (i.hasNext()) { TOperator from = i.next(); TreeSet<TOperator> ts2 = new TreeSet(mFromEdges.get(from)); Iterator<TOperator> j = ts2.iterator(); while (j.hasNext()) { buf.append(from.name()); buf.append("->"); buf.append(j.next().name()); buf.append(' '); } } buf.append("ToEdges: "); ts = new TreeSet(mToEdges.keySet()); i = ts.iterator(); while (i.hasNext()) { TOperator from = i.next(); TreeSet<TOperator> ts2 = new TreeSet(mToEdges.get(from)); Iterator<TOperator> j = ts2.iterator(); while (j.hasNext()) { buf.append(from.name()); buf.append("->"); buf.append(j.next().name()); buf.append(' '); } } return buf.toString(); } } abstract class TVisitor extends PlanVisitor<TOperator, TPlan> { protected StringBuilder mJournal; TVisitor(TPlan plan, PlanWalker<TOperator, TPlan> walker) { super(plan, walker); mJournal = new StringBuilder(); } public void visit(SingleOperator so) throws VisitorException { mJournal.append(so.name()); mJournal.append(' '); } public void visit(MultiOperator mo) throws VisitorException { mJournal.append(mo.name()); mJournal.append(' '); } public void visit(MultiInputSingleOutputOperator miso) throws VisitorException { mJournal.append(miso.name()); mJournal.append(' '); } public void visit(MultiOutputSingleInputOperator mosi) throws VisitorException { mJournal.append(mosi.name()); mJournal.append(' '); } public String getJournal() { return mJournal.toString(); } } class TDepthVisitor extends TVisitor { TDepthVisitor(TPlan plan) { super(plan, new DepthFirstWalker(plan)); } } class TDependVisitor extends TVisitor { TDependVisitor(TPlan plan) { super(plan, new DependencyOrderWalker(plan)); } } static class TOptimizer extends PlanOptimizer<TOperator, TPlan> { public TOptimizer(TPlan plan) { super(plan, TestOperatorPlan.MAX_OPTIMIZATION_ITERATIONS); } public void addRule(Rule rule) { mRules.add(rule); } } class AlwaysTransform extends Transformer<TOperator, TPlan> { public boolean mTransformed = false; private int mNumChecks = 0; AlwaysTransform(TPlan plan) { super(plan); } public boolean check(List<TOperator> nodes) { ++mNumChecks; return true; } public void transform(List<TOperator> nodes) { mTransformed = true; } public int getNumberOfChecks() { return mNumChecks; } @Override public void reset() { } } class NeverTransform extends Transformer<TOperator, TPlan> { public boolean mTransformed = false; private int mNumChecks = 0; NeverTransform(TPlan plan) { super(plan); } public boolean check(List<TOperator> nodes) { ++mNumChecks; return false; } public void transform(List<TOperator> nodes) { mTransformed = true; } public int getNumberOfChecks() { return mNumChecks; } @Override public void reset() { } } @Test public void testAddRemove() throws Exception { // Test that we can add and remove nodes from the plan. Also test // that we can fetch the nodes by operator key, by operator, by // roots, by leaves, that they have no predecessors and no // successors. TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; for (int i = 0; i < 3; i++) { ops[i] = new SingleOperator(Integer.toString(i)); plan.add(ops[i]); } // All should be roots, as none are connected List<TOperator> roots = plan.getRoots(); for (int i = 0; i < 3; i++) { assertTrue("Roots should contain operator " + i, roots.contains(ops[i])); } // All should be leaves, as none are connected List<TOperator> leaves = plan.getLeaves(); for (int i = 0; i < 3; i++) { assertTrue("Leaves should contain operator " + i, leaves.contains(ops[i])); } // Each operator should have no successors or predecessors. assertNull(plan.getSuccessors(ops[1])); assertNull(plan.getPredecessors(ops[1])); // Make sure we find them all when we iterate through them. Set<TOperator> s = new HashSet<TOperator>(); Iterator<TOperator> j = plan.iterator(); while (j.hasNext()) { s.add(j.next()); } for (int i = 0; i < 3; i++) { assertTrue("Iterator should contain operator " + i, s.contains(ops[i])); } // Test that we can find an operator by its key. TOperator op = plan.getOperator(new OperatorKey("", 1)); assertEquals("Expected to get back ops[1]", ops[1], op); // Test that we can get an operator key by its operator OperatorKey opkey = new OperatorKey("", 1); assertTrue("Expected to get back key for ops[1]", opkey.equals(plan.getOperatorKey(ops[1]))); // Test that we can remove operators plan.remove(ops[2]); assertEquals("Should only have two roots now.", 2, plan.getRoots().size()); assertEquals("Should only have two leaves now.", 2, plan.getLeaves().size()); j = plan.iterator(); int k; for (k = 0; j.hasNext(); k++) j.next(); assertEquals("Iterator should only return two now", 2, k); // Remove all operators plan.remove(ops[0]); plan.remove(ops[1]); assertEquals("Should only have no roots now.", 0, plan.getRoots().size()); assertEquals("Should only have no leaves now.", 0, plan.getLeaves().size()); j = plan.iterator(); assertFalse("Iterator should return nothing now", j.hasNext()); } @Test public void testInsertBetween() throws Exception { // Test that insertBetween works. TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; for (int i = 0; i < 3; i++) { ops[i] = new SingleOperator(Integer.toString(i)); plan.add(ops[i]); } // Connect 0 to 2 plan.connect(ops[0], ops[2]); Collection p = plan.getPredecessors(ops[0]); assertNull(p); p = plan.getSuccessors(ops[0]); assertEquals(1, p.size()); Iterator i = p.iterator(); assertEquals(ops[2], i.next()); p = plan.getPredecessors(ops[1]); assertNull(p); p = plan.getSuccessors(ops[1]); assertNull(p); p = plan.getPredecessors(ops[2]); assertEquals(1, p.size()); i = p.iterator(); assertEquals(ops[0], i.next()); p = plan.getSuccessors(ops[2]); assertNull(p); // Insert 1 in between 0 and 2 plan.insertBetween(ops[0], ops[1], ops[2]); p = plan.getPredecessors(ops[0]); assertNull(p); p = plan.getSuccessors(ops[0]); assertEquals(1, p.size()); i = p.iterator(); assertEquals(ops[1], i.next()); p = plan.getPredecessors(ops[1]); assertEquals(1, p.size()); i = p.iterator(); assertEquals(ops[0], i.next()); p = plan.getSuccessors(ops[1]); assertEquals(1, p.size()); i = p.iterator(); assertEquals(ops[2], i.next()); p = plan.getPredecessors(ops[2]); assertEquals(1, p.size()); i = p.iterator(); assertEquals(ops[1], i.next()); p = plan.getSuccessors(ops[2]); assertNull(p); } @Test public void testInsertBetweenNegative() throws Exception { // Test that insertBetween throws errors when it should. TPlan plan = new TPlan(); TOperator[] ops = new TOperator[4]; for (int i = 0; i < 4; i++) { ops[i] = new MultiOperator(Integer.toString(i)); plan.add(ops[i]); } plan.connect(ops[0], ops[1]); boolean caughtIt = false; try { plan.insertBetween(ops[0], ops[3], ops[2]); } catch (PlanException pe) { caughtIt = true; } assertTrue(caughtIt); } @Test public void testLinearGraph() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[5]; for (int i = 0; i < 5; i++) { ops[i] = new SingleOperator(Integer.toString(i)); plan.add(ops[i]); if (i > 0) plan.connect(ops[i - 1], ops[i]); } // Test that connecting a node not yet in the plan is detected. TOperator bogus = new SingleOperator("X"); boolean sawError = false; try { plan.connect(ops[2], bogus); } catch (PlanException ioe) { assertEquals("Attempt to connect operator X which is not in " + "the plan.", ioe.getMessage()); sawError = true; } assertTrue("Should have caught an error when we tried to connect a " + "node that was not in the plan", sawError); // Get roots should just return ops[0] List<TOperator> roots = plan.getRoots(); assertEquals(1, roots.size()); assertEquals(roots.get(0), ops[0]); // Get leaves should just return ops[4] List<TOperator> leaves = plan.getLeaves(); assertEquals(1, leaves.size()); assertEquals(leaves.get(0), ops[4]); // Test that connecting another input to SingleOperator gives // error. plan.add(bogus); sawError = false; try { plan.connect(bogus, ops[1]); } catch (PlanException ioe) { assertEquals("Attempt to give operator of type " + "org.apache.pig.test.TestOperatorPlan$SingleOperator " + "multiple inputs. This operator does " + "not support multiple inputs.", ioe.getMessage()); sawError = true; } assertTrue("Should have caught an error when we tried to connect a " + "second input to a Single", sawError); // Test that connecting another output to SingleOperator gives // error. sawError = false; try { plan.connect(ops[0], bogus); } catch (PlanException ioe) { assertEquals("Attempt to give operator of type " + "org.apache.pig.test.TestOperatorPlan$SingleOperator " + "multiple outputs. This operator does " + "not support multiple outputs.", ioe.getMessage()); sawError = true; } assertTrue("Should have caught an error when we tried to connect a " + "second output to a " + "org.apache.pig.test.TestOperatorPlan$SingleOperator", sawError); plan.remove(bogus); // Successor for ops[1] should be ops[2] Collection s = plan.getSuccessors(ops[1]); assertEquals(1, s.size()); Iterator i = s.iterator(); assertEquals(ops[2], i.next()); // Predecessor for ops[1] should be ops[0] Collection p = plan.getPredecessors(ops[1]); assertEquals(1, p.size()); i = p.iterator(); assertEquals(ops[0], i.next()); assertEquals("Nodes: 0 1 2 3 4 FromEdges: 0->1 1->2 2->3 3->4 ToEdges: 1->0 2->1 3->2 4->3 ", plan.display()); // Visit it depth first TVisitor visitor = new TDepthVisitor(plan); visitor.visit(); assertEquals("0 1 2 3 4 ", visitor.getJournal()); // Visit it dependency order visitor = new TDependVisitor(plan); visitor.visit(); assertEquals("0 1 2 3 4 ", visitor.getJournal()); // Test disconnect plan.disconnect(ops[2], ops[3]); assertEquals("Nodes: 0 1 2 3 4 FromEdges: 0->1 1->2 3->4 ToEdges: 1->0 2->1 4->3 ", plan.display()); // Test remove plan.remove(ops[1]); assertEquals("Nodes: 0 2 3 4 FromEdges: 3->4 ToEdges: 4->3 ", plan.display()); } @Test public void testDAG() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[6]; for (int i = 0; i < 6; i++) { ops[i] = new MultiOperator(Integer.toString(i)); plan.add(ops[i]); } plan.connect(ops[0], ops[2]); plan.connect(ops[1], ops[2]); plan.connect(ops[2], ops[3]); plan.connect(ops[3], ops[4]); plan.connect(ops[3], ops[5]); // Get roots should return ops[0] and ops[1] List<TOperator> roots = plan.getRoots(); assertEquals(2, roots.size()); assertTrue(roots.contains(ops[0])); assertTrue(roots.contains(ops[1])); // Get leaves should return ops[4] and ops[5] List<TOperator> leaves = plan.getLeaves(); assertEquals(2, leaves.size()); assertTrue(leaves.contains(ops[4])); assertTrue(leaves.contains(ops[5])); // Successor for ops[3] should be ops[4] and ops[5] List<TOperator> s = new ArrayList<TOperator>(plan.getSuccessors(ops[3])); assertEquals(2, s.size()); assertTrue(s.contains(ops[4])); assertTrue(s.contains(ops[5])); // Predecessor for ops[2] should be ops[0] and ops[1] s = new ArrayList<TOperator>(plan.getPredecessors(ops[2])); assertEquals(2, s.size()); assertTrue(s.contains(ops[0])); assertTrue(s.contains(ops[1])); assertEquals("Nodes: 0 1 2 3 4 5 FromEdges: 0->2 1->2 2->3 3->4 3->5 ToEdges: 2->0 2->1 3->2 4->3 5->3 ", plan.display()); // Visit it depth first TVisitor visitor = new TDepthVisitor(plan); visitor.visit(); // There are a number of valid patterns, make sure we found one of // them. String result = visitor.getJournal(); assertTrue(result.equals("1 2 3 4 5 0 ") || result.equals("1 2 3 5 4 0 ") || result.equals("0 2 3 4 5 1 ") || result.equals("0 2 3 5 4 1 ")); // Visit it dependency order visitor = new TDependVisitor(plan); visitor.visit(); result = visitor.getJournal(); assertTrue(result.equals("0 1 2 3 4 5 ") || result.equals("0 1 2 3 5 4 ")); // Test disconnect plan.disconnect(ops[2], ops[3]); assertEquals("Nodes: 0 1 2 3 4 5 FromEdges: 0->2 1->2 3->4 3->5 ToEdges: 2->0 2->1 4->3 5->3 ", plan.display()); // Test remove plan.remove(ops[2]); assertEquals("Nodes: 0 1 3 4 5 FromEdges: 3->4 3->5 ToEdges: 4->3 5->3 ", plan.display()); } // Test that we don't match when nodes don't match pattern. Will give // a pattern of S->S->M and a plan of S->M->S. @Test public void testOptimizerDifferentNodes() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new MultiOperator("2"); plan.add(ops[1]); ops[2] = new SingleOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[1]); plan.connect(ops[1], ops[2]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_1); rulePlan.connect(singleOperator_1, singleOperator_2); rulePlan.connect(singleOperator_2, multiOperator_1); AlwaysTransform transformer = new AlwaysTransform(plan); Rule<TOperator, TPlan> r = new Rule<TOperator, TPlan>(rulePlan, transformer, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r); optimizer.optimize(); assertFalse(transformer.mTransformed); } // Test that we don't match when edges don't match pattern. Will give // a pattern of S->S->M and a plan of S->S M. @Test public void testOptimizerDifferentEdges() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[1]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_1); rulePlan.connect(singleOperator_1, singleOperator_2); rulePlan.connect(singleOperator_2, multiOperator_1); AlwaysTransform transformer = new AlwaysTransform(plan); Rule<TOperator, TPlan> r = new Rule<TOperator, TPlan>(rulePlan, transformer, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r); optimizer.optimize(); assertFalse(transformer.mTransformed); } // Test that we match when appropriate. Will give // a pattern of S->S->M and a plan of S->S->M. @Test public void testOptimizerMatches() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[1]); plan.connect(ops[1], ops[2]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_1); rulePlan.connect(singleOperator_1, singleOperator_2); rulePlan.connect(singleOperator_2, multiOperator_1); AlwaysTransform transformer = new AlwaysTransform(plan); Rule<TOperator, TPlan> r = new Rule<TOperator, TPlan>(rulePlan, transformer, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r); optimizer.optimize(); assertTrue(transformer.mTransformed); } // Test that we match when the pattern says any. Will give // a pattern of any and a plan of S->S->M. @Test public void testOptimizerMatchesAny() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[1]); plan.connect(ops[1], ops[2]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, RuleOperator.NodeType.ANY_NODE, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); AlwaysTransform transformer = new AlwaysTransform(plan); Rule<TOperator, TPlan> r = new Rule<TOperator, TPlan>(rulePlan, transformer, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r); optimizer.optimize(); assertTrue(transformer.mTransformed); } // Test that we match when the whole plan doesn't match. Will give // a pattern of S->S->M and a plan of S->S->S->M. @Test public void testOptimizerMatchesPart() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[4]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new SingleOperator("3"); plan.add(ops[2]); ops[3] = new MultiOperator("4"); plan.add(ops[3]); plan.connect(ops[0], ops[1]); plan.connect(ops[1], ops[2]); plan.connect(ops[2], ops[3]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_1); rulePlan.connect(singleOperator_1, singleOperator_2); rulePlan.connect(singleOperator_2, multiOperator_1); AlwaysTransform transformer = new AlwaysTransform(plan); Rule<TOperator, TPlan> r = new Rule<TOperator, TPlan>(rulePlan, transformer, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r); optimizer.optimize(); assertTrue(transformer.mTransformed); } // Test that we match when a node is optional and the optional node is // present. Will give // a pattern of S->S->M (with second S optional) and a plan of S->S->M. @Test public void testOptimizerOptionalMatches() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[1]); plan.connect(ops[1], ops[2]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_1); rulePlan.connect(singleOperator_1, singleOperator_2); rulePlan.connect(singleOperator_2, multiOperator_1); AlwaysTransform transformer = new AlwaysTransform(plan); Rule<TOperator, TPlan> r = new Rule<TOperator, TPlan>(rulePlan, transformer, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r); optimizer.optimize(); assertTrue(transformer.mTransformed); } // Test that we do not match when a node is missing. Will give // a pattern of S->S->M and a plan of S->M. @Test public void testOptimizerOptionalMissing() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[2]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new MultiOperator("2"); plan.add(ops[1]); plan.connect(ops[0], ops[1]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_1); rulePlan.connect(singleOperator_1, singleOperator_2); rulePlan.connect(singleOperator_2, multiOperator_1); AlwaysTransform transformer = new AlwaysTransform(plan); Rule<TOperator, TPlan> r = new Rule<TOperator, TPlan>(rulePlan, transformer, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r); optimizer.optimize(); assertFalse(transformer.mTransformed); } // Test that even if we match, if check returns false then the optimization // is not done. @Test public void testCheck() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[1]); plan.connect(ops[1], ops[2]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_1); rulePlan.connect(singleOperator_1, singleOperator_2); rulePlan.connect(singleOperator_2, multiOperator_1); NeverTransform transformer = new NeverTransform(plan); Rule<TOperator, TPlan> r = new Rule<TOperator, TPlan>(rulePlan, transformer, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r); optimizer.optimize(); assertFalse(transformer.mTransformed); } @Test public void testReplace() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[6]; ops[0] = new MultiOperator("1"); plan.add(ops[0]); ops[1] = new MultiOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); ops[3] = new MultiOperator("4"); plan.add(ops[3]); ops[4] = new MultiOperator("5"); plan.add(ops[4]); plan.connect(ops[0], ops[2]); plan.connect(ops[1], ops[2]); plan.connect(ops[2], ops[3]); plan.connect(ops[2], ops[4]); ops[5] = new MultiOperator("6"); plan.replace(ops[2], ops[5]); assertEquals("Nodes: 1 2 4 5 6 FromEdges: 1->6 2->6 6->4 6->5 ToEdges: 4->6 5->6 6->1 6->2 ", plan.display()); } @Test public void testReplaceNoConnections() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[4]; ops[0] = new MultiOperator("1"); plan.add(ops[0]); ops[1] = new MultiOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[2]); ops[3] = new MultiOperator("4"); plan.replace(ops[1], ops[3]); assertEquals("Nodes: 1 3 4 FromEdges: 1->3 ToEdges: 3->1 ", plan.display()); } // Input and pattern are both // S S // \ / // M // Test that we match @Test public void testMultiInputPattern() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[2]); plan.connect(ops[1], ops[2]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_1); rulePlan.connect(singleOperator_1, multiOperator_1); rulePlan.connect(singleOperator_2, multiOperator_1); AlwaysTransform alwaysTransform = new AlwaysTransform(plan); Rule<TOperator, TPlan> r1 = new Rule<TOperator, TPlan>(rulePlan, alwaysTransform, "TestRule"); NeverTransform neverTransform = new NeverTransform(plan); Rule<TOperator, TPlan> r2 = new Rule<TOperator, TPlan>(rulePlan, neverTransform, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r1); optimizer.addRule(r2); optimizer.optimize(); assertTrue(alwaysTransform.mTransformed); assertTrue(alwaysTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); //default max iterations assertFalse(neverTransform.mTransformed); assertTrue(neverTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); //default max iterations } // Pattern // S M // \ / // M // Input has the roots swapped // M S // \ / // M // Test that we match @Test public void testIsomorphicMultiInputPattern() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new MultiOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[2]); plan.connect(ops[1], ops[2]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_2 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(multiOperator_1); rulePlan.add(multiOperator_2); rulePlan.connect(multiOperator_1, multiOperator_2); rulePlan.connect(singleOperator_1, multiOperator_2); AlwaysTransform alwaysTransform = new AlwaysTransform(plan); Rule<TOperator, TPlan> r1 = new Rule<TOperator, TPlan>(rulePlan, alwaysTransform, "TestRule"); NeverTransform neverTransform = new NeverTransform(plan); Rule<TOperator, TPlan> r2 = new Rule<TOperator, TPlan>(rulePlan, neverTransform, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r1); optimizer.addRule(r2); optimizer.optimize(); assertTrue(alwaysTransform.mTransformed); assertTrue(alwaysTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); //default max iterations assertFalse(neverTransform.mTransformed); assertTrue(neverTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); //default max iterations } // Input and pattern are both // M // || // M // Test that we match @Test public void testMultiInputMultiOutputPattern() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[2]; ops[0] = new MultiOperator("1"); plan.add(ops[0]); ops[1] = new MultiOperator("2"); plan.add(ops[1]); plan.connect(ops[0], ops[1]); plan.connect(ops[0], ops[1]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_2 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(multiOperator_1); rulePlan.add(multiOperator_2); rulePlan.connect(multiOperator_1, multiOperator_2); rulePlan.connect(multiOperator_1, multiOperator_2); AlwaysTransform alwaysTransform = new AlwaysTransform(plan); Rule<TOperator, TPlan> r1 = new Rule<TOperator, TPlan>(rulePlan, alwaysTransform, "TestRule"); NeverTransform neverTransform = new NeverTransform(plan); Rule<TOperator, TPlan> r2 = new Rule<TOperator, TPlan>(rulePlan, neverTransform, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r1); optimizer.addRule(r2); optimizer.optimize(); assertTrue(alwaysTransform.mTransformed); assertTrue(alwaysTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); //default max iterations assertFalse(neverTransform.mTransformed); assertTrue(neverTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); //default max iterations } // Input and pattern are both // M // / \ // S S // Test that we match @Test public void testMultiOutputPattern() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; ops[0] = new MultiOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new SingleOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[1]); plan.connect(ops[0], ops[2]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(multiOperator_1); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.connect(multiOperator_1, singleOperator_1); rulePlan.connect(multiOperator_1, singleOperator_2); AlwaysTransform alwaysTransform = new AlwaysTransform(plan); Rule<TOperator, TPlan> r1 = new Rule<TOperator, TPlan>(rulePlan, alwaysTransform, "TestRule"); NeverTransform neverTransform = new NeverTransform(plan); Rule<TOperator, TPlan> r2 = new Rule<TOperator, TPlan>(rulePlan, neverTransform, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r1); optimizer.addRule(r2); optimizer.optimize(); assertTrue(alwaysTransform.mTransformed); assertTrue(alwaysTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); //default max iterations assertFalse(neverTransform.mTransformed); assertTrue(neverTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); //default max iterations } // Pattern // M // / \ // S S // Input // M // | // S // Test that we don't match @Test public void testNegativeMultiOutputPattern() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[2]; ops[0] = new MultiOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); plan.connect(ops[0], ops[1]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(multiOperator_1); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.connect(multiOperator_1, singleOperator_1); rulePlan.connect(multiOperator_1, singleOperator_2); AlwaysTransform alwaysTransform = new AlwaysTransform(plan); Rule<TOperator, TPlan> r1 = new Rule<TOperator, TPlan>(rulePlan, alwaysTransform, "TestRule"); NeverTransform neverTransform = new NeverTransform(plan); Rule<TOperator, TPlan> r2 = new Rule<TOperator, TPlan>(rulePlan, neverTransform, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r1); optimizer.addRule(r2); optimizer.optimize(); assertFalse(alwaysTransform.mTransformed); assertTrue(alwaysTransform.getNumberOfChecks() == 0); //default max iterations assertFalse(neverTransform.mTransformed); assertTrue(neverTransform.getNumberOfChecks() == 0); //default max iterations } // Pattern // M // | // M // Input // M // || // M // Test that we don't match @Test public void testNegativeMultiOutputPattern1() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[2]; ops[0] = new MultiOperator("1"); plan.add(ops[0]); ops[1] = new MultiOperator("2"); plan.add(ops[1]); plan.connect(ops[0], ops[1]); plan.connect(ops[0], ops[1]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_2 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(multiOperator_1); rulePlan.add(multiOperator_2); rulePlan.connect(multiOperator_1, multiOperator_2); AlwaysTransform alwaysTransform = new AlwaysTransform(plan); Rule<TOperator, TPlan> r1 = new Rule<TOperator, TPlan>(rulePlan, alwaysTransform, "TestRule"); NeverTransform neverTransform = new NeverTransform(plan); Rule<TOperator, TPlan> r2 = new Rule<TOperator, TPlan>(rulePlan, neverTransform, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r1); optimizer.addRule(r2); optimizer.optimize(); assertFalse(alwaysTransform.mTransformed); assertTrue(alwaysTransform.getNumberOfChecks() == 0); //default max iterations assertFalse(neverTransform.mTransformed); assertTrue(neverTransform.getNumberOfChecks() == 0); //default max iterations } // Pattern // S S // \ / // M // Input // S S S S // \ / \ / // M M // Test that we match multiple instances in the disconnected graph @Test public void testMultipleMultiInputPatternInDisconnectedGraph() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[6]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[2]); plan.connect(ops[1], ops[2]); ops[3] = new SingleOperator("4"); plan.add(ops[3]); ops[4] = new SingleOperator("5"); plan.add(ops[4]); ops[5] = new MultiOperator("6"); plan.add(ops[5]); plan.connect(ops[3], ops[5]); plan.connect(ops[4], ops[5]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_1); rulePlan.connect(singleOperator_1, multiOperator_1); rulePlan.connect(singleOperator_2, multiOperator_1); AlwaysTransform alwaysTransform = new AlwaysTransform(plan); Rule<TOperator, TPlan> r1 = new Rule<TOperator, TPlan>(rulePlan, alwaysTransform, "TestRule"); NeverTransform neverTransform = new NeverTransform(plan); Rule<TOperator, TPlan> r2 = new Rule<TOperator, TPlan>(rulePlan, neverTransform, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r1); optimizer.addRule(r2); optimizer.optimize(); assertTrue(alwaysTransform.mTransformed); assertTrue(alwaysTransform.getNumberOfChecks() == (2* MAX_OPTIMIZATION_ITERATIONS)); assertFalse(neverTransform.mTransformed); assertTrue(neverTransform.getNumberOfChecks() == (2 * MAX_OPTIMIZATION_ITERATIONS)); } // Pattern is // S S // \ / // M // Input // S S S S // \ / \ / // M M // \ / // M // Test that we match multiple instances in a connected graph @Test public void testMultipleMultiInputPattern() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[7]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[2]); plan.connect(ops[1], ops[2]); ops[3] = new SingleOperator("4"); plan.add(ops[3]); ops[4] = new SingleOperator("5"); plan.add(ops[4]); ops[5] = new MultiOperator("6"); plan.add(ops[5]); plan.connect(ops[3], ops[5]); plan.connect(ops[4], ops[5]); ops[6] = new MultiOperator("7"); plan.add(ops[6]); plan.connect(ops[2], ops[6]); plan.connect(ops[5], ops[6]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_1); rulePlan.connect(singleOperator_1, multiOperator_1); rulePlan.connect(singleOperator_2, multiOperator_1); AlwaysTransform alwaysTransform = new AlwaysTransform(plan); Rule<TOperator, TPlan> r1 = new Rule<TOperator, TPlan>(rulePlan, alwaysTransform, "TestRule"); NeverTransform neverTransform = new NeverTransform(plan); Rule<TOperator, TPlan> r2 = new Rule<TOperator, TPlan>(rulePlan, neverTransform, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r1); optimizer.addRule(r2); optimizer.optimize(); assertTrue(alwaysTransform.mTransformed); assertTrue(alwaysTransform.getNumberOfChecks() == (2 * MAX_OPTIMIZATION_ITERATIONS)); assertFalse(neverTransform.mTransformed); assertTrue(neverTransform.getNumberOfChecks() == (2 * MAX_OPTIMIZATION_ITERATIONS)); } // Pattern is // S S // \ / // M // Test that we match only one instance in a connected graph @Test public void testSingleMultiInputPattern() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[6]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[2]); plan.connect(ops[1], ops[2]); ops[3] = new SingleOperator("4"); plan.add(ops[3]); ops[4] = new MultiOperator("5"); plan.add(ops[4]); plan.connect(ops[3], ops[4]); ops[5] = new MultiOperator("6"); plan.add(ops[5]); plan.connect(ops[4], ops[5]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_1); rulePlan.connect(singleOperator_1, multiOperator_1); rulePlan.connect(singleOperator_2, multiOperator_1); AlwaysTransform alwaysTransform = new AlwaysTransform(plan); Rule<TOperator, TPlan> r1 = new Rule<TOperator, TPlan>(rulePlan, alwaysTransform, "TestRule"); NeverTransform neverTransform = new NeverTransform(plan); Rule<TOperator, TPlan> r2 = new Rule<TOperator, TPlan>(rulePlan, neverTransform, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r1); optimizer.addRule(r2); optimizer.optimize(); assertTrue(alwaysTransform.mTransformed); assertTrue(alwaysTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); assertFalse(neverTransform.mTransformed); assertTrue(neverTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); } // Input and pattern are both // M // / \ // S S // \ / // M // Test that we match @Test public void testDiamondPattern() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[4]; ops[0] = new MultiOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new SingleOperator("3"); plan.add(ops[2]); ops[3] = new MultiOperator("4"); plan.add(ops[3]); plan.connect(ops[0], ops[1]); plan.connect(ops[0], ops[2]); plan.connect(ops[1], ops[3]); plan.connect(ops[2], ops[3]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_2 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(multiOperator_1); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_2); rulePlan.connect(multiOperator_1, singleOperator_1); rulePlan.connect(multiOperator_1, singleOperator_2); rulePlan.connect(singleOperator_1, multiOperator_2); rulePlan.connect(singleOperator_2, multiOperator_2); AlwaysTransform alwaysTransform = new AlwaysTransform(plan); Rule<TOperator, TPlan> r1 = new Rule<TOperator, TPlan>(rulePlan, alwaysTransform, "TestRule"); NeverTransform neverTransform = new NeverTransform(plan); Rule<TOperator, TPlan> r2 = new Rule<TOperator, TPlan>(rulePlan, neverTransform, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r1); optimizer.addRule(r2); optimizer.optimize(); assertTrue(alwaysTransform.mTransformed); assertTrue(alwaysTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); //default max iterations assertFalse(neverTransform.mTransformed); assertTrue(neverTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); //default max iterations } // Pattern // M // / \ // S S // \ / // M // Input has an additional edge from the bottom of the diamond // M // / \ // S S // \ / // M // | // S // Test that we match @Test public void testDiamondWithEdgePattern() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[5]; ops[0] = new MultiOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new SingleOperator("3"); plan.add(ops[2]); ops[3] = new MultiOperator("4"); plan.add(ops[3]); ops[4] = new SingleOperator("5"); plan.add(ops[4]); plan.connect(ops[0], ops[1]); plan.connect(ops[0], ops[2]); plan.connect(ops[1], ops[3]); plan.connect(ops[2], ops[3]); plan.connect(ops[3], ops[4]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_2 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(multiOperator_1); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_2); rulePlan.connect(multiOperator_1, singleOperator_1); rulePlan.connect(multiOperator_1, singleOperator_2); rulePlan.connect(singleOperator_1, multiOperator_2); rulePlan.connect(singleOperator_2, multiOperator_2); AlwaysTransform alwaysTransform = new AlwaysTransform(plan); Rule<TOperator, TPlan> r1 = new Rule<TOperator, TPlan>(rulePlan, alwaysTransform, "TestRule"); NeverTransform neverTransform = new NeverTransform(plan); Rule<TOperator, TPlan> r2 = new Rule<TOperator, TPlan>(rulePlan, neverTransform, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r1); optimizer.addRule(r2); optimizer.optimize(); assertTrue(alwaysTransform.mTransformed); assertTrue(alwaysTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); //default max iterations assertFalse(neverTransform.mTransformed); assertTrue(neverTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); //default max iterations } // Input and Pattern is // S S // \ / // M // | // M // / \ // S S // Test that we match once @Test public void testComplexInputPattern() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[6]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[2]); plan.connect(ops[1], ops[2]); ops[3] = new SingleOperator("4"); plan.add(ops[3]); ops[4] = new SingleOperator("5"); plan.add(ops[4]); ops[5] = new MultiOperator("6"); plan.add(ops[5]); plan.connect(ops[5], ops[3]); plan.connect(ops[5], ops[4]); plan.connect(ops[2], ops[5]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_1); rulePlan.connect(singleOperator_1, multiOperator_1); rulePlan.connect(singleOperator_2, multiOperator_1); RuleOperator singleOperator_3 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_4 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_2 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_3); rulePlan.add(singleOperator_4); rulePlan.add(multiOperator_2); rulePlan.connect(multiOperator_2, singleOperator_3); rulePlan.connect(multiOperator_2, singleOperator_4); rulePlan.connect(multiOperator_1, multiOperator_2); AlwaysTransform alwaysTransform = new AlwaysTransform(plan); Rule<TOperator, TPlan> r1 = new Rule<TOperator, TPlan>(rulePlan, alwaysTransform, "TestRule"); NeverTransform neverTransform = new NeverTransform(plan); Rule<TOperator, TPlan> r2 = new Rule<TOperator, TPlan>(rulePlan, neverTransform, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r1); optimizer.addRule(r2); optimizer.optimize(); assertTrue(alwaysTransform.mTransformed); assertTrue(alwaysTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); assertFalse(neverTransform.mTransformed); assertTrue(neverTransform.getNumberOfChecks() == MAX_OPTIMIZATION_ITERATIONS); } // Pattern is // S S // \ / // M // || // M // / \ // S S // Input is // S S // \ / // M // | // M // / \ // S S // Test that we don't match @Test public void testNegativeComplexInputPattern() throws Exception { // Build a plan TPlan plan = new TPlan(); TOperator[] ops = new TOperator[6]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[2]); plan.connect(ops[1], ops[2]); ops[3] = new SingleOperator("4"); plan.add(ops[3]); ops[4] = new SingleOperator("5"); plan.add(ops[4]); ops[5] = new MultiOperator("6"); plan.add(ops[5]); plan.connect(ops[5], ops[3]); plan.connect(ops[5], ops[4]); plan.connect(ops[2], ops[5]); // Create our rule RulePlan rulePlan = new RulePlan(); RuleOperator singleOperator_1 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_2 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_1 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_1); rulePlan.add(singleOperator_2); rulePlan.add(multiOperator_1); rulePlan.connect(singleOperator_1, multiOperator_1); rulePlan.connect(singleOperator_2, multiOperator_1); RuleOperator singleOperator_3 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator singleOperator_4 = new RuleOperator(SingleOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); RuleOperator multiOperator_2 = new RuleOperator(MultiOperator.class, new OperatorKey(SCOPE, nodeIdGen.getNextNodeId(SCOPE))); rulePlan.add(singleOperator_3); rulePlan.add(singleOperator_4); rulePlan.add(multiOperator_2); rulePlan.connect(multiOperator_2, singleOperator_3); rulePlan.connect(multiOperator_2, singleOperator_4); rulePlan.connect(multiOperator_1, multiOperator_2); rulePlan.connect(multiOperator_1, multiOperator_2); AlwaysTransform alwaysTransform = new AlwaysTransform(plan); Rule<TOperator, TPlan> r1 = new Rule<TOperator, TPlan>(rulePlan, alwaysTransform, "TestRule"); NeverTransform neverTransform = new NeverTransform(plan); Rule<TOperator, TPlan> r2 = new Rule<TOperator, TPlan>(rulePlan, neverTransform, "TestRule"); TOptimizer optimizer = new TOptimizer(plan); optimizer.addRule(r1); optimizer.addRule(r2); optimizer.optimize(); assertFalse(alwaysTransform.mTransformed); assertTrue(alwaysTransform.getNumberOfChecks() == 0); assertFalse(neverTransform.mTransformed); assertTrue(neverTransform.getNumberOfChecks() == 0); } //Swap two roots in a graph. Both the roots are disconnected //and are the only nodes in the graph @Test public void testSwapRootsInDisconnectedGraph() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[2]; for(int i = 0; i < ops.length; ++i) { ops[i] = new SingleOperator(Integer.toString(i)); plan.add(ops[i]); } plan.swap(ops[0], ops[1]); List<TOperator> roots = (ArrayList<TOperator>)plan.getRoots(); for(int i = 0; i < roots.size(); ++i) { assertEquals(roots.get(i), ops[i]); } } //Swap two nodes in a graph. //Input //S1->S2 //Ouput //S2->S1 //Swap again //Output //S1->S2 @Test public void testSimpleSwap() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[2]; for(int i = 0; i < ops.length; ++i) { ops[i] = new SingleOperator(Integer.toString(i)); plan.add(ops[i]); } plan.connect(ops[0], ops[1]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); plan.swap(ops[0], ops[1]); //planPrinter.visit(); List<TOperator> roots = (ArrayList<TOperator>)plan.getRoots(); assertEquals(roots.get(0), ops[1]); List<TOperator> rootSuccessors = plan.getSuccessors(roots.get(0)); assertEquals(rootSuccessors.get(0), ops[0]); List<TOperator> leaves = (ArrayList<TOperator>)plan.getLeaves(); assertEquals(leaves.get(0), ops[0]); List<TOperator> leafPredecessors = plan.getPredecessors(leaves.get(0)); assertEquals(leafPredecessors.get(0), ops[1]); plan.swap(ops[0], ops[1]); //planPrinter.visit(); roots = (ArrayList<TOperator>)plan.getRoots(); assertEquals(roots.get(0), ops[0]); rootSuccessors = plan.getSuccessors(roots.get(0)); assertEquals(rootSuccessors.get(0), ops[1]); leaves = (ArrayList<TOperator>)plan.getLeaves(); assertEquals(leaves.get(0), ops[1]); leafPredecessors = plan.getPredecessors(leaves.get(0)); assertEquals(leafPredecessors.get(0), ops[0]); } //Swap two nodes in a graph. //Swap S1 and S3 //Input //S1->S2->S3 //Intermediate Output //S3->S2->S1 //Output //S1->S2->S3 @Test public void testSimpleSwap2() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; for(int i = 0; i < ops.length; ++i) { ops[i] = new SingleOperator(Integer.toString(i)); plan.add(ops[i]); } plan.connect(ops[0], ops[1]); plan.connect(ops[1], ops[2]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); plan.swap(ops[0], ops[2]); //planPrinter.visit(); List<TOperator> roots = (ArrayList<TOperator>)plan.getRoots(); assertEquals(roots.get(0), ops[2]); List<TOperator> rootSuccessors = plan.getSuccessors(roots.get(0)); assertEquals(rootSuccessors.get(0), ops[1]); List<TOperator> leaves = (ArrayList<TOperator>)plan.getLeaves(); assertEquals(leaves.get(0), ops[0]); List<TOperator> leafPredecessors = plan.getPredecessors(leaves.get(0)); assertEquals(leafPredecessors.get(0), ops[1]); plan.swap(ops[0], ops[2]); //planPrinter.visit(); roots = (ArrayList<TOperator>)plan.getRoots(); assertEquals(roots.get(0), ops[0]); rootSuccessors = plan.getSuccessors(roots.get(0)); assertEquals(rootSuccessors.get(0), ops[1]); leaves = (ArrayList<TOperator>)plan.getLeaves(); assertEquals(leaves.get(0), ops[2]); leafPredecessors = plan.getPredecessors(leaves.get(0)); assertEquals(leafPredecessors.get(0), ops[1]); } //Swap two nodes in a graph and then swap it back again. //Swap S2 and S3 //Input //S1->S2->S3 //Intermediate Output //S1->S3->S2 //Output //S1->S2->S3 @Test public void testSimpleSwap3() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; for(int i = 0; i < ops.length; ++i) { ops[i] = new SingleOperator(Integer.toString(i)); plan.add(ops[i]); } plan.connect(ops[0], ops[1]); plan.connect(ops[1], ops[2]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); plan.swap(ops[1], ops[2]); //planPrinter.visit(); List<TOperator> roots = (ArrayList<TOperator>)plan.getRoots(); assertEquals(roots.get(0), ops[0]); List<TOperator> rootSuccessors = plan.getSuccessors(roots.get(0)); assertEquals(rootSuccessors.get(0), ops[2]); List<TOperator> leaves = (ArrayList<TOperator>)plan.getLeaves(); assertEquals(leaves.get(0), ops[1]); List<TOperator> leafPredecessors = plan.getPredecessors(leaves.get(0)); assertEquals(leafPredecessors.get(0), ops[2]); plan.swap(ops[1], ops[2]); //planPrinter.visit(); roots = (ArrayList<TOperator>)plan.getRoots(); assertEquals(roots.get(0), ops[0]); rootSuccessors = plan.getSuccessors(roots.get(0)); assertEquals(rootSuccessors.get(0), ops[1]); leaves = (ArrayList<TOperator>)plan.getLeaves(); assertEquals(leaves.get(0), ops[2]); leafPredecessors = plan.getPredecessors(leaves.get(0)); assertEquals(leafPredecessors.get(0), ops[1]); } //Swap two nodes in a graph and then swap it back again. //Swap S1 and S2 //Input //S1->S2->S3 //Intermediate Output //S2->S1->S3 //Output //S1->S2->S3 @Test public void testSimpleSwap4() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[3]; for(int i = 0; i < ops.length; ++i) { ops[i] = new SingleOperator(Integer.toString(i)); plan.add(ops[i]); } plan.connect(ops[0], ops[1]); plan.connect(ops[1], ops[2]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); plan.swap(ops[0], ops[1]); //planPrinter.visit(); List<TOperator> roots = (ArrayList<TOperator>)plan.getRoots(); assertEquals(roots.get(0), ops[1]); List<TOperator> rootSuccessors = plan.getSuccessors(roots.get(0)); assertEquals(rootSuccessors.get(0), ops[0]); List<TOperator> leaves = (ArrayList<TOperator>)plan.getLeaves(); assertEquals(leaves.get(0), ops[2]); List<TOperator> leafPredecessors = plan.getPredecessors(leaves.get(0)); assertEquals(leafPredecessors.get(0), ops[0]); plan.swap(ops[0], ops[1]); //planPrinter.visit(); roots = (ArrayList<TOperator>)plan.getRoots(); assertEquals(roots.get(0), ops[0]); rootSuccessors = plan.getSuccessors(roots.get(0)); assertEquals(rootSuccessors.get(0), ops[1]); leaves = (ArrayList<TOperator>)plan.getLeaves(); assertEquals(leaves.get(0), ops[2]); leafPredecessors = plan.getPredecessors(leaves.get(0)); assertEquals(leafPredecessors.get(0), ops[1]); } //Swap non-existent nodes in a graph and check for exceptions //Swap S1 and S4 //Swap S4 and S1 //Swap S5 and S4 //Swap S1 and null //Swap null and S1 //Swap null and null //Input //S1->S2->S3 S4 S5 @Test public void testNegativeSimpleSwap() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[5]; for(int i = 0; i < ops.length; ++i) { ops[i] = new SingleOperator(Integer.toString(i)); } for(int i = 0; i < ops.length - 2; ++i) { plan.add(ops[i]); } plan.connect(ops[0], ops[1]); plan.connect(ops[1], ops[2]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); try { plan.swap(ops[0], ops[3]); fail("Expected exception for node not in plan."); } catch (PlanException pe) { assertTrue(pe.getMessage().contains("not in the plan")); } try { plan.swap(ops[3], ops[0]); fail("Expected exception for node not in plan."); } catch (PlanException pe) { assertTrue(pe.getMessage().contains("not in the plan")); } try { plan.swap(ops[4], ops[3]); fail("Expected exception for node not in plan."); } catch (PlanException pe) { assertTrue(pe.getMessage().contains("not in the plan")); } try { plan.swap(ops[0], null); fail("Expected exception for having null as one of the inputs"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1092); } try { plan.swap(null, ops[0]); fail("Expected exception for having null as one of the inputs"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1092); } try { plan.swap(null, null); fail("Expected exception for having null as one of the inputs"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1092); } } //Swap nodes that have multiple inputs and multiple outs in a graph and check for exceptions //Input // S1 S2 // \ / // M1 // | // M2 // / \ // S3 S4 //Swap S1 and M1 //Swap M1 and S1 //Swap M1 and M2 //Swap M2 and M1 //Swap M2 and S3 //Swap S3 and M2 @Test public void testNegativeSimpleSwap1() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[6]; ops[0] = new SingleOperator("1"); plan.add(ops[0]); ops[1] = new SingleOperator("2"); plan.add(ops[1]); ops[2] = new MultiOperator("3"); plan.add(ops[2]); plan.connect(ops[0], ops[2]); plan.connect(ops[1], ops[2]); ops[3] = new SingleOperator("4"); plan.add(ops[3]); ops[4] = new SingleOperator("5"); plan.add(ops[4]); ops[5] = new MultiOperator("6"); plan.add(ops[5]); plan.connect(ops[5], ops[3]); plan.connect(ops[5], ops[4]); plan.connect(ops[2], ops[5]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); try { plan.swap(ops[0], ops[2]); fail("Expected exception for multi-input operator."); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1093); } try { plan.swap(ops[2], ops[0]); fail("Expected exception for multi-input operator."); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1093); } try { plan.swap(ops[2], ops[5]); fail("Expected exception for multi-input operator."); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1093); } try { plan.swap(ops[5], ops[2]); fail("Expected exception for multi-output operator."); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1093); } try { plan.swap(ops[5], ops[3]); fail("Expected exception for multi-output operator."); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1093); } try { plan.swap(ops[3], ops[5]); fail("Expected exception for multi-output operator."); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1093); } } //Push M11 before M10's inputs - 0 through 3 //Input // S1 S2 S3 S4 // \ | | / // M10 // / | \ // S5 M11 S6 // / | \ // S7 S8 S9 //Output when pushed before 1st input // S2 // | // S1 M11 S3 S4 // \ | | / // M10 // / / | \ \ // S5 S7 S8 S9 S6 @Test public void testpushBefore() throws Exception { for(int index = 0; index < 4; index++) { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[11]; for(int i = 0; i < ops.length - 2; ++i) { ops[i] = new SingleOperator(Integer.toString(i+1)); plan.add(ops[i]); } ops[9] = new MultiOperator("10"); plan.add(ops[9]); ops[10] = new MultiOperator("11"); plan.add(ops[10]); plan.connect(ops[0], ops[9]); plan.connect(ops[1], ops[9]); plan.connect(ops[2], ops[9]); plan.connect(ops[3], ops[9]); plan.connect(ops[9], ops[4]); plan.connect(ops[9], ops[10]); plan.connect(ops[9], ops[5]); plan.connect(ops[10], ops[6]); plan.connect(ops[10], ops[7]); plan.connect(ops[10], ops[8]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); plan.pushBefore(ops[9], ops[10], index) ; //planPrinter.visit(); Set<TOperator> rootSet = new HashSet<TOperator>(); rootSet.add(ops[0]); rootSet.add(ops[1]); rootSet.add(ops[2]); rootSet.add(ops[3]); Set<TOperator> expectedRootSet = new HashSet<TOperator>(plan.getRoots()); rootSet.retainAll(expectedRootSet); assertTrue(rootSet.size() == 4); Set<TOperator> leafSet = new HashSet<TOperator>(); leafSet.add(ops[4]); leafSet.add(ops[5]); leafSet.add(ops[6]); leafSet.add(ops[7]); leafSet.add(ops[8]); Set<TOperator> expectedLeafSet = new HashSet<TOperator>(plan.getLeaves()); leafSet.retainAll(expectedLeafSet); assertTrue(leafSet.size() == 5); List<TOperator> m10Predecessors = plan.getPredecessors(ops[9]); assertTrue(m10Predecessors.get(index) == ops[10]); List<TOperator> m11Predecessors = plan.getPredecessors(ops[10]); assertTrue(m11Predecessors.get(0) == ops[index]); } } //Push S5 and S6 before M10's input //Input // S1 S2 S3 S4 // \ | | / // M10 // / | \ // S5 M11 S6 // / | \ // S7 S8 S9 //Output when pushed before 1st input // S2 // | // S1 S5 S3 S4 // \ | | / // M10 // / \ // M11 S6 // / | \ // S7 S8 S9 @Test public void testpushBefore2() throws Exception { for(int outerIndex = 0; outerIndex < 2; outerIndex++) { for(int index = 0; index < 2; index++) { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[11]; for(int i = 0; i < ops.length - 2; ++i) { ops[i] = new SingleOperator(Integer.toString(i+1)); plan.add(ops[i]); } ops[9] = new MultiOperator("10"); plan.add(ops[9]); ops[10] = new MultiOperator("11"); plan.add(ops[10]); plan.connect(ops[0], ops[9]); plan.connect(ops[1], ops[9]); plan.connect(ops[2], ops[9]); plan.connect(ops[3], ops[9]); plan.connect(ops[9], ops[4]); plan.connect(ops[9], ops[10]); plan.connect(ops[9], ops[5]); plan.connect(ops[10], ops[6]); plan.connect(ops[10], ops[7]); plan.connect(ops[10], ops[8]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); int secondNodeIndex = outerIndex + 4; plan.pushBefore(ops[9], ops[secondNodeIndex], index) ; //planPrinter.visit(); Set<TOperator> rootSet = new HashSet<TOperator>(); rootSet.add(ops[0]); rootSet.add(ops[1]); rootSet.add(ops[2]); rootSet.add(ops[3]); Set<TOperator> expectedRootSet = new HashSet<TOperator>(plan.getRoots()); rootSet.retainAll(expectedRootSet); assertTrue(rootSet.size() == 4); Set<TOperator> leafSet = new HashSet<TOperator>(); for(int leafIndex = 4; leafIndex < 9; ++leafIndex) { if(leafIndex != secondNodeIndex) { leafSet.add(ops[leafIndex]); } } Set<TOperator> expectedLeafSet = new HashSet<TOperator>(plan.getLeaves()); leafSet.retainAll(expectedLeafSet); assertTrue(leafSet.size() == 4); List<TOperator> outerIndexNodePredecessors = plan.getPredecessors(ops[secondNodeIndex]); assertTrue(outerIndexNodePredecessors.get(0) == ops[index]); List<TOperator> m10Predecessors = plan.getPredecessors(ops[9]); assertTrue(m10Predecessors.get(index) == ops[secondNodeIndex]); } } } //Push non-existent nodes in a graph and check for exceptions //Push S1 after S4 //Push S4 after S1 //Push S5 after S4 //Push S1 after null //Push null after S1 //Push null after null //Input //S1->S2->S3 S4 S5 @Test public void testNegativePushBefore() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[5]; for(int i = 0; i < ops.length; ++i) { ops[i] = new SingleOperator(Integer.toString(i)); } for(int i = 0; i < ops.length - 2; ++i) { plan.add(ops[i]); } plan.connect(ops[0], ops[1]); plan.connect(ops[1], ops[2]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); try { plan.pushBefore(ops[0], ops[3], 0); fail("Expected exception for node not in plan."); } catch (PlanException pe) { assertTrue(pe.getMessage().contains("not in the plan")); } try { plan.pushBefore(ops[3], ops[0], 0); fail("Expected exception for node not in plan."); } catch (PlanException pe) { assertTrue(pe.getMessage().contains("not in the plan")); } try { plan.pushBefore(ops[4], ops[3], 0); fail("Expected exception for node not in plan."); } catch (PlanException pe) { assertTrue(pe.getMessage().contains("not in the plan")); } try { plan.pushBefore(ops[0], null, 0); fail("Expected exception for having null as one of the inputs"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1085); } try { plan.pushBefore(null, ops[0], 0); fail("Expected exception for having null as one of the inputs"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1085); } try { plan.pushBefore(null, null, 0); fail("Expected exception for having null as one of the inputs"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1085); } } //Negative test cases //Input // S1 S2 S3 S4 // \ | | / // M10 // / | \ // S5 M11 S6 // / | \ // S7 S8 S9 @Test public void testNegativePushBefore2() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[11]; for(int i = 0; i < ops.length - 2; ++i) { ops[i] = new SingleOperator(Integer.toString(i+1)); plan.add(ops[i]); } ops[9] = new MultiOperator("10"); plan.add(ops[9]); ops[10] = new MultiOperator("11"); plan.add(ops[10]); plan.connect(ops[0], ops[9]); plan.connect(ops[1], ops[9]); plan.connect(ops[2], ops[9]); plan.connect(ops[3], ops[9]); plan.connect(ops[9], ops[4]); plan.connect(ops[9], ops[10]); plan.connect(ops[9], ops[5]); plan.connect(ops[10], ops[6]); plan.connect(ops[10], ops[7]); plan.connect(ops[10], ops[8]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); try { plan.pushBefore(ops[0], ops[9], 0) ; fail("Expected exception for first operator having null predecessors"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1086); } try { plan.pushBefore(ops[10], ops[6], 0) ; fail("Expected exception for first operator having one predecessor"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1086); } try { plan.pushBefore(ops[9], ops[10], 4) ; fail("Expected exception for inputNum exceeding number of predecessors"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1087); } try { plan.pushBefore(ops[9], ops[0], 0) ; fail("Expected exception for second operator having null predecessors"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1088); } try { plan.pushBefore(ops[9], ops[9], 0) ; fail("Expected exception for second operator having more than one predecessor"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1088); } try { plan.pushBefore(ops[9], ops[8], 0) ; fail("Expected exception for second operator not being a successor of first operator"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1089); } plan.disconnect(ops[9], ops[4]); plan.disconnect(ops[9], ops[5]); MultiInputSingleOutputOperator miso = new MultiInputSingleOutputOperator("12"); plan.replace(ops[9], miso); try { plan.pushBefore(miso, ops[10], 0) ; fail("Expected exception for trying to connect multiple outputs to the first operator"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1091); } } //Push M10 after M11's outputs - 0 through 2 //Input // S1 S2 S3 S4 // \ | | / // M10 // S5 | S6 // \ | / // M11 // / | \ // S7 S8 S9 //Output when pushed after 1st output // S5 S1 S2 S3 S4 S6 // \ \ \ / / / // M10 // / | \ // S7 M11 S9 // | // S8 @Test public void testpushAfter() throws Exception { for(int index = 0; index < 3; index++) { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[11]; for(int i = 0; i < ops.length - 2; ++i) { ops[i] = new SingleOperator(Integer.toString(i+1)); plan.add(ops[i]); } ops[9] = new MultiOperator("10"); plan.add(ops[9]); ops[10] = new MultiOperator("11"); plan.add(ops[10]); plan.connect(ops[0], ops[9]); plan.connect(ops[1], ops[9]); plan.connect(ops[2], ops[9]); plan.connect(ops[3], ops[9]); plan.connect(ops[9], ops[10]); plan.connect(ops[4], ops[10]); plan.connect(ops[5], ops[10]); plan.connect(ops[10], ops[6]); plan.connect(ops[10], ops[7]); plan.connect(ops[10], ops[8]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); plan.pushAfter(ops[10], ops[9], index) ; //planPrinter.visit(); Set<TOperator> rootSet = new HashSet<TOperator>(); rootSet.add(ops[0]); rootSet.add(ops[1]); rootSet.add(ops[2]); rootSet.add(ops[3]); rootSet.add(ops[4]); rootSet.add(ops[5]); Set<TOperator> expectedRootSet = new HashSet<TOperator>(plan.getRoots()); rootSet.retainAll(expectedRootSet); assertTrue(rootSet.size() == 6); Set<TOperator> leafSet = new HashSet<TOperator>(); leafSet.add(ops[6]); leafSet.add(ops[7]); leafSet.add(ops[8]); Set<TOperator> expectedLeafSet = new HashSet<TOperator>(plan.getLeaves()); leafSet.retainAll(expectedLeafSet); assertTrue(leafSet.size() == 3); List<TOperator> m10Successors = plan.getSuccessors(ops[9]); assertTrue(m10Successors.get(0) == ops[index + 6]); List<TOperator> m11Successors = plan.getSuccessors(ops[10]); assertTrue(m11Successors.get(index) == ops[9]); } } //Push S5 and S6 after M11's outputs - 0 through 2 //Input // S1 S2 S3 S4 // \ | | / // M10 // S5 | S6 // \ | / // M11 // / | \ // S7 S8 S9 //Output when S5 is pushed after 1st output // S1 S2 S3 S4 // \ | | / // M10 // | S6 // | / // M11 // / | \ // S7 S5 S9 // | // S8 @Test public void testpushAfter1() throws Exception { for(int outerIndex = 0; outerIndex < 2; outerIndex++) { for(int index = 0; index < 3; index++) { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[11]; for(int i = 0; i < ops.length - 2; ++i) { ops[i] = new SingleOperator(Integer.toString(i+1)); plan.add(ops[i]); } ops[9] = new MultiOperator("10"); plan.add(ops[9]); ops[10] = new MultiOperator("11"); plan.add(ops[10]); plan.connect(ops[0], ops[9]); plan.connect(ops[1], ops[9]); plan.connect(ops[2], ops[9]); plan.connect(ops[3], ops[9]); plan.connect(ops[9], ops[10]); plan.connect(ops[4], ops[10]); plan.connect(ops[5], ops[10]); plan.connect(ops[10], ops[6]); plan.connect(ops[10], ops[7]); plan.connect(ops[10], ops[8]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); int secondNodeIndex = outerIndex + 4; plan.pushAfter(ops[10], ops[secondNodeIndex], index) ; //planPrinter.visit(); Set<TOperator> rootSet = new HashSet<TOperator>(); rootSet.add(ops[0]); rootSet.add(ops[1]); rootSet.add(ops[2]); rootSet.add(ops[3]); for(int rootIndex = 0; rootIndex < 6; ++rootIndex) { if(rootIndex != secondNodeIndex) { rootSet.add(ops[rootIndex]); } } Set<TOperator> expectedRootSet = new HashSet<TOperator>(plan.getRoots()); rootSet.retainAll(expectedRootSet); assertTrue(rootSet.size() == 5); Set<TOperator> leafSet = new HashSet<TOperator>(); leafSet.add(ops[6]); leafSet.add(ops[7]); leafSet.add(ops[8]); Set<TOperator> expectedLeafSet = new HashSet<TOperator>(plan.getLeaves()); leafSet.retainAll(expectedLeafSet); assertTrue(leafSet.size() == 3); List<TOperator> outerIndexNodeSuccessors = plan.getSuccessors(ops[secondNodeIndex]); assertTrue(outerIndexNodeSuccessors.get(0) == ops[index + 6]); List<TOperator> m11Successors = plan.getSuccessors(ops[10]); assertTrue(m11Successors.get(index) == ops[secondNodeIndex]); } } } //Push non-existent nodes in a graph and check for exceptions //Push S1 after S4 //Push S4 after S1 //Push S5 after S4 //Push S1 after null //Push null after S1 //Push null after null //Input //S1->S2->S3 S4 S5 @Test public void testNegativePushAfter() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[5]; for(int i = 0; i < ops.length; ++i) { ops[i] = new SingleOperator(Integer.toString(i)); } for(int i = 0; i < ops.length - 2; ++i) { plan.add(ops[i]); } plan.connect(ops[0], ops[1]); plan.connect(ops[1], ops[2]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); try { plan.pushAfter(ops[0], ops[3], 0); fail("Expected exception for node not in plan."); } catch (PlanException pe) { assertTrue(pe.getMessage().contains("not in the plan")); } try { plan.pushAfter(ops[3], ops[0], 0); fail("Expected exception for node not in plan."); } catch (PlanException pe) { assertTrue(pe.getMessage().contains("not in the plan")); } try { plan.pushAfter(ops[4], ops[3], 0); fail("Expected exception for node not in plan."); } catch (PlanException pe) { assertTrue(pe.getMessage().contains("not in the plan")); } try { plan.pushAfter(ops[0], null, 0); fail("Expected exception for having null as one of the inputs"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1085); } try { plan.pushAfter(null, ops[0], 0); fail("Expected exception for having null as one of the inputs"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1085); } try { plan.pushAfter(null, null, 0); fail("Expected exception for having null as one of the inputs"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1085); } } //Negative test cases //Input // S1 S2 S3 S4 // \ | | / // M10 // S5 | S6 // \ | / // M11 // / | \ // S7 S8 S9 @Test public void testNegativePushAfter2() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[11]; for(int i = 0; i < ops.length - 2; ++i) { ops[i] = new SingleOperator(Integer.toString(i+1)); plan.add(ops[i]); } ops[9] = new MultiOperator("10"); plan.add(ops[9]); ops[10] = new MultiOperator("11"); plan.add(ops[10]); plan.connect(ops[0], ops[9]); plan.connect(ops[1], ops[9]); plan.connect(ops[2], ops[9]); plan.connect(ops[3], ops[9]); plan.connect(ops[9], ops[10]); plan.connect(ops[4], ops[10]); plan.connect(ops[5], ops[10]); plan.connect(ops[10], ops[6]); plan.connect(ops[10], ops[7]); plan.connect(ops[10], ops[8]); //PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(System.err, plan); //planPrinter.visit(); try { plan.pushAfter(ops[6], ops[9], 0) ; fail("Expected exception for first operator having null successors"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1086); } try { plan.pushAfter(ops[0], ops[9], 0) ; fail("Expected exception for first operator having no inputs"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1088); } try { plan.pushAfter(ops[9], ops[6], 0) ; fail("Expected exception for first operator having one successor"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1086); } try { plan.pushAfter(ops[6], ops[10], 0) ; fail("Expected exception for first operator having one successors"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1086); } try { plan.pushAfter(ops[10], ops[9], 4) ; fail("Expected exception for outputNum exceeding the number of outputs of first operator"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1087); } try { plan.pushAfter(ops[10], ops[6], 0) ; fail("Expected exception for second operator having null successors"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1088); } try { plan.pushAfter(ops[10], ops[10], 0) ; fail("Expected exception for second operator having more than one successor"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1088); } try { plan.pushAfter(ops[10], ops[0], 0) ; fail("Expected exception for second operator not being a predecessor of first operator"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1089); } plan.disconnect(ops[4], ops[10]); plan.disconnect(ops[5], ops[10]); MultiOutputSingleInputOperator mosi = new MultiOutputSingleInputOperator("12"); plan.replace(ops[10], mosi); try { plan.pushAfter(mosi, ops[9], 0) ; fail("Expected exception for trying to connect multiple inputs to the first operator"); } catch (PlanException pe) { assertTrue(pe.getErrorCode() == 1091); } } // See PIG-1212 @Test public void testPushBefore2() throws Exception { TPlan plan = new TPlan(); TOperator[] ops = new TOperator[6]; ops[0] = new SingleOperator("Load0"); ops[1] = new SingleOperator("Load1"); ops[2] = new SingleOperator("ForEach0"); ops[3] = new SingleOperator("ForEach1"); ops[4] = new MultiOperator("Join"); ops[5] = new SingleOperator("Filter"); for (int i=0;i<6;i++) plan.add(ops[i]); plan.connect(ops[0], ops[2]); plan.connect(ops[1], ops[3]); plan.connect(ops[2], ops[4]); plan.connect(ops[3], ops[4]); plan.connect(ops[4], ops[5]); try { plan.pushBefore(ops[4], ops[5], 0); ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos); PlanPrinter<TOperator, TPlan> planPrinter = new PlanPrinter<TOperator, TPlan>(ps, plan); planPrinter.visit(); assertFalse(baos.toString().equals("")); } catch (PlanException pe) { } } }