package org.python.pydev.debug.newconsole;
import java.io.File;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IProcess;
import org.python.pydev.core.TestDependent;
import org.python.pydev.debug.model.AbstractDebugTarget;
import org.python.pydev.debug.model.AbstractDebugTargetWithTransmission;
import org.python.pydev.debug.model.IVariableLocator;
import org.python.pydev.debug.model.PyVariable;
import org.python.pydev.debug.model.PyVariableCollection;
import org.python.pydev.debug.model.remote.AbstractDebuggerCommand;
import org.python.pydev.debug.model.remote.GetFrameCommand;
import org.python.pydev.debug.model.remote.VersionCommand;
import org.python.pydev.runners.SimpleRunner;
import com.aptana.interactive_console.console.InterpreterResponse;
import com.aptana.shared_core.callbacks.ICallback;
import com.aptana.shared_core.io.FileUtils;
import com.aptana.shared_core.net.SocketUtil;
import com.aptana.shared_core.structure.Tuple;
/**
* The purpose of this test is to verify the pydevconsole + pydevd works. This
* test does not try to test the console or debugger itself, just the
* combination and new code paths that the feature introduces.
*
* TODO: Iterate over Jython/Python with and without IPython available.
*
*/
public class PydevConsoleDebugCommsTest extends TestCase {
private PydevConsoleCommunication pydevConsoleCommunication;
private Process process;
private DummyDebugTarget debugTarget;
/** Fake $HOME for IPython */
private File homeDir;
@Override
protected void setUp() throws Exception {
String consoleFile = FileUtils.createFileFromParts(TestDependent.TEST_PYDEV_PLUGIN_LOC, "pysrc", "pydevconsole.py")
.getAbsolutePath();
String pydevdDir = new File(TestDependent.TEST_PYDEV_DEBUG_PLUGIN_LOC, "pysrc").getAbsolutePath();
Integer[] ports = SocketUtil.findUnusedLocalPorts(2);
int port = ports[0];
int clientPort = ports[1];
homeDir = FileUtils.getTempFileAt(new File("."), "fake_homedir");
if (homeDir.exists()) {
FileUtils.deleteDirectoryTree(homeDir);
}
homeDir = homeDir.getAbsoluteFile();
homeDir.mkdir();
String[] cmdarray = new String[] { TestDependent.PYTHON_EXE, consoleFile, String.valueOf(port),
String.valueOf(clientPort) };
Map env = new TreeMap();
env.put("HOME", homeDir.toString());
env.put("PYTHONPATH", pydevdDir);
String sysRoot = System.getenv("SystemRoot");
if (sysRoot != null) {
env.put("SystemRoot", sysRoot); //Needed on windows boxes (random/socket. module needs it to work).
}
String[] envp = new String[env.size()];
int i = 0;
for (Object entry : env.entrySet()) {
Map.Entry e = (Entry) entry;
Object key = e.getKey();
envp[i] = key + "=" + e.getValue();
i += 1;
}
process = SimpleRunner.createProcess(cmdarray, envp, null);
pydevConsoleCommunication = new PydevConsoleCommunication(port, process, clientPort);
pydevConsoleCommunication.hello(new NullProgressMonitor());
ServerSocket socket = new ServerSocket(0);
pydevConsoleCommunication.connectToDebugger(socket.getLocalPort());
socket.setSoTimeout(5000);
Socket accept = socket.accept();
debugTarget = new DummyDebugTarget();
debugTarget.startTransmission(accept);
}
@Override
protected void tearDown() throws Exception {
if (homeDir.exists()) {
FileUtils.deleteDirectoryTree(homeDir);
}
process.destroy();
pydevConsoleCommunication.close();
debugTarget.terminate();
}
private final class CustomGetFrameCommand extends GetFrameCommand {
private final Boolean[] passed;
private CustomGetFrameCommand(Boolean[] passed, AbstractDebugTarget debugger, String locator) {
super(debugger, locator);
this.passed = passed;
}
@Override
public void processErrorResponse(int cmdCode, String payload) {
passed[0] = false;
}
@Override
public void processOKResponse(int cmdCode, String payload) {
super.processOKResponse(cmdCode, payload);
passed[0] = true;
}
}
private class DummyDebugTarget extends AbstractDebugTarget {
@Override
public void processCommand(String sCmdCode, String sSeqCode, String payload) {
System.out.println(sCmdCode + ":" + sSeqCode + ":" + payload);
}
public IProcess getProcess() {
return null;
}
public void launchRemoved(ILaunch launch) {
}
@Override
public boolean canTerminate() {
return false;
}
@Override
public boolean isTerminated() {
return false;
}
}
private void waitUntilNonNull(Object[] object) {
for (int i = 0; i < 50; i++) {
if (object[0] != null)
return;
try {
Thread.sleep(250);
} catch (InterruptedException e) {
// Retry now
}
}
Assert.fail("Timed out waiting for non-null");
}
/**
* This test is the basic comms working, send the command down via XML-RPC
* based new method postCommand and receive response back via
* {@link AbstractDebugTargetWithTransmission}
*/
public void testVersion() throws Exception {
final Boolean passed[] = new Boolean[1];
pydevConsoleCommunication.postCommand(new VersionCommand(debugTarget) {
@Override
public void processOKResponse(int cmdCode, String payload) {
if (cmdCode == AbstractDebuggerCommand.CMD_VERSION && "1.1".equals(payload))
passed[0] = true;
else
passed[0] = false;
}
@Override
public void processErrorResponse(int cmdCode, String payload) {
passed[0] = false;
}
});
waitUntilNonNull(passed);
Assert.assertTrue(passed[0]);
}
private void execInterpreter(String command) {
final Boolean done[] = new Boolean[1];
ICallback<Object, InterpreterResponse> onResponseReceived = new ICallback<Object, InterpreterResponse>() {
public Object call(InterpreterResponse arg) {
done[0] = true;
return null;
}
};
ICallback<Object, Tuple<String, String>> onContentsReceived = new ICallback<Object, Tuple<String, String>>() {
public Object call(Tuple<String, String> arg) {
return null;
}
};
pydevConsoleCommunication.execInterpreter(command, onResponseReceived, onContentsReceived);
waitUntilNonNull(done);
}
/**
* Test that variables can be seen
*/
public void testVariable() throws Exception {
execInterpreter("my_var=1");
IVariableLocator frameLocator = new IVariableLocator() {
public String getPyDBLocation() {
return "console_main\t0\tFRAME";
}
};
final Boolean passed[] = new Boolean[1];
CustomGetFrameCommand cmd = new CustomGetFrameCommand(passed, debugTarget, frameLocator.getPyDBLocation());
pydevConsoleCommunication.postCommand(cmd);
waitUntilNonNull(passed);
Assert.assertTrue(passed[0]);
PyVariable[] variables = PyVariableCollection.getCommandVariables(cmd, debugTarget, frameLocator);
Map<String, PyVariable> variableMap = new HashMap<String, PyVariable>();
for (PyVariable variable : variables) {
variableMap.put(variable.getName(), variable);
}
Assert.assertTrue(variableMap.containsKey("my_var"));
Assert.assertEquals("int: 1", variableMap.get("my_var").getValueString());
}
}