/*
* Copyright 2015 Nokia Solutions and Networks
* Licensed under the Apache License, Version 2.0,
* see license.txt file for details.
*/
package org.robotframework.ide.eclipse.main.plugin.launch.local;
import static org.robotframework.ide.eclipse.main.plugin.RedPlugin.newCoreException;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.rf.ide.core.execution.RobotAgentEventListener;
import org.rf.ide.core.execution.TestsMode;
import org.rf.ide.core.execution.server.AgentServerKeepAlive;
import org.rf.ide.core.execution.server.AgentServerTestsStarter;
import org.rf.ide.core.executor.RobotRuntimeEnvironment;
import org.rf.ide.core.executor.RobotRuntimeEnvironment.RobotEnvironmentException;
import org.rf.ide.core.executor.RunCommandLineCallBuilder;
import org.rf.ide.core.executor.RunCommandLineCallBuilder.IRunCommandLineBuilder;
import org.rf.ide.core.executor.RunCommandLineCallBuilder.RunCommandLine;
import org.robotframework.ide.eclipse.main.plugin.RedPlugin;
import org.robotframework.ide.eclipse.main.plugin.RedPreferences;
import org.robotframework.ide.eclipse.main.plugin.debug.model.RobotDebugTarget;
import org.robotframework.ide.eclipse.main.plugin.launch.AbstractRobotLaunchConfigurationDelegate;
import org.robotframework.ide.eclipse.main.plugin.launch.AgentConnectionServerJob;
import org.robotframework.ide.eclipse.main.plugin.launch.DebugExecutionEventsListener;
import org.robotframework.ide.eclipse.main.plugin.launch.IRobotProcess;
import org.robotframework.ide.eclipse.main.plugin.launch.RobotConsoleFacade;
import org.robotframework.ide.eclipse.main.plugin.launch.RobotConsolePatternsListener;
import org.robotframework.ide.eclipse.main.plugin.launch.RobotTestExecutionService.RobotTestsLaunch;
import org.robotframework.ide.eclipse.main.plugin.model.RobotModel;
import org.robotframework.ide.eclipse.main.plugin.model.RobotProject;
import org.robotframework.ide.eclipse.main.plugin.views.execution.ExecutionElementsTracker;
import org.robotframework.ide.eclipse.main.plugin.views.message.ExecutionMessagesTracker;
import com.google.common.annotations.VisibleForTesting;
public class RobotLaunchConfigurationDelegate extends AbstractRobotLaunchConfigurationDelegate {
@Override
protected LaunchExecution doLaunch(final ILaunchConfiguration configuration, final TestsMode testsMode,
final ILaunch launch, final RobotTestsLaunch testsLaunchContext) throws CoreException {
final RobotLaunchConfiguration robotConfig = new RobotLaunchConfiguration(configuration);
final String host = robotConfig.getAgentConnectionHost();
final int port = robotConfig.getAgentConnectionPort();
final int timeout = robotConfig.getAgentConnectionTimeout();
try {
final AgentServerTestsStarter testsStarter = new AgentServerTestsStarter(testsMode);
final LaunchExecution launchExecution;
if (testsMode == TestsMode.RUN) {
launchExecution = doLaunch(robotConfig, launch, testsLaunchContext, host, port, timeout,
Arrays.asList(testsStarter));
} else {
final RobotDebugTarget debugTarget = new RobotDebugTarget("Robot Test at " + host + ":" + port, launch);
final DebugExecutionEventsListener debugListener = new DebugExecutionEventsListener(debugTarget,
robotConfig.getResourcesUnderDebug());
launchExecution = doLaunch(robotConfig, launch, testsLaunchContext, host, port, timeout,
Arrays.asList(testsStarter, debugListener));
if (launchExecution.getRobotProcess() != null) {
debugTarget.connectWith(launchExecution.getRobotProcess());
}
}
testsStarter.allowClientTestsStart();
return launchExecution;
} catch (final InterruptedException e) {
throw newCoreException("Interrupted when waiting for remote connection server", e);
} catch (final IOException e) {
throw newCoreException("Unable to launch Robot", e);
}
}
private LaunchExecution doLaunch(final RobotLaunchConfiguration robotConfig, final ILaunch launch,
final RobotTestsLaunch testsLaunchContext, final String host, final int port, final int timeout,
final List<RobotAgentEventListener> additionalListeners)
throws InterruptedException, CoreException, IOException {
final RobotModel model = RedPlugin.getModelManager().getModel();
final RobotProject robotProject = model.createRobotProject(robotConfig.getProject());
final RobotRuntimeEnvironment robotRuntimeEnvironment = getRobotRuntimeEnvironment(robotProject);
final String suiteExecutorVersion = getSuiteExecutorVersion(robotConfig, robotRuntimeEnvironment);
final AgentConnectionServerJob serverJob = AgentConnectionServerJob.setupServerAt(host, port)
.withConnectionTimeout(timeout, TimeUnit.SECONDS)
.serverStatusHandledBy(new ServerProblemsHandler())
.agentEventsListenedBy(additionalListeners)
.agentEventsListenedBy(new ExecutionMessagesTracker(testsLaunchContext))
.agentEventsListenedBy(new ExecutionElementsTracker(testsLaunchContext))
.agentEventsListenedBy(new AgentServerKeepAlive())
.start()
.waitForServer();
if (serverJob.getResult() != null && !serverJob.getResult().isOK()) {
return new LaunchExecution(serverJob, null, null);
}
final RunCommandLine cmdLine = prepareCommandLine(robotConfig, robotProject, port);
final Process execProcess = DebugPlugin.exec(cmdLine.getCommandLine(),
robotProject.getProject().getLocation().toFile(), robotConfig.getEnvironmentVariables());
final String processLabel = createConsoleDescription(robotConfig, robotRuntimeEnvironment);
final IRobotProcess robotProcess = (IRobotProcess) DebugPlugin.newProcess(launch, execProcess, processLabel);
robotProcess.onTerminate(serverJob::stopServer);
final RobotConsoleFacade redConsole = robotProcess.provideConsoleFacade(processLabel);
redConsole.addHyperlinksSupport(new RobotConsolePatternsListener(robotProject));
redConsole.writeLine("Command: " + DebugPlugin.renderArguments(cmdLine.getCommandLine(), null));
redConsole.writeLine("Suite Executor: " + suiteExecutorVersion);
return new LaunchExecution(serverJob, execProcess, robotProcess);
}
private RobotRuntimeEnvironment getRobotRuntimeEnvironment(final RobotProject robotProject) throws CoreException {
final RobotRuntimeEnvironment runtimeEnvironment = robotProject.getRuntimeEnvironment();
if (runtimeEnvironment == null) {
throw newCoreException(
"There is no active runtime environment for project '" + robotProject.getName() + "'");
}
if (!runtimeEnvironment.hasRobotInstalled()) {
throw newCoreException("The runtime environment " + runtimeEnvironment.getFile().getAbsolutePath()
+ " is either not a python installation or it has no Robot installed");
}
return runtimeEnvironment;
}
private String getSuiteExecutorVersion(final RobotLaunchConfiguration robotConfig,
final RobotRuntimeEnvironment env) throws CoreException {
try {
return robotConfig.isUsingInterpreterFromProject() ? env.getVersion()
: RobotRuntimeEnvironment.getVersion(robotConfig.getInterpreter());
} catch (final RobotEnvironmentException e) {
throw newCoreException(e.getMessage(), e.getCause());
}
}
@VisibleForTesting
RunCommandLine prepareCommandLine(final RobotLaunchConfiguration robotConfig, final RobotProject robotProject,
final int port) throws CoreException, IOException {
final RedPreferences preferences = RedPlugin.getDefault().getPreferences();
final IRunCommandLineBuilder builder = robotConfig.isUsingInterpreterFromProject()
? RunCommandLineCallBuilder.forEnvironment(robotProject.getRuntimeEnvironment(), port)
: RunCommandLineCallBuilder.forExecutor(robotConfig.getInterpreter(), port);
builder.useArgumentFile(preferences.shouldLaunchUsingArgumentsFile());
if (!robotConfig.getExecutableFilePath().isEmpty()) {
final File executableFile = new File(robotConfig.getExecutableFilePath());
if (!executableFile.exists()) {
throw newCoreException("Executable file '" + executableFile.getAbsolutePath() + "' does not exist");
}
builder.withExecutableFile(executableFile);
builder.addUserArgumentsForExecutableFile(parseArguments(robotConfig.getExecutableFileArguments()));
builder.useSingleRobotCommandLineArg(preferences.shouldUseSingleCommandLineArgument());
}
builder.withProject(robotProject.getProject().getLocation().toFile());
builder.addLocationsToClassPath(robotProject.getClasspath());
builder.addLocationsToPythonPath(robotProject.getPythonpath());
builder.addUserArgumentsForInterpreter(parseArguments(robotConfig.getInterpreterArguments()));
builder.addUserArgumentsForRobot(parseArguments(robotConfig.getRobotArguments()));
builder.addVariableFiles(robotProject.getVariableFilePaths());
builder.suitesToRun(robotConfig.getSuitesToRun());
builder.testsToRun(robotConfig.getTestsToRun());
if (robotConfig.isIncludeTagsEnabled()) {
builder.includeTags(robotConfig.getIncludedTags());
}
if (robotConfig.isExcludeTagsEnabled()) {
builder.excludeTags(robotConfig.getExcludedTags());
}
return builder.build();
}
private List<String> parseArguments(final String arguments) {
return Arrays.asList(DebugPlugin.parseArguments(arguments));
}
private String createConsoleDescription(final RobotLaunchConfiguration robotConfig,
final RobotRuntimeEnvironment env) throws CoreException {
return robotConfig.isUsingInterpreterFromProject() ? env.getPythonExecutablePath()
: robotConfig.getInterpreter().executableName();
}
}