/* * Created on Apr 14, 2007 Copyright (C) 2001-6, Anthony Harrison anh23@pitt.edu * (jactr.org) This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the License, * or (at your option) any later version. This library is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. You should have * received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple * Place, Suite 330, Boston, MA 02111-1307 USA */ package org.jactr.tools.test; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jactr.core.concurrent.ExecutorServices; import org.jactr.core.model.IModel; import org.jactr.core.model.event.ModelEvent; import org.jactr.core.model.event.ModelListenerAdaptor; import org.jactr.core.module.procedural.event.IProceduralModuleListener; import org.jactr.core.module.procedural.event.ProceduralModuleEvent; import org.jactr.core.module.procedural.event.ProceduralModuleListenerAdaptor; import org.jactr.core.production.IInstantiation; import org.jactr.core.runtime.ACTRRuntime; import org.jactr.core.runtime.controller.IController; import org.jactr.io.environment.EnvironmentParser; /** * Utility class for ensuring that model's fire correctly * * @author developer */ public class ExecutionTester { /** * logger definition */ static private final Log LOGGER = LogFactory .getLog(ExecutionTester.class); private Map<IModel, Iterator<String>> _productionSequenceMap; private Map<IModel, Set<String>> _failedProductionMap; private Collection<Throwable> _exceptions; public ExecutionTester() { _productionSequenceMap = new HashMap<IModel, Iterator<String>>(); _failedProductionMap = new HashMap<IModel, Set<String>>(); _exceptions = new ArrayList<Throwable>(); } /** * called by the model listener so that Junit tests can handle the exception * gracefully * * @param thrown */ public void exceptionCaught(Throwable thrown) { _exceptions.add(thrown); } /** * called by the proceduralModuleListener after each production fires * * @param model * @param instantiation */ final public void verifyThatProductionShouldFire(IModel model, IInstantiation instantiation) { Iterator<String> productionSequence = _productionSequenceMap.get(model); Set<String> failedProductions = _failedProductionMap.get(model); String productionName = instantiation.getProduction() .getSymbolicProduction().getName(); if (failedProductions.contains(productionName)) throw new RuntimeException("(" + model + ") " + productionName + " is never supposed to fire"); if (!productionSequence.hasNext()) throw new RuntimeException("(" + model + ") No more productions should be firing, got " + productionName); String expectedName = productionSequence.next(); if (!productionName.equalsIgnoreCase(expectedName)) throw new RuntimeException("(" + model + ") Wrong production fired. Expecting " + expectedName + " got " + productionName); verifyModelState(model, instantiation); } /** * this can be overriden if you want to check the state of the model just * after a production has fired * * @param model */ public void verifyModelState(IModel model, IInstantiation instantiation) { } /** * test run. * * @param url * of the environment file * @param productionSequenceMap * keyed on model name, a sequence of productions that should fire * @param failedProductionMap * keyed on model name, a set of productions that should never fire */ public Collection<Throwable> test(URL url, Map<String, Collection<String>> productionSequenceMap, Map<String, Collection<String>> failedProductionMap) { try { EnvironmentParser envP = new EnvironmentParser(); envP.parse(url); } catch (Exception e) { throw new RuntimeException("Could not load environment ", e); } IProceduralModuleListener listener = new ProceduralModuleListenerAdaptor() { @Override public void productionFired(ProceduralModuleEvent pme) { verifyThatProductionShouldFire(pme.getSource().getModel(), (IInstantiation) pme.getProduction()); } }; for (IModel model : ACTRRuntime.getRuntime().getModels()) { String modelName = model.getName(); if (productionSequenceMap.containsKey(modelName)) { _productionSequenceMap.put(model, new ArrayList<String>( productionSequenceMap.get(modelName)).iterator()); _failedProductionMap.put(model, new TreeSet<String>(failedProductionMap .get(modelName))); model.getProceduralModule().addListener(listener, ExecutorServices.INLINE_EXECUTOR); } else if (LOGGER.isWarnEnabled()) LOGGER.warn("Have no production information for " + modelName + " will not test"); model.addListener(new ModelListenerAdaptor() { @Override public void exceptionThrown(ModelEvent me) { exceptionCaught(me.getException()); } @Override public void modelStopped(ModelEvent me) { Iterator<String> productionSequence = _productionSequenceMap.get(me .getSource()); if (productionSequence != null && productionSequence.hasNext()) _exceptions.add(new RuntimeException( "Not all productions have fired, expecting " + productionSequence.next())); } }, ExecutorServices.INLINE_EXECUTOR); } /* * all set up - let's execute.. */ IController controller = ACTRRuntime.getRuntime().getController(); try { controller.start().get(); controller.complete().get(); return _exceptions; } catch (Exception e) { throw new RuntimeException("Could not execute runtime ", e); } finally { for (IModel model : new ArrayList<IModel>(ACTRRuntime.getRuntime() .getModels())) try { ACTRRuntime.getRuntime().removeModel(model); model.dispose(); } catch (Exception e2) { _exceptions.add(e2); } } } /** * @param url * @param modelName * not null * @param productionSequence * not null or empty * @param failedProductions * not null */ public Collection<Throwable> test(URL url, String modelName, Collection<String> productionSequence, Collection<String> failedProductions) { Map<String, Collection<String>> sequence = new TreeMap<String, Collection<String>>(); sequence.put(modelName, productionSequence); Map<String, Collection<String>> failed = new TreeMap<String, Collection<String>>(); failed.put(modelName, failedProductions); return test(url, sequence, failed); } }