2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.jetbrains.env.python.debug;
18 import com.google.common.collect.Sets;
19 import com.intellij.execution.*;
20 import com.intellij.execution.configurations.ConfigurationFactory;
21 import com.intellij.execution.configurations.RunProfile;
22 import com.intellij.execution.executors.DefaultDebugExecutor;
23 import com.intellij.execution.process.KillableColoredProcessHandler;
24 import com.intellij.execution.process.ProcessAdapter;
25 import com.intellij.execution.process.ProcessEvent;
26 import com.intellij.execution.process.ProcessHandler;
27 import com.intellij.execution.runners.ExecutionEnvironment;
28 import com.intellij.openapi.application.Result;
29 import com.intellij.openapi.application.WriteAction;
30 import com.intellij.openapi.project.Project;
31 import com.intellij.openapi.util.Key;
32 import com.intellij.xdebugger.*;
33 import com.jetbrains.env.python.PythonDebuggerTest;
34 import com.jetbrains.python.debugger.PyDebugProcess;
35 import com.jetbrains.python.debugger.PyDebugRunner;
36 import com.jetbrains.python.run.PythonCommandLineState;
37 import com.jetbrains.python.run.PythonConfigurationType;
38 import com.jetbrains.python.run.PythonRunConfiguration;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41 import org.junit.Assert;
43 import java.io.IOException;
44 import java.lang.reflect.InvocationTargetException;
45 import java.net.ServerSocket;
47 import java.util.concurrent.Semaphore;
52 public class PyDebuggerTask extends PyBaseDebuggerTask {
54 private boolean myMultiprocessDebug = false;
55 protected PythonRunConfiguration myRunConfiguration;
58 public PyDebuggerTask(@Nullable final String relativeTestDataPath, String scriptName, String scriptParameters) {
59 super(relativeTestDataPath);
60 setScriptName(scriptName);
61 setScriptParameters(scriptParameters);
65 public PyDebuggerTask(@Nullable final String relativeTestDataPath, String scriptName) {
66 this(relativeTestDataPath, scriptName, null);
69 protected void init() {
75 public Set<String> getTagsToCover() {
76 return Sets.newHashSet("python2.6", "python2.7", "python3.5", "python3.6", "jython", "IronPython", "pypy");
79 public void runTestOn(String sdkHome) throws Exception {
80 final Project project = getProject();
82 final ConfigurationFactory factory = PythonConfigurationType.getInstance().getConfigurationFactories()[0];
85 final RunnerAndConfigurationSettings settings =
86 RunManager.getInstance(project).createRunConfiguration("test", factory);
88 myRunConfiguration = (PythonRunConfiguration)settings.getConfiguration();
90 myRunConfiguration.setSdkHome(sdkHome);
91 myRunConfiguration.setScriptName(getScriptName());
92 myRunConfiguration.setWorkingDirectory(myFixture.getTempDirPath());
93 myRunConfiguration.setScriptParameters(getScriptParameters());
97 protected void run(@NotNull Result result) throws Throwable {
98 RunManagerEx.getInstanceEx(project).addConfiguration(settings, false);
99 RunManagerEx.getInstanceEx(project).setSelectedConfiguration(settings);
100 Assert.assertSame(settings, RunManagerEx.getInstanceEx(project).getSelectedConfiguration());
104 final PyDebugRunner runner = (PyDebugRunner)ProgramRunnerUtil.getRunner(getExecutorId(), settings);
105 Assert.assertTrue(runner.canRun(getExecutorId(), myRunConfiguration));
107 final Executor executor = DefaultDebugExecutor.getDebugExecutorInstance();
108 final ExecutionEnvironment env = new ExecutionEnvironment(executor, runner, settings, project);
110 final PythonCommandLineState pyState = (PythonCommandLineState)myRunConfiguration.getState(executor, env);
112 assert pyState != null;
113 pyState.setMultiprocessDebug(isMultiprocessDebug());
115 final ServerSocket serverSocket;
117 //noinspection SocketOpenedButNotSafelyClosed
118 serverSocket = new ServerSocket(0);
120 catch (IOException e) {
121 throw new ExecutionException("Failed to find free socket port", e);
125 final int serverLocalPort = serverSocket.getLocalPort();
126 final RunProfile profile = env.getRunProfile();
128 PythonDebuggerTest.createExceptionBreak(myFixture, false, false, false); //turn off exception breakpoints by default
132 setProcessCanTerminate(false);
134 myTerminateSemaphore = new Semaphore(0);
136 new WriteAction<ExecutionResult>() {
138 protected void run(@NotNull Result<ExecutionResult> result) throws Throwable {
140 pyState.execute(executor, runner.createCommandLinePatchers(myFixture.getProject(), pyState, profile, serverLocalPort));
142 mySession = XDebuggerManager.getInstance(getProject()).
143 startSession(env, new XDebugProcessStarter() {
145 public XDebugProcess start(@NotNull final XDebugSession session) {
147 new PyDebugProcess(session, serverSocket, myExecutionResult.getExecutionConsole(), myExecutionResult.getProcessHandler(), isMultiprocessDebug());
150 StringBuilder output = new StringBuilder();
152 myDebugProcess.getProcessHandler().addProcessListener(new ProcessAdapter() {
155 public void onTextAvailable(ProcessEvent event, Key outputType) {
156 output.append(event.getText());
160 public void processTerminated(ProcessEvent event) {
161 myTerminateSemaphore.release();
162 if (event.getExitCode() != 0 && !myProcessCanTerminate) {
163 Assert.fail("Process terminated unexpectedly\n" + output.toString());
169 myDebugProcess.getProcessHandler().startNotify();
171 return myDebugProcess;
174 result.setResult(myExecutionResult);
176 }.execute().getResultObject();
178 OutputPrinter myOutputPrinter = null;
179 if (shouldPrintOutput) {
180 myOutputPrinter = new OutputPrinter();
181 myOutputPrinter.start();
185 myPausedSemaphore = new Semaphore(0);
188 mySession.addSessionListener(new XDebugSessionListener() {
190 public void sessionPaused() {
191 if (myPausedSemaphore != null) {
192 myPausedSemaphore.release();
197 doTest(myOutputPrinter);
200 protected String getExecutorId() {
201 return DefaultDebugExecutor.EXECUTOR_ID;
204 public PythonRunConfiguration getRunConfiguration() {
205 return myRunConfiguration;
208 private boolean isMultiprocessDebug() {
209 return myMultiprocessDebug;
212 public void setMultiprocessDebug(boolean multiprocessDebug) {
213 myMultiprocessDebug = multiprocessDebug;
216 protected void waitForAllThreadsPause() throws InterruptedException, InvocationTargetException {
218 Assert.assertTrue(String.format("All threads didn't stop within timeout\n" +
219 "Output: %s", output()), waitForAllThreads());
220 XDebuggerTestUtil.waitForSwing();
223 protected boolean waitForAllThreads() throws InterruptedException {
224 long until = System.currentTimeMillis() + NORMAL_TIMEOUT;
225 while (System.currentTimeMillis() < until && getRunningThread() != null) {
228 return getRunningThread() == null;
232 protected void disposeDebugProcess() throws InterruptedException {
233 if (myDebugProcess != null) {
234 ProcessHandler processHandler = myDebugProcess.getProcessHandler();
236 myDebugProcess.stop();
238 waitFor(processHandler);
240 if (!processHandler.isProcessTerminated()) {
242 if (!waitFor(processHandler)) {
243 new Throwable("Cannot stop debugger process").printStackTrace();
249 private void killDebugProcess() {
250 if (myDebugProcess.getProcessHandler() instanceof KillableColoredProcessHandler) {
251 KillableColoredProcessHandler h = (KillableColoredProcessHandler)myDebugProcess.getProcessHandler();
256 myDebugProcess.getProcessHandler().destroyProcess();