/* * Copyright 2016 Nokia Solutions and Networks * Licensed under the Apache License, Version 2.0, * see license.txt file for details. */ package org.rf.ide.core.executor; import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Optional; import org.rf.ide.core.executor.RobotRuntimeEnvironment.PythonInstallationDirectory; /** * @author Michal Anglart */ public class RunCommandLineCallBuilder { public static interface IRunCommandLineBuilder { IRunCommandLineBuilder withExecutableFile(final File executableFile); IRunCommandLineBuilder addUserArgumentsForExecutableFile(final Collection<String> arguments); IRunCommandLineBuilder useSingleRobotCommandLineArg(boolean shouldUseSingleRobotCommandLineArg); IRunCommandLineBuilder addUserArgumentsForInterpreter(final Collection<String> arguments); IRunCommandLineBuilder enableDryRun(); IRunCommandLineBuilder useArgumentFile(boolean shouldUseArgumentFile); IRunCommandLineBuilder addLocationsToPythonPath(final Collection<String> pythonPathLocations); IRunCommandLineBuilder addLocationsToClassPath(final Collection<String> classPathLocations); IRunCommandLineBuilder addVariableFiles(final Collection<String> varFiles); IRunCommandLineBuilder suitesToRun(final Collection<String> suites); IRunCommandLineBuilder testsToRun(final Collection<String> tests); IRunCommandLineBuilder includeTags(final Collection<String> tags); IRunCommandLineBuilder excludeTags(final Collection<String> tags); IRunCommandLineBuilder addUserArgumentsForRobot(final Collection<String> arguments); IRunCommandLineBuilder withProject(final File project); IRunCommandLineBuilder withAdditionalProjectsLocations(final Collection<File> additionalProjectsLocations); RunCommandLine build() throws IOException; } private static class Builder implements IRunCommandLineBuilder { private final SuiteExecutor executor; private final String executorPath; private final int listenerPort; private boolean enableDryRun = false; private boolean useArgumentFile = false; private final List<String> pythonPath = new ArrayList<>(); private final List<String> classPath = new ArrayList<>(); private final List<String> variableFiles = new ArrayList<>(); private final List<String> suitesToRun = new ArrayList<>(); private final List<String> testsToRun = new ArrayList<>(); private final List<String> tagsToInclude = new ArrayList<>(); private final List<String> tagsToExclude = new ArrayList<>(); private File project = null; private final List<File> additionalProjectsLocations = new ArrayList<>(); private final List<String> robotUserArgs = new ArrayList<>(); private final List<String> interpreterUserArgs = new ArrayList<>(); private File executableFile = null; private final List<String> executableFileArgs = new ArrayList<>(); private boolean useSingleRobotCommandLineArg = false; private Builder(final SuiteExecutor executor, final String executorPath, final int listenerPort) { this.executor = executor; this.executorPath = executorPath; this.listenerPort = listenerPort; } @Override public IRunCommandLineBuilder withExecutableFile(final File executableFile) { this.executableFile = executableFile; return this; } @Override public IRunCommandLineBuilder addUserArgumentsForExecutableFile(final Collection<String> arguments) { this.executableFileArgs.addAll(arguments); return this; } @Override public IRunCommandLineBuilder useSingleRobotCommandLineArg(final boolean shouldUseSingleRobotCommandLineArg) { this.useSingleRobotCommandLineArg = shouldUseSingleRobotCommandLineArg; return this; } @Override public IRunCommandLineBuilder addUserArgumentsForInterpreter(final Collection<String> arguments) { this.interpreterUserArgs.addAll(arguments); return this; } @Override public IRunCommandLineBuilder enableDryRun() { this.enableDryRun = true; return this; } @Override public IRunCommandLineBuilder useArgumentFile(final boolean shouldUseArgumentFile) { this.useArgumentFile = shouldUseArgumentFile; return this; } @Override public IRunCommandLineBuilder addLocationsToPythonPath(final Collection<String> pythonPathLocations) { pythonPath.addAll(pythonPathLocations); return this; } @Override public IRunCommandLineBuilder addLocationsToClassPath(final Collection<String> classPathLocations) { classPath.addAll(classPathLocations); return this; } @Override public IRunCommandLineBuilder addVariableFiles(final Collection<String> varFiles) { variableFiles.addAll(varFiles); return this; } @Override public IRunCommandLineBuilder suitesToRun(final Collection<String> suites) { suitesToRun.addAll(suites); return this; } @Override public IRunCommandLineBuilder testsToRun(final Collection<String> tests) { testsToRun.addAll(tests); return this; } @Override public IRunCommandLineBuilder includeTags(final Collection<String> tags) { tagsToInclude.addAll(tags); return this; } @Override public IRunCommandLineBuilder excludeTags(final Collection<String> tags) { tagsToExclude.addAll(tags); return this; } @Override public IRunCommandLineBuilder addUserArgumentsForRobot(final Collection<String> arguments) { this.robotUserArgs.addAll(arguments); return this; } @Override public IRunCommandLineBuilder withProject(final File project) { this.project = project; return this; } @Override public IRunCommandLineBuilder withAdditionalProjectsLocations( final Collection<File> additionalProjectsLocations) { this.additionalProjectsLocations.addAll(additionalProjectsLocations); return this; } @Override public RunCommandLine build() throws IOException { final RunCommandLine robotRunCommandLine = buildRobotRunCommandLine(); if (executableFile != null) { return buildRunCommandLineWrappedWithExecutable(robotRunCommandLine); } return robotRunCommandLine; } private RunCommandLine buildRunCommandLineWrappedWithExecutable(final RunCommandLine robotRunCommandLine) { final List<String> cmdLine = new ArrayList<>(); cmdLine.add(executableFile.getAbsolutePath()); cmdLine.addAll(executableFileArgs); if (useSingleRobotCommandLineArg) { cmdLine.add(String.join(" ", robotRunCommandLine.getCommandLine())); } else { cmdLine.addAll(Arrays.asList(robotRunCommandLine.getCommandLine())); } if (robotRunCommandLine.getArgumentFile().isPresent()) { return new RunCommandLine(cmdLine, robotRunCommandLine.getArgumentFile().get()); } return new RunCommandLine(cmdLine, null); } private RunCommandLine buildRobotRunCommandLine() throws IOException { final List<String> cmdLine = new ArrayList<>(); cmdLine.add(executorPath); if (executor == SuiteExecutor.Jython) { final String additionalPythonPathLocationForJython = extractAdditionalPythonPathLocationForJython(); if (!additionalPythonPathLocationForJython.isEmpty()) { cmdLine.add(additionalPythonPathLocationForJython); } cmdLine.add("-J-cp"); cmdLine.add(classPath()); } cmdLine.addAll(interpreterUserArgs); cmdLine.add("-m"); cmdLine.add("robot.run"); cmdLine.add("--listener"); cmdLine.add(RobotRuntimeEnvironment.copyScriptFile("TestRunnerAgent.py").toPath() + ":" + listenerPort); ArgumentsFile argumentsFile = null; if (useArgumentFile) { argumentsFile = createArgumentsFile(); cmdLine.add("--argumentfile"); cmdLine.add(argumentsFile.writeToTemporaryOrUseAlreadyExisting().toPath().toString()); } else { cmdLine.addAll(createInlinedArguments()); } if (project != null) { cmdLine.add(project.getAbsolutePath()); } for (final File projectLocation : additionalProjectsLocations) { cmdLine.add(projectLocation.getAbsolutePath()); } return new RunCommandLine(cmdLine, argumentsFile); } private List<String> createInlinedArguments() throws IOException { final List<String> robotArgs = new ArrayList<>(); if (!pythonPath.isEmpty()) { robotArgs.add("-P"); robotArgs.add(pythonPath()); } for (final String varFile : variableFiles) { robotArgs.add("-V"); robotArgs.add(varFile); } for (final String tagToInclude : tagsToInclude) { robotArgs.add("-i"); robotArgs.add(tagToInclude); } for (final String tagToExclude : tagsToExclude) { robotArgs.add("-e"); robotArgs.add(tagToExclude); } if (enableDryRun) { robotArgs.add("--prerunmodifier"); robotArgs.add(RobotRuntimeEnvironment.copyScriptFile("SuiteVisitorImportProxy.py").toPath().toString()); robotArgs.add("--runemptysuite"); robotArgs.add("--dryrun"); robotArgs.add("--output"); robotArgs.add("NONE"); robotArgs.add("--report"); robotArgs.add("NONE"); robotArgs.add("--log"); robotArgs.add("NONE"); robotArgs.add("--console"); robotArgs.add("NONE"); } for (final String suite : suitesToRun) { robotArgs.add("-s"); robotArgs.add(suite); } for (final String test : testsToRun) { robotArgs.add("-t"); robotArgs.add(test); } robotArgs.addAll(robotUserArgs); return robotArgs; } private ArgumentsFile createArgumentsFile() throws IOException { final ArgumentsFile argumentsFile = new ArgumentsFile(); argumentsFile.addCommentLine("arguments automatically generated"); if (!pythonPath.isEmpty()) { argumentsFile.addLine("--pythonpath", pythonPath()); } for (final String varFile : variableFiles) { argumentsFile.addLine("--variablefile", varFile); } for (final String tagToInclude : tagsToInclude) { argumentsFile.addLine("--include", tagToInclude); } for (final String tagToExclude : tagsToExclude) { argumentsFile.addLine("--exclude", tagToExclude); } if (enableDryRun) { argumentsFile.addLine("--prerunmodifier", RobotRuntimeEnvironment.copyScriptFile("SuiteVisitorImportProxy.py").toPath().toString()); argumentsFile.addLine("--runemptysuite"); argumentsFile.addLine("--dryrun"); argumentsFile.addLine("--output", "NONE"); argumentsFile.addLine("--report", "NONE"); argumentsFile.addLine("--log", "NONE"); argumentsFile.addLine("--console", "NONE"); } for (final String suiteToRun : suitesToRun) { argumentsFile.addLine("--suite", suiteToRun); } for (final String testToRun : testsToRun) { argumentsFile.addLine("--test", testToRun); } if (!robotUserArgs.isEmpty()) { addUserArguments(argumentsFile); } return argumentsFile; } private void addUserArguments(final ArgumentsFile argumentsFile) { argumentsFile.addCommentLine("arguments specified manually by user"); int i = 0; while (i < robotUserArgs.size()) { if (robotUserArgs.get(i).startsWith("-") && i < robotUserArgs.size() - 1 && !robotUserArgs.get(i + 1).startsWith("-")) { argumentsFile.addLine(robotUserArgs.get(i), robotUserArgs.get(i + 1)); i++; } else { argumentsFile.addLine(robotUserArgs.get(i)); } i++; } } private String classPath() { final List<String> fullClasspath = RedSystemProperties.getClassPaths(); fullClasspath.addAll(classPath); return String.join(RedSystemProperties.getPathsSeparator(), fullClasspath); } private String pythonPath() { return String.join(":", pythonPath); } private String extractAdditionalPythonPathLocationForJython() { String additionalPythonPathLocation = ""; final Path jythonPath = Paths.get(executorPath); Path jythonParentPath = jythonPath.getParent(); if (jythonParentPath == null) { final List<PythonInstallationDirectory> pythonInterpreters = RobotRuntimeEnvironment .whereArePythonInterpreters(); for (final PythonInstallationDirectory pythonInstallationDirectory : pythonInterpreters) { if (pythonInstallationDirectory.getInterpreter() == SuiteExecutor.Jython) { jythonParentPath = pythonInstallationDirectory.toPath(); break; } } } if (jythonParentPath != null && jythonParentPath.getFileName() != null && jythonParentPath.getFileName().toString().equalsIgnoreCase("bin")) { final Path mainDir = jythonParentPath.getParent(); final Path sitePackagesDir = Paths.get(mainDir.toString(), "Lib", "site-packages"); // in case of 'robot' folder existing in project additionalPythonPathLocation = "-J-Dpython.path=" + sitePackagesDir.toString(); } return additionalPythonPathLocation; } } public static IRunCommandLineBuilder forEnvironment(final RobotRuntimeEnvironment env, final int listenerPort) { return new Builder(env.getInterpreter(), env.getPythonExecutablePath(), listenerPort); } public static IRunCommandLineBuilder forExecutor(final SuiteExecutor executor, final int listenerPort) { return new Builder(executor, executor.executableName(), listenerPort); } public static class RunCommandLine { private final List<String> commandLine; private final Optional<ArgumentsFile> argFile; RunCommandLine(final List<String> commandLine, final ArgumentsFile argumentsFile) { this.commandLine = new ArrayList<>(commandLine); this.argFile = Optional.ofNullable(argumentsFile); } public Optional<ArgumentsFile> getArgumentFile() { return argFile; } public String[] getCommandLine() { return commandLine.toArray(new String[0]); } } }