All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sikuli.script.Runner Maven / Gradle / Ivy

package org.sikuli.script;

import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import org.apache.commons.cli.CommandLine;
import org.sikuli.basics.Debug;
import org.sikuli.basics.FileManager;
import org.sikuli.basics.Settings;
import org.sikuli.util.CommandArgs;
import org.sikuli.util.CommandArgsEnum;
import org.sikuli.util.JythonHelper;

public class Runner {

  static final String me = "Runner: ";
  static final int lvl = 3;
  static final RunTime runTime = RunTime.get();

  public static Map endingTypes = new HashMap();
  public static Map typeEndings = new HashMap();
  public static Map runnerTypes = new HashMap();
  public static String ERUBY = "rb";
  public static String EPYTHON = "py";
  public static String EJSCRIPT = "js";
  public static String EASCRIPT = "script";
  public static String ESSCRIPT = "ps1";
  public static String EPLAIN = "txt";
  public static String EDEFAULT = EPYTHON;
  public static String CPYTHON = "text/python";
  public static String CRUBY = "text/ruby";
  public static String CJSCRIPT = "text/javascript";
  public static String CASCRIPT = "text/applescript";
  public static String CSSCRIPT = "text/powershell";
  public static String CPLAIN = "text/plain";
  public static String RPYTHON = "jython";
  public static String RRUBY = "jruby";
  public static String RJSCRIPT = "JavaScript";
  public static String RASCRIPT = "AppleScript";
  public static String RSSCRIPT = "PowerShell";
  public static String RRSCRIPT = "Robot";
  public static String RDEFAULT = RPYTHON;

  private static String[] runScripts = null;
  private static String[] testScripts = null;
  private static int lastReturnCode = 0;

  private static String beforeJSjava8 = "load(\"nashorn:mozilla_compat.js\");";
  private static String beforeJS
          = "importPackage(Packages.org.sikuli.script); "
          + "importClass(Packages.org.sikuli.basics.Debug); "
          + "importClass(Packages.org.sikuli.basics.Settings);";

  static {
    endingTypes.put(EPYTHON, CPYTHON);
    endingTypes.put(ERUBY, CRUBY);
    endingTypes.put(EJSCRIPT, CJSCRIPT);
    endingTypes.put(EPLAIN, CPLAIN);
    for (String k : endingTypes.keySet()) {
      typeEndings.put(endingTypes.get(k), k);
    }
    runnerTypes.put(EPYTHON, RPYTHON);
    runnerTypes.put(ERUBY, RRUBY);
    runnerTypes.put(EJSCRIPT, RJSCRIPT);
  }

  static void log(int level, String message, Object... args) {
    Debug.logx(level, me + message, args);
  }

  public static String[] evalArgs(String[] args) {
    CommandArgs cmdArgs = new CommandArgs("SCRIPT");
    CommandLine cmdLine = cmdArgs.getCommandLine(CommandArgs.scanArgs(args));
    String cmdValue;

    if (cmdLine == null || cmdLine.getOptions().length == 0) {
      log(-1, "Did not find any valid option on command line!");
      cmdArgs.printHelp();
      System.exit(1);
    }

    if (cmdLine.hasOption(CommandArgsEnum.HELP.shortname())) {
      cmdArgs.printHelp();
      System.exit(1);
    }

    if (cmdLine.hasOption(CommandArgsEnum.LOGFILE.shortname())) {
      cmdValue = cmdLine.getOptionValue(CommandArgsEnum.LOGFILE.longname());
      if (!Debug.setLogFile(cmdValue == null ? "" : cmdValue)) {
        System.exit(1);
      }
    }

    if (cmdLine.hasOption(CommandArgsEnum.USERLOGFILE.shortname())) {
      cmdValue = cmdLine.getOptionValue(CommandArgsEnum.USERLOGFILE.longname());
      if (!Debug.setUserLogFile(cmdValue == null ? "" : cmdValue)) {
        System.exit(1);
      }
    }

    if (cmdLine.hasOption(CommandArgsEnum.DEBUG.shortname())) {
      cmdValue = cmdLine.getOptionValue(CommandArgsEnum.DEBUG.longname());
      if (cmdValue == null) {
        Debug.setDebugLevel(3);
        Settings.LogTime = true;
        if (!Debug.isLogToFile()) {
          Debug.setLogFile("");
        }
      } else {
        Debug.setDebugLevel(cmdValue);
      }
    }

    runTime.setArgs(cmdArgs.getUserArgs(), cmdArgs.getSikuliArgs());
    log(lvl, "commandline: %s", cmdArgs.getArgsOrg());
    runTime.printArgs();

    // select script runner and/or start interactive session
    // option is overloaded - might specify runner for -r/-t
    if (cmdLine.hasOption(CommandArgsEnum.INTERACTIVE.shortname())) {
      if (!cmdLine.hasOption(CommandArgsEnum.RUN.shortname())
              && !cmdLine.hasOption(CommandArgsEnum.TEST.shortname())) {
        runTime.interactiveRunner = cmdLine.getOptionValue(CommandArgsEnum.INTERACTIVE.longname());
        runTime.runningInteractive = true;
        return null;
      }
    }

    String[] runScripts = null;
    runTime.runningTests = false;
    if (cmdLine.hasOption(CommandArgsEnum.RUN.shortname())) {
      runScripts = cmdLine.getOptionValues(CommandArgsEnum.RUN.longname());
    } else if (cmdLine.hasOption(CommandArgsEnum.TEST.shortname())) {
      runScripts = cmdLine.getOptionValues(CommandArgsEnum.TEST.longname());
      log(-1, "Command line option -t: not yet supported! %s", Arrays.asList(args).toString());
      runTime.runningTests = true;
//TODO run a script as unittest with HTMLTestRunner
      System.exit(1);
    }
    return runScripts;
  }

