A way to filter out non-relevant environments
[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.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;
42
43 import java.io.IOException;
44 import java.lang.reflect.InvocationTargetException;
45 import java.net.ServerSocket;
46 import java.util.Set;
47 import java.util.concurrent.Semaphore;
48
49 /**
50  * @author traff
51  */
52 public class PyDebuggerTask extends PyBaseDebuggerTask {
53
54   private boolean myMultiprocessDebug = false;
55   protected PythonRunConfiguration myRunConfiguration;
56
57
58   public PyDebuggerTask(@Nullable final String relativeTestDataPath, String scriptName, String scriptParameters) {
59     super(relativeTestDataPath);
60     setScriptName(scriptName);
61     setScriptParameters(scriptParameters);
62     init();
63   }
64
65   public PyDebuggerTask(@Nullable final String relativeTestDataPath, String scriptName) {
66     this(relativeTestDataPath, scriptName, null);
67   }
68
69   protected void init() {
70
71   }
72
73   @Nullable
74   @Override
75   public Set<String> getTagsToCover() {
76     return Sets.newHashSet("python2.6", "python2.7", "python3.5", "python3.6", "jython", "IronPython", "pypy");
77   }
78
79   public void runTestOn(String sdkHome) throws Exception {
80     final Project project = getProject();
81
82     final ConfigurationFactory factory = PythonConfigurationType.getInstance().getConfigurationFactories()[0];
83
84
85     final RunnerAndConfigurationSettings settings =
86       RunManager.getInstance(project).createRunConfiguration("test", factory);
87
88     myRunConfiguration = (PythonRunConfiguration)settings.getConfiguration();
89
90     myRunConfiguration.setSdkHome(sdkHome);
91     myRunConfiguration.setScriptName(getScriptName());
92     myRunConfiguration.setWorkingDirectory(myFixture.getTempDirPath());
93     myRunConfiguration.setScriptParameters(getScriptParameters());
94
95     new WriteAction() {
96       @Override
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());
101       }
102     }.execute();
103
104     final PyDebugRunner runner = (PyDebugRunner)ProgramRunnerUtil.getRunner(getExecutorId(), settings);
105     Assert.assertTrue(runner.canRun(getExecutorId(), myRunConfiguration));
106
107     final Executor executor = DefaultDebugExecutor.getDebugExecutorInstance();
108     final ExecutionEnvironment env = new ExecutionEnvironment(executor, runner, settings, project);
109
110     final PythonCommandLineState pyState = (PythonCommandLineState)myRunConfiguration.getState(executor, env);
111
112     assert pyState != null;
113     pyState.setMultiprocessDebug(isMultiprocessDebug());
114
115     final ServerSocket serverSocket;
116     try {
117       //noinspection SocketOpenedButNotSafelyClosed
118       serverSocket = new ServerSocket(0);
119     }
120     catch (IOException e) {
121       throw new ExecutionException("Failed to find free socket port", e);
122     }
123
124
125     final int serverLocalPort = serverSocket.getLocalPort();
126     final RunProfile profile = env.getRunProfile();
127
128     PythonDebuggerTest.createExceptionBreak(myFixture, false, false, false); //turn off exception breakpoints by default
129
130     before();
131
132     setProcessCanTerminate(false);
133
134     myTerminateSemaphore = new Semaphore(0);
135     
136     new WriteAction<ExecutionResult>() {
137       @Override
138       protected void run(@NotNull Result<ExecutionResult> result) throws Throwable {
139         myExecutionResult =
140           pyState.execute(executor, runner.createCommandLinePatchers(myFixture.getProject(), pyState, profile, serverLocalPort));
141
142         mySession = XDebuggerManager.getInstance(getProject()).
143           startSession(env, new XDebugProcessStarter() {
144             @NotNull
145             public XDebugProcess start(@NotNull final XDebugSession session) {
146               myDebugProcess =
147                 new PyDebugProcess(session, serverSocket, myExecutionResult.getExecutionConsole(), myExecutionResult.getProcessHandler(), isMultiprocessDebug());
148
149
150               StringBuilder output = new StringBuilder();
151
152               myDebugProcess.getProcessHandler().addProcessListener(new ProcessAdapter() {
153
154                 @Override
155                 public void onTextAvailable(ProcessEvent event, Key outputType) {
156                   output.append(event.getText());
157                 }
158
159                 @Override
160                 public void processTerminated(ProcessEvent event) {
161                   myTerminateSemaphore.release();
162                   if (event.getExitCode() != 0 && !myProcessCanTerminate) {
163                     Assert.fail("Process terminated unexpectedly\n" + output.toString());
164                   }
165                 }
166               });
167
168
169               myDebugProcess.getProcessHandler().startNotify();
170
171               return myDebugProcess;
172             }
173           });
174         result.setResult(myExecutionResult);
175       }
176     }.execute().getResultObject();
177
178     OutputPrinter myOutputPrinter = null;
179     if (shouldPrintOutput) {
180       myOutputPrinter = new OutputPrinter();
181       myOutputPrinter.start();
182     }
183
184
185     myPausedSemaphore = new Semaphore(0);
186     
187
188     mySession.addSessionListener(new XDebugSessionListener() {
189       @Override
190       public void sessionPaused() {
191         if (myPausedSemaphore != null) {
192           myPausedSemaphore.release();
193         }
194       }
195     });
196
197     doTest(myOutputPrinter);
198   }
199
200   protected String getExecutorId() {
201     return DefaultDebugExecutor.EXECUTOR_ID;
202   }
203
204   public PythonRunConfiguration getRunConfiguration() {
205     return myRunConfiguration;
206   }
207
208   private boolean isMultiprocessDebug() {
209     return myMultiprocessDebug;
210   }
211
212   public void setMultiprocessDebug(boolean multiprocessDebug) {
213     myMultiprocessDebug = multiprocessDebug;
214   }
215
216   protected void waitForAllThreadsPause() throws InterruptedException, InvocationTargetException {
217     waitForPause();
218     Assert.assertTrue(String.format("All threads didn't stop within timeout\n" +
219                                     "Output: %s", output()), waitForAllThreads());
220     XDebuggerTestUtil.waitForSwing();
221   }
222
223   protected boolean waitForAllThreads() throws InterruptedException {
224     long until = System.currentTimeMillis() + NORMAL_TIMEOUT;
225     while (System.currentTimeMillis() < until && getRunningThread() != null) {
226       Thread.sleep(1000);
227     }
228     return getRunningThread() == null;
229   }
230
231   @Override
232   protected void disposeDebugProcess() throws InterruptedException {
233     if (myDebugProcess != null) {
234       ProcessHandler processHandler = myDebugProcess.getProcessHandler();
235
236       myDebugProcess.stop();
237
238       waitFor(processHandler);
239
240       if (!processHandler.isProcessTerminated()) {
241         killDebugProcess();
242         if (!waitFor(processHandler)) {
243           new Throwable("Cannot stop debugger process").printStackTrace();
244         }
245       }
246     }
247   }
248
249   private void killDebugProcess() {
250     if (myDebugProcess.getProcessHandler() instanceof KillableColoredProcessHandler) {
251       KillableColoredProcessHandler h = (KillableColoredProcessHandler)myDebugProcess.getProcessHandler();
252
253       h.killProcess();
254     }
255     else {
256       myDebugProcess.getProcessHandler().destroyProcess();
257     }
258   }
259 }