package oms3.dsl; import groovy.lang.Closure; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; import oms3.Access; import oms3.ComponentAccess; import oms3.ComponentException; import oms3.annotations.Execute; import oms3.annotations.Finalize; import oms3.annotations.Initialize; public class Tests extends AbstractSimulation { static final Logger log = Logger.getLogger("oms3.sim"); List<TestCase> testCases = new ArrayList<TestCase>(); class TestCase implements Buildable { String name; int count = 1; Closure pre; // Closure Closure post; // Closure String ignore = null; Throwable expected; long timeout = 0; boolean rangecheck = false; List data; public void setData(List data) { this.data = data; } public void setTimeout(long timeout) { if (timeout < 1) { throw new ComponentException("Illegal timeout value: " + timeout); } this.timeout = timeout; } public void setRangecheck(boolean rangecheck) { this.rangecheck = rangecheck; } public void setIgnore(String ignore) { this.ignore = ignore; } public void setExpected(Throwable expected) { this.expected = expected; } public void setName(String name) { this.name = name; } public void setCount(int count) { if (count < 1) { throw new ComponentException("Illegal number of test cases: " + count); } this.count = count; } public void setPre(Closure pre) { this.pre = pre; } public void setPost(Closure post) { this.post = post; } @Override public Buildable create(Object name, Object value) { return LEAF; } private String getTestName(int no) { return name != null ? name : ("test-" + no); } /** * * @param testNo * @throws Exception */ private void run(int testNo) throws Exception { if (ignore!=null) { System.out.println(getTestName(testNo) + " [ignored: " + ignore + "]"); return; } System.out.print(getTestName(testNo) + " "); // Path String libPath = model.getLibpath(); if (libPath != null) { System.setProperty("jna.library.path", libPath); if (log.isLoggable(Level.CONFIG)) { log.config("Setting jna.library.path to " + libPath); } } final Object comp = model.getComponent(); if (log.isLoggable(Level.CONFIG)) { log.config("TL component " + comp); } Map<String, Object> parameter = model.getParameter(); ComponentAccess.setInputData(parameter, comp, log); List<String> data_ins = null; Map<String, Object>[] ps = null; int numruns = count; if (data != null) { data_ins = getfromData(data, comp); numruns = data.size() / data_ins.size() - 1; ps = getParamsets(data_ins, data); } for (int i = 0; i < numruns; i++) { if (log.isLoggable(Level.INFO)) { log.info("Test ... " + i); } try { if (data != null) { ComponentAccess.setInputData(ps[i], comp, log); } if (pre != null) { if (log.isLoggable(Level.INFO)) { log.info(" Pre ..."); } pre.call(comp); } /////////////// Init if (log.isLoggable(Level.INFO)) { log.info(" Init ..."); } ComponentAccess.callAnnotated(comp, Initialize.class, true); if (rangecheck) { ComponentAccess.rangeCheck(comp, true, false); } /////////////// Execute if (log.isLoggable(Level.INFO)) { log.info(" Execute ..."); } if (timeout > 0) { // with execution timeout ExecutorService service = Executors.newSingleThreadExecutor(); Callable<Object> callable = new Callable<Object>() { @Override public Object call() throws Exception { ComponentAccess.callAnnotated(comp, Execute.class, false); return null; } }; Future<Object> result = service.submit(callable); service.shutdown(); boolean terminated = service.awaitTermination(timeout, TimeUnit.MILLISECONDS); if (!terminated) { service.shutdownNow(); } result.get(0, TimeUnit.MILLISECONDS); // throws the exception if one occurred during the invocation } else { // no execution timeout. ComponentAccess.callAnnotated(comp, Execute.class, false); } if (rangecheck) { ComponentAccess.rangeCheck(comp, false, true); } /////////////////// Finalize if (log.isLoggable(Level.INFO)) { log.info(" Finalize ..."); } ComponentAccess.callAnnotated(comp, Finalize.class, true); } catch (TimeoutException e) { throw new ComponentException(String.format(getTestName(testNo) + " timed out after %d milliseconds", timeout)); } catch (ComponentException e) { throw e; } catch (Throwable E) { if (expected != null) { if (E.getClass() != expected.getClass()) { throw new ComponentException("Expected " + expected + " but caught " + E); } } else { throw new ComponentException(E, comp); } } if (expected != null) { // should not happen since we expect a component exception. throw new ComponentException("Expected :" + expected); } if (post != null) { if (log.isLoggable(Level.INFO)) { log.info(" Post ..."); } post.call(comp); } System.out.print("."); } System.out.println(); } } @Override public Buildable create(Object name, Object value) { if (name.equals("test")) { TestCase tc = new TestCase(); testCases.add(tc); return tc; } return super.create(name, value); } @Override public Object run() throws Exception { if (testCases.isEmpty()) { throw new ComponentException("No test(s) to run."); } for (int i = 0; i < testCases.size(); i++) { testCases.get(i).run(i); } return null; } // static methods private static List<String> getfromData(List data, Object comp) { List<String> l = new ArrayList<String>(); ComponentAccess cp = new ComponentAccess(comp); Collection<Access> ins = cp.inputs(); for (int i = 0; i < data.size(); i++) { if (data.get(i) instanceof String) { String name = data.get(i).toString(); // cover GString Access a = findAccessByName(name, ins); if (a != null) { l.add(a.getField().getName()); } else { return l; } } else { return l; } } return l; } private static Access findAccessByName(String name, Collection<Access> ins) { for (Access access : ins) { if (name.equals(access.getField().getName())) { return access; } } return null; } private static Map<String, Object>[] getParamsets(List<String> fnames, List data) { int cols = fnames.size(); if (cols<1) { throw new ComponentException("no field mapping information for test data"); } int rows = (data.size() / fnames.size()) - 1; if (rows < 1) { throw new ComponentException("no test data provided."); } int idx = cols; // skip the header Map<String, Object>[] ps = new HashMap[rows]; for (int r = 0; r < rows; r++) { ps[r] = new HashMap<String, Object>(); for (int c = 0; c < cols; c++) { ps[r].put(fnames.get(c), data.get(idx++)); if (idx > data.size()) { throw new ComponentException("Invalid test parameter set, check the data size."); } } } return ps; } }