  public static int run(String givenName) {
    return run(givenName, new String[0]);
  }

  public static int run(String givenName, String[] args) {
    String savePath = ImagePath.getBundlePathSet();
    int retVal = new RunBox(givenName, args, false).run();
    if (savePath != null) {
      ImagePath.setBundlePath(savePath);
    }
    lastReturnCode = retVal;
    return retVal;
  }

  public static int runTest(String givenName) {
    return runTest(givenName, new String[0]);
  }

  public static int runTest(String givenName, String[] args) {
    String savePath = ImagePath.getBundlePath();
    int retVal = new RunBox(givenName, args, true).run();
    ImagePath.setBundlePath(savePath);
    return retVal;
  }

  public static int getLastReturnCode() {
    return lastReturnCode;
  }

  public static int runScripts(String[] args) {
    runScripts = Runner.evalArgs(args);
    String someJS = "";
    int exitCode = 0;
    if (runScripts != null && runScripts.length > 0) {
      boolean runAsTest = runTime.runningTests;
      for (String givenScriptName : runScripts) {
        if (lastReturnCode == -1) {
          log(lvl, "Exit code -1: Terminating multi-script-run");
          break;
        }
        someJS = runTime.getOption("runsetup", "");
        if (!someJS.isEmpty()) {
          log(lvl, "Options.runsetup: %s", someJS);
          new RunBox().runjs(null, null, someJS, null);
        }
        RunBox rb = new RunBox(givenScriptName, runTime.getArgs(), runAsTest);
        exitCode = rb.run();
        someJS = runTime.getOption("runteardown", "");
        if (!someJS.isEmpty()) {
          log(lvl, "Options.runteardown: %s", someJS);
          new RunBox().runjs(null, null, someJS, null);
        }
        if (exitCode == -999) {
          exitCode = lastReturnCode;
        }
        lastReturnCode = exitCode;
      }
    }
    return exitCode;
  }

  public static File getScriptFile(File fScriptFolder) {
    if (fScriptFolder == null) {
      return null;
    }
    File[] content = FileManager.getScriptFile(fScriptFolder);
    if (null == content) {
      return null;
    }
    File fScript = null;
    for (File aFile : content) {
      for (String suffix : Runner.endingTypes.keySet()) {
        if (!aFile.getName().endsWith("." + suffix)) {
          continue;
        }
        fScript = aFile;
        break;
      }
      if (fScript != null) {
        break;
      }
    }
    return fScript;
  }

  static JythonHelper pyRunner = null;
  static Class cIDE;
  static Method mShow;
  static Method mHide;

  protected static boolean initpy() {
    if (pyRunner == null) {
      pyRunner = JythonHelper.get();
      if (pyRunner == null) {
        return false;
      }
      pyRunner.exec("# -*- coding: utf-8 -*- ");
      pyRunner.exec("import org.sikuli.basics.SikulixForJython");
      pyRunner.exec("from sikuli import *");
    }
    return true;
  }

  static Object rbRunner = null;
  static Object txtRunner = null;
  static ScriptEngine jsRunner = null;

  public static ScriptEngine initjs() {
    ScriptEngineManager jsFactory = new ScriptEngineManager();
    ScriptEngine jsr = jsFactory.getEngineByName("JavaScript");
    if (jsr != null) {
      log(lvl, "ScriptingEngine started: JavaScript (ending .js)");
    } else {
      runTime.terminate(1, "ScriptingEngine for JavaScript not available");
    }
    if (RunTime.Type.IDE.equals(runTime.runType)) {
      try {
        cIDE = Class.forName("org.sikuli.ide.SikuliIDE");
        mHide = cIDE.getMethod("hideIDE", new Class[0]);
        mShow = cIDE.getMethod("showIDE", new Class[0]);
      } catch (Exception ex) {
        log(-1, "initjs: getIDE");
      }
    }
    return jsr;
  }

  public static String prologjs(String before) {
    String after = before;
    if (after.isEmpty()) {
      if (runTime.isJava8()) {
        after += beforeJSjava8;
      }
      after += beforeJS;
    } else {
      String commands = runTime.extractResourceToString("JavaScript", "commands.js", "");
      if (commands != null) {
        after += commands;
      }
    }
    return after;
  }

