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(),
152 myConsoleInitSemaphore = new Semaphore(0);
154 consoleRunner.addConsoleListener(new PydevConsoleRunnerImpl.ConsoleListener() {
156 public void handleConsoleInitialized(LanguageConsoleView consoleView) {
157 myConsoleInitSemaphore.release();
163 waitFor(myConsoleInitSemaphore);
165 myCommandSemaphore = new Semaphore(1);
167 myConsoleView = consoleRunner.getConsoleView();
168 myProcessHandler = consoleRunner.getProcessHandler();
170 myExecuteHandler = consoleRunner.getConsoleExecuteActionHandler();
172 myCommunication = consoleRunner.getPydevConsoleCommunication();
174 myCommunication.addCommunicationListener(new ConsoleCommunicationListener() {
176 public void commandExecuted(boolean more) {
177 myCommandSemaphore.release();
181 public void inputRequested() {
185 myProcessHandler.addProcessListener(new ProcessAdapter() {
187 public void processTerminated(ProcessEvent event) {
188 if (event.getExitCode() != 0 && !myProcessCanTerminate) {
189 Assert.fail("Process terminated unexpectedly\n" + output());
194 OutputPrinter myOutputPrinter = null;
195 if (shouldPrintOutput) {
196 myOutputPrinter = new OutputPrinter();
197 myOutputPrinter.start();
200 waitForOutput("PyDev console");
207 setProcessCanTerminate(true);
209 if (myOutputPrinter != null) {
210 myOutputPrinter.stop();
217 private void disposeConsoleProcess() throws InterruptedException {
218 myProcessHandler.destroyProcess();
220 waitFor(myProcessHandler);
222 if (!myProcessHandler.isProcessTerminated()) {
223 if (!waitFor(myProcessHandler)) {
224 if (!myProcessHandler.isProcessTerminated()) {
225 throw new RuntimeException("Cannot stop console process");
229 myProcessHandler = null;
233 * Waits until all passed strings appear in output.
234 * If they don't appear in time limit, then exception is raised.
237 * @throws InterruptedException
239 public void waitForOutput(String... string) throws InterruptedException {
242 List<String> missing = Lists.newArrayList();
243 String out = output();
245 for (String s : string) {
246 if (!out.contains(s)) {
255 Assert.fail("Strings: <--\n" + StringUtil.join(missing, "\n---\n") + "-->" + "are not present in output.\n" + output());
262 protected void waitForReady() throws InterruptedException {
264 while (!myExecuteHandler.isEnabled() || !canExecuteNow()) {
266 Assert.fail("Console is not ready");
273 protected boolean canExecuteNow() {
274 return myExecuteHandler.canExecuteNow();
277 public void setShouldPrintOutput(boolean shouldPrintOutput) {
278 this.shouldPrintOutput = shouldPrintOutput;
281 private class OutputPrinter {
282 private Thread myThread;
283 private int myLen = 0;
285 public void start() {
286 myThread = new Thread(() -> doJob(), "py console printer");
287 myThread.setDaemon(true);
291 private void doJob() {
299 catch (Exception ignored) {
303 private synchronized void printToConsole() {
305 if (s.length() > myLen) {
306 System.out.print(s.substring(myLen));
311 public void stop() throws InterruptedException {
313 myThread.interrupt();
318 protected void exec(final String command) throws InterruptedException {
320 myCommandSemaphore.acquire(1);
321 UIUtil.invokeAndWaitIfNeeded(new Runnable() {
324 myConsoleView.executeInConsole(command);
329 protected boolean hasValue(String varName, String value) throws PyDebuggerException {
330 PyDebugValue val = getValue(varName);
331 return val != null && value.equals(val.getValue());
334 protected void setValue(String varName, String value) throws PyDebuggerException {
335 PyDebugValue val = getValue(varName);
336 myCommunication.changeVariable(val, value);
339 protected PyDebugValue getValue(String varName) throws PyDebuggerException {
340 XValueChildrenList l = myCommunication.loadFrame();
345 for (int i = 0; i < l.size(); i++) {
346 String name = l.getName(i);
347 if (varName.equals(name)) {
348 return (PyDebugValue)l.getValue(i);
355 protected List<String> getCompoundValueChildren(PyDebugValue value) throws PyDebuggerException {
356 XValueChildrenList list = myCommunication.loadVariable(value);
357 List<String> result = Lists.newArrayList();
358 for (int i = 0; i < list.size(); i++) {
359 result.add(((PyDebugValue)list.getValue(i)).getValue());
364 protected void input(String text) {
365 myConsoleView.executeInConsole(text);
368 protected void waitForFinish() throws InterruptedException {
369 waitFor(myCommandSemaphore);
372 protected void execNoWait(final String command) {
373 UIUtil.invokeLaterIfNeeded(() -> myConsoleView.executeCode(command, null));
376 protected void interrupt() {
377 myCommunication.interrupt();
381 public void addTextToEditor(final String text) {
382 UIUtil.invokeAndWaitIfNeeded(new Runnable() {
385 getConsoleView().setInputText(text);
386 PsiDocumentManager.getInstance(getProject()).commitAllDocuments();