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.console;
18 import com.google.common.collect.Lists;
19 import com.google.common.collect.Maps;
20 import com.intellij.execution.ExecutionManager;
21 import com.intellij.execution.console.LanguageConsoleView;
22 import com.intellij.execution.process.ProcessAdapter;
23 import com.intellij.execution.process.ProcessEvent;
24 import com.intellij.execution.ui.RunContentDescriptor;
25 import com.intellij.openapi.application.Result;
26 import com.intellij.openapi.application.WriteAction;
27 import com.intellij.openapi.project.Project;
28 import com.intellij.openapi.projectRoots.Sdk;
29 import com.intellij.openapi.util.Disposer;
30 import com.intellij.openapi.util.Ref;
31 import com.intellij.openapi.util.text.StringUtil;
32 import com.intellij.psi.PsiDocumentManager;
33 import com.intellij.util.ui.UIUtil;
34 import com.intellij.xdebugger.frame.XValueChildrenList;
35 import com.jetbrains.env.PyExecutionFixtureTestTask;
36 import com.jetbrains.python.console.*;
37 import com.jetbrains.python.console.pydev.ConsoleCommunicationListener;
38 import com.jetbrains.python.debugger.PyDebugValue;
39 import com.jetbrains.python.debugger.PyDebuggerException;
40 import com.jetbrains.python.sdkTools.SdkCreationType;
41 import org.jetbrains.annotations.NotNull;
42 import org.junit.Assert;
44 import java.util.List;
45 import java.util.concurrent.Semaphore;
50 public class PyConsoleTask extends PyExecutionFixtureTestTask {
51 private boolean myProcessCanTerminate;
53 protected PyConsoleProcessHandler myProcessHandler;
54 protected PydevConsoleCommunication myCommunication;
56 private boolean shouldPrintOutput = false;
57 private PythonConsoleView myConsoleView;
58 private Semaphore myCommandSemaphore;
59 private Semaphore myConsoleInitSemaphore;
60 private PydevConsoleExecuteActionHandler myExecuteHandler;
62 private Ref<RunContentDescriptor> myContentDescriptorRef = Ref.create();
64 public PyConsoleTask() {
68 public PythonConsoleView getConsoleView() {
73 public void setUp(final String testName) throws Exception {
74 if (myFixture == null) {
75 super.setUp(testName);
80 protected String output() {
81 return myConsoleView.getHistoryViewer().getDocument().getText();
84 public void setProcessCanTerminate(boolean processCanTerminate) {
85 myProcessCanTerminate = processCanTerminate;
89 public void tearDown() throws Exception {
90 UIUtil.invokeAndWaitIfNeeded(new Runnable() {
94 if (myConsoleView != null) {
96 myCommunication.waitForTerminate();
98 PyConsoleTask.super.tearDown();
100 catch (Exception e) {
101 throw new RuntimeException(e);
107 private void disposeConsole() throws InterruptedException {
108 if (myCommunication != null) {
109 UIUtil.invokeAndWaitIfNeeded(new Runnable() {
113 myCommunication.close();
115 catch (Exception e) {
118 myCommunication = null;
123 disposeConsoleProcess();
125 ExecutionManager.getInstance(getProject()).getContentManager().getAllDescriptors().forEach((Disposer::dispose));
127 if (myConsoleView != null) {
130 protected void run(@NotNull Result result) throws Throwable {
131 Disposer.dispose(myConsoleView);
132 myConsoleView = null;
139 public void runTestOn(final String sdkHome) throws Exception {
140 final Project project = getProject();
142 final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.EMPTY_SDK);
144 setProcessCanTerminate(false);
146 PydevConsoleRunner consoleRunner =
147 new PydevConsoleRunnerImpl(project, sdk, PyConsoleType.PYTHON, myFixture.getTempDirPath(), Maps.newHashMap(),
148 PyConsoleOptions.getInstance(project).getPythonConsoleSettings(),
153 myConsoleInitSemaphore = new Semaphore(0);
155 consoleRunner.addConsoleListener(new PydevConsoleRunnerImpl.ConsoleListener() {
157 public void handleConsoleInitialized(LanguageConsoleView consoleView) {
158 myConsoleInitSemaphore.release();
164 waitFor(myConsoleInitSemaphore);
166 myCommandSemaphore = new Semaphore(1);
168 myConsoleView = consoleRunner.getConsoleView();
169 myProcessHandler = consoleRunner.getProcessHandler();
171 myExecuteHandler = consoleRunner.getConsoleExecuteActionHandler();
173 myCommunication = consoleRunner.getPydevConsoleCommunication();
175 myCommunication.addCommunicationListener(new ConsoleCommunicationListener() {
177 public void commandExecuted(boolean more) {
178 myCommandSemaphore.release();
182 public void inputRequested() {
186 myProcessHandler.addProcessListener(new ProcessAdapter() {
188 public void processTerminated(ProcessEvent event) {
189 if (event.getExitCode() != 0 && !myProcessCanTerminate) {
190 Assert.fail("Process terminated unexpectedly\n" + output());
195 OutputPrinter myOutputPrinter = null;
196 if (shouldPrintOutput) {
197 myOutputPrinter = new OutputPrinter();
198 myOutputPrinter.start();
201 waitForOutput("PyDev console");
208 setProcessCanTerminate(true);
210 if (myOutputPrinter != null) {
211 myOutputPrinter.stop();
218 private void disposeConsoleProcess() throws InterruptedException {
219 myProcessHandler.destroyProcess();
221 waitFor(myProcessHandler);
223 if (!myProcessHandler.isProcessTerminated()) {
224 if (!waitFor(myProcessHandler)) {
225 if (!myProcessHandler.isProcessTerminated()) {
226 throw new RuntimeException("Cannot stop console process");
230 myProcessHandler = null;
234 * Waits until all passed strings appear in output.
235 * If they don't appear in time limit, then exception is raised.
238 * @throws InterruptedException
240 public void waitForOutput(String... string) throws InterruptedException {
243 List<String> missing = Lists.newArrayList();
244 String out = output();
246 for (String s : string) {
247 if (!out.contains(s)) {
256 Assert.fail("Strings: <--\n" + StringUtil.join(missing, "\n---\n") + "-->" + "are not present in output.\n" + output());
263 protected void waitForReady() throws InterruptedException {
265 while (!myExecuteHandler.isEnabled() || !canExecuteNow()) {
267 Assert.fail("Console is not ready");
274 protected boolean canExecuteNow() {
275 return myExecuteHandler.canExecuteNow();
278 public void setShouldPrintOutput(boolean shouldPrintOutput) {
279 this.shouldPrintOutput = shouldPrintOutput;
282 private class OutputPrinter {
283 private Thread myThread;
284 private int myLen = 0;
286 public void start() {
287 myThread = new Thread(() -> doJob(), "py console printer");
288 myThread.setDaemon(true);
292 private void doJob() {
300 catch (Exception ignored) {
304 private synchronized void printToConsole() {
306 if (s.length() > myLen) {
307 System.out.print(s.substring(myLen));
312 public void stop() throws InterruptedException {
314 myThread.interrupt();
319 protected void exec(final String command) throws InterruptedException {
321 myCommandSemaphore.acquire(1);
322 UIUtil.invokeAndWaitIfNeeded(new Runnable() {
325 myConsoleView.executeInConsole(command);
330 protected boolean hasValue(String varName, String value) throws PyDebuggerException {
331 PyDebugValue val = getValue(varName);
332 return val != null && value.equals(val.getValue());
335 protected void setValue(String varName, String value) throws PyDebuggerException {
336 PyDebugValue val = getValue(varName);
337 myCommunication.changeVariable(val, value);
340 protected PyDebugValue getValue(String varName) throws PyDebuggerException {
341 XValueChildrenList l = myCommunication.loadFrame();
346 for (int i = 0; i < l.size(); i++) {
347 String name = l.getName(i);
348 if (varName.equals(name)) {
349 return (PyDebugValue)l.getValue(i);
356 protected List<String> getCompoundValueChildren(PyDebugValue value) throws PyDebuggerException {
357 XValueChildrenList list = myCommunication.loadVariable(value);
358 List<String> result = Lists.newArrayList();
359 for (int i = 0; i < list.size(); i++) {
360 result.add(((PyDebugValue)list.getValue(i)).getValue());
365 protected void input(String text) {
366 myConsoleView.executeInConsole(text);
369 protected void waitForFinish() throws InterruptedException {
370 waitFor(myCommandSemaphore);
373 protected void execNoWait(final String command) {
374 UIUtil.invokeLaterIfNeeded(() -> myConsoleView.executeCode(command, null));
377 protected void interrupt() {
378 myCommunication.interrupt();
382 public void addTextToEditor(final String text) {
383 UIUtil.invokeAndWaitIfNeeded(new Runnable() {
386 getConsoleView().setInputText(text);
387 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();