  public static Object[] runBoxInit(String givenName, File scriptProject, URL uScriptProject) {
    String gitScripts = "https://github.com/RaiMan/SikuliX-2014/tree/master/TestScripts/";
    String givenScriptHost = "";
    String givenScriptFolder = "";
    String givenScriptName;
    String givenScriptScript = "";
    String givenScriptType = "sikuli";
    String givenScriptScriptType = RDEFAULT;
    Boolean givenScriptExists = true;
    URL uGivenScript = null;
    URL uGivenScriptFile = null;
    givenScriptName = givenName;
    String[] parts = null;
    int isNet;
    boolean isInline = false;
    givenName = givenName.trim();
    if (givenName.toLowerCase().startsWith(RASCRIPT.toLowerCase())) {
      givenScriptScriptType = RASCRIPT;
      givenScriptName = null;
      givenScriptScript = givenName.substring(RASCRIPT.length() + 1);
      isInline = true;
    } else if (givenName.toLowerCase().startsWith(RSSCRIPT.toLowerCase())) {
      givenScriptScriptType = RSSCRIPT;
      givenScriptName = null;
      givenScriptScript = givenName.substring(RSSCRIPT.length() + 1);
      isInline = true;
    } else if (givenName.toLowerCase().startsWith(RRSCRIPT.toLowerCase())) {
      givenScriptScriptType = RRSCRIPT;
      givenScriptName = null;
      givenScriptScript = givenName.substring(RRSCRIPT.length() + 1);
      isInline = true;
    } else if (givenName.toLowerCase().startsWith("git*")) {
      if (givenName.length() == 4) {
        givenName = gitScripts + "showcase";
      } else {
        givenName = gitScripts + givenName.substring(4);
      }
    }
    if (!isInline) {
      boolean fromNet = false;
      String scriptLocation = givenName;
      String content = "";
      if (-1 < (isNet = givenName.indexOf("://"))) {
        givenName = givenName.substring(isNet + 3);
        if (givenName.indexOf(":") == -1) {
          givenName = givenName.replaceFirst("/", ":");
        }
      }
      if (givenName.indexOf(":") > 5) {
        parts = givenName.split(":");
        if (parts.length > 1 && !parts[1].isEmpty()) {
          fromNet = true;
          givenScriptHost = parts[0];
          givenScriptName = new File(parts[1]).getName();
          String fpFolder = new File(parts[1]).getParent();
          if (null != fpFolder && !fpFolder.isEmpty()) {
            givenScriptFolder = FileManager.slashify(fpFolder, true);
          }
        }
        scriptLocation = givenName;
        givenScriptExists = false;
      }
      if (fromNet) {
        if (givenScriptHost.contains("github.com")) {
          givenScriptHost = "https://raw.githubusercontent.com";
          givenScriptFolder = givenScriptFolder.replace("tree/", "");
        } else {
          givenScriptHost = "http://" + givenScriptHost;
        }
        if (givenScriptName.endsWith(".zip")) {
          scriptLocation = givenScriptHost + givenScriptFolder + givenScriptName;
          if (0 < FileManager.isUrlUseabel(scriptLocation)) {
            runTime.terminate(1, ".zip from net not yet supported\n%s", scriptLocation);
          }
        } else {
          for (String suffix : endingTypes.keySet()) {
            String dlsuffix = "";
            if (suffix != "js") {
              dlsuffix = ".txt";
            }
            givenScriptScript = givenScriptName + "/" + givenScriptName + "." + suffix;
            scriptLocation = givenScriptHost + "/" + givenScriptFolder + givenScriptScript;
            givenScriptScriptType = runnerTypes.get(suffix);
            if (0 < FileManager.isUrlUseabel(scriptLocation)) {
              content = FileManager.downloadURLtoString(scriptLocation);
              break;
            } else if (!dlsuffix.isEmpty()) {
              givenScriptScript = givenScriptName + "/" + givenScriptName + "." + suffix + dlsuffix;
              scriptLocation = givenScriptHost + "/" + givenScriptFolder + givenScriptScript;
              if (0 < FileManager.isUrlUseabel(scriptLocation)) {
                content = FileManager.downloadURLtoString(scriptLocation);
                break;
              }
            }
            scriptLocation = givenScriptHost + "/" + givenScriptFolder + givenScriptName;
          }
          if (content != null && !content.isEmpty()) {
            givenScriptType = "NET";
            givenScriptScript = content;
            givenScriptExists = true;
            try {
              uGivenScript = new URL(givenScriptHost + "/" + givenScriptFolder + givenScriptName);
            } catch (Exception ex) {
              givenScriptExists = false;
            }
          } else {
            givenScriptExists = false;
          }
        }
        if (!givenScriptExists) {
          log(-1, "given script location not supported or not valid:\n%s", scriptLocation);
        } else {
          String header = "# ";
          String trailer = "\n";
          if (RJSCRIPT.equals(givenScriptScriptType)) {
            header = "/*\n";
            trailer = "*/\n";
          }
          header += scriptLocation + "\n";
          if (Debug.is() > 2) {
            FileManager.writeStringToFile(header + trailer + content,
                    new File(runTime.fSikulixStore, "LastScriptFromNet.txt"));
          }
        }
      } else {
        boolean sameFolder = givenScriptName.startsWith("./");
        if (sameFolder) {
          givenScriptName = givenScriptName.substring(2);
        }
        if (givenScriptName.startsWith("JS*")) {
          givenScriptName = new File(runTime.fSxProjectTestScriptsJS, givenScriptName.substring(3)).getPath();
        }
        if (givenScriptName.startsWith("TEST*")) {
          givenScriptName = new File(runTime.fSxProjectTestScripts, givenScriptName.substring(5)).getPath();
        }
        String scriptName = new File(givenScriptName).getName();
        if (scriptName.contains(".")) {
          parts = scriptName.split("\\.");
          givenScriptScript = parts[0];
          givenScriptType = parts[1];
        } else {
          givenScriptScript = scriptName;
        }
        if (sameFolder && scriptProject != null) {
          givenScriptName = new File(scriptProject, givenScriptName).getPath();
        } else if (sameFolder && uScriptProject != null) {
          givenScriptHost = uScriptProject.getHost();
          givenScriptFolder = uScriptProject.getPath().substring(1);
        } else if (scriptProject == null && givenScriptHost.isEmpty()) {
          String fpParent = new File(givenScriptName).getParent();
          if (fpParent == null || fpParent.isEmpty()) {
            scriptProject = null;
          } else {
            scriptProject = new File(givenScriptName).getParentFile();
          }
        }
      }
    }
    Object[] vars = new Object[]{givenScriptHost, givenScriptFolder, givenScriptName,
      givenScriptScript, givenScriptType, givenScriptScriptType,
      uGivenScript, uGivenScriptFile, givenScriptExists, scriptProject, uScriptProject};
    return vars;
  }

