blob: 7de13d1ebed6ac506a36726fcc9a87c27f0c3db2 [file] [log] [blame]
package com.jetbrains.env.python.debug;
import com.intellij.execution.*;
import com.intellij.execution.configurations.ConfigurationFactory;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.process.KillableColoredProcessHandler;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.xdebugger.*;
import com.jetbrains.python.debugger.PyDebugProcess;
import com.jetbrains.python.debugger.PyDebugRunner;
import com.jetbrains.python.run.PythonCommandLineState;
import com.jetbrains.python.run.PythonConfigurationType;
import com.jetbrains.python.run.PythonRunConfiguration;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.concurrent.Semaphore;
/**
* @author traff
*/
public class PyDebuggerTask extends PyBaseDebuggerTask {
private boolean myMultiprocessDebug = false;
private PythonRunConfiguration myRunConfiguration;
public PyDebuggerTask() {
init();
}
public PyDebuggerTask(String workingFolder, String scriptName, String scriptParameters) {
setWorkingFolder(getTestDataPath() + workingFolder);
setScriptName(scriptName);
setScriptParameters(scriptParameters);
init();
}
public PyDebuggerTask(String workingFolder, String scriptName) {
this(workingFolder, scriptName, null);
}
protected void init() {
}
public void runTestOn(String sdkHome) throws Exception {
final Project project = getProject();
final ConfigurationFactory factory = PythonConfigurationType.getInstance().getConfigurationFactories()[0];
final RunnerAndConfigurationSettings settings =
RunManager.getInstance(project).createRunConfiguration("test", factory);
myRunConfiguration = (PythonRunConfiguration)settings.getConfiguration();
myRunConfiguration.setSdkHome(sdkHome);
myRunConfiguration.setScriptName(getScriptPath());
myRunConfiguration.setWorkingDirectory(getWorkingFolder());
myRunConfiguration.setScriptParameters(getScriptParameters());
new WriteAction() {
@Override
protected void run(Result result) throws Throwable {
RunManagerEx.getInstanceEx(project).addConfiguration(settings, false);
RunManagerEx.getInstanceEx(project).setSelectedConfiguration(settings);
Assert.assertSame(settings, RunManagerEx.getInstanceEx(project).getSelectedConfiguration());
}
}.execute();
final PyDebugRunner runner = (PyDebugRunner)ProgramRunnerUtil.getRunner(DefaultDebugExecutor.EXECUTOR_ID, settings);
Assert.assertTrue(runner.canRun(DefaultDebugExecutor.EXECUTOR_ID, myRunConfiguration));
final Executor executor = DefaultDebugExecutor.getDebugExecutorInstance();
final ExecutionEnvironment env = new ExecutionEnvironment(executor, runner, settings, project);
final PythonCommandLineState pyState = (PythonCommandLineState)myRunConfiguration.getState(executor, env);
assert pyState != null;
pyState.setMultiprocessDebug(isMultiprocessDebug());
final ServerSocket serverSocket;
try {
//noinspection SocketOpenedButNotSafelyClosed
serverSocket = new ServerSocket(0);
}
catch (IOException e) {
throw new ExecutionException("Failed to find free socket port", e);
}
final int serverLocalPort = serverSocket.getLocalPort();
final RunProfile profile = env.getRunProfile();
before();
setProcessCanTerminate(false);
myTerminateSemaphore = new Semaphore(0);
new WriteAction<ExecutionResult>() {
@Override
protected void run(@NotNull Result<ExecutionResult> result) throws Throwable {
final ExecutionResult res =
pyState.execute(executor, PyDebugRunner.createCommandLinePatchers(myFixture.getProject(), pyState, profile, serverLocalPort));
mySession = XDebuggerManager.getInstance(getProject()).
startSession(runner, env, env.getContentToReuse(), new XDebugProcessStarter() {
@NotNull
public XDebugProcess start(@NotNull final XDebugSession session) {
myDebugProcess =
new PyDebugProcess(session, serverSocket, res.getExecutionConsole(), res.getProcessHandler(), isMultiprocessDebug());
myDebugProcess.getProcessHandler().addProcessListener(new ProcessAdapter() {
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
}
@Override
public void processTerminated(ProcessEvent event) {
myTerminateSemaphore.release();
if (event.getExitCode() != 0 && !myProcessCanTerminate) {
Assert.fail("Process terminated unexpectedly\n" + output());
}
}
});
myDebugProcess.getProcessHandler().startNotify();
return myDebugProcess;
}
});
result.setResult(res);
}
}.execute().getResultObject();
OutputPrinter myOutputPrinter = null;
if (shouldPrintOutput) {
myOutputPrinter = new OutputPrinter();
myOutputPrinter.start();
}
myPausedSemaphore = new Semaphore(0);
mySession.addSessionListener(new XDebugSessionAdapter() {
@Override
public void sessionPaused() {
if (myPausedSemaphore != null) {
myPausedSemaphore.release();
}
}
});
doTest(myOutputPrinter);
}
public PythonRunConfiguration getRunConfiguration() {
return myRunConfiguration;
}
private boolean isMultiprocessDebug() {
return myMultiprocessDebug;
}
public void setMultiprocessDebug(boolean multiprocessDebug) {
myMultiprocessDebug = multiprocessDebug;
}
@Override
protected void disposeDebugProcess() throws InterruptedException {
if (myDebugProcess != null) {
ProcessHandler processHandler = myDebugProcess.getProcessHandler();
myDebugProcess.stop();
waitFor(processHandler);
if (!processHandler.isProcessTerminated()) {
killDebugProcess();
if (!waitFor(processHandler)) {
new Throwable("Cannot stop debugger process").printStackTrace();
}
}
}
}
private void killDebugProcess() {
if (myDebugProcess.getProcessHandler() instanceof KillableColoredProcessHandler) {
KillableColoredProcessHandler h = (KillableColoredProcessHandler)myDebugProcess.getProcessHandler();
h.killProcess();
}
else {
myDebugProcess.getProcessHandler().destroyProcess();
}
}
}