[Python-checkins] r50824 - in sandbox/trunk/pdb: Doc/lib/libmpdb.tex mpdb.py test/Makefile test/files/proc.py test/support.py test/test_mconnection.py test/test_mpdb.py test/test_mthread.py test/test_process.py
matt.fleming
python-checkins at python.org
Tue Jul 25 19:11:14 CEST 2006
Author: matt.fleming
Date: Tue Jul 25 19:11:12 2006
New Revision: 50824
Added:
sandbox/trunk/pdb/test/support.py
sandbox/trunk/pdb/test/test_process.py
Modified:
sandbox/trunk/pdb/Doc/lib/libmpdb.tex
sandbox/trunk/pdb/mpdb.py
sandbox/trunk/pdb/test/Makefile
sandbox/trunk/pdb/test/files/proc.py
sandbox/trunk/pdb/test/test_mconnection.py
sandbox/trunk/pdb/test/test_mpdb.py
sandbox/trunk/pdb/test/test_mthread.py
Log:
Change the way we do tests, instead of including docstrings let the name
of the class and test be the description. Also move some common code into
a 'support' file. Documentation update and unit tests for debugging already
running programs. Also added an '-e' option to mpdb to allow executing
commands specified on the cmdline.
Modified: sandbox/trunk/pdb/Doc/lib/libmpdb.tex
==============================================================================
--- sandbox/trunk/pdb/Doc/lib/libmpdb.tex (original)
+++ sandbox/trunk/pdb/Doc/lib/libmpdb.tex Tue Jul 25 19:11:12 2006
@@ -1149,11 +1149,44 @@
this session all commands come from the client and are executed on the
pdbserver.
-\section{External Process Debugging}
+\section{Debugging an already running process}
\label{proc-debug}
This section describes how \module{mpdb} debugs processes that are external
-to the process in which \module{mpdb} is being run.
+to the process in which \module{mpdb} is being run. A user can call the
+\code{mpdb.process_debugging} function inside their code to enable a debugger
+running as a separate process to debug their program. The way this works
+is that \module{mpdb} sets up a signal handler for a specified signal to
+\module{mpdb}'s signal handler. If there was a signal handler for this signal
+previously, it is saved. When this signal handler is traps the signal it
+starts a \code{pdbserver} that a debugger can connect to. Once the debugger
+has 'detached' from the \code{pdbserver}, \module{mpdb}'s signal handler
+is removed and the old signal handler (if any) is restored.
+
+\begin{funcdesc}{process_debugging}{sig, protocol, addr}
+This function enables debugging by another process by setting the signal
+handler for signal \code{sig} to \module{mpdb}'s signal handler. When a client
+wishes to connect to this process, it uses the \code{attach} command which
+sends \module{mpdb}'s \code{debug_signal} to a process. It is up to the user
+of \module{mpdb} to ensure that the programming they are debugging and the
+signal sent during the \code{attach} command are the same, \ref{command::set:}.
+When the signal is received by \module{mpdb}'s signal handler a \code{pdbserver}
+is started on \code{addr} using protoocol \code{protocol}.
+\code{sig}, \code{protocol} and \code{addr} are all optional arguments that
+specify the signal to use for debugging, the protocol to use for \code{pdbserver}
+and the add for the \code{pdbserver}, respectively.
+
+
+This function returns a string which is the address used for the
+\code{pdbserver}. If an address is not specified it defaults to
+a path consisting of the system's temporary
+directory is used as returned by \code{tempfile.gettempdir()}, and a filename
+made up from the process' pid and the word 'mpdb', i.e.
+\code{'/tmp/22987mpdb'}. If \code{protocol} is not specified a FIFO is used.
+The default debug signal is \code{SIGUSR1}.
+\end{funcdesc}
+
+\subsection{Example}
If a program wishes to allow debugging from another process it must import
and call the \code{process_debugging} function from the \module{mpdb} module.
This function sets up a signal handler for \module{mpdb}'s debugging signal
@@ -1168,7 +1201,9 @@
\end{verbatim}
From the debugger console a user must issue the 'attach' command, the
-\code{target_addr} variable must be set \ref{command::set}.
+\code{target_addr} variable must be set \ref{command::set}. The user can
+also select a signal to send to the process by calling the
+\code{set debug-signal} command, \ref{command::set}.
\begin{verbatim}
(MPdb) set target [protcol] [address]
@@ -1181,4 +1216,3 @@
specify the hostname, as it can only be 'localhost'.
-
Modified: sandbox/trunk/pdb/mpdb.py
==============================================================================
--- sandbox/trunk/pdb/mpdb.py (original)
+++ sandbox/trunk/pdb/mpdb.py Tue Jul 25 19:11:12 2006
@@ -54,10 +54,13 @@
self.lastcmd = ''
self.connection = None
self.debugger_name = 'mpdb'
- self._show_cmds.append('target-address')
- self._show_cmds.sort()
- self._info_cmds.append('target')
- self._info_cmds.sort()
+
+ self.setcmds.add('debug-signal', self.set_debug_signal)
+ self.setcmds.add('target-address', self.set_target_address)
+ self.showcmds.add('debug-signal', self.show_debug_signal)
+ self.showcmds.add('target-address', self.show_target_address)
+ self.infocmds.add('target', self.info_target)
+
self.target_addr = "" # target address used by 'attach'
self.debug_signal = None # The signal used by 'attach'
@@ -92,6 +95,9 @@
self.onecmd = lambda x: pydb.Pdb.onecmd(self, x)
self.do_rquit(None)
return
+ if 'detach'.startswith(line):
+ self.connection.write('rdetach')
+ self.do_detach(None)
self.connection.write(line)
ret = self.connection.readline()
if ret == '':
@@ -139,63 +145,41 @@
self.prompt = self.local_prompt
self.onecmd = lambda x: pydb.Pdb.onecmd(self, x)
- def do_info(self, arg):
- """Extends pydb do_info() to give info about the Mpdb extensions."""
- if not arg:
- pydb.Pdb.do_info(self, arg)
- return
-
- args = arg.split()
- if 'target'.startswith(args[0]) and len(args[0]) > 2:
- self.msg("target is %s" % self.target)
- else:
- pydb.Pdb.do_info(self, arg)
-
- def info_helper(self, cmd, label=False):
- """Extends pydb info_helper() to give info about a single Mpdb
- info extension."""
- if label:
- self.msg_nocr("info %s --" % cmd)
- if 'target'.startswith(cmd):
- self.msg("Names of targets and files being debugged")
- else:
- pydb.Pdb.info_helper(self, cmd)
-
def help_mpdb(self, *arg):
help()
- def do_set(self, arg):
- """ Extends pydb.do_set() to allow setting of mpdb extensions.
+ def set_debug_signal(self, args):
+ """Set the signal sent to a process to trigger debugging."""
+ try:
+ exec 'from signal import %s' % args[1]
+ except ImportError:
+ self.errmsg('Invalid signal')
+ return
+ self.debug_signal = args[1]
+ self.msg('debug-signal set to: %s' % self.debug_signal)
+
+ def set_target_address(self, args):
+ """Set the address of a target."""
+ self.target_addr = "".join(["%s " % a for a in args[1:]])
+ self.target_addr = self.target_addr.strip()
+ self.msg('target address set to %s' % self.target_addr)
+
+ def show_debug_signal(self, arg):
+ """Show the signal currently used for triggering debugging
+ of an already running process.
"""
- if not arg:
- pydb.Pdb.do_set(self, arg)
+ if not self.debug_signal:
+ self.msg('debug-signal not set.')
return
+ self.msg('debug-signal is %s' % self.debug_signal)
- args = arg.split()
- if 'debug-signal'.startswith(args[0]):
- self.debug_signal = args[1]
- self.msg('debug-signal set to: %s' % self.debug_signal)
- elif 'target-address'.startswith(args[0]):
- self.target_addr = "".join(["%s " % a for a in args[1:]])
- self.target_addr = self.target_addr.strip()
- self.msg('target address set to: %s' % self.target_addr)
-
- def do_show(self, arg):
- """Extends pydb.do_show() to show Mpdb extension settings. """
- if not arg:
- pydb.Pdb.do_show(self, arg)
- return
+ def show_target_address(self, arg):
+ """Show the address of the current target."""
+ self.msg('target-address is %s.' % self.target_addr.__repr__())
- args = arg.split()
- if 'debug-signal'.startswith(args[0]):
- if not self.debug_signal:
- self.msg('debug-signal is not set.')
- else:
- self.msg('debug-signal is %s.' % self.debug_signal)
- elif 'target-address'.startswith(args[0]):
- self.msg('target address is %s.' % self.target_addr.__repr__())
- else:
- pydb.Pdb.do_show(self, arg)
+ def info_target(self, args):
+ """Display information about the current target."""
+ self.msg('target is %s' % self.target)
# Debugger commands
def do_attach(self, addr):
@@ -222,21 +206,17 @@
if not self.debug_signal:
from signal import SIGUSR1
self.debug_signal = SIGUSR1
- else:
- # Because self.debug_signal may be a string
- self.debug_signal = eval(self.debug_signal)
try:
os.kill(pid, self.debug_signal)
except OSError, err:
self.errmsg(err)
return
- # Will remove this
- time.sleep(3.0)
-
- # At the moment by default we'll use named pipes for communication
+ # XXX this still needs removing
+ time.sleep(1.0)
+
self.do_target(self.target_addr)
-
+
def do_target(self, args):
""" Connect to a target machine or process.
The first argument is the type or protocol of the target machine
@@ -328,7 +308,7 @@
If a process, it is no longer traced, and it continues its execution. If
you were debugging a file, the file is closed and Pdb no longer accesses it.
"""
- pass
+ raise KeyboardInterrupt
def do_pdbserver(self, args):
""" Allow a debugger to connect to this session.
@@ -409,6 +389,7 @@
self._rebind_input(self.orig_stdin)
self._disconnect()
self.target = 'local'
+ sys.settrace(None)
self.do_quit(None)
def do_restart(self, arg):
@@ -427,6 +408,17 @@
else:
self.msg("Re exec'ing\n\t%s" % self._sys_argv)
os.execvp(self._sys_argv[0], self._sys_argv)
+
+ def do_rdetach(self, arg):
+ """ The rdetach command is performed on the pdbserver, it cleans
+ things up when the client has detached from this process.
+ Control returns to the file being debugged and execution of that
+ file continues.
+ """
+ self._rebind_input(self.orig_stdin)
+ self._rebind_output(self.orig_stdout)
+
+ self.cmdqueue.append('continue') # Continue execution
def pdbserver(addr, m):
""" This method sets up a pdbserver debugger that allows debuggers
@@ -472,7 +464,6 @@
def thread_debugging(m):
""" Setup this debugger to handle threaded applications."""
- sys.path.append(os.path.dirname(m._sys_argv[1]))
import mthread
mthread.init(m)
while True:
@@ -492,7 +483,8 @@
""" Allow debugging of other processes. This routine should
be imported and called near the top of the program file.
It sets up signal handlers that are used to create a pdbserver
- that a debugging client can attach to.
+ that a debugging client can attach to. The address of the pdbserver
+ is returned a string.
The optional argument 'sig', specifies which signal will be
used for running process debugging. If 'sig' is not specified
@@ -521,13 +513,12 @@
proto = 'mconnection.MConnectionServerFIFO'
if addr is not None:
- pdbserver_addr = addr
+ pdbserver_addr = proto + " " + addr
else:
- # XXX I don't think a successful symlink attack can be made here,
- # because pdbserver bails if the file for a FIFO already exists.
- tmp = os.tempnam(None,'mpdb') # use 'mpdb' as a prefix
+ from tempfile import gettempdir
+ tmp = gettempdir() + "/" + str(os.getpid()) + "mpdb"
pdbserver_addr = proto + " " + tmp
- print pdbserver_addr
+ return pdbserver_addr
def signal_handler(signum, frame):
""" This signal handler replaces the programs signal handler
@@ -545,13 +536,10 @@
del frame.f_globals['mpdb']
m.do_pdbserver(pdbserver_addr)
+ m.set_trace(frame)
- try:
- m.set_trace(m.curframe)
- finally:
- m.do_quit(None)
- import signal
- signal.signal(signum, old_handler)
+ import signal
+ signal.signal(signum, old_handler)
def main():
@@ -571,7 +559,9 @@
+ " 'protocol address scriptname'."),
make_option("-d", "--debug-thread", action="store_true",
help="Turn on thread debugging."),
- make_option("--pid", dest="pid", help="Attach to running process PID.")
+ make_option("--pid", dest="pid", help="Attach to running process PID."),
+ make_option("-e", "--exec", dest="commands",
+ help="Specify commands to execute.")
]
opts = process_options(mpdb, "mpdb", os.path.basename(sys.argv[0])
@@ -594,6 +584,10 @@
# module search path.
sys.path[0] = mpdb.main_dirname = os.path.dirname(mpdb.mainpyfile)
+ if opts.commands:
+ cmds = opts.commands.split(',')
+ mpdb.cmdqueue = cmds
+
if opts.target:
target(opts.target, opts, mpdb)
sys.exit()
Modified: sandbox/trunk/pdb/test/Makefile
==============================================================================
--- sandbox/trunk/pdb/test/Makefile (original)
+++ sandbox/trunk/pdb/test/Makefile Tue Jul 25 19:11:12 2006
@@ -8,10 +8,10 @@
PY = python
-.PHONY: all test test_mpdb test_mconnection test_mthread
+.PHONY: all test test_mpdb test_mconnection test_mthread test_process
all: test
-test: test_mpdb test_mconnection test_mthread
+test: test_mpdb test_mconnection test_mthread test_process
test_mpdb:
@$(PY) test_mpdb.py
@@ -21,3 +21,6 @@
test_mthread:
@$(PY) test_mthread.py
+
+test_process:
+ @$(PY) test_process.py
Modified: sandbox/trunk/pdb/test/files/proc.py
==============================================================================
--- sandbox/trunk/pdb/test/files/proc.py (original)
+++ sandbox/trunk/pdb/test/files/proc.py Tue Jul 25 19:11:12 2006
@@ -8,10 +8,14 @@
sys.path.append('../..')
import mpdb
-mpdb.process_debugging()
+mpdb.process_debugging(protocol='tcp', addr=':9000')
+
+try:
+ while True:
+ for i in range(10):
+ x = i
+except KeyboardInterrupt:
+ pass
+
-while True:
- for i in range(10):
- x = i
-
Added: sandbox/trunk/pdb/test/support.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/pdb/test/support.py Tue Jul 25 19:11:12 2006
@@ -0,0 +1,73 @@
+# This file contains classes that can be imported by any of the unit tests.
+
+import sys ; sys.path.append('..')
+import time
+import threading
+
+class DummyStdout(object):
+ """ This class is a replacement for sys.stdout. All output is
+ stored in this object's 'lines' instance variable.
+ """
+ def __init__(self):
+ self.lines = []
+
+ def flush(self):
+ pass
+
+ def write(self, msg):
+ pass
+
+from mpdb import MPdb
+
+class MPdbTest(MPdb):
+ """ This class provides a version of the MPdb class that is
+ suitable for use in unit testing. All output is captured and
+ stored in this object's 'lines' instance variable.
+ """
+ def __init__(self, cmds=[]):
+ """ The optional argument 'cmds' is a list specifying commands
+ that this instance should interpret in it's 'cmdloop' method.
+ """
+ MPdb.__init__(self)
+ self.lines = []
+ self.cmdqueue = cmds
+ self.botframe = None
+
+ def msg_nocr(self, msg):
+ self.lines.append(msg)
+
+
+class Pdbserver(threading.Thread, MPdb):
+ """ This class provides a fully functional pdbserver that runs
+ in a separate thread. The optional argument 'addr' specifies a
+ protocol and an address to use for pdbserver.
+ """
+ def __init__(self, addr=None):
+ MPdb.__init__(self)
+ threading.Thread.__init__(self)
+ self._sys_argv = ['python', '-c', '"pass"']
+ self.botframe = None
+
+ if not addr:
+ self.addr = 'tcp localhost:8000'
+
+ def run(self):
+ self.do_pdbserver(self.addr)
+ while True:
+ self.cmdloop()
+ if self._user_requested_quit:
+ break
+
+class MPdbTestThread(threading.Thread, MPdbTest):
+ """ This class provides a MPdbTest object that runs in a separate
+ thread.
+ """
+ def __init__(self, cmds=[]):
+ threading.Thread.__init__(self)
+ MPdbTest.__init__(self, cmds)
+
+
+
+
+
+
Modified: sandbox/trunk/pdb/test/test_mconnection.py
==============================================================================
--- sandbox/trunk/pdb/test/test_mconnection.py (original)
+++ sandbox/trunk/pdb/test/test_mconnection.py Tue Jul 25 19:11:12 2006
@@ -6,6 +6,7 @@
import os
import sys
import socket
+import time
import thread
import unittest
@@ -39,14 +40,12 @@
self.client = MConnectionClientTCP()
def testClientConnectToServer(self):
- """(tcp) Connect client to server. """
thread.start_new_thread(repeatedConnect, (self.client, __addr__))
self.server.connect(__addr__)
self.server.disconnect()
def testClientConnectAndRead(self):
- """(tcp) Connect to server and read/write. """
thread.start_new_thread(repeatedConnect, (self.client,__addr__))
self.server.connect(__addr__)
@@ -58,14 +57,12 @@
self.assertEqual('success\n', line, 'Could not read from client')
def testDisconnectDisconnected(self):
- """(tcp) Disconnect a disconnected session. """
s = MConnectionServerTCP()
s.disconnect()
s.disconnect()
def testReadline(self):
- """(tcp) Make sure readline method works. """
thread.start_new_thread(repeatedConnect, (self.client,__addr__))
self.server.connect(__addr__)
@@ -76,7 +73,6 @@
self.server.disconnect()
def testErrorAddressAlreadyInUse(self):
- """(tcp) Test address already in use error. """
thread.start_new_thread(repeatedConnect, (self.client, __addr__))
self.server.connect(__addr__)
@@ -85,31 +81,27 @@
self.assertRaises(ConnectionFailed, s.connect, __addr__, False)
def testInvalidServerAddress(self):
- """(tcp) Connect to an invalid hostname. """
addr = 'fff.209320909xcmnm2iu3-=0-0-z.,x.,091209:2990'
self.assertRaises(ConnectionFailed, self.server.connect, addr)
def testConnectionRefused(self):
- """(tcp) Test connection refused error. """
self.assertRaises(ConnectionFailed, self.client.connect, __addr__)
def testInvalidAddressPortPair(self):
- """(tcp) Test invald hostname, port pair. """
addr = 'localhost 8000'
self.assertRaises(ConnectionFailed, self.server.connect, addr)
def testServerReadError(self):
- """(tcp) Test the ReadError exception."""
thread.start_new_thread(self.server.connect, (__addr__,))
while not self.server._sock:
- pass
+ time.sleep(0.1)
repeatedConnect(self.client, __addr__)
# Wait to make _absolutely_ sure that the client has connected
while not self.server.output:
- pass
+ time.sleep(0.1)
self.client.disconnect()
self.assertRaises(ReadError, self.server.readline)
@@ -138,12 +130,10 @@
self.client.connect(TESTFN)
def testClientToServerConnect(self):
- """(serial) Connect client to server. """
self.client.disconnect()
self.server.disconnect()
def testClientWriteRead(self):
- """(serial) Connect client to server and read/write. """
self.client.write('success!')
line = self.server.readline()
self.assertEquals('success!\n', line, 'Could not read from client.')
@@ -157,11 +147,9 @@
self.assertEquals('great!\n', line, 'Could not read from server.')
def testDisconnectDisconnected(self):
- """(serial) Disconnect a disconnected session. """
self.server.disconnect()
def testReadline(self):
- """(serial) Make sure readline method works. """
self.client.write('success!\nNext line.')
self.client.disconnect()
line = self.server.readline()
@@ -172,7 +160,6 @@
self.assertEquals('', line, 'Could not read third line.')
def testInvalidFilename(self):
- """(serial) Connect to an invalid server. """
client = MConnectionSerial()
self.assertRaises(ConnectionFailed, client.connect,
'/dev/pleasepleasepleasedontexit')
@@ -188,12 +175,10 @@
self.client = MConnectionClientFIFO()
def testConnect(self):
- """(FIFO) Connect a client to a server. """
thread.start_new_thread(self.client.connect, ('test_file',))
self.server.connect('test_file')
def testReadWrite(self):
- """(FIFO) Test reading and writing to and from server/client."""
thread.start_new_thread(self.client.connect, ('test_file',))
self.server.connect('test_file')
@@ -212,12 +197,10 @@
self.assertEquals('received\n', line)
def testMultipleDisconnect(self):
- """(FIFO) Disconnect disconnected connections."""
self.client.disconnect()
self.server.disconnect()
def testReadError(self):
- """(FIFO) Test ReadError."""
thread.start_new_thread(self.client.connect, ('test_file',))
self.server.connect('test_file')
@@ -233,7 +216,6 @@
self.assertRaises(ReadError, self.client.readline)
def testWriteError(self):
- """(FIFO) Test WriteError."""
thread.start_new_thread(self.client.connect, ('test_file',))
self.server.connect('test_file')
@@ -249,7 +231,6 @@
self.assertRaises(WriteError, self.client.write, 'Ni!\n')
def testInvalidPipe(self):
- """(FIFO) Connect to an invalid named pipe."""
self.assertRaises(ConnectionFailed,self.client.connect, 'invalid')
os.unlink('invalid0')
Modified: sandbox/trunk/pdb/test/test_mpdb.py
==============================================================================
--- sandbox/trunk/pdb/test/test_mpdb.py (original)
+++ sandbox/trunk/pdb/test/test_mpdb.py Tue Jul 25 19:11:12 2006
@@ -19,7 +19,7 @@
from mpdb import MPdb, pdbserver, target
from mconnection import (MConnectionClientTCP, MConnectionServerTCP,
ConnectionFailed)
-
+from support import MPdbTest, Pdbserver
TESTFN = 'tester'
# This provides us with a fine-grain way of connecting to a server
@@ -39,32 +39,6 @@
else: raise ConnectionFailed, e
break
-class MPdbTest(MPdb):
- def __init__(self):
- MPdb.__init__(self)
- self.lines = []
- self.botframe = None
-
- def msg_nocr(self, msg):
- self.lines.append(msg)
-
-class Pdbserver(threading.Thread, MPdb):
- def __init__(self):
- MPdb.__init__(self)
- threading.Thread.__init__(self)
- self.botframe = None
- self._sys_argv = ['python', '-c', '"pass"']
-
-
- def run(self):
- self.do_pdbserver('tcp localhost:8000')
- while True:
- try:
- self.cmdloop()
- except Exit:
- break
-
-
class TestRemoteDebugging(unittest.TestCase):
""" Test Case to make sure debugging remotely works properly. """
def tearDown(self):
@@ -77,7 +51,6 @@
# and vice versa.
def testPdbserver(self):
- """ Test the pdbserver. """
client = MPdbTest()
thread.start_new_thread(connect_to_target, (client,))
@@ -92,7 +65,6 @@
self.assertEquals('*** Unknown protocol\n', line)
def testTarget(self):
- """ Test the target command. """
server = MConnectionServerTCP()
thread.start_new_thread(server.connect, (__addr__,True))
@@ -127,7 +99,6 @@
else: break
def testRebindOutput(self):
- """ Test rebinding output. """
self.server = MPdb()
f = open(TESTFN, 'w+')
self.server._rebind_output(f)
@@ -140,7 +111,6 @@
self.assertEquals('some text\n', line, 'Could not rebind output')
def testRebindInput(self):
- """ Test rebinding input. """
self.server = MPdb()
f = open(TESTFN, 'w+')
@@ -155,7 +125,6 @@
self.assertEquals(line, 'help', 'Could not rebind input.')
def testRestart(self):
- """ Test the restart command. """
server = Pdbserver()
server.start()
@@ -175,8 +144,71 @@
if server.connection != None: pass
else: break
+
+class TestMpdbDoc(unittest.TestCase):
+ """ Test the expected output from help commands against actual
+ output to ensure that documentation constantly stays the same.
+ """
+ def setUp(self):
+ self.m = MPdbTest()
+
+ def tearDown(self):
+ del self.m
+
+ def testHelpInfo(self):
+ self.m.onecmd('help info')
+
+ exp = ['Generic command for showing things about the program being debugged.\n',
+ '\nList of info subcommands:\n\n',
+ 'info args --', 'Argument variables of current stack frame\n',
+ 'info breakpoints --', 'Status of user-settable breakpoints\n',
+ 'info display --', 'Expressions to display when program stops, with code numbers\n',
+ 'info globals --', 'Global variables of current stack frame\n',
+ 'info line --', 'Current line number in source file\n',
+ 'info locals --', 'Local variables of current stack frame\n',
+ 'info program --', 'Execution status of the program\n',
+ 'info source --', 'Information about the current Python file\n',
+ 'info target --', 'Display information about the current target.\n']
+ self.assertEquals(self.m.lines, exp)
+
+
+ def testInfoCmd(self):
+ self.m.reset()
+ self.m.onecmd('info')
+ exp = ['args: ', 'No stack.\n',
+ 'breakpoints: ', 'No breakpoints.\n',
+ 'display: ', 'There are no auto-display expressions now.\n',
+ 'globals: ', 'No frame selected.\n',
+ 'line: ', 'No line number information available.\n',
+ 'locals: ', 'No frame selected.\n',
+ 'program: ', 'The program being debugged is not being run.\n',
+ 'source: ', 'No current source file.\n',
+ 'target: ', 'target is local\n']
+ self.assertEquals(self.m.lines, exp)
+
+ def testShowCmd(self):
+ self.m.reset()
+ self.m.onecmd('show')
+
+ exp = ['args: ', 'Argument list to give program being debugged when it is started is \n', '"".\n',
+ 'basename: ', 'basename is off.\n',
+ 'cmdtrace: ', 'cmdtrace is off.\n',
+ # Readline is not always available
+ 'debug-signal: ', 'debug-signal not set.\n',
+ 'history: ',
+ 'interactive: ', 'interactive is on.\n',
+ 'linetrace: ', 'line tracing is off.\n',
+ 'listsize: ', 'Number of source lines pydb will list by default is 10.\n',
+ 'logging: ', 'Future logs will be written to pydb.txt.\n', 'Logs will be appended to the log file.\n', 'Output will be logged and displayed.\n',
+ 'prompt: ', 'pydb\'s prompt is "(MPdb)".\n',
+ 'target-address: ', "target-address is ''.\n",
+ 'version: ', 'pydb version 1.17cvs.\n']
+
+ self.assertEquals(self.m.lines, exp)
+
+
def test_main():
- test_support.run_unittest(TestRemoteDebugging)
+ test_support.run_unittest(TestMpdbDoc, TestRemoteDebugging)
if __name__ == '__main__':
test_main()
Modified: sandbox/trunk/pdb/test/test_mthread.py
==============================================================================
--- sandbox/trunk/pdb/test/test_mthread.py (original)
+++ sandbox/trunk/pdb/test/test_mthread.py Tue Jul 25 19:11:12 2006
@@ -11,7 +11,6 @@
class TestThreadDebugging(unittest.TestCase):
def testMthreadInit(self):
- """ Test the init method of the mthread file. """
m = mpdb.MPdb()
mthread.init(m)
Added: sandbox/trunk/pdb/test/test_process.py
==============================================================================
--- (empty file)
+++ sandbox/trunk/pdb/test/test_process.py Tue Jul 25 19:11:12 2006
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+
+# Unit test for debugging running processes
+
+import os
+import signal
+import sys
+import time
+import threading
+import unittest
+
+from test import test_support
+from support import DummyStdout, MPdbTest, MPdbTestThread
+
+sys.path.append('..')
+
+import mpdb
+
+PROTOCOL = 'tcp'
+ADDRESS = ':9000'
+TESTFN = os.path.abspath(os.curdir+os.path.sep+'.mpdb')
+
+class TestProcessDebugging(unittest.TestCase):
+ def child(self):
+ os.chdir('./files')
+ pid = os.spawnlp(os.P_NOWAIT, './proc.py', './proc.py')
+ os.chdir('..')
+ return pid
+
+ def testTopLevelRoutine(self):
+ sys.stdout = DummyStdout()
+
+ mpdb.process_debugging()
+ self.assertRaises(ValueError, mpdb.process_debugging, -1)
+ self.assertRaises(TypeError, mpdb.process_debugging, 'invalid')
+ self.assertRaises(ValueError, mpdb.process_debugging, signal.NSIG)
+
+ # XXX We can't currently check the validity of the connection
+ # params.
+
+ sys.stdout = sys.__stdout__
+
+ def testSignalHandler(self):
+ pid = self.child()
+ client = MPdbTest()
+ # Allow the child process to catch up
+ time.sleep(0.1)
+ client.onecmd('set target-address tcp :9000')
+ client.onecmd('attach %s' % str(pid))
+ client.onecmd('where')
+ line = client.lines[1]
+ self.assertEquals(line, ' <module>\n(MPdb)')
+ try:
+ client.onecmd('detach')
+ except KeyboardInterrupt:
+ pass
+ os.kill(pid, signal.SIGINT)
+
+ def testCmdLineOption(self):
+ sys.stdout = DummyStdout()
+
+ pid = self.child()
+ os.chdir('..')
+ time.sleep(0.1)
+ os.system("./mpdb.py --exec='set target-address tcp :9000," + \
+ "attach %s,where,detach' -o %s" % (str(pid), TESTFN))
+ os.chdir('./test')
+ os.kill(pid, signal.SIGINT)
+
+ os.waitpid(pid, 0)
+
+ pid = self.child()
+ os.chdir('..')
+ time.sleep(0.1)
+ os.system("./mpdb.py --pid='%s tcp :9000' --exec='where,detach' -o %s" \
+ % (str(pid), TESTFN))
+ os.chdir('./test')
+ os.kill(pid, signal.SIGINT)
+
+ sys.stdout = sys.__stdout__
+
+ def testFifoFilename(self):
+ cls, addr = mpdb.process_debugging().split()
+
+ pid = os.getpid()
+ import tempfile
+ from tempfile import gettempdir
+ tmp = gettempdir()
+
+ path = tmp + os.path.sep + str(pid) + 'mpdb'
+ self.assertEquals(path, addr)
+
+ def tearDown(self):
+ try:
+ os.unlink(TESTFN)
+ except OSError:
+ pass
+
+def test_main():
+ test_support.run_unittest(TestProcessDebugging)
+
+if __name__ == '__main__':
+ test_main()
More information about the Python-checkins
mailing list