  public static void runjsEval(String script) {
    new RunBox().runjsEval(script);
  }

  public static int runjs(File fScript, URL uGivenScript, String givenScriptScript, String[] args) {
    return new RunBox().runjs(fScript, uGivenScript, givenScriptScript, args);
  }

  public static int runas(String givenScriptScript) {
    return runas(givenScriptScript, false);
  }

  public static int runrobot(String code) {
    File script = new File(ImagePath.getBundlePath());
    File fRobotWork = new File(script.getAbsolutePath() + ".robot");
    FileManager.deleteFileOrFolder(fRobotWork);
    fRobotWork.mkdir();
    String sName = script.getName().replace(".sikuli", "");
    File fPyCode = new File(script, sName + ".py");
    String pyCode = FileManager.readFileToString(fPyCode);
    int prefix = pyCode.indexOf("\"\"\")");
    if (prefix < 0) {
      log(-1, "runRobot: invalid robot text structure");
      return -1;
    }
    pyCode = pyCode.substring(prefix + 4).trim();
    int refLib = code.indexOf("./inline/");
    String sLib = "";
    File fInline = null;
    String fpInline = "";
    if (!pyCode.isEmpty()) {
      if (refLib < 0) {
        log(-1, "runRobot: invalid robot text structure");
        return -1;
      }
      sLib = code.substring(refLib + 9);
      sLib = sLib.substring(0, sLib.indexOf("\n")).trim();
    } else {
      if (refLib > -1) {
        log(-1, "runRobot: invalid robot text structure");
        return -1;
      }
      refLib = code.indexOf("./jar/");
      if (refLib > -1) {
        sLib = code.substring(refLib + 6);
        sLib = sLib.substring(0, sLib.indexOf("\n")).trim();
        fInline = new File (sLib + ".jar");
        if (!fInline.isAbsolute()) {
          fInline = new File(fRobotWork, sLib + ".jar");
        }
        runTime.addToClasspath(fInline.getAbsolutePath());
        sLib = "";
      }
    }
    if (!sLib.isEmpty()) {
      fInline = new File(fRobotWork, sLib + ".py");      
      FileManager.writeStringToFile(pyCode, fInline);
      fpInline = FileManager.slashify(fInline.getAbsolutePath(), false);
      code = code.replace("./inline/" + sLib, fpInline );
    }
    File fRobot = new File(fRobotWork, sName + ".robot");
    FileManager.writeStringToFile(code, fRobot);
    if (!initpy()) {
      log(-1, "Running Python scripts:init failed");
      return -999;
    }
//    String jarR4S = runTime.isOnClasspath("robot4sikulix");
//    if (null != jarR4S) {
//      pyRunner.addSysPath(jarR4S);
//    }
    pyRunner.exec("from sikuli import *;");
    pyRunner.exec("import robot.run;");
    pyRunner.exec(String.format(
                  "robot.run(\"%s\", "
                  + "outputdir=\"%s\""
                  + ");", fRobot, fRobotWork));
    return 0;
  }
  
