/* * #%~ * Combinatorial Testing Runtime * %% * Copyright (C) 2008 - 2014 Overture * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #~% */ package org.overture.ct.ctruntime; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.lang.Thread.UncaughtExceptionHandler; import java.net.InetAddress; import java.net.Socket; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Vector; import org.overture.ast.lex.Dialect; import org.overture.config.Release; import org.overture.config.Settings; import org.overture.ct.utils.TraceXmlWrapper; import org.overture.interpreter.VDMJ; import org.overture.interpreter.VDMPP; import org.overture.interpreter.VDMRT; import org.overture.interpreter.VDMSL; import org.overture.interpreter.messages.Console; import org.overture.interpreter.messages.rtlog.RTLogger; import org.overture.interpreter.messages.rtlog.RTTextLogger; import org.overture.interpreter.messages.rtlog.nextgen.NextGenRTLogger; import org.overture.interpreter.runtime.ContextException; import org.overture.interpreter.runtime.Interpreter; import org.overture.interpreter.runtime.SourceFile; import org.overture.interpreter.runtime.ValueException; import org.overture.interpreter.traces.TraceReductionType; import org.overture.interpreter.util.ExitStatus; import org.overture.parser.config.Properties; import org.overture.parser.lex.LexTokenReader; import org.overture.typechecker.TypeChecker; import org.overture.typechecker.assistant.TypeCheckerAssistantFactory; import org.overture.util.Base64; public class TraceRunnerMain implements IProgressMonitor { public static boolean USE_SYSTEM_EXIT = true; private final static boolean DEBUG = false; protected final String host; protected final int port; protected final String ideKey; protected final String moduleName; protected final String traceName; protected final File traceFolder; protected Socket socket; protected InputStream input; protected OutputStream output; protected final Interpreter interpreter; protected int sessionId = 0; protected String transaction = ""; protected byte separator = '\0'; protected boolean connected = false; protected boolean completed = false; float subset = 1.0F; TraceReductionType reductionType = TraceReductionType.NONE; long seed = 999; public TraceRunnerMain(String host, int port, String ideKey, Interpreter interpreter, String moduleName, String traceName, File traceFolder, float subset, TraceReductionType traceReductionType, long seed) { this.host = host; this.port = port; this.ideKey = ideKey; this.moduleName = moduleName; this.interpreter = interpreter; this.traceName = traceName; this.traceFolder = traceFolder; this.seed = seed; this.reductionType = traceReductionType; this.subset = subset; } /** * @param args * the args */ public static void main(String[] args) { Settings.usingDBGP = false; String host = null; int port = -1; String ideKey = null; Settings.dialect = null; String moduleName = null; List<File> files = new Vector<File>(); List<String> largs = Arrays.asList(args); VDMJ controller = null; boolean warnings = true; boolean quiet = false; String logfile = null; boolean expBase64 = false; boolean traceNameBase64 = false; File coverage = null; String defaultName = null; String traceName = null; String traceReductionPattern = null; // String remoteName = null; // Class<RemoteControl> remoteClass = null; File traceFolder = null; Properties.init(); // Read properties file, if any Properties.parser_tabstop = 1; for (Iterator<String> i = largs.iterator(); i.hasNext();) { String arg = i.next(); if (arg.equals("-vdmsl")) { controller = new VDMSL(); } else if (arg.equals("-vdmpp")) { controller = new VDMPP(); } else if (arg.equals("-vdmrt")) { controller = new VDMRT(); } else if (arg.equals("-h")) { if (i.hasNext()) { host = i.next(); } else { usage("-h option requires a hostname"); } } else if (arg.equals("-p")) { try { port = Integer.parseInt(i.next()); } catch (Exception e) { usage("-p option requires a port"); } } else if (arg.equals("-k")) { if (i.hasNext()) { ideKey = i.next(); } else { usage("-k option requires a key"); } } else if (arg.equals("-e")) { if (i.hasNext()) { moduleName = i.next(); } else { usage("-e option requires an expression"); } } else if (arg.equals("-e64")) { if (i.hasNext()) { moduleName = i.next(); expBase64 = true; } else { usage("-e64 option requires an expression"); } } else if (arg.equals("-c")) { if (i.hasNext()) { if (controller == null) { usage("-c must come after <-vdmpp|-vdmsl|-vdmrt>"); } controller.setCharset(validateCharset(i.next())); } else { usage("-c option requires a charset name"); } } else if (arg.equals("-r")) { if (i.hasNext()) { Settings.release = Release.lookup(i.next()); if (Settings.release == null) { usage("-r option must be " + Release.list()); } } else { usage("-r option requires a VDM release"); } } else if (arg.equals("-log")) { if (i.hasNext()) { try { logfile = new URI(i.next()).getPath(); } catch (URISyntaxException e) { usage(e.getMessage() + ": " + arg); } catch (IllegalArgumentException e) { usage(e.getMessage() + ": " + arg); } } else { usage("-log option requires a filename"); } } else if (arg.equals("-w")) { warnings = false; } else if (arg.equals("-q")) { quiet = true; } else if (arg.equals("-coverage")) { if (i.hasNext()) { try { coverage = new File(new URI(i.next())); if (!coverage.isDirectory()) { usage("Coverage location is not a directory"); } } catch (URISyntaxException e) { usage(e.getMessage() + ": " + arg); } catch (IllegalArgumentException e) { usage(e.getMessage() + ": " + arg); } } else { usage("-coverage option requires a directory name"); } } else if (arg.equals("-default64")) { if (i.hasNext()) { defaultName = i.next(); } else { usage("-default64 option requires a name"); } } // else if (arg.equals("-remote")) // { // if (i.hasNext()) // { // remoteName = i.next(); // } // else // { // usage("-remote option requires a Java classname"); // } // } else if (arg.equals("-t")) { if (i.hasNext()) { traceName = i.next(); } else { usage("-t option requires a Trace Name"); } } else if (arg.equals("-t64")) { if (i.hasNext()) { traceName = i.next(); traceNameBase64 = true; } else { usage("-t option requires a Trace Name"); } } else if (arg.equals("-tracefolder")) { if (i.hasNext()) { try { traceFolder = new File(new URI(i.next())); if (!traceFolder.isDirectory()) { usage("Tracefolder location is not a directory"); } } catch (URISyntaxException e) { usage(e.getMessage() + ": " + arg); } catch (IllegalArgumentException e) { usage(e.getMessage() + ": " + arg); } } else { usage("-tracefolder option requires a directory name"); } } else if (arg.equals("-traceReduction")) { if (i.hasNext()) { try { traceReductionPattern = i.next(); } catch (IllegalArgumentException e) { usage(e.getMessage() + ": " + arg); } } else { usage("-traceReduction option requires a pattern"); } } else if (arg.equals("-consoleName")) { if (i.hasNext()) { LexTokenReader.consoleFileName = i.next(); } else { usage("-consoleName option requires a console name"); } } else if (arg.startsWith("-")) { usage("Unknown option " + arg); } else { try { File dir = new File(new URI(arg)); if (dir.isDirectory()) { for (File file : dir.listFiles(Settings.dialect.getFilter())) { if (file.isFile()) { files.add(file); } } } else { files.add(dir); } } catch (URISyntaxException e) { usage(e.getMessage() + ": " + arg); } catch (IllegalArgumentException e) { usage(e.getMessage() + ": " + arg); } } } if (host == null || port == -1 || controller == null || ideKey == null || moduleName == null || Settings.dialect == null || traceFolder == null || files.isEmpty()) { usage("Missing mandatory arguments"); } if (Settings.dialect != Dialect.VDM_RT && logfile != null) { usage("-log can only be used with -vdmrt"); } if (expBase64) { try { byte[] bytes = Base64.decode(moduleName); moduleName = new String(bytes, VDMJ.filecharset); } catch (Exception e) { usage("Malformed -e64 base64 expression"); } } if (traceNameBase64) { try { byte[] bytes = Base64.decode(traceName); traceName = new String(bytes, VDMJ.filecharset); } catch (Exception e) { usage("Malformed -t64 base64 trace name"); } } if (defaultName != null) { try { byte[] bytes = Base64.decode(defaultName); defaultName = new String(bytes, VDMJ.filecharset); } catch (Exception e) { usage("Malformed -default64 base64 name"); } } // if (remoteName != null) // { // try // { // Class<?> cls = ClassLoader.getSystemClassLoader().loadClass(remoteName); // remoteClass = (Class<RemoteControl>)cls; // } // catch (ClassNotFoundException e) // { // usage("Cannot locate " + remoteName + " on the CLASSPATH"); // } // } controller.setWarnings(warnings); controller.setQuiet(quiet); Console.disableStdout(); if (controller.parse(files) == ExitStatus.EXIT_OK) { if (controller.typeCheck() == ExitStatus.EXIT_OK) { try { if (logfile != null) { RTLogger.setLogfile(RTTextLogger.class, new File(logfile)); RTLogger.setLogfile(NextGenRTLogger.class, new File(logfile)); } Interpreter i = controller.getInterpreter(); if (defaultName != null) { i.setDefaultName(defaultName); } // RemoteControl r emote = // (remoteClass == null) ? null : remoteClass.newInstance(); // new ConnectionListener(port).start(); // String[] parts = traceReductionPattern.split("\\s+"); // int testNo = 0; float subset = 1.0F; TraceReductionType reductionType = TraceReductionType.NONE; long seed = 999; // {subset,reduction,seed} if (traceReductionPattern != null && traceReductionPattern.startsWith("{")) { try { String settings = traceReductionPattern; String[] tmp = settings.substring(1, settings.length() - 1).split(","); if (tmp.length == 3) { subset = Float.parseFloat(tmp[0]); reductionType = TraceReductionType.valueOf(tmp[1]); seed = Long.parseLong(tmp[2]); } } catch (NumberFormatException e) { usage(traceReductionPattern + " <name> [test number]"); return; } } TraceRunnerMain runner = new TraceRunnerMain(host, port, ideKey, i, moduleName, traceName, traceFolder, subset, reductionType, seed); runner.startup(); if (coverage != null) { writeCoverage(i, coverage); } RTLogger.dump(true); // runner.progressTerminating(); exit(0); } catch (ContextException e) { System.err.println("Initialization: " + e); e.ctxt.printStackTrace(Console.out, true); RTLogger.dump(true); exit(3); } catch (ValueException e) { System.err.println("Initialization: " + e); e.ctxt.printStackTrace(Console.out, true); RTLogger.dump(true); exit(3); } catch (Exception e) { System.err.println("Initialization: " + e); e.printStackTrace(); RTLogger.dump(true); exit(3); } } else { final PrintWriter out = new PrintWriter(System.err); TypeChecker.printErrors(out); out.flush(); exit(2); } } else { exit(1); } } private static void exit(int code) { if (USE_SYSTEM_EXIT) { System.exit(code); } } private void startup() throws Exception { connect(); } protected static void usage(String string) { System.err.println(string); System.err.println("Usage: -h <host> -p <port> -k <ide key> <-vdmpp|-vdmsl|-vdmrt>" + " -e <expression> | -e64 <base64 expression>" + " [-w] [-q] [-log <logfile URL>] [-c <charset>] [-r <release>]" + " [-coverage <dir URL>] [-default64 <base64 name>]" + " [-remote <class>] [-consoleName <console>] {<filename URLs>}"); System.exit(1); } protected static String validateCharset(String cs) { if (!Charset.isSupported(cs)) { System.err.println("Charset " + cs + " is not supported\n"); System.err.println("Available charsets:"); System.err.println("Default = " + Charset.defaultCharset()); Map<String, Charset> available = Charset.availableCharsets(); for (String name : available.keySet()) { System.err.println(name + " " + available.get(name).aliases()); } System.err.println(""); usage("Charset " + cs + " is not supported"); } return cs; } protected static void writeCoverage(Interpreter interpreter, File coverage) throws IOException { for (File f : interpreter.getSourceFiles()) { SourceFile source = interpreter.getSourceFile(f); File data = new File(coverage.getPath() + File.separator + f.getName() + ".covtbl"); PrintWriter pw = new PrintWriter(data); source.writeCoverage(pw); pw.close(); } } protected void connect() throws Exception { if (!connected) { if (port > 0) { if (DEBUG) { System.out.println("Trying to connect to CT IDE"); } try { InetAddress server = InetAddress.getByName(host); socket = new Socket(server, port); input = socket.getInputStream(); output = socket.getOutputStream(); } catch (Exception e) { e.printStackTrace(); } } else { System.err.println("Something wrong no port"); socket = null; input = System.in; output = System.out; separator = ' '; } connected = true; init(); run(); // New threads wait for a "run -i" } } private void run() throws Exception { Thread t = new Thread(new Runnable() { public void run() { String tmp = ""; while (input != null && !socket.isClosed()) { int b; try { b = input.read(); if (b == -1) { } else { tmp += new String(new byte[] { (byte) b }); } if (tmp.equals("exit")) { completed = true; return; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }); t.setDaemon(true); t.start(); t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { e.printStackTrace(); } }); TraceXmlWrapper storage = new TraceXmlWrapper(new File(traceFolder, moduleName + "-" + traceName + ".xml")); new TraceInterpreter(this, subset, reductionType, seed, new TypeCheckerAssistantFactory()).run(moduleName, traceName, interpreter, storage); while (!completed) { try { Thread.sleep(500); } catch (InterruptedException e) { } } try { if (DEBUG) { System.out.println("Closing socket"); } socket.close(); } catch (IOException e) { } } private void init() throws IOException { if (DEBUG) { System.out.println("Connected"); } StringBuilder sb = new StringBuilder(); // interpreter.init(null); sb.append("<init "); sb.append("module=\"" + moduleName + "\" "); sb.append("/>\n"); write(sb); if (DEBUG) { System.out.println("Wrote init"); } } private String currentTraceName = ""; /* * (non-Javadoc) * @see org.overture.traces.vdmj.IProgressMonitor#progress(java.lang.Integer) */ public void progress(Integer procentage) throws IOException { StringBuilder sb = new StringBuilder(); // interpreter.init(null); sb.append("<response "); sb.append("status=\"progress\" "); sb.append("progress=\"" + procentage + "\" "); sb.append("tracename=\"" + currentTraceName + "\" "); sb.append("/>\n"); write(sb); } /* * (non-Javadoc) * @see org.overture.traces.vdmj.IProgressMonitor#progressStartTrace(java.lang.String) */ public void progressStartTrace(String traceName) throws IOException { this.currentTraceName = traceName; StringBuilder sb = new StringBuilder(); // interpreter.init(null); sb.append("<response "); sb.append("status=\"tracestart\" "); sb.append("tracename=\"" + traceName + "\" "); sb.append("progress=\"" + 0 + "\" "); sb.append("/>\n"); write(sb); } public void progressCompleted() throws IOException { StringBuilder sb = new StringBuilder(); sb.append("<response "); sb.append("status=\"completed\" "); sb.append("progress=\"" + 100 + "\" "); sb.append("/>\n"); write(sb); } public void progressTerminating() throws IOException { StringBuilder sb = new StringBuilder(); sb.append("<response "); sb.append("status=\"terminating\" "); sb.append("/>\n"); write(sb); } public void progressError(String message) throws IOException { StringBuilder sb = new StringBuilder(); // interpreter.init(null); sb.append("<response "); sb.append("status=\"error\" "); sb.append("message=\"" + message + "\" "); // sb.append("progress=\"" + 100 + "\" "); sb.append("/>\n"); write(sb); } protected void write(StringBuilder data) throws IOException { if (output == null) { // TODO: Handle the error in VDMJ, terminate? System.err.println("Socket to IDE not valid."); return; } byte[] header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>".getBytes("UTF-8"); byte[] body = data.toString().getBytes("UTF-8"); byte[] size = Integer.toString(header.length + body.length).getBytes("UTF-8"); output.write(size); output.write(separator); output.write(header); output.write(body); output.write(separator); output.flush(); } }