/** * Copyright 2010 JBoss Inc * * Licensed 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.drools.reteoo.test; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import junit.framework.AssertionFailedError; import org.antlr.runtime.ANTLRInputStream; import org.antlr.runtime.ANTLRReaderStream; import org.antlr.runtime.ANTLRStringStream; import org.antlr.runtime.CommonTokenStream; import org.antlr.runtime.RecognitionException; import org.antlr.runtime.TokenStream; import org.antlr.runtime.tree.CommonTree; import org.antlr.runtime.tree.CommonTreeNodeStream; import org.antlr.runtime.tree.Tree; import org.drools.RuleBaseConfiguration; import org.drools.common.InternalFactHandle; import org.drools.common.InternalWorkingMemory; import org.drools.common.PropagationContextImpl; import org.drools.core.util.Iterator; import org.drools.core.util.ObjectHashMap; import org.drools.core.util.ObjectHashMap.ObjectEntry; import org.drools.reteoo.AccumulateNode; import org.drools.reteoo.BetaMemory; import org.drools.reteoo.BetaNode; import org.drools.reteoo.LeftTuple; import org.drools.reteoo.LeftTupleMemory; import org.drools.reteoo.LeftTupleSink; import org.drools.reteoo.ModifyPreviousTuples; import org.drools.reteoo.ObjectSink; import org.drools.reteoo.ObjectTypeNode; import org.drools.reteoo.ReteooRuleBase; import org.drools.reteoo.RightInputAdapterNode; import org.drools.reteoo.RightTuple; import org.drools.reteoo.RightTupleMemory; import org.drools.reteoo.RuleTerminalNode; import org.drools.reteoo.Sink; import org.drools.reteoo.AccumulateNode.AccumulateMemory; import org.drools.reteoo.builder.BuildContext; import org.drools.reteoo.test.dsl.AccumulateNodeStep; import org.drools.reteoo.test.dsl.BetaNodeStep; import org.drools.reteoo.test.dsl.BindingStep; import org.drools.reteoo.test.dsl.DSLMock; import org.drools.reteoo.test.dsl.DslStep; import org.drools.reteoo.test.dsl.EvalNodeStep; import org.drools.reteoo.test.dsl.ExistsNodeStep; import org.drools.reteoo.test.dsl.FactsStep; import org.drools.reteoo.test.dsl.JoinNodeStep; import org.drools.reteoo.test.dsl.LeftInputAdapterNodeStep; import org.drools.reteoo.test.dsl.LeftTupleSinkStep; import org.drools.reteoo.test.dsl.MockitoHelper; import org.drools.reteoo.test.dsl.NodeTestCase; import org.drools.reteoo.test.dsl.NodeTestCaseResult; import org.drools.reteoo.test.dsl.NodeTestDef; import org.drools.reteoo.test.dsl.NotNodeStep; import org.drools.reteoo.test.dsl.ObjectTypeNodeStep; import org.drools.reteoo.test.dsl.RIANodeStep; import org.drools.reteoo.test.dsl.ReteTesterHelper; import org.drools.reteoo.test.dsl.RuleTerminalNodeStep; import org.drools.reteoo.test.dsl.Step; import org.drools.reteoo.test.dsl.WithStep; import org.drools.reteoo.test.dsl.NodeTestCaseResult.NodeTestResult; import org.drools.reteoo.test.dsl.NodeTestCaseResult.Result; import org.drools.reteoo.test.parser.NodeTestDSLLexer; import org.drools.reteoo.test.parser.NodeTestDSLParser; import org.drools.reteoo.test.parser.NodeTestDSLTree; import org.drools.reteoo.test.parser.NodeTestDSLParser.compilation_unit_return; import org.drools.spi.PropagationContext; import org.junit.runner.Description; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunNotifier; import org.junit.runner.notification.StoppedByUserException; import org.mvel2.MVEL; public class ReteDslTestEngine { private static final String OBJECT_TYPE_NODE = "ObjectTypeNode"; private static final String LEFT_INPUT_ADAPTER_NODE = "LeftInputAdapterNode"; private static final String BINDING = "Binding"; private static final String JOIN_NODE = "JoinNode"; private static final String NOT_NODE = "NotNode"; private static final String EXISTS_NODE = "ExistsNode"; private static final String ACCUMULATE_NODE = "AccumulateNode"; private static final String RULE_TERMINAL_NODE = "RuleTerminalNode"; private static final String EVAL_NODE = "EvalNode"; private static final String WITH = "With"; private static final String FACTS = "Facts"; private static final String RIGHT_INPUT_ADAPTER_NODE = "RightInputAdapterNode"; private static final String LEFT_TUPLE_SINK_STEP = "LeftTupleSink"; private static final String BETA_NODE_STEP = "BetaNodeStep"; private ReteTesterHelper reteTesterHelper; private Map<String, Object> steps; public ReteDslTestEngine() { this.reteTesterHelper = new ReteTesterHelper(); this.steps = new HashMap<String, Object>(); this.steps.put(OBJECT_TYPE_NODE, new ObjectTypeNodeStep( this.reteTesterHelper)); this.steps.put(LEFT_INPUT_ADAPTER_NODE, new LeftInputAdapterNodeStep( this.reteTesterHelper)); this.steps.put(BINDING, new BindingStep(this.reteTesterHelper)); this.steps.put(JOIN_NODE, new JoinNodeStep(this.reteTesterHelper)); this.steps.put(NOT_NODE, new NotNodeStep(this.reteTesterHelper)); this.steps.put(EXISTS_NODE, new ExistsNodeStep(this.reteTesterHelper)); this.steps.put(ACCUMULATE_NODE, new AccumulateNodeStep( this.reteTesterHelper)); this.steps.put(RULE_TERMINAL_NODE, new RuleTerminalNodeStep( this.reteTesterHelper)); this.steps.put(EVAL_NODE, new EvalNodeStep(this.reteTesterHelper)); this.steps.put(RIGHT_INPUT_ADAPTER_NODE, new RIANodeStep( this.reteTesterHelper)); this.steps.put(FACTS, new FactsStep(this.reteTesterHelper)); this.steps.put(WITH, new WithStep(this.reteTesterHelper)); this.steps.put(LEFT_TUPLE_SINK_STEP, new LeftTupleSinkStep( this.reteTesterHelper)); this.steps.put(BETA_NODE_STEP, new BetaNodeStep(this.reteTesterHelper)); } public NodeTestCaseResult run(NodeTestCase testCase, RunNotifier notifier) { if (testCase == null || testCase.hasErrors()) { throw new IllegalArgumentException( "Impossible to execute test case due to existing errors: " + testCase.getErrors()); } if (notifier == null) { notifier = EmptyNotifier.INSTANCE; } this.reteTesterHelper.addImports(testCase.getImports()); NodeTestCaseResult result = new NodeTestCaseResult(testCase); for (NodeTestDef test : testCase.getTests()) { notifier.fireTestStarted(test.getDescription()); NodeTestResult testResult = createTestResult(test, null); try { testResult = run(testCase, test); switch (testResult.result) { case SUCCESS: notifier.fireTestFinished(test.getDescription()); break; case ERROR: case FAILURE: notifier.fireTestFailure(new Failure(test.getDescription(), new AssertionError(testResult.errorMsgs))); break; } } catch (Throwable e) { notifier.fireTestFailure(new Failure(test.getDescription(), e)); } result.add(testResult); } return result; } private NodeTestResult run(NodeTestCase testCase, NodeTestDef test) { Map<String, Object> context = createContext(testCase); NodeTestResult result = createTestResult(test, context); try { // run setup run(context, testCase.getSetup(), result); // run test run(context, test.getSteps(), result); // run tearDown run(context, testCase.getTearDown(), result); result.result = Result.SUCCESS; } catch (Throwable e) { result.result = Result.ERROR; result.errorMsgs.add(e.toString()); } return result; } private NodeTestResult createTestResult(NodeTestDef test, Map<String, Object> context) { NodeTestResult result = new NodeTestResult(test, Result.NOT_EXECUTED, context, new LinkedList<String>()); return result; } private Map<String, Object> createContext(NodeTestCase testCase) { Map<String, Object> context = new HashMap<String, Object>(); context.put("TestCase", testCase); RuleBaseConfiguration conf = new RuleBaseConfiguration(); ReteooRuleBase rbase = new ReteooRuleBase("ID", conf); BuildContext buildContext = new BuildContext(rbase, rbase .getReteooBuilder().getIdGenerator()); context.put("BuildContext", buildContext); context .put("ClassFieldAccessorStore", this.reteTesterHelper .getStore()); InternalWorkingMemory wm = (InternalWorkingMemory) rbase .newStatefulSession(true); context.put("WorkingMemory", wm); return context; } public Map<String, Object> run(Map<String, Object> context, List<DslStep> steps, NodeTestResult result) { InternalWorkingMemory wm = (InternalWorkingMemory) context .get("WorkingMemory"); for (DslStep step : steps) { String name = step.getName(); Object object = this.steps.get(name); if (object != null && object instanceof Step) { Step stepImpl = (Step) object; try { stepImpl.execute(context, step.getCommands()); } catch (Exception e) { throw new IllegalArgumentException("line " + step.getLine() + ": unable to execute step " + step, e); } } else if ("assert".equals(name.trim())) { assertObject(step, context, wm); } else if ("retract".equals(name.trim())) { retractObject(step, context, wm); } else if ("modify".equals(name.trim())) { modifyObject(step, context, wm); } else { Object node = context.get(name.trim()); if (node == null) { throw new IllegalArgumentException("line " + step.getLine() + ": step " + name + " does not exist"); } if (node instanceof DSLMock) { // it is a mock MockitoHelper.process(step, (LeftTupleSink) node, context, wm); } else if (node instanceof BetaNode) { betaNode(step, (BetaNode) node, context, wm); } else if (node instanceof RightInputAdapterNode) { riaNode(step, (RightInputAdapterNode) node, context, wm); } else if (node instanceof RuleTerminalNode) { ruleTerminalNode(step, (RuleTerminalNode) node, context, wm); } else { throw new IllegalArgumentException("line " + step.getLine() + ": unknown node " + node); } } } return context; } private void betaNode(DslStep step, BetaNode node, Map<String, Object> context, InternalWorkingMemory wm) { try { List<String[]> cmds = step.getCommands(); List<InternalFactHandle> handles = (List<InternalFactHandle>) context .get("Handles"); BetaMemory memory = null; if (node instanceof AccumulateNode) { AccumulateMemory accmem = (AccumulateMemory) wm .getNodeMemory(node); memory = accmem.betaMemory; } else { memory = (BetaMemory) wm.getNodeMemory(node); } for (String[] cmd : cmds) { if (cmd[0].equals("leftMemory")) { String args = cmd[1]; String listString = args.replaceAll("h(\\d+)", "h[$1]"); Map<String, Object> vars = new HashMap<String, Object>(); vars.put("h", handles); List<?> expectedLeftTuples = (List<?>) MVEL.eval( listString, vars); LeftTupleMemory leftMemory = memory.getLeftTupleMemory(); if (expectedLeftTuples.isEmpty() && leftMemory.size() != 0) { throw new AssertionFailedError("line " + step.getLine() + ": left Memory expected [] actually " + print(leftMemory)); } else if (expectedLeftTuples.isEmpty() && leftMemory.size() == 0) { return; } // we always lookup from the first element, in case it's // indexed List<InternalFactHandle> first = (List<InternalFactHandle>) expectedLeftTuples .get(0); LeftTuple firstTuple = new LeftTuple(first.get(0), null, false); for (int i = 1; i < first.size(); i++) { firstTuple = new LeftTuple(firstTuple, null, false); } List<LeftTuple> leftTuples = new ArrayList<LeftTuple>(); for (LeftTuple leftTuple = memory.getLeftTupleMemory() .getFirst(firstTuple); leftTuple != null; leftTuple = (LeftTuple) leftTuple .getNext()) { leftTuples.add(leftTuple); } // lgomes: Need to sort the tuples here, because we might have asserted things // in the wrong order, because linking a node's side means populating its memory // from the OTN which stores things in a hash-set, so insertion order is not kept. Collections.sort(leftTuples, new LeftTupleComparator()); List<List<InternalFactHandle>> actualLeftTuples = getHandlesList(leftTuples); if (!expectedLeftTuples.equals(actualLeftTuples)) { throw new AssertionFailedError("line " + step.getLine() + ": left Memory expected " + print(expectedLeftTuples) + " actually " + print(actualLeftTuples)); } } else if (cmd[0].equals("rightMemory")) { String args = cmd[1]; String listString = args.replaceAll("h(\\d+)", "h[$1]"); Map<String, Object> vars = new HashMap<String, Object>(); vars.put("h", handles); List<?> expectedFactHandles = (List<?>) MVEL.eval( listString, vars); RightTupleMemory rightMemory = memory.getRightTupleMemory(); if (expectedFactHandles.isEmpty() && rightMemory.size() != 0) { throw new AssertionFailedError("line " + step.getLine() + ": right Memory expected [] actually " + rightMemory); } else if (expectedFactHandles.isEmpty() && rightMemory.size() == 0) { return; } RightTuple first = new RightTuple( (InternalFactHandle) expectedFactHandles.get(0)); List<RightTuple> actualRightTuples = new ArrayList<RightTuple>(); for (RightTuple rightTuple = memory.getRightTupleMemory() .getFirst(first); rightTuple != null; rightTuple = (RightTuple) rightTuple .getNext()) { actualRightTuples.add(rightTuple); } if (expectedFactHandles.size() != actualRightTuples.size()) { throw new AssertionFailedError("line " + step.getLine() + ": right Memory expected " + print(expectedFactHandles) + " actually " + print(actualRightTuples)); } for (int i = 0, length = actualRightTuples.size(); i < length; i++) { if (expectedFactHandles.get(i) != actualRightTuples .get(i).getFactHandle()) { throw new AssertionFailedError("line " + step.getLine() + ": right Memory expected [" + print(expectedFactHandles) + "] actually [" + print(actualRightTuples) + "]"); } } } else { throw new IllegalArgumentException("line " + step.getLine() + ": command does not exist " + Arrays.toString(cmd)); } } } catch (Exception e) { throw new IllegalArgumentException("line " + step.getLine() + ": unable to execute step " + step, e); } } private List<List<InternalFactHandle>> getHandlesList( List<LeftTuple> leftTuples) { List<List<InternalFactHandle>> actualLeftTuples = new ArrayList<List<InternalFactHandle>>( leftTuples.size()); for (LeftTuple leftTuple : leftTuples) { List<InternalFactHandle> tupleHandles = Arrays .asList(leftTuple.toFactHandles()); actualLeftTuples.add(tupleHandles); } return actualLeftTuples; } private String print(LeftTupleMemory leftMemory) { List<LeftTuple> tuples = new ArrayList<LeftTuple>(); Iterator it = leftMemory.iterator(); for ( LeftTuple tuple = (LeftTuple) it.next(); tuple != null; tuple = (LeftTuple) it.next() ) { tuples.add(tuple); } Collections.sort(tuples, new LeftTupleComparator()); return print(getHandlesList(tuples)); } /** Provides better error messages. */ protected String print(List<?> tuples) { StringBuilder b = new StringBuilder(); for (java.util.Iterator iterator = tuples.iterator(); iterator.hasNext();) { Object tuple = (Object) iterator.next(); if (tuple instanceof List<?>) { b.append("["); b.append(print((List<?>) tuple)); b.append("]"); } else if (tuple instanceof InternalFactHandle){ InternalFactHandle h = (InternalFactHandle) tuple; b.append("h").append(h.getId()-1); } else if (tuple instanceof RightTuple){ InternalFactHandle h = (InternalFactHandle) ((RightTuple) tuple).getFactHandle(); b.append("h").append(h.getId()-1); } if(iterator.hasNext()) b.append(", "); } if (b.length() == 0) return "[]"; return b.toString(); } private void riaNode(DslStep step, RightInputAdapterNode node, Map<String, Object> context, InternalWorkingMemory wm) { try { List<String[]> cmds = step.getCommands(); List<InternalFactHandle> handles = (List<InternalFactHandle>) context .get("Handles"); final ObjectHashMap memory = (ObjectHashMap) wm.getNodeMemory(node); for (String[] cmd : cmds) { if (cmd[0].equals("leftMemory")) { String args = cmd[1]; String listString = args.replaceAll("h(\\d+)", "h[$1]"); Map<String, Object> vars = new HashMap<String, Object>(); vars.put("h", handles); List<?> expectedLeftTuples = (List<?>) MVEL.eval( listString, vars); if (expectedLeftTuples.isEmpty() && memory.size() != 0) { throw new AssertionFailedError("line " + step.getLine() + ": left Memory expected [] actually " + memory); } else if (expectedLeftTuples.isEmpty() && memory.size() == 0) { return; } // create expected tuples List<LeftTuple> leftTuples = new ArrayList<LeftTuple>(); for (List<InternalFactHandle> tlist : (List<List<InternalFactHandle>>) expectedLeftTuples) { LeftTuple tuple = new LeftTuple(tlist.get(0), null, false); for (int i = 1; i < tlist.size(); i++) { tuple = new LeftTuple(tuple, new RightTuple(tlist .get(i)), null, false); } leftTuples.add(tuple); } // get actual tuples final List<LeftTuple> actualTuples = new ArrayList<LeftTuple>(); final Iterator it = memory.iterator(); for (ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it .next()) { actualTuples.add((LeftTuple) entry.getKey()); } // iterate over expected tuples and compare with actual // tuples for (LeftTuple tuple : leftTuples) { if (!actualTuples.remove(tuple)) { throw new AssertionFailedError("line " + step.getLine() + ": left Memory expected " + tuple + " not found in memory."); } } if (!actualTuples.isEmpty()) { throw new AssertionFailedError( "line " + step.getLine() + ": left Memory unexpected tuples in the node memory " + actualTuples); } } else { throw new IllegalArgumentException("line " + step.getLine() + ": command does not exist " + Arrays.toString(cmd)); } } } catch (Exception e) { throw new IllegalArgumentException("line " + step.getLine() + ": unable to execute step " + step, e); } } private void ruleTerminalNode(DslStep step, RuleTerminalNode node, Map<String, Object> context, InternalWorkingMemory wm) { try { List<String[]> cmds = step.getCommands(); // List<InternalFactHandle> handles = (List<InternalFactHandle>) // context.get( "Handles" ); for (String[] cmd : cmds) { throw new IllegalArgumentException("line " + step.getLine() + ": command does not exist " + Arrays.toString(cmd)); } } catch (Exception e) { throw new IllegalArgumentException("line " + step.getLine() + ": unable to execute step " + step, e); } } @SuppressWarnings("unchecked") private void assertObject(DslStep step, Map<String, Object> context, InternalWorkingMemory wm) { try { List<String[]> cmds = step.getCommands(); List<InternalFactHandle> handles = (List<InternalFactHandle>) context .get("Handles"); for (String[] cmd : cmds) { try { String nodeName = cmd[0]; Sink sink = (Sink) context.get(nodeName); if (sink == null) { throw new IllegalArgumentException("line " + step.getLine() + ": node " + nodeName + " does not exist"); } Map<String, Object> vars = new HashMap<String, Object>(); vars.put("h", handles); String args = cmd[1]; String listString = args.replaceAll("h(\\d+)", "h[$1]"); List<?> list = (List<?>) MVEL.eval(listString, vars); if (list == null) { throw new IllegalArgumentException(cmd + " does not specify an existing fact handle"); } for (Object element : list) { if (element == null) { throw new IllegalArgumentException( cmd + " does not specify an existing fact handle"); } if (element instanceof InternalFactHandle) { InternalFactHandle handle = (InternalFactHandle) element; PropagationContext pContext = new PropagationContextImpl( wm.getNextPropagationIdCounter(), PropagationContext.ASSERTION, null, null, handle); ((ObjectSink) sink).assertObject(handle, pContext, wm); } else { List<InternalFactHandle> tlist = (List<InternalFactHandle>) element; LeftTuple tuple = createTuple(context, tlist); PropagationContext pContext = new PropagationContextImpl( wm.getNextPropagationIdCounter(), PropagationContext.ASSERTION, null, tuple, null); ((LeftTupleSink) sink).assertLeftTuple(tuple, pContext, wm); } } } catch (Exception e) { throw new IllegalArgumentException("line " + step.getLine() + ": unable to execute command " + cmd, e); } } } catch (Exception e) { throw new IllegalArgumentException("line " + step.getLine() + ": unable to execute step " + step, e); } } private LeftTuple createTuple(Map<String, Object> context, List<InternalFactHandle> tlist) { LeftTuple tuple = null; String id = getTupleId(tlist); for (InternalFactHandle handle : tlist) { if (tuple == null) { tuple = new LeftTuple(handle, null, false); // do not keep // generated tuples // on the handle // list } else { tuple = new LeftTuple(tuple, new RightTuple(handle), null, true); } } context.put(id, tuple); return tuple; } private String getTupleId(List<InternalFactHandle> tlist) { StringBuilder id = new StringBuilder(); id.append("T."); for (InternalFactHandle handle : tlist) { id.append(handle.getId()); id.append("."); } return id.toString(); } private void retractObject(DslStep step, Map<String, Object> context, InternalWorkingMemory wm) { try { List<String[]> cmds = step.getCommands(); List<InternalFactHandle> handles = (List<InternalFactHandle>) context .get("Handles"); for (String[] cmd : cmds) { try { String nodeName = cmd[0]; Sink sink = (Sink) context.get(nodeName); if (sink == null) { throw new IllegalArgumentException("line " + step.getLine() + ": node " + nodeName + " does not exist"); } String args = cmd[1]; String listString = args.replaceAll("h(\\d+)", "h[$1]"); Map<String, Object> vars = new HashMap<String, Object>(); vars.put("h", handles); List<?> list = (List<?>) MVEL.eval(listString, vars); if (list == null) { throw new IllegalArgumentException(Arrays.toString(cmd) + " does not specify an existing fact handle"); } for (Object element : list) { if (element == null) { throw new IllegalArgumentException( Arrays.toString(cmd) + " does not specify an existing fact handle"); } if (element instanceof InternalFactHandle) { InternalFactHandle handle = (InternalFactHandle) element; PropagationContext pContext = new PropagationContextImpl( wm.getNextPropagationIdCounter(), PropagationContext.RETRACTION, null, null, handle); if (sink instanceof ObjectTypeNode) { ((ObjectTypeNode) sink).retractObject(handle, pContext, wm); } else { for (RightTuple rightTuple = handle .getFirstRightTuple(); rightTuple != null; rightTuple = (RightTuple) rightTuple .getHandleNext()) { rightTuple.getRightTupleSink() .retractRightTuple(rightTuple, pContext, wm); } handle.setFirstRightTuple(null); handle.setLastRightTuple(null); for (LeftTuple leftTuple = handle .getFirstLeftTuple(); leftTuple != null; leftTuple = (LeftTuple) leftTuple .getLeftParentNext()) { leftTuple.getLeftTupleSink() .retractLeftTuple(leftTuple, pContext, wm); } handle.setFirstLeftTuple(null); handle.setLastLeftTuple(null); } } else { List<InternalFactHandle> tlist = (List<InternalFactHandle>) element; String id = getTupleId(tlist); LeftTuple tuple = (LeftTuple) context.remove(id); if (tuple == null) { throw new IllegalArgumentException( "Tuple not found: " + id + " : " + tlist.toString()); } PropagationContext pContext = new PropagationContextImpl( wm.getNextPropagationIdCounter(), PropagationContext.RETRACTION, null, tuple, null); ((LeftTupleSink) sink).retractLeftTuple(tuple, pContext, wm); } } } catch (Exception e) { throw new IllegalArgumentException("line " + step.getLine() + ": unable to execute command " + Arrays.toString(cmd), e); } } } catch (Exception e) { throw new IllegalArgumentException("line " + step.getLine() + ": unable to execute step " + step, e); } } private void modifyObject(DslStep step, Map<String, Object> context, InternalWorkingMemory wm) { try { List<String[]> cmds = step.getCommands(); List<InternalFactHandle> handles = (List<InternalFactHandle>) context .get("Handles"); for (String[] cmd : cmds) { try { String nodeName = cmd[0]; Sink sink = (Sink) context.get(nodeName); if (sink == null) { throw new IllegalArgumentException("line " + step.getLine() + ": node " + nodeName + " does not exist"); } String args = cmd[1]; String listString = args.replaceAll("h(\\d+)", "h[$1]"); Map<String, Object> vars = new HashMap<String, Object>(); vars.put("h", handles); List<?> list = (List<?>) MVEL.eval(listString, vars); if (list == null) { throw new IllegalArgumentException(Arrays.toString(cmd) + " does not specify an existing fact handle"); } for (Object element : list) { if (element == null) { throw new IllegalArgumentException( Arrays.toString(cmd) + " does not specify an existing fact handle"); } if (element instanceof InternalFactHandle) { InternalFactHandle handle = (InternalFactHandle) element; PropagationContext pContext = new PropagationContextImpl( wm.getNextPropagationIdCounter(), PropagationContext.MODIFICATION, null, null, handle); ModifyPreviousTuples modifyPreviousTuples = new ModifyPreviousTuples( handle.getFirstLeftTuple(), handle .getFirstRightTuple()); handle.setFirstLeftTuple(null); handle.setFirstRightTuple(null); handle.setLastLeftTuple(null); handle.setLastRightTuple(null); ((ObjectSink) sink).modifyObject(handle, modifyPreviousTuples, pContext, wm); modifyPreviousTuples.retractTuples(pContext, wm); } else { List<InternalFactHandle> tlist = (List<InternalFactHandle>) element; String id = getTupleId(tlist); LeftTuple tuple = (LeftTuple) context.get(id); if (tuple == null) { throw new IllegalArgumentException( "Tuple not found: " + id + " : " + tlist.toString()); } PropagationContext pContext = new PropagationContextImpl( wm.getNextPropagationIdCounter(), PropagationContext.MODIFICATION, null, tuple, null); ((LeftTupleSink) sink).modifyLeftTuple(tuple, pContext, wm); } } } catch (Exception e) { throw new IllegalArgumentException("line " + step.getLine() + ": unable to execute command " + cmd, e); } } } catch (Exception e) { throw new IllegalArgumentException("line " + step.getLine() + ": unable to execute step " + step, e); } } public static NodeTestCase compile(final Reader reader) throws IOException { NodeTestDSLParser parser = getParser(reader); return compile(parser); } public static NodeTestCase compile(final InputStream is) throws IOException { NodeTestDSLParser parser = getParser(is); return compile(parser); } public static NodeTestCase compile(final String source) throws IOException { NodeTestDSLParser parser = getParser(source); return compile(parser); } private static NodeTestCase compile(final NodeTestDSLParser parser) { try { compilation_unit_return cur = parser.compilation_unit(); if (parser.hasErrors()) { NodeTestCase result = new NodeTestCase(); result.setErrors(parser.getErrorMessages()); return result; } NodeTestCase testCase = walk(parser.getTokenStream(), (CommonTree) cur.getTree()); return testCase; } catch (RecognitionException e) { NodeTestCase result = new NodeTestCase(); result.setErrors(Collections.singletonList(e.getMessage())); return result; } } private static NodeTestCase walk(TokenStream tokenStream, Tree resultTree) throws RecognitionException { CommonTreeNodeStream nodes = new CommonTreeNodeStream(resultTree); // AST nodes have payload that point into token stream nodes.setTokenStream(tokenStream); // Create a tree walker attached to the nodes stream NodeTestDSLTree walker = new NodeTestDSLTree(nodes); walker.compilation_unit(); return walker.getTestCase(); } private static NodeTestDSLParser getParser(final Reader reader) throws IOException { NodeTestDSLLexer lexer = new NodeTestDSLLexer(new ANTLRReaderStream( reader)); NodeTestDSLParser parser = new NodeTestDSLParser(new CommonTokenStream( lexer)); return parser; } private static NodeTestDSLParser getParser(final InputStream is) throws IOException { NodeTestDSLLexer lexer = new NodeTestDSLLexer(new ANTLRInputStream(is)); NodeTestDSLParser parser = new NodeTestDSLParser(new CommonTokenStream( lexer)); return parser; } private static NodeTestDSLParser getParser(final String source) throws IOException { NodeTestDSLLexer lexer = new NodeTestDSLLexer(new ANTLRStringStream( source)); NodeTestDSLParser parser = new NodeTestDSLParser(new CommonTokenStream( lexer)); return parser; } private final class LeftTupleComparator implements Comparator<LeftTuple> { public int compare(LeftTuple o1, LeftTuple o2) { InternalFactHandle[] h1 = o1.getFactHandles(); InternalFactHandle[] h2 = o2.getFactHandles(); // Handles have to be compared in the inverse order. for (int i = (h1.length-1); i >= 0; i--) { int diff = h1[i].getId() - h2[i].getId(); // Will continue comparing handles until // a difference is found. if (diff != 0) return diff; } return 0; } } public static class EmptyNotifier extends RunNotifier { public static final EmptyNotifier INSTANCE = new EmptyNotifier(); @Override public void fireTestAssumptionFailed(Failure failure) { } @Override public void fireTestFailure(Failure failure) { } @Override public void fireTestFinished(Description description) { } @Override public void fireTestIgnored(Description description) { } @Override public void fireTestRunFinished(org.junit.runner.Result result) { } @Override public void fireTestRunStarted(Description description) { } @Override public void fireTestStarted(Description description) throws StoppedByUserException { } } }