  //
//  -N --name name           Set the name of the top level test suite. Underscores
//                          in the name are converted to spaces. Default name is
//                          created from the name of the executed data source.
// -D --doc documentation   Set the documentation of the top level test suite.
//                          Underscores in the documentation are converted to
//                          spaces and it may also contain simple HTML formatting
//                          (e.g. *bold* and http://url/).
// -M --metadata name:value *  Set metadata of the top level suite. Underscores
//                          in the name and value are converted to spaces. Value
//                          can contain same HTML formatting as --doc.
//                          Example: --metadata version:1.2
// -G --settag tag *        Sets given tag(s) to all executed test cases.
// -t --test name *         Select test cases to run by name or long name. Name
//                          is case and space insensitive and it can also be a
//                          simple pattern where `*` matches anything and `?`
//                          matches any char. If using `*` and `?` in the console
//                          is problematic see --escape and --argumentfile.
// -s --suite name *        Select test suites to run by name. When this option
//                          is used with --test, --include or --exclude, only
//                          test cases in matching suites and also matching other
//                          filtering criteria are selected. Name can be a simple
//                          pattern similarly as with --test and it can contain
//                          parent name separated with a dot. For example
//                          `-s X.Y` selects suite `Y` only if its parent is `X`.
// -i --include tag *       Select test cases to run by tag. Similarly as name
//                          with --test, tag is case and space insensitive and it
//                          is possible to use patterns with `*` and `?` as
//                          wildcards. Tags and patterns can also be combined
//                          together with `AND`, `OR`, and `NOT` operators.
//                          Examples: --include foo --include bar*
//                                    --include fooANDbar*
// -e --exclude tag *       Select test cases not to run by tag. These tests are
//                          not run even if included with --include. Tags are
//                          matched using the rules explained with --include.
// -R --rerunfailed output  Select failed tests from an earlier output file to be
//                          re-executed. Equivalent to selecting same tests
//                          individually using --test option.
//    --runfailed output    Deprecated since RF 2.8.4. Use --rerunfailed instead.
// -c --critical tag *      Tests having given tag are considered critical. If no
//                          critical tags are set, all tags are critical. Tags
//                          can be given as a pattern like with --include.
// -n --noncritical tag *   Tests with given tag are not critical even if they
//                          have a tag set with --critical. Tag can be a pattern.
// -v --variable name:value *  Set variables in the test data. Only scalar
//                          variables are supported and name is given without
//                          `${}`. See --escape for how to use special characters
//                          and --variablefile for a more powerful variable
//                          setting mechanism that allows also list variables.
//                          Examples:
//                          --variable str:Hello  =>  ${str} = `Hello`
//                          -v str:Hi_World -E space:_  =>  ${str} = `Hi World`
//                          -v x: -v y:42  =>  ${x} = ``, ${y} = `42`
// -V --variablefile path *  File to read variables from (e.g. `path/vars.py`).
//                          Example file:
//                          |  import random
//                          |  __all__ = [`scalar`, `LIST__var`, `integer`]
//                          |  scalar = `Hello world!`
//                          |  LIST__var = [`Hello`, `list`, `world`]
//                          |  integer = random.randint(1,10)
//                          =>
//                          ${scalar} = `Hello world!`
//                          @{var} = [`Hello`,`list`,`world`]
//                          ${integer} = 
// -d --outputdir dir       Where to create output files. The default is the
//                          directory where tests are run from and the given path
//                          is considered relative to that unless it is absolute.
// -o --output file         XML output file. Given path, similarly as paths given
//                          to --log, --report, --xunit, and --debugfile, is
//                          relative to --outputdir unless given as an absolute
//                          path. Other output files are created based on XML
//                          output files after the test execution and XML outputs
//                          can also be further processed with Rebot tool. Can be
//                          disabled by giving a special value `NONE`. In this
//                          case, also log and report are automatically disabled.
//                          Default: output.xml
// -l --log file            HTML log file. Can be disabled by giving a special
//                          value `NONE`. Default: log.html
//                          Examples: `--log mylog.html`, `-l NONE`
// -r --report file         HTML report file. Can be disabled with `NONE`
//                          similarly as --log. Default: report.html
// -x --xunit file          xUnit compatible result file. Not created unless this
//                          option is specified.
//    --xunitfile file      Deprecated. Use --xunit instead.
//    --xunitskipnoncritical  Mark non-critical tests on xUnit output as skipped.
// -b --debugfile file      Debug file written during execution. Not created
//                          unless this option is specified.
// -T --timestampoutputs    When this option is used, timestamp in a format
//                          `YYYYMMDD-hhmmss` is added to all generated output
//                          files between their basename and extension. For
//                          example `-T -o output.xml -r report.html -l none`
//                          creates files like `output-20070503-154410.xml` and
//                          `report-20070503-154410.html`.
//    --splitlog            Split log file into smaller pieces that open in
//                          browser transparently.
//    --logtitle title      Title for the generated test log. The default title
//                          is ` Test Log`. Underscores in
//                          the title are converted into spaces in all titles.
//    --reporttitle title   Title for the generated test report. The default
//                          title is ` Test Report`.
//    --reportbackground colors  Background colors to use in the report file.
//                          Either `all_passed:critical_passed:failed` or
//                          `passed:failed`. Both color names and codes work.
//                          Examples: --reportbackground green:yellow:red
//                                    --reportbackground #00E:#E00
// -L --loglevel level      Threshold level for logging. Available levels: TRACE,
//                          DEBUG, INFO (default), WARN, NONE (no logging). Use
//                          syntax `LOGLEVEL:DEFAULT` to define the default
//                          visible log level in log files.
//                          Examples: --loglevel DEBUG
//                                    --loglevel DEBUG:INFO
//    --suitestatlevel level  How many levels to show in `Statistics by Suite`
//                          in log and report. By default all suite levels are
//                          shown. Example:  --suitestatlevel 3
//    --tagstatinclude tag *  Include only matching tags in `Statistics by Tag`
//                          and `Test Details` in log and report. By default all
//                          tags set in test cases are shown. Given `tag` can
//                          also be a simple pattern (see e.g. --test).
//    --tagstatexclude tag *  Exclude matching tags from `Statistics by Tag` and
//                          `Test Details`. This option can be used with
//                          --tagstatinclude similarly as --exclude is used with
//                          --include.
//    --tagstatcombine tags:name *  Create combined statistics based on tags.
//                          These statistics are added into `Statistics by Tag`
//                          and matching tests into `Test Details`. If optional
//                          `name` is not given, name of the combined tag is got
//                          from the specified tags. Tags are combined using the
//                          rules explained in --include.
//                          Examples: --tagstatcombine requirement-*
//                                    --tagstatcombine tag1ANDtag2:My_name
//    --tagdoc pattern:doc *  Add documentation to tags matching given pattern.
//                          Documentation is shown in `Test Details` and also as
//                          a tooltip in `Statistics by Tag`. Pattern can contain
//                          characters `*` (matches anything) and `?` (matches
//                          any char). Documentation can contain formatting
//                          similarly as with --doc option.
//                          Examples: --tagdoc mytag:My_documentation
//                                    --tagdoc regression:*See*_http://info.html
//                                    --tagdoc owner-*:Original_author
//    --tagstatlink pattern:link:title *  Add external links into `Statistics by
//                          Tag`. Pattern can contain characters `*` (matches
//                          anything) and `?` (matches any char). Characters
//                          matching to wildcard expressions can be used in link
//                          and title with syntax %N, where N is index of the
//                          match (starting from 1). In title underscores are
//                          automatically converted to spaces.
//                          Examples: --tagstatlink mytag:http://my.domain:Link
//                          --tagstatlink bug-*:http://tracker/id=%1:Bug_Tracker
//    --removekeywords all|passed|for|wuks|name: *  Remove keyword data
//                          from the generated log file. Keywords containing
//                          warnings are not removed except in `all` mode.
//                          all:     remove data from all keywords
//                          passed:  remove data only from keywords in passed
//                                   test cases and suites
//                          for:     remove passed iterations from for loops
//                          wuks:    remove all but the last failing keyword
//                                   inside `BuiltIn.Wait Until Keyword Succeeds`
//                          name::  remove data from keywords that match
//                                   the given pattern. The pattern is matched
//                                   against the full name of the keyword (e.g.
//                                   'MyLib.Keyword', 'resource.Second Keyword'),
//                                   is case, space, and underscore insensitive,
//                                   and may contain `*` and `?` as wildcards.
//                                   Examples: --removekeywords name:Lib.HugeKw
//                                             --removekeywords name:myresource.*
//    --flattenkeywords for|foritem|name: *  Flattens matching keywords
//                          in the generated log file. Matching keywords get all
//                          log messages from their child keywords and children
//                          are discarded otherwise.
//                          for:     flatten for loops fully
//                          foritem: flatten individual for loop iterations
//                          name::  flatten matched keywords using same
//                                   matching rules as with
//                                   `--removekeywords name:`
//    --listener class *    A class for monitoring test execution. Gets
//                          notifications e.g. when a test case starts and ends.
//                          Arguments to listener class can be given after class
//                          name, using colon as separator. For example:
//                          --listener MyListenerClass:arg1:arg2
//    --warnonskippedfiles  If this option is used, skipped test data files will
//                          cause a warning that is visible in the console output
//                          and the log file. By default skipped files only cause
//                          an info level syslog message.
//    --nostatusrc          Sets the return code to zero regardless of failures
//                          in test cases. Error codes are returned normally.
//    --runemptysuite       Executes tests also if the top level test suite is
//                          empty. Useful e.g. with --include/--exclude when it
//                          is not an error that no test matches the condition.
//    --dryrun              Verifies test data and runs tests so that library
//                          keywords are not executed.
//    --exitonfailure       Stops test execution if any critical test fails.
//    --exitonerror         Stops test execution if any error occurs when parsing
//                          test data, importing libraries, and so on.
//    --skipteardownonexit  Causes teardowns to be skipped if test execution is
//                          stopped prematurely.
//    --randomize all|suites|tests|none  Randomizes the test execution order.
//                          all:    randomizes both suites and tests
//                          suites: randomizes suites
//                          tests:  randomizes tests
//                          none:   no randomization (default)
//                          Use syntax `VALUE:SEED` to give a custom random seed.
//                          The seed must be an integer.
//                          Examples: --randomize all
//                                    --randomize tests:1234
//    --runmode mode *      Deprecated in version 2.8. Use individual options
//                          --dryrun, --exitonfailure, --skipteardownonexit, or
//                          --randomize instead.
// -W --monitorwidth chars  Width of the monitor output. Default is 78.
// -C --monitorcolors auto|on|ansi|off  Use colors on console output or not.
//                          auto: use colors when output not redirected (default)
//                          on:   always use colors
//                          ansi: like `on` but use ANSI colors also on Windows
//                          off:  disable colors altogether
//                          Note that colors do not work with Jython on Windows.
// -K --monitormarkers auto|on|off  Show `.` (success) or `F` (failure) on
//                          console when top level keywords in test cases end.
//                          Values have same semantics as with --monitorcolors.
// -P --pythonpath path *   Additional locations (directories, ZIPs, JARs) where
//                          to search test libraries from when they are imported.
//                          Multiple paths can be given by separating them with a
//                          colon (`:`) or using this option several times. Given
//                          path can also be a glob pattern matching multiple
//                          paths but then it normally must be escaped or quoted.
//                          Examples:
//                          --pythonpath libs/
//                          --pythonpath /opt/testlibs:mylibs.zip:yourlibs
//                          -E star:STAR -P lib/STAR.jar -P mylib.jar
// -E --escape what:with *  Escape characters which are problematic in console.
//                          `what` is the name of the character to escape and
//                          `with` is the string to escape it with. Note that
//                          all given arguments, incl. data sources, are escaped
//                          so escape characters ought to be selected carefully.
//                          <--------------------ESCAPES------------------------>
//                          Examples:
//                          --escape space:_ --metadata X:Value_with_spaces
//                          -E space:SP -E quot:Q -v var:QhelloSPworldQ
// -A --argumentfile path *  Text file to read more arguments from. Use special
//                          path `STDIN` to read contents from the standard input
//                          stream. File can have both options and data sources
//                          one per line. Contents do not need to be escaped but
//                          spaces in the beginning and end of lines are removed.
//                          Empty lines and lines starting with a hash character
//                          (#) are ignored.
//                          Example file:
//                          |  --include regression
//                          |  --name Regression Tests
//                          |  # This is a comment line
//                          |  my_tests.html
//                          |  path/to/test/directory/
//                          Examples:
//                          --argumentfile argfile.txt --argumentfile STDIN
// -h -? --help             Print usage instructions.
// --version                Print version information.
//
//Options that are marked with an asterisk (*) can be specified multiple times.
//For example, `--test first --test third` selects test cases with name `first`
//and `third`. If other options are given multiple times, the last value is used.
//
//Long option format is case-insensitive. For example, --SuiteStatLevel is
//equivalent to but easier to read than --suitestatlevel. Long options can
//also be shortened as long as they are unique. For example, `--logti Title`
//works while `--lo log.html` does not because the former matches only --logtitle
//but the latter matches --log, --loglevel and --logtitle.
//
//Environment Variables
//=====================
//
//ROBOT_OPTIONS             Space separated list of default options to be placed
//                          in front of any explicit options on the command line.
//ROBOT_SYSLOG_FILE         Path to a file where Robot Framework writes internal
//                          information about parsing test case files and running
//                          tests. Can be useful when debugging problems. If not
//                          set, or set to special value `NONE`, writing to the
//                          syslog file is disabled.
//ROBOT_SYSLOG_LEVEL        Log level to use when writing to the syslog file.
//                          Available levels are the same as for --loglevel
//                          command line option and the default is INFO.
//

