blob: 9a242934042d327f213401c0fa8793ea6fe245d5 [file] [log] [blame]
package com.jetbrains.env.python.console;
import com.google.common.collect.Lists;
import com.intellij.execution.console.LanguageConsoleView;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.util.ui.UIUtil;
import com.intellij.xdebugger.frame.XValueChildrenList;
import com.jetbrains.env.PyExecutionFixtureTestTask;
import com.jetbrains.python.console.*;
import com.jetbrains.python.console.pydev.ConsoleCommunicationListener;
import com.jetbrains.python.debugger.PyDebugValue;
import com.jetbrains.python.debugger.PyDebuggerException;
import com.jetbrains.python.sdkTools.SdkCreationType;
import org.jetbrains.annotations.NotNull;
import org.junit.Assert;
import java.util.List;
import java.util.concurrent.Semaphore;
/**
* @author traff
*/
public class PyConsoleTask extends PyExecutionFixtureTestTask {
private boolean myProcessCanTerminate;
protected PyConsoleProcessHandler myProcessHandler;
protected PydevConsoleCommunication myCommunication;
private boolean shouldPrintOutput = false;
private PythonConsoleView myConsoleView;
private Semaphore mySemaphore;
private Semaphore mySemaphore0;
private PydevConsoleExecuteActionHandler myExecuteHandler;
public PyConsoleTask() {
setWorkingFolder(getTestDataPath());
}
public PythonConsoleView getConsoleView() {
return myConsoleView;
}
@Override
public void setUp(final String testName) throws Exception {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
try {
if (myFixture == null) {
PyConsoleTask.super.setUp(testName);
}
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
});
}
@NotNull
protected String output() {
return myConsoleView.getConsole().getHistoryViewer().getDocument().getText();
}
public void setProcessCanTerminate(boolean processCanTerminate) {
myProcessCanTerminate = processCanTerminate;
}
@Override
public void tearDown() throws Exception {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
try {
if (myConsoleView != null) {
disposeConsole();
}
PyConsoleTask.super.tearDown();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
});
}
private void disposeConsole() throws InterruptedException {
if (myCommunication != null) {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
try {
myCommunication.close();
}
catch (Exception e) {
e.printStackTrace();
}
myCommunication = null;
}
});
}
disposeConsoleProcess();
if (myConsoleView != null) {
new WriteAction() {
@Override
protected void run(@NotNull Result result) throws Throwable {
Disposer.dispose(myConsoleView);
myConsoleView = null;
}
}.execute();
}
}
@Override
public void runTestOn(final String sdkHome) throws Exception {
final Project project = getProject();
final Sdk sdk = createTempSdk(sdkHome, SdkCreationType.EMPTY_SDK);
setProcessCanTerminate(false);
PydevConsoleRunner consoleRunner = PydevConsoleRunner.create(project, sdk, PyConsoleType.PYTHON, getWorkingFolder());
before();
mySemaphore0 = new Semaphore(0);
consoleRunner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() {
@Override
public void handleConsoleInitialized(LanguageConsoleView consoleView) {
mySemaphore0.release();
}
});
consoleRunner.run();
waitFor(mySemaphore0);
mySemaphore = new Semaphore(0);
myConsoleView = consoleRunner.getConsoleView();
myProcessHandler = (PyConsoleProcessHandler)consoleRunner.getProcessHandler();
myExecuteHandler = (PydevConsoleExecuteActionHandler)consoleRunner.getConsoleExecuteActionHandler();
myCommunication = consoleRunner.getPydevConsoleCommunication();
myCommunication.addCommunicationListener(new ConsoleCommunicationListener() {
@Override
public void commandExecuted(boolean more) {
mySemaphore.release();
}
@Override
public void inputRequested() {
}
});
myProcessHandler.addProcessListener(new ProcessAdapter() {
@Override
public void processTerminated(ProcessEvent event) {
if (event.getExitCode() != 0 && !myProcessCanTerminate) {
Assert.fail("Process terminated unexpectedly\n" + output());
}
}
});
OutputPrinter myOutputPrinter = null;
if (shouldPrintOutput) {
myOutputPrinter = new OutputPrinter();
myOutputPrinter.start();
}
waitForOutput("PyDev console");
try {
testing();
after();
}
finally {
setProcessCanTerminate(true);
if (myOutputPrinter != null) {
myOutputPrinter.stop();
}
disposeConsole();
}
}
private void disposeConsoleProcess() throws InterruptedException {
myProcessHandler.destroyProcess();
waitFor(myProcessHandler);
if (!myProcessHandler.isProcessTerminated()) {
if (!waitFor(myProcessHandler)) {
if (!myProcessHandler.isProcessTerminated()) {
throw new RuntimeException("Cannot stop console process");
}
}
}
myProcessHandler = null;
}
/**
* Waits until all passed strings appear in output.
* If they don't appear in time limit, then exception is raised.
*
* @param string
* @throws InterruptedException
*/
public void waitForOutput(String... string) throws InterruptedException {
int count = 0;
while (true) {
List<String> missing = Lists.newArrayList();
String out = output();
boolean flag = true;
for (String s : string) {
if (!out.contains(s)) {
flag = false;
missing.add(s);
}
}
if (flag) {
break;
}
if (count > 10) {
Assert.fail("Strings: <--\n" + StringUtil.join(missing, "\n---\n") + "-->" + "are not present in output.\n" + output());
}
Thread.sleep(2000);
count++;
}
}
protected void waitForReady() throws InterruptedException {
int count = 0;
while (!myExecuteHandler.isEnabled() || !canExecuteNow()) {
if (count > 10) {
Assert.fail("Console is not ready");
}
Thread.sleep(300);
count++;
}
}
protected boolean canExecuteNow() {
return myExecuteHandler.canExecuteNow();
}
public void setShouldPrintOutput(boolean shouldPrintOutput) {
this.shouldPrintOutput = shouldPrintOutput;
}
private class OutputPrinter {
private Thread myThread;
private int myLen = 0;
public void start() {
myThread = new Thread(new Runnable() {
@Override
public void run() {
doJob();
}
});
myThread.setDaemon(true);
myThread.start();
}
private void doJob() {
try {
while (true) {
printToConsole();
Thread.sleep(500);
}
}
catch (Exception ignored) {
}
}
private synchronized void printToConsole() {
String s = output();
if (s.length() > myLen) {
System.out.print(s.substring(myLen));
}
myLen = s.length();
}
public void stop() {
printToConsole();
myThread.interrupt();
}
}
protected void exec(String command) throws InterruptedException {
waitForReady();
int p = mySemaphore.availablePermits();
myConsoleView.executeInConsole(command);
mySemaphore.acquire(p + 1);
//waitForOutput(">>> " + command);
}
protected boolean hasValue(String varName, String value) throws PyDebuggerException {
PyDebugValue val = getValue(varName);
return val != null && value.equals(val.getValue());
}
protected void setValue(String varName, String value) throws PyDebuggerException {
PyDebugValue val = getValue(varName);
myCommunication.changeVariable(val, value);
}
protected PyDebugValue getValue(String varName) throws PyDebuggerException {
XValueChildrenList l = myCommunication.loadFrame();
if (l == null) {
return null;
}
for (int i = 0; i < l.size(); i++) {
String name = l.getName(i);
if (varName.equals(name)) {
return (PyDebugValue)l.getValue(i);
}
}
return null;
}
protected List<String> getCompoundValueChildren(PyDebugValue value) throws PyDebuggerException {
XValueChildrenList list = myCommunication.loadVariable(value);
List<String> result = Lists.newArrayList();
for (int i = 0; i<list.size(); i++) {
result.add(((PyDebugValue)list.getValue(i)).getValue());
}
return result;
}
protected void input(String text) {
myConsoleView.executeInConsole(text);
}
protected void waitForFinish() throws InterruptedException {
waitFor(mySemaphore);
}
protected void execNoWait(final String command) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
myConsoleView.executeCode(command, null);
}
});
}
protected void interrupt() {
myCommunication.interrupt();
}
public void addTextToEditor(final String text) {
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
@Override
public void run() {
getConsoleView().getLanguageConsole().setInputText(text);
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
}
}
);
}
}