Merge remote-tracking branch 'origin/master'
[idea/community.git] / python / testSrc / com / jetbrains / env / python / debug / PyDebuggerTask.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.jetbrains.env.python.debug;
17
18 import com.intellij.execution.*;
19 import com.intellij.execution.configurations.ConfigurationFactory;
20 import com.intellij.execution.configurations.RunProfile;
21 import com.intellij.execution.executors.DefaultDebugExecutor;
22 import com.intellij.execution.process.KillableColoredProcessHandler;
23 import com.intellij.execution.process.ProcessAdapter;
24 import com.intellij.execution.process.ProcessEvent;
25 import com.intellij.execution.process.ProcessHandler;
26 import com.intellij.execution.runners.ExecutionEnvironment;
27 import com.intellij.openapi.application.Result;
28 import com.intellij.openapi.application.WriteAction;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.xdebugger.*;
31 import com.jetbrains.env.python.PythonDebuggerTest;
32 import com.jetbrains.python.debugger.PyDebugProcess;
33 import com.jetbrains.python.debugger.PyDebugRunner;
34 import com.jetbrains.python.run.PythonCommandLineState;
35 import com.jetbrains.python.run.PythonConfigurationType;
36 import com.jetbrains.python.run.PythonRunConfiguration;
37 import org.jetbrains.annotations.NotNull;
38 import org.jetbrains.annotations.Nullable;
39 import org.junit.Assert;
40
41 import java.io.IOException;
42 import java.lang.reflect.InvocationTargetException;
43 import java.net.ServerSocket;
44 import java.util.concurrent.Semaphore;
45
46 /**
47  * @author traff
48  */
49 public class PyDebuggerTask extends PyBaseDebuggerTask {
50
51   private boolean myMultiprocessDebug = false;
52   private PythonRunConfiguration myRunConfiguration;
53
54
55   public PyDebuggerTask(@Nullable final String relativeTestDataPath, String scriptName, String scriptParameters) {
56     super(relativeTestDataPath);
57     setScriptName(scriptName);
58     setScriptParameters(scriptParameters);
59     init();
60   }
61
62   public PyDebuggerTask(@Nullable final String relativeTestDataPath, String scriptName) {
63     this(relativeTestDataPath, scriptName, null);
64   }
65
66   protected void init() {
67
68   }
69
70   public void runTestOn(String sdkHome) throws Exception {
71     final Project project = getProject();
72
73     final ConfigurationFactory factory = PythonConfigurationType.getInstance().getConfigurationFactories()[0];
74
75
76     final RunnerAndConfigurationSettings settings =
77       RunManager.getInstance(project).createRunConfiguration("test", factory);
78
79     myRunConfiguration = (PythonRunConfiguration)settings.getConfiguration();
80
81     myRunConfiguration.setSdkHome(sdkHome);
82     myRunConfiguration.setScriptName(getScriptName());
83     myRunConfiguration.setWorkingDirectory(myFixture.getTempDirPath());
84     myRunConfiguration.setScriptParameters(getScriptParameters());
85
86     new WriteAction() {
87       @Override
88       protected void run(@NotNull Result result) throws Throwable {
89         RunManagerEx.getInstanceEx(project).addConfiguration(settings, false);
90         RunManagerEx.getInstanceEx(project).setSelectedConfiguration(settings);
91         Assert.assertSame(settings, RunManagerEx.getInstanceEx(project).getSelectedConfiguration());
92       }
93     }.execute();
94
95     final PyDebugRunner runner = (PyDebugRunner)ProgramRunnerUtil.getRunner(getExecutorId(), settings);
96     Assert.assertTrue(runner.canRun(getExecutorId(), myRunConfiguration));
97
98     final Executor executor = DefaultDebugExecutor.getDebugExecutorInstance();
99     final ExecutionEnvironment env = new ExecutionEnvironment(executor, runner, settings, project);
100
101     final PythonCommandLineState pyState = (PythonCommandLineState)myRunConfiguration.getState(executor, env);
102
103     assert pyState != null;
104     pyState.setMultiprocessDebug(isMultiprocessDebug());
105
106     final ServerSocket serverSocket;
107     try {
108       //noinspection SocketOpenedButNotSafelyClosed
109       serverSocket = new ServerSocket(0);
110     }
111     catch (IOException e) {
112       throw new ExecutionException("Failed to find free socket port", e);
113     }
114
115
116     final int serverLocalPort = serverSocket.getLocalPort();
117     final RunProfile profile = env.getRunProfile();
118
119     PythonDebuggerTest.createExceptionBreak(myFixture, false, false, false); //turn off exception breakpoints by default
120
121     before();
122
123     setProcessCanTerminate(false);
124
125     myTerminateSemaphore = new Semaphore(0);
126     
127     new WriteAction<ExecutionResult>() {
128       @Override
129       protected void run(@NotNull Result<ExecutionResult> result) throws Throwable {
130         myExecutionResult =
131           pyState.execute(executor, runner.createCommandLinePatchers(myFixture.getProject(), pyState, profile, serverLocalPort));
132
133         mySession = XDebuggerManager.getInstance(getProject()).
134           startSession(env, new XDebugProcessStarter() {
135             @NotNull
136             public XDebugProcess start(@NotNull final XDebugSession session) {
137               myDebugProcess =
138                 new PyDebugProcess(session, serverSocket, myExecutionResult.getExecutionConsole(), myExecutionResult.getProcessHandler(), isMultiprocessDebug());
139
140               myDebugProcess.getProcessHandler().addProcessListener(new ProcessAdapter() {
141
142                 @Override
143                 public void processTerminated(ProcessEvent event) {
144                   myTerminateSemaphore.release();
145                   if (event.getExitCode() != 0 && !myProcessCanTerminate) {
146                     Assert.fail("Process terminated unexpectedly\n" + output());
147                   }
148                 }
149               });
150
151
152               myDebugProcess.getProcessHandler().startNotify();
153
154               return myDebugProcess;
155             }
156           });
157         result.setResult(myExecutionResult);
158       }
159     }.execute().getResultObject();
160
161     OutputPrinter myOutputPrinter = null;
162     if (shouldPrintOutput) {
163       myOutputPrinter = new OutputPrinter();
164       myOutputPrinter.start();
165     }
166
167
168     myPausedSemaphore = new Semaphore(0);
169     
170
171     mySession.addSessionListener(new XDebugSessionListener() {
172       @Override
173       public void sessionPaused() {
174         if (myPausedSemaphore != null) {
175           myPausedSemaphore.release();
176         }
177       }
178     });
179
180     doTest(myOutputPrinter);
181   }
182
183   protected String getExecutorId() {
184     return DefaultDebugExecutor.EXECUTOR_ID;
185   }
186
187   public PythonRunConfiguration getRunConfiguration() {
188     return myRunConfiguration;
189   }
190
191   private boolean isMultiprocessDebug() {
192     return myMultiprocessDebug;
193   }
194
195   public void setMultiprocessDebug(boolean multiprocessDebug) {
196     myMultiprocessDebug = multiprocessDebug;
197   }
198
199   protected void waitForAllThreadsPause() throws InterruptedException, InvocationTargetException {
200     waitForPause();
201     Assert.assertTrue(String.format("All threads didn't stop within timeout\n" +
202                                     "Output: %s", output()), waitForAllThreads());
203     XDebuggerTestUtil.waitForSwing();
204   }
205
206   protected boolean waitForAllThreads() throws InterruptedException {
207     long until = System.currentTimeMillis() + NORMAL_TIMEOUT;
208     while (System.currentTimeMillis() < until && getRunningThread() != null) {
209       Thread.sleep(1000);
210     }
211     return getRunningThread() == null;
212   }
213
214   @Override
215   protected void disposeDebugProcess() throws InterruptedException {
216     if (myDebugProcess != null) {
217       ProcessHandler processHandler = myDebugProcess.getProcessHandler();
218
219       myDebugProcess.stop();
220
221       waitFor(processHandler);
222
223       if (!processHandler.isProcessTerminated()) {
224         killDebugProcess();
225         if (!waitFor(processHandler)) {
226           new Throwable("Cannot stop debugger process").printStackTrace();
227         }
228       }
229     }
230   }
231
232   private void killDebugProcess() {
233     if (myDebugProcess.getProcessHandler() instanceof KillableColoredProcessHandler) {
234       KillableColoredProcessHandler h = (KillableColoredProcessHandler)myDebugProcess.getProcessHandler();
235
236       h.killProcess();
237     }
238     else {
239       myDebugProcess.getProcessHandler().destroyProcess();
240     }
241   }
242 }