  public static int runas(String givenScriptScript, boolean silent) {
    if (!runTime.runningMac) {
      return -1;
    }
    String prefix = silent ? "!" : "";
    String osascriptShebang = "#!/usr/bin/osascript\n";
    givenScriptScript = osascriptShebang + givenScriptScript;
    File aFile = FileManager.createTempFile("script");
    aFile.setExecutable(true);
    FileManager.writeStringToFile(givenScriptScript, aFile);
    String retVal = runTime.runcmd(new String[]{prefix + aFile.getAbsolutePath()});
    String[] parts = retVal.split("\n");
    int retcode = -1;
    try {
      retcode = Integer.parseInt(parts[0]);
    } catch (Exception ex) {
    }
    if (retcode != 0) {
      if (silent) {
        log(lvl, "AppleScript:\n%s\nreturned:\n%s", givenScriptScript, runTime.getLastCommandResult());
      } else {
        log(-1, "AppleScript:\n%s\nreturned:\n%s", givenScriptScript, runTime.getLastCommandResult());
      }
    }
    return retcode;
  }

  public static int runps(String givenScriptScript) {
    if (!runTime.runningWindows) {
      return -1;
    }
    File aFile = FileManager.createTempFile("ps1");
    FileManager.writeStringToFile(givenScriptScript, aFile);
    String[] psDirect = new String[]{
      "powershell.exe", "-ExecutionPolicy", "UnRestricted",
      "-NonInteractive", "-NoLogo", "-NoProfile", "-WindowStyle", "Hidden",
      "-File", aFile.getAbsolutePath()
    };
    String[] psCmdType = new String[]{
      "cmd.exe", "/S", "/C",
      "type " + aFile.getAbsolutePath() + " | powershell -noprofile -"
    };
    String retVal = runTime.runcmd(psCmdType);
    String[] parts = retVal.split("\\s");
    int retcode = -1;
    try {
      retcode = Integer.parseInt(parts[0]);
    } catch (Exception ex) {
    }
    if (retcode != 0) {
      log(-1, "PowerShell:\n%s\nreturned:\n%s", givenScriptScript, runTime.getLastCommandResult());
    }
    return retcode;
  }

  public static int runpy(File fScript, URL uGivenScript, String givenScriptScript, String[] args) {
    return new RunBox().runpy(fScript, uGivenScript, givenScriptScript, args);
  }

  public static int runrb(File fScript, URL uGivenScript, String givenScriptScript, String[] args) {
    return new RunBox().runrb(fScript, uGivenScript, givenScriptScript, args);
  }

  public static int runtxt(File fScript, URL uGivenScript, String givenScriptScript, String[] args) {
    return new RunBox().runtxt(fScript, uGivenScript, givenScriptScript, args);
  }

  static class RunBox {

    String jsScript;

    public RunBox() {
    }

    public static int runtxt(File fScript, URL script, String scriptName, String[] args) {
      Runner.log(-1, "Running plain text scripts not yet supported!");
      return -999;
    }

    public static int runrb(File fScript, URL script, String scriptName, String[] args) {
      Runner.log(-1, "Running Ruby scripts not yet supported!");
      return -999;
    }

    public static int runpy(File fScript, URL script, String scriptName, String[] args) {
      String fpScript;
      if (fScript == null) {
        fpScript = script.toExternalForm();
      } else {
        fpScript = fScript.getAbsolutePath();
      }
      if (!Runner.initpy()) {
        Runner.log(-1, "Running Python scripts:init failed");
        return -999;
      }
      if (args == null || args.length == 0) {
        args = RunTime.get().getArgs();
      }
      String[] newArgs = new String[args.length + 1];
      for (int i = 0; i < args.length; i++) {
        newArgs[i + 1] = args[i];
      }
      Runner.pyRunner.setSysArgv(newArgs);
      newArgs[0] = fpScript;
      int retval;
      if (fScript == null) {
        ImagePath.addHTTP(fpScript);
        retval = (Runner.pyRunner.exec(scriptName) ? 0 : -1);
        ImagePath.removeHTTP(fpScript);
      } else {
        if (null == ImagePath.getBundlePathSet())
          ImagePath.setBundlePath(fScript.getParent());
        else {
          ImagePath.add(fScript.getParent());
        }
        retval = Runner.pyRunner.execfile(fpScript);
      }
      return retval;
    }

    public void runjsEval(String script) {
      if (script.isEmpty()) {
        return;
      }
      String initSikulix = "";
      if (Runner.jsRunner == null) {
        Runner.jsRunner = Runner.initjs();
        initSikulix = Runner.prologjs(initSikulix);
      }
      try {
        if (!initSikulix.isEmpty()) {
          initSikulix = Runner.prologjs(initSikulix);
          Runner.jsRunner.eval(initSikulix);
        }
        Runner.log(lvl, "JavaScript: eval: %s", script);
        jsScript = script;
        Thread evalThread = new Thread() {
          @Override
          public void run() {
            try {
              Runner.mHide.invoke(null, new Class[0]);
              Runner.jsRunner.eval(jsScript);
              Runner.mShow.invoke(null, new Class[0]);
            } catch (Exception ex) {
              Runner.log(-1, "not possible:\n%s", ex);
              try {
                Runner.mShow.invoke(null, new Class[0]);
              } catch (Exception e) {
                Sikulix.terminate(901);
              }
            }
          }
        };
        evalThread.start();
      } catch (Exception ex) {
        Runner.log(-1, "init not possible:\n%s", ex);
      }
    }

    public int runjs(File fScript, URL script, String scriptName, String[] args) {
      String initSikulix = "";
      if (Runner.jsRunner == null) {
        Runner.jsRunner = Runner.initjs();
        initSikulix = Runner.prologjs(initSikulix);
      }
      try {
        if (null != fScript) {
          File innerBundle = new File(fScript.getParentFile(), scriptName + ".sikuli");
          if (innerBundle.exists()) {
            ImagePath.setBundlePath(innerBundle.getCanonicalPath());
          } else {
            ImagePath.setBundlePath(fScript.getParent());
          }
        } else if (script != null) {
          ImagePath.addHTTP(script.toExternalForm());
          String sname = new File(script.toExternalForm()).getName();
          ImagePath.addHTTP(script.toExternalForm() + "/" + sname + ".sikuli");
        }
        if (!initSikulix.isEmpty()) {
          initSikulix = Runner.prologjs(initSikulix);
          Runner.jsRunner.eval(initSikulix);
          initSikulix = "";
        }
        if (null != fScript) {
          Runner.jsRunner.eval(new FileReader(fScript));
        } else {
          Runner.jsRunner.eval(scriptName);
        }
      } catch (Exception ex) {
        Runner.log(-1, "not possible:\n%s", ex);
      }
      return 0;
    }

//    static File scriptProject = null;
//    static URL uScriptProject = null;
    RunTime runTime = RunTime.get();
    boolean asTest = false;
    String[] args = new String[0];

    String givenScriptHost = "";
    String givenScriptFolder = "";
    String givenScriptName = "";
    String givenScriptScript = "";
    String givenScriptType = "sikuli";
    String givenScriptScriptType = RDEFAULT;
    URL uGivenScript = null;
    URL uGivenScriptFile = null;
    boolean givenScriptExists = true;

    RunBox(String givenName, String[] givenArgs, boolean isTest) {
      Object[] vars = Runner.runBoxInit(givenName, RunTime.scriptProject, RunTime.uScriptProject);
      givenScriptHost = (String) vars[0];
      givenScriptFolder = (String) vars[1];
      givenScriptName = (String) vars[2];
      givenScriptScript = (String) vars[3];
      givenScriptType = (String) vars[4];
      givenScriptScriptType = (String) vars[5];
      uGivenScript = (URL) vars[6];
      uGivenScriptFile = (URL) vars[7];
      givenScriptExists = (Boolean) vars[8];
      RunTime.scriptProject = (File) vars[9];
      RunTime.uScriptProject = (URL) vars[10];
      args = givenArgs;
      asTest = isTest;
    }

    int run() {
      if (Runner.RASCRIPT.equals(givenScriptScriptType)) {
        return Runner.runas(givenScriptScript);
      } else if (Runner.RSSCRIPT.equals(givenScriptScriptType)) {
        return Runner.runps(givenScriptScript);
      } else if (Runner.RRSCRIPT.equals(givenScriptScriptType)) {
        return Runner.runrobot(givenScriptScript);
      }
      int exitCode = 0;
      log(lvl, "givenScriptName:\n%s", givenScriptName);
      if (-1 == FileManager.slashify(givenScriptName, false).indexOf("/") && RunTime.scriptProject != null) {
        givenScriptName = new File(RunTime.scriptProject, givenScriptName).getPath();
      }
      if (givenScriptName.endsWith(".skl")) {
        log(-1, "RunBox.run: .skl scripts not yet supported.");
        return -9999;
//        givenScriptName = FileManager.unzipSKL(givenScriptName);
//        if (givenScriptName == null) {
//          log(-1, "not possible to make .skl runnable");
//          return -9999;
//        }
      }
      if ("NET".equals(givenScriptType)) {
        if (Runner.RJSCRIPT.equals(givenScriptScriptType)) {
          exitCode = runjs(null, uGivenScript, givenScriptScript, args);
        } else if (Runner.RPYTHON.equals(givenScriptScriptType)) {
          exitCode = runpy(null, uGivenScript, givenScriptScript, args);
        } else {
          log(-1, "running from net not supported for %s\n%s", givenScriptScriptType, uGivenScript);
        }
      } else {
        File fScript = Runner.getScriptFile(new File(givenScriptName));
        if (fScript == null) {
          return -9999;
        }
        fScript = new File(FileManager.normalizeAbsolute(fScript.getPath(), true));
        if (null == RunTime.scriptProject) {
          RunTime.scriptProject = fScript.getParentFile().getParentFile();
        }
        log(lvl, "Trying to run script:\n%s", fScript);
        if (fScript.getName().endsWith(EJSCRIPT)) {
          exitCode = runjs(fScript, null, givenScriptScript, args);
        } else if (fScript.getName().endsWith(EPYTHON)) {
          exitCode = runpy(fScript, null, givenScriptScript, args);
        } else if (fScript.getName().endsWith(ERUBY)) {
          exitCode = runrb(fScript, null, givenScriptScript, args);
        } else if (fScript.getName().endsWith(EPLAIN)) {
          exitCode = runtxt(fScript, null, givenScriptScript, args);
        } else {
          log(-1, "Running not supported currently for:\n%s", fScript);
          return -9999;
        }
      }
      return exitCode;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy